把 AI Agent 从“会说话的玩具”变成“能办事的同事”,第一道坎往往不是模型能力,而是它能不能拿到对的知识。RAG(Retrieval-Augmented Generation,检索增强生成)就是解决这条知识获取管道的核心方法。这篇是“走进 AI Agent”系列的第四篇,我打算把 RAG 基础讲透:不绕弯、不堆名词,带你看清楚一条知识管道从文档到答案的完整链路,以及为什么很多团队把 RAG 做成了“能跑但不好用”。
如果你正在做企业内部问答、文档助手、智能客服、代码库问答,或者想把 Agent 接到本地私有资料上,这篇的内容会非常对味。它不会让你成为检索理论专家,但能帮你理解每个环节在干什么、出问题往哪儿查、怎样让一个 RAG 系统从“演示能用”走到“真的敢用”。
1. 先想清楚:Agent 的对话能力和“知道什么”是两回事
1.1 模型“知道”的边界
大模型的语言能力来自训练语料,这意味着它的知识有两个天然短板:一是截止时间,训练完之后发生的事情它不知道;二是覆盖范围,公司内部文档、个人笔记、竞品分析这类私有资料,几乎不可能出现在公开语料里。AI Agent 再会规划、再会调用工具,如果回答内容依赖的知识来源缺失,它就只能在空白处靠概率补一句出来——这就是“幻觉”最常见的来源。
我见过很多团队写 Agent 时先去调模型 prompt,把 system prompt 写到上千字,却发现该答错还是答错。原因很简单:prompt 只能约束模型怎么说话,不能凭空注入它没见过的知识。换一个更强的模型也不能解决私有资料的问题,除非把资料先“喂”给它。
1.2 为什么不把文档直接塞进上下文
有人会问:现在模型上下文窗口动辄 128K,把手册全塞进去不就行了?这个账要算清楚。首先,长上下文不等于高质量检索。把 100 页文档塞进去,模型注意力会被大量无关信息稀释,回答稳定性反而下降。其次,每次请求都带整篇文档,token 费用和延迟都吃不消。RAG 的思路是“先查再答”:从文档里找出与问题最相关的几个片段,只把这些片段拼进提示词。
这个动作像助手接到一个问题后先翻资料,翻到相关页面,摘出几段给你,而不是把整本书抱过来让你自己找。RAG 把“知识获取”从模型内部搬到了模型外部,做成一条显式的管道,好处是知识可以随时更新,出错了还能追溯到源头。
1.3 本篇的适用边界
如果你做的是企业内部问答、文档助手、智能客服这类“回答依赖特定资料”的场景,RAG 是绕不开的底座。如果只想闲聊、写文案,那 LLM 自带的知识就够了,RAG 反而多余。所以先判断需求:你的 Agent 回答问题时,需不需要查一个模型没见过的资料库?需要,就继续往下看。
2. 知识获取管道的第一站:把文档变成可检索的片段
2.1 数据清洗:知识管道的隐性成本
很多人搭 RAG 的第一个动作是安装向量库,这是不对的。真实业务里的 PDF、Word、网页、表格,重复、过期、版式混乱到处都是。如果“最终版”和“历史版本”混在一起,检索结果会互相打架。有一次我导入一批产品文档,同一款设备出现了三份不同时期的规格表,用户问“重量多少”,检索结果一会儿给 1.2kg,一会儿给 1.5kg,模型反而不知道听谁的。
我建议在建立索引前至少做四件事:去重(标题加内容哈希)、解析(按需要的版式提取标题、正文、表格)、统一编码(GBK/UTF-8)、打上元数据(来源、部门、日期、权限级别)。元数据尤其重要,后续做过滤检索、权限隔离全靠它。比如“权限级别”这个字段可以直接拦住不该被检索到的内容。
2.2 切分策略:chunk 大小不是拍脑袋
接下来是把长文档切成片段,这一步直接决定检索粒度。片段太大,内部主题混杂,向量表示被稀释,经常出现“检索到整个段落,但答案只有半句在里面”的情况;片段太小,上下文断裂,模型无法理解前因后果。常见经验是 512 到 1024 token 一个 chunk,相邻 chunk 保留 50 到 200 token 的重叠,防止一个完整语义单元被硬生生切断。
如果文档结构清晰,我更推荐按 Markdown 标题、段落、表格行来切,而不是死板地数 token。还有一种是父子块切分:先按大段切,让大段生成摘要或关键句作为索引,检索命中大段后,再把大段内部的更细子块交给模型生成。这相当于“先定位章节,再细读小节”,命中率和生成完整性都能兼顾。
| 切分方式 | 适用场景 | 优点 | 坑 |
|---|---|---|---|
| 固定 token 切分 | 快速原型、无结构文本 | 简单,不依赖解析 | 容易切开语义完整句子 |
| 结构感知切分 | 有标题、层级的文档 | 语义单元完整 | 依赖解析质量 |
| 父子块切分 | 片段较碎、答案需要上下文 | 命中准且生成完整 | 存储和索引量变大 |
2.3 向量化与向量库选型
片段准备好之后,需要把文本变成向量。Embedding 模型的选择,我的原则是不要只看榜单分数,要看领域和语言。中文通用场景可以选开源模型,比如 bge-m3、text2vec;英文或代码场景可以选对应的开源或商用模型;预算充足且数据不出内网,用本地方案;如果不介意数据出境,直接调云服务 API 也可以。需要重点提醒的是:线上使用的 embedding 模型一旦换了,所有已入库的向量都得重新生成,所以选型要早定,定完别频繁换。
向量库负责存放这些向量并提供相似度检索。对初学者,FAISS 或 Chroma 足够起步,数据量到百万级、需要团队协作,再看 Milvus、Weaviate 或 pgvector。选型时优先关注三件事:是否支持过滤检索、是否自带混合检索、方不方便做多租户隔离。框架可以换,向量里的数据迁移很麻烦,慎起点选型没坏处。
下面给一个最小入库流程的示意代码,我用的是 Python 生态里的 LangChain 加 FAISS,你只要把知识文档换成自己的文件就能跑通。代码里每一步都对应前面讲的清洗、切分、向量化、入库四个动作,实际落地时按需替换组件即可:
from langchain_community.document_loaders import TextLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_community.embeddings import HuggingFaceEmbeddings from langchain_community.vectorstores import FAISS docs = TextLoader("knowledge.md").load() splitter = RecursiveCharacterTextSplitter(chunk_size=512, chunk_overlap=50) chunks = splitter.split_documents(docs) embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-m3") vectorstore = FAISS.from_documents(chunks, embeddings) vectorstore.save_local("index")走到这步,你已经有了“可检索的知识库”,但先别急着庆祝,真正的考验在检索质量。
3. 检索的关键:别只靠向量相似度
3.1 相似度检索的失灵场景
向量检索很好用,前提是查询和文档在语义上“说得来”。现实问题往往不满足这个前提。举个典型例子:文档里写的是“GFC-202”这种产品编号,用户问“那个金属壳的稳定器怎么样”,向量模型很难把编号和描述关联起来;再比如原始文档用“checkout”表达结账流程,用户口语问“那个买完东西怎么付款来着”,如果只做向量匹配,召回结果会非常飘。错别字、方言、口语化表述,对纯向量检索都是减分项。
这不是向量模型不行,而是它擅长“意思相近”的匹配,不擅长“关键词精确命中”。产品编号、人名、标准术语这类高 precision 的信息,靠语义向量时灵时不灵。所以一个成熟的 RAG 不会只挂一个检索器。
3.2 混合检索:关键词和语义一起上
混合检索的做法是把 BM25 和向量检索的结果合并。BM25 做关键词精确匹配,对编号、缩写、标准术语的召回非常稳;向量检索做语义扩展,解决“换了说法也能找到”的问题。两者互补,效果通常比任何单路都好。
结果合并时常见算法是 RRF(Reciprocal Rank Fusion,倒数排名融合)。原理很简单:把每个文档在两路结果里的排名取倒数求和,作为融合分数,再按分数重排。假设向量检索里排第 1 的文档,在 BM25 里完全没出现,它就不会被 BM25 的结果拖累;而 BM25 里排第 2 的文档,在向量里可能排到第 50,融合后也能排上来。RRF 的好处是不需要调两路分数到同一量纲,直接用排名算,实现成本很低。
3.3 重排序(Rerank)值得尽早引入
召回阶段一般会拉回 Top 20 到 50 个片段,但最终进入提示词的只有 3 到 5 个。问题在于:向量检索的排序能力有限,经常把“相关但不够相关”的片段排在前面。解决办法是加一个 rerank 模型,对召回结果再做一次精排。rerank 通常用交叉编码器,把“问题+文档”拼在一起算相关度,比单纯的余弦相似度精细得多。
我在项目里实测的感受是:一个质量不错的 rerank 模型,能让最终回答的可用率提升一个级别。成本也完全可控,因为只对 Top 50 排序,耗时通常几十到几百毫秒,token 开销也不大。属于 RAG 管道里“花小钱办大事”的典型环节。
3.4 用 Hit Rate 说话,别靠感觉调参
检索质量不能凭感觉,要建一个评测集。最简单的方法:准备几十条真实问题,每条标注“对应哪篇文档的哪个 chunk”,然后跑检索,看 Top N 结果里能不能找到正确答案。指标通常叫 Hit Rate@N,另外还有 Recall@K、MRR。下面是一个假想的小对比,数字不是标准答案,但能反映趋势:
| 检索策略 | Hit Rate@5 | 说明 |
|---|---|---|
| 纯向量检索 | 0.61 | 口语化问题大量漏检 |
| 向量 + BM25 | 0.78 | 关键词命中补回不少 |
| 混合 + Rerank | 0.87 | 排序质量明显提升 |
这类评估不需要做成很重的平台,一个 CSV 文件加一段脚本就能跑。有了它,你改动切分大小、换检索器时才知道是变好了还是变坏了。我最常看到的问题就是:团队凭印象调参,调完感觉“好像好了一点”,实际上一跑评估集,指标没变甚至下降。
4. 生成环节:把检索结果变成可引用的回答
4.1 提示词里怎么拼检索片段
检索到片段之后,下一个关键决定是“怎么把这些片段交给大模型”。直接把 Top 5 的原始文本拼接丢给模型,效果通常不好,因为模型不知道你要它做什么。我的提示词结构大概是:先说明它是“知识库问答助手”,只能根据给定资料回答;再列出资料片段,每个片段带编号和来源;最后要求模型优先使用资料内容、不要自行补充、引用时标注编号。
一个简化模板长这样:
你是知识库问答助手。请只根据以下资料回答问题。 资料1(来自:产品手册2026版): ... 资料2(来自:FAQ-退货流程): ... 如果资料里没有答案,直接回答“资料中未找到”,不要编造。 请在回答中标注依据,格式如[1]。这里有个很容易犯的错:资料太多会超 token,太少又不够答。一般控制在 3 到 5 个片段,总 token 在模型上下文预算的 20% 以内比较稳。剩下的上下文要留给模型推理和回答本身,全塞满了,回答质量会急剧下降。
4.2 引用和溯源是 RAG 的信用基础
为什么一定要加引用?因为 RAG 面向真实业务时,用户和审核者都需要验证回答来源。没有引用的回答,哪怕内容是对的,也像模型胡编的;一旦有了引用,用户敢信,你也能快速定位是哪份文档给出的信息。
实现上不复杂:在提示词里给片段编号,要求模型在输出中标 [1]、[2],返回结果时把编号映射回文档 ID 和 chunk ID。前端可以做“回答来源”折叠面板,点击跳转原文位置。这一步让 RAG 从“看上去能用”变成“真的敢用”。我听不少团队说“内部工具最需要的就是这个”,一点不夸张。
4.3 多轮对话:不能只拿最后一句话去查
多轮对话是另一个大坑。用户先问“第一季度退单率怎么样”,你答了;用户追问“那第二季度呢?”,如果你拿“那第二季度呢”去检索,什么都查不到。正确做法是把对话历史和当前问题合并,生成一个独立的检索查询,比如“第二季度退单率是多少”,这步叫 query rewriting,也就是热词里常说的“查询改写”。
多轮里还有一个上下文管理问题:每轮都塞全部历史,token 会爆。务实的做法是只保留当前话题和最近一轮交互,必要时用模型把历史压缩成几个要点。对话类 Agent 的记忆管理和 RAG 的检索查询生成要分开做,别混在一起。
4.4 文档更新后索引怎么办
业务文档是活的,会改版、会新增。最粗暴的做法是整库重建,但文档多了成本很高。务实做法是:写入索引时记录来源和更新时间,更新时按来源先删除旧 chunk,再入库新 chunk,避免新旧版本在库里并存。这个生命周期管理很枯燥,却是生产级 RAG 的隐形门槛。早期不做好,后面文档一多个,回答里随时可能出现已经废止的旧制度,后果很麻烦。
5. 从 RAG 到 Agentic RAG:知识管道如何演进为工具
5.1 静态 RAG 的局限
上面这套流程是“用户提问 -> 固定检索 -> 固定生成”的管道,在单一知识库、固定场景下够用,但现实问题往往不这么乖。比如客服场景里,“今天天气怎么样”和“退货流程是什么”压根不该走同一套检索逻辑;再比如用户问“公司最近有哪些新产品”,答案可能分散在研发文档、公告、产品手册三个库,一次检索搞不定。
静态 RAG 对这类需求很僵硬,于是有了 Agentic RAG:把检索本身变成 Agent 的动态决策。核心不再是“每轮都检索”,而是让 Agent 自己判断“要不要查、查哪个库、查完要不要再查”。
5.2 把 RAG 当成 Agent 的一个工具
在 Agentic RAG 里,RAG 是 Agent 工具箱里的一项能力,而不是固定的前置流程。Agent 会先分析用户意图,再决定是否调用知识库工具。比如用户闲聊就直接聊,不浪费检索;用户问政策,就调用“公司政策知识库工具”;用户问“有没有相关案例”,可能要先查案件库再查产品库,两次检索的结果汇总成答案。
热词里“skill 怎么和 RAG 结合起来”说的就是这个点:你可以把不同领域知识库封装成不同的 skill,比如 HR 政策、技术支持、代码库问答,每个 skill 内部是 RAG,外部由 Agent 统一调度。这样知识隔离更清晰,权限管理也更方便。
5.3 从向量库走向知识图:GraphRAG 与 Ontology RAG
如果问题只要求“查一段原文”,向量检索足够。但如果问的是“这些资料之间有什么关系”,比如“总结所有产品线的共同卖点”,单片段检索会漏掉隐藏的结构化关系。GraphRAG 的做法是:先对整个文档集做实体抽取和关系构建,形成一张知识图谱,再基于图社区生成摘要。用户问全局性问题时,直接在图里找答案。Ontology RAG 更进一步,用领域本体来规范实体和关系,让检索受控于业务语义,适合企业里对准确性要求极高的场景。
不过要泼一盆冷水:GraphRAG 和 Ontology RAG 的实现成本比基础 RAG 高不少,需要建图、维护本体、处理实体抽取误差。普通团队我建议先跑通基础 RAG,等真的遇到全局性问题、单条检索解决不了的时候,再考虑升级路线,不要一上来就上重方案。
6. 最小可行 RAG 的落地细节与常见坑
6.1 技术选型:按团队语言定
RAG 的技术选型没有标准答案,尽量跟着团队已有技术栈走。Python 为主,LangChain 或 LlamaIndex 生态最顺;Java/Spring 后台团队,LangChain4j 或 Spring AI 的 RAG 支持已经比较成熟,热词里“spring cloud + spring ai 开发自己的 agent”就是这类方案。第一次搭的时候,我建议优先选文档多、社区活跃的框架,遇到问题好搜,别先纠结“谁性能最好”,先跑通一个最小闭环,再逐步替换组件。
6.2 本地知识库的几个大坑
我踩过不少坑,挑三个最典型的说。第一个是 PDF 解析:扫描版 PDF 不做 OCR,检索到的内容全是乱码;带复杂表格的 PDF 经常把行列打散。第二个是编码问题:导入旧文档时没注意 GBK 编码,文本变成乱码,检索还找不到。第三个是版本不一致:换了 embedding 模型后没有重建索引,检索结果全是“看似相近实则无关”的片段。
这些坑的共性是:问题不在推理链路,而在数据管道。所以我在前面反复强调数据清洗、元数据、生命周期管理,真不是纸上谈兵。想在 LLM 层调参数之前,先检查一下数据层,往往一通百通。
6.3 建立评估闭环,别靠感觉迭代
最后是我最想强调的:RAG 项目要可迭代,必须有评估集。准备 30 到 50 条真实用户问题,标注答案所在文档,每次改动切分、检索器、提示词,都跑一遍评估。没有评估集的时候,人会陷入“这次改完觉得好,下次又觉得坏”的循环,最后什么都不敢动。
评估脚本不需要复杂,一个小函数就能统计 Hit Rate。实践中我推荐的迭代顺序是:先保证“资料里没有答案时模型能正确拒绝”,再追求“有答案时答得准”。前者能挡住大量幻觉投诉,后者决定口碑。这两点稳住之后,再加图形化界面、权限管理、日志回溯才有意义。
最后聊一点个人感受。做 RAG 久了你会发现,工具链变化很快,但真正的工程难点始终在两头:数据准备和质量评估。很多团队把时间花在换模型、换框架上,效果却不稳定,多半是没把这两件事做成闭环。我的建议是,第一次练手别选太复杂的场景,找一份封闭文档集,把检索、生成、评估跑通,再逐步叠加 agentic 方向。RAG 看起来是给模型接知识的管道,实际上接的是你对数据工程和评测体系的理解。打通了这条管道,后面再做 Agentic RAG、GraphRAG 时心里才有底。