AI Agent实战:从ReAct循环到LangGraph与MCP生产级实现

AI Agent实战:从ReAct循环到LangGraph与MCP生产级实现 开场第二课我们开始动手如果你已经看完了第一课脑子里对AI Agent应该有了一个基本画面——它不是一个单跑的模型而是一个能自己规划、调用工具、迭代执行、最终交付结果的“数字员工”。但第一课看完大概率你还是会有一个困惑原理我都懂了ReAct循环也看过图解了可真让我从零搭一个能用的Agent我还是不知道第一步该干什么。这一课就把这个问题解决掉。我会从四个层面往下拆先讲清楚Agent在运行时的核心机制——这一层决定你写的Agent是“能演示”还是“能干活”再做一次框架选型把LangGraph、Spring AI这些主流方案的底层差异摊开看接着聊MCP这个绕不过去的工具标准化协议最后用一个真实的知识库问答Agent案例把整个过程串一遍。这套内容对应到面试官的提问逻辑里基本就是“你做过Agent吗”和“你做的是玩具还是生产级Agent”的区别。适合的读者已经看完第一课、对提示词工程和大模型基本调用有概念的人。如果你连Agent是什么都不知道建议回到第一课补基础。1. Agent运行机制深度拆解决定你写的是玩具还是工具很多人写Agent第一步就是去调LangChain或LangGraph的封装API跑通了一个demo就觉得自己会了。但一旦遇到真实业务需求立刻翻车——要么工具调用老出错要么多轮迭代下来上下文乱成一团要么Agent自己绕进死循环出不来。这些问题表面上看是代码问题根子上是对运行机制理解不到位。1.1 从ReAct到真正的循环控制第一课讲过ReAct的核心是“思考-行动-观察”的循环。但第一课限于篇幅没有展开一个关键问题这个循环在生产环境里到底是怎么被控制的标准ReAct循环在代码层面的本质其实是一个while循环模型根据当前对话上下文生成下一步动作调用工具 or 直接回答。如果是工具调用解析出工具名和参数执行工具。把工具返回结果拼接到上下文中回到第1步。如果模型判断任务完成输出最终答案循环终止。这个逻辑看起来简单但实际开发中你会遇到一个很要命的问题模型判断“任务完成”的时机对不对我见过太多Agent在工具返回一个错误结果时直接说“好的任务已完成”然后把错误结果交给用户。原因就是那套提示词里只写了“完成任务后输出答案”没有定义“什么叫任务真正完成”。生产级Agent必须在提示词里显式约定循环终止条件。我自己的做法是在系统提示词里写死两条规则一是只有当所有子任务都被标记为“成功”或“无需执行”时才允许输出最终答案二是如果某个工具调用连续失败两次必须切换策略或请求用户输入禁止第三次重试同一个操作。这两条规则在demo里看不出来差别但放到可靠运行场景里就是天壤之别。1.2 记忆系统Agent的“临时手写板”和“长期笔记本”另一个被严重低估的是记忆机制。Agent开发里记忆分两种短期工作记忆和长期存储。短期工作记忆就是当前对话的上下文窗口。工具返回的数据、中间推理过程都堆在这里。这个区域的容量是有限的——上下文窗口就这么大塞满了就溢出。我在开发中经常看到新手犯的错每轮工具调用都把完整返回结果塞进上下文五轮之后上下文就爆了。处理办法是“摘要压缩”。当上下文接近阈值时用一个轻量模型把前面的对话浓缩成摘要替代原始消息。这个技术在AutoGPT、BabyAGI那批早期项目里就已经是标配了但很多人做Agent时完全没有这个意识。长期记忆则要靠外部存储通常是向量数据库。它解决的问题是Agent下次接到类似任务时能不能回忆起上次的执行经验。这块我放到后面的知识库案例里一起讲因为单独聊太抽象配合具体场景比较容易理解。1.3 规划能力线性执行与动态规划的取舍ReAct框架天然是线性的——走一步看一步每一步都重新推理。这种方式的优点是灵活性高即时反馈缺点也很明显复杂任务可能要走十几步每一步都在消耗token而且一旦中途走偏纠偏成本极高。生产环境里我更喜欢Plan-and-Execute模式先让Agent基于任务目标生成一份执行计划再逐个执行计划中的步骤。这种模式把“规划”和“执行”解耦好处有两个——一是规划阶段可以一次性把任务拆透执行阶段每步都很聚焦二是如果某个步骤失败了Agent可以只重试那一步不用推倒重来。我曾经用一个多步骤数据处理任务做过对比线性ReAct模式跑了17轮其中有5轮是在无关的推理上打转Plan-and-Execute模式规划阶段用了2轮生成计划后面8轮执行完所有步骤总共10轮搞定。省下来的不只是token费用更是时间和出错的概率。维度线性ReActPlan-and-Execute执行方式边想边做先规划后执行灵活性高可随时调整中计划先行但可修改任务复杂度适合简单任务适合多步骤复杂任务Token消耗通常更高更可控典型应用工具调用、问答业务流程、数据处理真实项目里两种模式不是二选一。LangGraph里完全可以用规划节点来做顶层拆分每个子任务内部再走ReAct循环。这种“大规划小循环”的组合是我个人最推荐的Agent架构基线。2. 框架选型LangGraph、Spring AI与多Agent体系的对比聊完原理就该动真格的了。市面上Agent开发框架五花八门新手最容易被“哪个火选哪个”带偏。我先给出我的选型结论再展开分析单Agent场景LangGraph是当前最合适的选择Java技术栈团队重点看Spring AI复杂业务需要多角色协作的可以研究AutoGen或LangGraph的多Agent子图能力。2.1 主流框架横向对比框架语言核心优势适用场景学习曲线LangGraphPython/JS图状态机精确控制流程可生产化复杂工作流、有状态Agent中高LangChainPython/JS生态成熟组件丰富快速原型验证低AutoGenPython多Agent对话编排能力强多角色协作、群体讨论中Spring AIJava与Spring生态无缝集成企业级Java技术栈的AI应用中Semantic KernelC#/Python/Java微软系企业级支持好微软生态用户中这个表格只代表框架的“方向性差异”。实际选型时要考虑的不只是功能还有团队的存量技术栈。很多企业级项目里最合适的不是“功能最强的框架”而是“离现有系统最近的框架”。2.2 LangGraph为什么值得优先学LangGraph的核心思想是把Agent的运行流程定义成一张图。图的节点是处理步骤可以是模型调用、工具执行、条件判断边是转换逻辑状态是贯穿整个图的共享数据。这个设计的好处是流程的每个环节都可视化、可控、可调试。我自己学LangGraph的路径是从三个核心概念入手的也建议你按照这个顺序来State状态整个图的“共享内存”。所有节点都能读写State节点之间的数据传递就靠它。定义State时要提前想清楚哪些字段需要跨节点传递哪些字段用完即弃。Node节点图里的基本执行单元。一个节点可以是一个Python函数负责完成一件事比如“调用OpenAI API生成回复”或“执行数据库查询”。Edge边节点之间的连接关系。除了普通的前后顺序LangGraph还支持条件边——根据State里某个字段的值决定下一步走哪个节点。举个最直观的例子如果我要写一个“先检索知识库再生成回答”的Agent用LangGraph就是三个节点——一个负责把用户问题向量化并检索一个负责组装提示词调用模型一个负责格式化输出。节点连接以后运行过程完全透明哪一步出了问题直接看图上状态就知道。LangGraph还有一个杀手级功能状态检查点。它能把Agent每一步执行后的State快照存下来支持存到SQLite或PostgreSQL一旦中途崩溃可以从最近一个快照恢复继续跑。这个能力在生产环境有多重要做过的都懂——不是“锦上添花”是“没有它就别谈上线”。2.3 Multi-Agent更高级但更复杂的架构热词里反复出现“multi agent”我也简单展开一下。多Agent不是“用多个Agent分别干活”那么简单核心挑战在Agent之间的通信协议和任务仲裁机制。比如Spring AI里的Multi-Agent支持核心思路是通过一个编排层统一调度多个专职Agent一个负责写代码一个负责查文档一个负责测试。编排层要处理的核心问题包括任务该分配给谁、各Agent的输出怎么汇总、Agent之间意见冲突时听谁的。多Agent架构能显著提升单一任务的上限但同时也会引入额外的工作量每个Agent的上下文是独立的还共享的工具访问权限怎么隔离全链路怎么追踪我的建议是刚学Agent先老老实实把单Agent做到极致再碰多Agent。很多人一上来就盲目追multi-agent结果连单Agent的工具调用稳定性都没解决多Agent只会把问题放大。3. MCP协议让Agent长出手脚的关键一环Agent如果只能对话那它只是一个聊天机器人。Agent之所以是Agent在于它能调用外部工具——查数据库、调API、操作浏览器。而工具接入方式的设计经历了一个明显的演进这是今天我特别想讲清楚的一笔账。3.1 为什么需要MCP而不是Function Calling最初大家都是用Function Calling。开发者把工具定义用JSON Schema描述出来塞给模型模型根据用户意图选择调用哪个函数。这套方案的痛点在于每个工具都要为每个Agent单独做适配。你给Agent A接了一个搜索工具下次Agent B要用还得重新接一遍。接入方和被接入方完全耦合改一个字段两边都要动。MCPModel Context Protocol就是为了解决工具接入的标准化问题而生的。你可以把MCP理解成工具界的USB-C接口——所有工具都实现一套统一协议Agent通过同一个协议去发现、调用所有工具不需要为每个工具单独写对接逻辑。行业里把MCP称为“AI Agent的硬件接口标准”这个比喻我觉得挺贴切。3.2 MCP架构拆解与实战示例MCP的架构分三层MCP Server工具的实际提供方。把现有能力比如数据库查询、搜索、文件操作包装成一个MCP Server暴露标准化的工具接口。MCP ClientAgent侧的连接器。负责与MCP Server建立通信把Server提供的工具列表拉取给模型。传输协议默认基于JSON-RPC 2.0支持stdio和SSE两种传输方式。举个例子假设我要让Agent能查本地SQLite数据库。不用MCP的话我得写一个query_database函数格式化参数列表写进系统的提示词里。用MCP的话我可以用一个现成的SQLite MCP Server# server.py - 基于官方mcp库定义SQLite查询工具 from mcp.server import Server from mcp.server.stdio import stdio_server import sqlite3, json app Server(sqlite-server) app.tool() async def query_sqlite(query: str, db_path: str) - str: 在指定SQLite数据库上执行查询返回带格式的结果。 conn sqlite3.connect(db_path) try: cur conn.cursor() cur.execute(query) cols [d[0] for d in cur.description] rows cur.fetchall() return json.dumps({columns: cols, rows: rows}, ensure_asciiFalse, defaultstr) except Exception as e: return f查询失败: {e} finally: conn.close() if __name__ __main__: import asyncio asyncio.run(stdio_server(app))Agent侧接入时只需要在LangGraph的配置里告诉它“这个Agent挂了一个SQLite工具”剩下的动作——发现工具、生成调用请求、解析返回值——全走MCP标准协议不用写一行定制对接代码。3.3 一处制作、处处复用的工程意义对个人开发者来说MCP初期可能感觉有点“多此一举”。但对团队和企业收益极其明显。我参与的一个项目里团队把内部业务系统封了十几个MCP Server订单查询、库存盘点、物流追踪然后所有Agent统一接入这十几个Server。新Agent上线当天就能用所有现有工具不用重新做对接。MCP里还有一个“Agent网关”的用法值得提一句把企业内部的所有MCP Server统一在一个网关注册Agent通过网关统一鉴权、统一路由。这样既解决了工具标准化的问题又解决了工具访问权限控制的问题。这块现在各家云厂商都有产品在做大家在选型时可以多关注一下。4. 生产级执行全流程从任务输入到成果交付的完整链路很多教程讲到框架和工具就停了好像写完一个能跑的Agent就是终点。但真正从“能跑”到“能交付”中间隔着一条很长的路。行业内有人总结过一套方法论三阶段、六泳道、三十个核心节点我觉得这个框架很能说明问题这里给你完整拆一遍。4.1 三阶段规划-执行-交付阶段核心目标关键产出规划阶段理解任务、制定方案任务拆解清单、执行计划、所需资源列表执行阶段按计划完成任务中间产物、工具调用记录、结果数据交付阶段验证结果、输出最终成果格式化答案、结果验证报告、后续行动建议三个阶段不是简单的时间顺序而是每阶段都有质量门禁。阶段之间过不去门槛就回退到前一个阶段重新处理。4.2 六泳道贯穿全程的关注维度三阶段是把时间轴分成了三段六泳道则是从横向维度去看整个执行过程中需要并行关注的事情需求理解泳道持续校准“用户到底要什么”防止做偏。任务规划泳道负责把大目标拆解成可执行的小任务并安排顺序。工具调度泳道负责选择、调用、切换工具管理调用过程中的异常。上下文管理泳道控制上下文的增长、摘要、记忆存取。推理决策泳道负责每一步的模型推理和质量判断。结果验证泳道验证工具返回结果是否符合预期存在疑问时及时修正。这六个通道几乎覆盖了我在开发Agent过程中踩过的所有坑。比如上下文管理学习阶段基本没人会想这一层但真到生产环境我自己就遇到过上下文继续膨胀、费用突然飙升、以及运行速度下降好几个级别的现象。加了上下文摘要机制之后才算稳定下来。那“30个核心节点”是什么其实就是把三阶段六泳道进一步细化成30个具体动作比如“接收用户输入并解析目标”“制定执行方案”“初始化长期记忆”“选择首个工具”“执行工具调用”“验证工具返回数据”“判断是否需切换策略”“生成最终答案”“主动提出后续建议”等等。不需要把这30个节点背下来重点是理解一个道理生产级Agent的执行不是“模型自由发挥”而是“有流程、有检查点、有兜底方案”的工程化过程。4.3 工程启示从“模型逻辑”到“产品逻辑”整套三阶段六泳道的方法论本质上是在把Agent从“模型驱动”转成“流程驱动”。模型仍然负责推理和决策但不再负责整个执行过程的“自由落体”。每一阶段有目标每一泳道有规则整个系统才是可控的。这个思路在面试里也特别好用。当面试官问你“如何保证Agent的输出质量”你回答“我通过结果验证泳道对工具返回数据做格式和逻辑的双重校验不通过则触发修正流程”和回答“我让模型自己判断对错”说服力完全是两个级别。5. 实操演示用LangGraphMCP搭建一个知识库问答Agent理论部分差不多了我给一个我自己做过的具体项目一个基于私有知识库的问答Agent。这类Agent是目前企业里用得最多、也最适合用来练手的场景。它既涉及RAG检索增强生成又涉及工具调用逻辑链条完整而且每个环节都能独立调优。5.1 需求与架构设计需求很简单用户向Agent提问Agent从企业知识库一堆PDF/Word文档中检索相关内容基于检索结果回答。不能胡编必须引出处。基于这个需求架构分成四个模块文档处理模块把文档切块、向量化写入向量数据库。检索模块用户提问后从向量数据库检索top-k相关片段。模型问答模块把问题检索结果拼进提示词调用大模型生成回答。质量控制模块判断检索片段是否真的与问题相关不相关则换一种检索策略重试。5.2 核心代码实现LangGraph版本这是我的LangGraph实现节点定义就是上面说的四个模块from langgraph.graph import StateGraph, END from typing import TypedDict, List class AgentState(TypedDict): question: str context: List[str] answer: str def retrieve_node(state: AgentState): 检索节点从向量库拉取相关片段 docs vector_db.search(state[question], top_k5) # 问题top_k检索经常混入不相关内容通过重排序模型过滤掉 reranked reranker.rerank(state[question], docs, top_n3) return {context: reranked} def qa_node(state: AgentState): 生成节点基于检索结果生成答案 ctx \n\n.join(state[context]) answer llm.invoke(f基于以下资料回答问题。资料中未提及的信息明确回答资料中未找到相关内容。 资料{ctx} 问题{state[question]}) return {answer: answer} def guard_node(state: AgentState): 质量门禁检查检索片段和回答的关联度 if any(len(c) 50 for c in state[context]): return {context: state[context], answer: 检索不足请换一种问法} return state g StateGraph(AgentState) g.add_node(retrieve, retrieve_node) g.add_node(qa, qa_node) g.add_node(guard, guard_node) g.add_edge(retrieve, qa) g.add_edge(qa, guard) g.add_edge(guard, END) app g.compile()这段代码跑起来不难但真正要优化到“能上线”的水平我踩过几个大坑这里一起说清楚。5.3 关键参数选择与调优心得第一是检索的top_k设置。我一开始用top_k5结果回答质量不稳定。后来加了重排序模型reranker先把候选从10篇里筛出来再精排到3篇。这个步骤让回答准确率提升非常明显而且token消耗反而降了。强烈建议凡是做RAG的Agent都加一层重排序。第二是回答的幻觉控制。模型经常会把资料里没有的内容“推理”进去表现得一本正经。我的缓解办法是在提示词里强制要求“资料中未提及的信息明确说明未找到”并且让模型在回答末尾列出引用了哪几篇文档。加了这两条之后实际测试中幻觉类问题大幅减少虽然不能完全消除但已经达到了业务可接受的程度。对这个领域的从业者来说有一个基本认知特别重要幻觉不是靠提示词能根除的只能通过反复验证和系统设计来逼近零幻觉这才是Agent落地的正确心态。第三是切片策略。我一开始每500字切一片但很多知识条目恰好被拦腰截断导致检索到的内容不完整。后来改成“按段落边界切再结合语义合并”并且上下各保留50字作为重叠区检索质量好了一截。第四是模型选择。知识库问答其实不一定要用最强的模型。一个中等参数规模的模型配合好的检索结果和提示词效果已经足够好token成本却低了一半还多。给生产环境选模型时先跑一套评测集用“答案相关性引用正确率”这两个指标量化对比别凭感觉拍板。5.4 接入MCP工具后的能力跃迁基础版本跑通后我给这个Agent加了一个MCP工具一个可以查询资料更新时间的内部系统。之后Agent的行为逻辑多了一条分支用户问“XX政策现在还是不是最新版”时Agent先去MCP工具查询资料的“最后更新时间”再决定直接用知识库回答还是告诉用户“现有资料可能已过期请确认后再使用”。这个改动让Agent的业务价值明显上升。原因在于纯粹的知识库是一个静态快照但业务场景里的信息是动态的。Agent能主动调用外部系统验证信息时效才算真正从“资料查询机”进化成了“能辅助判断的工作助手”。这类“Agent主动发起工具调用”的场景建议你在做项目时多设计几个。6. 常见问题与排查技巧实录这部分是我自己实践过程中踩过的坑很多是翻官方文档翻不到的直接整理成速查清单你遇到类似问题时可以对照着排查。问题现象根因分析排查思路与解决方式Agent频繁调用同一个工具3-5次且参数不变模型陷入了“重复尝试”循环在系统提示词中约定“同一工具相同参数最多执行2次仍失败必须切换策略”也可以设置调用频率限制工具调用返回的参数总是解析失败工具返回的JSON结构不稳定不要直接让模型解析自己写解析逻辑前后加文字时用正则提取JSON再做异常兜底多轮对话后上下文爆满所有历史消息都保留在上下文里加“摘要节点”接近阈值后用轻量模型压缩长对话只保留摘要最近两轮原文Agent回答时引用不存在的内容检索相关度不够/提示词约束不严加入重排序模型提示词强制“未检索到必须说明”添加引用源字段让输出可追溯某个工具的调用老是超时工具在MCP Server中初始化开销大对MCP Server做连接池复用把高频工具的连接改为全局常驻避免每次从头建连多个Agent实例共享同一工具时互相干扰工具状态/配置被多实例共享将工具的上文无关性设计成纯函数式调用杜绝全局变量涉及状态的操作加实例级隔离Agent跑得很慢单轮推理时间太长或多次调用长上下文模型精简上下文过滤无关工具返回、使用摘要替换完整历史对分支操作并行化执行对新手来说第七个问题尤其值得警惕。很多Agent项目一开始“跑通了”一压真实数据就卡顿通常就是在早期阶段忽略了上下文瘦身。我们把这个理念放在相对靠前的位置来规划整个系统的稳定性会好很多。还有一条独门经验给Agent加一层“输入校验”。我遇到过用户输入信息不全Agent反复补问体验很不好。后来给Agent加了一个前置校验节点先判断用户信息是否完整不完整就一次把需要的字段全部列出来追问而不是一轮轮挤牙膏。这个改动让整个交互体验顺滑了很多算是我自己很满意的一个优化。最后的几句经验之谈写到这里第二课的核心内容就讲完了。回到我们开头说的那个问题——为什么很多人学完Agent开发做出来的东西还是停留在“能演示”的层面我的答案很简单因为只学了搭建没学工程。Agent开发能力的真正分水岭不在于你会不会调LangGraph的API也不在于你能不能跑通一个MCP工具而在于你有没有建立起一套“让Agent在未知环境里稳定交付结果”的系统思维。三阶段六泳道是这种思维的方法论LangGraph和MCP是这种思维的落地工具而无数次问题排查才是训练这种思维的最佳实践。这一课的知识密度不小建议你至少完整读两遍然后挑一个小场景动起手来。下一课我会重点讲Agent的评测体系和监控运维——毕竟在生产环境里怎么衡量Agent“好”或者“不好”怎么在出了问题之后快速定位和修复才是决定它能走多远的东西。到时候见。