☰
AI Agent知识获取管道:RAG全链路详解与踩坑指南
2026/9/28 8:58:37 网站建设 项目流程

这个系列走到第四篇,前面的内容基本把AI Agent的主体框架拆得差不多了——规划、工具调用、记忆管理都聊过。但你很快会发现,不管框架搭得多漂亮,Agent一遇到实际问题还是会翻车:它不知道你公司的内部制度,不知道你产品的参数表,不知道昨天刚发布的行业数据。原因在于,大模型的知识是训练时固化的,它不会自己主动去找资料。想让Agent真正“知道”该知道的东西,就得给它铺一条“知识获取管道”,而当前最主流的实现方式就是RAG(Retrieval-Augmented Generation,检索增强生成)。

这篇我打算把RAG的基础讲透,从它在Agent架构里的定位,到数据入库、切分、向量检索、生成的全链路,再给出一套能直接跑起来的最小实现,最后把实战里踩过的坑一并列出来。适合两类人:一类是刚学完Agent基础、准备给Agent接知识库的开发者;另一类是已经在用LangChain、Spring AI或LangChain4j做RAG、但总感觉效果不稳定的朋友。内容偏实践,理论部分我用大白话讲清楚,尽量不让概念卡住你。

1. 为什么AI Agent需要一条“知识获取管道”

1.1 Agent的短板:不是不会说,而是不知道

大模型本身就是一个“知识会被冻结”的系统。训练结束那天,它知道的东西就定格了,之后的世界它一概不知。你问它昨天刚发布的行业数据,它要么含糊其辞,要么一本正经地编。更麻烦的是企业内部的知识——财务制度、接口文档、售后话术——这些根本不在模型训练数据里,模型天生就不可能知道。所以Agent如果不对接外部知识,它所有的推理都是建立在“猜”的基础上。

我用一个类比讲给新手听:你请来一位见多识广的顾问,他逻辑缜密、能说会道,但一天都没在你公司上过班。你问他“我们公司的报销流程是什么”,他答不上来,只能根据通用常识给你编一套流程。RAG解决的事情,就是在你问他之前,把公司的《报销管理制度》递到他手上,让他照着内容归纳、复述。这个“递资料”的动作,本质上就是一条知识获取管道。

这里有必要把RAG和微调(Fine-tuning)分清楚。微调是让模型把知识“背”进参数里,成本高、更新慢,而且新知识和旧知识容易打架;RAG是让模型“开卷考试”,知识放在外部库里,随时可以替换、增补、下线。在Agent场景里,我几乎总是优先推荐RAG,因为Agent要面对的知识往往是来源多、变化快的,让模型逐一背下来,不如给它一条随时可查的管道。

1.2 知识管道在Agent架构里的位置

回看Agent的典型决策循环:规划(Plan)→ 动作(Act)→ 观察(Observe)→ 反思(Reflect)。在这个循环里,知识获取管道通常有两种身份。

第一种是工具。你把一个“查询知识库”的工具注册给Agent,Agent在规划阶段判断出“这个问题需要查资料”,于是触发这个工具,把返回的内容作为观察结果再送回推理链路。第二种是长时记忆的底层存储。Agent的长期记忆如果落在向量库里,本质上也是一个RAG系统:写入时把经验向量化,读取时按相关性召回。所以,把RAG理解成“啃知识的工具”或者“长期记忆的读写通道”,都不算错。

这也是我特意把这篇独立成一课的原因。前几篇我们讨论工具调用、记忆管理,是在解决“Agent能把动作做得多复杂”;这一篇解决的是“Agent依据什么来做决策”。没有可靠的知识获取,工具越强越容易一本正经地办错事——检索回来的前提是错的,后面所有推理都会跟着跑偏。

顺便提一个热词“Agentic RAG”。基础RAG是一条“用户问一次、系统查一次”的流水线;Agentic RAG则是把检索的决定权交给Agent自己,让它判断要不要查、查几次、拆成几个子问题查。判断的自主性向前移了,但底层依赖的仍然是基础RAG那套检索能力,所以基础必须扎实。这篇文章讲的东西,是所有高阶RAG玩法共同的地基。

