Skip to content

[Agent Core] 任务设置了 10 秒却可能跑几分钟:为 Agent 增加真正的截止时间 #69

Open

Description

@rojim666

先看这里:这条 Issue 在解决什么

现在的“最长运行时间”更像一个提醒,而不是闹钟。Agent 只会在一轮工作开始或结束时看一眼时间;如果它正在等待模型、工具或用户授权,即使已经超时,也不会马上停止。

举个例子

用户把任务上限设为 10 秒。第 8 秒时 Agent 发起了一个可能等待 120 秒的工具调用,最终任务可能两分钟后才结束。对用户来说,“10 秒上限”就失去了意义。

修改后应该是什么样

任务开始时确定一个统一的截止时刻。模型调用、工具、权限等待、子 Agent 和最后的总结都只能使用剩余时间;时间用完后立即进入清理流程,不再开始新的模型请求。

第一次看代码,从哪里开始

先看 ExecutionContext.wall_clock_exceeded() 如何记录运行时间,再沿着 AgentLoop.run() 中的模型请求和 invoke_tool() 的固定超时向下追踪。核心思路是“传递剩余时间”,而不是到处增加新的计时器。


下面是实现时需要参考的技术细节和验收标准。

问题

ExecutionContext.wall_clock_exceeded() 目前只在 Agent Loop 的若干检查点调用(src/sztu_code/core/loop.py:120、341)。正在进行的 LLM 请求不受剩余预算约束,工具调用又在 src/sztu_code/core/tools/invocation.py:207 使用固定 120 秒超时;wrap-up/conclude 阶段也可能在预算耗尽后继续请求模型。

因此配置 10 秒的 max_wall_clock_s,实际运行仍可能被一次慢 Provider、工具、权限等待或收尾请求拖到数分钟。

目标

把墙钟预算实现为贯穿整个 run 的单调时钟 deadline,而不是轮次之间的软检查。

实现范围

  • 在 run 开始时基于 time.monotonic() 计算 deadline,并提供 remaining_s()。
  • LLM 请求、工具执行、权限等待、后台子代理等待、压缩收尾和 wrap-up 都使用 min(自身超时, remaining_s)。
  • deadline 到达时取消底层 await,并等待必要的进程/任务清理,避免孤儿进程和后台写入。
  • 所有路径使用一致、可观测的终止原因,例如 max_wall_clock_exceeded,且只能发布一次最终事件。
  • deadline 耗尽后禁止再发起 wrap-up/conclude 模型请求。
  • 明确同步不可取消操作的处理方式,并在 Trace 记录超时层级和清理结果。

验收标准

  • 使用可取消的 slow fake provider/tool 时,run 在预算后 200 ms 内结束。
  • 工具固定 120 秒超时不会覆盖更短的 run 剩余预算。
  • 权限等待和后台子代理等待也会被父 deadline 截断。
  • 超时后不存在仍运行的子任务或测试进程。
  • RunFinishedEvent 只有一个,状态和 reason 在 provider/tool/wrap-up 超时路径上一致。
  • 增加 tests/unit/test_loop.py、tests/unit/test_runner.py 中的确定性 fake-clock/slow-await 测试。

非目标

不要求强行终止 Python 无法取消的任意同步第三方代码;此类情况需要被明确标记和隔离。

Activity

  1. added
    bug可复现的缺陷或行为回归
    area/agent-coreAgent Loop、Runner、预算、取消与运行时生命周期
    on Aug 9, 2026
  2. changed the title [-][Agent Core] ????????? LLM??????????????[/-] [+][Agent Core] 让墙钟预算成为覆盖 LLM、工具与收尾阶段的硬截止时间[/+] on Aug 9, 2026
  3. changed the title [-][Agent Core] 让墙钟预算成为覆盖 LLM、工具与收尾阶段的硬截止时间[/-] [+][Agent Core] 任务设置了 10 秒却可能跑几分钟:为 Agent 增加真正的截止时间[/+] on Aug 9, 2026
  4. z2Ace0107 commented on Sep 10, 2026

    @z2Ace0107
    Contributor

    你好,我希望持续参与 #69,而不是只做一次性的局部修复。

    这个 Issue 同时涉及 LLM、工具、权限等待、Subagent、上下文压缩、清理和 Trace。如果一次性修改,改动面会较大,也不利于逐步 Review。因此我准备按依赖关系拆成多个可独立 Review 的 PR:

    1. PR-01:Python Run Deadline Foundation

      • 使用 monotonic clock 建立一次性 Run deadline;
      • 在 ExecutionContext 中提供 remaining_s();
      • 保持 Run 重入时 deadline 不被重置。
    2. PR-02:AgentLoop LLM Hard Cutoff

      • LLM 请求前检查剩余时间;
      • 对可取消的慢 Provider 使用 AgentLoop 外层 hard cutoff;
      • deadline 到达后不再处理响应、调用工具或发起 wrap-up/conclude;
      • 统一报告 interrupted / max_wall_clock_exceeded;
      • 更新 Provider contract、第一方 wrapper 和测试 doubles,传递 remaining_s。
    3. PR-03:Provider Timeout and Retry Boundary

      • 让 Anthropic/OpenAI-compatible Provider 实际使用
        min(provider_timeout, run_remaining_time);
      • 约束 retry 和 backoff;
      • deadline cancellation 不再触发重试。
    4. 后续独立 PR:

      • 工具调用 timeout 和取消清理;
      • 权限等待、pending request 清理和 stale response;
      • Parent/Subagent deadline 传播与递归取消;
      • Context compaction、cleanup、Trace 和完整生命周期集成测试。

    目前 PR-01 已在本地完成并提交为 commit 6662090,PR-02 也已完成本地实现但尚未提交。PR-02 的 focused tests 为 123 passed,Ruff、mypy 和 diff check 均通过。

    PR-02 暂不声称完整解决 #69;工具、权限、Subagent、Provider 内部 retry 以及完整清理会分别在后续 PR 中处理。预计在拆分确认后先提交 PR-01,随后提交依赖它的 PR-02;后续阶段根据 Review 结果继续推进。

    如果这个拆分或 PR 依赖关系需要调整,请告诉我。我会先按维护者意见修改范围,避免与其他贡献者重复工作。

  5. rojim666 commented on Sep 10, 2026

    @rojim666
    OwnerAuthor
  6. z2Ace0107 commented on Oct 23, 2026

    @z2Ace0107
    Contributor

    进展同步:PR-01 至 PR-08 已全部合并(#159 / #161 / #162 / #164 / #165 / #166 / #167 / #168 / #169),覆盖 Run Deadline 基础、LLM hard cutoff、Provider timeout/retry、工具取消清理、权限 deadline、压缩边界、Subagent 继承与 Trace 可观测性。

    接下来按原拆分计划做最后一项:全生命周期集成测试——一次 Run 撞 wall-clock deadline 时,LLM / 工具 / 权限 / 子 Agent / 压缩五类等待都能有界收尾,只发布一次 RunFinishedEvent,且 Trace 中有一条带阶段与清理结果的 deadline 记录,对应 ADR 0004 的「超时状态、原因、清理结果和终态事件可以被一致验证」。

    预计以新增集成测试为主,不改生产行为;如果实测发现需要改代码才能让断言成立,我会先说明再调整范围。完成前不关闭本 Issue。

  7. z2Ace0107 commented on Oct 24, 2026

    @z2Ace0107
    Contributor

    进展汇总:原拆分计划的 9 个 PR 已全部合并。

    PR 覆盖范围 状态
    #159 PR-01 Run Deadline 基础(一次性 monotonic deadline + remaining_s()) 已合并
    #161 PR-02 AgentLoop LLM Hard Cutoff 已合并
    #162 / #164 PR-03 Provider timeout 与 retry 预算边界 已合并
    #165 PR-04 工具调用的 Run Deadline、Timeout 与取消清理 已合并
    #166 PR-05 权限审批共享 Run Deadline 已合并
    #167 PR-06 上下文压缩请求遵守 Run deadline 已合并
    #168 PR-07 子 Agent 继承父 Run 截止时间与终止语义 已合并
    #169 PR-08 Trace 的 deadline 阶段与清理结果记录 已合并
    #175 PR-09 全生命周期一致性集成测试 已合并

    其中 #175 在补测试的过程中抓到一个真 bug:cancel_descendants() 只回报它动手取消的
    后代,继承父 deadline 后自行命中的子 Agent 不被计入,导致 trace 把 cleanup 误报成
    completed。已修复。

    就本 Issue 的 Python runtime 范围而言,模型请求、工具、权限、后台子 Agent、压缩和
    Trace 都已接入同一个 Run deadline。仍未覆盖的项(当初都列在非目标里,这里明确列出
    以免误解为已完成):

    • 同步不可取消操作的通用清理编排(目前只在 Bash 进程树和 ToolExecutionState.UNKNOWN /
      cleanup: unknown 层面体现,没有跨工具的统一编排);
    • SubagentFinishedEvent 没有 reason 字段,子的具体终止原因只能经 agent_result 观察;
    • Trace schema 的进一步完善。

    这些是否需要继续做、以及本 Issue 现在能否关闭,由你判断。如果需要,我可以按同样的
    拆分方式继续。

  8. rojim666 commented on Nov 10, 2026

    @rojim666
    OwnerAuthor

    完成状态复核(main bbbe73f):deadline 主路径已有多轮合并,但暂不关闭。当前回归可复现:在 py-runtime 执行 pytest tests/unit/test_subagent_cancellation.py::test_child_cancel_reason_inherits_parent_deadline_reason -q,子任务 terminal_detail 实际为 cancelled,预期为 max_wall_clock_exceeded(该组测试合计 124 passed / 1 failed)。这与本 issue 的一致终止原因要求仍有差距。另 deadline lifecycle/foundation/trace 等针对性测试通过,不能据此认定所有终止分支已满足。

  9. rojim666 commented on Nov 11, 2026

    @rojim666
    OwnerAuthor

    2026-11-11 更新(复核当前 main e3fdb9e):上一条复核 中的取消原因失败已由合并的 #183 修复,不再是当前阻塞项。已在该 main 的独立快照重新运行原失败用例所在的 subagent cancellation、runner、loop、deadline foundation/lifecycle/trace 测试,共 145 passed;另 invocation 与 permission manager 共 55 passed。

    感谢 @z2Ace0107 此前完成 #159 至 #175 的 deadline 系列实现和集成验证;#183 是其基础上的父取消原因竞态修复,不应把此前主体工作归于这次补丁。

    这次验证确认原失败已消除,但不把这项补丁等同于整个 issue 全部验收完成:当前生命周期集成测试直接覆盖 provider 终态/Trace、子任务清理及 provider 的预算 + 200 ms,尚需逐项补齐或明确映射 slow tool、权限、compaction、wrap-up 的端到端终态一致性与时间边界证据。暂保留开放;这里不是声称这些路径仍有已复现故障,也不声明全仓 CI 全绿。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/agent-coreAgent Loop、Runner、预算、取消与运行时生命周期area/context上下文管理bug可复现的缺陷或行为回归priority/P0必须优先解决

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions