走进 AI Agent 第四篇:知识获取管道——RAG 基础
如果你跟着这个系列一路看下来,前面几篇我们聊了 Agent 的核心循环、工具调用、记忆机制,那这篇就该聊聊一个几乎绕不开的问题:Agent 的知识从哪来?
很多人一开始的直觉是,大模型本身已经记住了海量知识,直接问它就行。但真到落地场景就会发现,模型的知识是“公共的”,它不知道你公司内部的流程、你产品的具体参数、你私有文档里的规则。这时候 RAG(检索增强生成)就派上用场了。RAG 不是让模型“背”你的文档,而是给它一个“查资料”的能力:用户提问时,先从知识库里检索出相关内容,再把这些内容连同原始问题一起交给模型,让它基于检索到的材料来回答。
我自己的理解是,如果把 Agent 比作一个刚入职的员工,大模型是这个人的“脑子”,工具调用是“手”,记忆是“笔记本”,那 RAG 就是“资料室”。脑子再聪明,也不可能把公司所有规章制度都背下来,遇到不清楚的事情,知道去哪里查、怎么查、查到之后怎么用,这才是靠谱员工的正确打开方式。
这篇文章我打算把 RAG 这条知识获取管道拆开揉碎讲清楚,包括它解决什么问题、整体流程怎么走、每一步的关键点在哪里、常见坑有哪些,最后再给一套可以直接上手的菜谱式方案。无论你是刚接触 Agent 开发的新手,还是已经写过几个 demo 想往深了做的开发者,这篇内容应该都能让你少踩几个坑。
1. 为什么要 RAG:大模型的知识边界与 Agent 的真实需求
1.1 大模型的“知识截止”与“记忆幻觉”
先看一个最朴素的场景。你问一个训练数据截止到 2024 年的模型:“我们公司今年新发布的 X 系列产品支持哪些接口?”模型大概率会一本正经地编一个答案。这不能全怪模型,它的知识来源是训练语料,训练语料里根本没有你们公司今年新产品的信息。让它凭空回答,它只能根据“类似产品通常有哪些接口”这个规律去推断,推断出来的东西看着像模像样,实际上全是用幻觉填的。
这种幻觉在 Agent 任务里尤其致命。Agent 不是聊天玩具,它要做的是基于信息做出判断、调用工具、执行操作。如果第一步拿到的知识就是错的,后面的每一步都会在错误的基础上叠加错误。我之前见过一个客服机器人,用户问退货政策,它振振有词地说“支持七天无理由退货”,实际上那家店的定制类商品根本不支持退货。这种错误一次就能把用户信任败光。
所以 RAG 第一个要解决的问题,就是把“模型已知的知识”和“需要实时获取的知识”分开。模型负责推理和表达,知识库负责提供事实依据,两边各司其职。
1.2 为什么微调不是万能解
说到私有知识接入,有人会想到微调。确实,微调可以把特定领域的数据“教”给模型,但它有几个天然的短板:
- 更新成本高:每次知识变化都要重新训练或至少重新跑一轮 LoRA,一天改三次文档的团队根本玩不起。
- 数据量要求高:微调适合的是“行为对齐”和“风格迁移”,比如让模型学会用某种语气说话。你要靠微调塞进去几百个业务细节,效果通常不稳定,反而可能污染原有能力。
- 不可追溯:微调之后,模型记住了什么、忘记什么,你很难查。出了问题也不知道是哪条训练样本导致的。
RAG 正好补齐这些短板。知识更新只需要改向量库里的文档,源头可追溯,每次回答都能指出“我根据哪篇文档得出这个结论”。从 Agent 工程的角度说,可追溯性极其重要——你连事实来源都说不清的时候,根本不敢让 Agent 做有实际影响的操作。
1.3 Agent 场景下 RAG 的特殊性
很多人对 RAG 的理解停留在“问答机器人”层面,但在 Agent 场景里,RAG 承担的任务更复杂。Agent 的规划循环里,检索可能是多轮的:先检索“用户说的问题是什么”,再检索“解决这个问题需要哪些策略”,还可能检索“调用某个函数时参数应该怎么填”。每一次工具调用前,都可能在跟知识库打交道。
更关键的是,Agent 经常需要多步推理和组合多个知识片段。比如用户问“帮我找出所有库存低于安全阈值的商品,并生成补货建议”,Agent 需要先从 ERP 里检索库存数据(这部分可能是结构化查询,不是文档检索),再检索补货策略文档,然后把两者组合起来生成回答。这已经不是传统 RAG 一篇文档对应一个答案的简单模式,而是“多路检索 + 综合推理”的复合管道。
所以我把 RAG 称作“知识获取管道”,而不是“文档问答插件”,就是想强调它在 Agent 体系里的地位——它是 Agent 获取外部世界信息的主干道,而不是可有可无的附件。
2. RAG 整体架构拆解:从文档到答案的完整管道
2.1 标准 RAG 的五段式流程
一个标准的 RAG 管道,核心是五个环节:加载、切分、嵌入、检索、生成。我先用一个表格把这五段的输入输出和核心任务梳理清楚,然后逐段展开讲。
| 环节 | 输入 | 输出 | 核心任务 |
|---|---|---|---|
| 加载 | 原始文件(pdf/docx/md/数据库记录) | 纯文本数据 | 把各种格式的内容统一提取为文本 |
| 切分 | 长文本 | 多个文本块(chunk) | 按语义和结构把文本切成适合检索的片段 |
| 嵌入 | 文本块 | 向量 | 把文本映射到高维向量空间,便于相似度计算 |
| 检索 | 用户问题 | 相关文本块列表 | 找到与问题语义最匹配的若干片段 |
| 生成 | 问题+检索结果 | 最终答案 | 让模型基于检索结果组织回答 |
这五段看起来简单,实际每段都有讲究。很多初学者直接调库一把梭,跑通了就完事,结果精度一塌糊涂。问题基本都出在切分和检索这两个环节——这俩恰恰是看起来最容易、实际最深的部分。
2.2 以“查资料”作类比理解管道
为了更直观一点,可以把 RAG 管道类比成一个人去资料室查资料的完整过程。
你接到了一个任务:“公司今年差旅报销标准是什么?”你不可能把整个资料室的所有文件都看一遍,你需要做几步:找对书架(加载和路由)、翻到对的章节(切分和定位)、快速扫一眼判断是否相关(检索)、确认内容没问题后整理成回答(生成和校验)。
如果书架上的书全部堆成一坨没有分章,你翻起来就很累——这就是切分不好的后果。如果图书分类混乱,你去了文学区找财务制度——这就是检索召回质量差的后果。每一步都影响最终答案的质量,没有任何一步可以糊弄。
2.3 检索与生成之间的“信任边界”
这里我想重点说一个概念:RAG 的生成阶段不是把检索结果直接抄一遍,而是把检索结果当作“证据”来用。模型要根据问题来选择性的引用、推理、总结,而不是对着检索文本做摘要。
这意味着两件事。第一,检索结果的质量直接决定生成质量的上限,如果检索到的片段本身就不相关,再强的模型也救不回来。业界有个说法叫“垃圾进,垃圾出”,在 RAG 里体现得特别明显。第二,生成阶段要做“事实核查”,模型如果发现检索结果互相矛盾,应当指出矛盾而不是强行缝合。这一点在 Agent 场景里格外重要,因为 Agent 的回答可能会被后续流程直接消费。
我还想提醒一点:RAG 不是把知识库塞进模型,而是把知识库放在模型旁边,让模型按需取用。这个思维转换是很多人的坎——你不需要教模型“知道”什么,你只需要保证它“查得到”和“用得上”。
3. 一步步搭一个可用 RAG:文档处理与切分策略详解
3.1 文档加载:格式适配与内容清洗
RAG 的第一步是文档加载。你以为加载就是把 pdf 读出来?真做起来一堆事。
最常见的情况是这几种:
- PDF 文件:可能是文本型 PDF,也可能是扫描件。文本型 PDF 可以直接提取,扫描件需要 OCR。千万别忽略这个区别,我见过有人拿扫描版合同去跑 RAG,检索出来全是乱码,问题就出在没做 OCR。
- Word/PPT:用库提取文本时,注意表格内容的处理。表格被拍平成一串文本后,结构信息会丢,跨行跨列的语义关系直接消失。我的做法是尽量把表格区域单独识别,转换成 Markdown 表格格式,保留结构。
- 网页:需要先去除 HTML 标签、导航栏、广告等噪音。别小看这一步,一个新闻页面抓下来可能有一半是“相关推荐”和页脚信息,不清理干净,检索时全给你召回来。
- 数据库/API 导出的结构化数据:这类数据本身是字段化的,建议优先考虑查数据库而不是向量检索。结构化查询能精确满足条件,向量检索反而可能模糊匹配到一堆不相关的东西。
加载完成之后,还需要做一轮清洗。我的习惯是:去掉多余空行和特殊符号,统一换行符,修正 OCR 识别的常见错字。清洗这一步很多人跳过,但它直接影响切分质量。文本里有大量乱码和无关字符时,切分出来的 chunk 很多都是无效的,检索召回率会明显下降。
3.2 切分粒度:为什么“一刀切”最省事也最差劲
文本加载完就是切分。这是新手最容易踩坑的地方。很多人图省事,直接按固定字符数切,比如每 500 字一段,相邻重叠 50 字。这样切出来的 chunk 非常机械,经常把一句话拦腰截断,或者把一个完整段落拆到两段里。
切分的目标是让每个 chunk 在语义上尽量完整和自包含。一个段落讲一个完整观点,那它就是天然的一个 chunk;一个表格自成一个语义单元,那也按表格切。固定长度切分的唯一优点是没有思考成本,缺点则是全面牺牲检索质量。
我给一个实用的优先级排序:
- 优先按结构切分:Markdown 标题、章节号、段落空行是最天然的分隔符。把每个二级或三级标题下的内容切成一个 chunk,通常效果就很好。
- 结构化内容特殊处理:表格、列表、代码块尽量单独成 chunk,不要跟上下文混在一起。
- 不得已才用固定长度:如果文档没有明显结构(比如对话记录),再退而求其次按字数切分,但要设置“软边界”——尽量让切分点落在段落边界或者句子边界,而不是字符边界中间。
补充一个切分重叠的问题。为什么要有重叠?因为两个相邻 chunk 的语义往往有关联,比如一个 chunk 的最后一句和下一个 chunk 的第一句共同表达了一个完整意思。重叠部分可以减少信息断裂。但重叠不是越多越好,重叠太多会引入大量重复内容,检索时会召回好几个几乎一样的 chunk,浪费上下文窗口。我常用的策略是重叠长度控制在 chunk 长度的 10%~15%。
3.3 不同文档类型的切分实战建议
这里分不同文档类型说一下我的实操经验:
- 技术文档/帮助手册:结构化程度最高,按标题层级切分效果极好。如果文档有目录,甚至可以基于目录自动生成层级结构,检索时先命中章节目录再定位内容。
- 论文/研究报告:需要保留章节信息(摘要、引言、方法、结论)。这类文档切分时建议把引用信息一并保留,方便追溯来源。
- 聊天记录/客服工单:这类文本口语化严重,切分粒度要小一些,因为单条消息往往就是完整意图。按消息条数切,而不是按字符数切。
- 混合文档:包含正文、表格、图片说明的综合性文档,建议先用一个轻量级的版面分析工具把不同区块识别出来,再按区块类型分别处理。这一步看起来重,但对质量提升非常明显。
切分没有绝对正确的参数,只有适不适合你的数据和场景。我的习惯是:先用一到两篇代表性文档手动模拟切分,看看切出来的 chunk 语义是否完整,再确定是否有用户需求对应的整段语义,最后调整切分策略和参数。批量跑之前,一定要人眼验证一批样本,别直接拿全部数据去跑。
4. 嵌入、检索与生成:让知识真正被“查到”和“用上”
4.1 嵌入模型选型与向量的门道
切好 chunk 之后,要做的就是把每一段文本变成向量。这一步依赖嵌入模型。选嵌入模型最核心的考量是:它得懂你的语言和领域。
如果你处理的是中文文档,优先考虑中文语料表现好的嵌入模型。我之前试过直接用通用英文嵌入模型处理中文,检索出来的结果明显不靠谱,因为模型对中文语义的理解深度不够。后来换成了针对中文优化的模型,效果立刻上了一个台阶。
向量两个容易被忽略的工程细节:
- 维度:高维向量(比如 3072 维)表达能力强,但占用存储多、检索计算慢;低维向量(比如 384 维)速度快,但可能丢失细粒度语义。一般的中小型知识库用 768 或 1024 维是个比较平衡的选择。
- 归一化:计算余弦相似度时,如果向量没有归一化,长度信息会影响相似度分数。建议在入库前统一做归一化,检索时直接用点积即可。
很多人会忽略一个“坑”:不要把不同类型的文本混在一个向量库索引里。比如产品说明书和客服对话,它们的语义空间差别很大,混在一起检索时容易出现“想查产品参数,却召回了客服吐槽”的尴尬情况。策略上可以按文档类型建多个集合,或者给向量带上元数据标签,在检索时通过元数据过滤控制检索范围。
4.2 检索不是“查一下”那么简单
嵌入完成后,用户提问来了。系统先把问题也转成向量,然后在向量库里做相似度搜索,召回 Top K 个最相关的 chunk。这一步听上去简单,实际有几个决定成败的细节。
首先是检索范围。直接全库检索最大的问题是“语义相似但语境不同”的匹配错误。比如技术在问“获取用户信息的接口”,文档里有一处提到“用户信息获取失败时的错误处理”,语义上很像,但不相关。可以通过增加元数据过滤器来解决:限定检索范围到“接口文档”这个集合,再去做相似度搜索,干扰会少很多。
其次是重排(Rerank)。初检可能召回了 20 个候选,但真正跟问题强相关的可能只有三五个。这时候可以加一个重排环节,用一个专门的交叉编码器模型对候选内容重新打分,把最相关的往前提。重排模型的质量直接影响最终答案的精度,这块值得专门花时间调。
还有一个容易被忽略的点:检索不到内容时的降级策略。用户的问题如果跟知识库完全不相关,检索回来的结果都是低分片段。这时候要设置一个相似度阈值,低于阈值的直接判为“无结果”,走兜底话术,而不是硬塞几个低相关片段让模型强行编答案。我在生产环境里吃过这个亏,阈值不设,什么弱相关问题都能触发一堆乱七八糟的召回,然后模型基于这些噪音硬答,效果极差。
4.3 生成阶段的“拼图”技巧
检索完成,生成环节就是把用户问题、检索到的 chunk、Agent 的历史记忆拼接成最终的 prompt。这里也有一些技巧。
我的做法是把检索内容编排成带编号的“参考资料”区,并在 prompt 里强调模型必须优先依据参考资料回答,同时标注信息来源索引。这样一来,模型在组织语言时就有据可依,也能减少把“自己知道但资料里没有”的内容混进去。
具体到 prompt 设计,我沉淀了一个最小可用模板:
请根据以下参考资料回答用户问题。 参考资料: <doc id="1"> (检索到的第一段内容) </doc> <doc id="2"> (检索到的第二段内容) </doc> 要求: 1. 优先使用参考资料回答,不要在资料之外补充不确定的信息。 2. 回答时标注信息来源,例如内容后加 [来源: 文档名称]。 3. 如果参考资料不足以回答问题,只能根据你的常识回答,请先说明“以下内容为常识补充”。 4. 回答要简洁、准确,不要复述参考资料全文。为什么这样写?因为模型面对“一份参考材料”时,它容易把材料当成背景信息,然后自由发挥。一旦把材料明确定义为“证据”,它的行为模式会立刻变得更严谨。这是我实测多个模型后非常有效的一个技巧。
还有一个细节:检索结果太多时反而会坏事。上下文窗口有限,塞进去 5 段不相关的内容,可能把真正有用的 1 段淹没掉。所以生成前最好先做一次截断或筛选,只保留重排后分数最高的 3~5 段。信息贵精不贵多。
5. 进阶:从 RAG 到 Agentic RAG 的实践路径
5.1 传统 RAG 在 Agent 里的局限
标准 RAG 做问答没问题,但如果把它嵌入 Agent 的循环,很快就发现三个尴尬的地方。
第一,检索粒度跟不上多步任务。Agent 要回答“按产品类型汇总本月销售额”,它需要的不是一篇文章,而是多篇文章里分散的多个段落,甚至需要多次检索来逐步收窄信息范围。标准 RAG 是“一次检索定终身”,根本撑不住这种多跳查询。
第二,Agent 需要与外部工具联动。比如 Agent 要先查数据库得到一份清单,再检索这几项对应的补充说明文档,最后生成汇总。这意味着检索和工具调用要交替进行,标准 RAG 完全没有这种编排能力。
第三,标准 RAG 不知道“什么时候该检索”。有些简单问题模型自己就能答,非要检索反而引入噪音;有些复杂问题模型答不了,却不知道去查。这就是“路由”问题——是否需要检索、检索谁、检索结果要不要二次确认。
5.2 Agentic RAG 的核心:把检索变成工具
解决上面三个局限的思路,就是让 RAG 从“一次性管道”进化成“Agent 可调用的工具”。我把这称为Agentic RAG,本质上是把检索、重排、追问、多路召回这些能力封装成 Agent 可以编排的工具函数。
举个例子。Agent 接到任务“总结目前公司对账务处理相关的风险控制要求”。它先判断这个问题很泛,于是调用“全局检索”工具,先获取风险控制相关文档的整体目录。然后它发现文档里提到了“对账”“审核”“授权”三个子主题,于是再调用“定向检索”工具,分别检索三个子主题的详细内容。最后它还发现有一份最新制度文档的摘要页没有索引,于是通过工具直接打开原始文档获取页码信息。整个过程检索了三轮,每一轮都基于上一轮的结果做出下一步决策——这才是 Agent 该有的检索方式。
所以 Agentic RAG 不是一个“更高级的 RAG”,而是一种设计思路:把知识获取从“固定的管道”变成“可编排的能力”。检索工具像其他工具一样,可以由 Agent 根据任务目标动态决定调用时机、调用参数和调用次数。这样一来,Agent 对知识的获取能力就从“配了一个资料管理员”升级成“配了一个会自己淘资料的研究助理”。
5.3 落地时的三个脚手架
要让 Agentic RAG 真正跑起来,我建议先把下面三件事做扎实,再考虑上多轮检索和编排。
- 检索工具的参数要暴露清楚:至少支持传入检索关键词、筛选条件、返回条数。Agent 才能根据任务动态调整检索策略。如果检索工具只支持固定参数,Agent 的灵活度就废了一半。
- 要有可靠的检索质量信号:返回结果时把相似度分数一并透出,Agent 在规划时可以用这个分数判断结果可信度,分数过低时可以触发“重新检索”或“换关键词”。这个信号是 Agent 保证质量的最后一道防线。
- 预留人类确认的环节:在 Agent 可能基于不完整知识执行高风险操作时,给它一个“请求人类确认”的工具。例如涉及删除数据、发对外通知、做资金操作的时候,宁可让 Agent 先停下来确认。这个环节在传统 RAG 里没有存在感,但在 Agent 主导流程时,它是安全底线。
我自己的体感是,传统 RAG 能解决“80% 场景里 80% 的问题”,剩下的那 20%,就是 Agentic RAG 的用武之地。但从传统 RAG 走到 Agentic RAG,不是把管道改一下就行,更多是思维方式的转变——你要开始把“检索”当成一个可以被规划的行为,而不是一段固定的数据流。
6. 常见问题与排查技巧实录
最后这部分,把我在实际开发、部署、调优过程中踩过的坑、查到的问题整理成一张速查表,每条都附上排查思路和解决办法,你可以直接把表格存下来当备忘录用。
| 问题现象 | 可能原因 | 排查方向与解决办法 |
|---|---|---|
| 答案与文档内容不符,甚至反向胡说 | 检索召回了不相关 chunk 或生成阶段没有强约束 | 先检查重排后的 Top 1 是否相关;再确认 prompt 里是否明确“以参考资料为准”;最后检查相似度阈值是否过低 |
| 明明文档里有答案,但检索不到 | 切分粒度不合理或嵌入模型不匹配 | 优先按结构切分,chunk 太大容易淹没关键信息,太小容易丢上下文;换一个领域相关的嵌入模型对比指标 |
| 检索结果大量重复 | 切分重叠过大或向量归一化不一致 | 重叠比例控制在 10%~15%;入库前统一做向量归一化 |
| 检索速度很慢 | 向量库索引类型不合适或数据量过大 | 确认是否使用 HNSW/IVF 等近似最近邻索引;按文档类型拆分集合,减少单次检索范围 |
| Agent 生成长篇回答但关键信息很少 | 塞给模型的检索片段太多 | 限制输入片段数量,只保留重排后分数最高的 3~5 段;让模型输出结构化的要点而不是长篇大论 |
| 更新文档后回答没变化 | 向量库没有增量更新机制 | 检查管道是否在文档变更后自动重跑嵌入;建议对新增文档做增量入库,删除旧版本对应的向量 |
| 中文语义检索效果明显差于英文 | 嵌入模型对中文支持弱 | 换成中文语料优化的嵌入模型;必要时用同义词扩充技术增强召回 |
| 相似度分数整体偏高但没有实际意义 | 文本太短导致向量表达单一 | 对短文本做“查询扩展”或结合关键词检索做混合召回(BM25 与向量检索融合) |
| 回答中引用了不存在的来源 | 生成阶段对来源标注约束不足 | prompt 里明确要求“只标注参考资料中出现的文档 id”;生成后做一次简单的来源校验 |
我再单独聊一个我反复遇到的问题:切分粒度到底怎么定。很多人一开始都恨不得把每 200 字切成一块,觉得这样“更精准”。实测下来,太小的 chunk 缺乏上下文,检索时单个 chunk 的语义可能都不完整,模型看了也拼不出完整答案。后来我的经验法则很简单:chunk 至少要能独立回答一个“什么/为什么/怎么办”级别的提问,不够就往上合并到完整段落或小节。你把这句话刻在脑子里,切分策略基本不会跑偏。
还有一个所有新人都会踩的坑:向量库里的“脏数据”。文档更新时,旧版本向量没删,新版本向量也加进去了,结果同一主题检索出来两套互相矛盾的答案。我的做法是给每个 chunk 打上文档版本号和时间戳的元数据,更新文档时先按版本号删除旧向量,再写入新向量。这个习惯养成之后,省了无数排查“为什么答案不对”的时间。
7. 最后想说的几句实在话
写到这里,我估计你已经对 RAG 这条知识获取管道有了一个比较立体的认识。最后分享一点个人体会。
RAG 这个领域看起来很“成熟”,网上教程铺天盖地,照着跑 demo 特别快。但 demo 和生产之间隔着一整条河。几个月前我刚做一个知识库项目时也以为三步五步就搞定了,实际在切分策略、重排阈值、混合检索上调试了整整两周,才把指标从“勉强能用”拉到“基本可靠”。这中间没有什么魔法,就是一步步试、一步步对比。
我个人最深的感触是:RAG 的难点不在技术,而在“理解你的数据”。同样一篇文档,在不同业务场景里需要被检索的角度完全不同。你把数据理解透了,切分、嵌入、检索的所有决策都会变得顺理成章;数据理解不透,调参调破天也没用。所以如果你准备上手一个 RAG 项目,我建议流程是这样的:花最多的时间去读你的文档、梳理你的业务问题,然后再开始写代码。
最后再送一个小技巧:刚搭好 RAG 管道时,不要去跑那些复杂的测试问题,先从最简单、最核心的业务问题开始问。比如“我们公司的退款周期是几天”“XX 功能的开关怎么配置”,看看管道能不能答对。这些问题越简单越好,因为它们的答案是确定的,方便验证检索链路每一步的输出是否正确。链路通透了,再慢慢加大问题复杂度。这个由简到繁的验证路径,能帮你快速定位问题是出在加载、切分、嵌入、检索还是生成,而不是对着一个复杂问题瞎猜。
下一篇我准备聊聊多 Agent 协作的编排模式,以及在这套体系里知识管道如何被多个 Agent 共享和复用。如果你手头也正在做 Agent 项目,欢迎留言说说你遇到的具体问题,我针对性地写写。