Skip to content

[Agent Core] 让上下文压缩请求遵守 Run 截止时间并有界清理后台任务 - #167

Merged
rojim666 merged 1 commit into
rojim666:mainfrom
z2Ace0107:codex/issue-69-pr06-compact-run-deadline
Sep 22, 2026
Merged

rojim666 merged 1 commit into
rojim666:mainfrom
z2Ace0107:codex/issue-69-pr06-compact-run-deadline

Conversation

@z2Ace0107

Copy link
Copy Markdown
Contributor

Summary

关联 Issue #69。上下文压缩的摘要请求此前完全绕开了 PR-02 建立的 Run deadline 接缝:
Compactor._compact_chat() 直接 await provider.chat(...),不传 remaining_s、
不传 run_deadline_at、没有外层 asyncio.timeout,因此 provider 默认的 120 秒超时
不受 Run 预算约束。

后果与 Issue #69 标题描述的同构:配置 max_wall_clock_s=10 的 Run 仍可能被一次后台
压缩拖到约 130 秒;同时 AgentLoop 每轮顶部的 await wait_pending() 没有时间边界,
循环会阻塞在那里,到不了紧随其后的 wall_clock_exceeded() 检查。

本 PR 是 Issue #69 分阶段实现中的压缩阶段(PR-01 至 PR-05 已合并),只使用
Relates to #69,不在本 PR 合并时关闭父 Issue。

Behavior

  • compact_messages() 新增 remaining_s;Run 已无剩余时间时,在创建 provider 协程
    之前就放弃本次压缩。
  • 压缩请求通过 add_remaining_s() 获得 Run 剩余时间,并由外层 asyncio.timeout
    保证不会比 Run deadline 活得更久;有效预算为 min(provider timeout, run remaining)。
  • compact() 与 compact_async() 从 context.remaining_s() 取值;后台任务在真正
    发起请求时读取,而不是在 compact_async() 调用点读取。
  • wait_pending() 新增 remaining_s 有界等待:deadline 在等待期间到达时取消后台
    摘要请求并等其收尾,不把无界等待留给调用方。
  • deadline 截断不计入 compaction_failure_count,不会因一次超时通过熔断器永久关闭
    本次 run 的自动压缩。
  • 迟到摘要与清理期异常都归类为 deadline:provider 吞掉取消后仍返回的响应不会被安装,
    provider 在取消后的清理路径上抛出的异常也不会被记成普通 LLM 失败。
  • loop.py 每轮顶部的压缩等待由 Run 剩余时间界定,不依赖 pending 压缩任务恰好
    自我界定。
  • 手动 /compact 没有 run 上下文,行为不变(remaining_s 保持 None)。

Validation

  • 目标 Python 测试:166 passed(test_compactor.py / test_loop.py /
    test_runner.py / test_session_manager.py / test_budget_admission.py /
    test_long_compaction.py / test_simulated_loop.py)。
  • 新增 10 个测试,全部红→绿确认;其中两个端到端测试已核验在 origin/main 原样树上
    因 TimeoutError 失败(run 挂住),在本分支通过。
  • 严格 mypy(compactor.py + loop.py):Success: no issues found in 2 source files。
  • Ruff:改动文件仅 compactor.py 有 2 个既有告警(I001 导入块格式、E501 第 13 行
    105 字符),在 origin/main 上逐一相同;按本 PR non-goals 未顺手清理。
  • compileall:通过。
  • git -c core.whitespace=cr-at-eol diff --check:通过。
  • 未运行 TypeScript typecheck / 全量 TS 测试 / build:本 PR 未修改 TypeScript、协议、
    桌面端或构建配置。

Known limitations

  • tests/unit/test_loop.py::test_llm_request_is_cancelled_at_run_deadline 在完整套件中
    会因浮点边界抖动失败(0.05000000000291038 <= 0.05)。已核验:
    • 该测试不注入 compactor,本 PR 对 loop.py 的改动位于 if self._compactor is not None: 内不会执行,compactor.py 也不在其调用路径上;
    • 单独运行通过,完整套件中稳定失败(负载相关时序);
    • 未改动的 origin/main 在同一完整套件下同样失败同一个测试
      (156 passed / 1 failed);本分支为 166 passed / 1 failed。
  • runner.py 收尾对非 success 终态仍不取消挂起压缩。这是 PR-05 之前就存在的行为,
    对 deadline 场景无害(该场景下压缩任务剩余预算≈0,入口 gate 直接放弃)。改变它会
    让 max_steps / failed 的 run 不再落盘压缩摘要,属行为扩张,留作独立改动。
  • 房子模式 AgentLoop._chat 的 except asyncio.CancelledError 分支未在压缩器内复刻:
    把一次外部取消转换为 deadline 信号只在外层 timeout 已到期时正确,而该路径无法构造
    回归测试,故不加无测试代码。

Checklist

  • 变更范围与关联 Issue 一致,没有混入无关重构
  • 没有提交凭据、日志、缓存、私有源码或本地评测产物
  • 已添加与风险匹配的单元和集成测试(含两个端到端回归测试)
  • 未修改协议,无需重新生成 wire protocol 文档
  • commit 添加 Signed-off-by(DCO)
  • 已在文案中披露既有测试抖动与 runner 侧遗留边界

Relates

#69

压缩摘要请求此前直接调用 provider.chat,不传 Run 剩余时间也没有外层时间边界,
provider 默认 120 秒超时因此不受 Run deadline 约束:配置 10 秒的 Run 仍可能被
一次后台压缩拖到约 130 秒,循环也会阻塞在 wait_pending 而到不了自己的 deadline
分支。

- compact_messages 新增 remaining_s,入口与请求外层都以 Run 预算为界
- compact / compact_async 从 context.remaining_s() 取值,后台任务在真正发起
  请求时读取而非在调用点读取
- wait_pending 新增有界等待,deadline 到达时取消后台请求并等其收尾
- deadline 截断不计入 compaction_failure_count,不因一次超时触发压缩熔断器
- 迟到摘要与清理期异常都归类为 deadline,不作为普通 LLM 失败安装或记账
- loop 顶部的压缩等待由 Run 剩余时间界定,不依赖 pending 任务自我界定

Refs rojim666#69

Signed-off-by: Ace <[email protected]>
@z2Ace0107
z2Ace0107 requested a review from rojim666 as a code owner September 22, 2026 10:17
@rojim666
rojim666 merged commit ececfcb into rojim666:main Sep 22, 2026
0 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants