
这次接着上一篇往下写。AI Agent 跑起来之后大家遇到最多的问题往往不是模型不够聪明而是它“不知道”——不知道你公司的产品手册、不知道刚更新的技术文档、不知道私域知识库里沉淀的经验。RAGRetrieval-Augmented Generation检索增强生成就是给 Agent 装一条知识获取管道让它在生成回答之前先“查资料”。作为“走进 AI Agent”系列的第四篇这一篇专门讲 RAG 基础从原理、工作流到可落地的代码把这条知识管道完整拆开讲清楚。适合两类人正在搭第一个 Agent 的开发者以及想把内部资料变成可检索知识库的技术负责人。前几篇我们聊了 Agent 的架构、工具调用和记忆机制到了这一篇你会发现知识获取是 Agent 能不能干成事的地基。一个只会聊天、不会查资料的 Agent就像一个没有工作经验的实习生态度很好但经常给你编答案。RAG 的出现就是给这个实习生配上公司资料库和工作手册让它上岗前先学会“查”再开口。1. 先搞清楚Agent 为什么需要一条知识获取管道1.1 Agent 的“记忆”不只有模型参数大模型本身确实“知道”很多知识但这些知识是训练阶段封存在参数里的存在一个硬性的知识截止日期。你问它今年新发布的某个产品参数它很可能一本正经地给出一个旧版本答案甚至直接编一个。你把自己公司的内部流程文档发给它它更是一头雾水因为训练数据里根本没有这些东西。Agent 要真正在企业场景里干活必须能持续获取知识操作手册、API 文档、工单历史、竞品资料、内部 FAQ这些内容不可能全部塞进 Prompt也塞不进模型参数。所以需要一条外挂的知识获取管道把“需要时取回来”的内容实时送到生成器面前。不用 RAG 也有几种补救办法但都不太理想。第一是把文档全塞进 Prompt几万字塞进去之后模型直接迷失重点而且每次请求都烧钱第二是做微调让模型把文档内容背下来但每次文档更新都要重新训练周期长、成本高第三是让模型“假装知道”结果就是一本正经地胡说八道。RAG 的思路反过来不逼模型记住而是给它一个查资料的动作。生成答案之前先把问题转化成检索条件去知识库把最相关的几段内容捞出来再原样交给模型做上下文。它的本质是给 Agent 增加一个可插拔的知识接口我习惯把它称为“知识获取管道”。1.2 RAG 解决的三个核心问题第一个是知识新鲜度。模型的知识截止日期是旧账RAG 可以索引昨天刚更新的文档回答即刻跟上。第二个是私有知识覆盖。企业内部的合同、Spec、FAQ 不在公开训练集里只有检索管道能把它带进推理过程。第三个是降低幻觉。虽然 RAG 不能完全消灭幻觉但当模型回答时手边有原始文档碎片它会更倾向于照着文档说而不是自由发挥。我一直和团队强调一个观点RAG 不是“模型不够好的补偿”而是 Agent 系统的标准配置。一个没有检索能力的 Agent本质上只是个带记忆的聊天窗口有了 RAG它才开始具备“查阅资料再行动”的工作能力。这也是为什么现在主流的 Agent 框架里几乎都内置了 Retriever 这个组件就是想让你默认建立这条知识管道。1.3 这类管道到底用在哪最典型的场景是内部知识库问答把公司 Wiki、产品文档、售后记录灌进去业务人员直接问自然语言不用再翻共享文件夹。其次是客服与工单处理用户在对话框提问Agent 先检索同类工单的解决方案再生成回复准确率比纯模型回答高很多。还有代码辅助开发、竞品分析、教育辅导凡是“答案依赖特定资料”的场景都适合接一条 RAG 管道。另外这两年经常听到的 Agentic RAG、GraphRAG、Hybrid RAG本质上都是在这条基础管道上加控制逻辑或索引结构。先把基础流程吃透再往上做编排就会顺手很多。2. RAG 的完整工作流从文档到答案的四段式管道2.1 第一段文档接入与解析这条管道的第一步是把乱七八糟的源文件变成干净的文本。很多人忽略解析这一步直接把 PDF 塞给 Embedding 模型结果表格乱码、页眉页脚混入正文检索质量立刻崩掉。实际做的时候要注意几个细节。PDF 要用解析库抽文本最好保留标题层级和段落结构而不是按字符流硬抽Word、Markdown、HTML 要剥离样式只留纯文本和结构化标题扫描版 PDF 需要 OCR这一步会比较慢但没办法省表格数据要特殊处理可以转成键值对或自然语言描述否则向量化后语义是乱的。我自己常用的解析方案是 Unstructured 库配 PyMuPDF复杂表格会用 Table Transformer 之类的模型单独处理。解析完要先做一轮清洗去重、去空行、修正编码、过滤页眉页脚和模板噪音。清洗后的干净文本再进入切片否则后面全是坑。这一步踩过的人都知道检索不准的根因往往不是算法而是源头数据太脏。2.2 第二段切片与向量化切片是所有 RAG 项目里最“调参炼丹”的环节。简单做法是按固定字符数切比如每 500 个字符一片相邻重叠 50 字符。但更推荐按语义边界切优先以 Markdown 标题、段落、句子为单位避免在句子中间硬切。切片尺寸直接决定检索粒度切太大会把无关信息带进上下文切太小会丢失完整语义。切片之后是向量化也就是用 Embedding 模型把每片文本转换成一个几百到几千维的向量。这里的关键是选对 Embedding 模型中文场景用 bge-m3、m3e、text2vec 这类中文优化过的模型多语言场景可以用 bge-m3 或 OpenAI 的 text-embedding-3-small代码场景推荐 codebert 或相关专用模型。向量维度影响存储和速度1024 维比 384 维更精细但占用更大、检索更慢要做取舍。向量化这一步有个容易忽略的点查询Query和文档Chunk最好走同一个 Embedding 模型。如果一个是中文模型、一个是英文模型向量空间根本不对齐检索效果会非常差。遇到问答场景还可以对查询先做改写比如把口语化问题转成关键词明确的长句再去检索命中率有明显提升。2.3 第三段检索与重排离线部分建好索引后线上每次回答都要走“查询向量化 - 相似度搜索 - 结果重排”这条路。向量检索一般用余弦相似度或内积把知识库里最相似的 TopK 个切片捞回来。但只靠向量相似度有一个天然短板它抓得住语义接近抓不住关键词精确匹配比如型号“A-3000”很容易被向量化得面目全非。所以现在主流做法是混合检索向量检索 关键词检索BM25/TF-IDF并行再用 RRFReciprocal Rank Fusion把两路结果排序融合。融合之后再接一个重排模型Reranker比如 bge-reranker、Cohere Rerank把候选从 50 个压缩到 3~5 个真正相关的。为什么要重排因为向量检索返回的前几个不一定是最准的重排模型把问题和候选段落做深度交叉编码精度会高不少。我的习惯是粗召回 Top50重排后取 Top3~5效果和性能最平衡。加了重排之后很多项目的准确率能提升 10~20 个点这是我能给出的最直接的调优建议之一。2.4 第四段生成与引用检索到内容之后把它们拼进 Prompt 里让模型生成答案。这段看起来简单其实有几个细节决定成败。首先要明确告诉模型“仅基于给定资料回答资料里没有就直说不知道”这是降低幻觉最关键的一步其次要保留每个切片的来源元数据文档名、页码、章节生成答案时要求模型标注引用方便用户核对第三要控制拼入的上下文总量不要让检索结果反客为主一般 3~5 片、每片 200 到 500 字总上下文控制在 2000 字以内比较合适最后在多轮对话场景中要把用户上一轮的问题和本轮问题合并后再检索不然指代不清。这一整条管道在 Agent 里就对应一个“知识获取”动作Agent 决定“我需要资料”于是调用 Retriever拿到结果后把它塞进上下文再让大模型产出最终动作或回答。理解了这个过程后面学习 Agentic RAG 的编排会很容易。3. 在 Agent 里落地 RAG 的技术选型与关键参数3.1 向量库选型不是越重型越好市面上的向量库很多我按使用场景给一个大致的选型参考方案特点适合场景Chroma轻量、单机、Python 原生本地学习、小规模原型FAISS纯向量检索库性能高需要自己管索引检索性能敏感、愿意自己封装Milvus / Zilliz Cloud分布式、功能全支持混合检索生产环境、大规模文档Elasticsearch支持向量和 BM25老牌搜索能力已有 ES 团队、需要全文和向量混合QdrantRust 写的接口友好中等规模、Docker 部署方便选型的底层逻辑很简单先看数据量级。一万个 Chunk 以内Chroma 或 FAISS 足够别为了“生产”硬上分布式。百万级以上再考虑 Milvus 或 Qdrant。还要看团队已有设施如果公司本来就用 ES那就先用 ES 的向量插件避免多维护一个组件。技术选型不是追新而是选最容易让系统长期跑起来的方案。3.2 Embedding 模型与向量维度的经验值对中文场景我个人偏好的开源选型是 bge-m3它在检索、分类、相似度等任务上表现均衡支持 8192 输入长度对长文档比较友好。如果没有算力限制也可以用商用的 Embedding 接口省心但要注意数据隐私问题企业私有化部署时最好选开源模型本地跑。向量维度不用强求最高。bge-m3 默认输出 1024 维如果数据量大、内存吃紧可以降维或用 384 维的轻量模型。记住一个原则维度是精度和效率的权衡先保证语义准确再考虑性能。如果你用的是 Java 技术栈也不用太担心LangChain4j 和 Spring AI 都封装了类似的 Embedding 模型接入核心思路和 Python 侧保持一致。3.3 切片参数chunk_size 与 overlap 怎么调切片参数没有银弹但有靠谱的起点。我常用的基准是普通说明文、技术文档chunk_size512overlap50对话记录或工单chunk_size256overlap20代码文档chunk_size384overlap40。这里的单位是 token 而不是字符不同分词器差异很大务必统一用模型自带的 tokenizer 计算。为什么需要 overlap因为前后文经常共享关键信息一个知识片段的尾部往往在另一个片段头部延续完全没有重叠会导致检索时“话说到一半”。但重叠也不能太大否则大量冗余内容会让向量库变得臃肿检索时重复结果多。调整切片还有一个策略先粗切再精分。比如按官方给出的“按标题切小节”如果小节太长再递归按段落、句子切直到长度符合要求。LangChain 里的 RecursiveCharacterTextSplitter 干的就是这件事它接收一组分隔符优先按更长的语义边界切切不开再退到小边界。3.4 检索参数TopK、阈值、混合策略检索的时候有四个核心参数要关注。TopK 是最终送入模型的片段数问答场景 3~5 足够摘要场景可以放宽到 8~10相似度阈值低于阈值的片段直接丢弃避免把不相关内容硬塞进上下文但阈值得通过验证集调别拍脑袋Score 归一化是因为不同向量库的相似度分数范围不同设置统一归一化方式方便调阈值和融合混合比例是向量检索和 BM25 的权重一般先用 RRF 无脑融合再根据效果调整。调参数时建议做一个“评估脚本”准备 50~100 条真实的问答对记录每一条能否在 TopK 中命中正确答案然后批量跑不同参数组合选出召回率最高的配置。很多团队调 RAG 调了半天其实是在猜参数有了这个脚本之后效率会高很多。4. 一次完整的 RAG 实战基于 LangChain 搭建最小知识管道4.1 环境准备与最小依赖为了说明方便我用 Python LangChain 搭一个本地可运行的 RAG 管道这种组合也是社区里最常见的练手方式。先准备环境pip install langchain langchain-community langchain-openai chromadb pypdf这里用 OpenAI Embedding 接口做示例如果要本地化把langchain-openai换成langchain_community.embeddings里的 HuggingFaceEmbeddings指定 bge-m3 或 m3e 模型即可核心代码逻辑不变。准备一个测试文档建议用手头的一篇 Markdown 或 PDF比如产品说明、操作手册。这个实验的重点不是数据量大而是把管道跑通所以一两篇文档就够。4.2 文档加载与切片代码的第一步是加载文档并拆分from langchain_community.document_loaders import PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter loader PyPDFLoader(product_manual.pdf) documents loader.load() text_splitter RecursiveCharacterTextSplitter( chunk_size512, chunk_overlap50, separators[\n\n, \n, 。, , , , ], ) chunks text_splitter.split_documents(documents) print(f文档被切成了 {len(chunks)} 个片段)注意 PyPDFLoader 会把 PDF 每页存成一个 Document自带页码元数据。这意味着可以直接用doc.metadata[page]做引用。如果文档是 Markdown可以用 MarkdownHeaderTextSplitter 按标题切保留不同层级的结构信息检索时可以回溯到对应章节。4.3 写入向量库与检索测试切片完成后把它们 Embedding 并写入 Chromafrom langchain_openai import OpenAIEmbeddings from langchain_community.vectorstores import Chroma embedding OpenAIEmbeddings(modeltext-embedding-3-small) vectorstore Chroma.from_documents( documentschunks, embeddingembedding, persist_directory./chroma_db, ) retriever vectorstore.as_retriever(search_kwargs{k: 5}) # 测试检索 query 产品支持哪些网络协议 docs retriever.invoke(query) for doc in docs: print(doc.page_content) print(来源, doc.metadata)如果使用本地 Embedding把OpenAIEmbeddings换成HuggingFaceEmbeddings(model_nameBAAI/bge-m3)第一次运行会下载模型之后就在本地推理。检索结果出来后观察一下是否都是这个问题的相关片段。如果前三段几乎都不相关不要急着往下做生成先把切片和 Embedding 模型的问题排查掉。4.4 把管道接进 Agent 的生成层检索没问题后把结果组装成 Prompt交给大模型生成答案from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate template 你是内部知识库助手。请仅根据下面的资料回答用户问题如果资料中找不到答案请直接说“根据现有资料无法回答”。 资料 {context} 问题{question} prompt ChatPromptTemplate.from_template(template) llm ChatOpenAI(modelgpt-4o-mini, temperature0) chain prompt | llm context \n\n.join([doc.page_content for doc in docs]) answer chain.invoke({context: context, question: query}) print(answer)在 LangChain 里这条完整链路可以用 LCEL 写得非常简洁retriever | format_docs | prompt | llm。如果你不想依赖 LangChain完全可以用 FastAPI 自己封装一个/rag/query接口内部逻辑同样是“检索 拼 Prompt 调 LLM”框架只是加速器。懂底层之后框架换起来不伤筋动骨。4.5 从离线评估到上线监控跑通上面这些代码只是完成了 RAG 管道的“可用”阶段距离“好用”还差一个评估和监控环节。建议在项目里维护一套离线评估问答集每次修改解析逻辑、切片参数或重排策略后都重新跑一遍评估对比召回率和答案正确率。上线之后还要记录真实的用户 Query、检索到的片段和模型答案定期抽检“用户满不满意、检索相不相关”。这一步做扎实了RAG 系统的迭代才会有方向而不是靠感觉。5. 常见问题与排查经验5.1 检索结果和问题完全不搭遇到这种问题先检查三件事Embedding 模型对不对、查询改写有没有做、数据清洗干不干净。最容易踩的坑是查询和文档不是同一个模型比如文档用 bge 向量化查询却调了 OpenAI 接口两边向量空间不一致相关性直接归零。其次是领域术语问题。很多垂直领域词汇在通用 Embedding 模型里没学好导致“EPON”和“光猫注册”相关度很低。解决思路是给切片补充同义改写或者在查询时用大模型做一次术语扩展把缩写展开成全称再检索。做过之后命中率通常会有明显改善。5.2 答案还是乱编或者答非所问如果检索到了但模型不按资料说问题多半在 Prompt 和上下文组织上。我排查的顺序是先看 Prompt 里是否明确限制了“仅基于资料回答”再看给出的上下文是否混杂了无关片段最后看有没有要求模型输出引用。如果都做了还不行考虑加一层“答案校验”生成完答案后让另一个模型实例把答案和原始检索片段做一致性评分分数低就打回重新生成。虽然增加了一次模型调用但对准确率要求高的场景非常有效。5.3 向量库召回慢、索引膨胀数据量上来之后单机 Chroma 或 FAISS 会越来越吃内存。我的建议是尽早给 Chunk 打上业务标签产品线、部门、文档类型检索时先按标签过滤再在子集里做向量搜索召回速度和准确度都会更可控。同一份文档被反复更新会积累大量相似 Chunk做增量写入时先删除旧版本对应的向量避免索引膨胀。还可以用缓存把热门问题的检索结果缓存起来尤其是企业内部高频 FAQ命中缓存后可以跳过向量检索响应时间会明显下降。这也算 Agent 记忆机制的一种简单实现。5.4 Agentic RAG下一步从管道到决策最后简单提一句 Agentic RAG。基础 RAG 是“查询 - 检索 - 生成”的单向管道Agentic RAG 则让 Agent 自己决定要不要查、查几次、用什么工具规划查询。比如先根据用户问题判断需要哪些知识再决定是查向量库还是调外部 API得到结果不满意还可以多轮优化查询。这些都是后续可以继续展开的内容但前提是把今天这条基础管道彻底跑通。如果让我给第一次搭 RAG 的人一个最重要的建议那就是不要一上来追求花哨的框架。先用 50 条真实问答把文档切分、检索链路跑通把召回率测清楚再考虑重排、混合检索、Agentic 编排。我见过太多项目死在“先上 Milvus 微调 一堆中间件”这种重投入上最后发现是文档解析没做好。知识获取管道做得越简单、越经得起测试在真正的 Agent 产品里反而越耐操。希望这篇 RAG 基础能帮你把这条管道扎扎实实立起来我们下一篇继续往下深挖。