先说个可能颠覆认知的结论:AI Agent 项目里最容易翻车的环节,往往不是规划、不是工具调用,而是最不起眼的“知识获取”。我做过好几个 Agent 练手项目,也帮朋友调过基于 LangChain 的知识库问答,最后发现大家踩的坑高度一致——模型明明很强,却总在问私有数据时一本正经地胡编。这就是为什么我把这个系列的第四篇专门留给 RAG,它才是 Agent 真正"长记性"的管道。
这篇文章我想认真聊聊知识获取管道的底层逻辑:大模型为什么离不开 RAG、一条完整管道里每个环节到底在干什么、以及用 LangChain 从零搭一条可用的 RAG 链路需要调哪些参数。适合正在从 0 到 1 搭建 AI Agent 的开发者,也适合那些已经被"检索效果差""回答胡说八道"折磨过、想搞清楚问题出在哪的实践者。
1. 为什么 AI Agent 的知识管道必然要聊 RAG
1.1 大模型的三个天然缺陷
先别急着写代码。你要真想把 RAG 玩明白,得先理解这个问题到底在解决什么。
大模型本质上是一个"参数化的知识压缩器"。训练完成后,它的全部知识都冻结在网络权重里,这就带来三个要命的缺陷。第一,知识有时效性:模型训练数据截止的那一刻,就是它认知的终点,你问它今年新发布的政策、新出的产品规格,它只能靠猜。第二,幻觉问题:当它不知道答案时,语言模型的天性不是"说不知道",而是"编一个最像答案的东西",这在对话里极难察觉。第三,无法感知私有知识:你公司内部的制度文档、项目周报、数据库里的业务数据,模型一概不知。
我见过很多团队试图用微调来解决知识问题,结果成本高、更新慢、还容易过拟合。微调适合让模型改变"说话风格"或"固定行为模式",但不适合让它记住海量、频繁更新的业务知识。这就好比让一个员工把整本百科全书背下来再考试,不如让他坐在书架前随时翻书。这个"翻书"的动作,就是 RAG。
1.2 开卷考试式生成
RAG 的全称是 Retrieval-Augmented Generation,检索增强生成。核心思路一句话:给大模型配一个外部知识库,回答前先检索相关内容,把检索结果和问题一起塞给模型,让它基于这些资料作答。
我说一个生活化类比。传统大模型是闭卷考试,模型凭记忆答题,遇到不会的题只能硬编。RAG 则是开卷考试,模型拿到试卷后,先到资料库里快速翻找相关章节,然后照着资料结合自己的理解写答案。这个类比足够说明一个关键点:RAG 的答案质量上限,取决于你递给模型的"资料质量",而不完全取决于模型本身。
在 AI Agent 架构里,RAG 的位置是"知识获取管道"。Agent 要感知环境、要决策、要行动,但所有决策都需要知识支撑。一个没有 RAG 的 Agent,等于一个没有工作经验的实习生,能力再强也不知道你的业务规则。
1.3 RAG 与微调的取舍
很多刚接触这个概念的人会纠结:到底该用 RAG 还是微调?我把两者的差异整理成了对比表,方便你做决策。
| 对比维度 | RAG | 微调 |
|---|---|---|
| 知识更新 | 换文档即可,秒级生效 | 需重新训练,周期长 |
| 硬件成本 | 普通 CPU 也能跑 | 需要 GPU 训练资源 |
| 知识时效 | 实时可控 | 冻结在模型参数中 |
| 适合场景 | 事实性问答、动态知识库 | 风格迁移、特定输出格式 |
| 幻觉风险 | 相对可控,但仍需评估 | 可能产生灾难性遗忘 |
| 维护成本 | 需维护索引和检索链路 | 需管理训练数据集和版本 |
我的经验是:事实性知识用 RAG,行为规范用微调。比如你希望 Agent 回答问题时语气热情一点,可以微调;但如果你希望 Agent 知道"公司年假规定",请老老实实做 RAG。两条路可以结合,不是互斥关系。
2. RAG 管道核心环节拆解:从文档到向量的加工流水线
2.1 文档加载与切分:chunk size 不是拍脑袋
一条标准 RAG 管道可以拆成离线索引和在线查询两段。离线索引阶段负责把文档变成可检索的向量数据库,在线查询阶段接收用户问题、检索相关片段、拼装 prompt 给模型。很多人在离线阶段就栽了,因为忽略了一个关键设计决策:文档怎么切。
原始 PDF 或 Word 不可能整篇塞进模型上下文,必须切成一段段文本,术语叫 chunk。切分看似简单,却是影响召回质量最深的坑。chunk size 太小,语义被切断,比如一句"虽然表面看起来没问题,实际上存在隐患"被从中间切开,后半段就完全丢失了转折语境;chunk size 太大,向量粒度太粗,精准匹配率下降,而且检索到的块会挤占上下文窗口。
实际操作中我建议从 300 到 800 个 token 的块大小起步,再配合 50 到 100 token 的重叠。重叠的意义在于:一个完整的语义单元无论落在哪个切分边界,都能被至少一个块完整包含。另外,不要对所有文档用同一套切分逻辑。Markdown 文件按标题结构切,代码库按函数切,长篇幅规章制度按段落切,这比纯字符长度切好得多。LangChain 里的 RecursiveCharacterTextSplitter 支持按分隔符优先级递归切分,是我最常用的工具,你可以自定义分隔符列表。
2.2 嵌入模型与向量数据库选择
切好的文本块需要转成向量,这个转换器就是嵌入模型(Embedding Model)。选嵌入模型主要看三个指标:维度、语义理解能力和语言适配性。中文场景下,我强烈建议用专门针对中文优化的嵌入模型,比如 BAAI/bge 系列或 text2vec,直接用通用英文嵌入模型处理中文文档,检索质量会明显打折。
向量数据库负责存储这些向量并支持相似度检索。市面可选方案很多,我把常见选择做成了一张选型表。
| 向量数据库 | 适合场景 | 扩容方式 | 备注 |
|---|---|---|---|
| Chroma | 本地小规模学习、原型验证 | 单机 | 轻量,适合练手 |
| FAISS | 纯向量检索库 | 需结合外部存储 | Facebook 开源,高性能 |
| Milvus | 生产级大规模检索 | 分布式 | 功能全面,运维稍重 |
| pgvector | 已有 PostgreSQL 的场景 | 随数据库扩展 | 省去额外基础设施 |
| Elasticsearch | 已有 ES 且需混合检索 | 自带分片机制 | 支持 BM25 与向量融合 |
我个人的项目实践排序是:先拿 Chroma 跑通原型,需要混合检索或持久化更强时迁移到 pgvector 或 Milvus,别一上来就整生产级分布式集群,容易把精力耗在运维上。
2.3 检索不是搜索:相似度、TopK 与重排序
很多人以为检索就是把问题向量和库里所有向量算一遍余弦相似度,然后返回最接近的。这个理解没错,但不够。向量相似度检索只解决"语义相关"问题,它不解决"精确匹配"和"答案片段定位"问题。
一个常见的坑:用户问"2024 年销售额是多少",如果你只做向量检索,库里的"2024 Sales Revenue"片段可能因为表述差异排在很后面。要解决这个,建议引入混合检索,也就是 BM25 关键词检索和向量检索并行,再通过 RRF 或加权方式融合结果。关键词检索擅长精确命中"型号、编号、人名",向量检索擅长理解"意思相近但表达不同",两者互补。
TopK 参数决定了查多少条候选片段送给模型。设太小,比如 TopK=2,可能漏掉关键信息;设太大,比如 TopK=10,又可能把不相关噪音塞进上下文,干扰模型判断。我的经验是先从 4 到 6 起步,观察回答质量后再逐个实验。此外,有条件就加一个重排序环节,用 cross-encoder 模型对候选片段重新打分排序,这一步经常能把回答准确率提升一到两成。
3. 实操过程:用 LangChain 搭一条可用的 RAG 管道
3.1 技术选型与准备
说了这么多原理,到了动手环节。当前主流框架里,LangChain 生态最丰富、教程最多,适合快速验证;Spring AI 则适合 Java 技术栈、想融入企业级应用的同学。我自己在练手项目里用 LangChain,在公司 Java 团队里推过 Spring AI,两者都能完成标准 RAG 链路。
下面以 LangChain 为例,给出完整链路。先准备 Python 环境,安装依赖。
pip install langchain langchain-community langchain-openai chromadb bge-reranker如果你本地有模型文件,也可以用 Ollama 或 llama.cpp 跑本地嵌入模型,避免调用外部 API 产生的费用和数据隐私问题。我强烈建议涉及公司内部数据时优先考虑本地部署,这不是技术问题,是合规问题。
3.2 索引构建代码
索引阶段分四步:加载、切分、嵌入、存储。我用一个示例展示核心逻辑。
from langchain_community.document_loaders import PyPDFLoader, TextLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.embeddings import HuggingFaceEmbeddings from langchain_community.vectorstores import Chroma # 1. 加载文档 loader = PyPDFLoader("./员工手册.pdf") documents = loader.load() # 2. 切分:按结构切,token 级控制 text_splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=100, separators=["\n\n", "\n", "。", "!", "?", ".", " ", ""], ) chunks = text_splitter.split_documents(documents) # 3. 嵌入:用中文优化的 bge-small 模型 embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-small-zh-v1.5") # 4. 存储:本地 Chroma,持久化到目录 vectorstore = Chroma.from_documents( documents=chunks, embedding=embeddings, persist_directory="./chroma_db", ) vectorstore.persist()这段代码里最值得留意的是切分参数。我建议把中文断句符号"。!?"加进 separators,因为它们对中文语义边界的判断比单纯空格和换行更准确。很多英文教程只写了 "\n"、空格,直接套到中文文档上,效果会差不少。
3.3 检索问答链路代码
查询阶段要做的是:把用户问题向量化、检索相似片段、构造 prompt、调用大模型生成答案。
from langchain.chains import RetrievalQA from langchain_openai import ChatOpenAI retriever = vectorstore.as_retriever( search_type="similarity", search_kwargs={"k": 5, "score_threshold": 0.6}, ) qa_chain = RetrievalQA.from_chain_type( llm=ChatOpenAI(model="gpt-4o-mini", temperature=0), retriever=retriever, return_source_documents=True, ) question = "员工请年假需要提前几天申请?" result = qa_chain.invoke({"query": question}) print("答案:", result["result"]) print("参考来源:", [doc.metadata.get("source") for doc in result["source_documents"]])这里有两个关键参数。第一个是 temperature:问答场景建议设为 0 或接近 0,因为你要的是确定性事实,不是创意发散。第二个是 score_threshold 相似度阈值:低于阈值的片段会被过滤掉,有效减少"强行回答无关内容"的情况。但阈值设太高也会导致"检索不到答案",这个值需要你根据自己的嵌入模型和文档情况来回试。
我还有一个建议:不要直接用 RetrievalQA 模板了事。项目后期我习惯自建 prompt,把检索片段和对话历史一起塞进上下文,让 Agent 同时具备记忆和知识检索能力。自建 prompt 也方便你加入"如果资料中没有答案,请明确说不知道"的指令。
4. 从基础 RAG 到 Agentic RAG:检索不再是单向动作
4.1 基础 RAG 的局限
如果你用基础 RAG 跑过真实业务,一定会撞上三个痛点。第一是单轮检索的局限:复杂问题需要多步推理,比如"对比 A 产品和 B 产品在售后服务上的差异",一次检索拿到的是同一批候选,无法分别针对"售后服务"和"A 产品""B 产品"精准取料。第二是问题改写缺失:用户的问题常常是口语化的,比如"这个怎么弄?"里的"这个"指代不明,直接检索命中率很低。第三是知识密度不足:单个知识库答不了跨领域问题,Agent 可能需要先查规章制度、再查操作手册、甚至调一次外部 API 才能拼出完整答案。
这些问题催生了 Agentic RAG,也就是把 RAG 从"一次检索、一次生成"升级为"由 Agent 编排的多步检索与工具调用过程"。
4.2 三种常见的 Agentic RAG 模式
我整理了自己实践过的三种 Agentic RAG 模式,欢迎参考。
查表式路由检索:Agent 先判断问题属于哪个知识域,再路由到对应的向量库。典型场景是企业同时有 HR 知识库、技术文档库和财务制度库,用户提问"报销流程怎么走"时,先分类路由,而非在三个库各检一遍。这样做既省时间又提升准确率。
多跳检索:Agent 把一个复杂问题拆解成多个子问题,分别检索后合并推理。举个例子,问"上季度华南区的退货率是否异常",Agent 会先检索"退货率的正常范围"再检索"上季度华南区退货数据",最后基于两份资料综合判断,而不是试图一次检完。
反思式检索:Agent 生成初步回答后,自己审查答案缺失项,主动发起二次检索补充。这类似写论文时先写初稿,再根据审稿意见回头补资料。落地时要给 Agent 明确的指令,比如"如果当前资料不足以证明结论,请继续检索并标注补充来源"。
4.3 在 Agent 架构里整合 RAG 管道的建议
Agentic RAG 听起来很高级,但实操上我建议你用渐进式路线落。先保住基础管道质量,再给 Agent 增加两个工具:一个叫"知识库检索"工具,接收用户问题返回相关片段;一个叫"知识库路由"工具,帮 Agent 判断该查哪个库。让 Agent 自己决定是否调用这些工具,也就完成了 RAG 与 Agent 工作流的自然融合。
说到底,RAG 是 Agent 的一个工具,Agent 是 RAG 的调度者。只有把两者当作一个整体设计,知识获取管道才能真正为决策服务。
5. 常见问题与排查技巧实录
5.1 检索效果差:命中率低、答非所问
这是反馈最多的一类问题。当你发现答案明显不相关时,先别怀疑大模型,考察检索召回质量。我一般按这个顺序排查:第一步检查测试用例的 TopK 结果,把检索到的片段直接打印出来看是否相关;第二步检查切分设置,看是不是把关键内容切散了;第三步检查嵌入模型,换成中文优化模型试试;第四步引入混合检索,用 BM25 弥补向量检索的盲区。
既然提到评估,就顺便聊聊热词里常说的 Hit Rate 命中率。它的定义简单粗暴:在测试集中,对于每个问题,看标准答案片段是否出现在检索结果的前 N 条中。这个指标能客观衡量你的检索链路是否健康,建议在修改参数前后都跑一遍,用数据说话,别凭感觉调参。
5.2 回答仍有幻觉:来源与答案对不上
很多团队以为做了 RAG 就一定能消灭幻觉,这其实是误区。RAG 只能降低幻觉概率,不能根治。当我发现模型明明检索到了正确内容,却依然给出错误答案时,通常有两个原因。一是 prompt 没有明确约束模型"只能基于片段回答",导致模型开始自由发挥;二是 TopK 太多,噪音片段盖过了关键片段。前者改 prompt,后者降 TopK 或加重排序。
5.3 延迟过高与成本失控
在建管道时,延迟瓶颈通常不在检索,而在模型推理。检索返回 4 到 6 个片段,每个 500 token,拼起来大概 3000 token 的输入长度,这不会太耗时。真正拖慢速度的是模型输出和额外的重排序环节。我的建议是:把重排序做成可选策略,简单问题直接跳过;对用户频繁问的标准问题加一层缓存,命中缓存就不走模型,能大幅降低成本。
5.4 排障速查表
我把遇到的典型问题整理成了一张速查表,方便你对照自查。
| 现象 | 可能原因 | 建议操作 |
|---|---|---|
| 检索结果不相关 | 嵌入模型不支持中文 | 换 bge-zh / text2vec |
| 关键信息总是被切丢 | chunk_size 过小或分隔符不合理 | 增大 chunk 并加入中文标点分隔符 |
| 相似度阈值过低导致一堆噪音 | score_threshold 设太高了会有相反问题 | 观察检索得分分布,调平衡点 |
| 答案编造原书中没有的细节 | prompt 缺少"仅基于资料"约束 | 在 system prompt 中显式声明 |
| 多个文档越混合越乱 | 没有做路由 | 为不同知识域建独立向量库 |
| 检索慢、响应超时 | 数据量过大且未分区 | 加索引分区,或换 Milvus 级方案 |
6. 从 RAG 到 Agent 知识管道的扩展心得
6.1 本地化知识库:数据不出门的务实方案
如果你的业务涉及公司内部数据,或者你不想按 token 支付调用费,强烈建议做本地 RAG。本地化方案的核心是本地嵌入模型加本地向量库,再配合本地推理模型。我用过 Ollama 跑对话模型,本地部署的问题是显存占用,嵌入模型相对轻量,可放在 CPU 上跑,但生成式大模型强烈建议用带 GPU 的机器。实测下来,一台 16G 显存的机器跑中等规模的对话模型加上小型嵌入模型,完全能支撑几十人规模的知识库问答场景。
本地方案还有个隐藏好处:离线可用。这在一些网络受限的办公环境里是刚需。
6.2 知识更新与版本管理:别忽视管道维护
RAG 上线之后最大的敌人不是准确率,而是知识过期。员工手册改版了、产品规格更新了,向量库里还躺着旧版本内容,Agent 就会回答出"新瓶装旧酒"的答案。我建议把知识库更新纳入版本管理:每次批量写入都给知识库打标签,比如"2025Q3 员工手册 v3.2",查询时按版本标签过滤,或者在知识变更后主动重建相关模块的索引。
文本切分与向量存储的联动也要注意,如果你改了切分规则,旧索引必须重建,否则块与块之间会出现语义不对齐的问题。
6.3 个人体会
做了一整个系列之后,我最大的体会是:RAG 不是把文档丢进向量库就完事的玩具,它是需要持续打磨的知识基础设施。技术上每个环节都有讲究,但从项目角度看,清晰的文档结构、合理的知识分类、业务规则的沉淀,往往比参数调优更深远地影响最终效果。
如果让我给新手一个最快入门的路径,那就是先搭一个最小的管道,立刻跑通,再逐个换组件看效果差异。你会发现,换来换去,提升最大的一次往往不是换了更强的模型,而是把文档重新整理合格了。