☰
RAG实战指南:从索引管道到Agentic RAG,构建Agent知识获取管道
2026/9/26 17:46:59 网站建设 项目流程

讲真,写这个系列的时候我一直在想一个问题:一个 Agent 如果连喂给它的知识都拿不准,那后面谈什么规划、工具调用、多智能体协作都是空中楼阁。前几篇我们把 Agent 的结构骨架、大模型底座、任务编排梳理清楚了,这篇必须停下来解决一个最基础也最要命的问题——Agent 的知识从哪里来、怎么来、来了之后怎么用。答案就是 RAG(Retrieval-Augmented Generation,检索增强生成)。你可以把 RAG 理解成给大模型装了一个可插拔的“外挂记忆”,它不改变模型本身的参数,而是让模型在回答问题前先去一个外部知识源里查资料,再把查到的资料作为上下文一起交给模型生成答案。这篇文章就从“知识获取管道”这个视角,把 RAG 的基础原理、索引与检索两条管道的核心细节、可落地的代码实现、还有我实际踩过的坑,一次性讲清楚。

这篇内容适合谁看?我个人觉得,凡是准备做 AI Agent 应用开发的、正在搭知识库问答系统的、或者面试前想把 RAG 底层逻辑捋清楚的人,都应该花半小时把它读完。基础部分我会讲得尽量通俗,后面实操部分又会落到代码和参数上,所以新手和老手都能找到自己需要的那个段落。

1. AI Agent 为什么必须有一条知识获取管道

1.1 先对齐一个概念:RAG 到底解决什么问题

在聊 RAG 之前,我建议先把“Agent 和 LLM 有什么区别”这件事再捋一遍。网上有个段子说:LLM 是一个满肚子学问但是闭关锁国的学者,你问什么它答什么,但它无法接触外界的任何新信息;Agent 则是这个学者加上秘书、助理、外勤团队,能查资料、能打电话、能翻文件,最后交出一份像样的报告。这里面“查资料”的动作,落到技术上就是 RAG。

很多刚入门的朋友会把 RAG 理解成一个“知识库问答工具”,这个理解没错,但太窄了。RAG 在 Agent 体系里的角色,不是做一个问答机器人,而是为 Agent 提供一条稳定的知识获取管道。Agent 需要执行一个任务时,先判断“我缺哪些知识”,然后通过这条管道把相关知识捞进来,作为后续推理和决策的依据。所以第四篇讲 RAG,本质上讲的是 Agent 的知识基础设施。

这里还要纠正一个常见误区:有人觉得有了 Long Context(长上下文)大模型,就可以把整个知识库都塞进提示词里,RAG 没必要了。表面上看起来好像是这样——现在的模型上下文窗口动辄 128K、256K,几千页文档理论上都装得下。但实际工程里你马上会撞上三个问题:

  • 成本:上下文越长,每轮请求的 token 费用越高,喂几十万字进提示词,单次推理成本直接起飞。
  • 噪声:信息越多的同时无关信息也越多,模型在浩瀚的无关内容里寻找关键答案,准确率反而下降,这就是所谓的“lost in the middle”。
  • 延迟:token 增多意味着生成耗时显著拉长,用户体验会变得不可接受。

所以 RAG 的核心价值可以概括成一句话:让模型在需要的时候,以可控的成本,拿到最相关的那一小部分知识,而不是把整个资料库都背在身上。

1.2 RAG 和微调到底怎么选

我把这个问题放进基础篇,是因为几乎每个做 Agent 的人都会遇到。造一个行业问答系统,有人第一反应是“我得训练一个我们行业的模型”,你确定?微调的本质是改变模型内部的权重,它的作用是改变模型的行为风格、输出格式、掌握某种特定的推理模式,而不是让它记住一个个具体的知识条目。

举个例子:你想让模型学会输出特定格式的报告,学会某种行业的专业术语表达方式,用微调是合适的。但如果你想让模型知道“你们公司 2024 年第一季度的退货率是多少”,这属于典型的事实性知识,靠微调去记这种不断更新的数据,纯属灾难。因为你每次数据更新都要重新微调一次,成本极高,而且模型还会出现“知识混淆”——它可能把上一季度的数据和新季度的数据混在一起输出。

RAG 恰好是弥补这个短板的最佳方案。知识存在外部库里,随时增删改查,模型只是临时读取。这样一来,数据更新只需要更新数据库,模型本身完全不需要动。在实际项目中,RAG 和微调经常是配合使用的:用微调解决模型的表达风格和服务姿态问题,用 RAG 解决具体业务知识的获取问题。顺序一般先后微调再接 RAG,因为微调之后的模型对术语的理解更准,RAG 检索出来的内容它消化得也更好。

1.3 Agent 里的 RAG 和普通问答里的 RAG 有什么不一样

传统 RAG(比如 ChatPDF、智能客服)的用户交互模式是一问一答:用户提出一个问题,系统检索知识,生成回答,结束。但 Agent 场景下的 RAG 有两个明显的不同:

第一,RAG 的触发是动态的。Agent 在执行复杂任务时,可能一开始不需要知识,执行到中途才发现需要查一个具体数字,于是触发检索;也可能一次任务中触发多次检索,每次查不同类型的内容。这就是业内常说的 Agentic RAG。简单说,传统 RAG 里检索是固定流程,Agentic RAG 里检索变成 Agent 可以自主调用的“工具”,Agent 自己决定什么时候查、查什么、查几次。

第二,对结果质量的要求更高。普通问答时,检索结果稍微偏一点,生成的回答顶多不够准确;但 Agent 要用检索结果去推理、去规划、去调用其他工具,如果这一步拿到的是错误信息,后面全链条都会出错,而且错误会被放大。所以 Agent 场景下的 RAG 对召回精度、重排质量、上下文组织都提出了更高要求。

这就把我们引向了本文的核心:一条管道,两个阶段——索引管道负责“把知识存进来”,检索管道负责“把知识取出去”。接下来我按这两条管道拆开讲。

2. 索引管道:先把知识变成 Agent 能查的东西

2.1 从原始文档到切块:这一步决定了下限

RAG 的索引管道,业内一般分三个环节:解析、清洗、切块。解析就是把 PDF、Word、Markdown、网页这些原始格式变成纯文本;清洗是处理掉无关内容,比如页眉页脚、导航栏、广告、特殊符号;切块则是把长文本分割成合理大小的文本块。我见过太多团队花大把时间调检索和模型,最后发现问题出在切块参数上。切块这件事,直接决定了检索结果的下限。

为什么必须切块?答案藏在两个技术背景里:

  • 嵌入模型有最大输入长度限制。常见的嵌入模型(比如 OpenAI 的 text-embedding-3-small:8191 tokens,BGE 系列:512 或 1024 tokens)只能处理有限长度的文本。超过长度,要么截断,要么报错。
  • 向量检索是“以块搜块”。你最终做相似度比对的是文本块的向量,不是整篇文档的向量。如果一块太大,里面包含多个主题的混合内容,生成的向量就是一个“大杂烩”,跟任何单个查询的相似度都不高;如果一块太小,语义信息不够完整,匹配时又会错过许多关键联系。

我实际用下来的切块经验是:常见策略包含固定长度切块(比如每 500 个字切一块)和递归切块(先按段落、再按句子、最后按字符逐级切)。固定长度简单但容易切断语义,递归切块效果更好,但要注意参数的配合。如果你用的是 LangChain 的 RecursiveCharacterTextSplitter,这类工具通常需要设置两个关键参数:chunk_size(块大小)和chunk_overlap(块重叠)。重叠的意义在于避免一句话被拦腰切断——前一块末尾和后一块开头共享一部分内容,就算边界正好落在一句话中间,这句话的完整语义也至少被某一个块完整包含。

关于块大小,我的建议是:不能一刀切。按文档类型来区分。FAQ 类的短文档,块可以小一点(300-500 字),因为每个问答本身是独立的语义单元,块大了反而引入无关信息;长文报告和技术手册,块适合大一些(800-1500 字),因为这类文档的“可检索单元”通常是一整段论述,切小了检索出来只是断章取义。另外还有一个小技巧,Markdown 格式的文档可以按标题层级切块,让每个章节成为一个独立的语义单元,这种“结构感知切块”的效果往往比纯靠字符数硬切好很多。

关于切块,给你几条我踩过坑之后总结的实操经验:

