RAG与Agent实战:从手写检索到智能体设计的完整学习路线 最近好多朋友问我同一个问题AI大模型现在这么火RAG和Agent到底该怎么学市面上的教程要么是翻译文档堆砌概念要么是几个demo跑完就没了下文学完之后连一个像样的项目都搭不起来。我断断续续折腾了快一年踩了不少坑也有了一些自己的心得。这篇内容不整虚的就是把我实际做过的项目拆开讲RAG怎么搭、代码怎么写、Agent怎么设计、两者怎么结合以及我在实操中反复踩过的那些坑。如果你是刚入门、想系统掌握大模型应用开发的人这篇可以直接作为你的第一份实操地图。1. 先把RAG和Agent这两件事彻底搞清楚1.1 RAG到底解决什么问题RAG检索增强生成本质就是给大模型外挂一个可查的资料库。为什么需要它因为大模型的知识是训练时定格的它不知道你私有文档里的内容也不知道最新发生的事。你直接问它我们公司上季度的销售数据是多少它只能瞎编。RAG的思路很朴素先把你的文档切块、向量化、存起来用户提问时先从这个库里检索出最相关的几段内容再把问题和这些内容一起丢给大模型生成答案。这样一来回答就有了依据幻觉大幅减少。学习RAG最忌讳的是把它当成一个调API拼流程的活儿。很多教程会给你一段LangChain代码跑通了就以为学会了。但真实的RAG系统难点根本不在拼接上而在检索质量上。你检索进来的内容到底准不准、全不全直接决定最终答案的质量。这个道理我在后面会反复提到。1.2 Agent到底在做什么Agent智能体你可以把它理解为一个会自己做计划、会调用工具、能根据结果调整行动的编程框架。它不像普通程序那样按固定路线走而是有一个大模型作为大脑来决策先判断当前任务需要哪些步骤再调用对应的工具比如搜索、计算器、内部API观察结果然后决定下一步做什么。比如你让它帮我查一下这几家供应商的报价整理成对比表格发到群里它就得规划出搜索报价→解析页面→汇总对比→生成表格→发送消息这一串动作再一步步执行。RAG和Agent的关系不是二选一而是互补。RAG管知识Agent管行动。更准确地说Agent完全可以把RAG当作它调用的一种工具——当它需要查资料时就触发一个RAG检索流程。所以你现在看到的主流应用比如企业内部知识问答助手、自动生成分析报告的系统基本都是RAG与Agent的混合体。1.3 学习路线别走反了我见过太多人一上来就学LangChain、AutoGen这些框架结果连Embedding是什么都没搞清楚。我的建议是先手写一个最简RAG用原生Python和OpenAI接口把每个环节都跑通再去看框架。框架只是帮你省事的封装如果你不理解底层的索引结构、检索逻辑一旦出问题就是两眼一抹黑。学习顺序上可以分四步走第一步掌握大模型API调用、Prompt编写、Embedding和向量数据库这些基础第二步手写最小可用RAG理解切分、向量化、检索、合成的完整链路第三步接触主流的RAG框架和高级技巧比如多路召回、重排序、查询改写第四步学习Agent设计模式包括规划、工具调用、记忆、反思最后把RAG作为Agent的一项能力整合进去。这个过程走下来你才算真正迈进了大模型应用开发的门。2. 手写一个最小可用的RAG系统2.1 从文档到向量的完整流程我先说最基础的文本RAG不涉及复杂的OCR或多模态。整个链路就四步加载文档、切分成块、向量化存库、检索合成。听起来简单但每一步都有细节。加载文档不多说PDF、Word、Markdown各有各的解析库。重点是切分。切分策略直接决定检索效果。我踩过的最大的坑就是按固定长度切分——比如每500个字符一刀切。这样看着数量整齐实际会把一个完整的知识点拦腰截断。比如一段关于退货政策的文字被切成两半一半讲7天内可以退货另一半讲需要保留原包装用户问退货条件时系统只能检索到其中一半回答自然残缺。更合理的做法是按语义边界切分。最简单的方式是按段落切分如果段落太长再细分稍微复杂一点可以用递归字符切分先按段落切段落超长再按句子切句子还长再按固定长度兜底。切分时还要设置重叠overlap让相邻块之间保留一部分重叠内容避免边界信息丢失。向量化这一步关键是选Embedding模型。中文领域几个主流的开源模型效果都还不错比如BGE系列、M3E系列也有不少商用接口。选模型时不要只看榜单分数要在你自己的语料上测。我的经验是拿10个有代表性的问题在候选模型下各跑一遍检索直接看召回的结果是不是你想要的这比看任何评测指标都实在。向量数据库选型方面如果只是学习和原型验证用Chroma或FAISS就够了轻量、无需额外部署。等数据量到百万级以上或者有高并发生产需求再考虑专门的向量数据库服务。不用在选型上纠结太久原理都是向量索引无非是ANN算法和工程部署的差异。2.2 检索环节的三个层次很多初级教程把检索简化成算向量相似度取TopK。真实项目里这个逻辑只配做个底层不能直接用。我建议至少分三个层次来搭检索。第一个层次是关键词检索。别小看它很多场景下精确匹配比语义匹配更可靠。比如查订单编号A12345这种强标识信息向量检索往往不如精确匹配。BM25这种经典算法直到今天仍在发挥重要作用。所以生产级系统通常都会保留全文检索通道。第二个层次是向量语义检索。这用于解决词不达意但意思相近的情况比如用户问手机坏了想退文档里写的是产品支持七天无理由退货关键词不重合但语义可以匹配。第三个层次是混合检索加上重排序。把关键词和向量的结果各取一部分合并去重然后用一个交叉编码器重排序模型对候选结果逐条打分。为什么需要重排序因为向量召回阶段的打分是粗略的向量表示是压缩过的会损失很多细节。交叉编码器直接把查询和文档拼接起来输入模型可以更精确地判断相关性。我实测过加一层重排序之后答案质量有明显提升这是投入产出比极高的一步。还有几个检索细节值得注意。一是TopK不要设太大通常取5~10条就够了太多会把无关内容塞进上下文反而干扰大模型。二是每条检索结果最好带上来源或章节标题传给大模型的同时也让回答能引用出处。三是用户提问方式千奇百怪有的问题太短、指代不明需要先做查询改写比如用户问刚才说的那个方案预算多少得先结合上下文把那个方案补全成具体名称再进入检索。2.3 把检索结果交给大模型去生成检索做完了生成也有讲究。把背景块拼接进Prompt时不是简单堆在一起就完事。我的写法是先给系统设定角色和任务规则再给一段以下是从知识库中检索到的参考内容里面按顺序列出检索块及其来源最后才是用户问题并明确要求如果参考内容中没有答案直接说不知道不要编造。这个允许说不知道的指令非常重要。很多团队为了提高指标舍不得让模型认怂结果就是模型强行从无关内容里编答案知识库问答变成幻觉重灾区。我宁愿它说不知道再给用户一个帮你转人工/重新表述问题的引导长期使用体验远好过硬答。在实际生成时温度参数可以调低一点0.1到0.3之间让输出更保守、更忠实于检索内容。如果业务需要让回答更有总结性和条理性可以提示模型按核心结论依据细节的结构组织回答。另外如果你的RAG系统需要面对多轮对话还要考虑对历史的压缩管理不能无限把历史对话都塞进去要提取要点或滑动窗口保留最近几轮。2.4 最小代码骨架参考下面的示例用Python写了一个最简流程不依赖重型框架方便你对照理解。这个代码我实际跑过逻辑上没有坑但你需要自己准备API密钥和向量库路径。from openai import OpenAI import chromadb from chromadb.utils import embedding_functions client OpenAI(api_keyyour-key) # 1. 切分文档按段落切超长再按句子补一刀 def split_text(text, max_len600): paragraphs [p.strip() for p in text.split(\n) if p.strip()] chunks [] for para in paragraphs: if len(para) max_len: chunks.append(para) else: # 简单按句号切分后按长度合并这里略 chunks.extend(split_by_sentence(para, max_len)) return chunks # 2. 向量化并入库 chroma_client chromadb.PersistentClient(path./demo_db) embed_fn embedding_functions.OpenAIEmbeddingFunction( api_keyyour-key, model_nametext-embedding-ada-002) collection chroma_client.get_or_create_collection( nameknowledge, embedding_functionembed_fn) docs [这是你的文档内容...] # 从文件读取 chunks split_text(\n.join(docs)) ids [fchunk_{i} for i in range(len(chunks))] collection.add(idsids, documentschunks) # 3. 检索 query 你的问题 results collection.query(query_texts[query], n_results5) retrieved results[documents][0] # 4. 合成 context \n\n.join(retrieved) prompt f你是知识库问答助手。请根据参考内容回答问题。 参考内容 {context} 问题{query} 如果参考内容中没有答案请直接说“不知道”。 resp client.chat.completions.create( modelgpt-4o-mini, messages[{role: user, content: prompt}], temperature0.2, ) print(resp.choices[0].message.content)这个代码虽然简单但已经是一个完整的最小RAG。你可以在它之上逐步增加关键词检索、重排序、查询改写每个功能怎么加、加在哪里这个代码会给你很清楚的参照。3. 引入Agent从被动回答到主动行动3.1 Agent的四个核心组件如果你已经有一个能回答问题的RAG系统下一步就是让它动起来——从一个被动应答的接口变成一个能按目标拆解任务、主动调工具的执行体。Agent的设计万变不离其宗核心就四个组件规划、工具、记忆、反思。规划就是让大模型把复杂任务拆解成多个步骤。最简单的实现是让模型输出一个JSON数组里面包含步骤名称、工具名、参数。复杂一点就用思维链、思维树、或者先把任务分解成子任务再逐个解决。工具是Agent可以调用的函数每一个工具都要有明确的名称、描述、参数schema让模型能理解什么情况该调用什么工具、传什么参数。记忆分短期和长期。短期记忆就是当前任务的上下文通常以对话历史形式保存在上下文窗口里。长期记忆则用来存跨会话的知识比如用户偏好、历史结论通常存到向量数据库或普通数据库中。反思则是让Agent在行动后评估结果是否符合预期如果不符合就调整计划重来。这是当前最聪明的应用都会用到的环节。3.2 用ReAct模式做一个会行动的AgentReAct是目前最成熟的Agent模式核心循环是思考→行动→观察→再思考。你可以用最原始的方式实现它写一个循环每次给模型一个当前任务、历史步骤、工具列表及描述让模型输出下一动作是调用工具还是给出最终答案。如果是工具调用程序就执行对应的函数把结果返回给模型然后进入下一轮循环。我来给你一个简化的实现思路。假设你有两个工具search_knowledge_base用RAG查资料和calculate执行四则运算。核心代码结构如下tools [ {name: search_knowledge_base, description: 查询企业内部知识库输入为问题字符串返回参考文档片段。, parameters: {...}}, {name: calculate, description: 执行数学计算输入为表达式字符串返回计算结果。, parameters: {...}}, ] def call_llm(messages): # 调用大模型返回文本 pass def execute_tool(name, arguments): if name search_knowledge_base: return rag_search(arguments[query]) elif name calculate: return eval_safe(arguments[expression]) return 未知工具 def run_agent(task): messages [{role: user, content: f任务{task}\n\n你可以使用以下工具{tools}}] for _ in range(10): # 限制最大轮次防止死循环 response call_llm(messages) messages.append({role: assistant, content: response}) if response.strip().startswith(FINAL:): return response.replace(FINAL:, ).strip() # 假设模型输出格式为 ACTION: tool_name\nARGS: {...} action parse_action(response) result execute_tool(action[name], action[args]) messages.append({role: user, content: f工具返回{result}}) return 执行超时注意我把Agent的思考过程也放进了消息历史里这相当于让模型看着自己是怎么想的。实践中有个好技巧不需要单独记录思考和行动只要把文本反馈给模型它就能保持上下文连贯。真实生产中的Agent会比这复杂得多要处理工具调用失败、要做并发子任务、要维护更长的记忆。但理解这个循环之后你用任何Agent框架都能快速上手因为框架本质上还是这个循环的封装。3.3 给Agent配上RAG工具现在把RAG和Agent真正结合到一起。你在Agent的工具列表里注册一个search_knowledge_baseAgent面对用户的任务时会根据需要自主决定是否调用RAG检索以及检索几次。这就比硬编码流程灵活得多。举个实际的例子。用户问帮我把研发部关于网关的新方案整理成摘要同时核算一下如果采购量翻倍预算会变成多少 这个任务涉及两步先要检索网关新方案的资料再根据资料里的单价算预算。一个纯RAG系统很难直接完成这种复合任务而Agent可以先规划出两个步骤调用RAG拿到方案与报价再调用计算工具算出结果最后汇总成摘要返回。在实现这个组合时有个关键点RAG工具的描述要写清楚它的能力和边界。比如支持检索企业内部知识库适合查询政策、流程、方案等文档内容不适合查询实时数据实时数据请使用另一个工具。这样模型才不会拿着RAG去问今天天气怎么样。另一个容易忽略的点是RAG检索返回的内容可能很长Agent在二次处理时容易丢失细节。我的做法是在RAG工具内部就先做一次提炼——把检索结果传给一个轻量大模型让它先提取与查询相关的关键信息再返给Agent。这样既控制上下文长度又减少噪声。3.4 实战里Agent的规划质量与稳定性大模型做规划效果其实波动很大。同一个任务换一个模型、换一种Prompt措辞拆解质量就明显不同。我见过不少项目死在规划能力不稳上。提升稳定性有几个我反复验证的有效手段。第一个是给模型提供任务模板。与其让模型完全自由发挥拆解任务不如允许它从一系列预设流程中选择。例如系统支持检索并总结型任务和对比分析型任务两类每种都定义好了默认步骤。模型只需选择模板并填充参数这能大幅减少计划乱跳的情况。第二个是对决策结果做结构化和校验。让模型以JSON格式输出计划并用程序校验工具名是否存在、参数是否齐全、类型是否正确不合法就要求重新生成。这一步可以拦住很多低级错误。第三个是限制每条工具链路的执行深度和总轮数。比如设定最多调用6次工具、单次执行最长30秒。这不是偷懒而是防止模型陷入循环检索或无限计算的陷阱。用户可等不了你转10圈。4. 实际项目中的工程化改造4.1 评估RAG质量不能靠感觉很多团队做RAG验收方式是感觉回答得还行。这在学习阶段没问题但一旦要上线必须建立客观的评估集。我在自己的项目里维护了一个包含300条问题的评测集每条问题标注了标准答案和它对应的知识库段落ID。每次修改切分策略、换Embedding模型或调Prompt我都用这个集跑一遍看召回率、正确答案匹配率和无答案时的拒答率变化。构建评估集比你想象中重要得多。没有它你做的所有优化都是瞎调。哪怕刚开始只有30条问题也没关系先建立基线再慢慢扩充。具体操作上可以先用线上日志收集真实用户提问再人工标注标准答案。标准答案不需要是字字精确但至少要列出回答应有的几个关键要点评分时看要点覆盖度。4.2 切分与索引的进阶设置接着前面的切分话题我再补充两个工程化细节。第一个是切分块大小。块太大信息多但噪声大占用上下文窗口也多块太小上下文语义不完整检索更精确但合成时可能缺上下文。我在多个项目里调试出的经验值是中文场景下200到600字之间比较平衡具体要结合你的文档类型微调。常见文档类型、说明文档、合同条款适合偏小的块研究报告、长篇小说这类连贯性强的则适合偏大的块。第二个细节是保留文档结构与元数据。切分时不要只保存文本还要记录这个块来自哪个文件、哪个章节、处于什么层级。这样在检索时可以按文档范围过滤比如只看合同类文档也可以在回答时提供精确出处。Chroma支持在添加向量时附带metadata把文件名、页码、章节存进去检索结果里自然带出来使用成本很低收益却很大。4.3 多用户与权限过滤的现实问题如果你做的是企业内部助手权限过滤早晚会出现在需求里。最忌讳的做法是把所有用户的文档都放进一个向量库然后指望提示词让模型不要回答无权访问的内容。模型没有这个分辨能力——内容已经进入了它的上下文它就默认可以用。正确的做法是在检索阶段就做权限过滤。具体来说为每条文档或每个块打上允许访问的角色或部门标签检索时在向量查询中带上用户的权限条件只对允许的集合做相似度计算。实现层面如果你的数据量不大可以给每个权限组一个独立的Collection检索时选择对应的Collection如果数据量大、权限复杂就需要在存储层面维护权限字段并在查询时作为过滤条件向量数据库大多支持带过滤的搜索。这个环节还涉及账号体系的打通、文档标签的自动化标注等问题工程量大。我的建议是如果权限需求复杂先不要追求一次到位遵循先做到文档级权限控制再细化到块级。文档级权限意味着用户能搜到的就是他有权限读的那个文档的内容块这已经能解决绝大多数问题。4.4 LangChain等框架到底怎么用LangChain、LlamaIndex、Haystack这些框架我在学习阶段并不建议先碰。但当你理解了底层原理之后框架确实能帮你提升效率。LangChain的强项在于内置了超多文档加载器、切分器、检索器和Agent工具生态庞大。缺点也明显抽象层级多报错信息绕版本升级快网上教程经常对不上号。我的用法是把框架当作工具箱而不是运行时底座。需要处理多种文档格式时用它的加载器需要快速接入重排序模型时用它的检索器封装需要快速搭建Agent工具循环时参考它的实现思路但我还是倾向于在关键路径上写自己的代码。这样最核心的逻辑始终在你的掌控中问题好排查不会被框架的复杂抽象带偏。如果你更喜欢LlamaIndex或Haystack也没问题关键是理解你使用的每个组件在链条中扮演的角色。框架只是加速器不是学习替代品。5. 常见问题与排查技巧实录5.1 为什么检索结果总是不准这是我被问得最多的问题。用户问XX产品的保修政策检索回来的却是XX产品的技术规格。排查步骤如下先看召回的前几条内容到底相不相关。如果Top1都不相关问题出在Embedding模型或切分策略上如果Top3里有一条相关但排在后面问题出在重排序或TopK设置如果相关问题都在、但生成的答案还是不对那问题出在生成环节的Prompt或上下文组织上。定位之后常用的修复手段换成更合适的中文Embedding模型调整切分块大小增加查询改写把短查询扩写成带上下文的完整问法把标题和摘要一并存入向量元数据提高语义命中率。记住一条原则先看检索结果再调Prompt。不要一上来就调Prompt你在跟一个没查到正确资料的系统使劲纯属白费。5.2 Agent工具调用陷入死循环Agent在一次任务里翻来覆去地调用同一个工具每次都拿回相同的失败信息然后换个措辞再调一次。这种问题很典型。我在自己的Agent里加入了这样的防护机制同一个工具连续调用超过3次就强制返回失败并给模型一个指令请换一个策略或直接回答无法完成。另外我要求工具返回结果里自带结果状态字段如果工具明确返回未找到相关结果模型就应该停止检索而不是继续换关键字再试。还有一种情况是模型在计划中引用了不存在的工具或者参数类型传错了。这种我靠JSON Schema校验自动重试一次来解决。重试时把校验错误信息反馈给模型多数情况下模型都能自我纠正。5.3 上下文窗口撑爆与费用飙升RAG和Agent都会占用大量Token。我曾遇到一个Agent任务单次执行花了十几万Token一半消耗在反复把长文档塞进上下文里。后来我做了三件事一是增加上下文压缩RAG检索结果先经摘要模型压缩到指定长度二是Agent内部使用短期记忆裁剪只保留最近的3轮思考与行动记录三是给每个工具设定最大返回长度超长结果截断后只保留关键部分。成本这块纯RAG的费用相对可控Agent因为需要多轮模型调用费用会成倍增加。建议在做Agent之前先评估任务的每一步是否真的需要大模型决策固定的流程用代码写死只在需要判断时调用模型成本能降一个量级。很多人为了让系统显得智能把简单的流程也交给Agent自由发挥其实得不偿失。5.4 多轮对话中检索上下文错乱用户说刚才那个呢换一种说法解释一下如果你的系统没有处理指代检索就会失准。解决办法是加一个对话改写模块在进入检索前把最近几轮对话和当前问题一起发给一个轻量大模型让它将当前问题改写成一个脱离上下文的独立问句。比如刚才那个呢结合历史改写为请解释一下刚才提到的某某模型中的注意力机制。这样再去检索效果会好很多。另外在多轮对话中历史消息不要无限保留。可以做一个简单的提取每一轮对话结束后把本轮的关键结论存下来后续构造Prompt时把结论摘要最近两轮原文放进去。这套机制我已经用了很久稳定且省Token。6. 一个完整的混合应用示例这里贴一个我在学习过程中反复迭代的示例一个企业知识助手既能回答知识库问题也能处理一些简单的计算和比对任务。架构上我用FastAPI提供接口用Chroma做向量存储用OpenAI模型做决策和生成工具就两个一个是RAG检索一个是简单计算器。前端没怎么做就是个命令行交互。这个助手的核心逻辑如下接收用户输入。对话改写模块把输入转成独立问句。进入Agent循环模型判断是否需要检索知识库如果是就调用RAG检索如果问题是计算相关的就调用计算器如果模型认为已有足够信息直接生成最终回答。回答中带上引用来源。实际跑下来用户问对比一下A方案和B方案的优劣Agent会分别检索两份文档然后用模板生成对比表用户问预算100万够不够买50套这套设备它会先检索单价再算总价最后给出结论。整个流程非常顺畅。而且因为每一步都肉眼可见调试起来也直观。代码我不再全贴上面两段核心骨架已经足够你串起来。如果你动手能力还可以建议直接照这个思路写一个属于自己的版本写完再回去用LangChain你会发现问题排查变得非常容易。在这个示例中我额外加入了一个环节把检索结果与工具输出分为证据区域单独展示。这个设计对调试很有帮助。无论是模型生成了错误的答案还是自己需要向别人解释系统为什么这么回答证据区域都能让你快速回溯。很多项目在上线后才发现黑盒式Agent根本没法排查线上问题从一开始就要做好可解释性。7. 学习RAG与Agent的几个阶段心得7.1 学习时的三个建议第一不要只跟着教程敲代码。你跟着跑通一百个demo不如自己从零搭一个完整的RAG问答系统。我认识好几个朋友看完教程就觉得自己会了等真做项目一上来就被文档切分问题卡住。动手永远是第一位的。第二学会读官方文档和源码。框架更新很快网上教程容易过时官方文档虽然枯燥但信息最准确。遇到不懂的直接去LangChain或Chroma的源码里搜实现比在社交媒体上问效率高得多。第三尽量参与一个真实项目。哪怕是给团队内部做一个简单的知识库工具也比自己闭门造车进步快。真实项目中你会遇到权限、性能、评估、维护这些教程里根本不讲的问题而这些问题恰恰是价值所在。7.2 学习时容易踩的坑我见过最多的情况是本末倒置。天天研究各种Agent框架的新特性却连最基础的检索质量都没过关。RAG系统的上限大部分由检索决定不是由模型决定。先把你的知识库切分、索引、召回做扎实再加Agent不迟。还有一个坑是过度设计。学习初期就想着做多智能体协作、任务路由、记忆反思结果被复杂度和Bug淹没。我的建议是一个最小可用的RAG加上一个带两个工具的单Agent已经能覆盖80%的实际场景。先把这个跑稳再逐个加复杂度。最后说说关于要不要从头学机器学习的纠结。不是每个应用开发工程师都需要彻底理解Transformer的每个公式但核心概念要知道Token、Embedding、注意力、上下文窗口、微调与检索的区别。你在做RAG和Agent时天天都会和这些词打交道。花一周时间把大模型基础讲清楚后面的路会顺畅很多。我个人在实际操作中的体会是RAG和Agent的学习曲线并不陡峭但它是一条曲线不是直线。你会经历一段觉得很懂然后突然不会的时期这很正常。我现在回头看自己最早写的那版RAG代码切分逻辑粗糙检索全用向量也没有评估集但它让我真正理解了整个链路。技术的迭代永远在变方法论的底层并没有变太多清晰的输入、可靠的检索、可控的执行、可观测的调试这套思路在任何框架下都适用。最后再分享一个小技巧把你做过的每个实验记录下来包括用了什么模型、什么切分策略、检索效果如何、回答质量如何。这不仅是给未来留资料更是帮你沉淀判断力。很多技术决策到最后拼的就是你对什么方案在什么场景下大概是什么效果的经验。希望这篇内容能帮你少走一些弯路也期待你在自己的项目里做出超出预期的效果。