news 2026/9/28 15:43:28

从0到1搭建RAG管道:AI Agent知识获取实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从0到1搭建RAG管道:AI Agent知识获取实战指南

现在很多朋友聊 AI Agent,开口就是规划、工具调用、多智能体协作,但真到自己动手从 0 到 1 搭建一个能“干活”的 Agent 时,最先卡住的往往是那个最不性感、却最致命的环节——知识从哪来。模型参数里那点知识是死的,企业内部文档、产品手册、最新工单、私有代码库,这些才是活的。把活的业务知识喂给 Agent,并且让它按需取用,靠的就是 RAG(Retrieval-Augmented Generation,检索增强生成)这条知识获取管道。

这篇是“走进 AI Agent”系列的第四篇,我把话题聚焦在 RAG 最基础的部分:它解决什么问题,管道里每一截是干什么的,以及我实际搭建一个最小可用 RAG 管道的完整过程和踩坑记录。不管你是刚开始接触 RAG 概念的新手,还是已经在写 Agent 但发现知识召回总是不准的开发者,这篇都能给你一份可以直接照着做的参照系。

先说一个我的观点:很多人把 RAG 当成“给大模型外挂一个知识库”,这个说法没错,但容易误导。RAG 的本质不是挂载,而是构建一条管道——文档从进来到出去,要经过切分、向量化、索引、检索、重排、注入提示词等多个环节,任何一环糙一点,最后答出来的东西就跑偏。下面我把这条管道从头拆到尾。

1. 为什么 Agent 需要知识管道:先讲清楚“知识割裂”问题

1.1 从“对话玩具”到“干活”:Agent 的短板就是知识

我在前面几篇里反复强调一个判断:纯靠大模型自身参数里的知识,做问答、写文案没问题,但做 Agent 一定不够。原因很朴素——预训练数据是有截止日期的,而且模型不会知道你公司内部那套命名规范、你项目里那个历史包袱、你客户昨天提的那个具体需求。

拿我最近帮朋友搞的一个售后客服 Agent 举例。模型本身很聪明,能理解用户情绪,能组织语言,但它不知道这家公司产品最新的固件版本号,不知道已知问题清单里哪条对应“开机黑屏”,更不知道工单系统里那个“DTS20240815”是什么意思。如果没有外部知识接入,Agent 就只能一本正经地胡说八道——术语叫“幻觉”,说白了就是知识割裂:模型有语言能力,但没有业务事实。

Agent 解决这个问题的标准姿势,就是 RAG。它把知识“外置”到模型之外:先根据用户问题去知识库里检索相关内容,再把检索到的内容连同问题一起交给大模型,让模型基于这些材料组织回答。这样模型不必“记住”所有知识,只要会“查”和“读”就行。这就是为什么我说它是一条管道——用户问题进来,经过检索管道,带着证据出去生成回答。

1.2 RAG 在 Agent 里的位置:不是插件,是管道

聊 Agent 架构的时候,经常有人把 RAG 归类到“工具调用”里,跟搜索网页、调用 API 并列。我的理解不太一样:工具调用是 Agent 的“手”,RAG 是 Agent 的“记忆检索系统”——它负责把长时记忆(文档、知识库)转成工作记忆(当前上下文相关片段)。

这个定位很关键,直接决定了你怎么设计。Agent 通常分几个核心模块:规划(Planning)、记忆(Memory)、工具(Tools)、执行(Action)。知识获取管道横跨记忆和工具两块:从机制看,它像工具,可被调用;从功能看,它提供的是记忆内容,不是执行动作。所以在我的项目里,RAG 管道从来不是独立服务随便挂一下,而是和 Agent 的记忆模块、提示词模板结合在一起的。

另外要补充一点,RAG 不是单次检索就完了。你可能会搜到多个候选片段,Agent 需要判断哪个有用、哪个没用,甚至要不只检索一轮,根据初步回答再补检索一次。这种把检索过程和 Agent 推理循环结合起来的做法,就是现在热词里那个 “Agentic RAG” 的方向。不过这篇文章先打底子,把基础管道讲透,Agentic RAG 后续单独写。

