ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Agent知识管道实战:从RAG原理到全流程搭建

Agent知识管道实战:从RAG原理到全流程搭建 最近几个做 Agent 项目的朋友不约而同地问我同一个问题Agent 的“记忆力”和“知识”到底从哪里来很多人搭 Agent 时第一步想到的是调大上下文窗口第二步想到的是微调模型结果折腾一圈发现该幻觉的还是幻觉该答错的还是答错。这条路径我前两年也走过最后真正把问题解决掉的是一套很多人听过但没真正用起来的方案——RAGRetrieval-Augmented Generation检索增强生成。作为“走进 AI Agent”系列的第四篇这次我不打算泛泛讲概念而是把知识获取管道这件事从头拆到尾Agent 为什么需要知识管道、RAG 在这条管道里扮演什么角色、从 0 到 1 怎么搭、以及我踩过的那些坑。这篇内容适合两类人一类是刚接触 Agent 开发想搞清楚“知识库”到底是怎么接进大模型的初学者另一类是已经在代码层面跑通过 RAG但 hit rate命中率上不去、检索结果总是不对味想看看别人怎么排查问题的开发者。文章会尽量少用术语堆砌遇到必要的概念我会用实际场景讲透因为 RAG 这东西听懂原理和能落地中间差着十万八千里。1. 为什么 Agent 必须先解决“知识从哪里来”很多人在设计 Agent 的时候有个误区觉得只要把 LLM 接上工具调用Agent 就“无所不能”了。但只要你真的把 Agent 丢到业务场景里跑几天就会发现它处理不了两类很现实的问题。第一类是知识时效性问题。大模型的训练数据有截止日期它脑子里装的是过去某个时间点之前的互联网和公开资料。你今天问它你公司最新一版的产品手册、上周刚发布的接口文档、昨天才更新过的排班表它只能靠猜。第二类是专业边界问题。通用模型对垂直领域的细节知识掌握得相当浅它可能知道“什么是 ERP”但你问它“你们公司 ERP 里那张合同审批表有几个状态字段”它就彻底抓瞎了因为这份知识不在它的参数里。1.1 大模型的知识边界究竟在哪里我习惯把大模型的知识能力分成三层来看。第一层是参数化记忆也就是模型在预训练和微调阶段被“焊死”在权重里的知识。这一层知识质量高、速度快但有个致命问题——它是静态的。你今天想让模型知道一个新知识除非重训否则它永远不知道。而且这一层还容易“记串”模型会把相似概念混淆在一起这就是我们常说的幻觉源头之一。第二层是上下文记忆也就是你在提示词里塞进去的内容。这部分知识非常灵活随对话随时增减但受限于上下文窗口大小塞多了模型会“看不过来”注意力被稀释关键信息反而找不着。而且成本随 token 数线性上涨塞几万 token 进去一次调用的钱够吃一顿午饭。第三层才是真正的增量知识也就是通过外部管道临时取回来的内容。RAG 干的就是这件事把模型不掌握的、动态变化的、私有领域的知识在生成之前先检索回来放在提示词里作为“参考资料”让模型基于这些材料作答。1.2 RAG 是一条管道不是一个外挂数据库如果只把 RAG 理解成“向量数据库检索”那就太小看它了。我在实际项目中体会最深的一点是RAG 是一条完整的知识获取管道它解决的是“从海量文档中找到最相关的几段内容并以模型能有效利用的形式送进生成环节”这个全流程问题。这条管道往下拆至少包含四个环节知识入库清洗与切分、向量化与索引、检索含召回和重排、以及最后的增强生成把检索结果与用户问题组装进提示词。任何一个环节拉胯最后呈现给用户的效果都会打折扣。我见过很多人花大价钱买了向量数据库结果文档没切好检索出来一堆半截话最后模型只能拿残缺资料硬编效果比不接库还差。所以这篇文章我会把四个环节逐个讲透尤其是切分策略和重排这两块它们是最容易被忽略、但恰恰是决定 RAG 效果上限的地方。2. 拆解 RAG 管道的四个核心环节一条能用的 RAG 管道不是拿现成框架拼一拼就能跑的。每个环节背后都有明确的目标和对应的坑我按落地顺序一个个说。2.1 文档接入与切分决定检索质量的第一道关卡很多教程把切分chunking一笔带过好像用框架默认的字符分割就行了。但实操下来切分策略对 RAG 效果的影响有时候比换模型还大。我举个最直观的例子你要给 Agent 接一份公司规章制度 PDF里面有“第三章 考勤管理”这一节下面是若干条具体规定。如果你按纯字符数每 500 字一刀切很可能把“第三章 考勤管理”这个标题切在上一块把具体条文切在下一块。检索时用户问“迟到了怎么扣工资”系统召回的是只有条文没有标题的那块模型看完不知道这条规定出自哪里作答自然不够准确。切分的原则我总结为三句话保持语义完整、带上足够的上下文锚点、控制每块的信息密度。具体做的时候有几种常见策略固定长度切分按字符数或 token 数硬切最简单但容易切断语义。递归字符切分先用段落符分块超长再按句子分句子还超再按子句分。这是 LangChain 里RecursiveCharacterTextSplitter的做法属于比较稳妥的默认选择。结构感知切分针对 Markdown、HTML、PDF 的标题层级来切保留章节结构。对知识库类文档效果最好。语义切分用 embedding 算相邻句子的相似度在语义断点处切。效果最细腻但计算成本高。我在实际项目中建议优先采用结构感知切分配合标题前移策略——把每个块所属的章节标题拼到块内容前面让模型知道这段内容出自哪个上下文。这个小技巧不需要额外引入模型纯粹靠处理逻辑但 retrieval 之后生成准确率能明显提升。切分块大小也有讲究。块太小比如 100 个 token语义信息不完整模型拿到手“看了个寂寞”块太大比如 2000 个 token噪声太多还容易挤占后续生成的空间。我的经验值是在 400 到 800 token 之间具体取决于你的文档类型和检索粒度。长文档偏结构化的可以取大值短问答型的取小值。别迷信某个固定数字拿你自己的文档做一组对比实验看命中率说话。2.2 Embedding 与向量索引把文本变成可计算的距离切好的文档块要能被检索第一步是向量化。这里要选一个 embedding 模型把文本变成一串浮点数向量语义相近的文本在向量空间里距离更近。选 embedding 模型有一个核心标准它必须懂你业务的语言。通用中文 embedding 模型处理标准语料没问题但如果你做的是医疗、法律、工业这种术语密集的领域最好找领域适配过的模型或者自己在业务数据上做微调。向量化之后要建索引。常见的向量索引有 Flat暴力全量计算、HNSW分层小世界图、IVF倒排分桶等。小规模知识库几千条文档Flat 就够了精确度高、实现简单。当数据量到几十万甚至上百万条时才需要考虑 HNSW 这种 ANN近似最近邻索引牺牲一点点精度换检索速度。我在项目里一般先 Flat 起步等检索慢到用户体验受损了再切换别一上来就上复杂索引给自己添乱。存储层面市面上成熟方案不少Milvus、Qdrant、Weaviate、Chroma、pgvector 我都试过。个人建议如果你的技术栈已经有 PostgreSQL直接上 pgvector 是最省事的——不需要额外维护一套中间件事务和备份体系都可以复用。从零搭建且预算充足的团队可以选 Qdrant 这类专注向量搜索的引擎功能更纯粹。选型的时候不要被“高性能”三个字带跑你的检索量和数据量决定了你真正需要的下限在哪里。2.3 检索与召回从“近似相关”到“精确命中”单纯的向量检索有一个天然问题它只做语义相似度匹配不关心关键词是否精确出现。用户问“合同编号 CH-2024-001 的审批状态”向量检索很可能把包含这个编号的文档排在后面因为从语义来看 “编号查询”和“审批状态”这层关系向量化之后被稀释了。这就是为什么要混合检索Hybrid Search。混合检索的套路是把向量检索和关键词检索BM25的结果做融合最后按加权分数排序。BM25 对精确词匹配非常强向量检索对语义泛化强两者互补。融合算法我常用的是 RRFReciprocal Rank Fusion做法简单粗暴把两种检索结果各自的排名取倒数加总后重排。这个算法不需要调权重对两端分数尺度不一致的问题天然免疫实测非常稳。但召回只能保证“相关材料被捞出来”不能保证“捞出来的就是最该用的”。比如知识库里关于“退款流程”有十几条文档向量召回回来 10 块内容真正能直接回答用户问题的可能只有其中 2 块。如果把这 10 块全部塞进提示词模型会被无关信息干扰。这时候需要重排Rerank环节用一个专门的重排序模型对召回的候选块重新打分把最相关的几块顶到最前面。Rerank 模型本质上是“交叉编码器”它把用户问题和候选文档拼在一起过一遍模型做精细相关性判断效果远好于向量检索阶段的双塔式粗筛。很多教程不讲这一步但我可以负责任地说加上 rerank 之后RAG 的回答质量提升是肉眼可见的强烈建议不要省略。2.4 增强生成把检索结果变成模型能用的“参考资料”检索完成之后最后一步是把内容组装进提示词。这里的组装不是简单拼接有几个细节值得讲究。第一明确告诉模型哪些内容是可参考的、哪些是自己生成的。我在提示词里会固定写“以下是知识库检索到的参考资料请优先依据它们回答如果资料不足以回答问题请明确说明”。这能显著减少模型“超纲发挥”的幻觉。第二对检索结果做“边界约束”。如果候选块质量不高宁可少给几块也不要硬塞进去凑数。我在代码里会设一个分数阈值低于阈值的候选块直接丢弃只保留高置信度的内容。第三引用可溯源。给每块检索内容带上来源标识如文档名和章节号让模型在回答末尾标注引用来源。这一步在面向企业客户的项目里几乎必备方便做回答审计也方便用户在 Agent 上点击跳转原文核验。3. 从 0 到 1 搭建一套可用的 RAG 管道理论讲完进入实操。这一节我按“选型、入库、检索、接入 Agent”四个步骤带你跑一遍。3.1 技术选型适合新手起步的组件组合很多人问我选什么框架我的回答是初期阶段别选框架先写裸代码。LangChain 这类框架确实封装了很多东西但正因为封装太狠出问题时你根本不知道底层发生了什么。我建议第一版先直接用 Python 的 embedding 模型接口、向量数据库 SDK 和 LLM 接口手动搓一条管道跑通之后再考虑要不要换框架。我常用的起步组合是这样组件推荐选择说明Embedding 模型BGE-M3 或同等级中文模型中文语义效果好支持多种粒度向量存储pgvector 或 QdrantPostgreSQL 已有则选 pgvector关键词检索内置 BM25ES/OpenSearch或 rank_bm25 库混合检索的关键词侧Rerank 模型BGE-Reranker 系列轻量、效果出色LLM任何主流大模型 API优先上下文窗口大的这套组合的好处是每个组件都成熟稳定且都有中文社区的资料可以参考中途遇到问题搜索起来效率高。3.2 入库流程清洗、切分、向量化入库这一步我给的工程化建议是一条独立的脚本流水线输入是原始文档输出是向量库里的记录。以下是我在项目里实际用的入库伪代码流程# 文档入库流程简化版 import os from langchain_text_splitters import RecursiveCharacterTextSplitter from openai import OpenAI # 1. 读取文档并做基础清洗 raw_text load_pdf(company_handbook.pdf) # 去页眉页脚、去多余换行、统一编码 cleaned_text clean_document(raw_text) # 2. 结构感知切分按 Markdown 标题层级切保留标题上下文 splitter RecursiveCharacterTextSplitter( chunk_size600, chunk_overlap120, separators[\n## , \n### , \n\n, \n, 。, ], ) chunks splitter.split_text(cleaned_text) # 把章节标题拼回每一块作为上下文锚点 chunks [prepend_section_title(chunk) for chunk in chunks] # 3. 向量化 client OpenAI(api_keyos.getenv(EMBEDDING_API_KEY), base_urlos.getenv(EMBEDDING_BASE_URL)) for idx, chunk in enumerate(chunks): emb client.embeddings.create(modelbge-m3, inputchunk) store_vector(db, ididx, embeddingemb.data[0].embedding, textchunk, meta{source: handbook.pdf})这里有两个容易踩的细节。第一个是chunk_overlap相邻块之间留 120 token 的重叠是为了防止关键内容恰好落在切分缝上被切断。第二个是切分分隔符的优先级顺序我故意把 Markdown 标题放最前面确保切分优先尊重原有章节结构。3.3 检索流程混合召回加重排检索环节我固定用两条腿走路。先并行跑向量检索和 BM25 关键词检索再用 RRF 融合最后送进 rerank 模型精排取 Top-K。核心代码长这样# 检索与重排流程简化版 import numpy as np from rank_bm25 import BM25Okapi def search(query, top_k5): # 1. 向量召回 q_emb embed(query) vector_hits vector_db.search(q_emb, top30) # 2. 关键词召回 tokenized_corpus [tokenize(doc) for doc in all_docs] bm25 BM25Okapi(tokenized_corpus) bm25_hits bm25.get_top_n(tokenize(query), all_docs, n30) # 3. RRF 融合 fused reciprocal_rank_fusion(vector_hits, bm25_hits) # 4. Rerank 精排 reranked rerank_model.rerank(query, [fused[i].text for i in range(10)]) return reranked[:top_k] def reciprocal_rank_fusion(list_a, list_b, k60): scores {} for rank, doc_id in enumerate(list_a): scores[doc_id] scores.get(doc_id, 0) 1 / (k rank) for rank, doc_id in enumerate(list_b): scores[doc_id] scores.get(doc_id, 0) 1 / (k rank) return sorted(scores, keyscores.get, reverseTrue)注意向量召回和 BM25 召回我都先取 30 条候选融合后再取前 10 条进入 rerank最后只保留 5 条进提示词。这个漏斗设计是有意的粗召回阶段宁可召回宽一点别把正确答案漏掉精排阶段再收紧。3.4 把 RAG 接进 Agent从“查一次”到“按需查”RAG 接入 Agent最简单的形态是“每次对话先固定检索一轮”再把结果拼进系统提示词。但实际项目中这不够灵活。用户可能只是闲聊天根本不需要检索知识库强制检索反而浪费 token 还增加延迟用户也可能连续追问每次都检索同样的内容完全没有必要。更合理的方案是 Agentic RAGAgent 化检索。核心思路是把“是否检索”和“检索什么”的决定权交给 Agent 自己。Agent 内置一个决策节点先判断用户问题是否需要知识库支撑。如果需要再决定用几个查询词去检索、要不要做多轮检索。这个方案在最近一年非常火本质上是让 RAG 从僵硬的“固定管道”升级为“Agent 的自主工具”。我实际实践中最常用的 Agentic 形态是“路由 检索工具”给 Agent 定义一个search_knowledge_base(query)工具Agent 根据对话上下文自主决定何时调用、用什么关键词调用。这样既保留了 RAG 的准确性又让多轮对话中的追问变得自然——用户第一问问“报销流程”Agent 检索一次用户接着问“那发票丢了怎么办”Agent 会想一下判断这个问题需要新检索于是重新发起查询。4. 常见问题与排查技巧实录RAG 系统真正让人头疼的不是搭建过程而是线上跑起来之后出现的各种“不好用”。我在几个项目里摸爬滚打下面这些问题基本都遇到过整理成一份排查速查表。4.1 命中率上不去的五个主要原因所谓 hit rate命中率指的是检索结果中有多少比例是真正相关的内容。这是一个比“回答对不对”更前置的指标——检索都没命中后面生成环节再强也白搭。现象可能原因排查思路检索结果碎片化回答前言不搭后语切分块太小或切断语义调大 chunk_size启用重叠窗口检索结果一堆无关内容Embedding 模型领域不匹配换领域模型或在业务数据上微调精确编号/型号检索不到只有向量检索缺关键词检索上 BM25 混合召回相关文档排在后面Top-K 取不到缺 rerank粗排淹没关键项加 rerank 重排回答引用了错误段落上下文锚点丢失模型无法定位原文标题前移给块补充来源元数据4.2 两个惨痛教训切分翻车和“万能模型”陷阱第一个惨痛教训来自我做某制造企业的设备维修知识库。原始文档是扫描版 PDF文字层混乱大量换行符把段落拆得七零八落。我第一版直接用通用切分器处理结果入库的块基本都是半句残句检索 hit rate 只有 35% 左右基本没法用。后来我把预处理环节补上来——先用 OCR 校正工具把 PDF 转成干净文本再用正则清理异常换行和页眉页脚最后用结构感知切分重新入库hit rate 直接翻倍到 75% 以上。这件事给我的经验是文档质量决定 RAG 上限花一小时做清洗能省之后十小时的调试时间。第二个教训是“万能模型”陷阱。有一阵我被宣传话术迷惑觉得 GPT 级别的 embedding 模型应该什么场景都能打结果在中文法律语料上表现稀烂。后来才明白通用模型训练数据以网页和通用文本为主对法律条文的句式结构理解不够。换用中文法律领域微调过的 embedding 模型之后检索相关性立刻提了一截。所以选模型时一定拿自己的业务样本跑召回评测不要迷信名声。4.3 性能与成本把延迟控制在可接受范围加了混合检索和 rerank 之后最直接的代价是检索链路变长。我曾经有一次把一次检索的完整链路做到 3 秒多用户体感很差。排查之后发现三个瓶颈向量库没建索引走了全量扫描rerank 模型拿 CPU 在跑LLM API 并发受限。解决方案分别对应给向量库加 HNSW 索引、把 rerank 模型部署到 GPU 或者限制候选集大小从 10 条减到 6 条、给外部 API 调用加连接池和超时重试。优化完单次检索压缩到 800 毫秒以内。这里我建议各位在项目初期就把延迟目标定下来比如“检索环节不超过 1 秒”然后带着这个约束做选型和部署。5. 进阶方向从 RAG 到 GraphRAG 与本体增强基础 RAG 跑通之后你会发现它依然有天花板传统 RAG 处理的是“从一个知识碎片里找答案”但当你的知识库涉及大量实体之间的多跳关系时比如“这个零件由哪些供应商提供、每个供应商的评级是多少、评级最差的供应商供应了哪条生产线”纯向量检索很难把这种关系链一次捞齐。答案分散在多篇文档里需要多跳推理才能串起来。5.1 GraphRAG把知识织成网络GraphRAG 的思路是在向量检索之外额外维护一张知识图谱。文档入库时先用 LLM 抽取实体和关系存入图数据库检索时先做向量粗召回再用图谱沿着关系链扩展探索把多跳相关信息一并捞回。这样做的收益是回答需要推理链条的问题时准确率比纯向量检索高出一大截。代价也不小构建图谱需要消耗大量 LLM 调用维护成本高对文档质量的要求也更苛刻。我个人建议是如果你的知识库问题形态里“多跳关系查询”占比超过 30%才值得上 GraphRAG否则基础 RAG 加合理的切分重排已经够用。5.2 本体Ontology与领域建模的价值和 GraphRAG 搭配的另一个进阶方向是本体Ontology。所谓本体就是对你的领域手工定义一个概念框架实体有哪些类型、实体之间有哪些关系、每种关系的约束是什么。比如做产品知识库本体里定义“产品—所属分类—参数属性—适用场景”这套关系再拿它去引导抽取和检索能让图谱质量大大提高。热词里提到的 ontology rag基于本体的 RAG就是这条路线。我的体会是本体像是一张“领域的公路网地图”没有它LLM 抽取出来的实体关系可能是杂乱无章的乡间小路有了它检索才能在这张网上找到最优路径。再往外延伸还有 Agentscope 2.0 提出的 “RAG as Service” 思路——把 RAG 能力封装成标准服务让多个 Agent 共享一套知识基础设施。这个方向对大团队很实用避免了每个 Agent 各自搭一套知识库导致的知识割裂问题也是我接下来正在实践的方向。6. 写在最后我对 RAG 的几个核心认知聊了不少技术细节最后分享几条我自己反复验证过的认知希望能帮你少走弯路。第一RAG 的价值不在“酷”而在“边界控制”。大模型天生自信遇到不确定的知识容易编造。RAG 给模型划了一条“只在给定资料范围内作答”的边界这个边界感对生产环境至关重要。判断一套 RAG 方案好不好先看它能不能让模型在资料不足时老实说“我不知道”做到这一条方案就成功了一半。第二评估要先于优化。我见过太多团队埋头调参却连一套系统的评估集都没有。建议从第一天开始就攒一组有代表性的问答对比如 100 条覆盖不同文档类型和问题形态的测试集每次改动都跑一遍评估看 hit rate 和回答正确率的变化。没有评估的优化本质上是在凭感觉碰运气。第三RAG 是一条持续优化的管道不是一次性工程。文档会更新业务场景会变化embedding 模型也在迭代。我现在的习惯是每个月做一次全量评估报告对比不同模块版本的指标再决定是否升级。把它当运维工作来做而不是当项目做完就扔RAG 才能在真实业务里长期稳下去。最后分享一个小细节在我所有的 RAG 项目里都会在系统提示词里加上一句“如果检索资料与用户问题无关请直接向用户说明资料不足不要尝试猜测答案”。这句话看起来简单却是抑制幻觉性价比最高的一行字。你可以在自己的项目里试试大概率会有惊喜。
返回列表