做 AI Agent 应用,最难的不是让大模型会聊天,而是让它知道业务知识从哪里来。我见过太多 Agent 项目,前面意图识别、工具调用都跑通了,一到真实业务问题就翻车——模型训练时没见过你们公司的产品目录,也没读过你们最新的售后规范,你让它怎么答?这就是知识获取管道要解决的问题,也是 RAG 真正发光的地方。RAG(Retrieval-Augmented Generation,检索增强生成)是当前落地最稳的方案:先把知识切成块、做成索引,等 Agent 接到问题时去检索相关资料,再把资料塞给大模型让它基于资料作答。这篇是 AI Agent 系列第四篇,我会带你把一条完整的 RAG 管道走一遍,从索引、检索、生成到排查,再聊到 Agentic RAG 和 GraphRAG 这些进阶方向。文章面向两类人:刚开始接触 RAG 的开发者,以及已经跑通 Demo、但搞不明白为什么 Hit Rate 上不去的应用工程师。
1. RAG 在 AI Agent 里的定位:知识获取管道的三种接法
1.1 为什么大模型“背课本”解决不了业务知识问题
大模型的参数知识是它的“课本记忆”,训练截止日期之后发生的事、企业内部规范、某个项目的私有数据,它一概不知道。模型参数再大,也只能在高层面上“猜一个大概”,一猜就容易一本正经地胡说。给 Agent 配 RAG,本质上是给它配一间可以随时查阅的档案室。
我习惯用一个类比去解释这件事:你招了一个名校毕业的高材生,什么知识他都能聊两句,但他刚入职,不知道公司的报销流程,也不知道你们正在做的项目代号。你不可能让他瞎编一套流程出来,正确的做法是让他自己去查内部 Wiki 和手册。RAG 就是那本手册,还是可以按需翻页、自动定位的那一种。
在 Agent 的整体架构里,RAG 的角色需要再拆细一点。Agent 的决策靠大模型,行动靠工具调用,而 RAG 是把“非参数化知识”灌进决策过程的管道。它本身不是 Agent,更不是记忆系统,它是 Agent 的“知识后端”。如果你把这个定位搞混了,就会出现两种典型问题:一种是把所有问答都塞给 RAG,结果无关问题也检索半天;另一种是让 Agent 裸奔,什么问题都靠模型瞎猜,然后怪模型不够聪明。
1.2 三种接法:同步调用、路由分发与 Agentic RAG
RAG 接入 Agent 的方式,行业内能看到三类典型形态,我建议按顺序演进,不要一上来就追求最复杂的。
同步调用模式最直接:Agent 每次收到用户问题,都先去知识库检索一遍,再把检索结果随 Prompt 一起发给大模型。适合知识库数量少、对话问题几乎都依赖文档的小工具。缺点是无关问题也会走一遍检索,既浪费延迟又抬高了成本。
路由分发模式是把 RAG 当成一个可选路径:先做意图识别,判断这个问题需不需要走知识库。比如“今天天气怎么样”直接调天气工具,“公司报销上限是多少”走 RAG。Spring AI、LangChain4j 里都有类似路由的抽象,本质上是给 Agent 一段决策逻辑:什么情况调用检索技能,什么情况不用。路由逻辑写多了会变得很繁琐,所以一般只在意图边界清晰时使用。
Agentic RAG则是把检索决策完全交给 Agent 自己。Agent 收到问题后,自己决定要不要查、查什么、查几轮,检索结果不够就再查一次,甚至能把一个大问题拆成多个子查询,分配不同的知识库去查。这是当前企业级平台最流行的做法,但调试难度也最大,因为你要同时面对 LLM 规划不稳定和检索质量波动两个变量。
我给团队的建议永远是:先按同步调用跑通端到端,再根据业务需要加路由,最后再上 Agentic RAG。因为每一步都要有评估数据做支撑,跳级容易死得不明不白。
| 接法 | 典型场景 | 优点 | 主要风险 |
|---|---|---|---|
| 同步调用 | 文档问答工具、内部客服助手 | 实现简单,效果可控 | 无关问题也检索,资源浪费 |
| 路由分发 | 复杂业务系统多工具并存 | 能省成本,路径清晰 | 规则膨胀后维护困难 |
| Agentic RAG | 知识密集型企业平台 | 灵活,能处理多跳问题 | 规划不稳定,调试成本高 |
2. 索引阶段实操:文档清洗、切分策略与 Embedding 选型
2.1 文档加载与清洗:PDF、表格和爬虫内容的处理
索引阶段是整个 RAG 管道里最不起眼、但最决定上限的环节。资料类型五花八门:PDF 多栏排版、Word 带批注、HTML 带导航,还有从本地 ERP 导出的产品型号、规格属性这类结构化表格。清洗的目标是去掉噪声、保留语义。
我处理过最多的两类资料,一类是 PDF,一类是表格类导出文件。PDF 用 pdfplumber 处理带表格的页面比较顺手,PyMuPDF 速度快但复杂表格容易乱;扫描件就得先 OCR,PaddleOCR 和 Tesseract 是常用选择。通用抽取可以走 unstructured 这个库,它能把 PDF、Word、HTML 转成相对干净的结构化元素。爬虫抓到的网页,用 BeautifulSoup 或 Readability 提取正文,比直接拿整段 HTML 去切要可靠得多。
有个常见的坑必须提醒:PDF 里的表格一转出来,单元格内容会被拼成一行空格文本,看着像模像样,切分之后语义全碎。清洗完一定要人工抽检 10 到 20 篇文档,别只看前几页就觉得没问题。页眉页脚、目录页码、版权声明这些内容要么删掉,要么单独存成元数据,别让它混进正文参与切分。
2.2 切分策略:块大小、重叠参数怎么定才有效
切分的目标是让一个块尽可能覆盖完整的语义单元。我见过有人直接按字符固定长度切,结果一句话被拦腰截断,问什么检索都召回不了。这里必须根据文档结构选择合适的策略。
固定长度切片最简单,通过 token 数量控制,设置重叠区避免信息被切断,但缺点是无法感知语义边界。递归字符切分是更实用的方案:优先按段落切,段落太长再按句子切,句子还太长再按子句切,LangChain 里的 RecursiveCharacterTextSplitter 就是这个思路。语义切分更进一步,用 Embedding 计算相邻句子之间的相似度,差异突然变大就断句切块,质量最好但处理时间长。
参数经验我这里可以直接给参考值:初始建议 256 到 512 token 一块,重叠区 50 到 100 token,具体一定要拿自己的评测集跑 Hit Rate 确定,别抄网上现成的。块太大,噪声多,低相关片段会把正确答案的占比稀释掉;块太小,单块信息不完整,靠多个片段硬拼,逻辑容易出现断层。我做过一个产品资料库,512 token 固定切分时 Hit Rate 只有 0.6 左右,改成 420 token 递归切分后升到 0.74,这个提升完全不需要换模型。
另外,切分时要把标题、文档路径、页码这些元数据写进块对象里,后面做引用溯源和过滤都用得上。切分这一步做得干净,检索和生成阶段的麻烦能少一半。
2.3 Embedding 与向量库选型:本地部署还是云 API
Embedding 选型是另一个关键决策,核心看三件事:语言覆盖、领域匹配度、硬件条件。
中文场景下,bge-m3、bge-large-zh、m3e 这些模型都值得试验;英文和通用场景,OpenAI 的 text-embedding-3 系列、bge、e5 都被广泛使用。做垂直领域,比如法律、医疗、金融时,通用模型未必最好,可以找领域微调过的模型,或者拿标注数据自己微调一版,投入产出比通常比换大模型更明显。
部署方式上,云端 API 省事,但数据出境和调用费用是隐患;本地部署要考虑推理速度,CPU 上跑一个 bge-large-zh,看每秒能编码多少条文本,这会直接影响文档更新时重建索引的耗时。
向量库选型不要迷信“越重越好”。我的经验是:数据量小、快速验证,用 Chroma 或 FAISS 就够了;数据量到百万级,再考虑 Qdrant、Milvus 这类专用向量库;如果业务系统本来就用 Postgres 或 Elasticsearch,直接利用它们的向量扩展能力更划算,少引入一个组件,运维成本低一大截。本地搭建 RAG 知识库时,很多人一上来就上全套组件,其实单机场景 FAISS 加脚本完全够用。
3. 检索阶段优化:混合检索、命中率指标与 Reranker
3.1 命中率低、排序不对怎么办:先看懂 Hit Rate 和 MRR
很多同学搭完知识库,随口问我“效果怎么样”,我第一反应是反问一句:“你的 Hit Rate 是多少?”如果对方答不上来,说明他还在凭感觉调参。
Hit Rate@K 的含义是:前 K 个检索结果里,是否包含人工标注的正确文档。比如 50 道评测题,每道题标注了“正确答案在第几份文档的第几段”,跑一遍检索管道后统计命中比例。Hit Rate@3 是 0.8,意思是 80% 的问题在前 3 个结果里能找到正确答案。这个指标直接从业务角度回答了一个问题:召回到底行不行。
MRR(Mean Reciprocal Rank)看的是排序质量:第一个正确答案的排名倒数,然后对所有问题取平均。假如一个问题第一个正确答案排在第二名,那这条就贡献 1/2 ≈ 0.5 的分数。MRR 低,说明答案“有被召回但排名靠后”,大概率要加 Reranker。
评测集怎么建也有讲究。从用户真实提问记录里筛 50 到 100 个高频问题,让业务人员标出答案来源,比你自己拍脑袋造问题靠谱得多。我做过最值钱的一件事,就是把线上客服的历史工单整理成了评测集,之后每一次改动都能用数据说话。
下面这张表可以直接贴在项目文档里做排查参考:
| 指标 | 观察要点 | 指标低时的排查方向 |
|---|---|---|
| Hit Rate@K | 正确答案是否被召回 | 索引缺失、切分过碎、Embedding方向不对、没有混合检索 |
| MRR | 正确答案排得靠不靠前 | 召回没问题但排序差,考虑 Reranker、调整权重 |
| Recall@K | 相关片段占比 | 知识库覆盖面不足、元数据过滤过严 |
3.2 向量检索加关键词检索的混合策略
向量检索的优势是语义相似,用户问“报销上限多少”和文档里“差旅费用报销标准”能匹配上。但越具体的信息越容易翻车:产品型号“ACS-300”、工单编号“E1234”、代码报错信息,这些都是精确匹配问题,向量空间里它们可能跟一堆无关文本纠缠在一起。这时候 BM25 关键词检索能救命。
混合检索的标准做法是:向量检索和 BM25 各自返回 TopK,分数归一化后按权重合并,一般向量 0.7 加关键词 0.3 起步,再根据线上数据调。LangChain 里可以组合多个检索器;LangChain4j 里也有类似混合检索的封装。如果你用 Spring AI,通常要自己实现两个来源的结果合并,逻辑不复杂,但要注意分数字段口径统一。
这里我有一个很直观的经验:某个型号检索场景,纯向量召回经常把“ACS-300”和“ACS-310”混在一起,加 BM25 之后精确型号的召回率明显提升。做一个知识库,别急着上什么高端算法,先搭一个混合检索再说,它能把底兜住。关键词分支的细节也值得注意,中文场景要配中文分词器,否则 BM25 按单字匹配,反而引入更多噪声。
3.3 重排器(Reranker)到底什么时候该加
向量检索本质是“粗筛”,把问题和一个块各自的向量编码,然后算余弦相似度。这种做法高效但信息量有限。Reranker 走得更远:它把问题和候选文档拼在一起,用一个交叉编码器逐字比较相关性,分数比向量相似度可靠得多。
我的落地习惯是:先用向量检索加 BM25 召回 Top30 到 Top50,再用 Reranker 从里面挑出 Top5 到 Top8 作为最终上下文。测试一个客服知识库项目时,Hit Rate@10 从 0.72 提升到 0.81,体感更明显的是回答不再“东拉西扯”,因为低分段噪声块被清掉了。
但 Reranker 不是无脑加。它每次都要对 query 和每条候选做一次模型推理,延迟和算力开销不可忽视。实时性要求高的场景,要先粗排再重排,控制好候选数量。开源模型方面,bge-reranker 系列在中文场景表现稳定。如果项目没有 GPU,就要认真评估 Reranker 带来的增益是否值得额外延迟,有时候把召回阶段做好已经能解决大部分问题。
4. 生成阶段落地:提示词构造、上下文压缩与引用溯源
4.1 系统提示词怎么写:让模型只信检索结果
检索做得再准,生成阶段提示词写得不行,照样会胡编。我的 System Prompt 里有几条铁规矩,写在这里可以直接抄作业:
你是一个企业知识助手,请严格基于下面的资料回答用户问题。 资料: [1] {来源文档标题} 第{X}页/第{Y}节:{检索片段} [2] {来源文档标题} 第{X}页/第{Y}节:{检索片段} 要求: 1. 只允许使用资料中提供的信息回答问题。 2. 如果资料不足以完整回答,请明确回复“根据现有资料无法完整回答”,并告知用户缺少哪些信息。 3. 回答时在关键句末尾用[1]这种编号标注信息来源。 4. 禁止编造资料中不存在的产品参数、价格、日期或人物。这几条约束的底层逻辑是:把“模型可能用参数知识脑补”的概率压到最低。尤其是第 2 条,等于给模型提供了一个合法的“不知道”出口。你不给它出口,它就倾向于硬编一个答案,这是大模型的本能。
提示词之外,温度参数也要配合。做知识问答场景,温度一般设置在 0.1 到 0.3 之间比较合适。温度拉太高,同一个问题每次答案都不一样,用户会怀疑你知识库是不是坏掉了。
4.2 上下文压紧与去噪:别把一堆低分块塞给模型
检索返回的块不一定都相关,直接全部塞进 Prompt,会让模型抓不到重点,还白白消耗上下文窗口。你给模型 8 个块,只有 3 个有用,回答质量一定下滑。
我常用三招解决这个问题。第一招是相似度阈值过滤:比如余弦相似度低于 0.4 的块直接丢弃,阈值要根据你的线上分数分布统计出来,不要拍脑袋。第二招是用 Reranker 分数截断,只保留 TopK 里足够可靠的那几个。第三招是 LLM 压缩:让模型从候选块里抽取与问题相关的句子,再喂给主回答链路,相当于在检索和生成之间加了一层清洗。
还有一类场景要特别提一下:某个问题的关键信息分散在多个段落里,单块检索只能拿到其中一个片段。这时候“父子文档”模式值得用:检索小 chunk,把包含它的大段落完整取回,既保证精准定位,又保留上下文。但要注意,这会带来更大的 Prompt token 量,延迟和成本都要提前算好。
4.3 引用溯源和“不知道”机制是怎么配合的
企业场景里,用户和审核方都要验证答案的可信度。没有引用的 AI 回答,就像没有出处的新闻,很难让业务部门买账。我建议做三层设计:第一层,chunk 对象里保存 doc_id、标题、页码、章节号;第二层,提示词要求模型回答时带编号引用;第三层,后处理阶段把编号映射成文档链接,让用户可以点击跳到原文页面。
“不知道”机制也要配合问题改写。系统提示词里规定找不到就直说,但实践层面我更推荐先补救一轮:拿原始问题去检索,如果最高分数低于设定阈值,用大模型把问题改写得更具体,再检索一次。最多做一两轮改写,再多成本就失控了。这样处理之后,真正回“资料不足”的次数会明显减少,用户体验好很多。
顺带说一个工程上的做法:在 Agent 里,RAG 完全可以注册成一个工具函数,Agent 需要时调用 search_knowledge(query),而不是每次强制检索。这样检索行为就跟其他技能一样,可以被 Agent 自主调度,这也是 Skill 和 RAG 结合比较自然的切入方式。
5. 常见问题与排查技巧实录:从症状到根因的排障表
5.1 经典翻车现场:答案没召回、回答乱编、更新不生效
我整理了一份排障速查表,基本覆盖了日常维护中最常碰到的几类问题:
| 症状 | 可能原因 | 排查手段 |
|---|---|---|
| 知识库里明明有答案,但召回不到 | 切分把关键信息切碎、Embedding 匹配不理想、没做混合检索 | 把标准答案定位到具体块,打印该块的相似度得分,单独看检索日志 |
| 召回内容不少,但模型回答乱编细节 | TopK 太大、低分噪声块混入、Prompt 约束弱 | 降低 TopK、加 Reranker、提高相似度阈值、增强 System Prompt 约束 |
| 文档更新后,回答还是旧版本 | 向量库没有同步更新、索引未重建、有缓存 | 建立增量更新管道,监听文档变更事件,重建后校验版本号 |
| 相似度分数整体偏高或偏低 | Embedding 空间分布差异、归一化未统一 | 对分数做分位数统计,用分位数作为阈值,而不是绝对分数 |
| 中文按关键词查不中 | 没配中文分词器 | 按 ik/jieba 这类中文分词方式重新构建 BM25 索引 |
这里面最容易被忽略的是“文档更新不同步”。很多团队把知识库当成一次性工程,文档改了,向量库里还是旧向量,线上用户问到的答案永远是过去时。解决方式很简单:chunk 里记录文档版本号或更新时间,增量更新模块检测到源文件变化后,把受影响的块重新切分、重新 Embedding,再写入向量库。这件事听起来基础,但没做好的项目一抓一大把。
5.2 知识割裂问题:一个跨越多个文档的难题
“知识割裂”是我在大型企业知识库项目中反复遇到的场景。举个例子:公司资料里,“产品 A 的保修期”写在销售手册里,而“保修期内故障处理流程”写在一份技术文档里,客户问“产品 A 保修期内坏了怎么办”,需要同时用到这两份文档的知识。单一 RAG 管道如果只做相似度检索,往往只召回其中一个。
处理思路有三条。第一条是在元数据层面做关联:用产品型号把销售手册和技术文档对齐,检索时允许按型号扩展。第二条是切换到 GraphRAG 思路,把文档中的实体关系抽成图谱,检索时沿着关系扩展上下文。第三条是把问题做 query 拆分,先识别出问题涉及“保修期”和“故障处理”两个子主题,分别检索再合并。这三条不需要同时上,但你要知道单纯调大 TopK 解决不了知识割裂,因为割裂的本质是信息分散在多个文档、多个语义簇里。
5.3 中文场景的两个额外坑
中文环境的 RAG 有两个容易被忽略的坑。第一个是分词问题:BM25 这类词频算法依赖分词质量,英文天然按空格分词,中文必须选好分词器,否则“汽车保险”会被切成一堆单字,匹配结果自然乱七八糟。第二个是中文 Embedding 模型的领域差异:通用中文模型在新闻语料上表现不错,但换到机械设备参数、法务条款这类专业文本,分数分布可能完全不对,这个时候一定要用业务语料做验证,必要时做领域微调。
还有一个小提醒:同一个知识库如果内容横跨多个领域,比如既有产品规格表,又有售后流程,建议分别建立索引或知识点,在路由阶段就分流,而不是混在一个向量库里。这样既降低互相干扰,也让后续维护时能独立更新。
6. 从基础 RAG 走向 GraphRAG 与 Agentic RAG
6.1 基础 RAG 在多跳问题上的软肋
基础 RAG 擅长“根据相关文本直接回答”,但遇到多跳问题时容易力不从心。多跳问题是什么样的?比如“供应商 A 给项目 B 供过货,那么项目 B 的负责人是谁”,需要从供应商关系跳到项目信息,再跳到负责人员工。普通 RAG 拿这个问题去匹配,单次检索基本得不到完整链路。即便把问题拆成两个查询,两段搜索结果之间怎么拼接、怎么证伪,也是个麻烦。
另一个软肋是全局性问题。GraphRAG 的定位很好地补足了这一点:当用户问“整个产品线里哪些型号的售后投诉率偏高”这种需要跨文档统计的问题时,纯向量库很难给出完整答案。
6.2 GraphRAG 与 Ontology RAG:用关系补足语义
GraphRAG 的核心做法是:先让大模型从文档里抽取实体和关系,构建成知识图谱,再把相似实体聚成社群,最终面向社群做全局摘要。它的优势是能回答需要全局视角、关联关系的问题。代价也很直观:构建图谱的 LLM 调用量远高于普通切分 Embedding,处理一大份文档可能要跑很久。所以我一直提醒团队,别迷信 GraphRAG,大多数知识库其实只需要基础 RAG。
Ontology RAG 更强调“先定义一套领域本体”。比如产品知识库先把“产品型号、所属系列、防护等级、适用场景”这些概念和等级关系定义清楚,检索时按本体做查询扩展和下钻。用户问“户外高温场合用哪个型号”,本体允许你沿“适用场景→户外→高温”这条路径展开查询。对于本地 ERP 加产品检索这类结构化和半结构化数据混合的业务场景,Ontology 的价值能发挥得很明显。
6.3 Agentic RAG 怎么落到工程上
Agentic RAG 的落地套路大致分两种。一种是检索规划型:Agent 先判断当前问题是否需要查知识库、需要查哪几个知识点,把大问题拆成子查询,逐个子查询做检索,最终汇总结论。另一种是反馈循环型:Agent 读了一遍检索片段,觉得证据不足,再发起一轮检索,换关键词或者换个角度继续查。
工程上有几个硬约束必须设好。限制最大检索轮数,我一般设 2 到 3 轮,避免死循环和成本爆炸;每一轮检索的 query 和来源都记录日志,方便审计和复盘;决定“要不要再查”的条件尽量明确,比如置信度低于阈值,或者关键信息之间有矛盾。LangChain4j、Spring AI 这类框架组合起来,都能把 RAG 检索能力以工具函数的形式暴露给 Agent,这也是企业级 Java 平台里常见的模式。很多智能体产品在做盘点时,会把这类“把检索封装成独立服务”的架构称作 RAG as a Service,本质就是把知识获取从业务代码里抽离出来,变成可复用、可治理的能力。
6.4 RAG 建好之后还要持续运营:知识服务是长期迭代的过程
最后想强调一个被低估的真相:RAG 管道建好不是结束,而是开始。文档会更新,用户问题的分布会变化,新的知识域会不断出现。你至少要有三套机制保证系统长期可用:增量文档更新管道、线上质量监控(比如每天抽样回答让业务人员打分)、以及一个持续积累的回归评测集。每当你发现一个新 badcase,就把它加入评测集,这样系统只会越用越稳。
这个工作不性,但很值得。我见过太多团队在 Demo 阶段惊艳全场,上线三个月之后因为无人维护,回答质量一路下滑,最后被业务部门弃用。RAG 不是一次性工程,它是一条需要持续运营的知识管道。
我在实际项目里最大的体会是:先把端到端最小闭环跑通,再谈优化。RAG 看起来三步就能搭起来,但每一环都是评估的产物——切分参数要调,Embedding 要换着测,混合检索的权重要试,Reranker 要不要上完全看业务对精度的要求。不要指望一次建成完美管道。
给一个非常实用的建议:新建项目时,先花两天把 50 道带标准答案的评测题做出来,把 Hit Rate、MRR 记下来。之后无论怎么改,都有对照数据。否则你很容易陷入“感觉比之前好多了”的幻觉。下一篇我会顺着这条知识管道继续往下聊请求路由、多知识库隔离和更新机制,有兴趣的可以持续关注。