☰
RAG实战:为AI Agent打造可靠的知识获取管道
2026/9/28 15:11:45 网站建设 项目流程

这几年做 Agent 项目,我反复被问到一个问题:大模型已经这么强了,为什么做起企业知识问答还是经常一本正经胡说八道?原因其实不复杂——大模型的知识是训练时“背”进去的,训练数据截止日期之后的事、企业内部私有文档、ERP 里的产品参数,它一概不知道。RAG(检索增强生成)就是给 Agent 加一条知识获取管道,让它在回答之前先去外部知识库里“查资料”,再把查到的内容作为依据交给模型生成答案。

这篇是“走进 AI Agent”系列的第四篇,前几篇分别聊了 Agent 的基础框架、记忆设计和工具调用,这一篇把知识获取管道单独拎出来讲透。不管你是刚接触 RAG,还是已经调过几个 RAG 项目但效果不稳定,这篇都适合。我会从原理、链路、最小实现、进阶玩法和常见问题排查五个部分展开,目标是你看完能自己搭一条可用管道,并且知道效果不好时该从哪里下手。

1. 为什么要给 Agent 加一条知识获取管道

1.1 LLM 的天然短板:知识是“学”来的,不是“查”来的

先打一个比方。一个名校毕业的高材生,聪明、逻辑强、表达顺畅,但进了一家新公司,不看公司文档、不查系统、不带手机,你问他某款产品的保修期、某个流程的审批节点、某个客户的最近工单,他大概率只能凭常识猜,猜错了他自己都不知道。

LLM 就是这样的高材生。它的知识以参数形式固化在神经网络里,训练完之后就定死了。公开互联网上的常识、论文、代码它见过不少,但你的内部知识库、产品手册、售后记录、业务规范,它没见过。更麻烦的是,知识还在持续变化:公司发布了新版本、产品线调整了价格、流程更新了规则,这些增量信息模型根本感知不到。

所以做 AI Agent 时,我们必须承认一个事实:模型负责“聪明”,知识负责“准确”。聪明可以靠基座模型给,准确必须靠外部知识管道补。RAG 就是这条管道的主流实现方式,它不追求把知识塞进模型大脑,而是让模型在需要的时候能快速“查到”真实信息,再把查到的信息作为上下文去组织回答。

1.2 RAG 在 AI Agent 体系里到底管哪一段

一个完整的 AI Agent,通常可以拆成四层:模型层负责推理生成,规划层负责拆解任务,记忆层负责保存历史上下文,工具层负责调用外部能力。知识获取管道是独立于这四个层之外的横向能力,但在实际系统里它经常和记忆、工具产生重叠,这也是很多初学者概念混淆的地方。

我习惯这样区分:记忆层管的是“这个会话里我们聊过什么”,知识获取管道管的是“用户现在问的这件事,我需要的外部事实从哪来”。工具层里,检索工具、数据库查询工具、API 调用工具都属于广义的知识获取手段,而 RAG 是把“非结构化文档检索”这件事做得最成熟、最容易落地的一套方法。

RAG 的全称是 Retrieval-Augmented Generation,检索增强生成。它改变了单纯的“用户提问 -> 模型回答”这条路径,改成“用户提问 -> 先到知识库检检索 -> 把检索结果拼进上下文 -> 模型基于上下文回答”。这段流程在 Agent 内部就像一条补给线,决定了每一轮回答是否建立在真实资料上。

1.3 为什么不是微调,也不是无脑塞上下文

有人会问,既然要让模型知道企业内部知识,为什么不做微调?或者干脆把知识库全塞进上下文?这两个思路我都试过,也踩过坑,分别说一下。

微调的本质是把知识写进权重里。听起来很美好,但实际代价很高:收集标注数据、训练、评估、部署,一套流程走下来至少一到两周,知识更新一次就要重来一次。而且微调有一个很难受的问题——灾难性遗忘,模型学了新知识,旧知识可能就变模糊了。对知识频繁变化的企业场景,微调的性价比很低。

