AI agent 取消、中断与安全停机:任务运行到一半时怎样停止、回收和移交

HTMLPAGE 团队
20 分钟阅读

AI agent 运行到一半被用户取消、系统中断或人工接管时,最怕的不是停下来,而是停得不干净。本文讲清 cancel signal、safe stop、回收和移交流程。

#AI agent #cancellation #interruption #safe stop

很多 AI agent 系统把“取消”理解成一个前端按钮。用户点了停止,请求断掉,似乎就结束了。但真实问题恰恰在这里才开始:模型可能还在生成、工具请求可能已经发出、外部写操作可能已进入 outbox、worker 可能还持有 lease。

所以取消、中断和安全停机解决的不是“怎么停”,而是“停下来以后,系统还能不能保持一致”。如果这个问题没有被建模,取消只是把混乱藏到后台。

建议先结合 AI agent Checkpoint 与断点恢复AI agent Worker Lease 与心跳机制AI agent 人工审批控制台设计AI agent Run Ledger 审计模型 一起看。

先给结论:取消不是失败态,而是一种显式状态迁移

场景触发源正确收口
用户主动停止前端操作终止后续步骤,保留已生成 artifact
系统预算耗尽orchestrator进入 safe stop 或 degrade
人工接管review console冻结自动执行,转 handoff
依赖异常中断worker / 下游服务记录中断点,等待恢复或人工

如果取消只表现为“连接断开”,系统就无法知道自己现在是可恢复、可移交,还是应该直接关闭。

一、先区分取消、失败和挂起

这三种状态经常被混用,但意义完全不同:

  • 取消:有人明确要求停止自动继续
  • 失败:系统尝试继续,但没有成功
  • 挂起:当前不继续执行,等待外部条件满足

如果把三者都写成 failed,后续恢复和审计就会全部混乱。

二、取消信号需要变成统一事件,而不是到处 if abort

更稳的方式是把取消写成结构化事件:

{
  "runId": "run_123",
  "signal": "cancel_requested",
  "requestedBy": "user",
  "reason": "user_clicked_stop",
  "requestedAt": "2026-05-10T10:00:00Z"
}

这样 orchestrator、worker、tool gateway 和 review console 才能围绕同一事件工作,而不是各自定义“停止”的含义。

三、安全停机的核心不是终止线程,而是冻结新的副作用

一个最小 safe stop 协议通常至少包含这些动作:

  1. 不再接收新的工具调用
  2. 冻结外发与写入型动作
  3. 记录当前 step 与已产生的 artifact
  4. 更新 run 状态为 cancellinginterrupted
  5. 释放或转移 lease

重点在于:系统必须知道哪些东西已经发生,哪些还不该继续发生。

很多团队做到这里还不够,因为“停止中”本身也需要状态流。一个更稳的 safe stop 状态机通常长这样:

状态允许动作不允许动作
cancel_requested标记停止原因、冻结新任务再次派发新 tool call
draining等待只读步骤结束、回收 lease发起新副作用
stopped_cleanly进入 handoff 或 resume 决策自动恢复执行
stopped_dirty对账、补记 ledger、人工处理直接假装未发生

有了这层状态以后,系统才能区分“停住了”和“看起来停住了”。

四、运行中断后要先判断“能不能干净停住”,再决定要不要恢复

真实中断通常发生在这些位置:

中断位置风险常见收口
模型生成中结果不完整丢弃临时片段,保留 step trace
工具读请求中无副作用可直接重试或终止
外发动作前可冻结等待人工或 safe stop
外发动作后需查账本不能假设没发生

也就是说,并不是所有中断都能“立刻停止后原样恢复”。有些中断只适合进入 AI agent Checkpoint 与断点恢复 的安全恢复流程。

五、人工接管不是简单改 owner,而是完整 handoff

当取消是为了转人工时,系统至少要移交这些东西:

  • 当前 run 状态和停止原因
  • 已完成步骤和未完成步骤
  • 当前可复用的 artifact 与 evidence
  • 已冻结的副作用和 outbox 项

否则人工接手以后仍然得从日志和聊天记录里猜系统停在了哪里。

更进一步,handoff 最好不是口头意义上的“有人接手”,而是一份显式 stop packet:

{
  "runId": "run_123",
  "stopEpoch": 5,
  "stopReason": "manual_takeover",
  "frozenOutboxIds": ["outbox_11"],
  "leaseReleased": true,
  "resumeFrom": "checkpoint_04"
}

stopEpoch 这类字段很关键,它能帮助下游拒绝旧 worker 在停止后继续写回,避免出现“系统已经停了,但旧执行者还在落地结果”的脏写问题。

六、取消逻辑要和 Review Console、Lease、Ledger 一起联动

一次健康的停止流程,至少会影响三个面:

如果这三层没有联动,取消只会停住前台,不会停住系统事实。

七、上线后要观察“停止质量”,而不是只看取消次数

建议持续记录:

指标用途
cancel requested to safe stop latency看系统能多快真正停住
cancelled runs with side-effect leakage看停止后是否还有外发漏出
late write after stop count看停止后是否还有旧 worker 写回
interruption recovered cleanly ratio看中断后是否能稳定收口
manual handoff completion rate看移交流程是否真的可用

如果取消次数不高,但 side-effect leakage 持续存在,说明真正的问题不是用户爱不爱点停止,而是系统根本停不干净。

八、失败案例:用户点了停止,但消息还是发出去了

某个通知 agent 在人工审批前允许用户点“停止生成”。某次用户点停后,前端立刻提示已取消,但后台 outbox dispatcher 仍按旧状态扫描到准备发送的消息,最终真实外发。

根因有三个:

  • 取消只中断了前端请求,没有更新 run 状态
  • outbox 没有读取 cancel 信号
  • ledger 里也没有记录此次 stop request

修复后,团队把停止动作统一改成 cancel_requested -> safe_stop -> outbox_frozen 的状态流,前后台才真正一致。

九、取消与安全停机 Checklist

  • 是否区分取消、失败和挂起三种状态
  • cancel signal 是否被结构化记录,而不是只中断连接
  • safe stop 是否先冻结新的副作用,再停止执行
  • 是否有 cancel_requested -> draining -> stopped_* 的显式状态流
  • 中断位置不同,是否有不同收口动作
  • 人工接管时是否有完整 handoff packet
  • stopEpoch 或 fencing token 是否能阻断旧 worker 继续写回
  • 是否与 lease、review console、ledger 联动
  • 是否监控 side-effect leakage 和 safe stop latency

结语

AI agent 的取消、中断与安全停机,不是“给用户一个停止按钮”,而是把“何时停止、停到什么程度、停后交给谁”变成显式协议。只有这样,系统才不会在看似已经结束的时候,继续偷偷推进旧动作。

延伸阅读: