☰
RAG从入门到实战:为AI Agent搭建知识获取管道
2026/9/29 18:50:47 网站建设 项目流程

最近在团队里搭一个客服Agent,第一个让人挠头的问题不是“该选哪个大模型”,而是:模型怎么知道我们公司那些散落在OA、wiki、产品文档里的知识?它连我们产品版本的上线日期都不知道,更别提帮用户判断工单应该走哪个流程了。这个问题的标准解法,就是给Agent装一条知识获取管道,也就是 RAG(Retrieval-Augmented Generation,检索增强生成)。

这一篇咱们就聊 RAG 的基础。不扯太深的技术框架,重点把“数据怎么进、知识怎么存、答案怎么出”这条链路讲透,并且给出一套你今晚就能动手试的最小实现。适合两类人:一类是刚开始搭 AI Agent 但被各种概念绕晕的,另一类是已经跑通了一个简单 RAG demo,但想知道每个环节为什么这么设计、参数到底该怎么调。Agent 的能力边界,很大程度上取决于它身后的知识管道挖得有多深,这一篇就是先把地基打清楚。

1. 为什么Agent需要RAG:让模型从“闭卷考试”变成“开卷考试”

1.1 模型参数里的知识是有保质期的

大模型训练完成之后,参数就固定了。这意味着它的知识是有“截止日期”的,训练语料里没有的、训练之后新产生的信息,它一概不知道。你问它“我们公司上季度的业绩目标是多少”,它只能基于训练数据里的零星信息去“猜”,猜错了就是一本正经地胡说八道。

更麻烦的是企业内部的知识,几乎不可能全部进入训练语料。产品文档、售后工单、内部 Wiki、数据库里的交易记录,这些数据要么是私有的,要么是在持续流动的,谁也不会为了一个通用模型把自己的业务数据拿去训练一遍,成本太高,也不安全。所以大模型天生就处理不了“专业领域 + 新鲜数据”这类问题。

我刚开始做Agent时,一度觉得“反正模型已经很聪明了,直接问它不就行了”。实际一测就发现,它只能回答那些在互联网上被反复讨论过的通用问题,一旦涉及自家业务的细节,就立刻开始编。这就是我为什么坚持要在Agent外面加一层知识获取管道的原因——模型参数是静态的,但我们需要的是能持续更新的知识。

1.2 RAG不是把文档塞进Prompt,而是“按需取用”

RAG 的核心思路听起来很简单:在模型生成回答之前,先从一个外部知识库里检索出与问题最相关的内容片段,再把这些片段和原始问题一起放进提示词里,让模型基于它们生成答案。简单说,就是“先查资料,再回答”。

但这里有个关键误解:它不是把整个知识库一股脑塞进提示词,而是“按需取用”。一个企业知识库可能几百个G,大模型上下文窗口再大也装不下,而且塞进去的有效信息密度极低,模型反而会看花眼。RAG 的正确做法是把知识做成可检索的索引,每当用户提问时,只捞出一小撮最相关的片段。这就像你去图书馆查资料,不是把整馆的书都搬到桌上,而是让图书管理员帮你把可能相关的几本书找出来,你再打开具体页面读。

对 Agent 来说,RAG 承担的是“知识获取管道”的角色。Agent 需要一个子模块,专门负责回答“这个问题的事实依据在哪里”。没有这个管道,Agent 就只能靠模型自身那点通用知识过活,像一个两耳不闻窗外事的研究员,看似聪明,其实一问业务就露馅。

1.3 Agent场景下的RAG,和普通问答机器人有什么不同

你可能已经见过不少基于 RAG 的用户问答系统,比如企业知识库助手。那种场景一般是“用户问一句,系统查一次,模型答一句”,链路是直的。但放进 Agent 里,事情会复杂一些。

Agent 往往需要多步推理。它可能先要理解用户的真实意图,再拆解成若干子任务,其中一个子任务需要查业务规则,另一个子任务需要查历史工单,最后还要把两个结果合并,才能给出完整回答。这意味着 RAG 的触发不是简单的“每问必查”,而是由 Agent 的工作流决定的。

另外,Agent 的上下文窗口更“贵”。它前面可能已经积累了好几轮对话、工具调用记录,如果再塞入一大堆检索结果,很容易把窗口撑爆,或者在关键信息上注意力不够。所以 Agent 里的 RAG 对检索数量、相关性阈值、结果去重都提出了更高要求。基础 RAG 虽然看起来“笨”,但它是所有扩展玩法的地基,把这个地基夯实了,后面做 Agentic RAG 之类的东西才不慌。

