人这一整年有一个体会越来越深:做AI Agent,真正拉开差距的不是模型选得多强、不是Agent框架用得有多花,而是它能不能在关键时刻拿到它该知道的那些知识。模型自带的知识是死的,有截止日期、有偏见、还会一本正经地胡编;Agent要落地到具体业务里,必须有一套自己的“知识获取管道”,把外部资料变成它随时能查、能引用、能作为决策依据的东西。这条管道最基础、也是最成熟的做法,就是RAG(Retrieval-Augmented Generation,检索增强生成)。
这篇是这个系列的第四篇,前面几篇我们把Agent的“思考”和“行动”都聊过了,这篇专门补上“记忆”和“知识”的短板。无论你是刚从零开始搭Agent,还是已经在写业务代码,这篇的定位都是把RAG这条路讲透——它不是让你跑通一个Demo就行,而是帮你理解为什么RAG能解决Agent的知识问题、它在工程上到底怎么落地、以及哪些环节最容易被忽视。
1. 为什么Agent必须有自己的“知识获取管道”
1.1 模型脑子里的知识,本质上是一份“压缩过且过期了”的百科
先从一个反直觉的事实讲起:你问任何一个大模型“今天天气怎么样”,它如果没接工具,它就答不上来;你问它“我们公司上季度的销售数据是多少”,它大概率会顺口编一个。原因很简单——模型训练好之后,它的知识就冻结了。它的知识来自训练语料,是那批语料经过数千亿参数压缩后的结果。它知道“唐朝建立于618年”这种通识,但不知道你昨天新加的文档里写了什么。
这个“冻结”特性带出两个致命问题:
- 时效性差:训练截止时间之后的事,它一概不知道。就算模型厂商每月更新,也追不上业务里每天都在变的数据。
- 可靠性差:它不是“查”知识,而是“猜”知识。你问一个冷门问题,它可能把相似问题的答案混在一起说出来,而你根本分辨不出来。
所以,想让Agent处理真实业务,你不可能指望它脑子里那点“百科知识”。你需要一条管道,把外部资料在需要的时刻取回来,喂给模型,让它“看着资料说话”。这就是知识获取管道存在的根本原因。
1.2 RAG在整个Agent体系里到底扮演什么角色
前面几篇聊过,Agent的核心能力是“思考”和“行动”。但请大家扪心自问:一套系统里,如果思考没有可靠的信息输入,思考质量能有多高?这就像一个分析师,脑子再快,如果手上的报表全是假的、过期的,他分析出来的结论你敢信吗?RAG解决的问题,恰恰就是“让Agent的判断建立在真实、新鲜的资料之上”。
在Agent体系里,RAG通常被归入记忆模块下的外部知识这一层。它与Agent内部记忆的区别在于:
| 维度 | Agent内部记忆(对话上下文) | RAG外部知识 |
|---|---|---|
| 来源 | 用户跟Agent的对话历史 | 文档、数据库、API、知识库 |
| 生命周期 | 短对话会话内有效 | 独立于对话,长期有效 |
| 存储形式 | 消息列表、token序列 | 向量库、倒排索引、关系库 |
| 获取方式 | 直接取最近N轮 | 按需从大规模资料中检索 |
放在一个具体例子里就更清楚了。你做一个客服Agent,用户问“订单退款的截止时间是多久”。如果Agent只靠内部记忆,它只能根据刚才聊的几句猜;如果配上RAG,它会去公司的退款政策文档里检索相关条款,然后基于条款原文回答。同样一个Agent,前者是“蒙”,后者是“有据可查”,业务方要哪一个,不言而喻。
1.3 搞懂RAG,是通往Agentic RAG和更复杂架构的必经之路
最近“Agentic RAG”这个词很热,很多人一上来就研究怎么让Agent自主决定查哪个库、怎么拆解复杂问题、怎么迭代检索。但我个人的观点是:没把基础RAG的每个环节吃透之前,谈Agentic RAG就是空中楼阁。这一类进阶架构本质上是在基础RAG的管道上增加了“决策层”——Agent来决定检索哪些源、检索几轮、如何评估检索结果够不够。
所以这一篇把最基础的那条管道讲扎实。下一篇再谈怎么再把“管道”升级成“Agent自主检索”。
2. 一次性看懂RAG核心流程:索引、检索、生成三段拆解
2.1 整条管道的全景图先刻在脑子里
RAG听起来玄乎,但骨架极其简单,就三步:先把你有的资料准备好(索引)→ 用户问问题时把相关片段捞出来(检索)→ 让模型看着片段回答(生成)。
用一个比喻帮助记忆:RAG就像你去图书馆借书。你先把全馆的书按科目编好号放上书架(索引);有人来咨询问题时,你根据问题去书架查,找到几本最相关的书(检索);然后把书翻到对应章节,摘出关键段落给咨询者组织成答案(生成)。这一整套下来,就是一套完整的知识获取管道。
需要提前说清楚的是:索引是离线的,检索和生成是在线的。索引阶段,你跑一次批量任务,把几万份文档切块、编码、存入向量库,这个流程不要求实时;检索和生成则必须发生在用户提问那一刻,对性能要求极高。我在实际项目里见过不少新手把这两个阶段混在一起,结果每次新增文档都实时处理一遍,导致系统慢得不堪忍受——这个问题下文还会细说。
2.2 索引阶段:从一堆原始文档到“可检索的库”
索引阶段做的事情是把无结构的原始文档(PDF、Word、Markdown、数据库记录等)变成结构化的、可被快速检索的条目。
典型流程包括四个步骤:
- 文档加载(Load):用解析器把不同格式的文件读进来,变成纯文本或结构化文本。PDF要处理排版错乱,表格要单独抽取,这一步的坑在各大格式之间都不小。
- 切块(Chunking):把长文档切成若干小片段。为什么必须切?因为向量检索的语义匹配是看“片段级相似度”,一整本几十万字的书直接做Embedding,不仅消耗惊人,而且没有任何语义区分度。具体切多大,下文会专门展开。
- 嵌入(Embedding):用Embedding模型把每个片段变成向量。可以理解成给每本书贴了一个“索书号”,只不过这个索书号是几百维的浮点数数组,语义相近的文本,向量也相近。
- 入库(Store):把向量和其他元数据(来源文档、页码、章节标题等)存进向量数据库,并建立好索引。
2.3 检索阶段:拿到用户问题,快速召回“高度相关”的片段
用户提问的那一刻,系统要做的就是把问题变成向量,去向量库里找一批“离它最近”的片段。
这个阶段看似简单,实则是最容易被低估的。很多初学者以为“做了Embedding,扔进向量库,查一下距离完事”,但实际应用里有几个绕不开的问题:
- 用户问题通常很口语化,例如“那个退款怎么弄啊”,而文档里的表述是“退款申请需在签收后七日内提交”。语义相近但字面完全不同,仅靠向量语义匹配可以解决一部分,但不完全够。
- 向量检索召回的结果可能多而杂,前20个里可能只有两三个真的有价值,剩下都是“沾边但不相关”的噪声。
- 不同来源的文档,可能包含互相冲突的内容,检索出来了,生成时模型不知道该信谁。
所以检索阶段,实际工程里往往不是“一次向量查询”这么简单,而是“向量召回 + 关键词召回 + 重排”的一个组合拳。这个我在第4节会展开讲。
2.4 生成阶段:模型拿到“证据”后组织回答
生成阶段就是把你召回的片段、对话历史、原问题一拼,塞进Prompt里让模型回答。关键要点在于:你要在Prompt里明确告诉模型“优先使用参考资料里的内容”,还得告诉它“如果参考资料里没有,就明说不知道,不许编”。
这里有个容易被忽视的细节:RAG不是让模型“参考”资料,而是让模型“紧紧盯住”资料。很多时候生成效果差,不是检索没召回到正确答案,而是Prompt压根没约束住模型,模型自己“发挥”了一段。至于Prompt具体怎么设计,第5节我会给出一个可复用的模板。
3. 切块与嵌入:决定检索质量的两个工程细节
3.1 切块大小怎么选:这是一个需要反复试出来的参数
切块这个环节,看起来就是个“按长度切文本”,但它在RAG系统里的影响力,几乎和选哪个大模型一样大。切太小,单个片段缺乏上下文,语义不完整;切太大,一个片段里塞了太多无关信息,向量被稀释,检索精度会急剧下滑。
我在实际项目里的经验是没有万能值,只有适合你的文档结构和业务场景的区间。给个参考范围:
| 场景 | 推荐切块大小(字符) | 重叠字符 | 理由 |
|---|---|---|---|
| 问答型FAQ碎片 | 300-500 | 50 | 每个问答本身就是独立单元,切太大会混入其他条目 |
| 技术文档、操作手册 | 800-1200 | 100-200 | 需要保留完整步骤的语境 |
| 长文、论文、报告 | 1500-2000 | 200 | 语境依赖强,太碎会丢失论证链条 |
重叠(Overlap)的作用是防止某个关键句子正好落在两块的边界上被切碎。重叠这块没有标准答案,但有一条原则:重叠量建议是切块大小的10%-20%。我自己通常从800字切块、100字重叠起步,先跑通,再看检索命中率调。
还需要观察一个现象:切块策略和Embedding模型的上下文窗口是联动的。早期的Embedding模型(如text-embedding-ada-002)最大输入长度有限,切块大小超过上限就直接报错;现在很多开源模型窗口长了,很多项目就把切块往大了调,但检索效果不一定更好——因为长句子的语义向量会被肚子里那些无关紧要的词干扰。我最近在做一个运维文档问答项目,把切块从1500降到1000,检索命中率反而提升了。
3.2 结构化切块:按标题、章节、语义边界来切
按固定字数切是最简单的做法,但它在处理有结构的长文档时经常犯蠢。比如一份技术文档,第一章讲“安装”,第二章讲“配置”,固定切块很容易把两个章节的内容混到一个块里。
更稳的做法是优先利用文档本身的结构:先解析标题层级(H1/H2/H3),按章节边界切;每个章节太长再按段落切;段落还太长,再按句群切。这种“层级切块法”能保证每个片段内部在语义上是相对完整的,检索回来的内容也更自然,尤其是带Markdown或HTML结构的文档特别适合这么干。
说到结构,顺便提一个很常见的需求:表格怎么存RAG知识库。热搜里有人问“系列产品表格怎么存入RAG知识库”,这里给个经验:表格不要直接作为大块文本切进去,而是先转成每行一条的方式,用“表头+行内容”作为一条切块单元。比如一张产品参数表,把表头“型号, 尺寸, 重量, 功率”拼上每行数据,形成若干条“产品事实”,再入库。这样用户问“型号A的尺寸是多少”时,检索到的就是整行完整信息,而不是一个残缺的表格碎片。
3.3 Embedding模型选型:不同模型对中文、专业领域的支持差距很大
切块切完之后,每个块都要过一遍Embedding模型。这里有一个很多人踩过坑的地方:Embedding模型对语言的敏感度特别高。有些在英文上表现很好的模型,放到中文上效果直接对半砍;反过来也一样。
选Embedding模型时,我的建议是从三个维度评估:
- 中文支持度:务必拿你自己的中文测试集跑一遍相似度检索,看看同义句能不能被召回。网上找几个通用中文测试集也行,但不能不看中文效果就选。
- 维度与存储开销:向量维度越高,单条占用的存储和检索成本越大。常见的有768维、1024维、1536维,这些对效果有影响但决策权不必优先放在这里。
- 本地部署 vs API调用:API的Embedding模型一般效果好、不用管运维,但涉及敏感数据时不能用。本地部署开源模型,如
bge-m3、text2vec这一类,是很多政企和本地知识库项目的选择。效果上,bge-m3在中文检索里口碑不错,个人体感比更早的text2vec-large-chinese要强一截。
另外,别忽略一个重要事实:RAG系统里对Embedding模型的选择,应当和检索质量的评估绑定在一起看。我们公司有个项目由于没有做任何Embedding模型的评测对比,直接用了默认的通用模型,上线后检索召回率只有60%出头;后来换了针对中文优化的模型,召回率提升了近20个百分点。这一换,比调任何Prompt都立竿见影。
4. 检索不止相似度:元数据过滤、混合检索与重排
4.1 向量检索的天然短板:它不“识字”,它只看“语义距离”
大多数人对向量检索的理解是:把问题和文档都转成向量,然后算余弦相似度,取Top-K。这个理解没错,但它的局限性常常被忽略:向量检索没有绝对的筛选能力,它只有“近似度排序”能力。
什么意思?举个例子,你的知识库里有一份《员工手册》和一份《产品退换货政策》,用户问的是“退换货时间”,纯粹用向量检索,可能把员工手册里“试用期多久”这种句子也召回来,因为它们都说到了“时间”这个概念。你真正需要的,是“只从退换货政策文档里找答案”。
这个问题的解法很简单很暴力:在索引阶段给每个片段打好元数据标签,在检索阶段先按元数据过滤,再做向量相似度计算。元数据可以包括:来源文档类型、部门、日期、版本、角色(适用于管理员还是普通员工)等。比如你在知识库里给所有文档标注了category: 退换货,那检索时就先加一个filter: category == "退换货",直接砍掉其他噪声。
我见过太多项目把这种过滤直接扔掉不用,所有文档混在一个库,检索结果五花八门。哪怕你的向量库再强,也扛不住“什么都有”的库。元数据过滤是基础RAG里最能立竿见影提升精度的工程手段,是RAG基础中很重要的一环。
4.2 混合检索:为什么要把“精确匹配”救回来
另一个被很多人忽视的问题是:纯向量检索对“精确关键词”是无能为力的。比如用户问“如何取消订阅”,而文档里写的偏偏是“退订”——“取消订阅”和“退订”在向量上可能有一定关联,但靠那点模糊关联不如关键词检索直接命中的“退订”可靠。
成熟的RAG框架(如LangChain、LlamaIndex)里通常都有混合检索(Hybrid Search)的概念:向量检索负责语义,关键词检索负责字面命中。常见的做法是BM25加向量检索并行跑,两边各取一批结果,再做合并去重。
有个细节:混合检索不是简单地“把两个结果加在一起交出去”,而是要处理好分数归一化问题。因为Vectorscore和BM25的分数量纲完全不同,直接相加,某一方会吞掉另一方。实操上,通常先用Min-Max归一化把两边分数压到0-1区间,再按权重融合,比如向量0.7、关键词0.3;也可以先各取Top-N,再合并后交给重排模型统一打分。
4.3 重排(Rerank):从“差不多相关”里挑“真正对口”的
Top-K召回回来的片段,质量是参差不齐的。很多工程方案会再加一个重排环节,用一个小而精的Cross-Encoder模型,把用户问题分别和每个候选片段拼接成一对输入,重新计算“问题-片段”的相关性分数,然后去掉那些虚高的向量邻居。
讲一个我实际遇过的案例。某个项目,向量检索Top-5里其实已经含有正确答案,但正确答案排在第4位(前三个都是“语义沾边但不对题”的内容)。没加重排时,模型拿到的上下文里有太多干扰,回答了错误的内容;加了重排之后,正确答案被提到第1位,回答立刻对了。从那以后,我在任何一个稍大一点的RAG项目里都不会跳过重排环节。重排模型的成本不高,但对答案质量的提升非常显著。
4.4 检索环节的代码视角:一个不复杂的伪代码
为了让整个流程更直观,我用伪代码把检索阶段串起来。这里用Python风格的写法:
def retrieve(question, top_k=8): # 1. 向量召回:问题过Embedding得到向量,查询向量库 question_vec = embedding_model.encode(question) vector_hits = vector_db.query(question_vec, top_k=top_k) # 2. 关键词召回:BM25精确匹配 keyword_hits = bm25_index.search(question, top_k=top_k) # 3. 合并与去重 candidates = merge_and_deduplicate(vector_hits, keyword_hits) # 4. 元数据过滤:按业务维度裁剪 candidates = filter_by_metadata(candidates, category_filter) # 5. 重排:用cross-encoder重新精排 reranked = reranker.rerank(question, candidates) # 6. 截断,返回最相关的片段 return reranked[:5]很多框架把这些步骤封装成了现成的检索器,但懂了底层逻辑之后,出了问题才知道去哪排查——是向量库没召回到,是关键词没命中,还是重排把正确答案压下去了。这些环节从日志里一步步看,很快能定位到症结。
5. 生成段的设计:把“证据”变成“答案”
5.1 上下文组装:不是把检索片段“堆”进Prompt就完事
检索到片段之后,下一个关键动作是把它们组织成Prompt里的上下文。这里最容易犯的错有两个:一是把五六个片段不加区分地全部拼接,导致模型被冗余信息干扰;二是不告诉模型这些片段里谁优先,模型只能自己“看着办”。
正确的组装方式,我的做法是:
- 按相关性排序,最相关的放最前面。大模型对上下文前部的注意力普遍更强,这个顺序直接影响答案质量。
- 给每个片段标注来源,格式可以简单如
[1] 来源:《用户手册》第三章,这段在生成时常被用来做引用溯源。 - 用分隔符把片段和系统指令、用户问题分开。常见的做法是
=== 参考资料 ===和=== 用户问题 ===这种明确边界,让模型知道“什么是给它参考的、什么是需要回答的”。
5.2 Prompt模板:一个实战里我反复用的写法
下文给出一个经过多个项目验证的Prompt骨架:
你是一个基于参考资料回答问题的助理。 请优先使用参考资料回答用户的问题;如果参考资料不足以回答,请直接回答“资料中未找到相关信息”,不要编造。 要求: - 回答尽量精简,关键信息不遗漏。 - 如引用了参考资料的内容,请在句末标注来源编号,如[1]。 - 禁止把资料中没有出现的信息当作事实陈述。 参考资料: [1] {chunk_1_content} (来源:{doc_title}, 第{x}页) [2] {chunk_2_content} (来源:{doc_title}, 第{x}页) ... 用户问题:{question}看到没有,这个模板的核心不是花哨的“角色扮演”,而是三件事:限定使用范围、强制标注来源、禁止无中生有。很多生成效果差的项目,上一查Prompt,压根没做这三层约束,模型当然自由发挥。
5.3 多轮对话里的RAG:怎么让追问不丢上下文
热搜词里有“RAG多轮对话怎么设计”,这题确实值得单独讲一下。RAG最理想的形态是你问我答、一次结束。但在客服、医疗、法律咨询等场景里,用户会追问“那如果超期了呢?”“再比如我是在京东买的怎么办?”——这些追问本身没有完整上下文,直接拿去检索,效果惨不忍睹。
业界常见的处理方式叫查询改写(Query Rewrite):在检索动作之前,先让大模型把“当前用户问题 + 最近几轮对话”压缩成一个独立可检索的问题。举个例子:
用户:订单退款要多久到账? 助手:一般3-5个工作日。 用户:那如果遇到节假日呢? 改写后检索问题:节假日期间订单退款到账时间是否有变化?这个改写后的查询再送去检索,而不是把原始追问送去检索。我在项目里实测,加了这层改写之后,追问场景的检索命中率能提升不少。LangChain的MultiQueryRetriever也是类似思路:把原始问题从不同角度生成多个查询,再合并检索结果,降低“一次查询表达不准确”的风险。
5.4 兜底策略:检索不到答案时,宁可沉默也别硬编
最后再强调一下兜底。RAG系统的检索结果不可能永远完美,总有召回不到正确答案的时候。这时候系统该怎么表现,直接决定了产品可靠性。
我最推荐的兜底策略是:
- 设定一个相关性分数阈值,检索结果低于阈值时,不让模型硬答,而是让它回答“资料库中没有找到相关答案”。
- 给Agent设计一个“换一种方式问”的建议,比如反问用户“你能提供更多上下文吗”。
- 或者触发下一步动作,比如转人工(客服场景)、重新检索另一个知识源(进阶Agentic RAG)。
总之,不能设计成“检索什么就答什么、检索不到也硬凑”,这是RAG产品被用户骂“胡说八道”的最大源头。
6. RAG和MCP是两件事,别混着聊
6.1 为什么这两个词总被拿到一起说
最近RAG和MCP(Model Context Protocol,模型上下文协议)经常被放在同一个话题里讨论。原因也好理解:它们都出现在大模型应用开发的热门时间窗口,而且都是“让模型拿到外部信息”的手段。于是很多初学者产生困惑:我有了RAG是不是就不需要MCP?MCP会不会取代RAG?
我的回答是:它们解决的问题压根不在一个平面上,谈不上谁取代谁。
6.2 用一张表说清两者的分工
| 维度 | RAG | MCP |
|---|---|---|
| 本质 | 一种组织并检索知识的方法 | 一种连接模型与外部工具的通信协议 |
| 解决什么 | 模型缺乏特定知识时,如何从知识库取回资料 | 模型需要调用外部系统能力时,如何标准化对接 |
| 输入输出 | 输入问题,输出文档片段 | 输入工具调用请求,输出工具执行结果 |
| 典型场景 | 问答、客服、文档分析 | 调用数据库、浏览器、办公软件接口 |
| 生命周期 | 离线建索引 + 在线检索生成 | 每次调用实时协商和执行 |
举个例子,一个客服Agent,用户问“我的订单到哪了?”——这个场景如果走RAG,它去FAQ文档里找“查物流”的说明;如果走MCP,它可以直接调用订单系统的查询接口,传入订单号,拿回真实物流数据。
所以两者的关系其实是互补的。你在一个Agent系统里完全可同时拥有两边:遇到静态知识类问题,走RAG查知识库;遇到需要操作实时系统的动作,走MCP调接口。说“MCP会杀死RAG”的人,多半还没有把业务场景拆到这一层。
6.3 两者在Agent搭建中的实操选择
我一般跟团队的建议是:
- 你的知识藏在文档里,且不需要操作外部系统:RAG就够,别画蛇添足上MCP。
- 你的Agent需要“做事”,比如查订单、发消息、改配置:上MCP,让Agent具备工具调用能力。
- 你的Agent既要知道“规则”,又要执行“操作”:两个一起上有条不紊地配合起来。
这些判断,是在你动手搭Agent之前就要想清楚的,否则做完架构再改,成本会翻好几倍。这也是为什么我觉得在学MCP之前,先把RAG这块地基打牢比追新概念重要得多。
7. 从基础RAG到Agentic RAG:这一步是很多人的“进阶必答题”
7.1 基础RAG的三个天花板
基础RAG(Naive RAG)在真实场景中会遇到三个绕不开的瓶颈:
- 检索一次定生死:问题复杂时,一次查询往往找不到全部必要信息。
- 不做判断:系统不评估“当前检索到的资料够不够回答”,反正拿回来就生成。
- 拆解能力缺失:用户问一个复合问题,如“新员工的社保流程和请假流程分别是什么”,基础RAG可能只找到其中一段。
这就是为什么有了Agentic RAG——它把“决定搜什么、搜几次、够不够、要不要换方向”这些判断能力,交给Agent自己来做。
7.2 Agentic RAG做对了什么
Agentic RAG不再是一条单向管道,而是一个循环:Agent规划检索策略 → 执行检索 → 评估结果 → 不够就重新组织查询 → 直到满足条件或放弃。
比如用户问一个跨文档的问题:“第一季度各地区的销售额总和是多少?”基础RAG可能只掏出某个地区的销售文档,回答一办就算完事。Agentic RAG的做法是:先判断需要哪些地区的数据,然后逐一查询各地区文档,把结果汇总后再回答。这个过程中还有“自我反思”的痕迹——某次检索的结果明显不全,Agent会自己意识到并补查。
像LangChain里可以搭建的的create_history_aware_retriever、以及各种基于RunnableBranch的检索决策逻辑,都在帮你实现这类“检索智能”。更深一层的还有多Agent架构——一个Agent负责查询规划,一个Agent负责检索执行,一个Agent负责结果验证。这个方向在下几篇里我会专门展开,这里先给个方向感。
7.3 实操建议:先把基础RAG的基线跑出来,再谈智能化
我见过一个典型的团队,项目还没上线,就开始纠结“要不要用Agentic RAG”。我的意见是不建议这么干。你在基础RAG没有跑出可靠基线之前,你根本不知道你的问题到底出在检索还是生成;贸然上Agentic,会让排查问题变得极其复杂——到底是谁决策错了,是规划Agent还是检索器还是生成模型?
所以实操上的稳妥路径是:
- 先把基础RAG跑通,量化当前准确率(如何评估见下一节)。
- 找到最明显的瓶颈,是检索召回率低,还是生成时被噪声干扰,还是多轮上下文搞不定。
- 针对瓶颈做定向升级,这一步再决定是否引入Agentic RAG或查询改写,而不是为了追热点而上。
8. 落地时最容易翻车的几个环节与排查思路
8.1 坑一:知识库本身是脏的,RAG再强也白搭
这是我认为RAG项目里最要命的一个问题:索引阶段的文档质量,决定了整个系统的上限,而检索和生成只是在逼近这个上限。
常见脏数据包括:
- 同一产品有多个历史版本的文档,旧版本没有下线,新旧说法冲突。
- PDF解析之后乱码、表格错位、多栏排版读串行。
- 文档里有大量无意义的修饰语、免责声明、重复模板文字,切出来的块大半是废词,把向量带偏。
我印象很深的一次:给一个内部知识库做RAG,检索出来的内容总是“看起来相关但关键参数对不上”,排查了很久,最后发现索引里混进去三份不同年份的旧产品规格书,其中两份已废弃但没有从索引中删除。花了两个晚上清理元数据和下线旧版本,检索准确率直接上来一个档次。
所以,在做RAG之前,先做数据治理:建立文档来源清单、标注版本和有效状态、清洗解析错误、统一模板。这些脏活累活,比调任何一个模型参数都更值得投入。
8.2 坑二:没有评估体系,优化全靠“感觉”
另一个大坑是:很多团队上线RAG后,凭“感觉”判断效果好坏——某几个测试问题回答得好就觉得成功,回答得不好就随便调切块大小。这种搞法极难沉淀经验。
我建议至少搭建一个最小的离线评估集:50-100对“问题-标准答案”,标注每道题的正确答案在知识库里的哪个文档、哪个片段。然后跑两个指标:
- 检索命中率(Recall@K):正确答案片段是否出现在召回的Top-K里。
- 生成正确率(Answer Accuracy):模型最终回答是否与标准答案一致。
有了这套评估集,你再谈“调参”就有的放矢了。我自己每次调切块或换Embedding模型,都会先在评估集上跑一遍,用数据说话,而不是靠抽查几个例子拍脑袋。
8.3 坑三:知识更新了,但线上还在用旧索引
RAG系统上线后,知识库不会静止。产品文档更新、FAQ变化、部门边界调整,都会让索引里的内容过时。要命的是,很多团队在更新文档后,忘了同步重建索引,导致用户查到的是过时内容。
解决这个问题,需要定一个“知识更新策略”:
- 定时重建:每天凌晨跑一次索引,适合文档量不大且更新不频繁的场景。
- 增量更新:文档有增删时只更新变化的部分,适合数据量大、更新频繁的场景,需要文档系统有明确的变更记录。
- 双版本切换:新旧索引并行,验证新索引检索质量达标后再切流量,适合对检索质量要求极高的场景。
无论用哪种,都建议在向量库里给每条数据记录updated_at字段,线上出问题时可以反查“用户搜到的内容对应哪个版本”。这个细节在排查线上问题时会救你一命。
8.4 坑四:把全部希望押在“更大更强的模型”上
还有一个常见的心理误区:答案生成错了,第一反应是换更牛的模型,而实际上问题多半出在检索环节。答案错了,可能是相关片段根本没被召回,也可能是被噪声片段干扰了,也可能只是Prompt约束不够。
我的建议是遇到“回答质量不行”先做归因:
- 打印检索回来的Top-K片段,人工判断:正确片段在不在里面?
- 不在:问题出在索引或检索,排查切块、Embedding、召回策略。
- 在里面但答案还错:问题出在生成,调Prompt或换生成模型。
- 如果Top-K里混合了太多噪声:加强过滤、加重排、压缩上下文。
这套归因方法,能省掉你不计其数的“盲目调参”时间。这也是为什么我在这篇里反复强调:RAG的基础不只是“会调库”,而是能把一条管道的每一个环节都拆开来看。
最后,如果你正在从零搭建自己的AI Agent,我的建议是别一上来就追各种花哨的Agent框架。先从一份真实、干净的文档库开始,把索引、检索、生成这条管道亲手走通,然后安安静静地做一套评估集,测出自己系统的真实水平。这一篇虽然只是基础,但这块地基扎实了,后面谈Agent自主决策、谈多Agent协作,你才真的有底气。下一篇我会接着写RAG落地以后的进阶:从“检索增强生成”走到“检索增强思考”,看看Agent怎么把知识管道从“查一遍”升级成“反复推理”。