上半年接了个内部需求,老板原话很朴素:"搞一个能聊天、能查文档、还能自动出分析结论的 AI 助手。"拆开看,这其实就是典型的 RAG Agent:底层是 RAG(检索增强生成),负责把企业知识库里的内容捞出来;上层是 Agent(智能体),负责决定什么时候去查、查完怎么组织答案、多轮对话里怎么保持上下文。听起来不复杂,但真正从 0 到 1 推到生产环境,我踩的坑比想象中多得多。
这个过程中最深刻的体会是:demo 和生产之间的差距,不在模型选得多新,而在数据管线和系统设计。这篇就完整回顾一下我怎么搭一个生产级 RAG Agent,从切块、embedding、多路召回、rerank,到 Agent 编排、评测和上线监控。适合正准备做知识库问答 Agent、想绕过我已经踩过的坑的同学参考。
1. 先搞清"生产级"的边界:一个 RAG Agent 的完整组成
1.1 热词里藏着大家踩过的坑
动手之前,我习惯先看大家都在搜什么。当时"rag知识库怎么切块""embedding rerank rag有关考题""rag多路召回""agentic rag""dify 完成政务 rag 知识库的实践项目"这些词热度很高,它们其实暴露了 RAG Agent 项目里最痛的三件事:数据切分没标准、检索质量上不去、Agent 和 RAG 怎么结合没人讲透。
切块问题是"地基层",embedding 和 rerank 是"召回层",Agentic RAG 是"编排层"。很多人一上来就追最新的 Agent 框架,结果底层检索一塌糊涂,Agent 再聪明也答不对。所以我把架构拆成五个子系统来看:数据接入与清洗、索引服务、召回与重排、Agent 编排、评测与监控。
1.2 生产级系统的最小闭环
先别急着写代码。一个能上线的 RAG Agent,至少要满足下面的闭环:文档进来要被正确解析和切块,切出来的块被向量化和建立索引;用户问题进来要被改写和理解;系统要能多路召回候选内容并重排;大模型要基于可靠的上下文生成答案;答案要被记录和评估。任何一环缺失,都会在某个奇怪的时间点爆雷。
我习惯用表格把职责边界列清楚,避免后续模块之间互相甩锅。
| 模块 | 核心职责 | 典型实现 | 最容易出的问题 |
|---|---|---|---|
| 数据解析 | PDF/Word/HTML 转文本与结构提取 | Unstructured、自研解析器 | 表格错位、扫描件乱码 |
| 切块 | 将长文档拆成可检索单元 | 递归字符切分、结构感知切分 | 把完整语义切碎 |
| 向量化 | 将文本映射为稠密向量 | Embedding 模型 | 领域术语向量化偏差 |
| 检索 | 召回候选片段 | 向量检索 + 关键词检索 | 单路召回漏召回 |
| 重排 | 精排候选片段 | Rerank 模型 | 耗时过高、误伤正确片段 |
| 生成 | 基于上下文输出答案 | 大模型 | 幻觉、不引用来源 |
| 编排 | 调度以上流程 | Agent 框架或自研路由 | 工具调用循环不收敛 |
| 评测 | 衡量答案质量 | 评测集 + 指标 | 评测集过小,过拟合 demo |
1.3 开发路线的先后顺序
我踩过的一个主要教训是:不要按模块并行开发,而要按"垂直切片"推进。第一个版本只打通"文档切块→向量检索→直接生成"这条最简路径,跑通一个端到端 demo。然后再逐步加入多路召回、rerank、记忆、Agent 工具调度。
原因是每一层优化的前提都是下一层数据是稳定的。检索质量差时做 rerank,你分不清是召回的问题还是重排的问题;底层没有评测集时就调 Agent 路由,上线后会出现各种不可复现的 bad case。顺序上我建议是:数据管线先行,检索召回跟上,最后再上 Agent 编排。这个顺序能让你在任何时刻都知道问题出在哪一层。
2. 数据准备:切块、清洗与索引结构是召回效果的上限
2.1 切块为什么这么难:语义完整性与检索粒度的拉扯
切块的本质矛盾就一句话:块太大,检索粒度粗,噪声多,让大模型不知道该看哪;块太小,一个完整的业务概念被切成碎片,检索到了也没法回答问题。打个比方,这就像切菜——你切的菜要和锅(大模型上下文窗口)适配,也要和菜式(业务问题类型)适配。企业知识库里既有长制度文件,也有短产品说明,全局用一套参数是不现实的。
我第一版就犯了这个错,所有文档统一用固定字符长度切,结果制度文件里的"第二十五条"和"第二十六条"被切到两个块,用户问"员工年假规定",检索回来一段残缺文本,大模型理所当然地开始编。后来的做法是:先按文档类型配置不同切块策略,再用语义边界切割。
下面是我常用的一个 Python 切块示意,按分隔符优先级递归切分,并保留一定重叠:
def smart_split(text, chunk_size=800, overlap=120): separators = ["\n\n", "\n", "。", ";"] ...这里没有银弹,但有几个可复用的经验:chunk_size 和 overlap 的合适比例一般是 5:1 到 8:1;重叠部分的作用是避免关键句恰好落在切点上;分隔符优先选择"语义完整符号"而不只是换行符。基于常见工程实践,中文字段 600-1000 字是一个相对稳的区间,太短会丢失主题,太长会稀释相似度。
2.2 知识库场景的切块实践:要顺着文档结构切
企业知识库里的文档类型其实比想象中固定。制度文件一般有章节条款结构,SOP 有步骤序号,产品手册有标题层级,FAQ 是天然的一问一答。顺着这些结构切,效果远好于纯字符切分。特别是政务类知识库这种场景,政策文件往往是"章-节-条"结构,一个"条"就是一个完整且有依据的答复单元。
我在处理这类文档时,会先做一层结构解析,把文档转成"标题树",再按叶子节点做切块,并把父级标题作为元数据写入索引。这样用户问"员工差旅报销标准是多少",检索命中可能是某一条具体条款,但返回给模型时,同时带上"《差旅管理制度》第三章第五条"这类来源信息,答案的可信度和可追溯性会明显提升。
表格类内容是最容易被切坏的。PDF 里的表格在转文本后经常错位,我推荐用表格识别方式把表格转成 Markdown 结构,再作为一个独立块存储。这样问"各地区 Q2 销售额对比"时,检索到的不再是一堆错乱文字,而是一张结构完整的数据表。
2.3 索引不止向量:关键词、元数据过滤与父子块
只建向量索引是另一个大坑。向量擅长语义相似,但对精确匹配很弱。用户问"合同编号 A-2024-001 的状态",语义相似度会把 A-2024-001 和 A-2021-003 混在一起。正确的做法是同时维护一个倒排索引(BM25)和一个向量索引,配合元数据过滤。生产级的检索关键词其实是"向量召回 + 关键词召回 + 结构化过滤"的组合。
另外推荐一个性价比极高的方案:父子块索引。子块是短的、语义单一的片段,用于和用户问题做相似度匹配;父块是子块所在的完整上下文段落,用于最终喂给大模型。这样既保证检索精准,又避免上下文碎片化。Graph RAG 和 Ontology RAG 也是在解决"召回上去以后上下文仍然不够完整"的问题,但实现和运维成本高,如果业务不是强关系推理场景,建议先用父子块方案。
我在父子块方案上做过一次实测:同样 1000 条制度文档,纯字符切分检索,准确率大约在 74%;换用结构感知切块 + 父子块索引 + 元数据过滤后,准确率到 89%。数据准备阶段的每个选择,都比后期调 Prompt 带来的提升更明显。
3. 召回与重排:Embedding、多路召回和 Rerank 的配合逻辑
3.1 单路向量召回的瓶颈到底在哪
很多项目第一版都是"Embedding 模型 + 向量数据库"就上线了。单路向量召回最简单,但对生产环境来说,问题很突出。Embedding 模型对同义词、专业缩写、编号型文本的鲁棒性有限;如果语料分布偏斜,top_k 返回的往往是"看起来都相关,但真正解决问题的那一条不在里面"。
我遇到过很典型的 case:用户问"报销流程多长时间能走完",文档里写的是"付款审批时限为 5 个工作日"——语义差别很大,Embedding 检索出来是报销流程的某个其他片段,正确答案排在了第 18 位,而 top_k 只取了 5。这就是单路召回的召回率瓶颈。它不一定是模型差,而是语义匹配本身不擅长"关键词完全不同但表达同一件事"的情况。
如果你在面试或系统设计里被问到"RAG 召回怎么做",能讲清楚单路召回和混合召回的区别,说明你真的理解 RAG 的痛点,而不只是会调 API。
3.2 多路召回:向量 + BM25 + 结构化过滤
生产级 RAG 的标配是混合检索(Hybrid RAG),至少两条路并行:向量检索负责语义候选,BM25 负责精确和关键词候选。召回后进行分数融合,常用的是 RRF(Reciprocal Rank Fusion)。RRF 只依赖排名不依赖分数绝对值,能很好地规避不同检索方式分数分布不一致的问题。
我当时按下面这个流程落地,效果稳定:
- 用户问题经过查询改写后,同时发起向量检索 top_k=20 和 BM25 关键词检索 top_k=20。
- 根据元数据过滤条件(部门、日期、文档类型、权限范围)对候选进行裁剪。
- 用 RRF 融合两路候选,取前 10 条进入重排阶段。
- 重排后取前 5 条作为最终上下文。
RRF 的伪代码很简单:给每条候选按排名打分,命中多路检索的候选天然排名靠前。这个方案不需要模型训练,只要引入一个关键词检索服务就能实施。实测下来,在之前那个报销场景里,正确答案的排名从第 18 位提升到了第 4 位,再经过重排直接进入前 2。
有一个容易被忽略的点是查询改写。用户问题往往口语化、指代不完整,直接把"它什么时候能批下来"丢给检索系统显然不行。Agent 层要做一步 query rewrite:结合对话历史把它改写成"差旅报销申请单审批时间多久"再去查,这步对召回率的影响甚至比换一个更强的 Embedding 模型还大。
3.3 Rerank 在什么环节介入最合理
Embedding 模型是双塔结构,问题和文档各自编码,用向量相似度衡量相关性,计算快但精度有限;Rerank 模型通常是交叉编码结构,问题和文档拼接后进入模型打分,精度更高,但计算代价也高,不能对全库做,只能对第一轮召回的小批量候选做。这就是 Rerank 必须放在召回之后的原因。
我在生产系统里的标准链路是:召回候选 20 条,Rerank 打分层只保留 5 条。延迟上,单次 Rerank 调用的耗时通常在几十到几百毫秒,取决于候选数量和模型大小。这个代价换来的收益非常明显——RAG 问题的答案质量,很大程度取决于喂给大模型的上下文排序是否正确,前 5 条只要错 1 条,幻觉风险就在上升。
有个细节要提醒:Rerank 的输入长度是有限的,如果某个候选文本太长,建议截断到模型窗口内,否则会被静默丢弃。政务和企业文档经常出现一个条款下面挂着大段解释的情况,直接整段送给 Rerank,前面 512 个 token 是背景,真正的关键句在后面,就会漏排。我一般会按句切分后保留关键句前缀,或者直接用子块去重排,再把命中的子块映射回父块。
4. Agent 编排层:从"一次问答"到"自主任务"的关键设计
4.1 记忆机制:多轮对话中的上下文管理
当项目从单轮问答延伸到多轮对话,记忆模块就是 Agent 和普通 RAG 接口的分水岭。用户在对话里可能会说"再详细一点""上个问题的第二点是什么""换成华东区的数据重新分析一下"——这些指代如果不结合历史,根本无法检索。
记忆机制我按三层设计:会话级短期记忆(最近 N 轮对话原文)、任务级工作记忆(当前目标、已获取的检索片段、已调用的工具结果)、业务级长期记忆(用户偏好、常用口径、组织架构等相对稳定的信息)。短期记忆直接喂给大模型;任务级记忆由 Agent 自己维护;长期记忆存在数据库中,按用户或业务维度读取。
在设计时需要注意长度消耗。对话历史全部塞进上下文,很快就会把窗口占满。我的做法是:对早期对话做摘要,只保留用户关键诉求;对检索片段启用"引用压缩",取与当前问题相关的子块而不是把整个父块重复传入。Agent 框架里常见的 Memory 模块,本质做也是这件事,但不同框架的默认策略差异很大,自研时要清楚"哪些记忆必须保留,哪些可以丢弃"。
4.2 工具调用与路由:什么时候直接回答,什么时候查文档
Agent 的核心不是"能调工具",而是"决定调不调"。生产环境里我给 Agent 注册了四个工具:知识库检索(RAG)、业务数据库查询、指标计算器、工单状态查询。LLM 需要先做意图路由——直接回答类问题(闲聊、常识)就不进 RAG;需要内部数据的问题走工具调用;既涉及文档又涉及数据的,才进入多跳调用。
实现上我做了一个函数式工具注册表,每个工具暴露明确的参数 schema 和描述信息,LLM 根据用户的自然语言生成结构化调用参数。工具返回后,Agent 会把结果聚合再生成最终回答。这看起来很简单,但实际运行时需要防几个坑:工具调用失败要能让 Agent 感知并及时换个思路,而不是死循环;工具的返回结果要先经过格式校验再送入上下文;连续多次调用要设置最大步数,防止 Agent 在错误路线上反复横跳。
从 RAG 到 Agentic RAG 的升级点也在这里:传统 RAG 一次检索一次回答就结束;Agentic RAG 允许 Agent 根据检索结果主动改写查询、发现上下文不足后补充检索、甚至做多跳推理。比如用户问"对比 Q1 和 Q2 的销售差异并解释原因",Agent 要先检索销售报表,发现原因部分没有命中,再检索一次市场活动文档,最后综合两部分输出。这种能力不是"用框架自动获得"的,而是靠清晰的数据结构和检索反馈机制撑起来的。
4.3 Agent 框架选型:自研还是用 Dify 这类工具
"dify 完成政务 rag 知识库的实践项目"这种热搜能上榜,说明低代码/平台化 Agent 搭建方案在真实项目中用得越来越普遍。选型时没有标准答案,但有一个很实际的决策模型:如果业务场景相对固定、交付周期紧、团队没有太多工程化人力,用 Dify 这类平台能快速完成"文档导入、流程编排、API 发布",政务知识库这类场景尤其适合,因为大量需求都是"合规问答 + 权限管理 + 可追溯",不需要做太复杂的 Agent 推理。
但如果你要做的是高度定制化 RAG Agent——比如底层数据源是多个异构系统、检索逻辑依赖复杂规则、需要对每一步做细粒度观测,那纯低代码平台会变成瓶颈。平台抽象的节点越高级,你对底层细节的控制力越弱,出了问题也越难排查。我的建议是:迭代速度优先用平台跑通,当平台开始限制你时,再以平台的 API 为边界做替换或局部自研。
当时我们最终选择了自研编排层,理由很直接:需要和内部权限系统的深度集成,且要对检索质量做持续细粒度调优。但在早期 prototyp 阶段也用了不少现成组件,这样能快速验证思路。
5. 评测、调优与上线:别等上线了才发现问题
5.1 从你自己的数据里挖评测集
没有评测集的 RAG 项目,就像没有回归测试的代码库。你会陷入"感觉改了效果变好""又感觉上一个版本更好"的死循环。第一个评测集不需要很多,但必须真实。我从三个来源构建了 golden set:线上平台沉淀的真实用户问题、运维同事整理的常见咨询问题、每个文档里自然存在的"条款型 QA"。
每一条评测数据要包含三部分:用户问题、期望命中的文档块或来源、回答中必须包含的要点。不需要急着对每一条写标准答案,而是先能回答"这个问题应该引用哪几个来源"。召回评估对"来源可定位性"的要求很高,政务和制度类文档尤其如此——用户要的不是一句"可以报销",而是这句话的依据在哪。
评估指标里最值得关注的是:召回命中率(正确答案是否出现在召回前 5 中)、忠实度(回答是否完全基于检索内容,有没有添加文档中没有的信息)、拒答准确率(该回答不知道的时候是否诚实拒绝)。我见过很多 RAG 项目为了"用户体验"强制让模型每问必答,结果制造了大量幻觉。对于知识库场景,懂得拒答是重要的能力指标。
5.2 延迟、召回率与幻觉的工程取舍
生产系统不是所有指标无限优化,而是在约束下求均衡。我当时的延迟预算大概是:端到端回答控制在 5 秒内,其中检索 500ms 以内,Rerank 200ms 以内,大模型生成占大头。为了让 Rerank 不超时,候选数量压到 20 条以内;为了让大模型生成稳定,上下文控制在 2000 token 左右,超过的部分宁可截掉只保留重排前 5,也不要把所有候选都灌进去。
这个取舍牺牲了一点理论上的"全量上下文覆盖",换来了两个好处:一是幻觉显著下降,因为模型只看到高置信度的片段,不会涣散;二是成本下降,token 用量少,用户等待时间短。我认为在生产环境,"准确但简短"永远好过"全量但容易翻车"。
另一个必不可少的环节是安全和权限过滤。知识库里的文档往往有密级,Agent 检索结果必须在召回阶段就按用户权限过滤,而不能等生成后再筛,否则权限信息会泄露进模型上下文。政务和企业场景对这一点要求极高,建议把权限过滤前置到元数据层,并做访问审计。
5.3 上线后监控什么指标
上线不是终点,而是评测的起点。我建立了一个最小监控面板,包括:检索命中率(通过日志判断人工反馈是否满意)、Rerank 后首位准确率、端到端延迟分位数、幻觉投诉数、每轮请求的 token 成本、用户点赞点踩率。
日志回放机制是做 RAG 最容易忽略但最有价值的部分。每个用户反馈"回答不对"的请求,我都把日志完整保存下来,包括当时的检索候选、重排顺序、模型输入输出,然后定期把 bad case 重放到评测集里做回归。这套机制跑了一个月后,评测集从 20 条涨到了 200 多条,系统质量才真正稳定下来。你会发现,真正让 RAG Agent 变好的,不是换更强的模型,而是持续不断地用真实数据修正问题。
关于 prompt 防护,我还要额外提一句。知识库类 Agent 上线后,一定会遇到用户尝试让模型"忘掉之前的指令""直接输出原始文档"甚至试图注入恶意提示词。生产级系统要加入基础防护:检测明显的提示注入模式、分离指令与数据上下文、对系统指令做不可被用户输入覆写的隔离。
最后分享一个小经验:把数据管线和评测集做扎实,是 RAG Agent 项目里投资回报率最高的事。模型、框架可以随时换,但高质量的数据底座和评测闭环,才是让系统长期可信的根本。如果你现在正准备启动类似项目,别急着研究最新 Agent 框架,先拿 100 条真实问题把检索和评测跑通,你会回来感谢这个决定。