AI Agent 知识获取管道:RAG 检索增强生成实战与优化

发布时间:2026/9/29 11:54:44

AI Agent 知识获取管道:RAG 检索增强生成实战与优化 1. 为什么知识获取管道是 AI Agent 落地的第一道分水岭做 AI Agent 的人十有八九会在某个时刻撞上同一堵墙模型本身很聪明但你问它公司内部的报销标准、上周刚改的接口文档、某个客户的特殊约定它要么一本正经地胡说要么干脆说不知道。这不是模型不行而是它的知识边界被训练数据锁死了。知识获取管道要解决的就是把这个边界打开——让 Agent 在回答问题之前先去外部知识源里把相关材料捞回来再基于材料作答。这套机制的核心就是RAG检索增强生成。我在实际项目里见过太多团队模型选型纠结了两个月Prompt 调了几十版最后卡在答不准上回头一看问题根本不在模型而在检索这一环。检索没做好后面生成再强也是空中楼阁。所以我把 RAG 放在走进 AI Agent系列的第四篇因为它是从玩具 Demo迈向能用的产品的关键一跳。这篇文章适合谁看如果你已经能跑通一个基础的 Agent 对话循环但对怎么让 Agent 用上自己的知识还没有清晰路径那这篇就是给你写的。我会用TypeScript作为主要实现语言因为它在 Agent 工程化落地里越来越主流——类型系统能帮你在管道拼接时少踩很多坑。全文不堆概念重点讲清楚三件事RAG 的每个环节到底在干什么、为什么这么设计、以及我在实操中踩过的那些坑。先说一个反直觉的结论RAG 的瓶颈几乎从来不在生成而在检索。很多人第一次搭 RAG把 80% 的精力花在调 Prompt 和换模型上结果命中率死活上不去。真相是只要检索回来的内容是对的哪怕用中等能力的模型答案质量也能接受反过来检索回来的是一堆似是而非的片段再强的模型也只能垃圾进垃圾出。理解了这一点你就知道该把力气往哪儿使了。2. RAG 管道的四个环节拆开看每一步在做什么2.1 从文档到可检索单元的切分逻辑RAG 的第一步是把原始知识源PDF、Markdown、网页、数据库记录变成一段段可以被检索的文本块也就是Chunk。这一步看着简单实则决定了整个管道的上限。我见过最粗暴的做法是按固定字数硬切比如每 500 字一刀结果一句话被拦腰截断检索出来的片段语义残缺模型读得一头雾水。合理的切分要兼顾两个目标语义完整性和检索粒度。语义完整性指的是尽量在段落、标题、列表项这些自然边界处切检索粒度指的是块不能太大也不能太小——太大一块里混了好几个主题检索时噪声多太小上下文不足模型拼不出完整答案。我的经验值是中文技术文档单块控制在 300 到 800 字之间比较舒服同时保留 10% 到 20% 的重叠Overlap防止关键信息正好卡在切口上。重叠的意思是相邻两块共享一部分内容比如块 A 是 1 到 500 字块 B 就从 400 字开始这样跨边界的信息不会丢。用 TypeScript 写一个带重叠的递归切分器核心思路是先按标题层级切再按段落切最后才按字数兜底interface Chunk { id: string; text: string; metadata: { source: string; heading?: string; index: number }; } function splitByHeadings(text: string): string[] { // 按 Markdown 标题切分保留标题作为上下文 const sections text.split(/\n(?#{1,3}\s)/); return sections.filter((s) s.trim().length 0); } function splitWithOverlap(text: string, maxLen 600, overlap 100): string[] { if (text.length maxLen) return [text]; const chunks: string[] []; let start 0; while (start text.length) { const end Math.min(start maxLen, text.length); chunks.push(text.slice(start, end)); if (end text.length) break; start end - overlap; } return chunks; }这里有个细节值得说metadata 比文本本身还重要。每个 Chunk 都要带上来源文件、所属标题、在原文中的位置。为什么因为检索回来之后你需要给模型提供这段话出自哪里的线索模型才能判断可信度同时前端展示引用来源时也靠它。我早期偷懒没存 metadata后来做引用溯源时全部返工血的教训。2.2 向量化把语义变成可计算的坐标切好块之后要把每块文本转成向量Embedding也就是一串浮点数。这串数字的妙处在于语义相近的文本向量距离也近。这样检索就变成了找距离最近的几个向量数学上非常干净。选 Embedding 模型时别只盯着排行榜。我实际用下来要综合考虑三点语言支持中文场景必须选中文语料训练充分的、维度成本维度越高存储和计算越贵768 或 1024 维通常是性价比甜点、是否支持本地部署涉及敏感数据时这是硬要求。热词里提到的本地知识库场景基本都要求 Embedding 能在内网跑这时候开源模型就是首选。向量化本身没什么玄机但有个坑必须提醒查询和文档必须用同一个模型。我见过有人文档用 A 模型编码查询时手滑换成了 B 模型结果检索结果乱七八糟排查了半天才发现是模型不一致。向量空间都不一样距离计算毫无意义。async function embedBatch(texts: string[]): Promisenumber[][] { // 批量编码注意控制单次请求的文本数量避免超限 const BATCH_SIZE 32; const results: number[][] []; for (let i 0; i texts.length; i BATCH_SIZE) { const batch texts.slice(i, i BATCH_SIZE); const vectors await embeddingClient.encode(batch); results.push(...vectors); } return results; }批量处理时要注意单次请求的 token 上限。我一般把批大小设在 16 到 64 之间太大容易触发限流太小则吞吐上不去。这个值没有标准答案得根据你用的服务实测调整。2.3 向量库选型别一上来就上重型武器存向量需要一个向量数据库。市面上的选择从轻到重排一排内存数组、SQLite 扩展、FAISS、Milvus、Qdrant、Pinecone 等等。新手最容易犯的错是一上来就部署一套分布式向量库结果数据量才几千条纯属杀鸡用牛刀。我的建议是按数据规模分档数据规模推荐方案理由1 万条以内内存 余弦相似度零依赖启动快够用1 万到 100 万FAISS 或 SQLite 向量扩展单机性能强运维简单100 万以上Qdrant / Milvus支持分布式、过滤、持久化对于大多数内部知识库场景数据量其实就在几万条这个量级用 FAISS 单机跑完全没问题。等真的到了百万级再考虑上分布式别提前优化。用 TypeScript 做内存检索其实很简单核心就是算余弦相似度function cosineSimilarity(a: number[], b: number[]): number { let dot 0, normA 0, normB 0; for (let i 0; i a.length; i) { dot a[i] * b[i]; normA a[i] * a[i]; normB b[i] * b[i]; } return dot / (Math.sqrt(normA) * Math.sqrt(normB)); } function topK(query: number[], vectors: number[][], k 5) { return vectors .map((v, i) ({ index: i, score: cosineSimilarity(query, v) })) .sort((x, y) y.score - x.score) .slice(0, k); }这段代码在几千条数据下毫秒级返回完全够用。等数据涨到十万条以上再换成 FAISS 的 WASM 版本或者外部服务。2.4 检索与重排把差不多变成就是它最朴素的检索就是向量最近邻 Top-K但实际用起来你会发现Top-5 里经常混进一两条不相关的。这时候就需要重排Rerank。重排的思路是先用向量检索快速召回一批候选比如 20 条再用一个更精细的模型对候选逐条打分挑出真正相关的几条。为什么两阶段因为向量检索快但粗重排模型准但慢。先用快的把范围缩小再用准的精选是典型的工程折中。热词里提到的RAG 命中率hit rate很大程度上就靠重排来提升。除了重排还有两个提升命中率的实用手段混合检索和查询改写。混合检索是把向量检索和关键词检索BM25的结果融合因为有些查询靠关键词匹配更准比如查一个具体的错误码。查询改写则是让模型先把用户的口语化问题改写成更适合检索的形式比如把报销咋弄改写成差旅报销流程和标准。async function hybridRetrieve(query: string, k 5) { const [vectorHits, keywordHits] await Promise.all([ vectorSearch(query, k * 4), bm25Search(query, k * 4), ]); // 用 RRF倒数排名融合合并两路结果 const scores new Mapstring, number(); const RRF_K 60; vectorHits.forEach((hit, rank) { scores.set(hit.id, (scores.get(hit.id) ?? 0) 1 / (RRF_K rank)); }); keywordHits.forEach((hit, rank) { scores.set(hit.id, (scores.get(hit.id) ?? 0) 1 / (RRF_K rank)); }); return [...scores.entries()] .sort((a, b) b[1] - a[1]) .slice(0, k); }RRF 这个融合算法我很推荐它不需要调权重对两路结果的分数尺度不敏感实测下来比手动加权省心得多。3. 把管道接进 Agent检索结果怎么喂给模型3.1 上下文拼装不是塞得越多越好检索回来 5 条 Chunk怎么塞进 Prompt新手常见的做法是全塞进去觉得信息越多越好。但上下文窗口是有限资源塞太多无关内容反而会稀释关键信息让模型抓不住重点。而且 token 是要花钱的塞一堆废话纯属浪费。我的做法是按相关度排序取前 3 到 5 条每条前面标注来源让模型知道这是外部知识而非它自己的记忆。Prompt 模板大致长这样你是一个基于知识库回答问题的助手。请严格依据下面提供的资料作答 如果资料中没有相关信息直接说资料中未提及不要编造。 【资料 1】来源员工手册.pdf 差旅报销标准一线城市住宿每晚不超过 500 元…… 【资料 2】来源财务制度.md 报销需在出差结束后 15 个工作日内提交…… 用户问题出差去上海住宿能报多少这个模板里有两个关键约束严格依据资料和没有就说没有。前者防止模型自由发挥后者是抑制幻觉的关键。我实测下来加上资料中未提及这个兜底话术后编造答案的比例明显下降。3.2 引用溯源让答案可验证一个成熟的 RAG 系统答案里应该带上引用标记比如根据《员工手册》第 3 章……。这不仅是体验问题更是信任问题——用户能点开来源核对才敢把系统用在正经业务上。实现上就是在拼装上下文时给每条资料编号然后要求模型在答案里用[1][2]这样的标记引用。解析答案时把标记映射回原始来源即可。这一步不难但很多 Demo 都省了导致系统看起来能答实际没人敢用。3.3 多轮对话下的检索策略单轮问答好办多轮对话就麻烦了。用户第二句说那北京呢你拿那北京呢去检索啥也搜不到。这时候需要查询改写把当前问题和历史对话一起喂给模型让它改写出一个自包含的检索查询比如出差去北京的住宿报销标准是多少。async function rewriteQuery(history: Message[], current: string): Promisestring { if (history.length 0) return current; const prompt 根据以下对话历史把用户的最新问题改写成一个独立完整的检索查询 只输出改写后的查询不要解释。\n\n历史\n${history.map(m ${m.role}: ${m.content}).join(\n)}\n\n最新问题${current}; return await llm.complete(prompt); }这个改写步骤在多轮场景下几乎是必需的否则检索质量会断崖式下跌。代价是多一次模型调用延迟增加几百毫秒但换来的是命中率的大幅提升非常值。4. 实操中那些文档不会告诉你的坑4.1 切分粒度调优从能跑到好用的必经之路前面说了切分的重要性但具体切多大没有万能公式。我的做法是先跑一版基线再用真实问题测命中率然后针对性调整。具体操作准备 20 到 30 个真实用户问题人工标注每个问题的正确答案出自哪个文档的哪一段然后跑检索看 Top-5 里有没有命中。命中率低于 70% 就说明切分或检索有问题。调优时优先动这几个参数块大小、重叠比例、Top-K 数量。我遇到过一种情况块设成 1000 字时命中率只有 60%改成 400 字后跳到 85%——原因是原文里一段话讲了三个不同主题块太大导致检索时主题被平均掉了。这个教训是块大小要匹配你文档的主题密度主题密集的文档要切小主题松散的可以切大。4.2 元数据过滤被低估的检索利器纯向量检索有个盲区它只看语义相似不看该不该看。比如用户问2024 年的政策向量检索可能把 2022 年的相似条款也捞回来。这时候元数据过滤就派上用场了——在检索前先按年份、部门、文档类型过滤把范围缩小到该看的子集。实现上向量库基本都支持带过滤条件的检索。即使是内存方案也可以在算相似度前先筛一遍function filteredSearch(query: number[], chunks: Chunk[], vectors: number[][], filter: (c: Chunk) boolean, k 5) { const candidates chunks .map((c, i) ({ chunk: c, vector: vectors[i] })) .filter(({ chunk }) filter(chunk)); return candidates .map(({ chunk, vector }) ({ chunk, score: cosineSimilarity(query, vector) })) .sort((a, b) b.score - a.score) .slice(0, k); }元数据设计得好检索质量能上一个台阶。我在项目里给每个 Chunk 都打了source、heading、updatedAt、category四个字段过滤时灵活组合效果立竿见影。4.3 增量更新知识库不是一次性的知识库会变。文档会更新新文件会加进来。如果每次改动都全量重建向量数据量一大就受不了。正确做法是增量更新给每个 Chunk 一个稳定的 ID比如文件路径 块序号的哈希更新时只处理变化的文件删掉旧 Chunk、插入新 Chunk。这里有个容易忽略的点删除要彻底。我见过有人更新文档时只插入新块忘了删旧块结果同一个问题检索出新旧两个版本的答案模型直接精神分裂。所以更新逻辑必须是先删后插或者用文档 ID 做覆盖写。async function upsertDocument(docId: string, content: string) { const chunks splitDocument(content); const vectors await embedBatch(chunks.map(c c.text)); // 先删除该文档的所有旧块 await store.deleteByMetadata({ source: docId }); // 再插入新块 await store.insert(chunks.map((c, i) ({ ...c, vector: vectors[i] }))); }4.4 评估没有度量就没有优化最后说一个最容易被跳过、但最重要的环节评估。没有评估你根本不知道改动是变好了还是变坏了全靠感觉调参纯属瞎蒙。我的评估方案很朴素维护一个测试集每条包含问题 期望命中的文档片段每次改动后跑一遍看命中率和答案质量。命中率是客观指标答案质量可以人工打分或用一个强模型当裁判。这个测试集不用很大20 到 50 条就能反映趋势。关键是每次改动都跑形成闭环。热词里反复出现的RAG 瓶颈RAG 命中率本质上都是评估缺失导致的——你不知道瓶颈在哪自然无从优化。把评估做起来瓶颈会自己浮出水面。5. 从基础 RAG 到 Agentic RAG 的演进方向基础 RAG 跑通之后你会发现它有几个天然局限检索是一次性的不管问题多复杂都只查一轮检索策略是固定的不会根据问题类型调整。这就是Agentic RAG要解决的问题——让 Agent 自己决定要不要检索检索几次用什么策略检索。举个具体例子用户问对比一下 A 产品和 B 产品的退款政策。基础 RAG 会把这个当成一个查询去检索结果可能只捞到其中一个产品的政策。而 Agentic RAG 会先拆解成两个子查询分别检索再综合对比。这种检索规划能力是基础 RAG 给不了的。实现上Agentic RAG 通常把检索封装成一个工具Tool让 Agent 通过工具调用来触发检索并且可以多轮调用。这正好衔接了本系列前面讲的 Agent 工具调用机制。你可以先让 Agent 判断问题是否需要检索需要的话生成查询、调用检索工具、评估结果是否充分不充分就换个查询再检一次。const retrievalTool { name: search_knowledge, description: 在内部知识库中检索相关信息输入检索查询返回相关文档片段, parameters: { query: string }, execute: async ({ query }: { query: string }) { const hits await hybridRetrieve(query, 5); return hits.map(h 【${h.chunk.metadata.source}】${h.chunk.text}).join(\n\n); }, };把检索做成工具之后Agent 的自主性就上来了。它可以根据第一次检索的结果决定下一步而不是被动地接受一次检索的结果。这是从RAG 管道到RAG 智能体的关键转变也是当前这个方向最值得投入的地方。我个人在实际项目里的体会是先把基础 RAG 的每个环节做扎实再考虑 Agentic 化。基础没打好就上 Agentic只会把问题放大——检索本身就不准让 Agent 多检几轮也只是多捞几遍垃圾。顺序不能反。最后分享一个小技巧调试 RAG 时把每次检索的查询、召回的 Chunk、最终拼装的 Prompt 全部打日志。出问题时一眼就能看出是检索没召回、还是召回了但没用好。这个日志我在每个 RAG 项目里都会加省下的排查时间难以估量。
延伸阅读

更多相关文章

2026/9/29 11:54:44

PatchCore工业缺陷检测指南:特征嵌入与无监督异常检测实战

简介:面向深度学习与工业视觉领域的本科生和研究生,PDF 资料以 PatchCore 算法为核心,给出基于特征嵌入的工业缺陷检测完整作业方案。内容从作业背景出发,对比基于重构与基于嵌入两类方法,并围绕特征提取、核心集采样&…

2026/9/29 11:54:44

视网膜病变图像识别实战:从数据预处理到模型调参避坑指南

简介:这份PDF是一篇发表于2021年的学术论文,聚焦糖尿病性视网膜病变图像的自动识别问题,适合医学图像处理、计算机视觉及深度学习方向的研究者、学生和算法工程师阅读。文章在VGG-16基础上提出多特征融合的卷积神经网络框架,通过融…

2026/9/29 11:54:44

HCIA-Security实操指南:USG防火墙+Kali攻防闭环实验链

简介:本资源是一份面向HCIA-Security认证初学者的系统性学习笔记,聚焦网络安全核心原理与实操技能,适用于备考华为认证、高校网络专业学生及安全入门工程师。笔记以PDF形式呈现,共1个文件,大小19.3MB,内容结…

2026/9/29 12:44:47

Trea AI编辑器实测:从智能补全到Agent多文件操作的工作流革命

这些年我用过的编辑器不少,从Vim到VS Code再到JetBrains全家桶,但真正让我觉得“AI不是锦上添花而是重写工作流”的,还是Trea。如果你最近关注AI编程工具,大概率看过“字节trea官网”“trea下载”这些热词。Trea是一个AI原生的编辑…

2026/9/29 12:44:47

Windows电脑使用记录查询与审计:从登录日志到浏览器痕迹

简介:如何查看电脑使用记录是一份面向普通电脑用户与系统管理维护者的实用doc说明文档,系统讲解通过系统日志、计划任务日志、运行历史、浏览器缓存等途径追踪电脑开关机、程序运行、文档访问及上网浏览记录的具体操作方法。资源包仅含1个doc文件&#x…

2026/9/29 12:39:46

C++模板元编程面试必考:从入门到精通,一文全解析!

C++模板元编程面试必考:从入门到精通,一文全解析! 本文是C++高级面试系列第12篇,专注于模板元编程(Template Metaprogramming, TMP)。模板元编程是C++最强大的特性之一,也是大厂面试中最常被问到的难点。无论你是准备校招还是社招,这篇文章都将帮你彻底拿下这个知识点。…

2026/9/29 11:07:23

东莞市品牌网站建设报价常见报错与解决

东莞品牌网站建设报价单背后:一份保姆级建站教程避坑实录 网站做好了没人访问,这大概是很多老板最头疼的事。花了大几万做的品牌站,上线后流量惨淡,比路边摊还冷清。别急着骂外包公司,很多“东莞品牌网站建设报价”里藏着不少猫腻,比如用模板站冒充定制…

2026/9/28 6:05:15

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/29 7:00:49

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/29 0:04:04

AI Evals实战指南:从零搭建LLM应用评估体系与CI/CD集成

1. 为什么AI Evals值得你花时间搞明白做LLM应用的人,迟早会撞上同一堵墙:模型输出飘忽不定,今天答得好好的,明天换个问法就胡说八道。你改了一版提示词,感觉好像好了点,但到底好了多少?说不清。…

2026/9/29 0:04:04

Java采购管理系统实战:从数据库设计到事务一致性

简介:这是一套面向Java Web初学者与课程设计者的采购管理系统完整源码,采用JSP技术搭建,配合MySQL数据库,用于解决企业采购信息的管理问题,适合作为毕业设计、课程大作业或进销存类项目的参考模板。系统实现了用户登录…

2026/9/29 3:53:39

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/29 9:46:12

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/29 6:36:14

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