从零手写生产级 Agent:ReAct 循环、工具注册与容错设计

发布时间:2026/10/8 12:05:14

从零手写生产级 Agent:ReAct 循环、工具注册与容错设计 从零手写生产级 AgentReAct 循环、工具注册与容错设计框架用多了容易产生一种错觉Agent 就是调一个库。但真正遇到框架解决不了的场景——比如需要深度定制工具协议、严格审计每一步动作、或者目标环境过于受限时——你就必须理解 Agent 内部到底在发生什么。这篇文章不推荐你生产环境手写 Agent而是通过从零实现一个带工具调用的 ReAct Agent把模型决策、工具执行、循环终止、容错恢复这些核心机制彻底讲透。读懂这些你再用任何框架都会觉得是在看自己的代码。一、ReAct 循环的本质思考、行动、观察ReActReason Act是目前绝大多数 Agent 的基础范式。它把问题求解过程拆成三步循环Thought思考模型分析当前状态决定下一步做什么。Action行动模型输出一个工具调用意图。Observation观察程序执行工具把结果回传给模型。循环往复直到模型认为任务完成。这个循环的每个环节都需要工程化处理。# 核心循环骨架defagent_loop(query,tools,llm,max_steps10):messages[{role:user,content:query}]forstepinrange(max_steps):responsellm.chat(messages)actionparse_action(response)# 解析模型输出的动作ifactionisNone:returnresponse# 模型认为完成直接返回resultexecute_tool(action,tools)# 执行工具messages.append({role:assistant,content:response})messages.append({role:tool,content:result})return达到最大步数任务未完成## 二、工具注册让模型知道你能做什么Agent 的工具系统由两部分组成**工具的元信息**让模型知道有什么工具、怎么用和**工具的执行器**程序真正干活的部分。 python TOOLS{}defregister_tool(name,description,params_schema):defdecorator(func):TOOLS[name]{func:func,description:description,params_schema:params_schema,}returnfuncreturndecoratorregister_tool(nameget_weather,description查询指定城市的当前天气城市用中文名称。,params_schema{city:string, 必填城市名},)defget_weather(city:str):# 实际项目里这里调用天气 APIreturnf{city}今天晴25 度register_tool(namecalculator,description执行四则运算表达式如 3 4 * 2。,params_schema{expr:string, 必填数学表达式},)defcalculator(expr:str):returnstr(eval(expr))# 生产环境严禁 eval仅作演示 模型怎么知道这些工具存在靠的是**工具描述注入**。把每个工具的 name、description、params 拼进系统提示词让模型看到工具箱 pythondefbuild_tool_prompt():lines[你可以使用以下工具,]forname,metainTOOLS.items():lines.append(f-{name}:{meta[description]}参数:{meta[params_schema]})lines.append( 使用工具时输出格式为 ACTION: 工具名 PARAMS: JSON 参数 任务完成或无法解决时输出 DONE: 最终答案 )return\n.join(lines) 这里有两个工程细节一是工具描述要写清楚**适用场景和参数格式**描述越模糊模型越容易调用错二是要明确输出协议——模型用固定格式声明它要调用的工具程序端解析这个格式并执行双方靠协议沟通而不是靠猜。## 三、动作解析与执行协议的两端模型的输出是自由文本必须解析成结构化的工具调用。生产级实现应该让模型直接输出 JSON用 response_format 强制约束再用try-except兜底 pythonimportjsondefparse_action(response):textresponse.strip()iftext.startswith(DONE):returnNone# 任务完成iftext.startswith(ACTION:):linestext.split(\n)tool_namelines[0].replace(ACTION:,).strip()params_rawtext.split(PARAMS:,1)[1].strip()ifPARAMS:intextelsetry:paramsjson.loads(params_raw)exceptjson.JSONDecodeError:params# 解析失败时降级为空参数return{tool:tool_name,params:params}returnNone 执行环节必须有完整的错误处理链 pythondefexecute_tool(action,tools,max_tool_retries1):tool_name,paramsaction[tool],action[params]iftool_namenotintools:returnf错误工具{tool_name}不存在可选工具{list(tools.keys())}functools[tool_name][func]try:returnstr(func(**params))exceptTypeErrorase:returnf错误参数不匹配{e}请参考参数规范重新调用exceptExceptionase:returnf错误工具执行失败{e} 注意**工具执行失败的信息要原样回传给模型**让模型根据错误信息自行修正。比如参数格式错了模型看到错误描述后会重新组织参数再次调用。这个失败反馈-自我修正机制是 Agent 比固定脚本强的地方。## 四、Function Calling更可靠的替代协议上面手写的 ACTION/PARAMS 协议教学意义完整但生产环境有更可靠的方案让模型原生支持 function calling。OpenAI 兼容接口提供 tools 参数模型会在响应里返回结构化的 tool_calls省去文本解析这一步——解析错误和格式漂移都消失了。 pythondefagent_with_function_calling(query,max_steps10):messages[{role:user,content:query}]tools_spec[{type:function,function:{name:name,description:meta[description],parameters:{type:object,properties:{expr:{type:string}}}}}forname,metainTOOLS.items()]forstepinrange(max_steps):respclient.chat.completions.create(modelgpt-4o-mini,messagesmessages,toolstools_spec,)msgresp.choices[0].messageifnotmsg.tool_calls:returnmsg.content# 没有工具调用说明任务完成messages.append(msg)fortcinmsg.tool_calls:resultexecute_tool({tool:tc.function.name,params:json.loads(tc.function.arguments)},TOOLS)messages.append({role:tool,tool_call_id:tc.id,content:result})return达到最大步数 从手写协议到 function calling是 Agent 工程里一次重要的协议升级手写协议让你理解原理function calling 让你上生产。两者并非二选一——理解前者能帮你诊断后者的问题比如模型不触发工具调用时往往是工具描述写得太含糊。## 五、容错与安全Agent 的底线设计生产级 Agent 和教学 Demo 的分水岭全在容错与安全设计上。以下几条是必须的**第一最大步数限制。**循环必须有硬上限通常8~15步防止模型陷入无限循环。每步之间记录耗时超过阈值强制终止。**第二工具白名单与权限隔离。**生产环境绝不把任意代码执行暴露给模型。文件读写、网络请求、数据库操作等敏感工具必须走权限校验最好在独立沙箱进程里执行。**第三敏感操作的人工审批。**对不可逆操作删除、支付、对外发送插入确认环节——执行前暂停把动作详情展示给人工批准后才真正执行。这也是现在各大框架都在强调人在回路的原因。**第四幂等与重试。**工具调用可能重复执行模型超时重试导致同一步跑两遍写工具时要保证幂等性——同样的参数执行两次结果一致不产生副作用。**第五可观测性。**每一步的 Thought、Action、Observation 都要落 trace。线上出问题时能看到完整的推理轨迹而不是一个孤立报错。建议记录step 序号、模型输入输出、工具名称与参数、执行耗时、返回结果摘要。## 六、评测一个 Agent光看结果远远不够最后补一个关键认知Agent 系统的评测比普通 LLM 应用难一个量级因为过程复杂、路径众多。推荐从三个层次建立评测1.**结果正确性**最终答案是否达到目标需要人工或 LLM-as-Judge 标注。2.2.**工具调用正确性**每一步是否调用了正确工具、传了正确参数——这能定位是模型决策错了还是工具实现错了。3.3.**路径合理性**是否走了冗余步骤、是否反复横跳、是否过早放弃。这类过程指标暴露的问题往往比结果指标更早。 pythondefevaluate_tool_usage(traces,expected_tools):检查 Agent 执行轨迹里是否按预期顺序调用了工具used[t[tool]fortintraces]correctall(einusedforeinexpected_tools)returncorrect,used ## 七、小结手写一遍 Agent 的价值不在于我有自己的 Agent而在于你彻底理解了那条循环里每一环的职责模型负责决策协议负责沟通工具负责执行循环负责推进容错负责兜底。有了这套心智模型你再去用 LangGraph、AutoGen 或任何新框架看到的就不再是黑盒 API而是一个你亲手搭过一遍的系统的封装。请记住框架可以帮你省掉脚手架但省不掉的是——你对循环之外那些工程问题的理解。
延伸阅读

