做 AI Agent 的人,迟早会撞上一堵墙:模型把推理玩得很溜,但一问到它没见过的业务细节,就开始一本正经地胡诌。我之前给团队做内部问答 Agent 时,最典型的场景就是同事问"退款 pending 状态下一步要做什么",模型回答得很流畅,但流程完全是自己编的。这不是模型不够聪明,而是它压根没见过我们系统里的那份退款状态说明。这篇是 AI Agent 系列的第四篇,专门聊 Agent 的知识获取管道:RAG 基础。
前三篇我们分别聊了 Agent 的整体骨架、任务规划、工具调用,今天把目光移到知识的来源。RAG(Retrieval-Augmented Generation,检索增强生成)解决的是一个大问题:模型参数是它出生时的世界,而业务知识在你自己的身体里。如何把这两者接起来,让 Agent 既能推理又能"查证",就是这条管道的核心任务。这篇文章会从原理、最小实现、检索质量指标、Agent 场景下的演化,再到落地踩坑,把 RAG 这条线完整过一遍。
1. 为什么 Agent 需要一个"知识接口"
1.1 模型参数是"出生时的世界",不是你的知识库
先说一个常被忽略的事实:LLM 的所有知识,都来自训练时见过的数据。这意味着三件事。
第一,它有知识截止时间。训练完成那一刻,它对这个世界的认知就冻结了。你问它昨天的系统故障、上周上线的功能,它没有任何概念,只能靠猜。第二,它不知道你的私有知识。公司内部的 API 文档、历史项目复盘、产品手册、合同条款,这些东西从来不会出现在公开语料里,模型天然不知道。第三,即使是公开的长尾事实,它也可能记错。训练语料里某个冷门实体只出现过几次,模型大概率是在"编"而不是在"忆"。
做 Agent 时,这些缺陷会被直接放大。因为 Agent 要完成任务,不是陪聊。任务型场景对知识准确性的要求极高,差一个字段就可能导致整个流程走错。你不能让 Agent 靠"猜"去执行任务。
知识获取管道的本质,就是在模型推理之前或推理过程中,把外部知识检索出来、拼接进上下文,让模型基于证据作答,而不是基于记忆猜测。
1.2 微调不是万能的,RAG 才是日常主力
可能有人会问:那为什么不直接微调?这其实是很多团队最初的想法,我也走过这条路。要理解为什么 RAG 是常态、微调是特例,得看清两者的分工。
微调改变的是模型的行为模式和输出风格,比如让模型学会按你公司的文档格式写周报、学会在回答里附带风险提示。它适合固化一种"做事方式",但完全不适合承载事实型知识。原因很现实:微调一次要准备数据集、要训练、要评估、要上线,整个周期以天甚至周计。而业务知识是高频变动的——今天改了接口参数,明天新增了退款规则,后天修正了某个配置的默认值。如果每次知识变动都走一遍微调,光是维护成本就能拖垮一个小团队。
RAG 的知识注入是"改库"而不是"改模型":文档更新了,把新文档切块、向量化、写进向量库,Agent 下次回答就能用到新知识。整个过程可以做到分钟级,甚至做成自动管道。
那么什么时候该微调?我的经验是两个场景:一是模型在特定任务上始终学不会你期望的格式或风格,且规则很难用 prompt 描述清楚;二是你希望模型对某个专业领域形成"直觉",比如医疗术语的读法、编程语言的代码风格迁移。但即便这样,微调之后通常还要配 RAG 兜底,因为事实性知识依然要实时更新。
1.3 早期"硬塞 prompt"的做法有多痛苦
在引入 RAG 之前,团队最常用的土办法是把知识库内容整个塞进 system prompt。小规模验证时好像还行,知识量一大就全面崩坏。
我印象很深的一次:某项目的知识文档整理出来大概是 80 个片段,全塞进 prompt 后,单次请求的 token 数直接翻了五倍,延迟从 1 秒涨到 4 秒,成本同步上升。更隐蔽的问题是注意力稀释——模型面对一长串背景资料,开始"复读"prompt 里的内容而不是真正回答问题,甚至会把早期提到的过时规则和后期修正过的规则混在一起输出。
这个经历让我确定了技术选型方向:Agent 的知识获取不能靠"把库搬进 prompt",必须靠检索。模型一次能吃的上下文有限,而知识库可能有几十万条文档,唯一可行的路径是——先精准定位,再小范围投喂。
2. 知识管道从零搭建:两段式 RAG 的最小形态
RAG 管道的完整链路可以拆成两条:离线侧的索引构建和在线侧的检索生成。索引侧包含文档加载、解析、分块、向量化、入库;在线侧包含查询改写、向量检索、重排序、拼接上下文、生成答案。下面按最小可用形态一条条展开。
2.1 文档加载与解析:上游决定下游
很多新手一上来就调 Embedding、调检索,但我建议先检查文档解析这一步。因为下游的一切都依赖上游给到的文本质量。
以最常见的企业 PDF 文档为例,你拿到的 PDF 可能是扫描件、可能是排版混乱的导出件、可能是包含复杂表格的年度报告。如果解析环节把表格结构拆坏了,后面的分块和检索都是在垃圾上做优化。
我的建议如下:
- 先把文档类型盘清楚:PDF、Word、Markdown、HTML、Confluence 导出、数据库里的字段说明,每一种的解析策略都不同。
- 表格是最容易翻车的地方。PDF 里的表格如果用纯文本抽取,行列关系会稀碎。优先用带结构识别能力的解析器,比如把表格转成 Markdown 表格或 HTML 表格再入库。实测下来,表格用结构化格式保留,检索效果比纯文本好非常多。
- 给每个被索引的片段打好 metadata:来源文件、章节路径、更新时间、文档类型、业务线。这部分信息后面做过滤和引用溯源全都要用,别偷懒。
解析这一步的产出,是一批"干净且带元信息"的纯文本块。但解析完还不能直接切,先想清楚你要按什么粒度切。
2.2 分块策略:边界划在哪,决定了检索效果的上限
分块是整个 RAG 管道里最容易被低估的环节。我见过太多人直接按固定 token 数切,比如每 512 个 token 一刀,切完就入库。这种做法在短文档、FAQ 类语料上勉强能用,但一遇到长文档就出问题。
固定长度切块会把语义从中间切断。比如产品文档里写"退款状态分为 pending、approved、rejected。其中 pending 表示等待财务审核……",如果按固定长度切,状态说明很容易被切到下一个 chunk。用户问"pending 是什么意思",检索到的 chunk 里只有状态列表没有解释,模型就只能瞎编。
更合理的做法是"按语义单元切":一个小节的标题连同它下面的内容作为一个 chunk,一个表格作为独立 chunk,一段完整的操作说明作为 chunk。同时让 chunk 保留它的上下文信息,比如在内容前注入文档标题和章节路径。我常用的一种做法:
def parse_document_to_chunks(doc): chunks = [] for section in doc.sections: # 按文档结构遍历 text = f"# {doc.title} > {section.heading}\n{section.content}" chunks.append({ "text": text, "metadata": { "doc_id": doc.id, "source": doc.source_url, "section": section.heading, "updated_at": doc.updated_at } }) return chunks分块的粒度也直接影响检索效果。块太大,向量包含太多无关信息,检索精度下降;块太小,单个块可能无法独立回答问题。我的经验是:优先保证"每个块是一个能被独立理解的语义单元",而不是死守某个 token 数。FAQ 场景可以一个问答对一块,操作手册按步骤分组,长报告按小节切。如果有些块确实太长(超过 800 token),再按段落二次切分,但要让子块携带父块标题。
另外还有两个常用技巧:一是重叠切分,相邻块留 10% 到 20% 的重叠,缓解边界割裂;二是父文档回填,检索命中子块时,把父块或相邻块一并作为上下文返回,保证信息完整。这两招不用一开始就上,但当你发现"检索到了但答案不完整"时,优先考虑加它们。
2.3 Embedding 与向量库选型:别在工具上花太多时间,先跑通
Embedding 的本质是把一段文字变成一个向量,让语义相近的文字在向量空间里靠得近。选 Embedding 模型时,要看中文能力、领域词汇的覆盖度、维度大小、能否私有化部署,以及成本。
我的选型经验分三档:想快速验证,直接用 openai 的 text-embedding-3-small,效果稳定,成本低;中文内容占比高,优先试开源的 BGE-M3 或 m3e 系列,可以本地跑,对中文支持很好;有预算且追求上限,再考虑更大的商业模型或领域微调过的 Embedding 模型。
一个重要提示:Embedding 模型的效果差异,在"领域词汇"上体现得最明显。如果你做的是金融、医疗、法律这种术语密集的领域,通用 Embedding 可能把两个看似相近但含义不同的术语拉得很近,这时候就需要领域微调。但对于大多数场景,先别折腾微调,把精力放在分块和检索策略上,收益更大。
向量库的选择取决于数据规模和运维条件:
- 几千到几万条,用 Chroma 或 FAISS 起步足够,本地文件即可,不需要部署服务。
- 已有 Postgres 数据库,直接上 pgvector,少一个组件,事务和权限都复用现有体系。
- 数据量到百万级、有高并发或复杂过滤需求,再考虑 Milvus、Qdrant 这类专用向量库。
我的建议是:MVP 阶段别纠结向量库,Chroma 能让你把整条管道跑通,后面再迁移不迟。把数据规模、过滤条件、并发量这些需求先验证清楚,选型才有依据。
2.4 检索:从 Top-K 到可用门槛
检索阶段,大多数教程会让你做三步:查询向量化 → 相似度 Top-K → 把结果拼进 prompt。这个流程能跑通,但离可用有距离。
先说 K 的选择。K 太小容易漏信息,K 太大则把不相关内容塞进上下文、稀释注意力。我的默认值是 5,如果知识库内容比较长或问题复杂度高,会调到 8-10。但盲目调 K 不如把候选范围扩大后在重排序阶段收敛,后面细说。
其次是相似度阈值。向量检索本质上是一个排序问题,和"这个查询到底相不相关"是两回事。哪怕 query 和知识库毫无关系,它也会返回相似度最高的几个结果。所以一定要设阈值:当最高相似度低于某个值(比如 0.4 到 0.5,取决于 Embedding 模型的打分分布),宁可返回空,让 Agent 明确告诉你"知识库里没有相关信息",也不要硬塞一堆不相关内容。这也是避免幻觉的重要防线。
再就是利用 metadata 做预过滤。如果你的知识库有文档类型、更新时间、业务线这些字段,检索前先做结构化过滤,能显著提升准确率。比如用户问"2025 年的活动规则",那就先在 metadata 里过滤掉 2024 年的文档,再做向量检索。这比纯靠语义相似度区分时间信息靠谱得多。
最后说一个基础但重要的认知:纯向量检索对同义改写和专有名词缩写不敏感。比如用户问"订单 WAITING 状态卡住了",而文档里写的是"待支付状态",两者的向量相似度可能并不高。这个问题靠后面的混合检索和查询改写解决,但你要先知道"检索不到不等于知识库没有"。
3. 检索质量才是 RAG 的命门:Hit Rate 与调优玩法
3.1 为什么盯 Hit Rate 而不是盯生成答案
很多人评估 RAG 效果时,直接看"答案对不对"。这个视角有问题,因为你看到的是生成环节的最终结果,它同时受检索质量、上下文质量、模型能力、prompt 设计四方面影响。一旦答案错了,你很难判断是哪一环出的错。
所以我把评估拆成两段:先独立评估检索质量,再评估生成质量。而检索质量的核心指标就是 Hit Rate。
Hit Rate 的定义很直白:给一组测试问题,每个问题标注出"标准答案所依赖的知识块";跑检索看 Top-K 结果里是否包含这个知识块;包含算命中,最后统计命中比例。
举个例子,你准备了 100 条测试问题,每条问题对应知识库里的一个或几个 doc_id。跑完检索后发现,在 Top-5 结果里有 82 条问题命中了对应文档,Hit Rate@5 就是 82%。
为什么这个指标重要?因为它把"检索环节"从整个链路里独立出来了。你可以在不调用 LLM 的情况下,快速迭代分块策略、Embedding 模型、检索参数,观察 Hit Rate 的变化。只有检索命中率上去了,调 prompt 和生成才有意义。我踩过最大的坑,就是没建评测集就猛调 prompt,调了半天发现答案还是错的——一查才知道是检索本身就漏了,prompt 怎么调都白搭。
3.2 影响 Hit Rate 的真实因素清单
根据我的实测,影响 Hit Rate 的因素按权重排大概是这样的:
| 因素 | 影响方式 | 调优方向 |
|---|---|---|
| 分块质量 | 语义被切断直接导致检索不到 | 按语义单元切块,保留标题上下文,重叠切分 |
| 查询与文档的措辞差异 | 用户口语和文档书面语对不上 | 查询改写、同义词扩展、多路查询 |
| Embedding 模型 | 领域术语和中文支持不足 | 换开源中文模型或领域微调 |
| 检索策略 | 纯向量检索无法精确匹配 | BM25 混合检索、metadata 过滤 |
| 重排序 | Top-K 候选里相关结果被埋没 | 扩大候选后用 cross-encoder 重排 |
其中分块质量和查询改写这两个因素,在工程实践里贡献的收益最明显。Embedding 模型的影响当然也大,但在中文通用场景下,从开源模型起步基本够用。重排序是"最后一公里",能把 Hit Rate 再往上推一截,但前提是前面的分块和检索没出大问题。
3.3 查询改写、混合检索与重排序的实测收益
先说查询改写。用户提问通常是口语化的,比如"那个 Activity 系统最近咋老加载失败"。文档里写的是"Activity 模块启动异常排查指引",两者在词汇上没有重合,向量相似度也不会太高。怎么解决?用 LLM 把用户原始问题改写成适合检索的查询。
实际操作上,我一般让 LLM 输出 2-3 个检索查询:一个尽量贴近原文表达,一个补全同义词,一个翻译成文档可能的写法。比如:
用户问题:Activity 系统为什么一直加载失败? 检索查询1:Activity 系统加载失败原因 检索查询2:Activity 模块启动异常排查 检索查询3:Activity loading failure然后把这三路查询分别去做检索,合并结果去重后再进重排。这个操作的效果非常显著,尤其是用户提问方式和文档写法差异较大的场景。代价是每次查询多调一次 LLM,但可以用小型模型来做,成本不高。
混合检索是因为纯向量检索有盲区:它对精确的实体名、缩写、代码片段、版本号不敏感。而 BM25 关键词检索恰好擅长这些。两者结合后,用 RRF(Reciprocal Rank Fusion)合并排名:每个文档在两路结果里的名次都参与打分,公式是 score = Σ 1/(k + rank_i),k 通常取 60。这样即使一路漏掉了,另一路也能捞回来。
重排序是收益最直观的优化。向量检索的相似度是一种"粗略匹配",而重排序用 cross-encoder 模型把 query 和每个候选文档重新精确比较一遍,能捕捉到双塔结构容易忽略的交互信号。
我的一组实测数据可以给你参考:在某个内部知识库上,纯向量检索 Hit Rate@5 只有 0.58;加上 BM25 混合检索 + RRF 后提升到 0.71;再把候选从 20 扩到 50、用 rerank 模型取 Top-5 拼接,Hit Rate 到了 0.84。也就是说,单靠检索侧的优化,就能把命中率从不到六成拉到八成以上。
注意一个细节:重排序的候选集要足够大。如果你只取向量检索的 Top-5 再重排,重排序几乎没有意义;应该先混合检索取 Top-20 到 Top-50,再重排取 Top-5。这一步会多花 100-300ms 的延迟,但在需要准确答案的业务场景里非常值得。
4. 从"查一次"到"按需查":Agent 场景中的 RAG 演化
4.1 传统 RAG 与 Agent 工作流的落差
传统 RAG 的用法是:用户问一个问题 → 系统检索一次 → 把结果拼进 prompt → LLM 生成答案。这个模式适合"单跳问答"——一条问题可以通过一次检索获得答案。但 Agent 的真实任务往往是复杂的,比如"帮我分析一下这个月销售额下降的原因"。
这个问题无法通过一次检索解决。Agent 需要先了解哪几个地区、哪几条产品线在下降,然后分别查询对应数据,再综合判断,甚至可能需要检索不止一轮:先查月度销售报表,得到初步线索后再追查具体的产品线明细。传统 RAG 的"一次命中"模式,支撑不起这种多步骤任务。
这就是 Agent 与传统 RAG 的核心差异:传统 RAG 把检索当作"回答问题前的固定动作",而 Agent 需要把检索当作"可随时调用的工具"。至于什么时候查、查什么、查几轮,应该由 Agent 根据当前任务状态动态决策。
4.2 Agentic RAG 的几种工作模式
Agentic RAG 没有统一标准,但实践中我常用这几种模式,按复杂度递增排序:
路由式(Router):先对用户输入做分类,判断应该走 RAG、走数据库查询、还是直接由模型回答。比如"昨天晚上系统报了什么错"走日志检索,"2024 年全年财报"走数据库,"简述 Transformer 结构"直接模型回答。路由能避免把所有问题都塞给 RAG 导致检索垃圾进上下文。
多跳式(Multi-Hop):Agent 先执行一次检索,拿到初步结果后,如果发现信息不足,再改写查询、检索第二轮,甚至第三轮。每轮检索之间可以穿插推理。比如查"某功能上线后用户投诉最多的三个原因",第一轮检索拿到投诉列表,第二轮基于列表里的关键词再去查对应原因。
反思式(Reflection):Agent 生成答案后,不直接返回,而是先自我检查"这个答案有没有引用检索证据?证据是否充分?"如果发现关键信息缺失,主动补充检索再修正答案。这种模式能显著减少幻觉,代价是增加一轮到两轮的额外调用。
总结式(Summarization):当问题涉及大量知识块时,比如"把这份 50 页的研究报告浓缩成要点",Agent 会把文档拆成若干部分,对每部分分别检索和总结,最后再汇总。这可以理解成 RAG 的并行版本。
这几种模式并不是互斥的,一个生产级 Agent 很可能同时具备路由、多跳和反思能力。另外,这两年出现的 RAG-as-a-Service 理念也值得关注,它可以理解为把知识获取能力封装成标准服务,供 Agent 在运行时按需调用,而不是把检索逻辑和 Agent 逻辑耦合在一起。这算是 Agentic RAG 走向工程化的一个趋势。
4.3 知识割裂问题的本质与图方向
另一个在 Agent 场景中会被放大的问题是知识割裂。传统 RAG 把文档切成块之后,块与块之间的语义关联就丢失了。如果答案需要跨多个 chunk 的线索拼接,检索就会力不从心。
举个实际例子:你的知识库里有一份"退款流程说明",另一份是"风控规则明细"。用户问"退款金额超过 5000 元时,流程有什么特殊处理"。正确的答案需要把两份文档的信息拼起来,但传统 RAG 可能只检索到流程说明,风控规则那条信息排在 Top-5 之外,模型只凭一半信息作答。
解决这个问题有三个方向:
第一,用 metadata 做关联过滤。如果两份文档都打上了"退款"这个业务标签,检索时把标签作为硬条件,可以降低跨文档漏召回的概率。这是成本最低的做法。
第二,用父文档结构做关联。当命中一个子块时,把同一文档的相邻块或父块都取回来。这个方法能解决"同一份文档内跨小节"的知识割裂,但对"跨文档"割裂无效。
第三,用图结构建模实体关系,也就是 GraphRAG 或本体 RAG。先把文档中的实体(产品、状态、部门、系统)和关系抽出来,构建知识图谱;检索时先定位用户问题涉及的实体,再沿着图关系找到关联文档。这个方向对"多实体关系型问题"很有效,比如"A 系统变更会影响哪些下游系统",但代价是抽取成本高、维护复杂。
我的建议是循序渐进:基础 RAG 的 Hit Rate 还没达标时,先别碰图方向。先把分块、混合检索、重排序做好,再考虑结构化关联。GraphRAG 不是银弹,它解决的是"关联关系"问题,但解决不了"基础检索就漏"的问题。
5. 落地中的坑与我的操作习惯
5.1 没有评测集就优化,等于闭眼开车
这是最想说的一条经验。RAG 管道的每个环节都有参数可调——分块大小、重叠比例、K 值、相似度阈值、重排模型、查询改写 prompt。没有评测集,你根本不知道改动是在变好还是变坏,最后只能凭感觉调,调完心里也没底。
我的做法是:每条知识库对应准备一批测试问题。第一轮不追求多,50 条即可,但必须涵盖真实用户提问的方式,而不是你自己写的"标准问法"。给每条问题标注好它应该命中的 doc_id。之后每次改管道,都跑一遍这批问题,记录 Hit Rate 和端到端答案质量。
有了评测集之后你会发现,很多你以为的"玄学优化",其实都是可以量化对比的。这也是我推荐所有人做的第一件事。
5.2 优化优先级:先检索,再生成,最后调 prompt
很多人一上来就调生成 prompt,试图让模型"更准确地从给定上下文里抽取答案"。但如果检索回来的内容本身就不对,prompt 写得再好也没用。
我的调试顺序是:
- 先看解析和分块有没有破坏语义。
- 再看 Hit Rate,如果低于 0.8,优先做查询改写、混合检索、重排序。
- Hit Rate 达标后,再观察生成答案。这个时候如果答案还是不对,才轮到 prompt 工程的事。
还有一个小习惯:每次检索都要记录下 query、检索到的 chunk、每个 chunk 的相似度分数、最终答案、以及用户的反馈。这样当线上出问题时,你能回放完整链路,定位到底哪一环出了错。没有日志,排错基本靠猜。
5.3 知识库的增量更新与版本管理
知识库不是一次性建完就完事的。业务文档每周都在变,你需要一个可重复执行的更新流程。
我踩过的坑是早期全量重建:每次文档一更新,就要把整个库重新 embedding 一遍。文档少还好,文档一多,既浪费时间又浪费钱,而且更新期间检索会拿到旧数据。
更合理的做法是维护 document ID 级别的增量更新:新增文档时只对新文档做分块和向量化;文档变更时先按 doc_id 删除旧块,再写入新块;文档下线时对应删除。向量库基本都支持按 metadata 过滤删除,关键是在写入时就打好 doc_id 标记。
版本管理的另一面是"旧知识保留"问题。比如退款规则在 2025 年改过,那 2024 年的旧规则还有参考价值吗?如果完全删掉,历史问题就查不到了;如果保留,新问题可能检索到旧规则。我的做法是在 metadata 里存生效时间,检索时按"当前生效"过滤,同时保留历史版本供特定查询使用。
5.4 成本控制与资源有限时的取舍
最后聊一下成本。RAG 管道的成本大头有三个:Embedding 调用、向量库存储、重排序模型的推理开销。
Embedding 成本很低,但如果你有百万级文档且频繁全量重建,账单也不小。增量更新能省一大块。重排序是延迟和算力的大头,但只在查询时才跑,规模可控。如果预算紧张,可以先用 BM25 混合检索替代重排序,虽然 Hit Rate 会差一截,但至少把地基打好。
向量库存储的成本反而不高,真正贵的是 GPU 推理资源。如果你用开源 Embedding 和 rerank 模型,一台普通的推理服务器就够支撑中等规模团队的使用;如果是调用商业 API,那就更省心,只需要关注调用量和配额。
用一个小规模项目起步时,我的建议是:先全部用开源模型本地跑通,等验证了业务价值再考虑采购更强的模型或商业化组件。不要一上来就追求最贵的方案,RAG 的瓶颈从来不在模型有多强,而在数据准备和检索策略这些脏活累活上有没有下功夫。
我自己在踩过一轮坑之后的体会是:RAG 工程里没有银弹,但有一条很明确的主线——先把 Hit Rate 管住。你如果现在正准备给 Agent 接知识库,我强烈建议先拿 50 条真实问题跑一遍离线评测,无论结果多难看,先记录下来,再开始调。知识获取管道做通容易,做专业需要持续打磨;等基础命中率稳定了,再往多跳检索、图结构方向走也不迟。