Appearance
长 Run 什么时候必须停:Long-running Execution 与 Termination
“可以运行几天”和“让一个进程活几天”是两回事
一个代码修复任务可能今天搜索仓库,晚上等待 CI,明天等人工审批,再继续发布。逻辑 Run 持续两天是合理的;要求同一个 Python coroutine、同一个 HTTP connection、同一台 Worker 两天不退出则非常脆弱。
Long-running execution 真正要求的是:
text
logical Run survives
process restart
worker replacement
context reset
human wait
external event delay连续性来自 durable state、checkpoint、artifact/workspace identity 和外部事件记录,不来自进程内对象。
长 Run 需要两套时间概念
Active execution time:当前正在调用 Model/Tool 或执行 Node 的时间。它受 operation timeout、deadline 和 worker lease 约束。
Wall-clock lifetime:从 Run 创建到最终结束的总时间,中间可能包含数小时 WAITING。它不能用一个 HTTP request timeout 或 asyncio.wait_for() 覆盖。
例如:
text
RUNNING 30s
→ WAITING_APPROVAL 18h
→ RUNNING 45s
→ SUCCEEDED这不是“18 小时的 coroutine”,而是持久化状态机经历了两个短 execution slice 和一个 durable wait。
Termination 不是一个 max_turns
一个 Run 是否应停止,需要多种独立条件:
text
completion evidence
hard deadline
max turns / max node attempts
token / money / tool budget
explicit cancellation
no-progress detection
policy rejection
unrecoverable failure这些条件职责不同。max_turns 防止模型循环;deadline 防止任务无限拖延;cost budget 控制资源;cancel 是用户/系统命令;completion evidence 证明目标确实完成。
模型可以建议“还值得继续”,但不能覆盖 hard deadline、权限拒绝或预算上限。
No-progress 比单纯计数更能抓住 Agent Loop 失控
例如连续出现:
text
search(query=A) → same 20 results
search(query=A) → same 20 results
search(query=A) → same 20 results虽然只用了 3 turns,却已经没有新 evidence。Runtime 可以维护轻量 progress signal:
text
new artifact?
new observation hash?
plan changed?
state advanced?
completion criterion closer?连续 N 轮无进展时不一定立即失败,可以先触发 replan、要求模型解释缺口,或降低可用工具;仍无进展才终止。这里检查的是可观察系统进展,不是让模型自评“我觉得有进展”。
Termination decision 应落成 durable fact
如果 Runtime 发现 cost budget 已超限,只在内存里 break 不够。需要持久化为什么结束:
python
@dataclass(frozen=True)
class TerminationRecord:
run_id: str
reason: str # COMPLETED / DEADLINE / BUDGET / CANCELLED / NO_PROGRESS ...
evidence_ref: str | None
decided_at: datetime
policy_version: str这样重启后不会又把已终止 Run 放回 Ready Queue,Developer Console 也能解释“为什么停”。
对 success 更要谨慎:模型说“修复完成”只是语义判断;代码 Agent 的完成证据可能是 patch persisted + required tests passed + artifact produced。具体 completion contract 属于任务/domain,但 terminal transition 必须由 Runtime 验证后提交。
长 Run 退出时还要处理正在执行的工作
Cancellation/termination 不是瞬间删除所有对象。Runtime 要决定:
- 新 Node 是否禁止启动;
- 当前 read-only Tool 是否允许完成;
- 有副作用 Tool 能否安全取消;
- 已完成结果是否仍持久化;
- Worker 是否释放 lease;
- stream 是否发送 terminal event;
- pending approval/timer 是否撤销。
这些决定应由 policy + operation metadata 控制,而不是粗暴 task.cancel() 后假设世界已经回滚。
🔍 源码对照:Observation owner 会枚举 subagent / shell / cloud-agent 后台任务
完整源码与通用分析见 09-01 Run Lifecycle / State Machine。这里不重复代码;这里关注 long-running termination:后台 task 必须先成为可枚举的 Runtime fact,系统才能在终止时决定 drain、cancel 还是继续观察。
源码说明
仓库把 background work、terminal watch 与 turn observation 分开,说明长任务不是“把 timeout 调大”就结束。Runtime 需要持续知道后台工作是否存在、最后活动是什么、当前 Turn 是否等待,以及什么时候把无进展判断成 stall/termination condition。
工程边界
案例中的具体 stall threshold、shell watch 规则是产品选择。Shadow Harness 的 termination policy 应与预算、用户 SLA、tool semantics 和可恢复等待机制绑定。
Shadow Harness 对照
- Run termination 采用多维 budget:turns、wall clock、tool attempts、cost、no-progress。
- 真正长期等待转成 durable WAITING,而不是一直占住进程/Task。
验证
构造“模型不断重复同一 read tool 但没有新 Observation”的 runaway loop,验证 no-progress detector 能终止且保留可解释原因。
验证长 Run 的正确方式
最有价值的测试不是让 CI 真跑两天,而是控制时间和故障:
text
Fake clock advances beyond deadline
→ Run terminates once
Scripted Model repeats identical action
→ no-progress policy triggers
process dies while WAITING
→ new process reconstructs WAITING, not RUNNING
cancel committed while Worker executing
→ no new node starts after cancel fact长运行系统是否可靠,关键看它能否不断“停下来再继续”,而不是单次 uptime 有多长。
关联源码
自测
- Agent 一直能提出“下一步”,但没有进展。除了 turn count,还可以用哪些 termination 条件识别无效长跑?
- Budget exhausted、user cancel、goal satisfied 三种终止原因为什么应保留不同 final status/evidence?