
1. 项目概述从检索问答到可编排的智能检索先说结论Agentic RAG Pipeline不是把一个大模型包一层检索工具那么简单它是一个让模型像人一样去查资料、筛资料、质疑资料的完整流程系统。这两年只要做知识库问答、企业文档助理、垂直领域客服迟早都会从普通RAG迁到Agentic RAG这条路上来因为传统RAG的瓶颈卡得很死。传统RAG的做法一句话就能说清楚把用户问题拿去向量检索取回最像的几个片段塞进提示词让模型根据片段回答。这个模式在Demo里非常惊艳放到生产环境里却经常翻车。我见过最典型的几个事故用户问的是上个月的销售报表为什么和财务对不上普通RAG直接召回一堆销售报表格式说明模型一本正经地编了个对账结论用户连续追问那华东区呢换成季度维度再看看每一轮都是独立检索上下文完全断开还有的时候检索库里没有相关内容模型照样硬答根本不会说我不知道。Agentic RAG解决的就是这一类问答本身是一个决策过程的场景。它把一次问答拆成多步先判断该不该检索、检索哪个库、要不要换个关键词再查、查回来的片段够不够、答案有没有依据、需要不需要追问补充。每一步都像人在查档案一样能停能退能重来而不是一把梭到底。这个项目是我在做一个企业内部知识库问答系统时的完整实践复盘。系统要覆盖销售数据、技术文档、客服话术、竞品方案四类差异很大的资料用户提问方式从一句话指令到复杂对比都有。项目从普通的向量检索生成一路演进到Agent编排上了路由、工具调用、自我校验、缓存和分布式索引最后跑成了一个能扛住日活十万级调用、回答质量可回归测试的生产级服务。如果你正准备从传统RAG往Agentic RAG升级或者已经遇到召回不精准、多轮断裂、答案不可信这几个老问题这篇内容应该能帮你少踩一半的坑。下文会完整拆解我的架构选型、核心实现、生产级改造和故障排查过程尽量讲清楚每一个关键选择背后的原因而不是只给一个结论。2. 架构设计与技术选型先把场景想清楚再选轮子2.1 传统RAG的四个硬伤决定了架构必须变在动手设计Agentic架构之前我先复盘了传统RAG在真实场景里的四个硬伤这些硬伤直接决定了哪些组件必须引入。第一单次检索的召回上限太低。一个用户的真实需求往往不是一句话能表达完的。比如帮我找一下去年华东大客户的续约情况和主要流失原因这里其实包含了实体华东、大客户、去年、动作续约、流失、维度原因三个检索面。普通的相似度检索只做一次它只能用整个问句去匹配很可能把续约情况和流失原因两个面都匹配得模棱两可。解决这个问题的思路不是把向量模型调得更强而是引入多路检索先让Agent拆解需求分别检索再汇总。第二召回片段和用户问题在语义上相似但逻辑上无关。这是我调试中最头疼的一类问题。向量检索返回的top_k很多是文字风格像而不是逻辑上能回答问题。比如用户问预算审批流程需要几个工作日召回结果可能是预算编制工作日的计算方式词面高度相关实际答非所问。传统RAG没有机制去校验这段内容到底能不能回答问题只能靠模型硬生成。第三多轮对话上下文维护基本为零。用户说那换成季度呢普通RAG会把这句话拿去检索结果什么都查不到因为它没结合上一轮的华东大客户续约一起检索。生产环境里用户根本不是一次问完的这个问题不解决系统就没法真正上线。第四没有不知道的机制。传统RAG的提示词里写一百遍如果资料中没有答案请告知用户模型也会因为生成习惯而编造。真正有效的做法是在流程层面拦截检索结果经相关度校验后低于阈值的直接走无法回答分支根本不进入生成阶段。这就是Agentic和普通RAG的本质区别——决策逻辑从提示词挪到了代码和流程里。2.2 核心组件拆分六个环节各司其职基于上面四个硬伤我把整个Pipeline拆成了六个环节意图解析、检索规划、多路执行、结果融合、答案生成、质量自检。每个环节都不是新造的轮子而是把已有技术按特定方式编排起来。先看意图解析。这里不是简单做意图分类而是用模型把用户的原始输入整理成一份调研计划包含需要检索的子问题、限定条件、预期返回格式。比如用户问分析一下今年华南区客户的流失原因解析结果可能是子问题一华南区客户流失率子问题二流失原因分类及占比限定条件是今年、华南。这一步做得扎实后面每一步都会有正确的输入。再看检索规划和执行。规划决定用哪些检索工具执行则并行调用这些工具。我这边架构上支持三类检索工具向量库检索、结构化数据库查询、API接口检索。向量库负责非结构化资料SQL工具负责销售、财务等结构化数据API负责对接公司内部系统。Agent根据意图决定调哪个工具这一步也是生产环境中收益最明显的地方——以前所有问题都打向量库现在订单类问题直接走SQL准确率提升了不止一个量级。结果融合是很容易被忽略的环节。多路检索会返回很多片段而且各自来源不同直接全塞给模型会把上下文撑爆还会让模型被不相关信息带偏。我的做法是加一层重排器把所有候选片段重新打分去掉明显不相关的只保留跟子问题真正对齐的片段。这等于在检索和生成之间加了一道质量门禁。最后是质量自检。模型给出答案之后不能直接返回用户而是把答案和支撑片段交给一个独立的校验节点打分检查三件事答案有没有脱离资料、关键数字和原文是否矛盾、用户问题里的条件是否都被覆盖到。校验不通过就触发重新检索或者二次生成。这个环节听起来简单实际效果非常惊人——我上线后跑了两周的日志发现自检能拦下大概12%左右的错误回答而且绝大多数是那种看起来合理但实际错了的答案。2.3 技术选型的取舍模型可以通用流程必须可控选型层面我的核心原则是模型疯狂换可以流程骨架不能乱。现在基座模型迭代速度太快今天用的模型可能三个月后就想换掉但如果流程代码里到处耦合着模型API的细节每次换模型都是灾难。所以我在设计上做了一层薄薄的模型适配层统一管理调用、超时、重试和回退业务代码只依赖这个抽象接口。编排框架的选择也很关键。市面上的Agent框架很多从最基础的ReAct范式到图编排范式都有。ReAct范式写起来简单一个循环里反复思考-行动-观察但它的控制流很弱很难表达先并行检索两路再根据结果决定走SQL还是向量库这种带条件分支的流程。我最后选的是图编排范式把上面说的六个环节定义成节点节点之间用显式的边连接允许条件跳转、并行分支和循环回退。代价是代码量比ReAct多不少换来的是每个环节都可以单独插桩、定向调试、权限控制。向量数据库的选择上我测过纯托管服务和自建开源方案。托管服务省心但参数和索引策略都被锁死自建方案初期麻烦但后续调优空间很大。考虑到我们的数据量增长很快且查询模式多样最终选择了自建的方式把向量索引、标量过滤、动态字段更新全都掌握在自己手里。这里有一点要特别提醒不要因为贪图方便把所有数据放进同一个collection一定要按业务域分库否则不同语义空间的向量会互相干扰召回质量会显著下降。目录索引更新的策略也值得展开一下。生产环境里文档是持续变动的今天改了合同模板明天上线了新政策如果索引不更新Agent检索到的永远是老内容。我采用的方式是增量更新加版本管理每个文档块都带版本时间戳定时任务扫描变更只对变更部分重新切块和向量化。这样既能控制计算成本又保证检索到的是最新内容。3. 核心实现拆解每一步都有明确目的3.1 文档处理与向量索引切块参数不是玄学文档处理这个环节很多人会低估它的影响。切块大小、重叠长度、是否保留层级信息这些参数直接决定了后续所有环节的上限。一个典型的反例是把一份二十页的技术方案当成一个块去向量化检索时确实能召回这份文档但它是整块召回模型读到的上下文里有大量无关章节生成的答案自然稀碎。我最终的切块方案是这样的按语义段落切分块大小控制在400到600个token之间相邻块重叠80到120个token。这个数字不是拍脑袋定的它是根据我们业务文档的平均段落长度和问题粒度测试出来的。块太小了语义不完整一个问题的答案被切到两三个块里检索只能召回一半块太大了噪声增多重排器压力变大。重叠区域是为了防止检索时正好落在段落边界导致关键信息丢失。切完后每个块会标注来源文档ID、章节路径、标题层级、更新时间这些元数据在后面做过滤和展示时非常有用。向量化的细节同样值得注意。嵌入模型我们试过多个最后选了一个在中文业务文档上表现稳定的通用模型维度是1024。关键的调优点是嵌入之前要做文本清洗把表格转成带标题的文字描述、把PDF的页眉页脚去掉、把第X页共Y页这类噪音剔除干净。不做清洗的话检索结果里会混进大量页眉噪声尤其是技术文档几乎每页的页眉都是同一段话会严重干扰相关性排序。索引构建策略上我用的是IVF倒排加乘积量化的组合方式。检索时先通过倒排索引缩小候选集再计算向量距离精排。生产环境为了性能牺牲一点召回率是必要的。但这里有一个重要原则如果业务要求高召回率就要随时准备切回暴力检索模式。所以我留了一个开关支持按查询动态选择检索模式而不是全量走ANN索引。3.2 Agent编排层实现状态机是核心工具是边界Agent编排层是整个Pipeline的大脑。我的实现不是让模型自由发挥而是把它约束在一个状态机里。每个节点只干一件确定的事模型的能力被用在做出选择上而不是决定流程怎么走上。举个例子路由节点的内部逻辑是这样实现的def route_query(intent: Intent) - str: 根据意图解析结果决定走哪个检索分支。 返回: vector / sql / api / none if intent.query_type structured_data_analysis: # 订单、财务、人效这类问题直接走结构化查询 return sql if intent.query_type knowledge_retrieval: # 方案文档、流程制度、FAQ 这类问题走语义检索 return vector if intent.query_type real_time_status: # 涉及实时状态的问题走 API 调用 return api if intent.need_more_info: # 信息不足以定位问题进入澄清对话 return clarify return none这个函数看起来简陋但它把Agent的行为限定在了几种模式里不会出现模型自由发挥去调用一个不存在的工具这种生产环境事故。这里有个经验不要试图让模型自己发明工具工具集必须是提前定义好的白名单模型只能做选择题不能做填空题。工具定义也要注意两个细节。第一每个工具的描述要写得像给人类助手的指令一样清楚包括它能干什么、不能干什么、输入参数怎么填。模型就是靠这些描述来决定调哪个工具的描述含糊等于让模型盲猜。第二工具的参数要做强校验尤其要防止LLM输出非法参数。我的做法是在工具入口统一做一层schema校验不合法就返回一个友好的错误信息给Agent让它自己修正而不是直接让Pipeline崩溃。节点之间的状态传递也得仔细设计。我把中间结果设计成不可变的结构每个节点只读取上一个节点的输出写入自己名字下的新字段。这样最大的好处是可以随意回放任意一条请求的完整执行轨迹对定位问题帮助极大。排查的时候只要看每个节点的输入输出就能知道是哪一步出了岔子不需要在日志汪洋里捞针。3.3 缓存、并发与限流可扩展性的第一道地基很多做RAG的人都忽略了缓存我最初也没做缓存结果上线第一周就被同样的热门问题打了三千多次向量库成本和延迟双双爆表。后来我加了两层缓存问题立刻缓解。第一层是答案级别的语义缓存。用户问的问题经常有同义表述比如上个月退款率多少和上月退款比率本质是一样的。如果还按字符串精确匹配缓存毫无用处。我的做法是把用户问题先做一次嵌入然后用向量相似度去缓存里找历史问题相似度高于0.92的直接复用答案。这层缓存命中率在我们的场景里能有20%以上效果很明显。第二层是检索结果缓存。单纯缓存答案有一个问题如果资料更新了缓存里的答案还是旧的。所以我把缓存键设计成查询向量资料版本号的组合。资料版本号变了缓存自然失效不需要手动清理。这个设计在文档频繁更新的业务里非常实用。并发和限流这块直接关系到生产稳定性。Agentic RAG的每个请求会调用多次模型和检索工具比普通RAG的耗时和资源消耗大好几倍如果不做限流一个流量尖峰就能把下游模型服务的配额打爆。我的方案是双层限流入口层用令牌桶限制总QPS节点层给每个模型调用单独设置并发数和超时时间。再有就是熔断机制某个依赖服务连续报错超过阈值就自动切换降级策略比如检索服务挂了直接返回友好提示而不是让Agent无限重试把整个链路拖死。4. 生产级改造可扩展性、可观测性与评估4.1 水平扩展思路无状态优先有状态隔离很多人一听到可扩展就开始讨论分布式向量库但我的经验是先把无状态部分做干净。Agent编排层本身是无状态的状态只存在于请求上下文里这意味着我可以随时横向扩容编排服务的实例数。真正有状态的是向量索引、缓存和对话会话这三部分要单独设计。向量索引的水平扩展我采用的是按业务域分片加副本的方式。每个业务域维护独立的分片比如销售文档一个分片、技术文档一个分片。查询来的时候先在路由层确定要访问哪些分片再去对应分片上检索。这样单个分片的数据量不会无限膨胀内存占用稳定可控。加新业务域的时候直接挂新分片就行不动老集群。缓存服务的扩展要点是冷热分离。热数据放在内存层比如每天活跃问题的缓存冷数据放到专门的低成本存储。我设了一个简单的规则一周内被命中超过五次的访问进入热缓存其他全部下沉。这个策略让热缓存命中率稳定在70%左右内存占用却控制了80%以内。会话状态的处理也值得注意。Agentic RAG的多轮对话会把上下文越攒越长如果全部放在内存里并发一高就会吃满内存。我的做法是只保留当前任务窗口内的信息比如最近六轮的关键实体和检索结果而不是把每轮的完整中间状态都堆在内存里。任务结束就把压缩后的摘要写回会话存储下次继续时重新加载。4.2 可观测性设计没有追踪优化无从谈起Agentic RAG的请求链路比普通接口长得多一个请求可能经历多次模型调用、多次检索、一轮自检重试任何一个环节变慢都会影响整体体验。所以可观测性必须从第一天就开始做不能等出了问题再补。我做的第一件事是给每个请求生成一个全链路追踪ID。这个ID从入口开始贯穿意图解析、路由、检索、生成、自检、返回每跳落一条监控日志记录节点名、耗时、输入输出摘要和状态。查询的时候一条命令就能拉出这个请求完整走过了哪些节点、每步花了多长时间。排查为什么这么慢的时候真的事半功倍。第二件事是自定义三个核心指标端到端耗时、节点级耗时、工具调用成功率。端到端耗时是用户体验的直接反馈节点级耗时能快速定位瓶颈是在检索还是模型生成工具调用成功率则暴露Agent选错工具或参数错误的频率。这三个指标配合分位数统计我可以看到P50用户在体验什么水平、P95用户到底多卡不会被平均数的平滑效果骗过去。第三件事是埋点追踪Agent的决策过程。每次Agent选择了哪条路由、每个重排分数是多少、自检为什么通过或不通过都要记录。这么做的目的是让评估不只是看最终答案对不对而是能回溯到是哪一步决策导致答案不对。这个功能在后面对比不同路由策略时非常有用比如我调整了某个检索阈值可以通过历史日志看出它对后续所有步骤的连锁影响。4.3 离线评估与回归测试让优化不再靠感觉生产级和Demo级的本质区别在于有没有一整套评估机制。没有评估你改了路由逻辑或检索参数根本不知道是变好了还是变坏了。我在这套系统上花了很大精力做离线评估集大概两千多条真实用户问题每条都人工标注了预期检索结果和标准答案。每次改动上线前先跑一遍评估集对比改动前后的指标变化。评估指标的选取也要谨慎。单看正确率不够我还要看几个分项指标检索命中率就是标准答案对应的文档块有没有被召回排序质量就是重排后标准块排在什么位置最终答案的完整度和忠实度完整度关注用户问题里的关键条件有没有都被回答忠实度关注答案有没有超出检索资料的内容。这四项分开计算谁出了问题一目了然。我踩过一个很典型的评估坑一开始只看答案和标准答案的相似度结果模型开始用非常中庸的措辞来换取高分回答是说得对但都是废话。后来加入了必须引用资料片段ID的硬约束每个关键论点都要标记来源评估时校验这些引用是否存在且相关这套系统的言之有据水平一下就上来了。这个机制对生产环境意义重大因为用户投诉最多的就是AI在胡说八道。5. 常见问题与排查技巧实录5.1 检索召回质量差先从数据侧找原因别急着调参数我发现有相当多的人一看到检索结果不好就调top_k和相似度阈值这其实是本末倒置。检索质量差的根源一大半在数据侧只有一小半在参数侧。最常见的问题是文档切块不合理。比如一篇文章的某个结论分布在多个段落里切块时被物理分隔开检索自然召回不全。解决方法是切块前先做结构分析把同一个逻辑单元的内容尽量放进一个块里而不是死板地按字符数切。这个调整往往比换嵌入模型更见效。第二个原因是检索时缺少元数据过滤。前面提到每个块都带文档ID和章节路径但很多人构建索引时存了这些信息查询时却忘了用。比如用户只看2024年的资料那就应该在向量检索前先按年份过滤让嵌入模型只需要在正确的子集里找相似而不是在全部文档里大海捞针。加上过滤之后召回准确率的提升肉眼可见。第三个原因是重排器没有用对。向量检索的初排是语义相似重排器是问答相关这两者的判断逻辑完全不同。如果你的系统只做了初排没有重排建议优先补重排器而不是继续调top_k。我上线重排器之后把top_k从8调到6不仅回答质量变好生成阶段的token消耗还降了不少属于用更少的输入得到了更好的输出。5.2 Agent陷入循环或路径爆炸给Agent画一条边界线Agent化之后最常遇到的新问题就是循环。模型会反复调用同一个检索工具得到的还是同样的结果然后又决定再检索一次白白消耗算力和时间。这个问题我第一次上线的时候几乎每周都碰到后来做了三件事才算压住。第一给每个工具调用加上重复检测。同一个查询在同一个Agent会话里如果已经调用过同样的工具两次第二次返回结果时直接附带标记告诉Agent你之前已经查过这个了如果没有新条件不要再重复调用。这个检测在下层拦截比在提示词里叮嘱模型要可靠得多。第二给整个Agent流程设置最大步数。我设置的默认值是8步超过8步还没生成出最终答案就强制走总结当前已有信息并生成部分答案的兜底分支或者直接返回需要更多信息。这个限制必须写在硬代码里不能只靠模型自觉。第三给单次检索增加时间与范围衰减机制。同一个会话里如果Agent已经看过多轮检索结果继续检索时系统会自动把上下文里的历史结果标记为已知信息并降低它们在新一轮路由中的权重。这能在流程层面抑制反复看同一批资料的行为。5.3 延迟、成本与稳定性问题核心瓶颈在模型调用次数Agentic RAG的延迟和成本本质上跟模型调用次数强相关。普通RAG平均一次生成调用Agentic RAG的核心链路往往有三到五次模型调用多轮场景会更多。延迟压不下来先看调用次数是否合理。我优化的第一个突破口是能不用模型就不用模型。在我的流程里路由就是一段轻量规则代码绝大多数情况下不需要调模型做意图分类。只有规则判断不了的时候才升级到模型解析。这个设计让大概60%的请求只走了规则路由一次模型调用都不增加。类似的思想也用在自检环节先跑规则检查比如数字是否对得上、引用片段ID是否存在于结果中规则通过就直接放行只有规则检查存疑的请求才进入模型二次校验。这一步直接把自检环节的平均耗时砍掉了一半还多。第二个突破口是提示词的瘦身。一开始为了让Agent行为稳定提示词写得很长动辄两千多token。后来发现很多内容是冗余的真正约束行为的核心指令只有几百token。提示词越长首token延迟越高成本也越高。精简提示词之后端到端耗时平均降了10%生成质量没有下降。第三个突破口才是模型本身的提速。如果前面的环节都优化过了延迟还差再考虑换更快的模型或开流式输出。流式输出在生产环境里其实是双刃剑它确实能改善首字等待体验但如果你在自检通过前就把内容流给用户又遇到自检不通过需要重答体验反而更差。我的做法是只有通过自检的答案才开启流式返回宁可让用户多等一秒也不要让错误答案先发出去再撤回。说到稳定性还要提一下依赖服务的超时和重试策略。模型服务偶发超时在生产环境是常态不能指望它永远不超时。我给每次调用设置了两档超时快速超时和整体超时。快速超时如果发现模型服务响应异常立刻切换到备用模型服务整体超时则直接放弃本次调用走降级分支。重试次数我不能设太多最多两次再多就是浪费资源了因为Agentic链路里每次重试的成本比传统RAG高太多了。我在实际运维中还发现一个很容易被忽视的坑会话长度失控。Agentic RAG的多轮对话会把每轮的工具调用结果、中间思考过程都放进历史里轮次一多上下文越来越大每次请求的模型输入成本也跟着水涨船高。我的做法是两个限制会话轮次最多二十轮超过就强制总结归档每轮只保留最终的检索结果和答案摘要中间的决策中间态全部丢弃。这个改动让长会话的成本曲线从线性增长变成了平台期爬升。这套系统上线到现在给我最深的体会是Agentic RAG的难点根本不在让模型变得聪明而在让流程足够克制。模型本身的能力已经足够强真正需要下功夫的是给它一个结构清晰的决策边界、一套可信的工具、一扇能看到运行状况的观测窗口以及一个能随时回退重试的安全网。每一步看起来都不复杂组合在一起才会产生质变。如果你正在做类似的东西我建议从一条最小链路开始先把路由到检索到生成到自检跑通再逐个环节加深而不是一上来就铺很大的摊子——等每块砖都历过生产环境的检验再谈扩展性和全场景覆盖会稳得多。