ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

RAG原理与实战:为Agent构建可靠的知识获取管道

RAG原理与实战:为Agent构建可靠的知识获取管道 1. 为什么 Agent 需要一条“知识获取管道”接触 AI Agent 一段时间的人常会碰到同一个困惑模型明明很聪明可一问到私有知识、最新文档或者公司内部资料就开始一本正经地胡说八道。这不是模型不行而是缺少一个关键环节——让模型在回答之前“查资料”。RAGRetrieval-Augmented Generation检索增强生成就是干这件事的它是 Agent 获取外部知识的主要管道甚至可以说是从玩具级 Demo 走向可用级应用的必经之路。先说清楚经常混在一起的三组概念Agent、LLM、AI 模型到底是什么关系。AI 模型是个大类涵盖各种深度学习模型LLM大语言模型是其中擅长文本生成的那一类比如大家经常听到的 DeepSeek、GPT 系列都属于 LLM。Agent 则是构建在 LLM 之上的应用实体它不只会对话还会自己规划步骤、调用工具、读取知识最终完成一个目标。DeepSeek 本身是模型不是 Agent用 DeepSeek 做推理内核再给它配上工具和知识库这才叫 Agent。RAG 就是给 Agent 配的那根“输血管”把外部知识源源不断地喂给它。那 RAG 解决的核心痛点是什么总结下来无非三类知识的时效性、私有性与可溯源性。模型的训练数据有截止日期新发生的事情不知道公司的内部文档和数据不会公开出现在训练集里就算模型知道答案也没法告诉你这个结论出自哪份材料。RAG 用“先检索、后生成”的思路把这三件事一并解决。这一篇是系列第四篇如果前三篇你已经理解了 Agent 的规划能力和工具调用那么这篇要讲的就是Agent 到底从哪里“知道”那些模型本身不知道的事情。市面上关于 RAG 的文章非常多但大多要么太浅只讲了“切块-向量化-检索”三步要么太深直接甩一堆代码没讲为什么这么做。这篇文章会站在 Agent 开发的角度从原理讲到实操尽量把每一步的“为什么”也说清楚最后再加一些调试中踩过的坑。不管你是想给 Agent 加知识库还是想理解 RAG 的底层机制这篇应该能给你一个比较完整的视角。2. RAG 整体设计从“查资料”到“写答案”的完整链路把 RAG 想成一个图书馆的参考咨询台可能更好理解。用户来问一个问题咨询员不会凭记忆直接回答而是先去书架上检索相关书籍翻阅几本最相关的再结合自己的理解整理出一个答案并且告诉用户“这个说法来自哪本书的第几页”。RAG 就是把这个流程自动化用向量相似度代替人工翻书用 LLM 代替咨询员的语言组织能力。2.1 经典 RAG 的三个阶段一套完整的 RAG 流程从数据进入系统到最后生成回答一般分成三个阶段索引Indexing、检索Retrieval、生成Generation。索引阶段负责把原始文档拆解、清洗、向量化存进一个专门的数据库里这个过程是一次性的“入库”检索阶段负责在用户提问时从库里找出跟问题最相关的若干片段生成阶段负责把检索到的片段连同用户问题一起交给 LLM让它基于这些材料组织答案。索引阶段听起来简单但实际是三个环节里最容易出错、也最需要反复调优的。文档格式千奇百怪PDF 里的表格、PPT 里的要点、扫描件里的图片每种格式的处理方式都不同。更关键的是“切块策略”——把文档切成多大一块最适合检索这个参数直接决定了后面检索质量的上限。后面会专门展开讲。检索阶段的技术选型就比较丰富了。主流做法是向量检索把用户问题和文档片段都映射到同一向量空间然后算余弦相似度取 Top-K。但它有个天然缺陷只认“语义相似”不认“关键词精确”。比如用户问“今年 Q3 的营收是多少”如果知识库里的文档写的是“第三季度营业收入”向量检索大概率能找到因为语义相似但如果文档里写的是“三季度收入”向量检索也不一定找得到因为两个词表面差异比较大。所以稍微讲究一点的系统会加上倒排索引做关键词召回再做一个混合检索把两种结果融合起来。生成阶段是最后一步也是最容易“翻车”的一步。把检索到的片段拼进 Prompt让 LLM 严格基于片段回答。这里面的难点在于LLM 有很强的“发挥”倾向你让它基于文档回答它可能开头是文档里的内容后面开始自由发挥补充一些它“觉得对”的信息。所以 Prompt 里必须明确约束比如“如果片段中没有相关信息请直接回答不知道”这样能很大程度减少幻觉。2.2 数据准备决定 RAG 天花板的环节整个 RAG 链路里最不性感但最决定成败的往往是第一步——数据准备。很多人第一次搭 RAG 时兴冲冲把一堆 PDF 导进去向量化、检索、生成一套跑通然后发现效果惨不忍睹问什么都答非所问。绝大多数原因都出在这三个字数据太乱。文档解析是第一道坎。PDF 分两种文字版 PDF 可以直接抽取文本但排版复杂时比如双栏、带页眉页脚、混排表格抽取结果会很乱。扫描版 PDF 本质是图片必须走 OCR这就涉及 OCR 的准确率问题。Excel、PPT 又是另一套解析逻辑。我的经验是务必对文档做分类型处理不要用一个解析器打天下。比如文字版 PDF 按版面顺序抽取扫描版优先走专用的 OCR 模型表格导出成结构化的 Markdown 或 CSV效果会好很多。清洗则是第二道坎。解析出来的文本往往带着页眉页脚、页码、多余换行等噪音如果直接切块这些噪音会混进向量里把检索结果带偏。一个简单但高效的清洗流程是这样先去除页眉页脚干扰再按段落边界规整文本去掉多余空行、统一空格和标点最后对明显乱码的字符直接丢弃。这一步做扎实了后面切块和检索的难度都会低很多。2.3 三种不用向量库的 RAG 场景很多网上的教程默认 RAG 向量数据库选型 Embedding API 调用这套组合覆盖面确实广但不是所有场景都需要向量库。至少有三个场景完全可以不用向量检索第一种是“小知识库 强规则”的场景。如果知识库只有几百个条款、几十页 FAQ直接全文塞进 Prompt 反而可能比检索后再回答质量更高因为现代 LLM 的上下文窗口已经很大一两万字以内直接放进去完全扛得住还省掉了一整套架构。第二种是“强关键词场景”。比如查代码、查精确命名关键词匹配比向量相似度可靠得多用 Elasticsearch 的 BM25 算法就足够了。第三种是“规则限定场景”。比如知识库自带清晰的分类和编号体系用户提问时通常也会带上分类或编号那直接把分类作为筛选条件再做小范围检索速度和准确率都会更好。所以搭建 RAG 之前先问自己一个问题我遇到的问题真的是靠“语义相似度”才能解决的吗如果是向量库是对的如果不是别被技术惯性拖着走。3. 核心细节拆解切块、嵌入与检索三个最容易出问题的环节说完了整体链路这一节把最关键的技术细节单独展开讲。切块、嵌入、召回这三个环节直接决定了一个 RAG 系统的“检索质量”也是后续所有调优工作的高频阵地。3.1 切块策略大小、重叠与边界切块Chunking就是把长文档切成一个个小块的过程。为什么必须切两个原因一是向量检索的粒度问题如果一整本手册变成一个向量你问一个具体问题时向量会被整本书的“平均语义”拉偏检索精度极差二是 LLM 上下文窗口有限哪怕现在模型普遍支持几十万 token 的上下文塞进去太多不相关内容反而会稀释注意力回答质量会下降。切块大小是一个典型的需要调优的超参数并没有一个“放之四海而皆准”的答案。我自己的经验分布大概是这样的代码或技术文档256-512 token 的比较合适因为这类文档中信息密度高块太大噪音较多通用知识类文档512-1024 token 比较平衡如果是需要完整逻辑链的内容比如长篇合同或论文往往需要按章节甚至按大段落切同时保留章节标题作为上下文。切块时最容易踩的坑是“硬切”导致语义断裂。一句话被拦腰截断、一个表格被拆成两半、一个知识点的描述被分开到两个块里。这时检索很容易漏掉关键信息。解决办法有两个一是设置“重叠窗口”相邻块之间重叠一部分文字通常是 10%-20%这样即使一个知识点正好跨边界也能在相邻的块里完整保留二是尽可能按“语义边界”来切比如按段落切、按 Markdown 标题层级切、按代码函数边界切而不是按固定 token 数硬切。实际工程中我偏爱一种组合方案按结构切分 大小限制 重叠回退。先用文档结构标题、段落做初步切分如果一个段落太长再用固定大小切成子块并且保留 50-100 token 的重叠。这样整体结构完整细节又不丢失。市面上主流的切分工具比如 LangChain 的 RecursiveCharacterTextSplitter思路上就是这种但具体的分隔符优先级和大小参数需要自己反复实验调。3.2 嵌入模型怎么选、怎么比较嵌入Embedding是把文字变成向量的过程切出来的每一块文本都要通过嵌入模型生成一个向量。这个向量要能代表文本的语义同时语义相近的文本在向量空间里的距离也近。嵌入模型选得好不好直接决定“语义相近能有多近”。选嵌入模型时有四个关键点要关注中文效果、向量维度、最大输入长度、本地部署可行性。中文语料下市面上比较常见的开源模型包括 BGE 系列、智源的 text2vec 系列等商业 API 则有各家云厂商的通用文本向量模型。向量维度影响存储开销维度越高存储越大最大输入长度决定能否直接处理较长的块如果嵌入模型只支持 512 token那切块切成 1024 token 就会出问题必须先截断再嵌入而截断又会导致信息丢失。所以切块大小其实要跟嵌入模型的能力对齐。怎么比较不同嵌入模型的效果不要只看宣传数据自己上测试。拿一批有代表性的真实问题每个问题都从自己的知识库里找“应该召回”的正确答案然后分别用不同模型做嵌入和检索计算“召回率K”——也就是前 K 个召回结果里有多少比例的正确结果。我见过太多项目在嵌入模型上随便选了个 API最后检索效果不好还以为是切块的问题。用召回率这个指标做基准测试可以快速定位问题出在哪个环节。另外有个很容易被忽略的坑用户问题在嵌入前需要做预处理。很多场景下用户的提问方式跟文档里的表述方式差距很大比如用户问“下单后多久发货”文档里写的是“发货时效说明”。更好的做法是先让 LLM 把问题改写为更明确的检索语句Query Rewrite比如转成“发货时效是多久”再去检索。这个技巧后面在 Agentic RAG 部分还会再展开。3.3 检索策略从单路 Top-K 到混合召回与重排检索阶段最常见的实现就是向量检索取 Top-K但这种方式有两个明显缺陷一是只能召回语义相近的关键词精确匹配反而可能漏掉二是向量检索的分数精度不够Top-K 的返回结果里经常混入一些“看起来相关但实际没用”的片段。所以稍微认真一点的系统都会做混合召回 重排。混合召回的核心思路是“两条腿走路”。向量检索负责召回语义相近的内容关键词检索比如 BM25负责召回字面匹配的内容两边各有 Top-N 的结果然后合并去重。合并以后问题又来了两种检索的分数尺度不一样不能直接放在一起排序。这时候最好的办法是引入一个重排模型Reranker。它跟嵌入模型不同输入的是“问题 候选片段”这对组合输出一个相关性的精细分数专门用来给召回结果排序。实际效果上一个质量好的 Reranker 通常能带来 10-20 个百分点的检索效果提升性价比极高。用重排模型需要注意一个问题它跟嵌入模型一样也有成本调用价格更高速度也更慢。所以工程上的惯例是先在低成本阶段取 Top-20 或 Top-50再用重排模型精排到 Top-3 或 Top-5最后把高质量的几个片段送给 LLM 生成。既要效果也要控制延迟。另外一个常常被忽略的细节是“元数据过滤”。给每个块都打上来源、章节、时间、类别等标签检索时先按条件过滤一批再做相似度计算。比如用户问“2024 年的销售数据”那就先过滤年份为 2024 的块再在里面检索。这个办法能精准排除大量干扰项效果立竿见影。我自己项目的知识库基本都会保留“文档名 标题路径 时间戳 类型”四个字段作为元数据。4. 实操记录从零搭建一个本地 RAG 服务理论讲了一堆来说点实在的。下面这套方案是一个相对标准、可复现的 RAG 搭建流程主要以本地可部署的开源组件为主适合个人学习、小团队搭建内部知识库。我以最常见的“本地知识库”场景为例读一批 PDF 和 Markdown 文档建立索引然后能基于它们回答问题。整体架构不复杂但对理解整条链路非常有帮助。4.1 技术选型为什么这么搭整套技术栈我选了四件套文本解析用 LangChain 的文档加载器切块用 LangChain 的文本分割器向量库用 Chroma嵌入和生成用本地 Ollama 跑的模型这是我自己在项目中试过比较顺手的组合。Chroma 作为向量库轻量、开源、支持本地持久化不用单独起一个服务端对起步阶段非常友好。Ollama 则可以一条命令把嵌入模型和 LLM 都跑起来省掉配置推理服务端的繁琐。如果你的环境跑不动本地模型把嵌入和生成换成任意云厂商的 API 也完全可以流程逻辑不变。但强烈建议至少在起步阶段用本地模型一来调试方便不花钱二来可以反复实验不同模型的效果之后再迁移到云端服务。4.2 安装环境与依赖首先确保本机已装好 Python 3.10 以上、Ollama 和一个趁手的代码编辑器。然后按以下步骤操作# 安装依赖建议在虚拟环境里装 pip install langchain langchain-community chromadb ollama需要说明的是LangChain 版本迭代频繁API 时有变动如果跑代码时遇到某个类方法不存在先检查版本兼容性不要盲目改代码。我写这篇文章时用的组合是 LangChain 0.2.x整体接口比较稳定。接着拉取模型。嵌入模型我选的是nomic-embed-text体积小、中文效果够用生成模型选qwen2.5:7b中文能力在同尺寸模型里表现不错ollama pull nomic-embed-text ollama pull qwen2.5:7b这两个模型一个负责把文本变向量一个负责按检索结果生成回答。分别对应 RAG 的检索和生成两个阶段。4.3 索引加载、切块、入库先写一个脚本加载目录下的文档并切块、生成向量、存入 Chroma。为了方便理解这里只演示 Markdown 文件的处理PDF 和 Word 可以换成对应的加载器。import os from langchain_community.document_loaders import TextLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.embeddings import OllamaEmbeddings from langchain_community.vectorstores import Chroma # 1. 加载文档 docs [] for root, dirs, files in os.walk(./knowledge): for file in files: if file.endswith(.md) or file.endswith(.txt): loader TextLoader(os.path.join(root, file), encodingutf-8) docs.extend(loader.load()) print(f加载了 {len(docs)} 个文档) # 2. 切块 text_splitter RecursiveCharacterTextSplitter( chunk_size500, # 每块最大 500 字符 chunk_overlap80, # 重叠 80 字符 separators[\n## , \n### , \n\n, \n, 。, , ] ) chunks text_splitter.split_documents(docs) print(f切分为 {len(chunks)} 个块) # 3. 嵌入并入库 embeddings OllamaEmbeddings(modelnomic-embed-text) vectorstore Chroma.from_documents( documentschunks, embeddingembeddings, persist_directory./chroma_db, ) print(索引构建完成)这里的separators参数值得解释一下它的顺序代表切分时的优先级。我这里把\n##放在最前面意思是优先按 Markdown 二级标题切其次按三级标题、段落、句子、分号逐步回退。这样切出来的块相对语义完整比单纯按字符长度硬切质量好一个档次。chunk_size500是我在中文文档上比较常用的起点500 个中文字符大约对应 700-800 个 token。如果你的文档信息密度高可以适当调小到 300如果是叙述性内容可以调大到 800。这个参数后面一定会反复调别指望一次到位。4.4 检索混合召回加本地重排接下来实现检索。单纯向量检索很简单但为了把效果拉起来我同时做了关键词检索和向量检索的混合再用一个轻量模型对两边结果做重排保留 Top-3 片段传递给生成阶段。# 向量检索 vector_results vectorstore.similarity_search_with_score(query, k20) # 关键词检索可以直接用向量库内部的文本索引简化这里示意 # 用 Chroma 自带的 where 子句做简单元数据过滤 关键词匹配 keyword_filtered vectorstore.similarity_search_with_score( query, k20, filter{source: {$in: target_files}}, ) # 合并两组结果按文档名块序号去重 all_results {} for doc, score in vector_results keyword_filtered: key (doc.metadata.get(source), doc.metadata.get(chunk_index)) if key not in all_results: all_results[key] (doc, score) # 重排把候选块和问题拼在一起用 LLM 打分取前 3 candidate_blocks [doc.page_content for doc, _ in all_results.values()][:10]重排这一步在本地做有一个很轻量的办法不引入专门的 Reranker直接用生成模型对候选块做“相关性判断”。比如把每个候选块前加一句“这段内容是否与问题相关”让 LLM 输出 1-5 分的评分按评分排序。这种方式在候选只有 10-20 个块的轻量场景下完全够用且不需要额外部署模型服务。如果你的数据量更大、对延迟更敏感建议上专门的 Reranker 模型比如 BGE-reranker。本地部署一个小的 reranker 也不难只是这里不再展开。4.5 生成把召回结果交给 LLM检索做完最后一步是让模型基于召回的片段回答。这一步的 Prompt 设计有讲究from ollama import chat def generate_answer(question, contexts): ctx_text \n\n---\n\n.join( f[来源]{c[source]}\n{c[content]} for c in contexts ) prompt f你是一个基于知识库回答问题的助手。请只根据下面的资料回答问题。 如果资料中没有相关信息请直接回答“知识库中未找到相关信息”不要编造。 参考资料 {ctx_text} 用户问题{question} 请用中文回答并说明信息来源。 response chat(modelqwen2.5:7b, messages[{role: user, content: prompt}]) return response[message][content]这里有个关键细节Prompt 中把来源标注在每段内容前面。这么做有两个好处一个是在生成时提醒模型“所有信息都有出处别乱编”另一个是方便在生成答案时自动追溯来源。实际项目中可以在基座上再叠加一个“引用脚注”机制比如模型输出一段话后我们通过匹配原文来标注引用序号。虽然实现起来有点繁琐但当用户拿着答案去追查原文时体验会好非常多。到这里一个最朴素但可用的 RAG 服务就完成了加载文档、切块、向量化、检索、重排、生成整个闭环跑通。5. 把 RAG 接进 Agent多轮对话与 Agentic RAG跑了基础链路之后下一步就是要把 RAG 真正接进 Agent 的场景里。这一步会遇到几个新的问题其中最典型的是普通 RAG 是“一问一检”用户问什么就去查什么但 Agent 的对话是多轮的用户往往会先问“你们公司的退货政策”然后又追问“那运费谁出”。第二问里的“运费”跟第一问的“退货政策”是相关的如果 Agent 只拿第二句话去检索很可能查不到准确信息。多轮 RAG 的核心思路是先做“对话压缩”。把用户当前问题连同之前的对话历史一起交给 LLM让它改写成一个“独立的、完整的、包含必要背景的检索语句”。比如上面这个例子改写结果应该是“退货政策中关于运费承担的说明”。用改写后的语句去检索命中率会高很多。这个做法在 LangChain 里有现成的模块叫HistoryAwareRetriever核心逻辑就是对话打包 改写 检索。再进一步就是今年热度很高的 Agentic RAG。传统 RAG 流程是固定的一次检索一次生成Agentic RAG 则把 RAG 从固定流程变成了一个“可自主决策的 Agent 行为”。Agent 不再只是调一次检索工具而是可以自己判断这个问题需不需要检索需要检索哪类知识库当前的召回结果够不够回答不够的话要不要换个查询词再查一次这就把 RAG 变成了 Agent 能自主调用的“工具”而不是一条写死的链路。一个典型的 Agentic RAG 工作流长这样Agent 先接收到用户问题做一个初步判断——如果问题很简单、自己有把握就直接回答不进入知识库如果问题涉及事实性或私有知识就触发检索。检索完把结果给到模型模型判断结果是否足够回答用户的问题不够的话改写查询词搜索不同的子知识库或者查上下文更完整的相邻块再一次尝试。这种“尝试-判断-再尝试”的循环让 RAG 从流水线变成了一个有反馈回路的智能系统。实现上LangChain 的create_retrieval_agent或直接自己用思维循环控制检索工具的调用都可以。关键不是用哪个框架而是理解一个核心转变检索不再是固定动作而是 Agent 决策的一部分。6. RAG 与 MCP、Ontology 的关系以及常见问题排查RAG 相关话题里经常被一起提到的还有 MCP。很多刚接触 Agent 开发的人会把这两个概念搞混其实它们解决的是完全不同的问题。RAG 解决的是“模型怎么获得知识”MCPModel Context Protocol解决的是“模型怎么调用工具和数据源”。换句话说RAG 是知识获取管道MCP 是连接外部工具的统一协议。MCP 可以让 Agent 调用各种外部服务其中一个服务类型恰恰就是“知识库检索”。你可以用 MCP 暴露一个 RAG 服务给 Agent 用但 MCP 本身不负责检索和生成它只是管道。两者是互补关系可叠用不可互相替代。还有一个容易让人困惑的概念是 Ontology RAG。传统 RAG 把文档切成块做向量检索是扁平化的语义匹配Ontology RAG 则是在知识库之上加了一层结构化的“本体定义”也就是实体、属性和关系的显式体系。它把文档里的知识组织成图谱结构检索时既可以按向量语义找也可以按实体关系跳转。典型例子是医疗领域用户问“某种药能不能跟某食物同食”传统 RAG 可能只找得到描述类似的段落而 Ontology RAG 能沿着“药物-相互作用-食物”的关系路径做推理。代价是构建本体的人工成本较高不适合起步阶段。说完概念区分分享一下实际调试 RAG 系统最常见的问题和排查思路。如果出现“答案不相关”按以下顺序排查先看召回环节——把检索返回的 Top-K 片段打印出来手工判断片段跟问题是否相关。如果片段本身不相关说明问题出在检索请检查切块策略、嵌入模型、元数据过滤是否合理。如果片段相关但答案不相关说明问题出在生成环节——替换更好的模型或者在 Prompt 里加强约束。如果出现“答案有幻觉”即模型编造了知识库里没有的内容最常见的原因是 Prompt 约束不够。在 Prompt 里加一句强约束“如果资料中没有相关信息请直接回答不知道禁止自行补充。”这句往往立竿见影。其次可以检查是不是召回的片段数量太少导致关键信息没被送进生成环节。另一个常见的坑是“检索结果有大量重复内容”。比如知识库里有多份内容相似但说法不同的文档Top-K 结果里可能一半都是同一内容的不同版本。解决办法是在合并检索结果后增加“内容去重”比如用 SimHash 或 MinHash 计算重复度把重复度高的块去掉保留最有信息量的一条。这个优化能明显提升生成阶段的输入质量而很多教程里根本不会提。最后说一下知识库质量本身。RAG 系统最容易被忽略的事实是检索的上限由知识库的内容质量决定RAG 模型只是尽可能逼近这个上限。如果知识库里本身是一大堆过期、重复、互相矛盾的内容再好的检索和生成也救不回来。所以搭建 RAG 的另一个“隐藏工作”是知识库治理去重、更新、标注有效期、剔除垃圾信息。我自己做知识库项目时内容清洗和治理时间通常占总工时的四成以上这个比例一点都不夸张。7. 从基础 RAG 到可用系统一套投入产出比最高的迭代路径很多人在了解了 RAG 基础之后最关心的问题是接下来到底该往哪个方向投入才能让一个 rag 项目从“能跑”变成“好用”结合我自己做过项目后的体感给出一条性价比最高的迭代路径。第一阶段先把“检索质量”做扎实。这个阶段不要急着上重排、混合检索、多路召回这些高级特性先花时间把数据清理和切块策略调好。具体做法是建一个包含 50-100 个真实问题的基准集每个问题标注期望召回的文档跑一遍 RAG 流程把召回率测出来。然后逐个调整切块大小、重叠大小、嵌入模型直到召回率达到 80% 以上。这一步如果没做到位后面所有花哨的技术都是空中楼阁。第二阶段引入混合检索 重排。召回率卡在某处上不去时大概率是向量检索的单路召回到了天花板。这时候再引入 BM25 关键词检索加上 Reranker 精排。这一步通常是投入产出比最高的一步10%-20% 的效果提升往往就是靠重排拿到的。对应的代码量不大但要求你把检索逻辑重构得清晰一些把“召回”和“重排”两阶段分开。第三阶段再考虑 Agent 化。当单轮检索质量稳定后再加多轮对话改写、Agentic RAG 循环。这一阶段解决的是“用户体验”问题——用户不会总用标准方式提问Agent 需要学会引导、追问、修正查询。这个阶段开发量不小但之前的基础如果不牢Agent 化只会放大前面环节的问题。第四阶段最后再考虑基础设施优化比如流式输出、缓存加速、多路知识库隔离、权限控制、日志监控等。这些属于工程成熟度不直接影响单次回答质量但决定了系统能不能真正对外服务。这条路径的核心逻辑是先证明检索是对的再提升召回的丰富度再做交互智能最后做工程化。如果顺序反了比如一上来先搞 Agent 工作流再回头调检索质量很容易陷入“上面再怎么规划底层检索都是错的”的窘境。我自己第一次搭 RAG 项目时就走过这个弯路花了大量时间在 Agent 规划逻辑上最后发现大部分问题其实都出在检索当时的挫败感很强烈。根据我个人实际操作下来的体会RAG 是一个永远调不完的工程只要你换了一个文档类型、换了一类用户问题、甚至换了文档的写作风格原来的参数就可能失效。不要指望一次搭好从此一劳永逸更值得做的是把“评估集”建好、把“排查路径”走顺让每次调优都有据可依。建议你在项目的根目录下维护一个eval/文件夹里面放上问题集、期望答案和每次跑分的记录。将来每改一处逻辑就重新跑一遍评估看看是变好了还是变差了。这比任何花哨的 RAG 技巧都更能让项目稳步进化。
返回列表