☰
AI Agent知识获取管道:RAG从原理到工程落地全解析
2026/11/4 9:54:09 网站建设 项目流程

做AI Agent项目快两年,最让我头疼的从来不是模型推理,而是怎么让Agent“知道”那些模型根本没学过的东西。你问它一条公司内部制度、一个刚发布的政策、一段客户聊天里的结论,它大概率会一本正经地编一个看上去很合理的答案。这个问题的根源不在模型能力,而在知识获取管道——也就是RAG(Retrieval-Augmented Generation,检索增强生成)。这一篇,我们就把Agent知识管道的最底层逻辑、最小实现和调优思路完整说透,当作“走进AI Agent”系列的第四篇。不管你是刚写完第一个Agent Demo,还是想给自己的智能体接业务知识库,这篇都值得花十几分钟慢慢看。

1. 为什么Agent需要一条“知识获取管道”——先忘掉微调

很多同学第一次遇到这个问题时,第一反应都是“模型不懂,那就拿去微调”。这个方向没有错,但放错场景就会变成高射炮打蚊子。在动手搭RAG之前,我们得先理解LLM的“记忆”到底是怎么回事。

1.1 LLM的“记忆”只是一种回忆

预训练语言模型在做推理时,并不是在“查询数据库”,而是在计算一串词在概率分布中最可能的下一个词是什么。你可以把它理解成一个超大型的“知识回忆引擎”——它见过海量文本,所以能根据上下文把关联信息“接”出来。但这个“回忆”有几个天然限制:

  • 训练语料有截止时间,之后发生的事它根本不知道。
  • 私有知识、公司内部数据、个人对话记录,它从未见过,也就谈不上回忆。
  • 即使它“看过”某些数据,回忆的也是压缩后的概率化表达,而不是原文,所以细节容易变形。

举个例子:你问通用大模型“我们公司上个月发布的报销制度里,餐饮发票的上限是多少”,它只能编。因为这份制度是你的私有知识,不在它的“回忆”里。想让Agent稳定地处理这类问题,必须让它有能力去外部环境中“查资料”,而不仅仅是靠脑子回忆。

1.2 微调不是万能药

微调本质上是在改变模型内部的参数分布,让它在特定任务或特定风格上表现更好。但它有几个致命缺点:

  1. 知识更新成本太高。每次制度一改、产品参数一变,你都要重新训练一轮;即使做LoRA这种参数高效微调,也要重新构造数据集、训练和验证,整套流程快则几小时,慢则几天。
  2. 模型会“越训越窄”。微调时你灌进去的语料会在一定程度上改变模型的泛化能力,尤其是用小数据量反复训练时,容易过拟合到那批语料上,新问题反而不会了。
  3. 无法追溯答案来源。微调后模型输出的答案,仍然说不清依据是哪条文档。这对金融、医疗、企业内搜等需要合规审计的场景来说,几乎没法用。

类比一下就通了:微调像是给员工做一场封闭式培训,让他把公司政策背下来;RAG则是给他配一本随时能翻到最新版的手册。培训能让一个人在特定领域更专业,但没法让他每秒钟都记住两周前刚修订的第57条细则。对大多数Agent知识获取需求来说,手册比背课文更靠谱。

1.3 RAG不是插件,是Agent的主粮道

RAG的思路很直接:先让Agent根据问题去外部知识库检索相关片段,再把片段拼进提示词里,让模型基于这些片段作答。这样一来,Agent的回答就有了两个显著变化:

  • 答案基于实时检索到的原文,而不是纯靠模型回忆,幻觉概率大幅下降。
  • 可以给出引用来源,你甚至能定位到具体文档和段落,方便人工复核。

在Agent体系里,RAG不是锦上添花的“知识小插件”,而是整个知识获取管道的主干。Agent需要判断自己缺什么信息、去哪里查、查到之后怎么用——这跟一个人类实习生拿到新任务时的表现一模一样。所以我在做Agent编排时,第一件事不是画复杂的规划流程图,而是先把知识管道搭稳。管道不稳,Agent规划得再漂亮,回答也是空中楼阁。

2. 拆开RAG的四个核心环节——每个环节都在决定答案质量

