
老读者应该知道这个系列前几篇聊过 Agent 的本质、规划能力和工具调用。这一篇干货我们来聊聊让 Agent 真正“有知识”的基础设施——RAGRetrieval-Augmented Generation检索增强生成。很多朋友把 RAG 简单理解成“往提示词里塞几篇文档”真做起来才发现从文档清洗、分块嵌入到检索重排每一环都在实打实地影响 Agent 最终的回答质量。这篇是知识获取管道的基础篇适合准备引入 AI Agent、搭建知识库或者已经在调 RAG 但效果不稳定的朋友。先说为什么会写这个选题。Agent 越做越复杂但我见过太多项目卡在最底层模型不是没能力是没资料。让 Agent 回答一个内部系统问题它连数据库表结构都不知道让它总结一份新发布的行业政策训练数据里根本没有。知识获取管道就是解决“模型不知道”这件事的标准姿势。今天这篇不堆概念直接把它拆成索引、检索、生成三段讲透每段背后的取舍再附一份可跑的最小实现最后把常见坑挨个填平。1. 为什么 AI Agent 需要一条知识获取管道1.1 大模型的知识短板到底短在哪大模型的知识来自预训练阶段见过的语料这是一次性的、有截止日期的、且完全不可控的。我用一句话概括模型记住的是“世界的大致样子”但记不住“你的系统里到底有什么”。它知道什么是订单但不知道你们公司订单表里有哪些字段它知道什么是设备巡检但不知道你们工厂设备的编号规则和最近检修记录。这种短板在日常问答里还能靠话术糊弄放到 Agent 场景里就是硬伤。Agent 是要替人干活的一个需要查资料才能完成的任务如果它手里没有资料就只能瞎编。所以我们说 RAG 是 Agent 的知识获取管道本质上是在模型的外部挂一个可控的知识源让模型在回答前先去“查资料”而不是凭记忆硬答。还有个经常被忽略的点知识是动态的。预训练一次动辄几个月而业务系统的数据每天都在变。RAG 把知识从模型参数里解放出来文档更新了索引跟着重建Agent 立刻就知道了。这种“知识不上模型”的思路在工程上极大降低了更新成本。1.2 知识割裂与 RAG 在 Agent 架构里的位置知识割裂是很多团队做 Agent 时说不清道不明的痛点知识散落在 Wiki、钉钉文档、数据库、PDF 文件、老员工的脑子和聊天记录里。你让 Agent 去检索它不知道该去哪找你让用户去问用户也不知道该问谁。RAG 做的事情就是把这些割裂的来源统一成一条管道最后收敛到一个向量库里Agent 检索时只需要面对这一个入口。在 Agent 的整体架构里知识管道和规划、工具调用是并列的三大件。规划负责拆任务工具负责执行动作知识管道负责提供依据。没有 RAG 的 Agent 像个记忆力差但嘴皮子溜的实习生有了 RAG 才像是配了联网电脑的正式员工。不过要提醒一句RAG 不是替代码逻辑兜底的。它管的是“知识密集型”任务如果你的任务必须靠精确计算或固定流程完成该走函数还是要走函数。2. RAG 管道拆解索引、检索、生成三件套2.1 索引阶段加载、切分、嵌入、入库索引阶段的目标是把原始文档变成计算机能快速查找的向量。完整链路是加载文档 → 清洗去噪 → 切分成块 → 向量化嵌入 → 存入向量库。加载这一步看似无聊其实最累。PDF 有扫描件、有双层 PDF、有表格错位HTML 有导航栏和广告噪声Word 里有批注和修订痕迹。我在实际项目里数据清洗耗时经常占整个管道开发的一半以上。别急着上模型先写规则把页眉页脚、重复模板、乱码字符处理掉效果立竿见影。切分是索引阶段最需要调优的地方。常见做法是按固定长度切也可以用 RecursiveCharacterTextSplitter 按语义层级切。中文场景下我常用 200~300 字一块overlap 设 20~50 字。为什么要有重叠因为语义边界不会刚好落在字符边界上一个知识点被切成两半检索时谁也不完整。但也要小心切得太大单块噪音多切得太小上下文丢失。后面我会详细说怎么调。嵌入这一步是把文本变成向量。中文场景常用 m3e-base、bge-m3 这类模型也可以直接调用内置的 Embedding API。选模型的标准不是维度越高越好而是看它在你领域语料上的表现。如果预算有限先用通用 embedding跑一遍效果再考虑微调或换更强的模型。向量库选型上项目早期用 FAISS 足够数据量到百万级别再考虑 Milvus 这类分布式方案。2.2 检索阶段召回、过滤、重排检索阶段的目标是从向量库里捞出和用户问题最相关的 Top N 块。最简单的做法是只做向量相似度召回把问题也做一次 embedding然后在库里找余弦相似度最高的几块。但基础向量检索有几个坑。第一关键词匹配敏感同一个意思换个说法向量可能就偏了。第二相似度高的块不一定是最有用的信息它可能只是字面上像。第三用户问题往往很短向量很难捕捉完整意图。所以稍微靠谱一点的管道都会加上混合检索BM25 精确匹配 向量相似度两道召回结果合并后再重排。重排Rerank是很多 RAG 项目效果提升的关键一步。召回拿到 Top 20重排模型逐条算相关分再选出 Top 5 给模型。这一步非常耗时但收益极大。我见过很多项目召回率还行最终答案却稀烂问题就出在 Top 5 里混进了两条无关内容。重排模型就是干这个的把真正有用的文档排到最前面。2.3 生成阶段上下文组装与提示词生成阶段是把检索结果塞进 Prompt 给大模型。表面上就是拼接字符串实际上有两个关键点上下文组装和指令约束。上下文组装要考虑 token 预算。Embedding 模型有最大输入长度LLM 也有上下文窗口。经验值是把检索结果限制在 3~6 条每条不超过 500 字整体控制在 2000 字以内。太长了模型抓不住重点太短了信息不够。按权重排序相关度高的放前面这是一个低成本但有效的技巧。指令约束决定模型怎么用这些资料。Prompt 里至少要说明三件事第一只依据给定资料回答第二资料里没有就明确说不知道不要编造第三回答时尽可能引用来自哪份资料的线索。第一条抑制幻觉第二条保住可信度第三条方便调试。你甚至可以要求模型在回答后附“参考来源列表”这样定位问题非常方便。3. 从零搭一条最小可用的 RAG 管道附代码3.1 环境准备与工具选型我建议新手不要一上来就上 LangChain 全家桶先用手写代码跑通最小链路理解每一环在干什么再引入框架抽象。下面这套方案用到的库很少全部可在本地环境运行FAISS 做向量库m3e-base 做 embedding任意国产大模型 API 或本地 Ollama 做生成。环境安装命令很简单pip install faiss-cpu sentence-transformers requests向量模型用 m3e-base因为它在中文语料上表现稳定模型文件下载后可本地缓存不依赖外部网络调用。大模型接口我用的是兼容 OpenAI 格式的 endpoint这样代码上只改 base_url 和 api_key 就能切换服务商。3.2 索引端代码与参数选择先准备几段测试文档自己写十来个句子比直接拿长篇 PDF 调试更容易观察效果。索引端代码如下from sentence_transformers import SentenceTransformer import faiss import numpy as np model SentenceTransformer(moka-ai/m3e-base) documents [ 公司内部报销流程员工在OA系统提交申请部门经理审批后财务在3个工作日内完成打款。, 制度规定差旅住宿标准为一二线城市每晚不超过500元其他城市不超过350元。, 产品手册设备型号HT-2000支持温度在-20℃到50℃区间正常运行超出范围需加装防护罩。, 售后政策整机保修一年核心部件保修三年人为损坏不在保修范围内。, ] # 切分这里每段已经是一个块生产环境中需要按规则自动切分 chunks documents vectors model.encode(chunks, normalize_embeddingsTrue) # 建立 FAISS 索引 dimension vectors.shape[1] index faiss.IndexFlatIP(dimension) index.add(vectors.astype(float32)) # 保存索引 faiss.write_index(index, knowledge_base.index)看到没核心就这几行。normalize_embeddingsTrue是为了用内积代替余弦相似度这样效能更好。生产环境里你要加一步元数据存储每条向量对应的来源文件名、章节、更新时间等。后续做过滤和溯源都靠它。3.3 检索端与生成端串联检索端代码同样简洁query 出差去上海住酒店一晚400元超标了吗 query_vector model.encode([query], normalize_embeddingsTrue) # 检索 Top K k 3 scores, indices index.search(query_vector.astype(float32), k) retrieved_chunks [chunks[i] for i in indices[0]] print(召回内容) for idx, chunk in zip(indices[0], retrieved_chunks): print(f索引 {idx}: {chunk})接下来把召回的块拼进 Prompt 调大模型import requests def call_llm(question, contexts): context_text \n\n.join([f[资料{i1}] {c} for i, c in enumerate(contexts)]) prompt f请根据提供的资料回答问题。资料中没有的信息请直接说“资料中未提及”不要猜测。 资料 {context_text} 问题{question} resp requests.post( https://your-endpoint/v1/chat/completions, headers{Authorization: Bearer YOUR_API_KEY}, json{ model: qwen-plus, messages: [{role: user, content: prompt}], }, timeout30, ) return resp.json()[choices][0][message][content] answer call_llm(query, retrieved_chunks) print(答案, answer)跑出来结果会告诉你向量检索能不能召回正确的那条“差旅住宿标准”。如果答案不对先看召回的 chunks 对不对——这直接决定了问题出在检索端还是生成端。这一步是整个调试流程的核心习惯。4. 把坑填平RAG 实战的常见问题与排查4.1 检索不到召回率hit rate低怎么查hit rate 是 RAG 项目最该看的指标含义是“正确答案出现在召回结果里的比例”。如果 Top 5 里根本没有正确资料后面模型再聪明也白搭。我建议你动手之前先构造30~50条带标准答案的测试问题把正确文档片段标注好然后统计 hit rate。低的原因通常有三类。第一切分方式不对正确答案被切碎或混入了大量噪音。检查方法是把正确片段拎出来看它是不是完整表达了一个意思边界会不会刚好在一个名词短语中间第二查询和文档用语不一致。用户口语说“报销能给多少钱”文档里写的是“补偿标准”向量相似度就低。解决思路是混合检索加上 BM25或者做一个查询改写模块先把问题改写扩展再检索。第三embedding 模型不适合领域。遇到专业术语多的场景通用模型经常抓瞎需要换领域预训练的模型或微调。4.2 检索了一堆却没用重排与无关内容污染召回没问题、答案却差通常是“上下文污染”导致的。我有一次测试Top 3 里正确资料排第二第一是份沾了点边但完全不相关的公告模型被带跑输出了一堆套话。从那以后我学乖了Top K 不要贪多K5 或者 K8 就够后面接一个重排模型。重排可以用 cross-encoder 类型的模型它对 query 和 doc 做整体相关性计算比向量相似度准一个档次。代价是慢所以只对召回结果做几十条的规模完全能接受。引入重排后正确答案从“不确定排第几”变成“稳定进前三”这一步值得投入。4.3 切分与中文语境的老大难中文切分比英文麻烦得多。英文按空格和标点就行中文没有天然词边界从句经常跨越大段文本。我个人经验是不要迷信固定长度按段落优先段落太长再按句号、分号切。overlap 不是越多越好太多会让索引数据膨胀、检索噪音变多。先用 200~300 字起步跑 hit rate 看效果再逐步调。另一个中文场景特有问题是简繁和同义改写。用户问“内存不足如何处理”文档写的是“存储空间不够时建议清理缓存”字面上不匹配但语义相似。这种情况只能靠更强的语义模型或扩展训练集解决短期内可以在检索前加一层同义扩展把关键词的常见近义形式都填进去。4.4 常见问题速查表现象可能原因排查方向答案胡说八道提示词没约束“不知道就直说”检查生成阶段指令正确答案排在后面没有重排引入 rerank 模型检索结果经常为空文档切分过碎或查询改写不足调整 chunk_size加 BM25回答前后不一致到了 K1 就生成缺少综合判断加大 Top K 并约束综合逻辑召回慢向量库无索引或不支持 GPU换 HNSW 索引或上 Milvus更新了文档但回答没变化知识库没重建索引确认增量更新链路是否打通5. 从 RAG 基础走向 Agentic RAG5.1 基础 RAG 的边界到底在哪基础 RAG 是标准的“一问一检一答”流程它的问题在于用户问题复杂一点比如“比较 A 产品与 B 产品的售后差异”它往往只会做一次检索检索到的内容可能只覆盖一半。再比如用户连续追问“那如果超了标准找谁审批”基础 RAG 没有记住上文的检索状态回答就断片。Agentic RAG 的思路是在基础管道外面套一层 Agent 逻辑让模型自己判断需不需要检索、需要检索几次、用哪个查询词、检索结果不够时要不要换种说法再查一次。这已经不只是知识管道的范畴而是把检索变成了 Agent 的工具调用。你可以把基础 RAG 看成调一个 fetch 函数Agentic RAG 则是让模型自己写 SQL、查完拿结果再决定要不要连表查第二张。5.2 与 Skill 的配合把检索封装成能力前面热词里有人问“Skill 怎么和 RAG 结合起来”。我的经验是把 RAG 检索封装成一个 Skill函数暴露给 Agent 按需调用。Agent 在回答用户问题之前先看自己的工具清单判断“这个问题需要查资料吗”需要就调用knowledge_search(query, top_k)这个 Skill。Skill 内部做的是经典的检索流程外部只暴露一个接口。这样设计的好处是解耦。知识库怎么切分、怎么重排、用什么向量库都是 Skill 内部的事情Agent 层不需要关心。你可以任意替换知识库实现只要保证接口返回格式不变就行。真要升级到 Agentic RAG无非是让 Agent 多调用几次这个 Skill比如第一版查询太宽泛Agent 自动改写查询词再搜第二次。5.3 落地前先想清楚的三件事第一别一上来就上高级方案。GraphRAG、本体 RAG、意图路由这些概念听着很唬人但都是解决特定问题的。如果你的文档是几十个 PDF基础 RAG 加上重排足够。第二评测集必须提前建。没有评测集的 RAG 优化就是盲人摸象每个改动你都不知道是变好还是变坏。第三知识更新要有机制。知识库不是建完就完事文档变更后索引多久重建一次、如何处理已删除的文档这些都要有明确方案。还有一个常被忽略的是成本。向量化、重排、大模型推理每一步都是算力开销。基础 RAG 中向量检索本身很便宜重排才是大头成本。生产环境建议对不同类型的查询分流简单问题走重排疑难问题才走 Agentic 多次检索。这些优化不是炫技而是在保证质量的前提下省钱。我个人在实际项目里的体会是RAG 基础这篇文章我写了删、删了写核心原因是一句话绕不开知识的质量决定了 Agent 的上限。模型再强管道再花哨你知识库里的内容本身就是错的、是乱的、是切碎的出来的东西一定不能看。所以如果你时间有限请把 70% 的精力花在清洗知识、构造评测集和调整切分这三个动作上。先把基础链路跑稳、跑慢、跑准再考虑要不要上 Agentic 那套花活。毕竟对一个连资料都查不准的 Agent 来说让它自己决定查得更聪明只会错得更远。