切块策略适用场景注意点
固定长度切块快速原型、格式统一的数据容易切断语义,务必加 overlap
递归切块大部分通用场景按段落-句子-字符递减切分,效果好
结构感知切块有明确标题结构的文档需要保留文档解析的标题层级信息
语义切块内容主题多样、风格多变的文档计算成本高,小型项目慎用

2.2 嵌入:让文字变成可计算的向量

切块之后做的事情,是把每个文本块送去嵌入模型,得到一组浮点数向量。这个过程在所有 RAG 教程里都被一笔带过,但恰恰是这里最容易出问题。嵌入模型的选择,直接决定了相似度检索的“语义理解”上限。

嵌入模型的核心逻辑:把文本映射到高维向量空间,语义相似的文本在向量空间中距离近,语义不相关的内容距离远。这个“语义空间”的能力来自模型的预训练语料和对齐策略。所以选嵌入模型时最重要的一件事是:它对你的领域语言是否友好。做中文项目就要用中文语料优化过的模型;做代码相关项目就找专门针对代码训练的嵌入模型;做英文项目选英文模型。这个原则是硬性的。

你大概会问:选通用模型行不行?行,但如果你的知识库里有大量行业术语、专业缩写、特殊格式,通用模型很可能把它们映射到了奇怪的位置,到时候检索出来的结果会让你怀疑人生。我自己的经验是:先拿一小批有代表性的文档和一个标准问答集做离线评测,不同嵌入模型跑一遍,看谁召回出来的内容最能让大模型生成正确答案,而不是单纯看网上的榜单分数。

做嵌入向量化,我通常加一层“元数据丰富”的操作。也就是说在索引时,除了把文本块的内容做向量化,还把来源文档、章节标题、页码、创建时间等信息一并存下来。这样后续检索的时候,返回的不只是一段文本,而是一份带完整上下文的“证据片段”。这个操作对 Agent 场景意义尤其大,因为 Agent 拿到知识之后往往还要溯源、做判断,元数据就是它的溯源证据。

2.3 向量数据库选型:别一上来就上分布式

既然向量化之后要“存起来”,那就绕不开向量数据库。这几年向量数据库的赛道已经卷成一锅粥了:有专门的向量数据库,比如 Chroma、Qdrant、Milvus、Weaviate、Pinecone;也有在传统数据库上扩展向量能力的,比如 PostgreSQL 加 pgvector、Elasticsearch 加向量字段。选型的时候最忌讳一上来就选最热门的分布式方案,因为大部分个人项目和中小型团队,根本用不到那个规模级别。

我的建议很明确:项目小于 10 万条向量,直接用 Chroma 或者 pgvector,跑本地开发毫无压力;10 万到百万级,可以考虑 Qdrant 或者 Elasticsearch;真正到了千万级以上、需要分布式横向扩展、需要复杂过滤和混合检索的时候,再上 Milvus 也不迟。理由很简单:复杂度即成本,早期把精力浪费在运维分布式数据库上,是最亏的事。

说句题外话,很多 RAG 框架也内置了内存向量存储,比如 LangChain 的 FAISS 集成、LlamaIndex 的基础向量索引,用来做概念验证和 Demo 完全够用。我自己搭原型的时候,都是先用内存向量库把流程跑通,确认效果之后再去迁移正式存储。这比一开始就架集群要快得多,也聪明得多。

3. 检索管道:把最相关的内容捞回来

3.1 向量检索与关键词检索:谁更靠谱,为什么两者都要

索引管道建好了,轮到了真正上战场的一环:检索。检索的目的只有一个——面对用户的查询,从知识库里把最相关的文本块召回回来,准备交给模型。

大多数教程会教你:把用户问题也做嵌入,然后在向量库里做近似最近邻搜索(ANN),取 Top-K 个相似结果。这个方法叫纯向量检索,是现在 RAG 的主流做法。但工程里如果你只依赖这一招,很快就会遇到两个尴尬场景:

  • 精确匹配场景:用户查询里包含一个产品编号“SP-2024-0817”,向量检索很可能找不到,因为这个编号在语义空间里并没有特别明确的“邻居”。但关键词检索一条 SQL 就搞定了。
  • 专业术语场景:用户说的是“溃疡性结肠炎”,文档里写的是英文缩写“UC”,两边的嵌入向量可能并不像你想象的那么接近,结果什么都没召回。

