ARTICLE DETAIL

资讯详情

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

RAG基础构建实战:为AI Agent打造可靠的知识获取管道

RAG基础构建实战:为AI Agent打造可靠的知识获取管道 写这篇的时候我刚从一个大模型项目的坑里爬出来。当时我们的 AI Agent 已经能流畅聊天、调用工具但只要问到企业内部的具体制度、产品参数、历史项目细节它就答得吞吞吐吐甚至睁眼说瞎话。问题很明显模型的参数记忆撑不起业务对精确知识的需求。于是我们开始动手给 Agent 接上 RAG也就是检索增强生成。这件事做下来我的感受是RAG 确实是 Agent 的知识获取管道没有它Agent 就像一个不看书却要答题的学生再聪明也白搭。这篇是“走进 AI Agent”系列的第四篇专门聊 RAG 的基础构建。我不打算堆概念而是把从零搭建过程中最关键的决策点、参数计算方式、踩过的坑一并讲清楚。适合正在做 Agent 开发、或者准备给业务系统接入知识库的工程师参考。看完你至少能明白为什么 Agent 需要 RAG、一条最小管道怎么搭、检索质量到底怎么量化以及最常见的翻车现场长什么样。1. 为什么 Agent 需要一条知识获取管道1.1 模型的记忆是有边界的先聊一个很朴素的观察。大语言模型的知识等于它训练时见过的数据。训练数据有截止日期模型不知道那之后发生的事情训练数据里没写你公司的内部规范它就不可能凭空“想”出来。就算你不停地追问它也只能靠上下文里的只言片语去猜猜错了就是一本正经地胡说八道。这不怪模型这是它的固有限制参数化记忆的容量和时效都有限。很多团队一开始试图靠换更大参数的模型来解决问题试过就知道参数再大也只是把“胡说的底气”变强了它依然不会因为说错了就自动去查资料。真正需要的是给模型一个“查资料”的渠道让它在回答之前先把相关知识检索出来再基于这些材料组织答案。1.2 这个管道要解决什么问题RAG 要解决的核心问题可以概括成一句话让生成过程有据可依。拆开看有四个层面。知识时效性模型不知道的最新政策、新闻、内部通知通过检索实时补上。私有知识接入企业文档、数据库、聊天记录里的私有信息不经训练也能被模型引用。可追溯性答案能关联到具体来源文档出了问题可以反查这在很多业务场景里是硬性要求。成本控制如果每次有新知识都重新微调模型成本和时间都受不了。RAG 只需要更新索引不用动模型权重。我常用一个类比模型像一位经验丰富的顾问但顾问不可能记住所有客户的陈年档案所以每次见客户前助手先把相关档案翻出来放在桌上。顾问只管读桌上的材料再结合自己的经验给出判断。这套“翻档案”的机制就是知识获取管道也就是 RAG。1.3 RAG 在 Agent 工作流里的位置到了 Agent 的语境里RAG 不是独立的功能它是 Agent 的一根“取水软管”。Agent 在执行任务时需要做规划、调用工具、与用户交互而 RAG 往往作为其中一个工具或环节被调用。比如用户问“今年公司的年假政策有没有调整”Agent 先判断这个问题需要查内部制度库于是触发检索工具拿到相关政策片段再整理成答复。这样做的好处是知识获取和推理决策解耦了。知识库更新不影响 Agent 的决策逻辑Agent 的行为调整也不依赖知识库的重训。在实际工程里这意味着一套知识库可以服务多个 Agent一个 Agent 也可以挂多个知识库。后面会讲到这种解耦也催生了更复杂的 Agentic RAG 模式但基础依然是这条管道本身。2. RAG 的三段式流程先把骨架搭对2.1 索引阶段从文档到向量RAG 的第一个阶段是离线准备也叫做索引。目标是把一堆原始文档PDF、Word、Markdown、HTML甚至数据库里的文本列转化成可以被快速检索的形式。流程大致是文档加载用不同的加载器把不同格式的文件读成纯文本。文本清洗去掉页眉页脚、乱码、多余换行保留正文内容。这一步最容易被忽略但直接影响后面的分块质量。分块Chunking把长文本切成合适大小的块。这是全文最影响效果的操作之一后面我会专门展开讲。向量化Embedding把每个文本块交给 embedding 模型变成一个固定维度的向量。存储把文本块、向量、元数据来源、页码、时间等一起存入向量数据库。这里有个常见的误解以为向量化是“理解语义”其实 embedding 模型只是把文本映射到高维空间里的一个点语义相近的文本在空间里距离更近。它不理解“意思”它只负责“摆位置”但这种位置关系已经足够支撑检索。2.2 检索阶段召回与排名第二个阶段是检索发生在用户提问之后。流程也比较固定把用户问题也做一次向量化。用问题向量去向量数据库里做相似度搜索找到最相近的一批文本块。按相似度分数排序返回 Top K 个结果。这里的核心是“相似度怎么算”。最常见的是余弦相似度公式是计算两个向量夹角的余弦值值越接近 1 表示方向越一致。也有的数据库默认用内积或欧氏距离转换关系需要注意否则会出现“分数明明很高但结果不相关”的错觉。只靠向量检索有一个天然短板它擅长找语义相近的内容但不擅长精确匹配。比如你搜产品编号“P-2024-071”向量检索很可能把它拆得七零八落。所以实践中经常用混合检索把向量检索和关键词检索BM25的结果合并再做一次融合排序。这一步能明显拉高检索质量。2.3 生成阶段让模型学会引用第三个阶段是生成也就是把检索到的文本块注入 LLM 的提示词里让模型基于这些材料作答。这一步的难点不在技术而在提示词设计。你需要明确告诉模型哪些是检索到的参考材料。如果材料里没有答案应该怎么回应说不知道而不是编。回答时是否要标注来源。回答风格是简洁还是要完整。我们常用的提示词骨架是这样的SYSTEM_PROMPT 你是一名企业内部知识助手。请仅根据以下检索到的参考资料回答用户问题。 要求 1. 如果参考资料中没有明确信息明确回答“资料库中未找到相关信息”不要编造。 2. 回答时尽量引用参考材料的原文表述但可以用自己的语言组织结构。 3. 在回答末尾列出引用的参考来源编号。 参考材料 {context} 用户问题{question} 注意生成阶段要处理一个隐性风险检索回来的内容可能包含与问题无关的噪声。如果草料里混入了错误信息模型很可能被带偏。这也是为什么提示词里的“边界声明”必不可少它在约束模型不要自由发挥。3. 实操一条最小 RAG 管道3.1 技术选型别一上来就上重武器我见过不少团队一聊 RAG 就先上分布式向量库加微服务结果业务还没跑通运维先被拖垮。起步阶段我建议按数据量和并发量选择方案而不是追求“大而全”。场景推荐方案理由个人项目/原型验证Chroma、FAISS轻量、零部署几百 MB 数据量完全够用中小团队业务系统PostgreSQL pgvector复用现有数据库少一个组件事务一致性好大规模生产环境Milvus、Qdrant支持分布式、高并发具备完整的索引管理和权限能力企业级统一知识平台Elasticsearch 向量插件天然支持混合检索适合已有 ES 的团队我个人的偏好是能用 PG 的地方尽量用 pgvector因为大多数业务数据本来就在关系型数据库里去掉额外的同步环节能少踩很多一致性的坑。向量数据库不是越多越好是越顺手越好。3.2 核心代码串联下面我用 Python 加 LangChain 写一个最小闭环方便你把整个流程感知一遍。这个版本省略了细节优化但完整跑通是没问题的。from langchain_community.document_loaders import TextLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_community.embeddings import HuggingFaceEmbeddings from langchain_community.vectorstores import Chroma from langchain.chains import RetrievalQA from langchain_openai import ChatOpenAI # 1. 加载文档 loader TextLoader(company_policy.md) documents loader.load() # 2. 分块先给一个相对保守的参数 text_splitter RecursiveCharacterTextSplitter( chunk_size500, # 每个块最多字符数 chunk_overlap50, # 相邻块的重叠字符数 separators[\n\n, \n, 。, , , , , , ], ) chunks text_splitter.split_documents(documents) # 3. 向量化中文场景选择 bge 系列比较稳 embeddings HuggingFaceEmbeddings( model_nameBAAI/bge-m3, model_kwargs{device: cpu}, encode_kwargs{normalize_embeddings: True}, ) # 4. 存入向量库 vectorstore Chroma.from_documents( documentschunks, embeddingembeddings, persist_directory./chroma_db, ) # 5. 构建检索问答链 qa_chain RetrievalQA.from_chain_type( llmChatOpenAI(modelgpt-4o-mini, temperature0.1), retrievervectorstore.as_retriever(search_kwargs{k: 4}), return_source_documentsTrue, ) # 6. 提问 result qa_chain.invoke({query: 今年的年假政策相比去年有什么变化}) print(result[result])这段代码每一行都有讲究。分块的参数不是拍脑袋定的是我测过一轮之后折中的结果细节在下一节讲。embedding 选择 bge-m3 是看重它对中文长文本的支持同时支持 8192 的输入长度做分块时不用频繁截断。temperature 设成 0.1 是为了让回答尽量稳定少一点幻想。3.3 关键参数怎么定参数调优是最能体现“做没做过”的分水岭。几个核心参数我挨个说说我的经验值。chunk_size块大小块太大单块包含的信息多检索精度会下降因为你可能只想要其中一句话却把整个段落拖回来噪声变大块太小上下文断裂模型很难抓住完整逻辑而且 embedding 对短文本的语义表达能力也弱。我实测下来中文场景下 300~600 字符是一个比较稳的区间。制度类文档用小一点技术手册用大一点灵活调整。chunk_overlap重叠长度不加重叠的话一句完整的话可能被拦腰截断前半句在一个块后半句在下一个块无论检索到哪一个都缺信息。加重叠就是让上下文平滑过渡。经验值是 chunk_size 的 10%~20%。我习惯按分隔符切完之后再看相邻块的结尾和开头是否语义完整不够就手动调大 overlap。top_k检索返回数量这个参数影响的是“给模型多少草料”。太少信息不足太多噪声和 Token 成本一起涨。我一般从 4 开始调。内部知识比较零散的时候调到 6~8如果是单篇长文档3~4 就够了。总的原则是以答案质量为基准不要一味地多给。embedding 模型对中文场景我推荐优先试 bge-m3 或 gte-Qwen2它们在中文语义匹配和长文本上的表现比很多英文模型好得多。OpenAI 的 text-embedding-3-small 也很好但如果你处理的是中文文档本地部署中文模型往往性价比更高。4. 检索质量怎么量化别只靠感觉4.1 用 Hit Rate 做核心指标很多团队优化 RAG 全凭感觉“感觉回答变好了”。但感觉会骗人尤其是你亲手搭完管道之后你天然会相信自己的系统。所以必须量化。最容易上手的指标是 Hit Rate也叫命中率。含义是对于一组测试问题系统检索出的 Top K 块里有多少比例的查询能找到正确的内容。我维护一个评估集格式类似[ { question: 年假天数跟工龄的对应关系是什么, expected_chunk_ids: [chunk_1024] }, { question: 报销审批需要哪几个环节, expected_chunk_ids: [chunk_2077, chunk_2078] } ]然后跑一个脚本把每个问题拿去检索检查返回结果里是否包含 expected_chunk_ids。算一下命中率改动任何参数之后重新跑直接看数字说话。这套流程看起来简单但它能帮你挡掉 80% 的“我感觉变好了”陷阱。4.2 用召回率纠正偏差单纯看 Hit Rate 也会漏一个问题如果问题是多块联合回答的只命中其中一个块答案还是不完整。这时候辅助看召回率也就是返回的块中正确的比例、以及所有正确块被找到的比例。这两个指标配合起来才能判断问题是出在“没找到”还是“找得不全”。比如 Hit Rate 很高但回答质量一般多半是“找到了一点但没找全”需要调大 chunk_size 或者把 overlap 提高如果 Hit Rate 本身低多半是切分策略有问题或 embedding 模型不适合当前文本类型。4.3 重排值得认真对待的下一步向量检索返回的 Top K本质上是“靠相似度排序谁离得近谁排前面”。但相似度高的块未必是最终答案的最佳素材因为生成阶段对上下文连贯性的要求比单纯相似度更复杂。解决方案是加一个重排模型Reranker。我用过 Cross-Encoder 类的排序模型比如 bge-reranker它把问题——文档块对一起送入模型直接输出相关性分数比单纯的向量距离更准。用法也很简单先用向量检索召回 Top 50 的候选块。再用重排模型从 50 个里挑出最相关的 Top 5 送入大模型。这个两段式结构能显著提升答案质量代价是多一次模型推理大约增加几十毫秒延迟对于多数内部工具场景可以接受。我的建议是先跑通了基础管线再上重排它适合做第二轮的提优而不是第一轮的救火。5. 常见问题与排查实录5.1 一张我自己的排查速查表我把这一年多攒下的 RAG 问题整理成了表格每次调不好就到表里定位。按被问到的频率排序以下都是实打实的坑。现象可能原因排查思路与解法回答与资料无关分块太小导致语义断裂调大 chunk_size检查分块后的文本是否语义完整检索结果有明显相关但排名靠后纯向量检索丢失关键词匹配切换混合检索融合 BM25 结果回答泛泛而谈没有细节上下文被截断或 K 值太小调大 top_k或检查 LLM 上下文窗口是否被其他系统提示占满模型拒答说找不到资料索引数据没更新或检索范围配置错误确认数据是否成功入库检查元数据过滤条件相似度分数高但内容牛头不对马嘴embedding 维度归一化设置不一致统一 normalize_embeddings检查距离度量和相似度换算关系同一问题每次答案不一样温度参数偏高或向量检索存在随机性调低 temperature固定随机种子数据库越来越大检索越来越慢缺索引或数据量过大检查向量索引类型考虑 HNSW 参数调优PDF 加载出来是乱码扫描件纯文本提取失效需要 OCR 前置处理文字版 PDF 直接提取则不需要带数字、型号的问题总找不对中文分词把连续字母数字拆断混合检索解决或在预处理阶段用正则保护编号引用来源不准确元数据缺失导致链路断裂入库时保证每个 chunk 都带 source、page、section 等元数据5.2 三个值得展开的经典案例第一个样式很典型的案例把公司员工手册整本丢进去之后问“休假审批流程”模型回答得还算顺畅但“病假要提交什么证明材料”这一类问题答案经常缺项。排查发现病假材料这段描述分散在三个不同的章节里而我们的 chunk_size 定得很小每个 chunk 恰好吃掉一小段按相似度召回时只捞回了其中两块另一块排在了 Top 10 之外。解法是调大 chunk_size 到 600同时让分块逻辑优先按章节边界切尽量别把完整的一句话拆开。改完再跑评估集Hit Rate 从 0.68 提到了 0.86。第二个案例是关于产品编号的。检索框里输入“型号 A1000 的功耗是多少”向量检索返回的全是散热设计相关的段落一个都没提到功耗参数。原因是模型把“功耗”和散热文本匹配上了而“A1000”这个编号本身在 embedding 里没有起到强约束。后来在 pipeline 里加了混合检索让 BM25 负责确保这个精确编号出现过的文本块不会漏掉结果很快就对了。这个方法对物料代码、ISBN、快递单号之类的隐含逻辑同样有效。第三个案例稍微绕一点我们最初把表格内容直接转成 Markdown 文本块遇到“3 年以上工龄享 15 天年假”这种规则检索没问题但模型会把“3 年以上”理解成“3年以上含3年”。要解决这种边界模糊需要给生成阶段补充一句话“请严格按资料原文的数字范围表达不要自行推断上下限”。要不要在系统提示里加入这种约束其实取决于你的业务是否涉及准确的数字规则如果涉及就是刚需。5.3 关于“更新的知识”这件事RAG 上线以后知识会持续更新。更新不及时Agent 就会拿旧的资料回复新问题这种错误比回答不出来更危险。我们处理更新的方式很简单每次文档变动记录文档版本和变更时间。入库时把文档版本写入元数据。用户提问时在生成阶段注入“参考资料的更新时间”并让模型在资料过时时提示。这里有一个细节不要频繁重建整个索引。企业知识库通常是增量更新不是推倒重来。每次更新时按文档级别的向量做增删比无脑全量重建高效得多系统稳定性也更好。6. 基础之外的演进方向这一篇讲的是 RAG 的基础但基础不是终点。当你把固定管道跑顺了自然而然会遇到几个新需求它们是 RAG 在 2026 年最常见的演进方向也跟 Agent 体系深度耦合Agentic RAG检索路径不再是固定的“提问——召回——生成”而是由 Agent 动态决定每个问题用什么姿势查。比如先问“你是在问政策还是问产品”再选择查询策略。Agentic RAG 适合复杂任务链但前提是基础 RAG 已经足够可控。GraphRAG传统 RAG 把文档切成一个个块缺少实体之间的关系。GraphRAG 在建索引阶段先抽取实体和关系把知识组织成图谱再联合检索文本块和图谱内容。它的优势在于回答需要跨多个实体联动的“关系型问题”比如“哪几个部门参与了报销流程的审批”。RAG as a Service把检索能力作为独立服务统一提供多个 Agent 和业务系统共享一套知识库。这个方向在工程上解决的是重复建设和知识孤岛的问题一套管道到处插拔。我对这些方向的建议是先别急着追新。GraphRAG 再漂亮你的基础管道如果分块没做好、命中率没量化好图谱也救不了。把基础做的能打再往上加东西才是稳的路子。最后说几句我自己的体会。RAG 这个技术入门门槛不高跑通 demo 用不了两小时但生产环境里的效果差距往往就在“细节”二字分块策略、embedding 选择、重排层、命中率度量、元数据设计每一项都有文章可做。与其追逐更花哨的框架和工具不如把自己的知识获取管道打磨成一条真正可维护、可度量、可追踪的管道。毕竟 Agent 的上限很大程度取决于它“能拿到什么”而 RAG 就是决定这件事的那根管道。
返回列表