开篇先聊个我最近被问得最多的问题AI Agent 跑通了工具调用、也有了任务规划为什么回答一个稍微专业点的问题时还是会出现“一本正经胡说八道”答案其实不复杂。Agent 的能力再强它的知识源头就是底座大模型在训练时学到的那些参数化知识。这类知识是静态的、有截止日期的而且在大模型眼里全世界的信息都被压成了“概率分布”它根本不区分“我知道”和“我好像知道”。所以当 Agent 要处理企业文档、产品手册、私有知识库、实时资讯这些非参数化信息时就必须在“大脑”之外再给它装一套知识获取管道。这就是 RAG 的核心价值也是我把这一篇放在 AI Agent 系列第四篇的原因前三篇我们把 Agent 的骨架搭起来了这篇解决的是“脑子里的货从哪儿来”的问题。这篇文章我会先从 RAG 为什么非上不可讲起再拆解完整的 RAG 管道设计随后给出一套可以直接跑起来的本地实现方案然后聊检索质量的评估方法最后整理我在实战中踩过的坑和排查思路。如果你是刚接触 Agent 开发或者已经在写 RAG 但总觉得效果不稳定这篇应该能帮你把整条链路看通透。1. 为什么 Agent 需要一套“知识获取管道”1.1 大模型的知识困境参数化知识与非参数化知识的割裂我先用一个比喻把问题说清楚。大模型有点像那种“考前突击的学霸”它把教科书内容浓缩成了自己的“记忆”考试时凭记忆作答。但现实中的问题是教科书每年都在更新公司内部还有只有自己人才知道的行话和流程这些内容根本不在它记忆范围内。更麻烦的是这个学霸对自己“没记住”这件事没有感知它宁可编一个看起来合理的答案也不肯说“我不知道”。业界把大模型自身携带的知识称为参数化知识也就是训练完成后固化在权重里的那部分。它有两个硬伤一是时效性差训练数据有截止日期二是覆盖不了私有领域企业内部的文档、数据库、专家经验它都接触不到。RAG 要解决的正是这个“知识割裂”问题——把外部知识通过检索管道注入到生成过程中让大模型每次回答时都能“翻书”而不是全靠脑子里的存货。光知道要“翻书”还不够关键是翻书的方式不能是“每次全量读一遍”。真实场景下一个企业知识库可能有几万份文档、数十亿字符全部塞进上下文既不现实也没必要。RAG 的做法是先把知识“切碎、编码、存好”再把用户的问题也编码然后用向量相似度这一类的检索手段只把最相关的几个片段捞出来连同问题一起交给大模型。这个“先筛后答”的机制就是知识获取管道的核心逻辑。1.2 把 RAG 看成 Agent 的记忆子系统很多初学者会把 RAG 理解成一个“问答增强插件”但在 Agent 架构里它的位置要重要得多。我把 Agent 的知识体系拆成三层来看长期记忆知识库本体可以是文档库、数据库、图谱存放的是结构化或半结构化的沉淀知识不会随对话结束而消失。工作记忆当前任务相关的检索片段和对话上下文是 RAG 在单次请求里召回的内容作用类似人类的“临时笔记”。动机/技能Agent 的工具调用逻辑知道“什么场景该去查知识库”这部分通常由 Prompt 或路由模块承担。这样拆完你就明白RAG 不只是回答问题前“查一下资料”它其实是 Agent 与外部世界交互的记忆接口。对话类 Agent 需要它来做上下文增强决策类 Agent 需要它来做事实核查自动化流程类 Agent 甚至会用检索结果来决定下一步调用哪个工具。理解了这个定位后面很多设计决策比如要不要做重排序、要不要多路召回就都有了判断依据。1.3 哪些场景必须上 RAG不是所有 Agent 都需要 RAG。如果任务只依赖通用常识、不需要最新信息直接对话就行。但下面这几类场景不上 RAG 基本没法用私有文档问答公司章程、产品手册、项目文档这些内容大模型不可能预先学过。实时信息获取比如最近几天的行业资讯、系统监控数据大模型训练数据里根本没有。垂直领域术语与规范法律条文、医疗指南、工业设备参数通用模型容易答偏需要检索权威来源来锚定表述。多文档交叉比对比如比较两份合同的差异、汇总多个部门的制度文件需要先定位到具体段落再让模型做分析。直接让大模型硬答这些场景轻则答得不准重则产生严重幻觉在金融、医疗、工业这类领域甚至会带来合规风险。RAG 的价值不只是“提升准确率”更是让 Agent 的每一次输出都有据可查、可以追溯。这也是我为什么把 RAG 称作 Agent 的“知识获取管道”而不是“检索插件”的原因——它应该是一条稳定、可评估、可观测的链路而不是一个随缘触发的小功能。2. RAG 管道设计与核心环节拆解2.1 索引侧管道从文档到向量的完整过程RAG 管道分成两条链路一条是离线的索引侧负责把知识处理成可检索的形态另一条是在线的查询侧负责把用户问题变成检索指令并组织答案。大多数初学者搭建 RAG 效果不稳定问题往往出在索引侧没做好而不是模型不行。索引侧的标准流程是文档加载 → 文本清洗 → 切分Chunking→ 嵌入Embedding→ 入库。每一步都有隐藏的坑。文档加载要考虑源文件格式PDF 要处理扫描件和排版异常Markdown/HTML 要剥离导航噪声Word 还要处理表格和不连续段落。文本清洗阶段我建议至少做这几件事统一编码、去页眉页脚、移除无意义符号、保留必要的表格结构信息。很多人会跳过清洗直接切分结果检索时召回一堆乱码片段。清洗完后就是切分。这一步是索引侧最容易被低估的环节。切分策略直接决定了召回粒度和语义完整性切太碎单个片段信息量不足模型回答时缺乏上下文切太粗一个片段里混了多个主题向量表征被“平均化”检索精度反而下降。我在 2.3 小节专门展开讲参数选择。切分完成后每个切片通过嵌入模型转成向量连同原文、元数据来源、页码、标题路径一起写入向量数据库。注意元数据一定要写完整后面做来源溯源、权限过滤、增量更新全都靠它。2.2 查询侧管道检索、增强与生成的分工查询侧的流程是查询理解 → 检索 → 重排序 → 增强提示词构造 → 生成回答。很多初学者搭的所谓 RAG 其实就是“向量检索 把原文拼进去”这能用但效果远没到合格线。先说查询理解。用户的问题往往不够精准比如“托底方案是什么时候确定的”这种问题直接拿去向量检索效果大概率不如先做一次查询改写提取实体“托底方案”补全为“【项目名】的托底方案确定时间”必要时还可以拆成多个子查询。LangChain 里有一个 QueryTransformer 工具可以做这件事即使不用框架你自己写几条改写规则也能显著提升召回。再说检索后的重排序。向量检索的初筛结果一般取 Top 20 到 Top 50精度并不高。如果不做重排序直接把 Top 3 塞给大模型很容易混入不相关内容导致答案被带偏。重排序模型比如 bge-reranker 或 Cohere Rerank会逐条计算“问题-候选片段”的语义匹配分把真正相关的排到最前面。这一步在我的实测中能让答案准确率提升 10 到 20 个百分点成本只是多一次模型调用非常划算。最后是增强提示词构造。这里有个容易犯的错误把所有召回片段不加区分地拼进 Prompt。正确做法是给每个片段标注来源和序号并在 Prompt 里明确“如果片段中没有相关信息请直接说明不知道不要编造”。这样既提升忠实度又方便用户核查答案来源。生成阶段目前主流还是直接调大模型没有太多需要额外加工的地方。2.3 切分策略详解固定窗口、语义切分与父子块切分策略是 RAG 里影响效果最直接、却最容易被“拍脑袋”决定的一环。常见的方案有三种我分别说一下适用场景和参数经验。固定大小窗口切分是最容易上手的方式把文本按字符数或 token 数切成固定长度的块相邻块之间保留 overlap。我比较常用的参数是块大小 500 到 800 字符overlap 80 到 150 字符。这个参数组合对大多数中文技术文档效果比较稳。overlap 的作用是避免把一个完整句子或知识点恰好切到边界上但 overlap 太大会造成大量冗余既浪费存储又干扰检索。如果你在 LangChain 里用RecursiveCharacterTextSplitter我建议按先段落、再句子、再固定长度的优先级切比单纯按\n\n切效果好很多。语义切分则是用嵌入模型判断句子之间的语义断点在语义转折处切开。它切出的块更符合人类理解但计算成本高而且对嵌入模型的敏感度要求也高。如果你的文档结构很规整比如每段一个小标题我实测用普通的按标题切分就能达到类似效果不一定非要上语义切分。语义切分最值得用的场景是文档没有明确结构、长段落居多、且对切分质量要求很高的时候。父子块切分是我目前最推荐的一种策略把小片段父块内再细分出的子块用于精确检索再把它们所属的父块大一点的段落或章节作为上下文喂给模型。这样做的好处是“检索准、生成全”。具体实现时可以先按章节切成大块再对大块内的句子或小段落做二次切分两级之间维护映射关系。代价是索引结构复杂一些但检索质量的提升非常明显。2.4 嵌入模型选型通用模型、领域模型与向量维度的影响嵌入模型决定了“语义是否相近”这件事判断得准不准是整个 RAG 管道的“感觉中枢”。选型我有几条经验第一先用通用中文嵌入模型跑通基线。目前开源社区有明显优势的通用模型有 BGE 系列、M3E 系列、GTE 系列等它们对中文支持好、尺寸适中、部署成本低。不要一上来就追求超大模型先把管道跑通再用评测数据指导升级。第二如果领域特色强比如医疗、法律、工业代码在通用模型召回效果不达标时要做领域微调或用领域适配的嵌入模型。实测中通用模型在处理“特种设备检验规程”这类专业词汇时偶发会把同义词召回成无关结果领域微调后的模型能显著改善这问题。第三向量维度会影响存储和检索性能。768 维和 1024 维在单条记录检索时差别不大但上了百万级数据量维度每翻一倍索引体积和检索延迟都会明显上升。如果数据量不大十万级以内不必过度纠结维度差异如果准备做大规模生产部署建议先用 PCA 之类的降维手段做一轮压缩测试再决定最终维度。索引侧的质量决定检索召回的上限查询侧的设计决定召回内容能不能被有效利用。这两侧都做扎实之后RAG 的“可用性”才算真正立住了。3. 从 0 到 1 搭建一个本地 RAG 管道3.1 技术选型LangChain Ollama Chroma 的组合逻辑实践是检验理论最好的方式。这一节我会带你从零搭一条能跑的本地 RAG 管道选型为LangChain编排框架 Ollama本地推理 Chroma向量数据库。这套组合最大的优势是零 API 成本、完全本地运行、代码量少非常适合学习和快速验证。选 LangChain 是因为它把文档加载、切分、嵌入、检索、Prompt 组装这些繁琐的管道操作抽象成了标准组件初学者可以在一两百行代码内跑通全流程。等真正进了生产环境你再换成 LangChain 背后的原生组件也来得及。Ollama 负责两件事提供嵌入模型和生成模型。Chroma 是一个轻量级向量数据库单机版足够支撑个人知识库和大多数 POC 项目。如果你预感未来数据量会上千万级再把 Chroma 换成 Milvus 或 Qdrant业务代码基本不用动。需要说明的是我用 Ollama 只是因为它本地部署方便如果你已经有 OpenAI 兼容接口或者国内大模型服务的 key把代码里的模型名和 base_url 换成你的即可管道逻辑完全一致。3.2 索引侧核心代码文档加载、切分与入库下面这段代码完成索引侧的主要工作。我用的是 Markdown 格式的《产品操作手册》实际换成 PDF、TXT 也没问题LangChain 的DirectoryLoader支持几十种格式。from langchain_community.document_loaders import DirectoryLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_ollama import OllamaEmbeddings from langchain_chroma import Chroma # 1. 加载文档目录下的所有 markdown 文件 loader DirectoryLoader(./knowledge_base, glob**/*.md, show_progressTrue) docs loader.load() print(f加载文档数量: {len(docs)}) # 2. 文本清洗去掉常见噪声 for doc in docs: lines [line.strip() for line in doc.page_content.split(\n)] lines [line for line in lines if line and not line.startswith(!--)] doc.page_content \n.join(lines) # 3. 切分按标题/段落优先块 800 字符overlap 150 splitter RecursiveCharacterTextSplitter( chunk_size800, chunk_overlap150, separators[\n## , \n### , \n\n, \n, 。, , , ], keep_separatorTrue, ) chunks splitter.split_documents(docs) print(f切分后片段数: {len(chunks)}) print(f示例片段长度: {len(chunks[0].page_content)} 字符) # 4. 嵌入 入库Chroma 自动创建集合 embedding OllamaEmbeddings(modelbge-m3) vector_store Chroma.from_documents( documentschunks, embeddingembedding, persist_directory./chroma_db, collection_nameproduct_manual, ) print(索引完成向量库已持久化到 ./chroma_db)代码里有几个值得解释的细节。第一切分器的separators我把\n## 放在最前面这意味着遇到 Markdown 二级标题时会优先在标题处切开把每个章节作为一个相对完整的语义单元。第二keep_separatorTrue会把标题保留在下一段开头避免丢失层级信息。第三persist_directory指定了向量库落盘路径下次启动时直接加载不用重新索引。这里顺便提醒一句Chroma 的persist_directory在较新版本的 API 里有变化如果你遇到“目录下没有数据”的问题检查一下是不是用了旧的PersistentClient写法。3.3 查询侧核心代码检索、重排序与增强生成索引建好之后查询侧的实现就是“检索 组装 生成”三步。我先给一个不依赖重排模型的最小可用版本然后再说怎么加重排序。from langchain_chroma import Chroma from langchain_ollama import OllamaEmbeddings, ChatOllama from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser # 加载已有向量库 embedding OllamaEmbeddings(modelbge-m3) vector_store Chroma( persist_directory./chroma_db, embedding_functionembedding, collection_nameproduct_manual, ) # 用户问题 question 设备校准时对环境温度有什么要求 # 1. 向量检索召回 Top 5 retriever vector_store.as_retriever(search_kwargs{k: 5}) docs retriever.invoke(question) print(f检索到 {len(docs)} 个相关片段) for i, doc in enumerate(docs): print(f--- 片段 {i 1} (score 由底层实现提供) ---) print(doc.page_content[:150].replace(\n, )) print(f来源: {doc.metadata.get(source, unknown)}) # 2. 构造增强 Prompt template 你是一个严谨的技术支持助手。 请基于以下检索到的资料回答用户问题。 如果资料中没有相关信息请直接说明根据现有资料无法回答不要编造。 回答时可以引用资料中的内容并在末尾标注来源文件名。 【检索资料】 {context} 【用户问题】 {question} 【回答要求】 1. 先用一句话给出直接结论 2. 再分点补充关键细节 3. 最后列出引用的资料文件名 prompt ChatPromptTemplate.from_template(template) # 3. 生成 llm ChatOllama(modelqwen2.5:7b, temperature0.2) chain prompt | llm | StrOutputParser() response chain.invoke({ context: \n\n---\n\n.join( f[来源: {d.metadata.get(source, unknown)}]\n{d.page_content} for d in docs ), question: question, }) print( 最终回答 ) print(response)这个版本的查询侧做了几件在初版时特别重要的事检索结果按来源文件拼接、Prompt 里明确了“无资料就承认不知道”的规则、要求输出引用来源。单是这三条就能让回答的可靠性上一个台阶。temperature我建议设置得低一些RAG 场景要的是稳定和忠实不需要模型发挥想象力。如果你想要更好的检索精度可以在大模型生成前插入一个重排序步骤。我通常的做法是先用向量检索拿 Top 20再用重排序模型筛出 Top 4过滤掉语义分数明显偏低的结果。这样多花一次推理时间但回答质量提升明显。3.4 把 RAG 接入 Agent 的检索工具RAG 管道本身不是终点它要成为 Agent 的一个可调用工具才算真正融入。在 LangChain 里最直接的做法是把整个 RAG 链路包成一个 Tool然后注册给 Agent让 Agent 根据用户意图自行决定是否调用。from langchain_core.tools import tool from langchain.agents import create_tool_calling_agent, AgentExecutor tool def knowledge_base_search(question: str) - str: 当用户的问题涉及产品规格、操作流程、故障排查、参数要求时 调用此工具查询产品手册知识库。 docs retriever.invoke(question) if not docs: return 知识库中没有找到相关资料。 return \n\n---\n\n.join( f[来源: {d.metadata.get(source, unknown)}]\n{d.page_content} for d in docs ) agent_prompt ChatPromptTemplate.from_messages([ (system, 你是产品技术支持 Agent。涉及产品手册问题时必须调用 knowledge_base_search。), (human, {input}), (assistant, {agent_scratchpad}), ]) agent create_tool_calling_agent(llm, [knowledge_base_search], agent_prompt) executor AgentExecutor(agentagent, tools[knowledge_base_search], verboseTrue) result executor.invoke({input: 设备校准前需要预热多久}) print(result[output])把 RAG 包成 Agent 的 Tool 之后一个很重要的好处是不再每次强制检索。用户只是闲聊或者问通用问题时Agent 会直接回答省去无谓的检索开销一旦问题涉及知识库内容它才触发检索。这也是后续要讲的 Agentic RAG 的雏形。4. 检索质量怎么量化不要靠“感觉”调参4.1 离线评估指标Hit Rate、MRR、NDCG 与忠实度很多开发者在调 RAG 时有一个通病改了切分参数、换了嵌入模型只能靠“多问几个问题感觉一下”来判断好坏。这种方法既不客观也无法沉淀成可复用的优化经验。正确的做法是建立一套离线评估集用指标说话。我常用的指标有四类Hit Rate命中率对每个测试问题预先标注出“期望召回哪些文档片段”然后看检索结果里是否包含这些片段命中算 1否则算 0。它衡量的是“该召回的东西有没有漏”。MRR平均倒数排名在命中基础上进一步看重排的位置。如果正确答案排在第 1 位MRR 贡献 1排在第 3 位贡献 1/3。它衡量的是“正确答案排得够不够靠前”。NDCG归一化折损累计增益更精细的排序质量指标允许你把召回片段按相关性强弱分级打分不只看命中和排名。忠实度Faithfulness评估生成答案中是否有“知识库里没有的信息”。做法是逐句检查回答判断每个句子能否在检索片段里找到依据。忠实度低的回答即使听起来很流畅生产环境也不能用。理论上说Hit Rate 和 MRR 负责检索质量忠实度负责生成质量。调试时先看检索侧指标检索侧不达标就直接检索侧不要盲目改 Prompt。4.2 搭建一个最小可用的评估集我实践下来最小可用的评估集不用做得多庞大30 到 50 条高质量问题就够发现大部分问题。构造评估集的步骤是从知识库里随机抽 5 到 10 个有代表性的章节每个章节人工浏览后提炼 3 到 6 个问题。问题类型尽量覆盖事实型“某某参数是多少”、列举型“有哪些模式”、流程型“故障后怎么处理”、对比型“A 和 B 的区别”。对每个问题人工标注出答案所在的原文片段用片段 ID 或文本开头几个字标注。把评估集存成 JSON 或 CSV留好字段question、gold_chunk_ids、expected_answer。跑评估的方式很简单对每个问题跑一遍检索把召回的 Top 5 片段 ID 和 gold_chunk_ids 比对算出 Hit Rate 和 MRR。再调用生成链路对回答做忠实度检查。跑完一轮之后你才好判断改参数到底有没有效果。这里分享一个我踩过的坑评估问题的时候别只写和原文一模一样的问句那样测出来都是虚高。要模仿真实用户的口吻适当用同义词改写、省略上下文。比如原文写“RAG 管道包含索引侧和查询侧”评估问题就要写“知识库检索和答案生成这两部分是怎么连接的”而不是直接问“RAG 管道包含哪些侧”。4.3 线上评估日志、反馈与引用可追溯离线评估只能保证“上线前的基线”真正的问题往往在真实流量里才暴露。我在线上系统里至少会给 RAG 链路补三块第一是完整链路日志。每次检索请求记录下用户问题、检索用 query、召回的片段 ID、排序分数、最终生成的回答。这看起来是基本的可观测要求但很多项目上线后才意识到没留日志出了问题无从查起。第二是用户反馈信号。在回答下方放“有帮助/没帮助”按钮或者让用户能点赞点踩。这类信号噪声很大但能帮助你发现“某个领域的回答持续被点踩”这种系统性问题。更可靠的是“追问率”如果用户频繁追问同一个问题说明第一轮回答大概率没答到点上。第三是引用可追溯性。生产环境的每个回答不仅要给出答案还要能明确知道“这个回答基于哪几个片段”。这样出现问题时可以直接定位到引用的片段去修正知识库而不是盲目调模型。这也是 RAG 相对纯大模型对话最核心的生产优势之一。质量评估这块很容易被初学者忽略等到上线再补往往已经晚了。我的建议是第一天搭管道第二天就顺手把评估集建起来哪怕只有 20 条问题也比“凭感觉调参”强得多。5. 实战中的常见问题与排查方案5.1 检索引擎拉不出真正相关的片段这是 RAG 项目里最普遍的问题明明知识库里就有答案检索却把它漏掉了。根据我的经验原因大概率出在三个地方。第一个是切分策略不合理。如果片段切得过大一个片段里混了多个主题嵌入向量会被平均化检索时哪个主题都不突出。解决办法是换成父子块策略用小子块检索、大上下文生成。第二个是嵌入模型和领域不匹配。通用嵌入模型对专业术语的理解能力有限我遇到过一个法律文档场景通用模型把“不可抗力条款”检索成“自然灾害保险条款”换了领域微调过的模型之后情况明显改善。第三个是查询本身的表达方式和原文差异太大。比如原文用的是“信息安全管理规范”用户问的是“数据保护制度”向量检索可能匹配不到位这时候需要做查询改写或同义词扩展。排查这类问题我的标准动作是先人工看一遍 Top 10 检索结果把“其实相关但没召回”的样例收集起来分析是切分边界问题、语义鸿沟问题还是排序问题。有针对性地调整不要盲改参数。5.2 回答照抄原文、缺乏归纳或者幻觉和编造并存一种常见的情况是检索确实召回了相关内容但生成的回答像是把原文段落直接复制粘贴没有形成对用户问题的直接回应。这通常是因为 Prompt 里的任务指令太弱。我建议在 Prompt 里明确“基于资料用自己的话组织回答不要大段引用原文”同时要求“先给出直接结论再展开说明”。另一种情况更危险回答里有编造的细节但这些细节并不是知识库提供的。这多半是生成模型自己发挥了“创造力”。我的排查顺序是先看回答中的关键数字、日期、专有名词能否在召回片段里找到依据找不到就说明生成侧没有严格遵守“只依据资料回答”的约束。这时把temperature调低同时把 Prompt 里“没有资料就承认不知道”的约束写得更强硬一般能压住大部分幻觉。如果还压不住就要考虑换一个指令遵循能力更强的生成模型。5.3 知识更新与增量入库的坑RAG 上线之后知识库不是一成不变的文档会更新、会删除、会新增。增量入库这里有个经典连环坑同一个文档更新后向量库里新旧版本并存检索时旧片段仍然会被召回。避免这个问题的标准做法是给每个文档分配稳定的文档 ID入库前先按 ID 清理旧片段再写入新片段。如果你用 Chroma可以先把旧的按 metadata 里的文档 ID 过滤删除再执行新增。另外边更新边查询时可能会出现“刚更新的内容查不到”的现象。这不是检索的问题而是索引管道有延迟。如果你对更新时效要求很高可以考虑维护一个“热数据通道”高频更新的少量文档直接做实时切分和嵌入冷数据走批量管道。这个设计在实践上比单纯调检索参数更有效。5.4 Java 生态里的 RAG 差异Spring AI、LangChain4j 侧重点提示热词里出现了不少 Java 相关的 RAG 话题比如 Spring AI、LangChain4j。如果你所在团队是 Java 技术栈这里简单提一下差异LangChain4j 的架构里EmbeddingStore、ContentRetriever等组件和 Python 生态的逻辑类似最大的区别在于类型约束更强、文档加载器相对少一些。Spring AI 的RetrievalAugmentationAdvisor则把 RAG 做成了 Advisor 模式可以优雅地和 Spring Boot 的依赖注入整合。Java 生态里我特别想提醒的一点是不要在 Java 侧重新发明一套切分和检索逻辑直接复用框架内置的组件就好。很多团队为了“可控性”选择手写向量检索结果既没控制好又慢了项目进度。先把标准管道跑通后续再根据评测结果做定制。问题现象常见原因排查与解决方向召回不到相关片段切分粒度不当 / 嵌入模型不匹配 / 查询改写缺失检查 Top 10 人工结果换切分策略或嵌入模型召回内容多且杂无重排序 / Top K 过大加重排序模型减小 Top K回答照抄原文Prompt 任务指令过弱增加“用自己的话组织结论”的要求回答中出现编造信息temperature 过高 / 约束不足调低 temperature强化约束 Prompt更新后旧内容仍被召回未按文档 ID 清理旧片段入库前先按元数据删除旧版本刚入库内容查不到索引管道延迟热数据走实时通道冷数据批量入库6. 进阶方向从 RAG 到 Agentic RAG6.1 Agentic RAG把检索决策交给 Agent基础 RAG 的流程是固定的不管用户问什么系统都走一遍“检索 → 生成”。这种方式在问题简单时够用但遇到复杂任务就暴露局限。比如用户问“根据产品手册和项目文档给出设备部署方案”这种问题既涉及多知识源交叉又涉及检索策略选择固定流程很难一次搞定。Agentic RAG 的思路是把检索的决策权交给 Agent让 Agent 根据当前问题动态决定“要不要检索、检索什么、检索几次、要不要换一种检索方式”。具体做法我在 3.4 里已经展示了雏形——把 RAG 包成 Tool。进阶版是给 Agent 配多个检索工具一个负责向量检索、一个负责关系型查询、一个负责调用搜索 APIAgent 根据问题类型选用合适的工具还能根据第一轮检索结果决定是否补充二次检索。我个人的看法是Agentic RAG 比基础 RAG 灵活得多但也复杂得多。它的效果强依赖底层模型的推理能力如果模型本身的工具调用能力一般强行上 Agentic RAG 反而会翻车。建议先把基础 RAG 的检索质量和评估体系建好再逐步开放 Agent 的检索决策权。6.2 GraphRAG、Ontology RAG 与向量 RAG 怎么选最近热词里 GraphRAG、Ontology RAG 出现得不少。它们本质上都是在弥补向量检索在“关系理解”上的不足。向量检索擅长找“内容相似”的片段但不擅长回答“实体 A 和实体 B 有什么关系”这类问题因为这些关系没有直接写在某个片段里而是分散在多处。GraphRAG 的做法是先把文档构建成知识图谱节点是实体边是关系检索时既做向量召回又沿图结构做多跳扩展。Ontology RAG 更进一步在图上叠加了本体层给实体和关系定义了明确类型和约束适合强规范领域。选择建议很简单如果你的场景大多是单文档问答向量 RAG 就够了如果场景涉及大量实体关系比如“这个设备由哪些部件组成”“哪些项目用过这个方案”再考虑 GraphRAG。Ontology RAG 维护成本更高一般只建议在知识体系非常规范且对答案一致性要求极高的场景如医疗、法律引入。我个人一般不推荐小团队一上来直接上图谱方案先把向量基线做到位用数据说话。6.3 Skill 与 RAG 的结合方式让技能触发检索而不是每次强制检索一个我最近在探索的方向是“Skill RAG”的组合模式。之前的方案里检索是挂在 Agent 上的一个工具Agent 来决定调不调。但更精细的做法是把“何时检索、检索后怎么用”当成一个 Skill 来编排让不同的任务类型拥有不同的知识获取策略。举一个实际的例子在客服 Agent 里用户问“退货流程”时需要的是一份高度结构化的流程说明检索策略应该偏向章节级片段并且生成时要求按步骤输出用户问“某产品是否支持某个接口协议”时需要的则是技术规格片段检索策略应该偏向精确匹配生成时要求给出协议版本号。这两种场景如果共用一个检索配置效果一定会互相拖累。解决方式是定义多个 Skill每个 Skill 里写清楚触发条件、检索参数Top K、相似度阈值、检索范围按元数据过滤、Prompt 模板和输出格式。Agent 根据用户意图先路由到对应 Skill再执行 Skill 内定好的检索和生成策略。这个模式的效果比单一大 RAG 稳定得多也更方便团队分工维护。6.4 把 RAG 放进更大的 Agent 工程里最后聊一点工程层面的体会。RAG 在演示环境里很容易出彩但生产环境里它只是知识子系统的一环要跟权限控制、日志审计、模型网关这些设施一起设计才走得稳。比如企业内部知识库不同角色能检索的文档范围不同如果你不把权限过滤设计进检索阶段再强的模型也会泄密。另外一点是成本控制。RAG 的推理成本中很大一部分来自“喂进去的上下文长度”。每次检索召回 5 个片段每个片段 800 字加上问题和其他指令单次调用的 token 消耗跟你直接对话比要高得多。上线前建议做一轮 token 用量估算必要时用小模型做生成侧用大模型只处理复杂问题。我在前几篇里提过一个观点Agent 的工程复杂度本质上就是“把不可控变得可控”。RAG 做的正是这件事它把大模型的知识来源从不可控的参数记忆变成可检索、可追溯、可更新、可过滤的非参数知识管道。你在 RAG 上花心思做的每一次切分调整、每一条评测标注、每一份日志留存都是在为 Agent 的可靠性加码。这也是我最想通过这篇系列文章传达的东西Agent 的能力上限很大程度取决于它获取知识的方式有多扎实。