把知识全塞进上下文也不现实。一个几百页的产品手册,token 量可能十几万,即使模型窗口够大,成本也扛不住,而且超长上下文里模型注意力会分散,真正有用的信息被淹没,反而更容易答错。RAG 走的是“按需取用”的路线,每次只把最相关的几段资料拉进来,成本可控、知识可更新,这是它成为 Agent 标配的根本原因。

2. RAG 的完整链路:索引、检索、生成

2.1 离线索引:把文档变成可检索的向量

RAG 第一步不是检索,而是“先把知识库准备好”。这个环节叫离线索引,核心动作包括文档加载、清洗、切分、向量化和入库。

文档加载就是把 PDF、Word、Markdown、HTML 等格式的原始文件读取成纯文本。这里要注意:很多 PDF 的排版信息会干扰文本顺序,表格会被截断,页眉页脚会被当成正文。清洗就是把这些杂质去掉,只保留对回答有用的内容。这一步看似简单,但决定了索引质量的上限。

切分是索引环节最值得花时间的操作,也就是 chunking。一个很自然的想法是把整篇文档变成一个向量,但这样检索时粒度太粗,用户问“第三章节里的某个参数”,你召回的是整篇文档,模型找不到重点。反过来,如果每个句子都变成一个向量,检索粒度太细,又会丢失上下文,模型看到的是一个孤零零的句子,不知道它属于哪个章节,同样答不好。

我的经验是,切块要兼顾语义完整和检索精度,一般按段落级别再加重叠窗口处理。所谓重叠窗口,就是两个相邻 chunk 之间保留一小部分重复文本,避免一句话被硬生生切开后两边都不完整。针对中文技术文档,我常用的参数是 chunk_size 在 300 到 600 字之间,overlap 留 10% 到 20%。具体怎么调,后面实操部分再说。

切完块之后,需要用 Embedding 模型把每块文本转成稠密向量。这里要建立两个基础认知:第一,向量是一个几百维到上千维的浮点数数组,代表文本在语义空间里的位置;第二,语义越相近的文本,向量距离越近。有了向量,才能实现“按意思找资料”,而不是只按关键词找。

最后把向量和原始文本一起存进向量数据库,常见的有 FAISS、Chroma、Milvus、Qdrant、Weaviate、pgvector 等。小项目用 FAISS 或 Chroma 就能跑,生产环境则要考虑规模、并发、权限过滤和运维成本,后面选型部分会展开。

2.2 在线检索:问题进来以后发生什么

索引建好之后,系统进入在线服务阶段。用户发来一个问题,RAG 的检索环节大致分四步。

第一步,把用户问题用同一个 Embedding 模型转成向量。这里有一个很容易被忽略的坑:如果你索引文本用的是 model A,查询时也必须用 model A,两边的向量空间才一致。混用不同模型,检索质量会断崖式下降。

第二步,在向量数据库里做相似度计算,找到与问题向量最相近的候选 chunk。常见的距离计算方式有内积、余弦距离和欧氏距离,具体用哪种取决于向量数据库和 Embedding 模型的匹配关系。这一步返回的是一批候选,排序依据是相似度分数。

第三步,取回 top-k 个候选。k 一般取 3 到 6,不是越大越好。k 太大,不相关的内容混进来,模型会被带偏;k 太小,关键信息可能漏掉。第四步,可选的重排序(rerank)环节,用一个专门的排序模型对候选重新打分,把最贴合的排到最前面。这个环节能明显提升质量,尤其是候选之间相似度差距不大的时候。

这里多说一点:纯向量检索对“精确匹配”并不友好。比如用户问产品型号“ZX-3000”,向量检索可能觉得“ZX-3000 系列新款”语义相近,但它不一定能精准锁定型号字符串。所以主流方案都倾向采用混合检索,把关键词检索(比如 BM25)和向量检索结合起来,再用 RRF(Reciprocal Rank Fusion)合并排序结果,兼顾精确匹配和语义理解。

2.3 生成:把检索结果“喂”给模型

