这篇是几份公开实践分享的交叉整理,属于实践总结,不是官方口径。各来源在不少点上说法一致,也有明显分歧的地方,比如单次运行的步数上限,文中会把分歧写出来而不是替读者选一个。
工具失败的处理
最常见的结构性错误是:工具内部抛异常,异常一路冒泡,整个 turn 终止,模型永远看不到出了什么事。它只知道这一轮没有结果,于是换个参数再试一次,或者直接宣布完成。
正确做法是把错误当作内容返回。模型读到"路径 ./data.csv 下没有文件,请改用绝对路径"这样的描述,通常会在下一轮自己修正。有来源把这种思路叫防呆设计:把工具接口改成强制接受绝对路径,这类错误就从结构上消失了,不必依赖模型猜。
下面这段是自写的返回结构,工具内部不抛异常,而是把错误组装成消息交回给模型:
@dataclass
class ToolResult:
ok: bool
content: str # 成功时是精简后的结果,失败时是可读错误
hint: str = "" # 下一步该怎么做,直接写给模型看
def read_csv(path: str) -> ToolResult:
if not os.path.isabs(path):
return ToolResult(
ok=False,
content=f"Error: '{path}' 不是绝对路径,文件未读取。",
hint="Use an absolute path, e.g. /data/reports/2026Q3.csv",
)
if not os.path.exists(path):
return ToolResult(
ok=False,
content=f"Error: No file at '{path}'.",
hint="Use list_dir() to list available files first.",
)
rows = load_rows(path)
return ToolResult(ok=True, content=summarize(rows, max_rows=20))
这段解决的是"模型看不到失败原因":失败不再是中断,而是一条带修正建议的观察结果,循环能继续下去。注意成功路径也做了截断,工具返回只留关键字段,这是控制上下文的第一步。
终止条件
没有终止条件的 Agent 会在工具反复失败时无限循环。多个来源都强调终止条件要多路独立设置,而不是只挂一个步数计数器:最大迭代步数、累计 token 预算、墙钟超时、连续失败次数,各自独立生效,任何一条触发就结束。
def run_agent(task, tools, limits):
steps, tokens, consecutive_failures = 0, 0, 0
started = now()
messages = [system_prompt(), user_msg(task)]
while True:
if steps >= limits.max_steps: return graceful_exit("已达步数上限", trace)
if tokens >= limits.max_tokens: return graceful_exit("已用尽 token 预算", trace)
if now() - started > limits.wall_clock: return graceful_exit("已超时", trace)
if consecutive_failures >= 3: return escalate("连续失败过多,转人工", trace)
step = llm(messages, tools=tools)
if step.finish: return verify_and_finish(step, trace)
result = dispatch(step.tool_call, tools)
consecutive_failures = 0 if result.ok else consecutive_failures + 1
tokens += count_tokens(step) + count_tokens(result)
steps += 1
messages.append(step)
messages.append(tool_msg(result.content, result.hint))
这段解决的是"循环没有出口":四类限制互相独立,避免某一类失效后整体裸奔;退出时返回的是可交付的降级消息或升级到人工,而不是把堆栈抛给用户。
工具描述与参数
"更多 agent 等于更聪明"是来源一致否定的误区。多 agent 的成本量级被估到单 agent 的十几倍,且上下文碎片化;它适合可并行的检索类任务,不适合需要共享上下文的编码任务。同样,一次给模型 15 个工具也是新手最贵的错误之一,建议先给 2 到 3 个跑稳再逐个加。
工具描述要具体。写成"搜索任何内容"会让每个查询都命中它;写成"搜索内部 HR 知识库中的政策文档",触发范围立刻收敛。有来源提到,在工具的名字、参数、文档描述上投入的时间,比打磨顶层提示词更多。
参数结构也要克制:尽量用基本类型,嵌套最多一层,复杂结构宁可拆成两个工具。模型编参数不是它坏了,是 schema 不友好。网关层再做一次校验,校验失败时写明哪个字段不合法、应该填什么格式。
上下文增长
窗口是白板,不是硬盘。多个来源提到,生产环境里的 Agent 失败多数不是逻辑错误,而是上下文溢出。
排查方法是看单个任务的 token 曲线:如果每一轮都把全部历史原样重发,曲线会线性飙升。三板斧分别是工具返回截断只留关键字段、历史按需注入而不是全量塞入、固定步数触发摘要压缩。有来源给出的组合是"最近 5 轮保留原文,更早的压缩成要点",认为是质量与成本的平衡点。轮流数超过 20 之后,历史被截断会产生各种伪影,模型答非所问时先怀疑这里。
重试边界
"失败就重试"在处理写操作时会出事:一次支付请求超时但服务端已执行,盲目重试就是重复扣款。
分级处理更稳妥:网络类、临时类错误自动重试 1 到 2 次;参数类错误把校验信息反馈给模型让它自己改;业务类错误把替代方案一起返回;严重错误触发反思或人工介入。自动重试只对幂等操作开放,并且要带幂等键。
完成判定
模型说完成不等于完成。有来源举的例子是:安排 50 人的活动,它联系了 40 个就宣布成功。这类问题需要显式的验证环节——用独立的 evaluator 检查目标的真实约束是否都满足,不满足就把缺口重新喂回去,而不是采信自述。
系统提示词也要说清角色、边界、输出格式,以及哪些高风险动作必须先确认。
步数上限的分歧
经验值方面,有来源给出单次运行上限 8 步、每工具调用 20 秒超时,读操作工具放开跑、改状态的工具要确认。但这里存在分歧:8 步是从大量常规任务里归纳出的经验值,不是铁律。深度研究类任务本身就需要几十步,硬性卡 8 步会让它永远做不完。
更稳的做法是按 token 预算和时间预算设限,配合 checkpoint:长任务分段提交,每段结束存一次状态,超时后从最近的 checkpoint 继续,而不是从头重跑。
trace 与成功率
每次运行把 thought、工具调用、结果记录成链。可观测性不是上线后才补的东西,它是判断 Agent 行为是否合理的唯一依据。
有来源给出的参考水位是:任务成功率到 85% 算达标,步数中位数落在 3 到 5 步。跑到步数上限的那些 run 基本都是循环,排查时优先看它们。
最后提醒一句模式堆砌:从工具路由加规划执行的最小版本起步,遇到具体问题再引入状态机、反思、多 agent。这些模式都是为解决特定问题存在的,提前引入只会增加调试面积。