这就是混合检索(Hybrid Search)存在的必要性:向量检索负责理解语义,关键词检索(BM25)负责精确匹配,两者并行跑,再把结果合并起来。实际的收益我自己试下来是非常明显的:单独向量检索命中率大概 70% 左右,加上 BM25 做混合检索之后,命中率能到 90% 以上。尤其在处理包含代码、型号、编号这些精确信息的查询时,这个提升几乎是质的飞跃。

那“向量 + 关键词”的合并结果是直接丢给模型吗?不是。有一个非常突出的问题:两个通道返回的结果高度重叠。向量返回了 5 条,关键词返回了 5 条,其中 3 条一模一样。如果直接合并,等于只带回了 7 条上下文,其中还有重复内容,白白浪费上下文窗口。解决办法是引入重排阶段。

3.2 重排:检索的上限是召回,重排的收益是精度

重排(Rerank)是我在所有 RAG 实战里最推荐大家加上的一个环节,也是进阶和基础的分水岭。一句话概括它的作用:向量检索和关键词检索负责把候选集捞得足够宽(召回优先),重排模型负责把候选集排得足够准(精度优先)。

为什么要重排,而不是直接用向量相似度排序?因为向量相似度本质上是一个粗糙的“语义距离”度量,它适合做初筛,但不适合做精排。真实项目里面有大量这样的现象:某一文本块和用户问题的向量距离最近,但内容实际上只是在同一个话题边缘打转,并没有真正命中问题核心。重排模型的原理是:把查询和候选文档拼接起来,输入一个专门的交叉编码器,让模型对查询-文档相关性做精细打分,这比双塔结构的向量相似度要精细得多。

实话说,不要只计算向量相似度。原因有两个:

  • 双塔结构(查询和文档分别编码产生的向量之间做余弦距离)天然有信息损失,因为查询和文档在编码阶段没有交叉交互。
  • 交叉编码器(Cross-Encoder)可以把查询和文档放到同一个模型里做深度融合,精度显著更高,工业界主流做法是:先靠向量检索过滤器粗筛出 50-100 个候选,再用重排模型挑出 top 5-10 个交给大模型。

用一句话记住这个设计逻辑:“宽进严出”。召回阶段宁多勿漏,重排阶段宁缺毋滥。

3.3 检索结果怎么喂给模型:上下文组装技巧

这一步是容易被忽略、但对回答质量影响极大的“最后一公里”。检索得到的结果往往是一堆文本块(chunk),长度可能是几百到上千字,包含 5~10 个这样的块,拼在一起就超过了 5000 字。放在提示词的什么位置?是全部塞进去吗?顺序怎么排?这些都有讲究。

我个人的标准做法,是做一个“上下文压缩”之后再送进提示词:

  • 把检索结果按照重排分数从高到低排序。
  • 转成统一的文本块格式,每块都附上来源标识,比如“[1] 来源:xxx文档.pdf 第三章”。
  • 限制输入的最大 token:比如最多送 3000 字或 4000 字,超出部分截断,优先保留排在前面的内容。
  • 在提示词里明确告诉模型:“请优先参考引用片段中的信息回答,如果引用片段中没有相关内容,请明确说明不知道,不要编造。”

这里有必要提醒一个使用技巧:上下文顺序确实会影响模型对信息的关注度。大模型在长上下文处理中,通常对开头和结尾的内容更敏感,中间的部分容易“漏看”。所以如果你有一个最关键的答案片段,尽量把它排在上下文靠前的位置,而不是把它混在中间段。这一点没有写在任何模型的论文里,是我在多个项目里实际对比测试的结论。

所以上下文组装的元规则是:让模型能轻易找到它需要的内容,而不是让它自己在五千米的长文本里考古。

4. 用代码搭一条最小的可运行 RAG 管道

4.1 技术选型:Python 生态和 Java 生态怎么选

写代码之前先把选型聊清楚。国内 RAG 项目主要分两个技术栈阵营:Python 阵营基本集中在 LangChain、LlamaIndex 和 LlamaCloud;Java 阵营则有 Spring AI + Spring Cloud 的整套体系。你自己公司如果是 Java 背景、技术栈已经重度绑定 Spring Boot,那就别硬切 Python,Spring AI 的 RAG 支持也已经相当成熟了。如果是个人项目、或者想快速验证想法,Python 生态无疑更省事,资料多、坑少。

