在之前的篇幅里,我们把 Agent 的核心框架、规划能力和工具调用都聊了一遍,今天终于要补上一个大件——知识获取管道。
先说一个实际的感受:很多朋友搭 Agent 时,第一步就把 prompt 写得又快又长,什么“你是一个资深专家”“请充分利用你的知识”,但运行起来之后却发现,Agent 总是在“一本正经地胡说八道”。原因很简单,大模型的训练知识是凝固的,它不知道你公司的内部文档、不知道你本地 ERP 里的产品参数、更不知道今天早上刚更新的工单记录。这时候你需要的是 RAG——给 Agent 接一条按需取用的知识管道,让它从你的知识库里检索相关内容,再结合自己的语言能力组织答案。
这篇是“走进 AI Agent”系列的第四篇。我不打算再泛泛谈概念,而是把 RAG 从索引构建、检索设计到生成融合的完整链路拆开,结合我搭建知识库和 Agent 时的踩坑经历,聊聊一个能落地的 RAG 基础管道到底该怎么搭,以及怎么判断管道质量是否过关。
1. 为什么 Agent 离不开一条“知识获取管道”
1.1 纯 Prompt 的 Agent,知识天花板太低了
不少入门教程都在教你“把所有知识塞进 System Prompt”。场景简单时没问题,比如你只做一个回答常见问题的客服bot。但真实业务里,文档数量很快就膨胀了。
我做过的第一个带知识库的 Agent 项目,公司内部有大约 120 篇产品说明书和运维手册,PDF 加 Word 混着,合计约 280 万字。试想一下把这些全塞进 prompt:
- Token 成本先不谈,光是每次调用动辄几万 Token 的输入长度,延迟就拉到不可接受;
- 模型对超长上下文的关注度会严重稀释,相关段落淹没在无关信息里;
- 知识更新相当于改 prompt,容易出现格式错误,维护成本极高。
更重要的是,大模型的参数知识是“死”的。它学的是某个时间点之前的公开数据,公司内部的私有文档它根本没见过。如果不给 Agent 一条从外部获取知识的管道,它就只能靠你的文本片段“猜”,或者靠工具实时拉取——但工具调用产出的是原始数据,不是经过组织、打分、排序后的上下文。
1.2 RAG 在 Agent 里的定位:检索模块,而不是回答模块
很多人把 RAG 理解成“一种回答问题的技术”,这容易陷入误区。准确的定位是:RAG 是 Agent 知识的获取层,它负责回答“我要回答这个问题,需要看哪些资料”——至于最终的语言组织和推理,仍然交给大模型。
Agent 的一次完整行为循环可以简化为:
感知(用户输入) → 判断(是否需要外部知识) → 检索(知识管道) → 注入(上下文组装) → 生成(回答/行动) → 校验(是否满足需求)如果缺了“检索”这一步,Agent 在面对事实性问题、最新资料、私有知识时就是“裸奔”。这也是为什么现在很多企业级 Agent 产品把 RAG 作为标配模块,而不是一个可选项。
我见过很多团队直接拿通用聊天接口接业务,结果用户在群里问一句“最近库存政策是什么”,回答全是从互联网旧文里抄来的。搭建 RAG 不是为了赶时髦,而是在体系上解决“模型不知道”和“模型知道了但没有依据”这两个问题。
2. 打通管道:从原始文档到可检索的向量索引
RAG 基础管道的前半段,是离线的知识入库阶段。这段做得好不好,直接决定后续检索效果的上限。我见过不少人一上来就调 embedding 模型、换向量数据库,但其实根子上是 chunk 切分和文档清洗出了问题。
2.1 文档加载与清洗:这一步偷懒,后面全是坑
先要明确一点:不是所有文件都能直接切分,切分之前得做文档解析和清洗。
常见的文档类型有 PDF、Word、Markdown、HTML、纯文本。每种格式的解析差异很大:
- PDF:很多是扫描件或由排版软件生成,直接按行提取会得到错乱文本。建议先用 OCR 工具(如 PaddleOCR)处理扫描件,再用 pdfplumber 或 PyMuPDF 按块提取文字;
- Word:注意表格和页眉页脚,这些内容按段落切分时会变成噪声;
- HTML:要剥离标签,但保留标题结构,因为标题信息对后面分段非常有用;
- Markdown:结构最友好,保留标题层级即可。
我在项目里踩过一个很典型的坑:文档里大量使用了全角空格和特殊符号,看起来没事,可一旦切进向量库,检索匹配时这些字符就会成为干扰项。清洗阶段至少要处理掉:
- 多余空白、不可见字符;
- 页眉、页脚、页码;
- 大量重复的导航文本和版权声明;
- 表格内容丢失导致的语义断裂。
提示:花 30% 的时间在清洗上,胜过之后花 100% 的时间调检索参数。脏数据进管道,检索结果必然脏,这不是 embedding 模型能兜住的。
2.2 Chunk 切分:为什么说“一刀切”是最大的误区
清洗完成后,就是 RAG 环节里争议最多的 chunk 切分。我见到过的最常见做法是“固定 500 字切一段,overlap 50 字”——这样确实简单,但效果往往平庸。
核心问题是:语义边界在哪里,切分就应停在哪里。固定长度切分属于“物理边界”,很容易把一段完整论述拦腰切断,或者把一个表格拆得七零八落。
我推荐按“结构感知 + 长度控制”的方式切分:
- 优先按 Markdown 标题、PDF 章节、HTML 的
h1/h2识别语义块; - 如果语义块过大,再按段落、句子、固定窗口递进切分;
- 对于长文本,需要引入 overlap(重叠窗口),避免切分点把关键句子拆断。
以下是结构化切分与固定长度切分的对比:
| 维度 | 固定字符切分 | 结构感知切分 |
|---|---|---|
| 语义完整性 | 容易切断句子或段落 | 按标题/段落边界,完整性更好 |
| 检索准确率 | 中等,易引入无关块 | 较高,检索结果更聚焦 |
| 实现难度 | 低,几行代码搞定 | 中等,需要解析文档结构 |
| 适合场景 | 纯文本、无层级的长文档 | 结构化文档、说明书、Wiki |
过小的 chunk 会丢失上下文,过大的 chunk 又会把多个主题混在一起,导致检索得分不精准。最有效的做法是用“多级切分”——先按章节切,章节不够再往下切。我实测下来,把 chunk 大小控制在 400~800 个 token 之间、overlap 控制在 50~100 个 token 是比较合理的起步区间,但具体还得看你的文档风格。
2.3 Embedding 选型:大而全不如贴得准
切分完成之后,每一段文本都需要被转换成向量。这一步的关键是选择合适的 embedding 模型。
embedding 模型的作用,是把“文字语义”映射成高维空间里的向量坐标。语义越相近的文本,向量距离越近。目前主流的选择有:
- OpenAI 的 text-embedding-3-small / 3-large:通用效果好,但对中文的支持依赖语料,接口调用需考虑网络条件;
- BGE(BAAI 系列):中文场景下性价比高,本地部署方便,是我在本地知识库项目里最常用的;
- M3E:另一个专注于中文的模型,对短文本检索更友好;
- Cohere 的 embed 系列:多语言能力不错,在需要多语言检索时可以考虑。
选型时不要只看榜单分数,要在自己的语料上做小规模评测。方法很简单:拿 20 个真实问题,对每个问题人工标注它应该命中哪个文档块,然后分别用 A 模型和 B 模型做检索,看谁排在前面的概率高。
还需要留意两点:
一是向量维度与存储开销。BGE-large 是 1024 维,OpenAI small 是 1536 维,维度越高通常精度越好,但存储和查询耗时也上升,在千万级数据量下差距极其明显。
二是模型统一性。索引阶段用什么模型,检索阶段就必须用同一个模型,否则向量空间不一致,检索结果毫无意义。这个看似常识的问题,我见过不止一个人因为中途换模型导致线上效果骤降。
2.4 向量数据库选型:别被“全功能平台”带偏
有了向量,还得有个地方存。向量数据库的核心功能就是近似最近邻搜索。当前常用方案大致分成三类:
| 类型 | 代表 | 优势 | 劣势 |
|---|---|---|---|
| 专业向量库 | Milvus, Qdrant, Weaviate | 性能强、功能多、支持过滤筛选 | 部署运维成本较高 |
| 轻量库 | FAISS, Chroma | 部署简单,适合原型和中小规模 | 功能相对简单 |
| 关系库扩展 | pgvector(PostgreSQL), SQLite-VSS | 可以复用原有数据栈,事务能力强 | 大规模向量检索性能弱于专业库 |
如果你只是个人项目和原型验证,用 Chroma 或者 FAISS 就够了。如果要上生产、数据量到百万级甚至更多,建议直接上 Milvus 或 Qdrant,别在高并发阶段频繁迁移。
我这里要特别提醒一点:不要把向量数据库当成万能搜索引擎。它擅长的是“语义相似”,对于精确匹配(比如订单号、物料编码、人名)并不擅长。生产环境里通常需要把向量检索和传统的关键词检索(BM25)组合起来用,这就是后文要讲的混合检索。
3. 检索端的工程细节:召回不准确,一切上层优化都是徒劳
RAG 管道的后半段是检索和生成。很多教程直接说“把问题发给向量数据库,拿 top-k 结果拼进 prompt”,但实际上检索端有大量工程细节,直接决定你知识库的回答质量。
3.1 Query 改写:用户的问题不等于检索词
用户原始输入通常不适合直接作为检索 query。一个典型例子是:用户问“咱们公司的年假政策跟去年有什么区别”,索引里可能既没有“咱们公司”也没有“去年”对应的精确文本。如果不做 query 改写,检索到的可能是一些完全不相关的公司介绍。
比较实用的做法有以下几种:
- 关键词抽取:从用户问题中抽出核心实体词,比如公司名、产品型号、年月;
- 问题分解:如果问题包含多个意图,拆成多个子查询分别检索,再合并结果;
- LLM 改写:让大模型把口语化问题改写成适合检索的短句或关键词组合,这在 Agentic RAG 里很常见。
我实际项目中用得最多的是“关键词抽取 + 子查询合并”。举个例子,用户提问“M3 型号的主机在 Linux 环境下的安装注意事项”,我会抽取“M3 型号”“Linux 环境”“安装注意事项”三组关键词,分别召回不同的文档块,再做融合。
3.2 向量检索、关键词检索与混合检索的取舍
向量检索擅长语义匹配,但精确匹配弱;BM25 这类关键词检索擅长精确匹配,但无法处理同义词和模糊表达。两者各有死穴,所以实战中基本都要做混合检索。
简单说,混合检索就是:
- 向量检索得到一列候选结果;
- BM25 关键词检索得到另一列候选结果;
- 使用某种融合策略(比如RRF 倒数排名融合)把两列结果合并排序。
RRF 的核心思路很朴素:一个文档如果在多路结果中排名都靠前,那它应该更靠前。它不是直接比较分数,而是比较排名,这样就不受不同检索方式分数量纲不同的影响。
我见过不少项目直接跳过混合检索,只看向量召回效果。在小规模 demo 上可能看不出问题,但到了真实文档上,产品型号、编号这类强标识词往往会被向量模型“语义化”,导致精确匹配结果暴死。混合检索可以有效兜底。
3.3 重排序:从“看起来相关”到“确实相关”
向量检索拿回来的 top-k 是“粗略相关”。因为我们只依赖 embedding 的向量距离,而向量距离并不能完全代表相关性。这时候需要加一层reranker(重排序模型)。
常见的重排序方案:
- 用 cross-encoder 类模型(如 bge-reranker-base),把 query 和每个候选文档拼在一起计算相关性得分,比向量检索的 bi-encoder 更准;
- 也可以直接用大模型打分,但成本高、速度慢,适合对精度要求极高的场景。
重排序的工程要点是:先粗召回,再精排序。召回阶段拿 50~100 个候选,重排序阶段取 top 3~5 个进入最终 prompt。这样既保证了效果,又控制住了时间成本。
我再提醒一个细节:做重排序时,不要把“得分最高的候选”直接塞进 prompt 而不管长度。有些候选虽然相关,但信息冗余,多个高相关块可能来自同一章节,这时要按内容去重。
3.4 评估指标:Hit Rate 和 MRR 怎么看
聊 RAG 检索效果,绕不开评估指标。网上一搜全是名词,我用自己的理解说清楚:
- Hit Rate(命中率):在 top-k 个检索结果中,是否出现了“正确文档”。如果有,就算 hit。计算时就是“命中的问题数 / 总问题数”。
- MRR(平均倒数排名):不仅关心是否命中,还关心正确结果排在第几位。排第一得 1 分,排第二得 0.5 分,排第三得 0.33 分,取平均。
- NDCG:综合考虑排序位置和相关性层级,适合有多级相关性标注的场景。
我在项目里先看 Hit Rate,再做人工抽检看 MRR。如果 Hit Rate 高于 90%、MRR 高于 0.7,我才会进入下一步——即调 prompt 和 Agent 行为逻辑。评估集是花时间建的:从真实用户问题里挑 100 条,人工逐个标出应该召回哪段文档。这个投入不该省。
4. 从基础 RAG 到 Agentic RAG:管道开始拥有判断力
基础 RAG 是“用户问 → 检索 → 拼上下文 → 回答”的直线流程。但如果在 Agent 体系里,管道必须更加智能——因为 Agent 需要自主决定“该不该查”“查什么”“查不到怎么办”。这就进入 Agentic RAG 的讨论范围。
4.1 传统 RAG 的痛点:只会查,不会想
我搭过一个小型客服 Agent,基础 RAG 上线后最大问题是:当知识库里没有相关答案时,它要么硬编一个回答,要么随便拿一段低相关文档凑数。用户追问“你确定吗”,它还会重复错误答案。
根本原因是基础 RAG 的检索是“只进不退”的:检索不到就结束,不会尝试改写问题,不会去别的知识库,更不会承认不知道。
4.2 Agentic RAG 的典型模式与适用场景
Agentic RAG 的核心,是把检索动作从“一次调用”变成“一个可决策、可循环、可纠错的动作”。
常见的套路有:
- Self-RAG:大模型在生成时自我反思,判断检索结果够不够、需要不需要二次检索;
- Corrective RAG(CRAG):如果初始检索置信度低,先尝试轻量级修正(比如改写 query),再决定是否需要换检索源;
- Adaptive RAG:让 Agent 自己决定“这道题需不需要检索”,以及“先检索还是先推理”。
从工程落地角度,我建议不要一上来就上复杂的 Self-RAG 架构,而是先用“粗糙的规则 + 反馈回路”:
- Agent 先判断问题是否需要外部知识,如果是纯常识/计算题,直接回答;
- 需要检索时,执行检索并拼接上下文;
- 让 LLM 对检索内容做“是否足够回答”的打分判断;
- 如果不足,改写 query 再查一次,或者告知用户当前知识库里没有相关信息。
这样实现起来并不复杂,但效果提升非常明显——至少它知道什么时候应该“承认不知道”了。
4.3 知识管道与 Agent 工具、记忆的协同
知识获取管道不是孤立存在的。在 Agent 内部,除了 RAG 管道,还有短期记忆(对话上下文)、长期记忆(用户偏好和历史事实)、工具调用(实时查询外部 API)。它们之间要有一个清晰的协同逻辑。
我个人的实践结论是:
- 动态事实,比如天气、库存、价格,优先走工具或 API,不要写进知识库;
- 相对稳定的规则和文档,比如政策、说明书、FAQ,走 RAG;
- 跨会话的用户偏好,走长期记忆存储,不参与 RAG 索引。
这样分层的价值在于,Agent 不会被“拿到什么用什么”冲昏头脑,而是按知识的新鲜度和稳定度选择合适的来源。
5. 知识管道的进阶形态:GraphRAG 与 Ontology RAG
当文档之间不是简单堆叠,而是存在大量关联关系时,传统向量 RAG 就会暴露出不足。我最近两年越来越多地接触到 GraphRAG 和 Ontology RAG 这两个方向,它们的共同点是给“知识片段”增加结构。
5.1 GraphRAG:从“相似匹配”到“关系推理”
GraphRAG 的思想很直接:把文档里的实体和关系抽取出来,构建成知识图谱,检索时不只是找相似文本块,还能沿着关系路径做推理。
举个例子,如果用户问“A 产品影响了哪些 B 产品的兼容性”,传统 RAG 需要文档中恰好出现这句描述;但 GraphRAG 可以先找到 A 产品节点,再沿着“影响”“被影响”关系找到 B 产品节点,哪怕没有任何一句话直接写了这句话。
GraphRAG 的代价也很明显:构建图谱成本高,实体抽取质量直接决定图谱质量,不适合快速迭代的小项目。适合的典型场景包括:大量互相关联的设备参数、法规条款之间的引用关系、代码仓库中模块间的依赖关系。
5.2 Ontology RAG:用领域本体约束检索边界
Ontology RAG 是另一个在我看来很有意思的方向。它引入领域本体(ontology),也就是对该领域概念、关系、约束的显式建模。检索时先通过本体确定用户问题中的实体和关系类型,再限定在对应的候选集合内检索,避免向量模型跑偏到其他语义区域。
我之所以会关注 Ontology RAG,是因为它在“专业领域限定检索”场景下非常有价值,比如医疗器械、法律合同,这些场景对术语精确度要求极高,普通 embedding 很容易把同义词搞混,而本体可以约束住边界。
不过这两个方向都还属于进阶玩法。基础 RAG 没跑通之前,不建议直接扑向它们。从工程优先级来看,先把 chunk、embedding、混合检索、重排序做扎实,再考虑图谱和本体。
6. 实战复盘:我从 0 到 1 搭建一个本地知识库 Agent
最后用一次实际的项目经历收尾吧。有一次我接到一个需求:把本地 ERP 里的产品数据和一个约 800 篇的运维文档库接通,让业务同事能用自然语言查询产品性能和安装注意事项。
6.1 方案选型与整体架构
考虑到数据在本地,有隐私要求,我选了完全本地化的方案:
- 文档解析:PyMuPDF + python-docx,配合 PaddleOCR 处理扫描版 PDF;
- chunk 切分:按标题层级做结构感知切分,目标 chunk 约 500 token,overlap 50 token;
- embedding:本地部署 BGE-large-zh,向量维度 1024;
- 向量存储:Milvus 的 standalone 模式,单机部署;
- 关键词检索:ES 里维护了一份与向量库同步的倒排索引;
- 重排序:bge-reranker-base,取 top 5;
- LLM 部分:本地部署的 Qwen 系模型,走 OpenAI 兼容接口。
整体管道就是:
问句 → Query改写 → 混合检索(向量+BM25) → RRF融合 → 重排序 → Prompt组装 → LLM生成6.2 落地过程中最容易拖垮效果的三个坑
第一个坑是表格数据。产品参数表中的型号名称被 chunk 切断后,检索经常只召回半行信息。最后我做了特殊处理:表格按行提取并生成“行级描述文本”,再进索引。这样一次检索能直接命中某个具体型号的参数。
第二个坑是同义词问题。用户口语中说“硬盘”对应文档里的“磁盘存储”,向量检索偶尔能匹配上,但频率不稳。我是在 query 改写阶段加了一层同义词扩展,从业务术语表里取同义词追加到检索关键词中。
第三个坑是长尾问题。总有那么 5% 的问题,知识库里根本没有答案。之前基础 RAG 会硬答,后来我在 prompt 里加了明确指令:“如果检索内容与问题不直接相关,必须给出‘根据现有资料暂无法回答’并建议用户找技术支持渠道”。效果比任何技术优化都立竿见影——用户差评率降了一大截。
6.3 效果评估与迭代方向
上线后我建了一个 100 条真实问题的评测集,记录了 Hit Rate 和单次检索耗时。基础版完成时 Hit Rate 大概在 76% 左右,主要丢分原因是 chunk 切分粗导致召回不聚焦。优化完切分、加入重排序后,命中率升到了 88%。后续加上了 Agentic 式的二次检索,整体稳定在 93% 左右。
单次检索耗时方面,混合检索加 RRF 加 rerank 的完整链路大约耗时 1.2 秒,接口整体响应在 4 秒左右(包含 LLM 生成),业务方可以接受。
这套管道说白了并不高深,底层全是成熟的组件,只要愿意把细节抠到位,基本都能复现。真正拉开差距的是对业务文本的理解,以及愿意花多少时间在切分、清洗和评测这些“脏活”上。
这也是我想在这篇文章里反复强调的一句话:RAG 没有魔法,它是一门需要反复校准的管道工程。知识获取管道越扎实,Agent 的地基就越稳。