2. 一条RAG流程的四个核心环节:加载、切分、检索、生成

2.1 文档加载与清洗:上游水质决定下游口感

RAG 的第一步,是把零散资料变成可处理的文本。这个环节看起来最没技术含量,但我后来发现,它恰恰是决定整个项目上限的一步。你从 PDF 里抽出来的文字可能是乱序的,页眉页脚混在正文里,表格里的内容被生硬地拼成一段,这些问题如果不在入口处解决,后面检索质量再高也白搭。

我常用的做法是:能拿到 Markdown、TXT 这类结构化文本,就优先用;如果只有 PDF,需要用 PDF 解析库做两层处理——先是版面分析,把文本块按阅读顺序抽取出来,再针对表格单独处理;如果 PDF 是扫描件,还得加一层 OCR。市面上有很多现成解析器,比如 Unstructured、PyMuPDF,效果比直接用最原始的 pdf 文本抽取好得多。

还有一个容易被忽略的点:清洗元数据。每个文档应该保留标题、来源、更新时间、所属业务线等字段。这些字段在检索阶段用来过滤“过期版本”或者“某个业务线的文档”,能大幅减少无关内容的干扰。没有元数据的知识库就像没有目录的档案室,只能大海捞针。

2.2 文本切分:找对“知识单元”的颗粒度

加载进来的文档通常很长,不可能整篇丢进模型,所以要先切成小块,业界叫 chunk。切分粒度直接决定检索命中的质量。

如果切得太碎,一个完整的逻辑单元可能被腰斩,比如“先决条件”和“适用规则”被拆到两个块里,模型只检索到一半,回答自然残缺。如果切得太粗,一个块里塞了太多主题,向量化后的语义会被拉平,检索时很难精确命中问题对应的那一段。

具体参数上,我常用的是chunk_size=400~600字符,chunk_overlap=50~100字符。这个量级对中文文档比较保险,既保留了一定的上下文,又不至于让单个向量里的语义太嘈杂。字符数和 token 数不同,如果你用的是英文文本,token 和字符比例接近 1:4,可以把 chunk_size 放大一些。

切分策略也很有讲究。最简单的按字符数硬切,费钱还容易切坏语义;稍微好一点的是递归字符切分,它优先尊重段落和句子边界,让每个 chunk 至少从完整句子开始、在完整句子结束;更高级的是结合文档结构切分,比如根据 Markdown 标题、列表层级来切,保证每个 chunk 对应一个相对独立的知识点。我在企业内部文档上做过对比,按标题结构切分的命中率普遍比纯字符切分高 5~10 个百分点,因为文档本身的章节边界往往就是知识的天然边界。

2.3 向量化:让文本在数学空间里“可比较”

切好的文本片段需要转成向量,这一步靠 Embedding 模型完成。所谓向量,就是一组几百维的浮点数,它把文本的语义压缩成一个坐标。两个文本越相似,它们的向量在坐标系里就越接近。检索的本质,就是在向量空间里找出离问题向量最近的几个文本向量。

选 Embedding 模型时,我通常会看三件事:语言支持、向量维度、最大输入长度。如果你处理的主要是中文,尽量选中文语料预训练充分的模型,比如 BGE 系列、M3E 系列;如果中英混合,需要确认模型对中英文对齐的处理能力。向量维度代表信息容量,但也不是越高越好,维度高意味着存储开销和计算成本都变大,适合你的才是最好的。

这里有个特别容易踩的坑:当你切换 Embedding 模型时,之前生成的索引数据全部作废,必须重新把所有文档再过一遍向量化。因为不同模型生成的向量空间不同,新旧向量不能混用。我见过有人换了模型但不重建索引,结果检索出来的东西驴唇不对马嘴,查了半天才发现是这个问题。

2.4 检索与重排序:先召回,再精排

检索阶段的目标,不是“一口气找到最准的那段”,而是“先把可能相关的都捞出来”。这背后是召回和排序两步骤逻辑。

召回的常用手段有三种:纯向量检索、纯关键词检索、混合检索。向量检索擅长语义相似,比如“报销流程”和“费用怎么申请”这种字面差异大但意思相关的匹配;关键词检索(BM25)擅长精确匹配,比如文档编号、产品型号这种专有名词。实际项目中,混合检索往往效果更稳,因为它两边都不会漏。