这一节我给出一个最小化的端到端示例:从本地加载文档、切块、向量化、存入向量库,到查询、检索、生成回答。这个流程是 RAG 最经典的基线,任何复杂架构都是从这里演进来的。

4.2 索引侧:文档加载与切块实现

我用 Python 为例,框架用 LangChain 的轻量接口,底层向量库先用 Chroma(本地免部署)。展示第一个核心阶段的代码。

from langchain_community.document_loaders import TextLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_openai import OpenAIEmbeddings from langchain_chroma import Chroma # 1. 加载文档 loader = TextLoader("knowledge_base/agent_guide.md", encoding="utf-8") documents = loader.load() # 2. 切块:递归切分,块大小 800,重叠 150 text_splitter = RecursiveCharacterTextSplitter( chunk_size=800, chunk_overlap=150, separators=["\n\n", "\n", "。", "!", "?", " ", ""], length_function=len, ) chunks = text_splitter.split_documents(documents) print(f"切块数量: {len(chunks)}") # 3. 嵌入并入库 embeddings = OpenAIEmbeddings(model="text-embedding-3-small") vectorstore = Chroma.from_documents( documents=chunks, embedding=embeddings, persist_directory="./chroma_db", ) print("索引完成,向量库已持久化到 ./chroma_db")

这段代码里值得留意的两个细节。第一,separators参数里我把中文标点也加进去了,这个对中文文档非常关键。默认的切分分隔符是为英文设计的,对中文文档直接跑,你会看到无数个句子被从中间断开。第二,chunk_overlap我设成 150,差不多等于 2-3 个句子的长度,能保证跨块语义不断裂。

如果你用的是 OpenAIEmbeddings,注意服务要能正常访问,还需要配置 API Key;如果是在国内环境,可以换阿里的通义千问嵌入、智谱的 embedding 接口,或者开源的 BGE 系列模型,接口格式类似,替换起来不麻烦。

4.3 检索侧:混合检索加重排的完整链路

接下来是关键部分。前面讲了混合检索和重排,这里给出一个偏向生产可用的检索链路:BM25 关键词检索 + 向量检索并行召回复合,再用重排模型精排。

from rank_bm25 import BM25Okapi import jieba import numpy as np # ---------- 关键词通道:BM25 ---------- # 假设 query 是用户输入,candidate_chunks 是候选文本块 def bm25_search(query, candidate_chunks, top_k=10): # 用 jieba 做中文分词 tokenized_corpus = [list(jieba.cut(c)) for c in candidate_chunks] bm25 = BM25Okapi(tokenized_corpus) tokenized_query = list(jieba.cut(query)) scores = bm25.get_scores(tokenized_query) top_indices = np.argsort(scores)[::-1][:top_k] return [(candidate_chunks[i], scores[i]) for i in top_indices] # ---------- 向量通道:语义检索 ---------- def vector_search(query, vectorstore, top_k=10): results = vectorstore.similarity_search_with_score(query, k=top_k) return [(doc.page_content, score) for doc, score in results] # ---------- 合并与重排 ---------- from sentence_transformers import CrossEncoder reranker = CrossEncoder("BAAI/bge-reranker-v2-m3") def hybrid_rag_retrieve(query, vectorstore, candidate_chunks, top_k=5): # 召回:两个通道并集 bm25_results = bm25_search(query, candidate_chunks, top_k=10) vector_results = vector_search(query, vectorstore, top_k=10) # 合并去重,按分数简单融合(这里用各通道最大分进行归一化加权) fusion_score = {} for text, score in bm25_results: fusion_score[text] = fusion_score.get(text, 0) + 0.4 * (score / max(s for _, s in bm25_results)) for text, score in vector_results: fusion_score[text] = fusion_score.get(text, 0) + 0.6 * (score / max(s for _, s in vector_results)) unique_texts = list(fusion_score.keys()) # 交叉编码器重排 pairs = [[query, text] for text in unique_texts] rerank_scores = reranker.predict(pairs) ranked_idx = np.argsort(rerank_scores)[::-1][:top_k] return [(unique_texts[i], rerank_scores[i]) for i in ranked_idx]

