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