2. RAG核心链路拆解:从入库到生成,五个环节一个都不能少

2.1 先记住这条流水线

不管用LangChain、LlamaIndex、Spring AI还是LangChain4j,RAG的主链路基本一致,总共五步:

  1. 加载(Load):从PDF、Word、Markdown、网页、数据库里把原始文本读出来。
  2. 切分(Split):把长篇文档切成大小合适的文本块(chunk)。
  3. 向量化(Embedding):用embedding模型把每个文本块转成向量。
  4. 存储与索引(Store):把向量、原始文本和元数据一起存进向量库。
  5. 检索与生成(Retrieve & Generate):用户提问时,把问题转成向量,在库里做相似度检索,取回Top-K个文本块,连同问题一起交给LLM生成答案。

还是用“开卷考试”来类比:第一步是决定带哪些资料进考场,第二步是给资料分章节、编目录,第三步是给每个章节做索引卡片,第四步是把卡片按主题分类放进检索柜,第五步是开考后按题目快速抽出最相关的几张卡片,再照着卡片写答案。

不少新手把重点放在embedding模型和向量库选型上,但我做了几个项目之后发现,翻车现场大多集中在前两步——数据没洗干浄、文档切得不合理。数据怎么进库,直接决定了检索效果的上限。

2.2 文档加载与切分:把资料切成“能查的样子”

先说加载。常见的坑是把PDF直接丢给程序就完事。PDF分两种:文字版和扫描版。文字版可以直接抽文本;扫描版其实就是图片,不做OCR的话,进去的全是乱码。我处理扫描版一般先过一遍OCR,开源可以用PaddleOCR,云端用各家文档理解API,把图片转成带位置信息的文本,再进后续流程。Office文档、网页、数据库同理,格式越乱,清洗工作量越大。这是RAG项目里最不性感、但最花时间的部分。

接下来是切分(chunking)。为什么不把整篇长文塞进去?两个原因:一是上下文窗口有限,二是检索粒度太粗。你想查“2024年销售奖励政策的适用范围”,如果把整篇文档作为一个检索单元,模型拿到一大坨内容,相关段落被无关信息淹没,回答质量必差。切分的目的,就是把知识切成合适的“最小可检索单元”。

切分策略没有银弹,但有几条朴素的实操经验:

  • 纯文本按固定大小切,我常用400到800个token一块,overlap设50到100。重叠是为了防止句子在切分边界被拦腰截断,让相邻块保留上下文尾巴。打个比方:你读一本每一页都重复印了上一页最后一行的书,顺畅度会高很多,overlap就是干这个的。
  • 有结构的文档优先按结构切。Markdown标题、HTML标签、Word标题样式都可以作为切分点,这比按字符数硬切干净得多。
  • 代码、表格、对话这类特殊格式,默认切法基本都会切坏,需要单独写解析逻辑。

为什么是“400到800 token”而不是精确数字?因为太小信息量不足,太大又让相似度计算被无关词稀释。给个具体感受:一篇3000字的中文文档,假设按500 token切,大约会生成6到8个块,加上overlap,存储量会比原文多出零星几个百分点,这是正常开销,不用心疼。

切分的同时一定要维护元数据(metadata)。来源文件名、章节标题、页码、更新时间这四样,我建议至少存前三样。作用有两个:一是检索时可以按元数据过滤,比如只查2025年的制度;二是回答时能附上引用来源,用户看到“来源:《XX制度》第三章第2节”,信任感完全不一样。这个细节在实际交付时特别加分。

2.3 向量化与向量存储:选模型就是选“怎么理解语义”

embedding模型的作用是把文本变成一串数字(向量),让语义相近的内容在数学空间里距离也近。它比关键词匹配强的地方在于理解“语义相似”。你写“今年业绩第一名”,文档里写的是“2025年销售冠军”,字面完全不同,但在好的embedding模型下,向量距离会非常近。

一个生活化类比:embedding模型就像给每块内容写“索书标签”,但不是按书名的死标签,而是按“它大概在说什么事”的活标签。检索时把你的问题也写成一份标签,然后在书库里找标签最像的几页。

选embedding模型,我主要看三个维度:

  • 语言:中文场景优先选中文优化过的模型,bge系列在中文语义上的表现一直比较稳。
  • 向量维度:越高通常越精细,但存储和计算成本也更高。OpenAI的text-embedding-3-large有3072维,一些轻量开源模型只有384维。
  • 部署方式:有私有化要求就选开源模型本地部署,省心且数据不出域。

向量库选型可以参考这张表:

向量库适合场景特点
Chroma / FAISS个人练手、原型验证轻量、安装快,本地就能跑
pgvector已有PostgreSQL的业务系统复用PG运维体系,不用多引一套中间件
Qdrant中小规模生产功能全,支持过滤和Payload,部署简单
Milvus大规模生产、海量向量分布式能力强,但运维成本也高

检索环节的关键参数是Top-K。为什么只取前几个而不把相似的全都返回?因为LLM上下文有限,塞太多无关资料反而冲淡重点,还会增加成本和延迟。K值一般取3到10,具体看知识粒度:每块只有一两句话,K可以大一点;每块是完整章节,K就得小一点。

2.4 生成环节:把资料变成答案

检索回来后,生成环节的核心是“组装Prompt”和“约束模型行为”。我常用的Prompt模板大致是这样:

你是一名助手。请仅根据下面提供的资料回答问题。如果资料中没有相关信息,请直接回答“资料中未找到相关信息”,不要编造。回答时尽量引用资料的来源。

资料: [来源:《2025年销售政策》第三章]

  1. ...
  2. ...

问题:...

这句话有两层用意:一是给模型划边界,明确不让它用训练记忆里的通用知识来补充发挥;二是给它引用钩子,因为资料里带了元数据,模型回答时就有据可附。当然,模型不总是听话,所以生产环境我会再加一层轻量校验:看答案里的关键数字、专有名词是否能在返回的资料里找到,找不到就标记“低置信度”或触发二次检索。这不能完全消灭幻觉,但能把明显离谱的回答拦下来。

3. 从基础RAG到Agentic RAG:检索这件事情也需要思考

3.1 基础RAG的局限

基础RAG最大的问题是“用户问什么,系统就原样查什么”,缺少对问题的判断和转化。现实提问往往带口语、省略、指代和歧义。比如用户问“今年的情况怎么样”,不结合上下文,系统根本不知道“今年”是哪年、“情况”指什么指标。再比如多跳问题:“目前卖得最好的产品是哪一年发布的?”这需要先查出“卖得最好的产品是谁”,再拿产品名去查“发布时间”,一次检索根本解决不了。

热词里的“hit rate”(检索命中率)衡量的就是这件事:在所有测试问题里,正确答案所在的文档块被成功召回的占比。基础RAG的hit rate不稳定,主要因为它只做了一次“按相似度盲查”,缺少问题拆解和转换。

3.2 让Agent介入检索决策:Agentic RAG

Agentic RAG的核心思路,是把“要不要查、怎么查”变成Agent的推理动作。典型做法是给Agent注册一个知识库检索工具,由Agent在规划循环里决定调用。相比基础RAG,它能做到几件很实际的事情:

  • 查询改写:先把口语问题改写成适合检索的表达。例如“去年卖得最好的那款产品是啥”改写成“2025年销售额最高的产品名称”,再用改写后的query去向量库查。
  • 子问题分解:“卖得最好的产品是哪年发布的”拆成“找出销售额最高的产品”和“查询该产品的发布时间”两个子检索,串行执行。
  • 多轮补查:第一轮检索结果不够充分时,Agent自己决定再查一次,甚至换一个角度查,比如先查实体,再查该实体的关联数据。

热词里频繁出现的GraphRAG和本体RAG(Ontology RAG),本质上是“用结构增强检索”的进阶路线。基础RAG把文本切成块,块与块的关系是隐性的;GraphRAG把实体和关系显式抽出来建图,检索时可以沿着关系路径找“知识之间的连接”。当你处理的知识密集且关系复杂,比如公司组织架构、产品家族、法规条款互相引用时,这条路线的价值会很明显。学习路线上,我建议先把基础RAG做扎实再上图谱,不要一上来就上重型方案。

3.3 多路召回与重排序:把“检得到”变成“检得准”