召回之后还要排序。向量检索返回的“相似度最高”,并不一定就是用户最想要的那段。比如问题里包含“流程”二字,索引里所有提到“流程”的段落都可能排在前面,但真正和本场景相关的可能排在第五。所以现在很多 RAG 系统会加一个 rerank(重排序)环节,用一个更强的模型把召回结果重新打分,再取前 3~5 个最重要的片段。第一次做 RAG 的人容易把 k 值调太大,比如取 10 个片段,结果上下文里全是背景噪音,模型反而找不到重点。我一般建议召回多一些、精排后再只取 top_k=3~5 个片段,效果通常更干净。

2.5 生成:把检索结果变成有根据的回答

最后一个环节是把检索到的片段和用户问题一起交给大模型。很多人以为这步最省事,其实提示词的写法很影响回答质量。

我常用的生成提示词大概长这样:

你是一名企业知识助手。请基于以下参考资料回答用户问题。 参考资料: {检索到的片段,用来源标签区分} 要求: 1. 如果参考资料足以回答,请直接给出答案,并尽量引用对应的来源标签。 2. 如果参考资料不足以回答,请明确说“资料中没有找到相关信息”,不要编造。 3. 回答保持简洁,不要展开与问题无关的内容。

注意这里有一个容易被忽略的点:模型默认有一种“讨好用户”的倾向,你给它的检索结果越模糊,它就越倾向于编个像样的答案糊弄你。所以在生成阶段,要在提示词里反复强调“不知道就说不知道”,允许模型承认信息不足。这一步做好,能治住不少幻觉问题。

另外,生成温度参数记得调低。RAG 的本质是“用外部事实约束模型”,温度太高等于自己破坏约束。我日常会设为 0.2 左右,既保留一点组织语言的空间,又不至于发散跑偏。

3. 搭一条最小RAG流程的实操记录

3.1 技术选型:别一上来就图大而全

很多人一听到 RAG,下意识就想去搭一个分布式向量数据库。其实最小可行版本,完全可以用轻量方案起步。

我这里给一个我的选型经验表:

场景推荐方案原因
学习原理、快速验证Chroma 或 FAISS本地零配置,代码量少
个人项目、几千条文档SQLite + 简单向量扩展易维护,备份方便
团队协作、十万级以上文档Qdrant 或 Milvus支持分布式、过滤、高并发
已经用了 LangChain直接用它封装的向量库接口降低切换成本

Embedding 模型方面,中文场景我推荐从BAAI/bge-small-zh-v1.5起步,模型体积小,效果不错。等流程跑通了,再根据命中率评估要不要换更大的模型。大语言模型部分,你可以用任何你常用的模型接口,也可以换成本地部署的开源模型。RAG 的链路是模型无关的,这一步不用太纠结。

3.2 环境准备和数据样本

我建议用 Python 3.10 以上环境,跑下面几条命令安装基础依赖:

pip install langchain pip install langchain-community pip install chromadb pip install sentence-transformers

然后准备一份测试文档,比如就叫agent_basics.md,里面写几句关于 Agent 和 RAG 的说明,内容不需要长,但要有几个明显不同的知识点,方便测检索。比如:

# Agent基础 AI Agent 是一个能够感知环境、做出决策并执行动作的智能体。 它通常由大模型、规划模块、工具调用模块和记忆模块组成。 # RAG基础 RAG(检索增强生成)是一种让模型在生成前检索外部知识的技术。 它解决的是模型参数中知识过时、私有数据缺失的问题。 RAG 流程包括文档切分、向量化、检索和生成四个环节。

3.3 最小RAG代码:从文档到答案

下面这段代码是基于 LangChain 社区版写的一个最小链路,注释里解释了每一步在干什么:

from langchain_community.document_loaders import TextLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.embeddings import HuggingFaceEmbeddings from langchain_community.vectorstores import Chroma # 1. 加载文档 loader = TextLoader("agent_basics.md", encoding="utf-8") documents = loader.load() # 2. 切分:400字符左右一块,重叠80字符 splitter = RecursiveCharacterTextSplitter( chunk_size=400, chunk_overlap=80, separators=["\n\n", "\n", "。", "!", "?", " "] ) chunks = splitter.split_documents(documents) # 3. 初始化Embedding模型并写入向量库 embedding = HuggingFaceEmbeddings(model_name="BAAI/bge-small-zh-v1.5") vectorstore = Chroma.from_documents(chunks, embedding) # 4. 构造检索器 retriever = vectorstore.as_retriever(search_kwargs={"k": 3}) # 5. 测试检索 query = "什么是RAG?" relevant_docs = retriever.invoke(query) for doc in relevant_docs: print("得分候选片段:", doc.page_content) print("---")