检索引擎把候选资料找回来后,才算进入真正的“生成”环节。这一步的难点不是调用模型,而是怎么把检索结果组织成模型能高效利用的上下文。

我的做法是,在 prompt 里把检索结果明确标成“参考资料”,并给每一段编号。然后告诉模型:请优先依据参考资料回答;如果资料里没有答案,直接说明资料中没有相关信息,不要自己编造。如果你希望回答可追溯,还要要求模型在引用时标注来源编号,比如“根据产品手册第 3 节(资料[1])……”。

上下文拼装时有一个度的问题。检索结果太多,模型会淹没在文字里,抓不住重点;太少,又可能信息不全。经验值是单个问题给 3 到 5 段资料,每段控制在 300 字左右,总体上下文不超过两千字。另外,生成阶段的温度参数要调低一点,比如 0.2 到 0.3,减少模型自由发挥的空间。RAG 场景要的是“有根据的陈述”,不是创意写作。

2.4 三个阶段一表对比

阶段核心输入核心输出关键指标主要成本
离线索引原始文档向量库 + 元数据切块质量、覆盖率算力、存储
在线检索用户问题候选片段列表命中率、召回率、排序质量向量计算、数据库查询
生成候选片段 + 问题最终答案准确性、可溯源性LLM 推理 token

三个阶段的权重不一样。很多团队第一次搭 RAG,把精力全花在调模型 prompt 上,结果检索出来的东西根本不对,模型再强也答不好。我个人的经验比例是:索引和检索占 70% 的优化空间,生成只占 30%。检索不到、检索不准,后面全白搭。

3. 实操:从零搭一条最小可用的文档问答管道

3.1 选型:别一上来就上全家桶

RAG 的生态非常繁荣,选型很容易眼花缭乱。先说我的选型原则:从最简方案开始,跑通之后再按需升级。

语言层,Python 生态最全,LangChain 和 LlamaIndex 是两大主流框架。LangChain 的组件化程度高,适合自己控制链路;LlamaIndex 对索引和检索的抽象更细腻,文档问答场景上手很快。Java 技术栈可以看 Spring AI、LangChain4j 和 Semantic Kernel,最近企业级 Agent 项目里用这几个的越来越多。

向量库层,做验证直接用 FAISS 或 Chroma 就够,零运维,代码里几行就能跑起来。上生产环境再考虑 Qdrant、Milvus、pgvector 这类。pgvector 的优势是如果你已经在用 PostgreSQL,可以直接在原库上扩展,少一套组件;Milvus 则更适合大规模、高并发、需要复杂过滤的场景。

模型层,Embedding 选型要结合语言。我处理中文文档比较多,常用的开源模型是 BAAI/bge-base-zh-v1.5、m3e 这类,效果不错且部署成本低。生成模型可以直接用 GPT 系列、Claude 这类商用模型的 OpenAI 兼容接口,也可以用本地部署的 Qwen 系列。RAG 对生成模型的要求不算高,关键是检索质量要稳。

3.2 最小实现流程

我用一个最简单的场景示范:本地有一份产品手册 PDF,希望 Agent 能回答“这款设备的保修期是多久”这类问题。

第一步,加载文档并按自然段落切块:

from langchain_community.document_loaders import PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.embeddings import HuggingFaceEmbeddings from langchain_community.vectorstores import FAISS loader = PyPDFLoader("product_manual.pdf") docs = loader.load() splitter = RecursiveCharacterTextSplitter( chunk_size=300, chunk_overlap=50, separators=["\n\n", "\n", "。", "!", "?", ";", " ", ""] ) chunks = splitter.split_documents(docs) embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-base-zh-v1.5") vectorstore = FAISS.from_documents(chunks, embeddings) vectorstore.save_local("knowledge_index")

这段代码里的两个关键点:separators 列表让切分器尽量在句号、问号等完整语义边界上断开,避免把一句话腰斩;chunk_overlap 保证跨块语义不断裂。切块完成后,向量库就建好了。

第二步,检索并组装上下文:

