1. 为什么要用RAG给AI Agent建知识库
1.1 先搞清楚RAG到底解决什么问题
我先说个很扎心的场景。很多人把GPT、Claude这类大模型接进自己的业务系统,跑了一周发现一个问题:问它公司内部的报销制度,它一本正经地编了一套流程,跟实际完全对不上。原因不复杂——大模型的知识截止于训练数据,它根本没见过你公司的制度文档。你总不能为了一个内部制度去微调一次大模型,成本高不说,制度一改你又要重训。
RAG(Retrieval-Augmented Generation,检索增强生成)解决的就是这个“模型不知道但你又希望它知道”的问题。核心思路一句话:不让模型凭空回答,而是先从一个外部知识库里检索出相关片段,把片段拼进提示词里,再让模型基于这些材料生成答案。模型还是那个模型,但它的回答有了“参考书”,而不是闭卷硬写。
我见过太多人把RAG当成一个“向量数据库+提示词拼接”的简单活儿,实际落地的时候才发现坑特别多。切分大小怎么定、向量模型选哪个、召回多少条、重排序要不要上、Agent在中间怎么编排——每一个环节都直接影响用户最终拿到的答案质量。这也是为什么现在AI Agent和RAG经常被放在一起讨论:Agent负责“想”,RAG负责“找”,两者配合才能真正做出一个能用的知识库系统。
1.2 Agent+RAG的组合价值在哪里
先打个比方。基础RAG像是一个只会查资料的实习生:你问它问题,它去资料库翻几段相关内容贴给你,翻到什么算什么,不会追问你、也不会调整策略。AI Agent则像一个有经验的助理:它会先理解你的问题,觉得信息不够就去换一种问法再查,查到多个来源后自己综合判断,甚至还能反问“您说的是A还是B”。把Agent和RAG组合起来,相当于给这个助理配了一个专业的档案室。
具体到知识库场景,Agent+RAG能解决三类问题。第一类是复杂问题的拆解,比如“对比我们这个项目和竞品在安全策略上的差异”,不能只做一次检索,而是要先拆成“本项目安全策略”“竞品安全策略”“对比维度”多个子任务,分别检索后再汇总。第二类是信息缺失时的应对,基础RAG遇到检索结果为空就瞎编,Agent可以自动改写查询词、换一种表达方式重新检索。第三类是跨文档的关联推理,比如“根据这三份合同,找出所有付款时间晚于交付时间的地方”,需要同时定位多个文档片段再做推理。
从工程角度看,把Agent和RAG结合还有个好处:可观测性变强了。Agent的思考链路、每一步的工具调用、检索命中的文档片段都可以被记录和追踪。这一点在企业知识库场景里特别重要,因为业务方往往不会只看答案,还会问一句“这个结论是从哪份文档里来的”。
2. 整体架构设计:一个可落地的知识库方案
2.1 核心组件与数据流向
在动手写代码之前,我建议先画一条完整的数据流水线。一个典型的知识库系统包含五个环节:采集与清洗、切分、向量化、存储与检索、生成与回答。
采集与清洗很多人会忽略,但这是决定上限的一步。你从内部系统导出的文档可能是Word、PDF、Markdown、HTML混合体,有的带页眉页脚,有的是扫描件,有的里面嵌了图片表格。这一步的核心目标是去噪:删掉重复的导航栏文本、水印、无意义的空行,统一编码格式,把PDF里的表格结构化。清洗不干净,后面切分出来的片段就会夹带大量垃圾内容,检索时这些垃圾还可能因为词频高而被误召回。
切分是RAG里最容易被低估的环节。切太大,片段之间主题混杂,检索命中后塞给模型的上下文太长,既浪费token又容易让模型抓不住重点;切太小,语义被切碎,检索出来的片段缺乏上下文,模型看了也不知道在说啥。我常用的基线方案是:普通说明文档按300到500个字符切分,重叠50个字符左右;代码类文档按函数或类切分;表格类内容单独处理,尽量不要把整张表硬塞进一个切分块。
切分完之后进入向量化。现在主流的做法是用Embedding模型把文本转成向量,模型选型直接影响检索效果。中文场景下,可以优先考虑bge-large-zh、m3e这些针对中文优化的模型,也可以直接用OpenAI的text-embedding-3-small,具体看你对数据出境的合规要求。向量化后的数据写入向量数据库,目前常用的有Milvus、Qdrant、Chroma、pgvector,选型要综合看数据量、查询并发、部署运维成本。
存储与检索环节还包含一个容易被忽略的组件——重排序(Rerank)。向量检索找回来的top-k个片段只有初步的相关性打分,质量参差不齐。接一个Rerank模型对候选片段做精细化排序,往往能让最终答案质量上一个台阶。我在实际项目里的经验是:加了Rerank之后,用户反馈“答非所问”的情况至少减少一半。
生成与回答就是最后一步。把检索到的相关片段拼接成提示词,连同用户问题一起送给大模型。这里要注意控制上下文的长度,以及明确告诉模型“如果材料中没有相关信息,直接说不知道,不要编造”。这句话对于知识库型应用至关重要。
2.2 主流框架选型:Dify、LlamaIndex、LangChain怎么选
框架选型是很多新手第一个纠结的问题。我直接给结论:如果目标是一个开箱即用、带界面的知识库应用,优先看Dify;如果目标是深度定制检索和生成逻辑,用LlamaIndex;如果目标是让Agent和各种工具、API深度协作,LangChain更合适。
Dify最大的价值在于低门槛和完整闭环。它把知识库管理、文档切分、向量化、检索配置、Agent编排、模型接入这些环节都做成了可视化配置。我甚至见过非技术背景的产品经理用它搭出了一套客服知识库demo。但低门槛也意味着定制能力受限,如果你要做很特殊的切分逻辑或者复杂的多路召回,Dify的配置项可能不够用。另外,社区里经常看到“Dify升级后无法保存知识库”或者“修改知识库时报Internal Server Error”这类问题,实际多半是版本升级后数据库迁移没跑干净,或者向量数据库连接配置失效,遇到这种问题先去查服务日志,比反复点击界面有用得多。
LlamaIndex更像是一个RAG开发框架,它对索引结构、检索器、节点解析器的抽象做得非常细致。如果你想把一套复杂的文档解析流程嵌入到自己的代码里,LlamaIndex提供的能力比LangChain更聚焦。它有个很实用的概念叫“Node Parser”,可以针对不同文档类型配置不同的切分器,这在处理混合内容的知识库时优势明显。
LangChain则是一个更通用的Agent和工具编排框架。它擅长做的事情是把大模型、工具调用、记忆、检索模块组装成一条可执行的链。但这也意味着你需要自己组合的组件更多,踩坑的几率也更高。我的建议是:如果是新项目,优先从Dify或者LlamaIndex起步,跑通之后再考虑是否需要引入LangChain做更自由的编排。不要一上来就全上,工具链越复杂,排查问题越痛苦。
3. 从零搭建:知识库构建的完整实操
3.1 文档加载与清洗实操
我拿一个实际项目举例:给一家制造业客户搭建设备运维知识库,原始资料包括设备手册PDF、维修工单Excel、工程师的Markdown笔记、还有扫码出来的存档图纸(带大量噪点)。第一步不是急着写代码,而是先做盘点,明确有多少文档、什么格式、质量如何。
PDF文档的处理最容易踩坑。用PyPDF直接抽文本,遇到扫描件什么都抽不出来;用OCR工具,又容易出现错字。我的处理方式是分层处理:原生文本型PDF,用pdfplumber直接提取文本,同时尝试保留段落结构;扫描型PDF,先通过ocrmypdf做OCR预处理,生成带文本层的PDF再提取,这样既提升了识别质量,后续还能保留版面信息。表格类PDF不要强行提取成纯文本,优先考虑转成HTML或CSV结构,否则表格语义在切分阶段会完全丢失。
清洗阶段我通常会写一个管道脚本,包含这几步:去除页眉页脚、统一换行符、删除连续空行、过滤掉纯图片链接、把全角符号转半角。这些看起来琐碎,但如果你图省事跳过,等检索阶段发现召回结果里有大量“第X页共X页”的噪声时再回头处理,返工成本反而更高。
3.2 切分策略与参数选择
切分是知识库工程质量的分水岭。很多人直接用框架的默认参数,结果就是检索效果不稳定。我建议花时间针对自己的文档类型做实验。
先看一个基础示例,用LlamaIndex做自定义切分:
from llama_index.core.node_parser import TokenTextSplitter splitter = TokenTextSplitter( chunk_size=512, chunk_overlap=64, separator="\n\n", ) nodes = splitter.get_nodes_from_documents(documents)chunk_size和chunk_overlap的选择有讲究。行业里有个模糊的共识:英文场景200到500个token比较稳妥,中文场景如果按字符算,300到600个字符比较合适。但这不是拍脑袋定的,我一般会按内容类型区分:操作手册类文档,按“步骤块”切,把每个步骤作为一个独立片段;问答类文档,按照“问题+答案”整体切,避免问题和答案被拆到两个片段里;技术规范类文档,优先按章节标题切,保留层级结构。
切分重叠值决定了上下文连贯性。重叠太小,跨片段的信息容易断裂;重叠太大,又会造成大量冗余内容,浪费存储和检索资源。我的经验值是从chunk_size的10%到15%开始尝试,实测后用标注数据评估,再用梯度搜索的方式微调。
还有一个细节值得注意:不要把Markdown标题、表格标记直接当普通文本切掉。更好的做法是在切分前把这些结构化标记转换成提示词里的引导词。比如把### 设备校准流程这一级标题保留,检索到的片段里就有标题信息,模型看到后更容易理解内容在讲什么。
3.3 向量化与索引构建流程
向量化这一步要同时考虑效果和成本。Embedding模型的选择我前面提过,中文场景建议先试bge-large-zh,它在C-MTEB的中文检索榜单上长期靠前,而且支持中文长文本场景,很多项目实测效果不错。如果你的文档以英文为主,OpenAI的text-embedding-3-small性价比很高,但要注意API调用的QPS限制和数据隐私。
向量化完成后要建立索引。这一步很多人不理解为什么需要“索引”,简单说,向量数据库不能每次都暴力全量比对,那就太慢了。索引结构的目的和书的目录一样,先圈定一个候选范围,再在范围内精确搜索。目前最常用的是HNSW(Hierarchical Navigable Small World)索引,它通过多层的图结构快速找到相似向量的近似近邻。
以Milvus为例,一个基础集合的创建大概是这样的:
from pymilvus import connections, CollectionSchema, FieldSchema, DataType, Collection connections.connect(host="localhost", port="19530") fields = [ FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True), FieldSchema(name="document_id", dtype=DataType.VARCHAR, max_length=64), FieldSchema(name="content", dtype=DataType.VARCHAR, max_length=2000), FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=1024), ] schema = CollectionSchema(fields=fields) collection = Collection(name="knowledge_base", schema=schema) index_params = { "index_type": "HNSW", "metric_type": "IP", "params": {"M": 16, "efConstruction": 200}, } collection.create_index(field_name="embedding", index_params=index_params)这里有几个参数要解释:metric_type用IP还是COSINE取决于你用的Embedding模型,很多向量模型训练时用的是余弦相似度,但内积在有归一化向量的前提下结果等价,所以很多Milvus最佳实践里直接设成IP;M控制每个节点的邻居数量,M值越大召回越准但内存占用越高;efConstruction是构建索引时的动态候选集大小,值越大索引质量越好但构建越慢。生产环境对这两者的调法通常是先按默认值跑通,再根据数据量逐步调大。
索引建立之后,不要着急接入Agent,先单独跑一轮检索测试。写一个小的检索脚本,输入几个典型的业务问题,看看召回的片段是否相关。这一步能帮你尽早发现问题,比如数据集方向不对、Embedding模型不合适、切分粒度不合理,而不是等到整个系统联调的时候才排查。
4. Agent增强检索:从基础RAG到Agentic RAG
4.1 简单RAG的痛点
基础RAG的流程很简单:问题进来,向量检索top-k,拼接提示词,大模型生成。但只要你实际用得足够多,就会发现几个明显的痛点。
第一个痛点是“查不准”。用户的问题往往不是规范的关键词,而是口语化表达,比如“我们上次那个设备报警是怎么回事”,直接拿整句话去向量检索,匹配效果可能很差。因为向量检索的输入是整段自然语言,而不是提炼过的关键词,口语问题往往让你得不到好的检索结果。
第二个痛点是“查不到”。有些问题需要多次检索才能回答。比如“根据过去半年的工单数据,分析最常见的故障原因”,如果只检索一次,你能拿到的只是一个局部片段,根本不足以支撑综合分析。基础RAG没有多步推理和多次检索的机制。
第三个痛点是“不验证”。检索回来的片段可能相互矛盾,也可能和问题并不直接相关,但基础RAG会不加判断地把这些片段全部塞给模型,最终输出的答案质量就完全看运气。
Agentic RAG的思路就是针对这些问题:让一个AI Agent来编排整个检索过程。它不是机械地执行“一次检索生成答案”,而是根据问题的复杂度自主决定:是否需要改写查询词、是否需要检索多次、是否需要用工具调用外部API补充信息、是否需要综合多段内容后再作答。
4.2 用Agent编排检索流程
我实际落地过的一个方式是定义三个核心工具:search_knowledge_base(向量检索)、search_web(外部搜索)、read_document(读取指定文档内容)。Agent收到问题后,先做一个意图判断:问题是否涉及内部知识,如果涉及,检索优先级是内部知识库优先;如果问题超出知识库范围,则尝试外部搜索补充。
下面是一个用LangChain实现Agent工具调用的简化示例:
from langchain.agents import Tool, AgentExecutor, create_openai_tools_agent from langchain_openai import ChatOpenAI from langchain.prompts import ChatPromptTemplate tools = [ Tool( name="knowledge_base_search", func=kb_search, description="在内部知识库中检索与问题相关的文档片段,适用于公司制度、产品资料、技术文档等内部信息。" ), Tool( name="web_search", func=web_search, description="在互联网上搜索公开信息,适用于知识库中找不到答案的问题。" ), ] llm = ChatOpenAI(model="gpt-4o", temperature=0.1) agent = create_openai_tools_agent(llm, tools, prompt) executor = AgentExecutor(agent=agent, tools=tools, verbose=True)这里有个容易出错误的地方:工具描述一定要写清楚“这个工具适合解决什么问题”。因为Agent本身不具备空间知识,它完全靠工具描述来判断什么时候调用哪个工具。描述写得太模糊,Agent就会乱选工具,比如本该查内网知识库的问题,它偏偏去查了外网,结果自然不理想。
编排流程中还有一个核心机制:迭代式检索。当Agent发现第一次检索的结果不足以回答问题时,它可以对查询词做改写,然后再次检索。比如“这个设备报警频率太高的原因是什么”,可能先检索“设备报警频率高”找不到精确内容,改写为“设备故障报警 排查”后就能检索到运维手册里的相关章节。这模拟的就是人查资料时调整搜索词的行为。
4.3 查询改写与多路召回
查询改写是提升召回质量最直接的手段。我在生产系统里实现过两种改写策略:一种是用大模型改写,把口语问题转成更规范的检索词;另一种是规则改写,提取问题中的实体和关键词,组合成多个候选查询词。两者的取舍在于成本和延迟:大模型改写效果好但引入额外一次LLM调用,规则改写快但覆盖有限。
多路召回的设计也很重要。向量检索是主力,但不能是唯一通道。我会同时跑关键词检索(BM25)和向量检索,再把两路结果合并后统一做重排序。原因很简单:向量检索擅长语义相似,但对于精确的产品型号、人名、编号这类信息,关键字匹配往往更准。合并之后用Rerank模型做交叉编码打分,取top-k进上下文。
我给一个小项目画过数据流,简化后大概是这样的:
- 用户提问
- Agent意图识别
- 查询解析(实体抽取、改写候选生成)
- 多路检索:向量检索 + BM25关键词检索
- 合并去重
- Rerank精排
- 上下文构建(附带来源标注)
- 大模型生成,并给出引用出处
多路召回的一个注意事项是控制的候选数量。向量检索取top50、BM25取top50,合并后可能有一堆重叠内容,这时候如果直接全部塞给Rerank,耗时和成本都会增加。我会先做一层快速去重,比如按内容hash去掉完全重复的片段,再按粗排得分取前30条进入Rerank,最后精排取前5条进入大模型。这个流程每个环节都有明确的目的,而不是简单堆砌组件。
5. 评测与优化:知识库效果到底怎么样
5.1 RAG测评怎么做
聊RAG落地,最容易被问到的就是“效果到底行不行”,但多数人回答不上来,因为压根没设计过一套评测方案。RAG的测评不是只测最终回答对不对,而是要分阶段拆开测:检索环节测召回质量,生成环节测回答质量,整体流程测端到端效果。
检索质量的经典指标是召回率(Recall@k)、命中率(Hit Rate)和平均倒数排名(MRR)。我实际常用的方式是这样的:构造一组“问题—标准答案片段”的测试集,每条数据包含一个真实用户问题,以及对应的标准文档片段和期望答案。跑一轮检索后统计,标准片段是否出现在top-5结果中,如果出现,位置越靠前越好。Hit Rate就是看命中比例,MRR则能进一步反映排名精度。
生成质量的评估比较主观。现在业界有两种做法:一种是人工打分,找领域专家对答案做1到5分的评分,评估维度包括答案准确性、忠实度、完整度和可解释性。另一种是用大模型做自动评测,把参考答案和生成结果交给裁判模型打分。我建议前期项目至少保留人工评测,自动评测分数只做参考,因为裁判模型在某些领域不一定足够可靠。
RAG测评还需要关注一个容易被忽视的点:空值率和幻觉率。空值率是检索不到相关内容导致模型无法回答的情况,幻觉率则是模型在材料不足时强行编造答案的情况。这两个指标往往此消彼长——召回的内容少、模型胆子大,幻觉率就高;召回的内容多、提示词限制严,空值率就可能上升。你需要在产品策略上明确倾向:面向客服场景,宁可回复“我暂时无法回答”也不要编答案;面向辅助创作场景,可以适当放宽生成自由度。
下面是我在实际项目中常用的一份评估维度表:
| 评估维度 | 说明 | 常用评估方式 |
|---|---|---|
| Hit Rate | 标准片段是否被召回 | 程序自动统计 |
| MRR | 标准片段的排名位置 | 程序自动统计 |
| 答案忠实度 | 答案是否严格基于检索材料 | LLM评测+人工抽检 |
| 答案完整性 | 是否覆盖问题的所有关键点 | 人工评分 |
| 事实一致性 | 答案是否与知识库冲突 | LLM评测 |
| 可追溯性 | 是否给出引用来源且来源正确 | 人工抽检 |
5.2 常见问题与排查技巧实录
我整理几个在RAG项目里反复出现的典型问题,每个都是踩过坑之后总结出来的。
第一个是“检索结果相关,但答案质量仍然差”。这个问题出现频率最高。排查顺序我建议先看提示词:你是不是明确告诉模型“只基于提供的材料回答”?如果没有,模型很可能混入了自己的知识,导致答案和材料内容不一致。然后再看上下文拼接顺序:相关片段是不是被埋在一堆无关文本后面,模型根本注意不到。解决办法是把最重要的片段放在离问题最近的位置。
第二个是“Dify升级后无法保存知识库”或者“修改知识库报Internal Server Error”。这类问题在社区里讨论很多,我实际遇到过,绝大多数原因是升级过程中数据库表结构迁移没有完整执行,或者向量数据库中的集合状态与元数据库不一致。排查方法很简单:查看服务日志,定位到具体的异常栈,如果是数据库迁移的问题,用docker-compose down加上数据备份后重建容器,再触发一次完整迁移基本能解。如果继续报错,检查是否多个Dify实例同时连接了同一个数据库,比如需要enable_multi_agent相关的配置是否有冲突。
第三个是“本地跑得好好的,一上线就延迟暴涨”。这很可能是索引参数或者检索调用方式的问题。比如在Milvus里查询时每来一个请求都临时建立连接,甚至每次做collection.load(),这会产生大量额外开销。正确做法是启动时统一连接、加载集合,查询时复用链接。另一个常见原因是向量维度太高导致内存占用过大,在Embedding模型和维度选择上就要提前规划,避免后期迁移。
第四个是“Agent频繁调用错误的工具”。这个问题源自工具描述不够明确。我给Agent配工具的时候,每个工具的描述都像个FAQ条目,写清楚“什么情况下使用”“输入应该是什么格式”“输出后下一步通常是什么”。比如kb_search的描述里我会注明:“当问题关于公司制度、产品手册、技术文档等内部资料时使用;输入应为问题原文或改写后的检索词;输出为文档片段列表,若结果为空说明知识库可能没有相关内容,不要重复使用本工具,考虑切换其他工具。”
第五个是“检索结果重复率极高”。用多条候选查询词做多路召回时,不同查询词往往拿到同一批片段。解决办法我在前面提过:合并后先做内容级去重,不只是文本完全一样才去重,相似度超过0.95的片段也要视为重复,只保留一条。这能显著降低Rerank和生成阶段的运算量。
6. 后续还能怎么扩展
聊完了搭建、优化和评测,最后分享几个我觉得特别值得继续深入的方向。
一个是知识库的自动更新机制。静态的知识库做了上线,如果不更新,三个月后价值就衰减一半。我现在推荐的做法是把知识库更新也做成一类Agent任务:定期扫描源文档目录,比对文档指纹,发现变更后自动触发重新解析和向量化,然后在管理后台留一个“待确认的更新清单”让人工审批。这样既保证了效率,又留了人工审核的闸门。
另一个方向是个人知识库的轻量化方案。很多只给自己用的知识库不需要上Dify或者Milvus这么大的体系,直接用Obsidian管理Markdown笔记,再配一个轻量级Agent工具做本地检索,组合起来就是一个“第二大脑”。社区里有人用Codex配合本地文件夹做个人知识库,也有人在Obsidian里挂接AI Agent插件,核心思路其实是一样的:用文件系统做载体,用Embedding做索引,用Agent做问答界面。
再往后的话,Agent和RAG的边界会越来越模糊。现在行业里已经在讨论一个趋势:把检索能力内嵌到Agent的每一个推理步骤里,让Agent在规划时就能感知哪些知识存在、哪些知识缺失,而不再是人肉指定“要用检索了”。这种能力的演进会让知识库系统从“问答机器”逐步变成“能自主完成信息收集、分析、产出结论的工作助手”。
我个人在实际落地RAG项目过程中最大的体会是:不要迷信框架,也不要迷信调一次参就能一劳永逸。做一个能用、好用的知识库,本质上是在持续打磨数据管道和评测闭环。先把最基础的切分、向量化、检索跑通,再用评测数据驱动优化,最后再逐步引入Agent编排这些复杂逻辑。按这个顺序走,基本不会翻车。