另一个让hit rate大涨的组合拳是“混合检索 + 重排序”。混合检索指的是同时用BM25关键词召回和向量相似度召回。

为什么需要关键词召回?因为向量模型对专有名词、型号、代码、缩写经常表现不稳定。比如用户搜“Q7-F2型传感器”,文档里写的是“Q7/F2 series”,语义相近但字符差异大,纯向量可能排不上;而BM25是精确字符匹配,对这种查询非常友好。两条路各召回一批,合并去重,就是“多路召回”。

但多路召回之后必须面对噪声。向量检索Top-100里可能有大量相似但真正不相关的块,直接塞给LLM会灾难性拉低回答质量。这时用Rerank模型把粗召回结果重新精排一遍,只保留最相关的10个。我实测下来,加Rerank后hit rate普遍能再涨10到20个百分点。在很多场景里,Rerank对最终效果的影响比换一个更强的LLM还大。

4. 从0到1跑通一个最小RAG(附可抄的配置)

4.1 为什么我推荐LangChain + Chroma这套起手组合

讲完原理,直接上实战。这个最小实现用Python + LangChain + Chroma向量库,embedding调用本地模型接口。选这套组合是因为依赖少、好理解:Chroma是嵌入式向量库,不需要单独启服务;LangChain把加载、切分、检索的组件都封装好了,适合看清整条链路。如果你用的是Java技术栈,参照Spring AI或LangChain4j里对应的Retriever、EmbeddingModel、VectorStore组件即可;用.NET的话,Semantic Kernel里也有对应的Memory和VectorStore抽象,核心思路完全一致。

先装依赖:

pip install langchain langchain-community langchain-ollama langchain-text-splitters langchain-chroma chromadb

再准备embedding模型。我这里以Ollama本地部署的bge-m3为例,你也可以用任何OpenAI兼容接口,只是换一下配置:

ollama pull bge-m3

4.2 三步实现:入库、检索、生成

第一步,加载文档并切分入库。假设你有一个policy.md,内容是一段内部制度:

from langchain_community.document_loaders import TextLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_community.embeddings import OllamaEmbeddings from langchain_chroma import Chroma # 1. 加载文档 loader = TextLoader("policy.md", encoding="utf-8") documents = loader.load() # 2. 切分:块大小500 token,重叠50 token splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=50, separators=["\n\n", "\n", "。", "!", "?", " "], ) chunks = splitter.split_documents(documents) # 3. 向量化并写入Chroma embeddings = OllamaEmbeddings(model="bge-m3") vectorstore = Chroma.from_documents( documents=chunks, embedding=embeddings, persist_directory="./chroma_db", ) print(f"已入库 {len(chunks)} 个文本块")

第二步,构建检索器,加上相似度阈值过滤,避免把太不相关的资料捞回来:

retriever = vectorstore.as_retriever( search_type="similarity_score_threshold", search_kwargs={"k": 4, "score_threshold": 0.5}, )

注意,score_threshold的取值取决于embedding模型的相似度分布,不是一个万能值。我的做法是先不设阈值跑一遍,打印检索结果的得分,观察相关和不相关的分界线,再回填这个数。

第三步,组装问答链路:

from langchain_ollama import OllamaLLM from langchain_core.prompts import ChatPromptTemplate from langchain_core.runnables import RunnablePassthrough llm = OllamaLLM(model="qwen2.5:7b") prompt = ChatPromptTemplate.from_messages([ ("system", "你是一名助手。请仅根据下面提供的资料回答问题。如果资料中没有相关信息,请直接回答'资料中未找到相关信息',不要编造。\n\n资料:\n{context}"), ("human", "{question}"), ]) def format_docs(docs): return "\n\n".join(f"来源:{d.metadata.get('source', '未知')}\n{d.page_content}" for d in docs) rag_chain = ( {"context": retriever | format_docs, "question": RunnablePassthrough()} | prompt | llm ) answer = rag_chain.invoke("2025年的销售目标是多少?") print(answer)

这套代码跑通之后,你就有了一个能回答“内部制度问题”的最小Agent。再进一步,把整个rag_chain包装成一个函数注册成Agent的工具,交给Agent在规划中自主决定何时调用,这就是从基础RAG迈向Agentic RAG最简单的一步。技能(skill)和RAG的结合,通常也是从这个入口开始的。