vectorstore = FAISS.load_local( "knowledge_index", embeddings, allow_dangerous_deserialization=True ) query = "这款设备的保修期是多久?" docs_with_score = vectorstore.similarity_search_with_score(query, k=4) context = "\n\n".join( [f"[资料{i}] {doc.page_content}" for i, (doc, _) in enumerate(docs_with_score)] )

这里可以看到,我把每段召回结果都编了号。分数在这个阶段先不下结论,因为 FAISS 返回的分数跟具体向量模型强相关,不同模型之间分数不可直接比较,所以更可靠的做法是人工看召回内容,而不是死盯阈值。

第三步,把上下文和问题一起交给生成模型:

prompt = f""" 请根据下面的资料回答问题。只依据资料内容回答。 如果资料中没有相关信息,请直接回复:“资料中没有找到相关信息”,不要编造。 回答时请在句子末尾标注你参考的资料编号,例如 [资料1]。 资料: {context} 问题:{query} """

用任意 OpenAI 兼容接口把 prompt 发给模型,就能得到带引用的答案。这个最小实现的核心价值在于:它完整跑通了“文档 -> 切块 -> 向量化 -> 检索 -> 生成”的全流程,适合作为后续所有调优的起点。

3.3 关键参数怎么调

跑通只是第一步,效果不好才是常态。RAG 最值得调的参数有三个:chunk_size、top_k、检索策略。

chunk_size 决定了检索粒度的粗细。我在中文技术文档上的经验是:如果文档是手册、规范一类,300 到 500 字比较稳;如果是代码注释或工单记录,可以更小,200 字左右;如果是很长的章节,宁可切多块也不要把几万字塞进一个 chunk。判断标准很简单:随机取 20 个问题,看召回的 chunk 里是否都能覆盖关键信息。

top_k 决定了每轮问答投喂给模型的资料条数。我一般从 5 开始调。如果发现检索结果里经常混入不相关内容,就降到 3;如果觉得漏信息,就升到 6,并加 rerank。top_k 不是越高越好,模型处理无关上下文的能力有限,喂太多反而降低准确率。

检索策略方面,如果项目里专有名词多、型号代码多,一定考虑混合检索,否则会出现“语义是近的、字面是错的”这种诡异结果。简单做法是先跑向量检索,再用 BM25 跑一遍关键词检索,最后用 RRF 合并排序,两种互补,效果会明显改善。

4. 进阶:让知识管道配得上 Agent

4.1 从固定流程到 Agentic RAG

基础 RAG 的流程是死的:只要有用户问题,就一定先检索再生成。但在真实 Agent 场景里,这么做会显得很笨。用户问“今天天气怎么样”,你不需要查企业知识库;用户问“上个月销售额Top3的产品是什么”,这不该走文档检索,而应该走数据查询。

Agentic RAG 的思路就是把这个固定流程交给 Agent 自己决策。Agent 根据用户问题判断:需不需要外部知识?该查文档、查数据库,还是查 API?查一次不够,要不要再查第二次?这相当于把 RAG 从一个管道,变成 Agent 手中的一组工具。

我建议你在 Agent 里注册多个知识工具,比如“非结构化文档检索”“产品主数据查询”“工单历史查询”。让模型根据问题内容做路由。这个方案没有听起来那么复杂,本质上就是工具调用加一点判断提示词,但效果比单管道 RAG 灵活得多。这也正是“Agentic RAG”近几年热度这么高的原因。

4.2 结构化数据与知识图谱

RAG 不只是文本检索,企业知识里有很大一部分是结构化数据:产品参数表、ERP 里的库存、价格、订单状态。这些数据如果转成文本再走向量检索,一是精度差,二是更新困难。更合适的做法是用 text-to-SQL,让 Agent 把自然语言问题转成 SQL 查询,从数据库里取准确答案。

还有一类场景适合上知识图谱,也就是 GraphRAG / Ontology RAG。比如产品、供应商、订单之间的多跳关系,“某个供应商供货的某个产品,在上季度被哪些客户大量采购了”,这类问题用平面向量检索很难回答,因为答案藏在关系链里。知识图谱把实体和关系显式建模,配合图检索,多跳问答能力会强很多。代价是构建和维护成本高,所以只建议在关系密集、查询模式相对稳定的领域引入。

