最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
Agent 请求失败以后,别再反复点击重新生成了
时间:2026-07-29 12:32:04 编辑:袖梨 来源:一聚教程网
假设 Agent 正在替你处理一次测试失败。
Agent 查看报错日志并沿着线索定位到相关配置文件后,模型开始生成修改方案,回答却在半个代码块处停止。疑惑之下,你点击重新生成,可能会得到完整结果,但更多时候依旧卡住。还有一种情况更直接,接口会立即返回 529 overloaded,当前这一轮甚至没有半句话。
如果 Agent 面对任何失败都采取同一种做法,例如不加判断地重试,后续很容易产生问题。
当回答被截断后,程序必须判断是否应该保存已有的半段内容。若问题是上下文过长,无论把原请求重新发送多少次,都会继续超限;如果服务处于过载状态,连续点击重试也同样无法发出请求。
s11_error_recovery 需要做的是由 Agent Loop 依据失败发生的位置选择相应恢复动作。虽然它无法确保模型永不中断,却可以避免部分可恢复错误直接终止整轮任务。
程序可以采取什么动作,取决于失败发生的位置
本章将处理三种情况:
| 现象 | 程序获得的信息 | 恢复方式 |
|---|---|---|
| 模型输出触及长度上限 | 响应内的停止原因 | 扩大输出上限,必要时继续续写 |
| 请求的上下文过长 | 接口调用期间抛出异常 | 紧急精简消息后再次请求 |
| 接口受到限流或服务过载 | 接口调用时抛出 429、529 类错误 | 等待间隔逐步增加后重试 |
接口异常与输出截断存在很大差异。
模型输出遭到截断时,程序其实已经取得响应,只是内容尚未完成。上下文超限、429 和 529 则出现在请求模型期间,程序无法获得正常响应,只能转入异常处理分支。
将三种恢复路线放在同一张图中,更容易辨别它们分别调整的是输出空间、消息历史,还是请求节奏。
回答中途停止时,先在第一次扩大输出空间
本章首先假定模型可以输出 8000 token。
当模型因输出长度不足而停止,程序不会在第一次把这段回答写进消息历史,而是先将上限提升到 64000,再使用原任务和原消息记录重新请求模型。
if response.stop_reason == "max_tokens":if not state.has_escalated:max_tokens = ESCALATED_MAX_TOKENSstate.has_escalated = Truecontinue
这部分逻辑位于回复写入消息历史之前。
这种安排并不等同于普通重试。程序此时保持同一份输入不变,只为模型扩大输出空间,使其有机会完整回答当前任务,也不会因为读到旧回复的半截内容而重新补充一段背景说明。
只有当 64000 token 依旧不够时,程序才保存当前输出,并添加一条续写要求:
Output token limit hit. Resume directly — no apology, no recap. Pick up mid-thought.
这条提示只要求继续写,不允许模型重新解释已经说过的内容。续写最多执行三次,超过次数后循环结束。
设置这种限制十分必要。模型若连续多次无法完成输出,通常意味着任务粒度过大,或者回答方式不恰当。不断追加续写请求,最终可能只会得到篇幅惊人、阅读体验却没有改善的回答。
上下文过长时,仅保留最近五条消息只是应急手段
一旦上下文超限,模型接口会直接拒绝本次请求。
教学代码进行紧急压缩时没有生成摘要,仅留下消息历史中的最后五条,这里还要补充一项说明:
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 后,如果已经配置备用模型,代码将更新当前模型名称:
state.current_model = FALLBACK_MODEL
仅从这一行来看,备用模型似乎已经完成切换。
但接口调用时存在问题:请求函数已将当前模型保存为 lambda 的默认参数:
lambda mt=max_tokens, mdl=state.current_model:client.messages.create(model=mdl, ...)
后续重试仍会反复调用同一个 lambda。即使恢复状态中的模型名称已经改变,mdl 仍会保留 lambda 建立时使用的旧值。
因此,教学代码现在虽然会输出切换备用模型的日志,之后的重试却仍采用原模型。这是 s11 中需要单独记录的一处实现缺口。
若要让切换立即生效,请求函数必须在每次调用时重新读取恢复状态:
def request():return client.messages.create(model=state.current_model,system=system,messages=messages,tools=TOOLS,max_tokens=max_tokens,)
恢复逻辑里有一个常见误区:状态字段已经修改,不代表下一次操作一定会读取这个字段。中间如果缓存了参数、闭包保存了旧值,状态变化就只停留在日志里。
这份教学代码还省略了哪些内容
| 位置 | 现有处理方式 | 实际系统仍需补充的部分 |
|---|---|---|
| 紧急压缩 | 留下最近五条消息 | 维护工具调用与工具结果之间的关联 |
| 服务端建议的等待时间 | 延迟函数能够接收该参数 | 目前调用位置没有读取接口响应提供的等待信息 |
| 重试次数 | 循环共发起 10 次请求 | 清楚区分总尝试次数与额外重试次数 |
| 备用模型 | 刷新当前模型状态 | 确保下一次请求取得最新模型 |
| 输出续写 | 固定上限为三次 | 依据新生成的内容判断继续是否仍有价值 |
这些简化不会妨碍理解本章主线。s11 所要展示的是,错误恢复应当调整不同对象:有时需要改变输出空间,有时需要处理消息历史,也有时只需调整请求节奏。
本章总结
本章为 Agent Loop 增加了三类恢复动作。
模型输出空间不足时,程序会先扩大空间,再决定是否续写;消息过长时,程序压缩消息并重试一次;若接口只是暂时不可用,程序则按照逐渐增加的等待时间重试。每条路线都设置了次数上限,防止循环持续困在失败状态。
这套教学实现仍有若干部分需要完善,其中尤其包括备用模型切换,以及紧急压缩后的消息完整性。这说明恢复逻辑也必须接受测试,不能只检查状态是否发生更新。
下一章将从单次任务如何结束,转入对任务本身的管理。待办列表可以记录眼前的几个步骤,但要处理跨会话恢复、依赖关系和长期执行,还需要一套更完整的任务系统。
相关文章
- 漫画群星大集结女帝怎么样 群星集结女帝角色分享 07-29
- 血薪记罪恶园区入侵白晓手机攻略 07-29
- 漫画群星大集结祢豆子好玩吗 群星集结祢豆子攻略分享 07-29
- 漫画群星大集结排位如何上分 漫画群星大集结排位上分攻略 07-29
- 漫画群星大集结由哪个公司出品 漫画群星大集结公司介绍 07-29
- 漫画群星大集结角色技能机制解析 漫画群星大集结角色强度介绍 07-29