Agent 请求失败后,别再疯狂点击重新生成了

本文是「从零理解 Claude Code:20 个 Agent Harness 机制」系列的第 11 篇。

对应源码:s11_error_recovery

源码仓库:shareAI-lab/learn-claude-code

设想 Agent 正在帮你处理一个测试失败。

Agent 读过报错日志后,顺着日志找到相关配置文件,然后模型就开始生成修改方案,回答却停在半个代码块里。你感到有点疑惑,点击了重新生成,随后可能得到完整结果,当然更多时候却是仍然卡住。另一种情况更干脆,接口直接返回 529 overloaded,这一轮连半句话都没有。

如果 Agent 对所有失败都做同一件事,比如无脑重试,后面很容易出问题。

回答被截断时,程序需要决定那半段内容是否应该保存。比如上下文太长时,原请求重发多少次都会超限。又或者服务过载时,连续点击重试请求也照样发不出去。

s11_error_recovery 做的事情,是让 Agent Loop 根据失败位置选择恢复动作。它不能保证模型永远不中断,但能让一部分可恢复错误不至于直接结束整轮任务。

失败发生的位置,决定了程序能做什么

这一章处理三种情况:

现象 程序拿到的信息 恢复动作
模型输出到达长度上限 响应中的停止原因 提高输出上限,必要时续写
请求上下文过长 调用接口时抛出异常 紧急缩短消息后重试
接口限流或服务过载 调用接口时抛出 429、529 类错误 按递增间隔等待后重试

输出截断和接口异常的区别很大。

模型输出被截断时,程序已经拥有一份响应,只是内容还没结束。上下文超限、429 和 529 发生在请求模型的过程中,程序得不到正常响应,只能进入异常处理分支。

把三条恢复路线放在一张图里看,会更容易分清它们改动的是输出空间、消息历史,还是请求节奏。

回答写到一半时,第一次先扩大输出空间

本章先默认允许模型输出 8000 token。

模型因为输出长度不够而停止时,程序第一次不会把这段回答写入消息历史。它先把上限提高到 64000,再用原来的任务和消息记录重新请求模型。

ini 复制代码
if response.stop_reason == "max_tokens":
    if not state.has_escalated:
        max_tokens = ESCALATED_MAX_TOKENS
        state.has_escalated = True
        continue

这段逻辑出现在回复写入消息历史之前。

这样安排和一次普通重试还不太一样。此时程序保留的是同一份输入,只给模型更多输出空间。模型有机会完整回答当前任务,不会因为读到半截旧回复,又从头补一段背景说明。

如果 64000 token 仍然不够,程序才会保存当前输出,再加入一条续写要求:

perl 复制代码
Output token limit hit. Resume directly --- no apology, no recap. Pick up mid-thought.

这条提示只要求继续写,不允许模型重新解释已经说过的内容。续写最多执行三次,超过次数后循环结束。

这里的限制很有必要。模型连续多次输出不完,往往说明任务粒度太大,或者回答方式不合适。继续追加续写请求,最后很可能得到一份长度惊人、阅读体验却没有改善的回答。

上下文太长时,最近五条消息只是应急方案

上下文超限时,模型接口会直接拒绝这次请求。

教学代码中的紧急压缩没有生成摘要,只保留消息历史最后五条,这里再补一条说明:

arduino 复制代码
def reactive_compact(messages: list) -> list:
    tail = messages[-5:]

    return [{
        "role": "user",
        "content": "[Reactive compact] Earlier conversation trimmed. "
                   "Continue from where you left off.",
    }, *tail]

压缩后的消息会替换原来的消息历史,循环随后重新调用模型。

这条路径和 s08 中的常规上下文压缩不在同一个层级。s08 会尽量保留任务目标、工具结果和已有结论,而 s11 的紧急压缩只负责让请求长度尽快降下来。

假设 Agent 前面读取了十几个文件,最近五条消息恰好只剩工具结果和模型的一句中间判断。更早的用户要求、工具调用来源和项目约束可能已经不在消息历史里。模型可以继续生成内容,却未必还知道自己为什么走到这里。

因此,本章只允许紧急压缩一次。压缩后还是超限,程序直接返回错误。继续删消息当然能让上下文变短,但任务也可能被删得只剩一个标题。

还有一个实现细节:这里直接取最后五条消息,没有像 s08 那样确认工具调用和工具结果是否保持配对。它适合说明恢复方向,不适合直接搬进需要严格维护消息格式的系统。

429 和 529 的处理重点是控制重试节奏

429 表示限流,529 表示服务暂时过载。任务内容没有变化,消息历史不需要裁剪,程序需要做的是隔一段时间再尝试。

本章使用指数退避。等待时间会逐步增加:

