生产级RAG三大支柱:上下文感知检索、代理式RAG与混合重排序

发布时间:2026/7/1 23:11:37
生产级RAG三大支柱:上下文感知检索、代理式RAG与混合重排序 1. 项目概述RAG 已不是“加个向量库就能跑”的玩具而是生产级 AI 系统的呼吸系统你肯定见过那种 RAG 教程加载 PDF → 切块 → 嵌入 → 存进 Chroma → 写个 prompt 让 LLM 回答。跑通了界面弹出“答案已生成”大家鼓掌。但真把它塞进一个每天处理 5000 客服工单的 SaaS 后台或者嵌进医生用的临床决策辅助工具里十有八九第二天就崩在凌晨三点——不是召回了完全无关的旧版说明书就是把两个不同年份的药品禁忌混在一起生成了危险建议。这不是模型不行是 RAG 的“呼吸”出了问题吸气检索不准换气融合不稳呼气生成带杂质。2025 年的真实战场里RAG 已彻底脱离“问答 demo”阶段它成了 AI 应用的循环系统——负责把海量、异构、动态更新的知识血液精准、低延迟、可追溯地泵送到大模型的推理引擎里。它不再是一个独立模块而是和 agent 框架、监控告警、数据治理深度耦合的基础设施。本文讲的不是“怎么搭个能跑的 RAG”而是“怎么搭一个上线后敢让 CEO 在董事会演示、敢让三甲医院主任签字上线、敢在金融风控场景里扛住每秒 200 次并发查询的 RAG”。核心就三条Contextual Retrieval上下文感知检索解决“为什么我问的是‘2024 版医保报销比例’你却给我弹出 2022 年的政策解读”Agentic RAG代理式 RAG解决“用户说‘帮我对比 A 药和 B 药对老年糖尿病患者的肝肾影响并给出用药建议’单次检索根本无法覆盖多跳逻辑”Hybrid Retrieval Re-ranking混合检索重排序解决“纯语义搜索在专业术语、缩写、数字精度上天然失准比如‘CRP’和‘C-reactive protein’明明是同一个东西但向量距离可能比‘CRP’和‘CRISPR’还远”。这三者不是并列选项而是递进关系没有扎实的 Hybrid 检索打底Agentic RAG 就是空中楼阁没有 Contextual Retrieval 的动态适配能力Hybrid 检索再强也救不了冷启动场景。接下来我会像带新人进产线一样拆开每一颗螺丝告诉你为什么这么设计、参数怎么调、坑在哪、监控看什么。2. 核心技术路线解构为什么是这三条而不是别的2.1 Contextual Retrieval让检索器学会“听弦外之音”传统 RAG 的检索本质是“关键词/向量匹配”。你输入“苹果手机电池续航差”它就去知识库里找包含“苹果”“手机”“电池”“续航”“差”的 chunk。问题在于人类提问从不按字面意思来。同一句话在客服场景下可能是投诉“差”需要补偿在导购场景下可能是比价“差”想了解竞品优势在维修场景下可能是故障诊断“差”需要查电池健康度阈值。Contextual Retrieval 的核心是把“用户身份、当前对话历史、业务上下文、甚至设备型号”这些非 query 文本的信息编码进检索过程。这不是简单拼接字符串而是构建一个上下文感知的查询重写Query Rewriting管道。我实测过三种主流方案最终在金融合规问答系统里选了第三种Prompt-based Query Expansion提示词扩展用 LLM 把原始 query 改写成更丰富的版本。比如输入“年报审计意见类型”LLM 输出“请返回上市公司 2023 年年度报告中由会计师事务所出具的审计意见类型标准无保留意见/带强调事项段的无保留意见/保留意见/否定意见/无法表示意见的原文描述及对应法规依据”。优点是开发快缺点是引入额外 LLM 调用延迟且改写质量不稳定——我们曾遇到 LLM 把“科创板上市规则”错写成“创业板上市规则”导致召回结果全偏。Dense Context Fusion稠密上下文融合把用户画像如“客户等级VIP”、会话状态如“上一轮已确认用户所在行业医疗器械”、时间戳如“当前日期2025-03-15”分别编码成向量与 query 向量做加权平均或拼接后送入检索器。听起来很美但实测发现当上下文字段超过 5 个时向量空间会严重扭曲。一个“VIP”标签的向量可能比“审计意见”这个核心概念的向量权重还高导致检索结果偏向“VIP 专属服务流程”而非“审计意见定义”。Hybrid Context-Aware Scoring混合上下文感知打分这才是 2025 年生产环境的主流。它不改变原始 query 和文档的向量表示而是在检索后的重排序Re-ranking阶段引入上下文信号作为独立的打分因子。具体做法是先用标准语义检索召回 top-100 chunk然后对每个 chunk计算三个分数Semantic Score原始向量相似度如 cosineContext Match Score用规则或轻量模型判断该 chunk 是否匹配当前上下文。例如chunk 中是否包含“2023 年报”字样匹配时间戳是否属于“审计”知识域匹配用户角色为“内审员”Freshness Score基于 chunk 元数据如最后更新时间计算衰减因子2025 年的文档得分 2023 年的文档。最终总分 Semantic Score × α Context Match Score × β Freshness Score × γ。α、β、γ 不是固定值而是根据业务场景动态调整在法规咨询场景β 权重拉到 0.6在历史数据分析场景γ 权重提到 0.5。这套方案的好处是解耦——检索层保持稳定所有上下文逻辑都在可监控、可灰度的重排序层实现。我们在某银行反洗钱知识库上线后将“可疑交易识别标准”的误召回率从 38% 降到 9%关键就是把“用户角色一线柜员”这个上下文信号作为硬性过滤条件嵌入了 Context Match Score 的计算逻辑里。提示Contextual Retrieval 的最大陷阱是试图用一个“万能上下文编码器”解决所有问题。真实世界里客服、法务、研发的上下文需求天差地别。我的经验是为每个核心业务线单独设计 Context Match Score 的计算规则而不是追求一个通用模型。规则可以很简单比如“法务场景下只接受来源为‘司法解释’或‘最高院指导案例’的 chunk”这种确定性规则比任何黑盒模型都可靠。2.2 Agentic RAG当单次检索失效时让 RAG 学会“分步思考”用户问“对比分析特斯拉 Model Y 和比亚迪海豹在 2024 年 Q3 的销量、电池供应商、起售价、以及各自在欧洲市场的准入认证状态。” 这根本不是一个检索能解决的问题。它需要第一步分别检索“特斯拉 Model Y 2024 Q3 销量”、“比亚迪海豹 2024 Q3 销量”第二步对每个车型分别检索“电池供应商”、“起售价”、“欧洲市场准入认证”第三步对检索到的分散信息进行结构化对齐和冲突检测比如两个来源对“起售价”的定义不同一个是官网标价一个是含补贴价第四步生成符合用户要求的对比表格。Agentic RAG 就是把这一系列动作封装成一个可编排、可中断、可回溯的 agent 工作流。它不是让 LLM “自己想”而是给 LLM 一套清晰的“操作手册”和“工具箱”。我们落地的 Agentic RAG 架构核心是三层Orchestrator调度器一个轻量 Python 函数负责解析用户 query拆解为子任务Task并决定执行顺序串行/并行。它不碰 LLM只做逻辑路由。比如识别到 query 中有“对比”“差异”“优劣”等词就自动触发并行检索模式。Tool Interface工具接口为每个知识源PDF 库、数据库、API封装标准化的调用函数。关键设计是每个工具函数必须返回结构化结果JSON和元数据source_url, last_updated, confidence_score。我们强制要求所有工具函数在返回前对原始文本做一次“事实锚定”——即提取出最核心的实体和数值比如从一段文字中抽取出{model: Model Y, quarter: 2024 Q3, sales_volume: 125000}。这为后续的冲突检测打下基础。Reasoning Layer推理层这才是 LLM 发挥作用的地方。它接收 Orchestrator 分发的子任务结果执行信息整合、逻辑判断、格式生成。重点在于我们禁用了 LLM 的“自由发挥”能力所有输出必须严格遵循预定义的 JSON Schema。比如对比表格的输出 schema 是{ comparison_table: { columns: [指标, 特斯拉 Model Y, 比亚迪海豹], rows: [ [2024 Q3 全球销量, 125,000 辆, 89,200 辆], [电池供应商, 宁德时代、LG 新能源, 弗迪电池] ] } }LLM 只负责填空不负责编造。如果某个字段缺失它必须明确返回status: missing而不是胡编一个数字。这套架构带来的最大收益是可调试性。当结果出错时我们不再面对一个黑盒的“LLM 乱说”而是能精准定位到是 Orchestrator 拆错了任务是 Tool Interface 从数据库里取错了字段还是 Reasoning Layer 的 Schema 填写逻辑有 bug在某车企的智能座舱知识库项目中我们将问题平均定位时间从 47 分钟缩短到 6 分钟。注意Agentic RAG 最容易犯的错误是把 Orchestrator 做得太重。我见过团队用 Llama-3-70B 去做任务拆解结果 80% 的请求卡在调度层。记住Orchestrator 必须是确定性的、规则驱动的、毫秒级响应的。它的唯一使命是把复杂问题变成一堆简单、独立、可并行的 API 调用。让 LLM 去干它最擅长的事——理解模糊语言、整合碎片信息、生成自然文本而不是当一个低效的流程引擎。2.3 Hybrid Retrieval Re-ranking告别“语义搜索万能论”拥抱工程现实纯向量检索Semantic Search在 2025 年依然存在三个无法绕过的硬伤专业术语鸿沟医学文献中“MI” 代表心肌梗死Myocardial Infarction“MRI” 代表磁共振成像Magnetic Resonance Imaging。它们的向量表示在通用语料上训练距离可能很近但在临床场景下混淆是灾难性的。数字与符号失敏用户搜“Python 3.12 新特性”向量检索可能召回大量关于“Python 3.11”甚至“Python 2.7”的内容因为“3.11”和“3.12”的向量距离远小于“新特性”和“旧特性”的距离。长尾实体召回弱一个冷门但关键的法规名称如《医疗器械网络安全注册审查指导原则2024 年修订版》在通用 embedding 模型里缺乏足够训练样本其向量表示极易被淹没。Hybrid Retrieval 的本质是承认“没有一种检索方式是完美的”转而用组合拳弥补短板。我们的生产方案是“BM25关键词 ColBERTv2细粒度语义 Custom Rule Engine业务规则” 三路并行再通过 Learning-to-RankLTR模型融合。BM25 路负责精确匹配。我们用 Elasticsearch 部署专门索引文档的标题、章节名、代码块、表格中的数值。当用户 query 包含明确数字如“3.12”、缩写如“MI”、专有名词如“指导原则”时BM25 的召回精度碾压语义检索。关键是我们为 BM25 索引配置了领域词典把“MI”强制映射到“心肌梗死”把“CRP”映射到“C-reactive protein”这一步在数据预处理时完成不依赖运行时 LLM。ColBERTv2 路负责语义泛化。相比传统 Sentence-BERTColBERT 对 query 和文档是“词粒度”交互的。它能把“心肌梗死”和“MI”在词向量层面直接对齐而不是靠整个句子向量的粗略匹配。我们用它处理 query 中的动词、形容词、抽象概念如“风险”“有效性”“监管要求”。Custom Rule Engine 路这是 2025 年区别于 2023 年的关键。它是一套基于正则和 AST抽象语法树解析的轻量规则引擎。例如当检测到 query 中有“vs”“对比”“差异”等词时Rule Engine 会主动触发“双实体检索”先抽取出“A”和“B”如“Model Y”和“海豹”然后分别检索关于 A 和 B 的文档并标记为“对比组”。这个信号会传给后续的 Re-ranker。三路检索各自返回 top-50 结果汇总去重后得到约 120 个候选 chunk。然后进入 Re-ranking 阶段。我们不用简单的加权平均而是训练了一个小型 XGBoost 模型仅 12 个特征输入包括BM25 分数、ColBERT 分数、Rule Engine 匹配强度chunk 与 query 的实体重叠数用 spaCy 提取chunk 所在文档的权威性得分内部评估白皮书 博客 社区帖子用户 query 的长度短 query 更依赖关键词长 query 更依赖语义实时业务信号如“当前是财报季则财务类文档权重20%”。这个 LTR 模型在标注数据上训练标注标准不是“相关/不相关”而是“相关程度1-5 分”且标注员必须给出理由如“相关chunk 明确提到了 2024 Q3 销量数字”。模型上线后top-5 的准确率从 63% 提升到 89%。更重要的是它让“为什么这个结果排第一”变得可解释——运维同学可以直接查看特征贡献度快速判断是 BM25 还是 Rule Engine 起了主导作用。3. 生产级 RAG 管道搭建从零开始的完整实操指南3.1 数据准备与切块别再用“固定 512 字符”了数据是 RAG 的源头活水切块Chunking是第一道滤网。2023 年流行“固定长度切块”2024 年转向“语义切块”2025 年的共识是没有银弹切块法只有场景定制切块策略。我们为不同数据源设计了五种切块器并在 pipeline 中动态路由PDF/Word 文档产品手册、法规文件使用unstructured库解析保留标题层级。切块逻辑是以二级标题H2为最小单元若 H2 下内容 300 字合并到上一个 H2若 H2 下内容 1200 字则按语义断点如“步骤”“注意事项”“示例”二次切分。关键点强制保留标题路径。一个 chunk 的元数据必须包含[产品手册, Model Y, 充电指南, 家用充电桩设置]这个路径会在 Contextual Retrieval 的 Context Match Score 中作为强信号。数据库记录CRM、ERP 数据不切块整行作为 chunk。但必须做字段加权。例如CRM 表中customer_name字段权重设为 3.0notes字段权重为 1.0created_at字段用于 Freshness Score 计算。我们用pgvector的vector类型存储加权后的混合向量。API 返回的 JSON天气、股票、物流按 JSON Schema 的叶子节点切分。比如一个物流 API 返回{ tracking_number: SF123456789, status: 已签收, location: 北京市朝阳区, timestamp: 2025-03-15T14:22:00Z }我们会生成 4 个 chunk每个 chunk 的文本是tracking_number: SF123456789、status: 已签收等并附带完整的schema_path: $.status。这样当用户搜“SF123456789 的签收时间”系统能精准命中timestampchunk而不是在整段 JSON 里模糊匹配。代码仓库GitHub用tree-sitter解析 AST按函数、类、模块切分。每个 chunk 的文本是函数签名 docstring 前 5 行代码。元数据包含language: python,repo: ai-agent-core,path: /src/retriever/hybrid.py。这使得开发者搜“如何配置重排序权重”能直接定位到set_re_ranking_weights()函数的实现而不是一堆无关的 README。网页新闻、博客用trafilatura提取正文去除导航栏、广告、评论。切块按段落但强制保留段落间的逻辑连接词。我们修改了切块算法如果前一段结尾是“因此”后一段开头是“这表明”则合并为一个 chunk。否则单段就是最小单元。测试显示这对理解因果类 query如“为什么 XX 政策会导致 YY 结果”提升显著。所有切块完成后进入元数据富化Metadata Enrichment阶段。这是很多教程忽略的致命环节。我们为每个 chunk 注入 7 类元数据source_typepdf/db/api/json/code/websource_url或db_tablelast_updated从文件修改时间或数据库updated_at字段获取authorPDF 属性或 Git commit authorconfidence_level人工标注或模型预测高/中/低sensitive_flag是否含 PII/PHI用于权限控制domain_tags用轻量分类模型打标[金融, 合规, 反洗钱]。这些元数据不是摆设它们是 Contextual Retrieval 和 Re-ranking 的燃料。没有它们你的 RAG 就是蒙眼开车。3.2 Embedding 与向量库选型别迷信“越大越好”Embedding 模型选型2025 年的真相是SOTAState-of-the-Art不等于 SOPStandard Operating Procedure。我们对比了 8 个主流开源模型BGE-M3、nomic-embed-text-v1.5、jina-embeddings-v2、text-embedding-3-large 等结论很务实BGE-M3综合性能最强支持多语言、多任务检索/分类/聚类但体积大2.7GB推理延迟高A10 GPU 上 120ms/query。适合对精度要求极高、且能接受延迟的场景如法律文书分析。nomic-embed-text-v1.5体积小380MB速度快A10 上 35ms/query在中文长文本检索上表现惊艳但英文稍弱。这是我们 70% 场景的默认选择。text-embedding-3-largeOpenAI 的闭源模型效果确实好但成本高、有网络依赖、无法私有化。我们只在 PoC概念验证阶段用绝不进生产环境。最终我们采用混合 embedding 策略对每个 chunk同时生成 nomic 和 BGE-M3 两个向量存入向量库。检索时先用 nomic 快速召回 top-200再用 BGE-M3 对这 200 个做精排。这把延迟控制在 85ms 内精度损失不到 1.2%。向量库选型我们弃用了 Chroma社区版功能弱、企业版贵和 Pinecone云服务绑定深选择了Qdrant pgvector 组合Qdrant作为主检索引擎利用其强大的 payload filtering元数据过滤能力。比如Contextual Retrieval 中的“只返回 2024 年后更新的 chunk”就是通过 Qdrant 的filter参数在毫秒级完成无需召回后遍历。pgvector作为元数据和向量的统一存储。所有业务元数据source_url,author,sensitive_flag和向量存在同一张 PostgreSQL 表里。这让我们能用 SQL 做复杂的联合查询比如“找出所有由‘张三’编写、标记为‘高置信度’、且在 2025 年更新的、关于‘RAG 重排序’的 chunk”。这种灵活性是纯向量库无法提供的。部署细节Qdrant 集群用 3 节点1 主 2 从启用 WALWrite-Ahead Logging确保数据不丢pgvector 表的向量字段建ivfflat索引lists100平衡精度和速度所有写入操作走事务保证向量和元数据的一致性。3.3 检索与重排序流水线代码级实现与参数详解下面是我们生产环境 Hybrid Retrieval Re-ranking 流水线的核心 Python 代码已脱敏包含关键注释和参数说明# hybrid_retriever.py from qdrant_client import QdrantClient from pgvector.psycopg2 import register_vector import psycopg2 import xgboost as xgb import numpy as np class HybridRetriever: def __init__(self): # 初始化 Qdrant 客户端语义检索 self.qdrant_client QdrantClient( urlhttp://qdrant:6333, timeout5.0 # 关键超时必须设避免阻塞 ) # 初始化 PostgreSQL 连接元数据 向量存储 self.pg_conn psycopg2.connect( hostpostgres, databaserag_db, userrag_user, passwordxxx ) register_vector(self.pg_conn) # 启用 pgvector # 加载训练好的 XGBoost Re-ranker 模型 self.reranker xgb.Booster() self.reranker.load_model(/models/reranker_v2.json) def retrieve(self, query: str, user_context: dict) - list: 主检索入口 :param query: 原始用户 query :param user_context: 上下文字典如 {role: compliance_officer, time: 2025-03-15} :return: 重排序后的 chunk 列表每个元素是 dict含 text, metadata, score # Step 1: BM25 检索 (Elasticsearch) bm25_results self._es_search(query) # 省略 es_search 实现 # Step 2: ColBERTv2 语义检索 (Qdrant) # 使用 nomic embedding 生成 query 向量 query_vector self._get_nomic_vector(query) colbert_results self.qdrant_client.search( collection_namechunks_nomic, query_vectorquery_vector, limit50, # 关键利用 Qdrant 的 payload filter 做初步上下文过滤 # 这里只过滤掉明显不相关的 source_type精细过滤留到 re-rank filter{ must: [{key: source_type, match: {value: pdf}}] } ) # Step 3: Rule Engine 触发示例检测对比 query rule_results [] if vs in query.lower() or 对比 in query: entities self._extract_entities(query) # 提取 A 和 B for entity in entities: # 对每个实体做单独检索 entity_vector self._get_nomic_vector(entity) rule_results.extend( self.qdrant_client.search( collection_namechunks_nomic, query_vectorentity_vector, limit10, filter{must: [{key: domain_tags, any: [product]}]}, ) ) # Step 4: 合并、去重、截断 all_candidates self._merge_and_dedup( bm25_results, colbert_results, rule_results ) # 去重逻辑基于 chunk_id # Step 5: Re-ranking with XGBoost ranked_chunks self._rerank(all_candidates, query, user_context) return ranked_chunks[:10] # 返回 top-10 def _rerank(self, candidates: list, query: str, user_context: dict) - list: XGBoost 重排序核心 features [] for chunk in candidates: # 特征 1: BM25 分数归一化到 0-1 bm25_score chunk.get(bm25_score, 0.0) / 100.0 # 特征 2: ColBERT 分数cosine 相似度 colbert_score chunk.get(colbert_score, 0.0) # 特征 3: Rule Engine 匹配强度0 或 1 rule_match 1.0 if chunk.get(rule_triggered, False) else 0.0 # 特征 4: 实体重叠数query 和 chunk 的命名实体交集 query_ents set(self._extract_ner(query)) chunk_ents set(self._extract_ner(chunk[text])) ent_overlap len(query_ents chunk_ents) # 特征 5: 权威性得分基于 source_type 和 author authority_score self._get_authority_score(chunk[metadata]) # 特征 6: Freshness 得分指数衰减 days_old (datetime.now() - chunk[metadata][last_updated]).days freshness_score np.exp(-days_old / 365.0) # 一年后衰减到 37% # 特征 7: query 长度短 query 更依赖 BM25 query_len len(query) query_length_factor 1.0 if query_len 20 else 1.5 # 特征 8: 用户角色匹配如果 chunk 的 domain_tags 包含用户角色 role_match 1.0 if ( user_context.get(role) in chunk[metadata].get(domain_tags, []) ) else 0.0 # ... 其他 4 个特征省略 features.append([ bm25_score, colbert_score, rule_match, ent_overlap, authority_score, freshness_score, query_length_factor, role_match, # ... 其他特征 ]) # 转为 numpy 数组预测 X np.array(features) scores self.reranker.predict(xgb.DMatrix(X)) # 将分数赋给 candidates for i, chunk in enumerate(candidates): chunk[rerank_score] float(scores[i]) # 按 rerank_score 降序排列 return sorted(candidates, keylambda x: x[rerank_score], reverseTrue)关键参数说明与调优心得limit50Qdrant 检索数量不是越大越好。我们测试过 100、200发现 top-50 外的 chunk 对最终 top-10 影响微乎其微但会显著增加网络传输和 CPU 计算开销。50 是精度和性能的黄金分割点。lists100pgvector ivfflat 索引lists参数决定了索引的聚类数。太小如 10会导致召回率暴跌太大如 1000会让索引构建时间爆炸。100 是我们 500 万 chunk 数据集的实测最优值。freshness_score的衰减周期365.0这个数字来自业务 SLA。我们承诺“所有法规类内容在发布后 1 年内保持有效”所以一年后权重自然衰减。如果是新闻类应用这里会改成30.030 天。query_length_factor1.5短 query 加权这是通过 A/B 测试确定的。当 query 长度 20 字符时如“MI 治疗指南”用户意图更明确关键词匹配更重要所以给 BM25 分数加权。3.4 Agentic RAG 的 Orchestrator 实现规则驱动拒绝 LLM 调度Orchestrator 的代码必须像瑞士手表一样精准、确定、快速。以下是我们的核心调度逻辑简化版# orchestrator.py import re from typing import List, Dict, Any class RAGOrchestrator: def __init__(self): # 预定义的任务模板Task Template self.task_templates { compare: { pattern: r(vs|对比|差异|比较|优劣), extractor: self._extract_two_entities, tool: parallel_retrieve, max_parallel: 4 }, step_by_step: { pattern: r(首先|然后|接着|最后|第一步|第二步), extractor: self._extract_steps, tool: sequential_retrieve, max_parallel: 1 }, definition: { pattern: r(什么是|定义|含义|解释), extractor: self._extract_term, tool: single_retrieve, max_parallel: 1 } } def plan(self, query: str) - List[Dict[str, Any]]: 将 query 解析为可执行的任务列表 :return: 任务列表每个任务是 dict含 type, params, dependencies tasks [] # Step 1: 匹配最高优先级的模式compare step_by_step definition for task_type, config in self.task_templates.items(): if re.search(config[pattern], query, re.I): entities config[extractor](query) if not entities: continue # 构建任务 task { type: task_type, params: {entities: entities}, dependencies: [], # 当前无依赖 tool: config[tool], max_parallel: config[max_parallel] } # 如果是 compare生成两个子任务 if task_type compare and len(entities) 2: task[sub_tasks] [] for entity in entities[:2]: # 只处理前两个 task[sub_tasks].append({ type: single_retrieve, params: {query: f{entity} 相关信息}, dependencies: [] }) tasks.append(task) break # 只匹配一个最高优先级模式 # Step 2: 如果没匹配到走默认单次检索 if not tasks: tasks.append({ type: default, params: {query: query}, tool: single_retrieve, max_parallel: 1 }) return tasks def _extract_two_entities(self, query: str) - List[str]: 从 query 中提取两个对比实体 # 简单规则找 A vs B 或 A 和 B 模式 vs_match re.search(r(.?)\s(vs|和|与)\s(.?)$, query, re.I) if vs_match: return [vs_match.group(1).strip(), vs_match.group(3).strip()] # 备用找连续的两个名词短语 # 此处省略更复杂的 NLP 逻辑生产环境用 spaCy return [] # 其他 extractor 方法省略...为什么不用 LLM 做调度我们做过对照实验用 Llama-3-8B 做任务拆解平均耗时 1.2 秒且有 7% 的概率把“对比 A 和 B”错拆成三个任务A、B、对比。而上面的规则引擎耗时 8ms准确率 99.99%。在高并发场景下这 1.2 秒就是生死线。Orchestrator 的哲学是用最简单、最确定、最快的方法解决 95% 的问题把剩下的 5%交给 LLM 去攻坚。4. 真实问题排查与避坑指南那些文档里不会写的血泪教训