4.3 落地到本地知识库:数据更新与离线部署

很多人做本地知识库,想把ERP里的产品数据、企业内部Wiki、客服话术接进来。这种场景我提醒三个实操要点。

第一,先拿小规模数据验证。第一次跑通,一两份文档就够;全量数据要等链路稳定之后再灌。我见过不少项目一上来就灌几十万份文档,结果检索慢、结果乱,问题都没法定位,最后还是退回小样本验证。

第二,更新策略要提前想清楚。向量库不像关系型数据库有完整的事务能力,删改一条数据需要同步清理对应块的向量和元数据。简单方案是“全量重建 + 增量追加”:低频变化的文档用定时全量重建,高频新增内容用增量写入,并且记录每个数据块的来源版本号,方便失效数据下架。具体怎么设计,跟数据变化频率强相关,没有统一标准。

第三,离线部署要控制模型规模。本地知识库通常还要求LLM也本地跑,硬件有限时,尽量选小参数模型加RAG的组合,效果反而比裸用大模型更可控。我自己的体会是,在8GB显存的机器上,量化为Q4的7B模型配合RAG,对“内部知识准确性”的满足度能高出不少,因为资料是从你自己库里来的,模型只需要规规矩矩做归纳就行。

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

最后把这几年做RAG最常见的翻车现场整理成一张排查表,方便对照处理:

症状常见原因排查思路解决参考
检索结果明显不相关切分太碎或太粗;embedding模型与领域不匹配打印检索Top-5的chunk和得分调整chunk size;换领域匹配的embedding;加Rerank
完全检索不到扫描PDF没OCR;编码乱码;加载器漏字段先确认原始文档文本是干净的补OCR;清洗数据;检查loader配置
回答答非所问Top-K太大,无关内容冲淡上下文查看最终拼进Prompt的context减小K;提高score_threshold;加Rerank
回答有明显幻觉Prompt约束不够;资料确实缺失让模型输出引用来源,人工抽查强化“只根据资料回答”约束;加引用验证环节
检索延迟高向量库没建索引;embedding调用串行看慢在检索还是模型调用给向量建索引;embedding走批量或缓存
知识更新不生效向量库里旧块没有清理检查数据版本号增量删除加重建,维护好元数据

再说两个容易踩的大坑。

第一个,切分方案不合适造成的“假性失效”。有次我把一份合同按固定500 token切,结果“违约责任”条款被拦腰切开,一半在上一个块、一半在下一个块,检索时模型只看到前半句,答得驴唇不对马嘴。后来改成按条款结构切分,问题立刻消失。切分不是为了满足某种参数审美,而是为了对齐内容的天然结构。

第二个,忽略评估环节。没有评估的RAG项目,效果好坏全靠感觉。至少准备20到50个“问题-正确答案所在文档”的测试样例,每次跑一遍看hit rate,改一个参数就重跑一次,用数据说话。“hit rate”现在在面试里已经是常见考点了,能讲清楚这个概念本身就是加分项。

再补充一个成本与延迟的调优心得。生产环境里,embedding调用是最容易被忽略的成本项,特别是文档量大时。我的做法是把所有chunk离线批量embedding,存成文件再批量入库,避免在线逐条调用;线上检索时只embedding用户问题这一条,成本几乎可以忽略。向量库如果数据量大,可以启用量化,比如把float32向量压成int8,检索精度损失通常在可接受范围内,但存储能降四倍。

最后说点大实话。我见过太多人花大量时间研究向量库选型、对比embedding模型,最后效果不好,发现是文档解析乱码、切分把上下文切断了。RAG做得好不好,八成取决于数据侧的处理,而不是模型多高级。先把一条数据从“原始文档”到“能被正确检索回来”的整条链路跑通,再谈优化。这篇按基础版把链路拆开讲了一遍,接下来的查询改写、重排序、GraphRAG,都会在这条链路上继续做文章。如果你正在给Agent搭知识库,照这个最小实现先跑起来,再回来对照问题表自查,会省掉很多弯路。

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

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

立即咨询