ARTICLE DETAIL

资讯详情

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

生产级Agentic RAG实战:路由、规划、执行与反思全解析

生产级Agentic RAG实战:路由、规划、执行与反思全解析 最近把production-agentic-rag-course这个课程从头到尾啃了一遍坦白说收获比想象中大得多。市面上的 RAG 教程大多停在“跑通一个 LangChain 脚本”的阶段能端到端讲清楚 Agentic RAG 生产化落地的不多。这门课最值钱的地方不是给你一套能跑的代码而是把“从原型到生产”这条路上所有容易被忽视的细节摊开讲路由怎么做、规划怎么做、反思怎么做、可观测性怎么接、评估怎么搞。我自己的项目之前就卡在“building for production”这一步——demo 里效果好一上真实数据和并发立刻现形。跟着课程重新梳理了一遍架构才慢慢找到节奏。如果你已经写过几个 RAG demo或者正在为生产环境的“检索智能体”头疼这篇内容值得往下看。我会从课程核心思想、系统拆解、复现路径、踩坑实录到最后的生产建议尽量讲得实在一点方便你直接参考。1. 课程到底是什么为什么值得专门写一篇1.1 传统 RAG 和 Agentic RAG 的差别先说个容易混淆的点普通 RAG 和 Agentic RAG 不是“谁更高级”而是解决问题的层级不同。传统 RAG 是固定流程“用户提问 → 向量检索 → 拼接提示词 → 大模型生成”。它适合一类比较稳定的问答场景比如“从公司制度里查年假规则”。但一旦你的问题涉及多个数据源、需要多步推理、甚至要在检索结果不足时换一种方式查询固定流程就很容易翻车。Agentic RAG 的核心是让大模型扮演“指挥官”角色自己决定下一步动作要不要检索去哪个库检索用不用计算器还是要把检索结果拿回来再反思一轮每一轮动作都像一次工具调用模型根据中间结果不断调整策略。可以类比成普通 RAG 是去固定货架拿货Agentic RAG 是一个手里拿着购物清单的助手会先判断清单上的东西该去哪个仓库找拿回来还检查一下是不是真的符合要求不符合就换条路再找。我把这个课程理解成一套 Agentic RAG 的“工程化指南”它不只讲概念更关注这些能力在生产环境里如何组织、如何容错、如何被监控。1.2 什么样的人最适合跟这门课课程内容更适合三类人已经写过基础 RAG但一直没有把方案推上生产的开发者。这类人对向量数据库、Embedding 都不陌生缺的是“如何设计一个可维护的 Agent 流程”。在做多源知识库问答、企业内部资料检索等场景的工程师。当公司里有十几个数据源、权限也不一样的时候一次性检索所有库显然不现实这时候 Agentic RAG 的路由和规划能力就派上用场了。对 LangGraph 这类编排框架感兴趣想了解生产级状态管理、并行执行和超时控制的人。我个人的体会是如果只是学概念看几篇博客就够了但如果你想自己搭一个能扛请求、能出问题还能查日志的系统这门课的复现过程非常值得完整走一遍。2. 生产级 Agentic RAG 的系统拆解路由、规划、执行、反思课程里把 Agentic RAG 拆成四个核心环节路由、规划、执行、反思。这四块每块都有独立的坑也是生产环境是否稳定的关键。2.1 路由Routing真的没你想的那么简单很多人在 demo 阶段只做一个向量库所以路由显得多余。但只要数据源多了路由就必须第一个面对。路由解决的是“这个查询应该去哪取数”。常见做法是给大模型一个候选数据源列表让它输出 JSON指定选择哪个数据源。课程里强调的点是不要只给数据源一个名字要给出完整的 description包括它覆盖什么内容、什么情况下该选它、什么情况下不该选它。例如我当时构建的一个系统里有两个数据源一个是技术文档库一个是产品报障记录。如果我们定义{ reason: 用户询问的产品故障现象与历史报障记录相关应查询报障记录库, datasource: incident_reports }模型通过 JSON 输出再用一个校验层解析比直接生成自然语言路由要稳定得多。生产环境中我建议用 Pydantic 或 JSON Schema 做严格校验。如果模型输出了不存在的datasource应当触发重试或兜底到默认源。还有一点容易被忽略路由的 few-shot 示例很重要。你可以在系统提示词里放两三个典型例子比如“查工资单请选择 HR 政策库而非项目文档库”。课程里给的做法是做一个小型分类集每类问题准备 10~20 条样本跑到准确率满意为止。2.2 规划Planning用 Planner 拆解复杂查询不是所有问题都一步能查完。用户可能问“上次我们讨论的那个线上事故导致支付的模块出了什么 bug后来修复方案里涉及哪些文档”这背后至少需要两步先去“会议纪要库”找到事故主题提取关键术语再去“研发文档库”搜索相应修复方案。课程里的 Planner 就是负责把这类复合请求拆成一个多步动作序列。它和普通“ReAct”循环的区别是Planner 不是每调一个工具就想一下而是先做出整体计划再逐步执行执行中可以修正计划。具体的实现思路使用plan-and-execute模式先由 LLM 生成计划节点列表例如[search_minutes, extract_terms, search_docs]。把计划存在状态里每执行完一步就评估下一步是否仍然合理。设置最大步数防止计划无限膨胀。实际生产中对规划要求比较高的场景我习惯在 Prompt 里明确“每一步只能做一件事”并限制动作类型。不然模型容易把两个检索任务硬塞进一个工具调用导致参数混乱。2.3 执行Execution工具调用必须用规范约束到了执行层最关键的是工具函数的定义和检索器的行为。课程用 LangGraph 的状态图来管理执行流程工具函数全部按照 OpenAI Function Calling 的 schema 注册。以检索工具为例工具定义大概长这样{ name: retrieve_docs, description: 从技术文档库中检索与用户问题相关的片段用于回答技术类问题, parameters: { type: object, properties: { query: {type: string, description: 用于向量检索的查询语句}, top_k: {type: integer, description: 返回片段数量默认5}, source: {type: string, enum: [docs, incident_reports, hr_policy]} }, required: [query, source] } }这里有几个生产要点给检索器的query尽量是经过重写或抽取关键信息的语句而不是用户的原始句子。原始句子口语太重向量召回质量不稳定。top_k不是越大越好。通常 5~10 足够太多会引入噪声。你可以在回调阶段再用条件过滤分数。检索时一定要有 score threshold。我一般设 0.3 左右视 embedding 模型而定。低于阈值的片段直接丢弃避免“垃圾拼接”。执行层的另一个大坑是工具调用的死循环。如果模型反复调用同一个工具或者调用参数一直触发出错整个 Agent 会卡住。因此在状态图中必须要有一个“迭代计数”字段到达上限就强制进入生成环节并把已收集的内容作为上下文输出。2.4 反思Reflection不追求一次到位反思是 Agentic RAG 比普通 RAG 强很多的地方。反思环节做的事情是在最终生成回答前评估已经检索到的文档是否足够回答用户问题。不够就再补一轮检索或换个关键词。课程里提供了一个很轻量的反思方式单独用一次 LLM 调用输入用户原始问题、当前答案草稿和参考文档片段让模型输出{sufficient: true/false, missing_points: [...]}。如果sufficient为 false就提取missing_points重新生成查询并继续检索。听起来很简单但如果不做约束反思会成为性能黑洞。每一轮反思都要消耗时间和 token。所以我在自己的项目里给反思设了两条底线反思最多执行两次第三次直接出场。反思输出的missing_points必须压缩成一条检索 query而不是把几个点都堆进一次检索。反思的本质是让系统有“承认自己不足”的能力。做得好回答会显得更可靠做不好就是给用户多等待几秒钟最后回答还和之前一样。所以建议对反思逻辑做单独的离线评测而不是拍脑袋上线。3. 从课程代码到生产落地我的复现路径与关键参数这部分我按自己实际跑通的过程整理步骤上尽量和课程主线一致但也补充了一些我在本地环境里的调整。3.1 环境准备与依赖我建议使用 Python 3.10 以上版本配合uv或poetry做依赖管理比 pip 一步步装省心得多。核心依赖如下langgraph0.2.0 langchain0.2.0 langchain-openai0.1.0 qdrant-client1.9.0 fastapi0.110.0 uvicorn0.29.0 pydantic2.6.0 unstructured0.14.0如果你不想一直烧 OpenAI 的 API 费用也可以把 LLM 换成本地 Ollama 的模型。我复现时用 OpenAI 跑通了一遍再用ollama拉一个 7B 模型做了一次完整流程效果差异主要在路由准确率上。生产环境如果预算宽裕建议路由这种高敏感环节用强模型文本生成环节可以用中等模型。安装完成后我用 FastAPI 写了一个服务壳子但一开始并没有接完全体的 Agent而是先把检索接口单独调试好。这一步很重要先保证数据链路稳定再往上加 Agent 逻辑否则排查问题时分不清是检索问题还是编排问题。3.2 数据切分与索引构建课程里没有刻意强调切分方式但我实践中发现分块策略直接影响召回质量。我当时的数据包含 Markdown 文档和部分 PDF 合同两种格式分开处理。对于 Markdown 文档我用RecursiveCharacterTextSplitter设置了chunk_size400chunk_overlap50separators[\n## , \n### , \n\n, \n, 。, ]这个配置是综合平衡了信息完整性和检索粒度。段落之间留一点 overlap能避免句子被切断导致语义不完整。对于 PDF 合同我使用了unstructured先做格式解析再同样切分。Embedding 我选的是text-embedding-3-small维度 1536存入 Qdrant。创建集合时直接指定from qdrant_client import QdrantClient, models client QdrantClient(urlhttp://localhost:6333) client.create_collection( collection_namedocs, vectors_configmodels.VectorParams( size1536, distancemodels.Distance.COSINE ) )这里有个细节多个数据源尽量放在同一个 collection 里用 payload 的source字段区分。这样路由层只需传source参数过滤而不是维护多个向量集合运维更简单。查询时用query_filter按source过滤效率也足够。3.3 Agent 编排核心代码实现我用 LangGraph 实现了课程里的四段流程。状态定义如下from typing import TypedDict, List class AgentState(TypedDict): query: str source: str iterations: int documents: List[str] answer: str missing_points: List[str]节点包括route_node: 分类数据源写回state[source]retrieve_node: 调用检索工具写回state[documents]reflect_node: 判断是否足够写回missing_pointsgenerate_node: 生成最终答案条件边这样设计from langgraph.graph import StateGraph, END graph StateGraph(AgentState) graph.add_node(route, route_node) graph.add_node(retrieve, retrieve_node) graph.add_node(reflect, reflect_node) graph.add_node(generate, generate_node) graph.set_entry_point(route) graph.add_edge(route, retrieve) graph.add_edge(retrieve, reflect) graph.add_conditional_edges( reflect, should_continue, { retry: retrieve, generate: generate } ) graph.add_edge(generate, END)其中should_continue的逻辑是def should_continue(state: AgentState) - str: if state[iterations] 2: return generate if state[missing_points]: return retry return generate检索节点里的查询改写我根据 missing_points 重新生成一个更明确的 query。示例retry_query_prompt f 根据用户原始问题{state[query]} 以及缺失点{state[missing_points]} 生成一个搜索关键词要求简洁不超过20个字。 这一步能明显提升第二轮召回的相关性。课程里用的检索器是自带的函数我在生产里把它替换成了 Qdrant 接口业务上完全无感。3.4 生产化接口与可观测性课程后段专门讲了生产化。我用 FastAPI 把 Agent 包成了一个 POST 接口from fastapi import FastAPI from pydantic import BaseModel app FastAPI() class QueryRequest(BaseModel): question: str app.post(/agent) def agent_endpoint(req: QueryRequest): result graph.invoke({query: req.question, iterations: 0}) return {answer: result[answer], source: result[source]}生产部署时有几个参数必须设置请求超时我用 30 秒超过直接返回 504。如果 Agent 设计的步数少这个值可以更小。最大并发看你的 LLM API 限流情况。我前期只开 10 并发观察延迟和错误率再逐步上调。跟踪接入了 LangSmith每个请求都能看到完整的工具调用链和 token 消耗。在这个环节我建议一定要加“请求 ID”。每次进入 Agent 生成一个 UUID作为日志关联字段后续不管是排查超时还是查召回问题都靠这个 ID 串起来。4. 常见问题与排查实录下面这些坑是我在实际复现和压测中真实遇到的分享出来给各位省点时间。4.1 问题一Agent 陷入检索循环迟迟不回答我最早跑通代码后用了一组测试问题发现有一类问题会不断触发检索一直输出“需要更多信息”把 iterations 上限调到 5 才停。一查 trace原来是反思环节对“足够”的判断过于严格每次总能挑出一些小毛病于是不断重试。解决方案是两层把should_continue条件里的最大迭代次数设为 2。业务上允许的范围是 1~3 次超过就强制生成。反思的提示词里补充一条规则“如果当前文档已经包含与问题核心直接相关的信息立即返回 sufficientTrue不要追求覆盖所有细枝末节。”改完之后平均请求耗时从 12 秒降到 5 秒回答质量反而没怎么下降。4.2 问题二路由判断错数据源导致答案质量差有几次查询明明是关于 HR 政策的问题模型却跑去检索项目文档回答自然一塌糊涂。我检查了路由的提示词发现我把数据源描述写得太简单比如“项目文档”就只写了“项目文档”模型根本不知道里面包含什么内容。后来参考课程建议把描述改成了带明确边界的长描述“项目文档库包含产品需求、研发设计、故障复盘不包含人事和财务政策。”并在 few-shot 里加了两个反例“问年假规则时不要用项目文档库。”改完后路由准确率从 82% 提升到 94%。4.3 问题三召回结果相关性低另一类高频问题是检索召回的片段确实和关键词有关但语义上并不是用户想要的。比如查“支付超时如何排查”召回的是“支付超时背景说明”而非“排查步骤”。原因是 embedding 模型理解的是语义相似不是意图匹配。我用的处理方案是加一个 reranker重排模型。第一轮用向量检索召回 20 条再用 reranker 按相关性打分取前 5。虽然多了一次模型调用但最终答案质量提升非常明显。课程里没有强制要求复用 reranker但在生产场景我强烈建议加。如果不想引入额外的模型至少要把分数阈值调高并且对检索的 top_k 适当放宽后二次过滤。4.4 问题四并发一高就超时和报错压测时发现 QPS 一超过 5接口就开始超时。原因有两个一是 LLM 的响应时间本身就长二是存在重复的 embedding 调用。后来我做了两层优化在 LLM 调用前接入简单的语义缓存。相似问题在短时间内直接命中缓存显著降低压力。把路由和反思用的模型换成响应更快的gpt-4o-mini只在最终生成时用更强模型。这样整个 Agent 的延迟下降了一半以上。同时建议把 Qdrant 和编排服务分开部署至少不要放在同一台开发机上跑压测不然磁盘 I/O 互相影响。下面把常见问题整理成速查表症状可能原因解决方案Agent 不结束反复调用检索反思条件太严格迭代无上限设置最大迭代次数放宽 sufficient 判断检索结果不相关切分过大/过小、Embedding 不适配调整 chunk 大小增加 reranker回答内容牛头不对马嘴路由选错数据源优化数据源描述补充 few-shot 反例高并发下延迟高强模型串行调用、缓存缺失用小模型承担低阶环节增加语义缓存工具调用报参数错误schema 描述不清晰严格按 Function Calling 规范校验生成参数文档格式解析错乱PDF 表格被切碎用 unstructured 做结构化解析不要直接切分 PDF5. 课程之外给想直接上生产的人的几条实践建议最后聊点课程讲得比较轻、但我在真实项目里觉得极其重要的几点。第一不要照搬课程代码直接上生产。课程提供的代码是“教学最优解”但不同公司的数据分布、检索场景、安全要求差异巨大。最好的做法是先搭一个最小闭环把路由和生成两件事跑通再逐步加规划、反思这些高级能力。一开始就把 Agent 做成全功能排查问题时你会疯掉。第二在开始调 Agent 之前先建一个小规模评估集。我通常会给每个关键场景准备 30~50 条问题标注好标准答案或核心文档来源。之后每一次改 Prompt、改检索参数都在这个评估集上跑一遍。没有评估集的 Agent 优化就像在摸黑开车。第三成本控制要提前算。Agentic RAG 比普通 RAG 的 token 消耗要高很多因为每次路由、反思、规划都是模型调用。一个复杂请求可能消耗 3000~5000 token。如果每天几万请求就是一笔不小的开销。建议给每个请求记录 token 数设置告警阈值比如单请求超过 10000 token 就触发人工检查。第四安全与权限千万别漏。如果你的数据源涉及多部门必须在路由或检索阶段就控制权限不能让用户检索到无权访问的内容。可以在检索 payload 里增加scope字段查询时根据用户角色过滤。课程里没有过多涉及权限但生产环境这是底线。第五也是我踩过很多次坑之后最深的体会尽量把“反思”做得轻一点、收敛一点。反思能力强是好事但每多一轮反思就是一次模型往返。生产系统需要的不是最强智能体而是“足够聪明且可预测”的智能体。我见过太多团队把 Agent 调得很聪明结果上线一周就因为超时率太高被迫回滚。如果你正准备做生产级 Agentic RAG希望这篇文章能帮你少走几个弯路。像我前面说的从“能跑”到“生产可用”中间那层窗户纸不是几条代码补丁能捅破的而是一套完整的工程思维。把路由、规划、执行、反思四件事理顺评估集和可观测性跟上你的系统才有资格拿给真实用户用。
返回列表