4.3 可信度、溯源与知识更新

RAG 带给我们最大的收益,其实是“答案有据可查”。但前提是你把溯源当成一等公民来设计。从索引阶段开始,每个 chunk 都要保留来源元数据,比如文档名、章节、页码、更新时间。检索结果要同源展示,用户才能回查原文。我在 3.2 的示例里要求模型标出资料编号,就是溯源的最小实现。

同时必须有“不知道”策略。很多 RAG 系统回答错,不是模型不行,是它没有机会说“我不知道”。把“资料里没有相关信息就直接承认”写进 prompt,并且用检索分数作为辅助判断,分数低于经验阈值时就主动提示用户资料不全。这个兜底策略比一味追求“答上来”更重要。

知识更新也要纳入管道设计。文档修订后,旧 chunk 要失效,向量库里不能同时存在新旧两个版本。增量更新、定时重建、版本过滤,三者你至少选一个,否则知识库会越用越脏,回答会自相矛盾。

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

5.1 高频问题与排查速查表

症状可能原因排查方向
用户问题明明有答案,却检索不到切块太大把关键信息稀释了把 chunk_size 调小,或直接看召回的 top 10 内容
召回了,但模型答错上下文里噪声太多,关键内容被淹没降低 top_k,加 rerank,强化 prompt 约束
模型答得流畅但纯属瞎编检索结果为空,模型在靠常识作答加“无答案”兜底策略,观察检索分数
答案引用了过时内容向量库里存在旧版本文档引入版本元数据和失效过滤,重建索引
检索慢,接口超时向量库过大或没有做过滤加元数据过滤、量化索引、缩小检索范围

这张表只是起点。实际排查时,我建议你把一次问答拆成三段分别验证:先看检索结果相关不相关,再看 prompt 拼装有没有问题,最后看模型回答有没有遵循约束。分而治之,问题很快就能定位。

5.2 我踩过的几个坑

第一个坑是关于表格的。有一回知识库里放了一张几十行的产品参数表,我图省事把整张表切成一个 chunk,结果用户问某行参数时,模型经常答错行。后来我把表格按行切块,并在每块前面加上表头信息作为前缀,准确率立刻就上来了。表格类内容不要当普通段落处理,这是非常容易忽略的细节。

第二个坑是 top_k 开太大。为了“多给模型一点资料”,我把 top_k 设成 10,结果模型被大段无关内容干扰,反而把答案带偏。后来降到 4,加上混合检索和 rerank,效果好了很多。RAG 不是资料越多越好,精准相关资料两三段就够。

第三个坑是没做“拒绝回答”。一次测试中,用户问一款停产产品的价格,检索库里根本没有这个产品的信息,模型却按照类似产品推断了一个价格,导致用户差点按错误价格下单。从那以后,我在所有知识问答 Agent 里都把“资料没有就明说”放在 prompt 第一位。宁可不答,不要乱答。

第四个坑是知识版本管理。知识库上线三个月后,我发现有些答案引用的还是旧版手册,原因是文档更新时直接增量入库,旧 chunk 没删除。最终我用“文档版本号 + 更新时间”作为元数据过滤,每次检索前先过滤掉非最新版本,问题才彻底解决。如果你是从零搭建,一定要从第一天就给每个 chunk 打上来源和版本标记,别等数据量大了再补。

我做过的几个知识问答 Agent 有一个共同规律:最终决定体验上限的,不是换一个更大的模型,而是知识管道的稳定度。RAG 作为 Agent 知识获取管道的基础形态,技术和生态都已经很成熟,先跑通最小闭环,再逐步加入 Agent 决策、混合检索和知识图谱,这条路径走下来基本不会跑偏。最后再分享一个小技巧:所有文档在进入知识库之前,统一加上“来源 + 更新时间 + 适用范围”三要素,后面做溯源、权限和知识过期清理时,你会回来感谢这个决定。

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

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

立即咨询