更多相关文章

2026/10/8 12:05:14

dsh-crew 派发不出去?五类报错照着查

dsh-crew 是 ZSeven-W 维护的 DeepSeek Harness 插件(MIT 许可,npm 包 zseven-w/dsh-crew):在 Claude Code / Codex / Antigravity / Grok 里把活派给 DSH agent,同时保留宿主原生的子代理界面——干活的是一个完整的 …

2026/10/8 12:05:14

RAG 生产级架构:文档解析、切片策略与召回链路设计

RAG 生产级架构:文档解析、切片策略与召回链路设计 RAG(检索增强生成)从概念到落地,中间隔着的不是模型能力,而是文档工程。很多团队在概念验证阶段跑通了 Demo——上传几篇 PDF,问几个问题,回答…

2026/10/8 12:00:12

事隔多年,终于发布了我的第一个微信小程序:密存记账本

做开发很多年,小程序也略略尝试过,却始终没有真正深入。现在终于把它做出来、发出去,心里有一种补上一块空白的感觉。 为什么是现在?除了想完成一个作品,更现实的原因是:AI 辅助开发已经方便了很多。基本上…

2026/10/8 13:55:55

医药管理系统源码拆包:从class反编译到MySQL落库的完整链路

简介:这是一套基于Java Web技术栈的医药管理系统源码,面向计算机专业学生、课程设计开发者及需要练手SSM/JSP项目的初学者,可帮助快速搭建药品进销存管理场景。系统覆盖药品添加与查看、高级查询、库存管理、类别维护与统计、购买药品、销售管…