2. RAG 基础链路拆解:从切分、向量化到检索生成的完整流程

2.1 第一关:文档加载与切分,切不好后面全白搭

RAG 管道的输入是文档,但大模型不能直接“读”整个文档库,所以第一步要把文档拆成合适的块(chunk)。这个环节看着不起眼,却决定了后面检索质量的天花板。我见过太多人兴致勃勃做 RAG,结果效果一塌糊涂,查来查去发现根因就是切分太粗暴。

切分的核心矛盾是:块太大,向量化后语义太杂,检索出来的内容不够精准,还可能撑爆上下文窗口;块太小,语义不完整,单个块信息量不足,模型看了也拼不出完整结论。我常用的经验标准是:通用文本按 300-500 字左右切,带标题结构的文档优先按语义段落切,代码或表格按逻辑块切。

实操中,我建议先用结构感知切分,而不是纯按字数硬切。比如 Markdown 文档,先按标题层级把文档拆成章节,再对过长的章节做二次切分;PDF 先抽取标题、段落、表格结构,尽量保持段落完整。现在主流框架里的 recursive character text splitter 就是先按段落切,再按句子切,最后按字符切,这种递归策略能最大限度保留语义边界。

还有个容易被忽略的点:切分时的“重叠”(overlap)。我在相邻块之间保留 50-100 字的重叠区,这样即使关键信息正好落在边界上,也至少能在两个块里完整出现,避免检索时漏掉。这个技巧用好了,召回率能有肉眼可见的提升。

2.2 第二关:Embedding 与向量库选型

文档切好之后,下一步是把每个块转成向量——一串能够表征语义的浮点数。这一步靠的是 Embedding 模型。选 Embedding 模型,我主要看三个指标:语义相似度质量、支持的语言、向量维度。

语义质量怎么判断?不要只看榜单分数,拿你自己领域的语料去测。比如我做企业文档问答,会准备二三十个“问题-标准答案片段”对,跑一遍召回,看 Top-5 里能不能命中正确片段。这比任何公开 benchmark 都实在。语言方面,如果知识库是中英混合,建议选多语言模型,比如 BGE 系列、text-embedding-3 之类,别选纯英文优化的模型,否则中文文档语义全扭曲了。向量维度影响存储和检索速度,一般 768 或 1024 维足够,维度越高越精细但成本也越高,平衡着来。

向量库的选型,我按项目规模给三条建议。个人练手或小团队原型,直接用 Chroma 或 FAISS,一个本地文件就能跑起来,零运维负担。中等规模、需要并发查询和过滤,用 Qdrant 或 Milvus,支持元数据过滤,这在实际业务里非常重要——比如限定只看某类文档时,提前用元数据缩小范围能省大量计算。再大规模、高并发生产系统,上 Elasticsearch 或 OpenSearch,它本身支持向量检索加全文检索的混合模式,后面我会详细说为什么混合检索重要。

注意:向量检索不是万能的。它擅长语义相近的召回,但精确匹配——比如产品型号“DTS-2024”、订单号“SO123456”——往往表现不佳。所以正规 RAG 管道必须做混合检索,把关键词匹配和向量召回结合起来,我后面实操部分会示范。

2.3 第三关:检索、重排与生成融合

管道后半段就是检索和生成了。检索不只是“找相似度最高的几个块”,理想做法是三步走:召回(Recall)、重排(Rerank)、生成(Generate)。

召回阶段,用向量相似度(比如余弦相似度)从库里捞出候选 Top 20-50 个块。这个阶段标准是“宁滥勿缺”,因为后面还有重排兜底,召回太严反而容易漏。实战里我最常用的是混合召回:向量检索拿语义相似的,BM25 拿关键词精确匹配的,两边结果合并去重。这样既能抓住“意思相近但用词不同”的文档,也能精确命中“型号完全一致”的硬指标。

