取消语义:Abort Signal 不能撤销已经发生的副作用
自动关联目录:取消语义:Abort Signal 不能撤销已经发生的副作用
核心判断:取消只表示调用方不再等待;工具是否停止、外部效果是否发生和能否重试是三个不同事实,必须分别记录。
一、业务冲突
用户点击停止时,模型请求、并行读取、文件写入、Shell 子进程、编辑器命令和远端生成任务可能处于不同阶段。若统一标成 Cancelled,已经提交的副作用会被误判为未发生,下一次自动重试就可能重复写入、重复启动或重复生成。
二、实际实现
实际调度器已经区分三类结果:尚未派发的调用是 Cancelled Before Dispatch;已启动且在取消或超时后正常收敛的调用返回中断结果;副作用工具在 Drain Deadline 前不确认终态则记录 Unknown Outcome。
副作用派发前写入持久检查点。恢复时,同一幂等身份若已有成功终态就复用结果;若只剩 Dispatch Checkpoint 或 Unknown Outcome,就要求 Reconcile,禁止自动重放。父 Agent 可以取消整个子树,子 Agent 失败则不默认反向取消父级和兄弟节点。
三、值得保留
这是成熟的取消模型:停止等待不被伪装成撤销,未知结果也不会被压成普通失败。按树向下传播取消、为每个工具创建独立 Abort Signal、等待有限 Drain,再持久化唯一终态,都是长任务可靠性的核心。
四、结构性缺陷
只有真正消费 Abort Signal 并清理子进程、网络请求和编辑器任务的工具才会停止;Promise 先返回取消,不代表工作体已经结束。若工具缺少查询接口和外部幂等键,Unknown Outcome 最终仍只能人工判断。旧执行路径与新调度器并存时,两套超时、分阶段和回滚语义也可能不一致。
五、目标设计
每个副作用工具都实现同一恢复协议:
interface EffectTool {
PreparedEffect Prepare(Call call);
EffectHandle Dispatch(PreparedEffect effect, IdempotencyKey key);
EffectStatus Query(EffectHandle handle);
CancelResult RequestCancel(EffectHandle handle);
Receipt Reconcile(EffectHandle handle);
}
switch (Query(handle)) {
case Committed: return ReuseReceipt(handle);
case Aborted: return RetryAllowed;
case Running: return WaitOrRequestCancel;
case Unknown: return ManualOrToolSpecificReconcile;
}取消策略由 Effect 类型决定:只读可以丢弃结果,文件写使用事务提交,远端生成持有任务句柄,发布类动作必须对账。
六、取舍与验证
可查询任务和幂等回执需要工具服务配合,但这是可靠自动化的必要成本。MCP 在 2026-07-28 版本中把长任务建模为显式 Task Handle,并提供获取、更新和取消流程,也体现了“取消需要可观察任务状态”的方向。MCP Tasks
故障测试要在 Prepare 前、Dispatch 后、效果提交后但响应前、取消后仍运行、进程崩溃和恢复重放等位置注入失败。唯一可接受的不变量是:每个调用都有一个可对账终态;Unknown 不自动重试,Cancelled 不冒充 Rolled Back。