当前很多智能体系统都基于 ReAct 思想构建。所谓 ReAct,本质上是Reason + Act,也就是让大模型不断经历:
思考 → 行动 → 观察 → 再思考 → 再行动 → 最终回答一个典型流程是:
User Query ↓ LLM 判断下一步 ↓ 调用工具 ↓ 获得 Observation ↓ 将结果放回上下文 ↓ LLM 继续推理 ↓ 输出最终结果从原理上看,ReAct 很简单;但从工程落地看,真正困难的不是“让 Agent 会调用工具”,而是当工具失败、参数错误、结果为空、任务跑偏、陷入循环时,系统如何兜住。
因此,工业级 Agent 的核心不是裸 ReAct,而是:
ReAct Loop + State Machine + Tool Schema + Error Policy + Verifier + Fallback + Logging + Human Confirmation一句话概括:
大模型负责判断下一步,系统负责控制边界、校验结果、处理失败。
一、为什么失败处理机制如此重要?
很多 Agent Demo 看起来很智能,但一到真实业务场景就容易失控。
常见问题包括:
工具选错 工具参数错误 工具调用超时 接口返回为空 权限不足 结果格式异常 重复调用同一个工具 上下文越来越长 任务目标逐渐漂移 模型误读工具返回结果 最终答案缺乏证据支撑如果没有失败处理机制,Agent 很容易进入这种状态:
失败 → 再试 → 继续失败 → 换个错误方式再试 → 输出一个看似合理但实际错误的答案所以,失败处理不是补丁,而是 Agent Runtime 的核心能力。
二、工业级 ReAct Agent 的核心架构
一个更稳的 Agent 不应该只是一个简单的 while loop,而应该有完整的执行控制层。
推荐架构如下:
User Request ↓ Task Classifier ↓ Planner ↓ Agent Controller ↓ Tool Router ↓ Tool Validator ↓ Tool Executor ↓ Observation Normalizer ↓ State Store ↓ Verifier ↓ Final Response可以简化成五个阶段:
任务分类 → 规划 → 执行 → 校验 → 兜底其中最关键的是Controller。它负责控制 Agent 能跑几步、能调用什么工具、失败后能不能重试、什么时候必须停止、什么时候需要人工确认。
三、失败处理机制一:最大步数限制
最基础也最重要的机制是Max Steps。
不要让 Agent 无限循环。
MAX_STEPS=8forstepinrange(MAX_STEPS):result=agent.run_step()ifresult.is_final:returnresult.answerreturnfallback_answer("执行步骤过多,任务已终止")不同任务可以设置不同步数:
| 任务类型 | max_steps 建议 |
|---|---|
| 简单问答 | 2 - 3 |
| RAG 查询 | 3 - 5 |
| 表格 / 文档处理 | 5 - 8 |
| 多工具 Agent | 8 - 12 |
| 代码修复类 Agent | 10 - 20 |
Max Steps 主要防止:
一直搜索 一直查数据库 一直重试同一个工具 一直陷入“我再试一次”四、失败处理机制二:工具调用超时
每个工具都必须设置 timeout。
尤其是这些工具:
浏览器自动化 OCR 远程 API 数据库查询 文件解析 大模型二次调用示例:
defrun_tool_with_timeout(tool,args,timeout=10):try:returntool.run(args,timeout=timeout)exceptTimeoutError:return{"ok":False,"error_type":"TIMEOUT","message":"工具调用超时","retryable":True}如果没有 timeout,一个慢接口就可能拖死整个 Agent。
五、失败处理机制三:重试策略
失败不能盲目重试,要区分错误类型。
| 错误类型 | 是否重试 | 处理方式 |
|---|---|---|
| 网络超时 | 可以 | 退避重试 |
| 502 / 503 | 可以 | 等待后重试 |
| 限流 | 可以 | 延迟重试 |
| 参数格式错误 | 不建议直接重试 | 让模型修参数 |
| 权限不足 | 不重试 | 返回权限问题 |
| 数据不存在 | 不重试 | 换路径或标记为空 |
| 业务规则拒绝 | 不重试 | 给出拒绝原因 |
推荐策略:
第一次失败:自动重试 第二次失败:换策略 第三次失败:降级返回示例配置:
retry_policy={"TIMEOUT":2,"NETWORK_ERROR":2,"RATE_LIMIT":3,"PARAM_ERROR":0,"PERMISSION_DENIED":0}重试机制的重点不是“多试几次”,而是根据错误类型选择不同恢复方式。
六、失败处理机制四:工具参数校验
模型生成的工具参数不一定可靠。
例如模型可能生成:
{"date":"明天","user_id":null,"limit":"很多"}但工具真正需要的是:
{"date":"2026-05-20","user_id":"u_123","limit":20}所以每个工具调用前都要经过 schema 校验。
frompydanticimportBaseModel,FieldclassSearchArgs(BaseModel):query:strlimit:int=Field(default=10,ge=1,le=50)defvalidate_args(args):returnSearchArgs(**args)完整链路应该是:
LLM 生成参数 ↓ Schema 校验 ↓ 参数补全 / 标准化 ↓ 权限检查 ↓ 执行工具 ↓ 返回结构化 Observation尤其是数据库、文件写入、发消息、提交代码这类高风险工具,不能让模型直接自由传参。
比如不要直接暴露:
execute_sql(sql:str)更推荐封装成安全接口:
search_candidates(job_id:str,min_score:int,limit:int)七、失败处理机制五:Observation 标准化
工具结果不要直接把一大段原始文本塞回模型。
应该统一成结构化格式:
{"ok":true,"tool_name":"search_user_doc","data":[],"error_type":null,"message":"找到 3 条结果","confidence":0.82,"next_suggestion":"可以继续读取第 1 个文档"}失败时也要结构化:
{"ok":false,"tool_name":"read_doc","error_type":"PERMISSION_DENIED","message":"没有该文档读取权限","retryable":false,"next_suggestion":"请用户授权或跳过该文档"}这样模型才能明确知道:
是否成功 为什么失败 能不能重试 下一步建议是什么如果工具只返回一个模糊的:
error模型很容易继续乱猜。
八、失败处理机制六:循环检测
Agent 很容易出现重复调用。
例如:
search(query=A) → no result search(query=A) → no result search(query=A) → no result需要记录工具调用历史,检测重复行为。
ifsame_tool_and_args_called_more_than(2):returnfallback("检测到重复工具调用,已终止")建议规则:
| 调用类型 | 限制 |
|---|---|
| 同一工具 + 同一参数 | 最多 1 次 |
| 同一工具 + 相似参数 | 最多 3 次 |
| 全局工具调用次数 | 最多 10 次 |
| 连续失败次数 | 最多 2 - 3 次 |
循环检测可以显著降低 Agent 的不稳定性和无效 token 消耗。
九、失败处理机制七:任务目标防漂移
Agent 在多轮执行中容易偏离用户原始目标。
比如用户要求:
提取 50 个文档的 part1 原文,不要总结但 Agent 跑着跑着可能变成:
我来总结一下这 50 个文档的主要内容解决办法是把原始目标和约束写入 state,每一步都带着执行。
{"original_goal":"提取每个文档中的 part1 原文,不做总结","current_step":"读取第 7 个文档","forbidden":["不要归纳","不要改写","不要合并个人内容"]}这也是为什么工业级 Agent 更适合用State Machine,而不是裸 Prompt 循环。
十、改进机制一:Reflection 自我反思
失败处理是兜底,改进机制是让 Agent 做得更好。
最常见的是 Reflection。
流程是:
Agent 执行 ↓ 生成初稿 ↓ Critic 检查 ↓ 发现问题 ↓ Agent 修正 ↓ 最终输出适合场景:
复杂文档总结 代码生成 报告生成 多步骤任务 数据分析但 Reflection 不建议无限循环。
工程上通常最多:
1 次反思 + 1 次修正否则成本高、延迟长,甚至可能越改越偏。
十一、改进机制二:Verifier 独立校验器
比让同一个模型自我反思更稳的是增加独立 Verifier。
Generator Agent 负责生成 Verifier Agent 负责检查常见校验包括:
SQL Verifier:检查表名、字段、limit、where 条件 Args Verifier:检查工具参数是否合法 Evidence Verifier:检查最终答案是否有证据支撑 Format Verifier:检查输出是否符合 JSON / Markdown / 表格格式 Business Verifier:检查是否违反业务规则架构如下:
User ↓ Agent ↓ Tool Call ↓ Draft Answer ↓ Verifier ↓ Pass → Final ↓ Fail → Repair在工业系统里,Verifier 往往比单纯 Reflection 更可靠。
十二、改进机制三:Plan-and-Execute
裸 ReAct 是一步一步贪心决策,适合简单任务。
复杂任务更推荐:
先规划,再执行例如一个飞书周报汇总 Agent,可以先生成计划:
Step 1:读取多维表格,获取员工姓名和文档链接 Step 2:依次读取每个人的文档 Step 3:提取 part1 到 part5 Step 4:按 part 维度汇总 Step 5:写入目标飞书文档 Step 6:输出失败列表和成功列表架构:
Planner ↓ Executor ↓ Verifier ↓ Final适合:
多文档处理 招聘流程自动化 数据同步 报表生成 跨系统操作 自动写代码Plan-and-Execute 的优势是:全局目标更稳定,执行过程更可控。
十三、改进机制四:工具结果缓存
Agent 经常重复查询相同内容,比如:
读取同一个文档 查询同一个员工 搜索同一个关键词 计算同一段文本 OCR 同一张图片可以增加缓存:
cache_key = tool_name + normalized_args_hash命中后直接返回:
{"ok":true,"from_cache":true,"data":"..."}不同数据类型适合不同缓存策略:
| 数据类型 | 缓存策略 |
|---|---|
| 静态文档 | 长缓存 |
| 搜索结果 | 短缓存 |
| 用户权限 | 极短缓存 |
| 实时价格 / 天气 | 不缓存或极短缓存 |
| 工具失败结果 | 短缓存,防止重复打爆接口 |
缓存的价值不只是降成本,也能减少重复失败。
十四、改进机制五:运行经验记忆
这里的 Memory 不是普通聊天记忆,而是Agent 运行经验记忆。
例如:
{"task_type":"read_feishu_doc","failure":"文档链接需要先转换为 doc_token","fix":"调用 parse_feishu_url 再调用 read_doc","success_rate":0.91}可以沉淀:
成功工具链 失败参数模式 常见报错原因 用户偏好 业务规则 系统限制比如周报汇总 Agent 可以记住:
不要总结个人内容 按 part 维度汇总 每个人原文保留 先批量读取文档,再做结构化提取长期看,这类经验记忆能让 Agent 越跑越稳。
十五、改进机制六:Human-in-the-loop
不是所有操作都应该自动执行。
有些高风险操作必须让人确认:
发邮件 删数据 改数据库 提交代码 发布任务 审批流程 向候选人打招呼 大规模批量操作可以按照风险等级设计:
| 操作类型 | 风险等级 | 处理方式 |
|---|---|---|
| 查询 | 低 | 自动执行 |
| 总结 | 低 | 自动执行 |
| 写草稿 | 中 | 自动执行但不发送 |
| 修改文档 | 中 | 执行前确认或保留版本 |
| 发送消息 | 高 | 人工确认 |
| 删除数据 | 高 | 强制确认 |
| 金融 / 法律 / 医疗决策 | 极高 | 只辅助,不自动决策 |
Human-in-the-loop 不是降低智能化,而是提升系统可信度。
十六、推荐落地的 10 个关键机制
如果要做一个真正可用的 Agent,建议优先做下面这些。
| 优先级 | 机制 | 价值 |
|---|---|---|
| P0 | max_steps | 防止无限循环 |
| P0 | timeout | 防止工具卡死 |
| P0 | tool schema validation | 防止参数乱传 |
| P0 | structured observation | 让模型正确理解工具结果 |
| P0 | retry policy | 网络 / API 异常可恢复 |
| P1 | loop detection | 防止重复调用 |
| P1 | fallback strategy | 失败时可降级 |
| P1 | verifier | 防止最终答案胡编 |
| P1 | tool result cache | 降低成本和延迟 |
| P2 | reflection / memory | 持续改进效果 |
十七、一个更稳的 Agent 执行伪代码
defrun_agent(user_query):state=init_state(user_query)plan=planner.make_plan(user_query)state.plan=planforstepinrange(MAX_STEPS):action=agent.decide_next_action(state)ifaction.type=="final_answer":checked=verifier.check(action.answer,state)ifchecked.ok:returnaction.answerelse:state.add_feedback(checked.feedback)continueifaction.type=="tool_call":validation=tool_validator.validate(action.tool_name,action.args)ifnotvalidation.ok:state.add_observation({"ok":False,"error_type":"PARAM_ERROR","message":validation.message,"retryable":False})continueifloop_detector.is_repeated(action,state):returnfallback("检测到重复工具调用,已终止")result=tool_executor.run(tool_name=action.tool_name,args=validation.args,timeout=10,retry=2)observation=normalize_observation(result)state.add_observation(observation)ifobservation.okisFalseandobservation.retryableisFalse:state.add_failure(observation)continuereturnfallback("执行步骤超过上限,已降级返回")十八、一个具体业务例子:飞书周报汇总 Agent
假设要做一个飞书周报汇总 Agent,任务是:
读取多维表格中的员工姓名和文档链接; 依次读取每个人的文档; 提取 part1、part2、part3、part4、part5; 最后按 part 汇总; 每个人的内容保持原文,不要归纳总结。失败处理可以这样设计:
| 场景 | 处理机制 |
|---|---|
| 飞书表格读取失败 | 重试 2 次,失败后返回表格读取失败 |
| 某员工文档无权限 | 跳过该员工,记录失败原因 |
| 文档里找不到 part3 | 标记为空,不让模型编造 |
| 文档过长 | 分块读取,按标题定位 |
| part 内容提取不完整 | Verifier 检查标题边界 |
| 写入汇总文档失败 | 保留本地中间 JSON |
| 多人内容混淆 | 按 employee_id 做状态隔离 |
| 模型总结了原文 | 规则校验失败后重新提取 |
中间状态可以这样存:
{"employee_name":"张三","doc_url":"...","doc_status":"success","parts":{"part1":{"status":"success","content":"原文内容..."},"part2":{"status":"not_found","content":""}},"errors":[]}这样最后汇总时,不会把不同人的内容混在一起,也不会在缺失内容时让模型自由发挥。
十九、建议的工程目录结构
可以按照下面的结构设计 Agent 项目:
agent/ ├── runtime/ │ ├── controller.py # 主循环控制 │ ├── state.py # Agent 状态 │ ├── planner.py # 任务规划 │ ├── executor.py # 工具执行 │ ├── verifier.py # 结果校验 │ └── fallback.py # 降级策略 │ ├── tools/ │ ├── base.py # Tool 基类 │ ├── registry.py # 工具注册 │ ├── schemas.py # 参数 schema │ └── business_tools.py # 业务工具 │ ├── memory/ │ ├── short_term.py # 本轮状态 │ ├── long_term.py # 长期经验 │ └── cache.py # 工具缓存 │ ├── guardrails/ │ ├── permission.py # 权限检查 │ ├── pii.py # 敏感信息控制 │ ├── policy.py # 安全策略 │ └── loop_detector.py # 循环检测 │ └── prompts/ ├── planner_prompt.py ├── react_prompt.py ├── verifier_prompt.py └── repair_prompt.py这个结构的核心思想是:把模型能力和系统控制能力分开。
模型负责理解和决策,工程系统负责约束和保障。
二十、结语:ReAct 只是起点,Runtime 才是关键
ReAct 提供了智能体的基础范式:
思考 → 行动 → 观察 → 再思考但它本身并不等于工业级 Agent。
真正能落地的 Agent,一定需要:
可控的执行循环 可靠的工具调用 清晰的失败分类 结构化的状态管理 明确的重试和降级策略 独立的结果校验 必要的人类确认机制最终可以总结成一句话:
ReAct 让 Agent 具备“会做事”的能力,而失败处理和改进机制,决定 Agent 能不能“稳定做事”。
所以,做 Agent 不能只关注模型有多强、工具有多少,更要关注:
失败时怎么办? 结果怎么验证? 状态怎么保存? 风险怎么控制? 下次怎么改进?这才是从 Demo Agent 走向工业级 Agent 的关键。