重排阶段,用一个专门的 Rerank 模型(比如 BGE-Reranker、Cohere Rerank),对候选块重新打分排序。为什么要重排?因为 Embedding 模型算的是“大概语义接近”,而 Rerank 模型是拿用户问题和每个候选块做深度交叉编码,精细判断相关性。这一步能把 Top 20 里最相关的 3-5 个块提到最前面,从而显著提升回答质量。我的经验值是,加了重排后,答案准确率通常能提高 10-20 个百分点。

生成阶段,把最终筛出的块拼进提示词。这里有个关键设计:提示词里不仅要放检索到的内容,还要明确告诉模型哪些该信、哪些不该信。我会在提示里写“请基于以下资料回答问题,如果资料中没有相关信息,请直接说不知道,不要编造。”这一句话能显著降低幻觉。同时,让模型在回答末尾标出引用了哪几个文档片段,方便你排查回答依据。

3. 实操过程:我搭一个最小可用的 RAG 管道

3.1 工具选型和环境准备

理论说了一堆,上点硬的。我这里搭一个最小可用的 RAG 管道,目标场景是:给一个内部技术手册做问答,Agent 能根据手册内容回答“如何重启网关设备”这类问题,而且能给出依据。

我的选型如下,都是经过多次对比后留存的一套组合:

  • 框架:LlamaIndex(也可以用 LangChain,但 LlamaIndex 对检索链路封装更直接)
  • Embedding:BAAI/bge-small-zh-v1.5,中文效果好,维度 512,资源占用小
  • Rerank:BAAI/bge-reranker-base,加在管道里做精排
  • 向量库:Chroma,本地文件持久化,原型阶段足够了
  • 大模型:DeepSeek 或 Qwen,主要看你有哪个 API,这一步其实不影响语法

安装依赖的时候,我建议用一个干净的虚拟环境,避免依赖冲突。这步不复杂,但很值得做:

python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install llama-index chromadb sentence-transformers

如果你用的是 OpenAI 兼容 API(比如 Qwen、DeepSeek 都有兼容模式),建议直接配环境变量,方便切换:

export OPENAI_API_KEY="你的key" export OPENAI_BASE_URL="你的API地址"

3.2 核心实现:加载、切割、入库

下面这段代码演示文档加载和切分入库。假设我有一份 Markdown 格式的技术手册manual.md,同时配套一份产品说明书 PDFdatasheet.pdf。

from llama_index.core import SimpleDirectoryReader, Settings from llama_index.core.node_parser import MarkdownNodeParser from llama_index.core.ingestion import IngestionPipeline from llama_index.vector_stores.chroma import ChromaVectorStore from llama_index.embeddings.huggingface import HuggingFaceEmbedding import chromadb # 1. 设置 Embedding 模型 Settings.embed_model = HuggingFaceEmbedding( model_name="BAAI/bge-small-zh-v1.5" ) # 2. 加载文档 reader = SimpleDirectoryReader( input_files=["./data/manual.md"] ) docs = reader.load_data() # 3. 切分:Markdown 结构感知切分 parser = MarkdownNodeParser.from_defaults( chunk_size=512, chunk_overlap=80 ) nodes = parser.get_nodes_from_documents(docs) # 4. 初始化向量库 chroma_client = chromadb.PersistentClient(path="./chroma_db") collection = chroma_client.get_or_create_collection("tech_manual") vector_store = ChromaVectorStore(chroma_collection=collection) # 5. 入库(自动完成 chunk 向量化) pipeline = IngestionPipeline( transformations=[Settings.embed_model], vector_store=vector_store ) pipeline.run(nodes=nodes)

这里几个参数说明一下。chunk_size=512对中文技术文档是一个比较稳的起点,按字符数算,512 个中文字大概能承载 2-3 个要点,不会太碎也不会太宽。chunk_overlap=80保留块间重叠,防止关键句在边界被切断。如果你的文档段落本身很长,可以调小 chunk_size,让切分更细。