2026/10/8 13:55:55

商用热水系统远程监控系统实战指南

1. 这不是“远程看个温度”,而是商用热水系统真正的神经中枢商用热水工程远程监控系统,这个词组里藏着三个关键角色:商用、热水工程、远程监控。它不是给自家热水器装个APP那么简单,而是面向酒店、学校、医院、工厂这类24小时不间…

2026/10/8 13:55:55

10个免费离线开发工具清单:从DevToys到DBeaver提升日常效率

做开发这些年,真正让我觉得“好用得不敢发朋友圈”的工具,往往不是发布会宣传得满天飞的东西,而是藏在 GitHub 某个角落、只在同事群里小声流传的小玩意儿。这篇要说的 10 个工具,基本都符合一个特点:免费、可离线使用…

2026/10/8 13:55:55

Git实战笔记:三大区域模型与高频命令,告别记不住

很多人学Git,其实是卡在“命令太多记不住”这个坎上。我自己刚开始也是这样,每天打开终端就对着一个git help发呆,今天提交忘了加文件、明天分支合并变出一堆冲突,最后干脆回到老办法:把代码压缩包改名加日期。直到后来…

2026/10/8 13:50:54

Qt+C++模拟经营游戏源码:毕设级实战项目解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 10:03:18

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/8 10:03:20

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/8 6:05:44

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 0:02:17

自然数立方等于连续奇数之和:从证明到编程验证

十几年来我一直游走在数学科普和编程教学这两块内容之间,对“看起来像魔法、拆开全是数学”的结论总是格外敏感。最近翻资料时又撞见一句话:任何一个自然数 m 的立方,都可以写成 m 个连续奇数之和。2 的立方等于 3 加 5,3 的立方等…

2026/10/8 0:02:17

C#上位机SSH连接实战:用SSH.NET补齐超时、批量与密钥认证

简介:这是一份基于 C# 开发的 SSH 连接功能半成品工程,原本作为另一个主项目的子功能模块,现独立打包分享。工程采用 WinForms 界面,包含源码、解决方案、安装部署工程、NuGet 依赖包及说明文档,适合正在做远程连接、网…

2026/10/8 0:02:17

Java SpringBoot一体化智能售后系统设计与实现全解析

毕业设计年年做,Java Web 方向的题目翻来覆去就那么几个,但“一体化智能售后系统”这个题,每次看到我都觉得值得认真聊一聊。它不是一个简单 curd 堆出来的管理系统,而是把客户、工单、派单、处理、回访、统计整条链路串起来的一套…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