第几次失败 基础等待时间
第 1 次 0.5 秒
第 2 次 1 秒
第 3 次 2 秒
第 4 次 4 秒
后续 最多 32 秒

每次等待还会加入少量随机时间。

如果多个 Agent 在同一时刻收到 529,又严格在 0.5 秒后同时重试,服务刚恢复就会再次收到一批请求。随机抖动让重试时间错开一些,压力不会在某个时间点重新堆起来。

代码把 429 和 529 放在 with_retry() 里处理,其他异常会重新抛给外层循环。这种分工让恢复逻辑比较清楚:临时服务问题交给重试函数,长度问题交给消息压缩逻辑,无法判断的错误记录后结束任务。

教学代码里,备用模型实际上还没有接上

连续三次 529 后,如果配置了备用模型,代码会更新当前模型名称:

ini 复制代码
state.current_model = FALLBACK_MODEL

单看这行代码,备用模型似乎已经切换成功。

问题出在调用接口时,请求函数把当前模型作为 lambda 的默认参数保存了下来:

ini 复制代码
lambda mt=max_tokens, mdl=state.current_model:
    client.messages.create(model=mdl, ...)

后续重试会继续调用同一个 lambda。即使恢复状态里的模型名称已经更新,mdl 仍然保持 lambda 创建时的旧值。

因此,教学代码当前会打印切换备用模型的日志,后续重试却还是使用原模型。这是 s11 里一个值得单独记下来的实现缺口。

如果希望切换立即生效,请求函数需要在每次调用时读取恢复状态:

ini 复制代码
def request():
    return client.messages.create(
        model=state.current_model,
        system=system,
        messages=messages,
        tools=TOOLS,
        max_tokens=max_tokens,
    )

恢复逻辑里有一个常见误区:状态字段已经修改,不代表下一次操作一定会读取这个字段。中间如果缓存了参数、闭包保存了旧值,状态变化就只停留在日志里。

这份教学代码还省略了什么

位置 当前做法 实际系统还需要补上的部分
紧急压缩 保留最近五条消息 保护工具调用与工具结果的关联
服务端建议等待时间 延迟函数支持该参数 当前调用处没有读取接口响应中的等待信息
重试次数 循环总共请求 10 次 明确区分总尝试次数和额外重试次数
备用模型 更新当前模型状态 保证下一次请求读取最新模型
输出续写 固定最多三次 根据新产生的内容判断是否还值得继续

这些简化并不影响理解本章的主线。s11 想展示的是,错误恢复需要调整不同的对象:有时调整输出空间,有时调整消息历史,有时只需要调整请求节奏。

本章小结

这一章给 Agent Loop 补上了三种恢复动作。

模型输出不够时,程序先扩大输出空间,再考虑续写。如果消息过长的话,程序压缩消息后重试一次,但是如果是接口暂时不可用,程序会按递增等待时间重试。每条路径都有自己的次数上限,避免循环在失败状态里一直打转。

这套教学实现也留下了几个需要继续完善的地方,尤其是备用模型切换和紧急压缩后的消息完整性。它们说明恢复逻辑本身同样需要测试,不能只看状态有没有更新。

下一章会从一次任务的结束,进入任务本身的管理。待办列表能记录当前几步,但跨会话恢复、依赖关系和长期执行,还需要更完整的任务系统。

相关推荐
小宋10211 小时前
不用学算法也能玩转 AI?普通 Java 程序员转 AI 应用开发路线
java·人工智能·重构
迷茫中的自我1 小时前
KMP全栈开发:从Android到AI Agent的技术演进与实践
android·人工智能
龍德明宇1 小时前
Emu3的「阳谋」:当AI不再「看」图,它还剩下什么?-龍德明宇
人工智能·大语言模型llm·负主体性·ai存在论
糖果店的幽灵1 小时前
WorkBuddy + 好提示词 = 生产力。43 个场景,复制就能用
人工智能·langgraph
硅谷秋水1 小时前
ABot-M0.5:统一的移动-与-操作世界动作模型
人工智能·深度学习·机器学习·计算机视觉·语言模型·机器人
AI大模型-小华1 小时前
Codex 为什么越用越慢?大型项目中的上下文管理与方案选择
人工智能·chatgpt·软件开发·codex·开发效率·chatgpt plus·chatgpt pro
D202020201 小时前
TikTok达人履约乱象?达秘带你拆解供需权力关系错位的本质
大数据·人工智能
Rocky Ding*2 小时前
一文读懂Kimi k1.5大模型核心基础知识
论文阅读·人工智能·深度学习·机器学习·aigc·ai-native·kimi k1.5
MartinYeung52 小时前
[论文学习]LLM 中的个性化安全:一个基准测试与基于规划的智能体方法
人工智能·学习·安全