代码跑完后,你可以在本地./chroma_db看到持久化的向量数据。以后每次更新知识库,只需要重新加载文档、增量跑一遍 transform 就行。

3.3 查询端实战:混合检索加重排

入库只是基础,查询端的表现才是关键。下面是查询处理的完整代码,我故意把检索过程拆开,方便你观察每一步的作用:

from llama_index.core import VectorStoreIndex from llama_index.core.postprocessor import SentenceTransformerRerank from llama_index.core.retrievers import VectorIndexRetriever from llama_index.core.query_engine import RetrieverQueryEngine from llama_index.core.indices.query.query_transform import HyDEQueryTransform from llama_index.core.query_engine import TransformQueryEngine # 加载已有索引 index = VectorStoreIndex.from_vector_store(vector_store) # 1. 基础向量检索器 retriever = VectorIndexRetriever( index=index, similarity_top_k=20 # 召回20个候选 ) # 2. 重排器 reranker = SentenceTransformerRerank( model="BAAI/bge-reranker-base", top_n=4 # 重排后保留4个 ) # 3. 组装查询引擎 query_engine = RetrieverQueryEngine( retriever=retriever, node_postprocessors=[reranker] ) # 4. 执行查询 response = query_engine.query("如何重启网关设备?") print(response) print("--- 参考依据 ---") for node in response.source_nodes: print(node.node.get_content()[:200])

这段代码里有几个值得展开的地方。similarity_top_k=20是召回数量,top_n=4是最终进入上下文的片段数。这两个数字的关系是金字塔形:召回多一些,保证不漏;重排精一些,保证不杂。在实际项目里,我通常把 top_k 控制在 20-50,top_n 控制在 3-6,具体看你上下文窗口大小。

上面的检索器我只用了向量召回。如果你要做混合召回,可以用 LlamaIndex 的QueryFusionRetriever,把 BM25 和向量检索结果融合起来。具体代码大致是:

from llama_index.core.retrievers import QueryFusionRetriever from llama_index.core.retrievers import BM25Retriever bm25_retriever = BM25Retriever.from_defaults( docstore=index.docstore, similarity_top_k=10 ) fusion_retriever = QueryFusionRetriever( [retriever, bm25_retriever], similarity_top_k=20, num_queries=1, # 可以改成多个查询变体做扩展 mode="reciprocal_rerank" # 用RRF融合排序 )

reciprocal_rerank是 RRF(Reciprocal Rank Fusion)模式,它把多个检索结果按排名取倒数分值融合,不需要调权重,效果稳定。这是混合检索的标配,强烈建议优先用这个模式,而不是简单拼接两个结果集。

3.4 检索效果检查:先跑三组测试再谈调优

管道跑通之后,第一件事不是接 Agent,而是验证检索质量。我习惯做一个“黄金问答集”,手工整理 10-20 个问题和对应的标准答案文档片段,然后跑一遍管道,看每个问题能不能在 Top-4 里找到正确答案。

以“如何重启网关设备”为例,我实际测试时看到召回的重排结果前四名分别是:

  1. 网关复位操作章节(正确)
  2. 网关设备电源指示灯说明(相关但不直接)
  3. 远程升级网关固件步骤(语义相近但主题偏离)
  4. 常见故障排除之设备无响应(正确)

这个结果说明管道工作正常,前四名里有两条直接命中。如果我看到 Top-4 里全是“不相关”的内容,就会往回查:切分是否合理(是不是大段塞在一起)、embedding 模型是否匹配语言、重排是否生效。大部分问题都出在这三个环节,而不是模型本身。

如果你希望进一步提升召回率,还有一个非常推荐的做法——HyDE(Hypothetical Document Embeddings)。思路是:先用大模型根据用户问题生成一个假想答案,再用假想答案的向量去检索。这样能极大缓解“用户问法跟文档表述不一致”的问题,代价是多一次 LLM 调用。实践中我觉得在问题含糊、文档分散的时候,HyDE 效果特别明显。

4. 常见问题与排查技巧实录

4.1 检索不到:问题根源基本在切分和召回

RAG 上线后最常见的反馈就是“问什么都不对”。我排查这类问题的套路很固定,照着来基本能定位:

第一步,看召回结果。把用户的问题直接丢进检索器,打印 Top-10 内容,看看有没有相关内容被召回。如果没召回,问题出在“检索端”:要么切分把关键内容切碎了,要么 Embedding 语言不匹配,要么向量库索引有问题。

第二步,看过召回是否回答了问题。召回有相关内容但回答不对,问题出在“生成端”:提示词没把约束说清楚,或者重排后留下的上下文不完整。这时候不是调检索,而是调提示词和生成参数。一个非常有效的做法是把top_n从 4 提到 6,因为有些回答需要多个片段拼起来,给少了模型看不全。

第三步,看数据本身。如果文档是扫描版 PDF,文字全是图片,你切分入库之前必须先做 OCR。这个坑太常见了,电子签章、扫描件、拍照件,这些在入库前没转成可检索文本,后面全白搭。我一般用 PaddleOCR 或 MinerU 这类工具先做文档解析,再进管道。

4.2 答非所问:多半是知识冲突和上下文污染

还有一种情况是内容检索到了,但模型答出来驴唇不对马嘴。我遇到过的两个高频原因。

第一个原因是上下文污染。如果你的 Agent 同时接了多个知识库检索,有些检索结果跟当前问题无关,但被一并放进了上下文,模型就会被带偏。解决办法是严格做相关性过滤,重排后的片段设置一个最低分数阈值,低于阈值的直接不进入上下文。宁缺毋滥,这是 RAG 上下文管理的核心原则。

第二个原因是知识冲突。知识库里同时存在“旧版操作流程 A”和“新版操作流程 B”,模型可能各取一半拼出个四不像。这种情况的处理方式是元数据过滤加时间排序。入库时给每个 chunk 打上版本号、日期、来源标记,检索时限定“只看最新版本”,或者在重排阶段优先选择时间新的片段。我实际维护知识库时,还专门做了一版数据清洗,把明显过时的内容标记但不删除,这样既能避免冲突,也保留了历史记录。

4.3 系统卡顿和成本失控:索引和缓存的优化

RAG 管道一旦面向真实业务,性能和成本问题必然冒出来。我给出三个最容易见效的优化方向。

第一,索引分层。不要把所有文档塞进一个集合。我按业务域拆成多个 collection 或分区,查询时先用元数据锁定可能相关的子集,再做向量检索。这个操作能把单次检索的扫描量降低一个数量级。

第二,缓存命中。对高频相似问题,加一层语义缓存。意思相近的问题直接复用上次的完整响应,只缓存准确性和好评率高的问题-回答对。实现方式也很简单,检索前先拿用户问题跟缓存库里最近 N 条问题做相似度比对,超过阈值就直接返回缓存。这一招能砍掉不少大模型调用成本。

第三,局部向量化。长文档更新时只需要重新切分和索引变更的块,不要每次都全量重建。Chromadb 原生支持增量写入,配合你的文件变更检测,更新成本可以压得很低。

4.4 从 RAG 到 Agentic RAG:下一步怎么演进

这篇文章标题是基础,但现在的趋势是 Agentic RAG——让 Agent 自己决定“要不要检索”、“检索什么”、“要不要再检索一次”。我实践下来的体会是:不要一上来就搞复杂,先把基础管道跑稳,再逐步把控制权交给 Agent。

一个轻量演进路径是这样的。第一阶段,单轮 RAG:用户问题进来,检索一次,生成回答。第二阶段,带路由的 RAG:Agent 先判断问题属于哪类知识域,再决定调用哪条检索管道。第三阶段,多轮交互式 RAG:Agent 根据初步检索情况,决定是直接作答还是改写问题二次检索,这就是 Agentic RAG 的雏形。第四阶段,工具融合:RAG 检索结果可以和数据库查询、API 调用结果合并,共同作为生成依据。

我在最近的一个项目里就是把“查询 ERP 产品库存”这类精确数据和“产品介绍语义检索”这类模糊知识结合,Agent 先用语义检索找到产品候选,再调 ERP 接口拿库存数量,最后汇总回答。这种“检索定候选、工具落实锤”的配合,比单纯 RAG 或者单纯 API 查询都要好用。

写在最后的个人体会

这条 RAG 管道我前前后后重写过不下五版,最深刻的体会是:RAG 的难点不在“跑通”,而在“可控”。跑通只需半天,但要让召回稳定、答案可信、更新不翻车,需要花很多心思在切分策略、检索融合、元数据管理这些细节上。很多人一开始就冲 Agentic RAG,结果基础管道不稳定,Agent 越是自主决策,错误被放得越大。

如果你正要从 0 到 1 搭建 AI Agent,我建议把 RAG 当成第一优先级的基础工程来做。先把一两条业务线的知识库管道跑稳,再做 Agent 的规划与工具调用,你会发现后面的路顺畅得多。下一篇我打算写 Agent 的记忆管理——RAG 解决的是“从外部拿知识”,记忆要解决的是“怎么记住对话过程中产生的临时信息”,两者配合起来,才算一个完整的知识体系。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 15:43:09

YOLOv5行人超界报警实战:从解压配置到判定部署

简介:一套基于YOLOv5与Qt5的行人范围超界报警系统完整方案,面向计算机视觉开发者、智能监控项目集成人员及Qt应用学习者,解决实时行人检测、目标区域划定与越界报警联动问题。系统使用YOLOv5从视频流中识别行人,再经Qt5界面实现区…

作者头像 李华
网站建设 2026/9/28 15:42:17

GitHub Trending日榜深度玩法:从star增速到项目评估清单

每天早上到工位,我做的第一件事往往不是开邮箱,而是打开 GitHub Trending 的日榜。这个习惯保持了挺多年,手机、电脑上各存了一个入口。很多人觉得日榜是"热门项目集合",每天翻一翻图个新鲜就关了。但我逐渐发现&#x…

作者头像 李华
网站建设 2026/9/28 15:42:17

RK3568 AMP双系统实战:Linux+RT-Thread一芯双核,3分钟搞定实时性

做嵌入式这几年,凡是碰过工业控制、机器人、实时通信的项目,迟早都会面临同一个尴尬:主控芯片性能足够强,但Linux的实时性就是差点意思。尤其是在RK3568这种四核A55的平台上,跑个EtherCAT主站、运动控制算法&#xff0…

作者头像 李华
网站建设 2026/9/28 15:40:36

树莓派CM4物联网实战:4G模块与CSI摄像头远程监控开发全指南

我是一个把树莓派当“万能插座”折腾了快八年的老玩家,从最早的树莓派1代B型一路玩到CM4,踩过的坑比吃过的盐还多。这次收到一套树莓派CM4核心板,配的是微雪(Waveshare)CM4 IoT底板、4G模块和CSI摄像头,算是…

作者头像 李华
网站建设 2026/9/28 15:39:46

Cadence Allegro 17.4入门:从原理图工程到元件库完整流程

很多新手拿到Allegro 17.4的第一反应,是发现它跟网上老教程里那种界面完全不一样,菜单密密麻麻,还不知道该从哪个入口进。我自己带人的时候最深的感受是,真正劝退新手的并不是后面PCB Layout那些高深技巧,反而是最前面…

作者头像 李华
网站建设 2026/9/28 15:39:42

uniApp安卓串口通信实战:RS485与MODBUS协议解析指南

做安卓串口通信,在uniApp里绕不开一个现实问题:官方没有现成的串口插件,市场上能用的第三方模块又良莠不齐。半年前我接手一个农业环境监测项目,需要在安卓平板上通过RS485总线读取温湿度、光照、土壤墒情等多路传感器数据&#x…

作者头像 李华