上面这段代码是我常用的套路模板,几个要点说明一下:

  • 召回阶段,我把 BM25 的权重设成 0.4,向量权重设成 0.6,这是经验值。如果你们的业务里精确匹配场景多(型号、编号、名字),把 BM25 权重调高到 0.6;如果语义查询偏多,向量权重可以调到 0.8。这个超参数值得你在自己的数据集上做个简单网格搜索。
  • 重排模型我选择了bge-reranker-v2-m3,它支持中文和英文混合,也是目前国产开源模型里性价比很高的一个选择。如果你的部署环境不方便下载模型,可以换成调用 API 的重排服务,效果大同小异。
  • 重排之后直接交付给模型,上下文压缩这一步可以封装成一个函数,然后拼入提示词。

4.4 多轮对话下的 RAG 设计

第四篇之后肯定要写多轮交互,但这里先把最常见的问题摆出来:多轮对话里做 RAG,到底应该拿哪句话去检索。

多轮对话的场景很典型:用户先说“帮我看看上面那份合同”,Agent 回答完,用户接着说“那如果改成三年期呢?”——如果此时只用“那如果改成三年期呢”去检索知识库,那基本什么都召不回,因为它没有上下文。业界有三种处理策略:

  • 简单拼接:把最近几轮的用户消息和助手消息拼接在一起去检索。实现简单,但随着轮数变多,查询越来越长,噪声越来越大。
  • 重写/改写:用一个轻量模型把多轮对话改写成一条独立的、没有歧义的查询,比如把“那如果改成三年期呢?”改写成“如果合同租赁期限由两年改成三年,需要修改哪些条款?”这种重写质量可以从根本上改善检索效果,是推荐的做法。
  • 混合策略:第一轮用原句检索,后续轮提前面几轮的问答摘要做条件查询,兼顾时效和准确。

我建议从改写方案入手。可以用一个大模型,提示词写清楚“你是一个信息提取助手,请结合对话历史,把用户最新一条消息改写成一个独立的、完整的信息检索查询,只输出改写结果,不做额外解释”,实现成本不高,收益立竿见影。RAG 的所有失败案例里,相当高的比例是“检索问题提问姿势不对”,先把问题改明白,召回效果立刻上一个台阶。

5. 实战中踩过的坑和排查思路

5.1 常见问题速查表

这部分是我最想写的内容。网上 RAG 教程千篇一律地讲概念、讲流程、给代码,但实际部署的时候,绝大部分时间都花在调参和排错上。我整理了这些年在 RAG 项目里高频踩到的坑,做出一个排查速查表:

症状可能原因排查方法解决方案
回答内容完全和知识库无关检索环节失败(没召回任何内容)打印检索中间结果,检查召回列表是否为空检查向量库是否为空、查询嵌入模型是否一致
回答内容相关但细节错误召回结果不对(召回内容不相关)人工检查 top-5 召回内容的相关性换嵌入模型、增加重排环节、调整切块
回答编造了知识库没有的东西上下文缺失关键信息 + 模型幻觉检查召回内容是否包含问题答案增大 top_k、调整检索阈值、修改提示词加强约束
回答时好时坏切块大小和文档结构不匹配对典型文档做切块检查按文档结构调整切块参数或改用结构感知切块
查询含编号/型号时检索不到纯向量检索的精确匹配能力差用关键词搜索同一查询做对照加 BM25 混合检索
检索结果多但回答仍然跑偏上下文过长导致关键信息被淹没观察上下文 token 数、记录关键问题位置压缩上下文、限制输入长度、将关键片段前置

5.2 查准率低时的三步排查法

如果你的 RAG 系统回答质量上不去,先别急着换模型。我每次做劣化分析时都按“数据、检索、生成”三个环节逐段排查:

第一步,先查数据质量。把文档重新读一遍,看是不是原始文档本身有很多无效内容、表格被解析得支离破碎、图片上的文字完全没有被提取。数据源头是污水,下游整个管道干净不了。

第二步,再看检索命中。把用户查询拿去检索,人工看召回的前 5 条到底是不是真正能回答这个问题的内容。如果是能回答的内容但模型没答对,那就是生成环节的问题;如果召回的内容本身就答不了,那就是检索环节有问题,从嵌入模型、切块、混合检索、重排一层层往下查。

第三步,才是生成提示词。提示词的影响往往没你想的那么大——它只能改良,不能救场。数据对、检索准,提示词稍微糙一点也能用;数据错、检索偏,提示词写得再花哨也白搭。所以排查顺序必须是数据 → 检索 → 生成,千万别反过来一上来就折腾提示词。