RAG听起来只有“检索”和“生成”两步,实际跑起来你会发现,四个环节环环相扣:文档切分、向量化、检索召回、生成合成。任何一个偷懒,最后答案都会变形。

2.1 文档切分:这是最容易被低估的“效果上限”

我见过很多团队的RAG效果不好,最后排查下来,文档切分的问题占了七八成。切分的目的,是把长文档切成一簇一簇适合检索和喂给模型的文本块(chunk),既要保留语义完整性,又不能太长导致信息噪声太大。

常见的切分方式有以下几种:

切分方式做法适用场景坑点
固定长度切分按字符数切成300-500字的块,每两个块之间加50-100字重叠脱敏文本、日志、通用文章容易把一个完整段落或表格拦腰截断
递归字符切分先按段落、再按句子、最后按字符逐级切,尽量保住语义边界Markdown文章、结构化文本对PDF、扫描件效果不稳定
结构感知切分按Markdown标题、代码块、表格结构切分技术文档、Wiki、帮助中心需要额外解析器,成本高
语义切分用嵌入模型计算句子相似度,在语义断层处断开对话记录、FAQ、交错文本计算量大,小项目不太划算

这里我给一个比较稳的经验值:中文业务文档,chunk_size控制在300~500字之间,overlap设置在50~100字之间。太短了,上下文被截断,检索到的片段不足以支撑回答;太长了,一个块里塞了四五个主题,向量表示会变得平均化,检索精度反而下降。overlap的作用是“搭桥”,防止一个关键信息刚好落在两个块的夹缝里。如果你用的是Markdown源文件,优先按标题和段落切,保留结构信息。

另外,PDF里的表格是重灾区。直接用文本提取器切出来的表格,顺序往往是乱的,检索到也是残废。建议在文档进入管道之前,先把复杂表格转成Markdown表格或结构化JSON,再参与切分。

2.2 向量化:把文字变成可比较的数字

切分之后,每个文本块要转换成向量,才能在向量空间里做相似度比较。选Embedding模型时,我比较看重三点:语义理解能力、向量维度、对中文的支持。

  • 通用型Embedding(如OpenAI的text-embedding-3-small)适合多语言场景,接口稳定,但如果你完全在本地或国内模型环境跑,需要自建服务。
  • 中文专用Embedding(如BAAI的bge系列、m3e)在中文业务文本里通常表现更好,体积可控,可通过Sentence-Transformers或FlagEmbedding调用。
  • 向量维度直接影响存储和检索性能:高维模型(1024维以上)表达更细腻,但占用资源明显;小模型(256/384维)跑得快,表达粗一些。可以先拿验证集对比一轮再定。

我经常遇到一个误区:Embedding模型和问答LLM必须是同一个模型。其实不是。Embedding只负责“语义向量化”,LLM负责“生成答案”,两者可以完全独立。选Embedding的原则是让相似文本离得更近,选LLM的原则是让指令理解更准。很多团队把ChatGPT的向量接口和问答接口混用,效果不差,但成本偏高;换成开源的bge-m3做检索,本地部署后推理成本几乎为零,答案质量也没有掉链子。

2.3 检索召回:Top-K越拉越多,不等于越准

向量检索的核心是计算查询向量和文档块向量的余弦相似度或内积,取排名最高的Top-K个块。但向量检索有个天然盲区:对关键词和精确术语不敏感。比如你搜“ERP系统的库存周转率怎么算”,如果文档里写的是“库存周转次数”,向量可能得分很高但搜不中;再比如产品编号“PRD-3021”,向量化之后很容易被语义噪声淹没。

所以生产级RAG很少只用纯向量检索,而是用混合检索:

  • 向量检索,负责语义相似度召回。
  • BM25关键词检索,负责精确词、编号、术语召回。
  • RRF(Reciprocal Rank Fusion)把两路结果做加权排名融合,让最终结果更稳。

Top-K的选择也很讲究。K值太小,可能漏掉关键段落;K值太大,不相关的内容混进来,LLM反而容易被带偏。我的习惯是先取K=5,观察答案质量,然后从3到10逐档测一轮,选一个“答案信息密度”最高的档位。所谓信息密度,就是检索回来的段落里有多少内容真的被答案用上了。

2.4 生成合成:让模型带着引文答题

