首页
/ get-shit-done 3095 深度解析:Executor 子代理的配额/限流故障专属分类与恢复分支

get-shit-done 3095 深度解析:Executor 子代理的配额/限流故障专属分类与恢复分支

2026-09-03 16:23:06作者:钟日瑜

在 get-shit-done(GSD)中,/gsd:execute-phase 会把执行计划以子代理(subagent)形式派发到 Claude Code、Copilot CLI、Codex CLI、Gemini CLI 四种宿主运行时之一。本变更(#3095)引入了一套"配额/限流终止"的专属故障分类机制:通过新的 SDK 查询 agent.classify-failure 对子代理返回的自由文本做哨兵(sentinel)匹配,产出 quota-exceeded / classify-handoff-bug / unknown-failure 三类结构化结果,并让 execute-phase 第 7 步为配额终止走"等待重置 + 恢复"而非"立即重试"的独立恢复分支。读完本文,你可以完整理解这一分类器的判定规则、哨兵优先级、retryAfterSeconds 解析方式、与 workflow 恢复提示的衔接,以及四种运行时的限流信号覆盖情况。

一、问题背景:配额终止为何需要单独的故障类

GSD 的编排器(orchestrator)派发 executor 子代理后,只能拿到一段自由文本的返回体(return body)。在 #3095 之前,除了一个特例——Claude Code 的 classifyHandoffIfNeeded is not defined 运行时 bug——所有非成功的返回体都被视为同一种"真实失败"(generic "real failure"),恢复提示会向用户提供"立即重试或中止"的选项(见 agent-failure-classifier.ts 头注释)。

但配额/限流终止(quota / rate-limit termination)与"代理崩溃"在编排器看来外观完全相同:代理中断、没有 SUMMARY.md,返回体只是一段报错文本。差异在于正确的用户响应完全不同——配额耗尽时立即重发请求会被运行时在配额窗口重置前再次拒绝,正确动作是"等待重置后再恢复(resume)"。#3095 因此把配额终止从通用失败分支中拆出,作为独立故障类处理。

二、SDK 查询入口:agent.classify-failure

该分类器注册为一条 GSD SDK 查询,标准调用形式(来自变更集 .changeset/3095-quota-failure-classification.md):

gsd-sdk query agent.classify-failure -- "<agent 返回体>"
  • 返回体通过 -- 之后的位置参数传入。查询处理器 agentClassifyFailure 将全部位置参数以空格连接(args.join(' '))后送入分类函数,因此 workflow 里的 shell 片段可以安全地用 "$AGENT_RETURN_BODY" 传多行文本。
  • 该查询在 command-manifest.non-family.ts 中登记为 { canonical: 'agent.classify-failure', aliases: ['agent classify-failure'], mutation: false, outputMode: 'json' }——无副作用(mutation: false)、输出 JSON;command-static-catalog-foundation.ts 把两个名字(点号形式与空格形式)都路由到同一处理器,别名映射另见 command-aliases.generated.ts

三、分类实现:哨兵匹配、优先级与输出契约

核心实现全部位于 sdk/src/query/agent-failure-classifier.ts,规模很小、逻辑完全确定,便于在 workflow 中直接调用。

3.1 输出契约

export type AgentFailureClass =
  | 'quota-exceeded'        // 配额 / 限流终止
  | 'classify-handoff-bug'  // Claude Code 已知运行时 bug
  | 'unknown-failure';       // 兜底:通用真实失败

export interface AgentFailureClassification {
  class: AgentFailureClass;
  /** 命中的小写子串(若有),便于写日志 */
  sentinel?: string;
  /** 运行时提示的等待秒数,解析自 "retry-after: N" */
  retryAfterSeconds?: number;
}

(见 类型定义。)sentinel 字段返回命中的原始哨兵小写形式,workflow 用它拼进恢复提示,让用户看到"到底哪条信号触发了配额判定"。

3.2 配额哨兵清单与运行时覆盖

配额哨兵是一个有序数组,首个命中者胜出(first match wins),因此更具体、更可操作的哨兵排在前面(见 QUOTA_SENTINELS 及顺序注释):

哨兵(小写) 覆盖的运行时 / 场景
429 通用 HTTP 状态码;Claude Code、Codex、Gemini 的错误体均可能出现
usage_limit_reached OpenAI Codex CLI 专属哨兵
usage limit Claude Code(如 You've hit your org's monthly usage limit
rate limit 通用短语(Claude Code rate_limit_error、Copilot hit a rate limit 等)
rate-limited 带连字符的变体
rate_limit 词干(stem):一个前缀同时覆盖 rate_limitedrate_limit_errorrate_limit_exceeded 以及 Copilot 的 user_weekly_rate_limited
resource_exhausted Google Gemini CLI 的 RESOURCE_EXHAUSTED
quota 通用词元(universal token),如 exceeded your current quota
too many requests OpenAI / Codex 的 Too Many Requests(在 429 未出现时命中)
exceeded your OpenAI / Gemini 的通用配额措辞

所有匹配均为大小写不敏感:classifyAgentFailure 先把返回体整体 toLowerCase() 再逐个 includes 检查。空串或纯空白输入直接归为 unknown-failure

变更集里声明的"哨兵覆盖 GSD 实际派发的每种运行时"与上述清单对应:Claude Code(usage limit429)、Copilot CLI(rate_limituser_weekly_rate_limited)、Codex(usage_limit_reachedtoo many requests)、Gemini(RESOURCE_EXHAUSTEDexceeded your)。

3.3 retryAfterSeconds 解析

当提供商在返回体里回显 Retry-After 时,parseRetryAfter 用正则 /\bretry[-_ ]after[:\s]+(\d+)\b/i 提取整数秒

  • 兼容 retry-after: NRetry-After: Nretry_after N 等写法(分隔符为 -_、空格或冒号);
  • 只捕获整数值,HTTP-date 形式在代理返回体中很少见,作者明确不为其增加复杂度;
  • 词边界 \b 保证 noretry-after: 3600 这类嵌入词不会误报(有对应负向测试用例,见下文)。

3.4 判定优先级

优先级规则(L85-L104):

  1. 配额哨兵优先于 classify-handoff-bug:如果一次配额杀死了代理、同时 completion handler 又崩出了 classifyHandoffIfNeeded is not defined,返回体里两个哨兵都会出现——此时它仍是配额事件(等待 vs 抽查后视为成功的恢复路径不同),分类器返回 quota-exceeded
  2. 只有配额哨兵全部未命中、且文本不含 classifyhandoffifneeded is not defined 时,才落到 unknown-failure

四、execute-phase 第 7 步:三分支恢复路由

分类结果被消费的位置是 get-shit-done/workflows/execute-phase.md 的 "Handle failures" 环节(Step 7.0–7.3)。

4.1 Step 7.0 — 先分类再分支

workflow 在分支前先用一段 shell 把返回体过一遍分类器(原文片段,可直接复制):

CLASS_JSON=$(gsd-sdk query agent.classify-failure -- "$AGENT_RETURN_BODY")
CLASS=$(echo "$CLASS_JSON" | jq -r '.class')
SENTINEL=$(echo "$CLASS_JSON" | jq -r '.sentinel // empty')
RETRY_AFTER=$(echo "$CLASS_JSON" | jq -r '.retryAfterSeconds // empty')
if [ -n "$RETRY_AFTER" ]; then RETRY_HINT="  Provider hinted retry-after: ${RETRY_AFTER}s"; else RETRY_HINT=""; fi

一段分类分支即可横跨 Claude / Copilot / Codex / Gemini 四种运行时;workflow 中还显式指向研究笔记 docs/research/provider-rate-limit-signals.md 作为信号层的参考。

4.2 Step 7.1 — quota-exceeded:不重试,等重置再恢复

这是本次变更的核心行为变化:恢复提示不再提供"立即重试"(运行时会在配额窗口重置前再次拒绝),而是先做第 5 步的 spot-check(核对 SUMMARY.md 与 worktree 分支上的部分提交);若 SUMMARY.md 缺失但提交已存在,则走 safe-resume 路径(state.verify-against-disk)而不是立即重新派发。恢复提示模板(workflow 原文):

⚠ Plan {plan_id} terminated by provider quota / rate limit
  Runtime sentinel: {SENTINEL}
  {RETRY_HINT}
  Partial commits on worktree branch: {N}
  SUMMARY.md present: {yes|no}
  1. Wait for quota reset, then resume (recommended)
  2. Switch to a different runtime / model and resume
  3. Abort phase and report partial state

选项 1 的执行方式是配额重置后重新运行 /gsd:execute-phase。变更集同时说明:这条 resume 路径本身依赖 safe-resume 门禁(在 #3212 中落地)的 execute-phase 卡死安全恢复能力,即恢复机制与既有断点续跑体系(STATE.md 记录最后完成的 plan / 当前 wave / 待处理 checkpoint)衔接。

4.3 Step 7.2 / 7.3 — 其余两类

  • classify-handoff-bug:错误体含 classifyHandoffIfNeeded is not defined 时按 Claude 运行时 bug 处理;跑同样的 spot-check——PASS 则视为成功,FAIL 则落入通用失败处理。这个既有特例被并入同一分类器后,Step 7 有了单一的分发点(single dispatch point);
  • unknown-failure:报告失败 plan 并询问 Continue/Stop;继续执行可能引发依赖链上后续 plan 的级联失败(wave 依赖关系)。

workflow 尾部的 failure_handling 段 也同步更新,声明了跨运行时的配额哨兵与"不重试、等重置"规则;resumption 段 说明恢复机制:重跑 /gsd:execute-phase {phase}discover_plans 会找到已完成的 SUMMARY 并跳过,从第一个未完成的 plan 续跑——这正是"等待配额重置后恢复"能够无状态地工作的前提。

五、测试:判定边界与负向用例的分类验证

单元测试 agent-failure-classifier.test.ts 把每个运行时变体和边界情况都固化为可执行断言,选摘几个关键用例:

返回体样例 期望 class / sentinel 验证点
You've hit your org's monthly usage limit quota-exceeded / usage limit #3095 报告中的原始组织级月限额文案
Request failed: HTTP 429 Too Many Requests quota-exceeded / 429 429 优先于 too many requests(更具体)
agent failed: usage_limit_reached quota-exceeded / usage_limit_reached Codex CLI 哨兵
Server Error: user_weekly_rate_limited quota-exceeded(sentinel 有定义即可) Copilot 周限额,经 rate_limit 词干命中
Error: RESOURCE_EXHAUSTED — You exceeded your current quota quota-exceeded Gemini 哨兵;quotaresource_exhausted 谁先命中均可接受
USAGE LIMIT REACHED quota-exceeded / usage limit 大小写不敏感
rate_limit_error: retry-after: 3600 retryAfterSeconds: 3600 整数秒提取
Failed: 429 Too Many Requests (Retry-After: 60) retryAfterSeconds: 60 头式值回显也能解析
noretry-after: 3600 quota exceeded class 正确,但 retryAfterSecondsundefined 词边界负向用例
ReferenceError: classifyHandoffIfNeeded is not defined classify-handoff-bug 既有特例保持可调用
配额文案 + classifyHandoffIfNeeded 并存 quota-exceeded 优先级:配额压过运行时 bug
空串 / 纯空白 / 通用报错 unknown-failure 兜底分支

(测试用例见 agent-failure-classifier.test.ts。)

六、前瞻:从"事后分类"到"事前预警"

provider-rate-limit-signals.md 记录了各提供商限流信号的三层结构——HTTP 头、SDK 事件、事后错误体——并指出当前宿主运行时只把"事后错误体"这一层暴露给 GSD:Claude Code 不向 hooks 转发 anthropic-ratelimit-* / retry-after 头,Copilot 的前置警告只出现在子进程 stdout,Codex 的 x-ratelimit-remaining-* 头同样未转发到 hooks。因此在宿主运行时把主动信号暴露到 hooks 之前,agent.classify-failure 就是当前可操作的能力边界:事后错误体哨兵 + execute-phase Step 7 的配额专属恢复提示。

该研究笔记同时给出了信号打通后的演进方向("从源码结构看"属于计划路径,非当前已实现能力):

  1. 在 PreToolUse / SessionStart hook 中读取 requests-remaining / tokens-remaining,当值低于可配置阈值时,在派发新 wave 前向用户做软警告;
  2. 把 Anthropic Agent SDK 的 RateLimitEvent.status == "allowed_warning" 作为长时 executor 的 checkpoint 信号——先产出部分 SUMMARY,让编排器在重置后接续;
  3. executor.stall_threshold_minutes(#3329)联动,让编排器不必等满卡死检测间隔,在运行时已发出 rejected 时即转入恢复路径。

七、小结

#3095 用一个约百行的纯函数分类器 + 一处 workflow 分支,解决了多运行时编排中的一个真实痛点:把"配额耗尽"从"代理崩了"里识别出来,让用户收到"等重置、再恢复"而非"再试一次"的正确指引。关键落点:

适用前提:agent.classify-failure 依赖已安装的 GSD SDK(gsd-sdk query 可用);哨兵清单面向的是 GSD 当前派发的四种宿主运行时,若接入其他运行时,可参照 QUOTA_SENTINELS 的有序数组自行追加对应错误体措辞。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
980
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384