5.3 顺带说一句 RAG 和 MCP 的关系

最近总有人问 RAG 和 MCP 到底什么区别,这个系列看到这里应该能理清了。RAG 是知识获取管道,解决的是“怎么把静态知识送进模型上下文”;MCP(Model Context Protocol)是工具调用的开放协议,解决的是“模型怎么以标准方式调用外部工具和数据源”。两者层次不同,但在 Agent 架构里经常配合使用:MCP 提供标准化的工具接口,RAG 作为其中一个“知识检索工具”被 Agent 通过 MCP 协议调用。

在实际工程里,你可以把 RAG 封装成一个 MCP 工具,让 Agent 在需要外部知识时主动触发它。这个组合正是 Agentic RAG 的一种落地形态。

6. 从基础 RAG 走向 Agentic RAG

6.1 传统的“查一次答一次”和 Agentic RAG 的差别

写到这里已经把 RAG 基础讲全了,但作为这个系列的第四篇,我觉得有必要把视野再拉高一截,聊一下 RAG 在 Agent 时代的新形态。

传统 RAG 是线性管道:用户问 → 检索 → 生成 → 回答。Agentic RAG 则是把检索变成 Agent 的一项自主能力:Agent 先理解任务,发现自己缺什么知识,然后调用检索工具,拿到结果后可能还要再检索一次,多次检索之后把信息汇总,再去决策。举个例子:一个 Agent 接到任务“对比 A 产品和 B 产品的售后条款”,它可能先检索 A 的条款,再检索 B 的条款,再检索两者的附加协议,最后汇总出对比结论。这个多次检索、动态决策的过程就是 Agentic RAG。

要做好 Agentic RAG,检索就不能再是一个黑盒函数,而是要变成一个有明确输入输出定义的“技能”(Skill)。这也是 Agents 领域经常讨论的 skill 概念。在我的实践中,把 RAG 作为技能组织时的核心原则是:一个技能只解决一类检索需求。不要做一个万能检索技能,而要把“合同条款检索”“产品参数检索”“项目历史决策检索”分别打成独立的技能,每个技能定义好触发条件、输入参数、输出格式、后置处理逻辑,Agent 才能精准调用。

6.2 检索策略的进阶方向:历史用例检索与适配

最后说一个进阶方向,也是我在 AI Agent 书籍里看到后觉得最有用的一种 RAG 变体:历史用例检索与实例化适配。这种方法在“决策类 Agent”里特别有用——它不只是检索静态文档,而是检索类似的历史执行案例,然后适配到当前场景。

它的思想非常朴素:人类解决问题时,第一反应往往是回忆“以前有没有处理过类似问题”,有的话套用当时的解决方案做局部修改。Agent 也可以这么做:把过去每次成功解决任务的案例(包括问题描述、环境条件、采用的方案、执行结果、经验教训)沉淀成案例库,新任务来了,先检索最相似的案例,再在上面做适配调整。

这种“经验型 RAG”比单纯的文档问答型 RAG 高一个层次,因为它检索的不只是知识,而是“过去行动的路径”。如果配合结构化的事件表示,检索到案例后还能直接提取其中的参数和步骤,大大加速 Agent 的决策过程。对我来说,这会是后续几篇更进阶的内容——但从基础 RAG 到案例检索型 RAG,中间的知识获取管道逻辑是一脉相承的。先把管道的基础打好,后面的扩展就是水到渠成的事。

最后说一个我这几年做 RAG 项目最深的体会:RAG 系统的复杂度,通常不是来自某一个环节,而是来自“每个环节都差一点点”。切块差一点、嵌入匹配差一点、混合检索权重差一点、重排没有加、上下文组装没压缩——每个环节只损失 5% 的精度,叠加起来就是灾难性的 40% 损失。这也是为什么我一直强调,不要迷信某个单独的技巧能救全场,而是把每条管道、每个参数都抠到及格线以上,整个系统才能稳定地输出高质量结果。如果你今天只能带走一个信息,我希望你记住:RAG 不是“文档扔进去、问题捞出来”那么简单,它是一条需要精细化运营的知识管道,而管道的质量,决定了 Agent 的上限。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询