检索到候选块之后,最后一步是把它们拼进Prompt,让LLM生成答案。这个环节最考验工程细节,也最容易被粗暴对待。一个合格的RAG生成Prompt,至少包含这几部分:

  1. 指令约束:告诉模型“只根据提供的资料回答,资料中没有的信息要明确说不知道”。
  2. 检索片段:按来源编号陈列,比如“[1]《报销制度》第3章... [2]《报销制度》第5章...”。
  3. 用户问题:原样放到末尾。
  4. 输出格式要求:如果希望答案带引用,可以要求模型在每一句后面标注来源编号,比如“……上限为500元[1]”。

我强烈建议在生成阶段开启“引用级回答”,而不是让模型凭感觉长篇大论。做法也很简单:要求模型在输出时把答案拆成短句,每一句都标明依据的是哪个文本块。这样一方面方便你追溯,另一方面模型在组织语言时会不自觉地更谨慎,因为它知道每个结论都有可能被核验。

3. 用代码搭一条最小可用的RAG管道——从0到1手写一遍

很多教程一上来就讲LangChain、LlamaIndex,但我更建议第一次接触RAG的同学,先自己把流程“裸写”一遍,哪怕只用几百行代码。框架再方便,也只是把切分、向量化、检索、生成串起来的胶水;等你亲手拼一次,才知道每一环节在干什么,后面出了问题才不会一头雾水。

3.1 技术选型:框架还是裸写

我看过不少项目,一开始就接入LangChain,结果发现版本升级后API改了一大堆,调个向量库都要翻源码。如果你只做单租户的小型Agent,完全可以手工实现:

  • 文档加载:用PdfPlumber、BeautifulSoup、pypdf这些轻量库。
  • 切分:自己写一个递归字符切分函数,带上overlap,几十行就搞定。
  • 向量化:用Sentence-Transformers加载本地Embedding模型,输出numpy数组。
  • 向量存储:FAISS是入门首选,内存型索引,安装快,小数据量完全够用;后续数据量大再换Milvus或Qdrant。
  • LLM生成:通过Ollama或vLLM调用本地模型,或直接调用云API。

这样设计的最大好处是“裸流程可视化”,Everything is under your control。等需求复杂到需要多数据源、权限隔离、在线更新,再引入框架也不迟。

3.2 最小可运行示例代码

下面这段是我调通最小RAG管道的参考代码,用的是FAISS + Sentence-Transformers + Ollama本地模型。你可以在任意普通笔记本上把它跑通(演示用示例,注释已经写得比较清楚):

import os from sentence_transformers import SentenceTransformer import faiss import numpy as np # 1. 准备知识文本(实际场景中从PDF/数据库/接口读取) docs = [ "公司报销制度:餐饮发票单次上限500元,需附小票。", "报销流程:员工提交申请,直属主管审批,财务在三个工作日内打款。", "项目A的供应商是B公司,B公司总部位于上海。", "员工入职需在三日内完成指纹录入,并领取工牌。", ] # 2. 文档切分(最小示例不做复杂切分,直接用句子列表) chunks = [d.strip() for d in docs if d.strip()] # 3. 加载Embedding模型,对文本块向量化 model = SentenceTransformer("BAAI/bge-m3") chunk_vectors = model.encode(chunks, normalize_embeddings=True) # 4. 建FAISS索引,存向量 dimension = chunk_vectors.shape[1] index = faiss.IndexFlatIP(dimension) # 内积索引,配合归一化向量即余弦相似度 index.add(np.array(chunk_vectors)) # 5. 检索 def retrieve(query, k=2): query_vector = model.encode([query], normalize_embeddings=True) scores, indices = index.search(np.array(query_vector), k) return [(chunks[i], float(scores[0][j])) for j, i in enumerate(indices[0])] # 6. 生成答案(通过Ollama调用本地LLM,需先启动ollama serve) import requests def generate_answer(query, retrieved_chunks): context = "\n".join([f"[{idx+1}] {chunk}" for idx, chunk in enumerate(retrieved_chunks)]) prompt = f"""请根据以下资料回答问题。如果资料中没有相关信息,请明确回答“资料中未找到”。 资料: {context} 问题:{query} 请用中文回答,并标注答案依据的资料编号。""" response = requests.post( "http://localhost:11434/api/generate", json={"model": "qwen2.5:7b", "prompt": prompt, "stream": False} ) return response.json()["response"] # 7. 跑一遍看看 query = "项目A的供应商B公司总部在哪里?" retrieved = retrieve(query) print("检索结果:", retrieved) answer = generate_answer(query, [c for c, _ in retrieved]) print("答案:", answer)