跑完这一步,你会看到检索器返回了和“什么是 RAG”相关的几个文本块。如果文档切分正常,它应该优先返回包含“RAG(检索增强生成)”的那一段。接下来再把这些片段拼进提示词,调用大模型生成答案:

from openai import OpenAI client = OpenAI() # 这里换成你自己的模型访问配置 context = "\n".join( f"[片段{i+1}] {doc.page_content}" for i, doc in enumerate(relevant_docs) ) response = client.chat.completions.create( model="gpt-4o-mini", temperature=0.2, messages=[ {"role": "system", "content": "你是知识库助手,只能基于参考资料回答,不要编造。"}, {"role": "user", "content": f"参考资料如下:\n{context}\n\n用户问题:{query}"} ] ) print(response.choices[0].message.content)

这段代码里,OpenAI 客户端可以是你用的任何兼容接口。实际项目里,我会把“检索”和“生成”封装成两个函数,方便 Agent 在规划阶段调用。比如一个函数叫search_knowledge(query),另一个叫answer_with_knowledge(query, docs),这样后续做 Agentic RAG 时,Agent 可以直接调用前者去拿原始材料,而不是每次都走完整套生成流程。

3.4 参数怎么调:chunk_size、top_k 和温度

参数调整是最容易被新手忽略的部分,因为代码不报错,但效果千差万别。我的习惯是每调一个参数,就对着一个固定测试集跑一遍,人工看回答质量。

chunk_size:如果你发现很多问题命中了片段,但片段内容“只有一半”,说明 chunk_size 太小或 overlap 不够;如果你发现一个片段里掺杂了好几个主题,检索结果“沾边但不精准”,说明 chunk_size 太大,需要切小。

top_k:如果回答显得啰嗦、抓不住重点,大概率是 top_k 太大。3~5 是常见合理区间。如果问题比较复杂,需要交叉比对多个来源,再适当增大。

temperature:前面说过,RAG 场景通常设低一点。但如果你发现答案太生硬、像在复读材料,可以适度提到 0.3~0.4。这个值是经验值,没有绝对标准。

还有一个我常用的方法是看“检索到的片段原文”,先不看生成结果。如果片段本身就对不上问题,那问题出在前面链路;如果片段对上了但模型答错,才需要考虑换模型或调提示词。这样能快速定位瓶颈在哪一段,而不是盲目整体调参。

4. Agent场景里RAG最容易踩的几个坑

4.1 检索结果污染上下文,模型反而变笨了

我在最初跑通 RAG 后兴冲冲地接入 Agent,结果发现带检索的 Agent 有时候比不带检索的还笨。原因很简单:检索出来的片段里有大量“看起来相关但实际无关”的内容,模型被这些噪音带偏了。

尤其是企业知识库里经常存在多个版本的文档,比如“报销制度(2023版)”“报销制度(2024版)”,如果你检索时只看语义相似度,2023 版和 2024 版都会排到前面,模型分不清哪个是最新规定,回答就乱了。

应对方法有两个。第一是在向量库里附带文档版本号和生效日期,检索时过滤掉非当前版本;第二是在提示词里明确要求“如果多个片段存在冲突,优先采用元数据标明的最近更新版本;如果资料中没有明确信息,请直接说明”。前者靠过滤,后者靠提示词约束,两个都做才稳。

4.2 切分把完整逻辑“腰斩”了,回答残缺

前面说过切分粒度问题,这里举个真实例子。我有一份“客服处理流程”的文档,里面写着“如果订单已发货,用户申请退款,需要联系仓库确认拦截。”因为这句话正好跨了段落,被切成了两个 chunk:一个 chunk 只有“如果订单已发货”,另一个 chunk 只有“用户申请退款需要联系仓库”。检索到第一个 chunk 的 Agent,回答直接变成了“订单已发货就不能退款”——完全反了。

后来我把 separator 里加入中文句号、感叹号等标点,并且加大 overlap,才缓解了这个问题。更保险的做法是切分后自动检查,凡是 chunk 的开头或结尾不是完整句子,就往前/往后多取一个句子边界。这件事看起来小,却直接影响回答的准确性。

4.3 用户问得越自然,检索越容易失配

向量检索擅长语义匹配,但用户在实际对话里往往说得非常口语化。比如知识库里写着“差旅费报销需要提交发票”,用户可能会问“我上周出差打车,发票微信里的行不行?”这种问法在向量空间里未必能找到最接近的片段。

面对这种情况,基础 RAG 很无力。我现在的做法是做查询改写:先让模型把用户口语化的问题改写成适合检索的语句,比如提炼出“差旅费 报销 发票 微信”这样的关键词组合,再拿改写结果去检索。更进一步,可以让 Agent 自己判断要不要拆分成多个子查询,从不同角度检索后合并结果。这就是 Agentic RAG 的雏形了,但在那之前,至少先把查询改写这步加上,检索命中率会有一个肉眼可见的提升。

4.4 知识库更新后,旧向量像“幽灵”一样冒出来

还有一个很容易被忽略的坑:向量库里的数据不是改了就生效的。你直接删掉源文档或修改了源文档,但如果不同时删除或更新对应的向量记录,检索结果里依旧会出现旧内容。我见过有人更新了制度文档,但 Agent 依然引用旧条款,就是因为只更新了原始文件,忘了重建索引。

用 Chroma 这类本地库时,如果文档变化较大,我一般直接删除整个 collection 再重新写入。生产环境则要建立数据版本管理,每次入库一批文档,打一个版本标签;Agent 查询时默认只检索最新版本的数据。否则,旧知识“幽灵”会在不经意间冒出来,而 Agent 还会一本正经地给你引用来源,让你很难察觉。

5. 基础RAG之上的三个扩展方向

5.1 Agentic RAG:把检索交给Agent自己调度

基础 RAG 的链路是“一次问答,一次检索”。但真实业务问题往往没这么简单。一个用户问“发票丢了还能报销吗”,背后至少涉及两个子问题:报销制度里对发票的要求、有没有特殊情况处理流程。如果只检索一次,很容易漏掉其中一个关键知识点。

Agentic RAG 的思路是:让 Agent 把主问题拆成几个检索诉求,逐个去查,拿到结果后再综合分析回答。它不再是一个被动的“检索-生成”过程,而是由 Agent 自己决定“查什么、查几次、什么时候查”。这要求你给 Agent 暴露一个检索工具接口,并让它具备规划和汇总能力。这个方向很有意思,也是目前企业落地的大趋势之一,后续值得单独写一篇。

5.2 GraphRAG与Ontology RAG:用结构对抗碎片化

向量检索擅长处理“非结构化文档”,但对“多跳关系”类问题比较吃力。比如“哪些项目使用了A组件,而这些项目的负责人分别是谁”,这种问题需要跨多个文档做关系推理,纯向量检索很难一次命中。

GraphRAG 的思路是:先对文档做实体和关系抽取,构建成知识图谱,再在问答时沿着实体关系访问知识。Ontology RAG 类似,但更强调用本体定义概念、属性、上下位关系。这两者都适合知识关联性强、需要反复多跳查询的企业场景。问题是构建和维护成本都不低,基础 RAG 还没跑通阶段别急着上,先把管线走顺,再考虑要不要引入图。

5.3 多知识库路由:让正确的知识流向正确的地方

当企业的知识库越来越庞大,把它塞进单一向量库会造成严重的相互干扰。产品文档、财务制度、售后工单的语义空间差异很大,混在一起检索时,一个财务问题可能会召回几条产品宣传文案,模型再强也会被带偏。

我现在的做法是分库管理:按业务线建多个向量库,路由模块先判断问题属于哪个领域,再定向检索。这一步可以靠规则匹配,也可以用模型分类。路由是 Agent 工作流里的重要决策节点,做好之后,各个知识库的“纯度”会明显提升,检索命中率也更稳定。

最后说一点个人体会。RAG 这段路我走过不少弯路,最开始以为难点在模型和框架上,后来发现能让你翻车的全是数据质量、切分策略、参数匹配这些基础环节。如果你正准备从零搭 Agent,建议先把这条最小管道认真跑通,反复把“检索片段”和“最终答案”对照着看,直到你对每一个环节的脾气都摸清楚了,再想着上什么花活。地基稳了,后面长出来的东西才不会歪。

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

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

立即咨询