这套代码的核心逻辑很简单:文档切块 → 向量化 → FAISS检索 → Prompt拼装 → LLM生成。真实项目里,你只需要把第1步的docs换成真正的知识加载器,把第6步的模型换成线上环境使用的模型即可。

3.3 跑通之后的三个观察

第一次跑通这个管道,我总结了三个在文档里很少被写出来的观察,供你参考:

  1. 答案质量与检索结果几乎完全挂钩。检索回来的段落里有正确信息,LLM就会答对;段落里没有,LLM就会硬编。所以你调试RAG时,第一位任务是“看检索结果”,而不是反复改Prompt。
  2. 相似度得分会在0.75~0.85之间徘徊,千万别嫌低。不同Embedding模型的得分尺度完全不同;bge-m3的0.75很可能已经是很强的关系。不要用绝对分值的直觉去判断,要放在同一模型下做相对比较。
  3. 本地小模型(7B/8B级别)对长上下文很敏感。如果你把10个检索块全塞进去,模型容易抓不住重点,甚至复读最后一个块。所以给本地小模型做RAG时,K值建议缩到3~5,并且把Prompt写得极简。

4. 从“能跑”到“好用”——Agent场景下的RAG质量调优

最小管道跑通只是起点。真正把RAG放进Agent里,你会发现原来有一堆问题:命中率到底怎么评估?Agent该不该自己决定查几次?知识库更新了老向量怎么办?这一节我聊几个自己踩出来的心得。

4.1 先分清问题出在检索还是生成

遇到RAG答错,第一件事不是改Prompt,而是做一个拆分测试:把同一个问题,分别输入“纯LLM”和“RAG管道”,同时打印检索到的文本块。对比之后基本能定位病根:

  • 纯LLM答错,RAG也答错,且检索块里没有相关信息→ 知识库缺内容,或者切分/检索方式不对。
  • RAG检索块里有正确答案,但最终生成的答案还是错的→ 生成环节有问题,可能是Prompt指令不明确,也可能是上下文被无关片段干扰。
  • 检索块里有部分相关,但被另一段不相关信息淹没了→ Top-K太大,或排序权重不对,需要调整混合检索参数和K值。

我建议在每个RAG接口的调试模式里,把返回的chunk内容、相似度得分、最终答案一起打出来。Agent在生产环境里可以关掉,但开发阶段一定要留这个出口,否则排查一次要命很久。

4.2 用Hit Rate和引用级答案做体检

RAG效果不能靠“感觉回答变准了”来判断。我比较常用的三个指标:

指标含义怎么用
Hit Rate评判集里,多少比例的查询把“正确答案所在文档”召回进了Top-K低于0.6说明检索路有问题
MRR正确答案在检索结果中的平均排名倒数,越高越好排到第5和第1的体验差异很大
Faithfulness模型生成的答案有多少信息来自检索片段,而非自己胡编用人工抽检或LLM评判两相对照

建立一个小型评测集并不难:挑30~50条真实问题,每条问题标注“答案来源文档”。每次改切分参数、Embedding模型、检索Top-K,就跑一遍评测集,对比Hit Rate和答案引用命中率。我踩过最大的坑是“调优时只看两个Demo答案变好了”,结果上线后一测整体Hit Rate只有0.4,才发现Demo答案是被调参者“手工选样”选出来的。评测集一定要放在看不到的杂质之上,用真实用户问题,而不是自己挑出来的顺口问题。

4.3 让Agent自己决定查什么:Agentic RAG

基础RAG是线性的:用户问一次,系统检索一次,生成一次。但Agent场景下,问题往往需要多跳推理。比如“项目A的供应商B公司的总部在哪?”基础RAG可能会先检索到“项目A供应商是B公司”,但“B公司总部在哪”存在于另一篇文档里,一次Top-K检索可能只召回其中一半信息。

解决办法是把RAG嵌进Agent的推理循环,也就是Agentic RAG。具体来说,Agent在执行任务时,会自己拆解问题,决定下一步是检索、是读全文、还是基于已有内容继续推理。最常见的方法有两种:

  • 计划-检索-再计划:Agent先列出信息需求清单,然后为每个需求执行一次检索,最后汇总答案。
  • 带工具调用的ReAct风格:Agent把“知识库检索”当成一个工具,边推理边决定“我现在需要查询供应商信息”,然后把查询词写成参数调一次。

我建议先从第一种开始,因为它不需要很复杂的工具调用协议,只需要在Agent编排里加一个“拆解问题 → 子查询多路召回 → 汇聚”的流程,Pipeline就比单次检索稳很多。等Agent对工具调用足够熟悉,再上完整的Agentic RAG。

4.4 生产环境不可忽略的三件事

调优之后,还有三个生产环境的问题,处理不好会让整套管道归零:

  1. 缓存。Agent可能对同类型问题重复检索,开一个基于查询串哈希的结果缓存,能省不少向量检索开销,也让响应更稳定。
  2. 知识过期。昨天的制度今天改了,旧的文本块还躺在向量库里。建议每次知识库更新时,对变更文档做增量重切和向量覆盖,同时清理旧版本,否则会检索到过期信息。
  3. 权限隔离。不同角色的Agent能访问的知识范围不同。不要让Agent通过同一个知识库接口能查到所有文档,哪怕只是内部系统,权限过滤也必须放在检索前面,而不是在生成阶段靠Prompt去限制。

5. 下一步:从RAG到GraphRAG与Ontology RAG

看到这里,基础RAG的流程和调优方法你已经掌握了。但最后我还想聊一个趋势:为什么越来越多人开始把RAG和知识图谱(GraphRAG)或本体(Ontology RAG)结合起来。

5.1 基础RAG的多跳短板

基础RAG擅长“从某一段文字里找答案”,但遇到需要跨文档推理的问题,它就会显得吃力。比如:“A产品线新增了几位工程师?他们分别参与过哪些项目?”这个问题可能散落在多份周报、排期文档、简历库中,单次向量检索很难同时覆盖所有实体关系。如果硬做,只能把Top-K调到很大,然后让LLM在一堆材料里“大海捞针”,效果很不稳定。

5.2 知识图谱来兜底

GraphRAG的思路是:在离线阶段,让LLM从文档中抽取实体(如人、产品、供应商)和关系(如“参与”“位于”“属于”),组织成一个知识图谱。检索答案时,可以先在图谱里定位相关实体,再沿着关系去拉取上下文,这样跨文档的多跳问题就有了解法。Ontology RAG则更进一步,预先定义好领域概念和层级关系(比如“报销单属于财务流程”),再让模型按这个本体去抽取和检索,降低随机性。

这个方向的副作用也很明显:建图、存图、查询图的工程复杂度比向量检索高一个量级。我的建议是,如果你的Agent大部分问题都是一跳检索能答的,老老实实把基础RAG做扎实;只有当你发现大量问题需要跨文档推理时,再考虑给管道加一张“知识图谱辅助网”。

5.3 给正在做Agent的同学的落地建议

最后分享几条过来人的建议,都是线上环境磨出来的体会。

第一,做Agent知识获取管道,先把评测集建起来再谈调优。没有评测集,你连“改好了还是改坏了”都不知道。

第二,RAG不是一次性组件,而是需要持续运营的管道。文档会变、问题分布会变、向量模型也会升级,要定期重新切分文档、重新评测效果。

第三,不要一上来就追求全自动Agentic RAG。先把基础管道跑稳,再让Agent自己决定查什么;否则你会在调度、引用、权限这些环节里手忙脚乱。

我自己在给Agent接企业知识库时,最常复用的一个习惯是:每一次上线前,都把真实用户问题里的20条拿出来,一条一条重建检索日志,检查召回块长什么样、最终答案引用了哪个来源。这个动作看似耗时,却能最快发现管道里的隐藏问题。知识获取是一场持久战,先把基础打好,后面才不会翻车。

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

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

立即咨询