一家制造企业的采购专员提交了一份供应商比价任务,等了半分钟界面没有变化,顺手点了取消,转去处理别的事。当天下午他收到一封系统邮件,附件正是这份比价的结果。他重新提交一次,盯着看,取消按钮点亮之后,后台任务列表里那条记录依旧停在执行中。运维调取日志,取消请求确实收到了,下游几个环节却仍然跑到了结束。
遇到这类现象,一个自然的假设是界面没有刷新,或者取消按钮没有接上后端。于是有人把取消做成前端隐藏,有人延长按钮的等待动画,问题却依旧复现。取消之所以看起来没有生效,是因为它并不是一次点击动作,而是一个要沿着整条链路往下传递、并在每个环节被确认的状态。
一个任务在系统里通常要经过编排、排队、执行、回写几个阶段,取消请求从界面发出后,要依次穿过这些阶段才可能停下。编排层收到取消,如果只改了主流程的状态,已经在运行的子任务不会自动感知;运行中的子任务多数不可打断,一次外部接口调用、一段内容生成,发出去就要等它返回;排队中的任务尚未开始,取消在这里本应容易处理,但若标记只落在主任务上,子任务出队时读不到,照样会执行。与此相关的还有取消与结果的先后,取消恰好落在任务即将完成的一瞬,结果已在回写路上,取消标记晚于结果写入,记录里就会出现已取消与已出结果并存,这不是取消失效,而是两个动作在争同一条记录的写入权,缺少仲裁规则时结果便不可预期。
先定义取消的语义
因此取消需要先定义语义,再落到状态机。业务上要区分两种取消,一种是“别再继续了”,允许已产生的中间结果保留;另一种是“整件事撤销”,已产生的副作用要一并清理。这两种要求对应的处理完全不同,如果产品上只给一个取消按钮,系统内部就无从判断该做到哪一步。状态机里至少要有一个明确的终态,并约定取消请求到达之后,所有子任务在下一个检查点必须读取这个状态。
取消请求和任务完成发生竞争时,终态更新需要通过原子状态转换或版本校验仲裁,而不是由最后写入的一方决定。状态流转至少包含 RUNNING → CANCEL_REQUESTED → CANCELLED 与 RUNNING → COMPLETED 两条路径,它们之间不能随便互相覆盖。如果取消已经成功把主任务推进到 CANCEL_REQUESTED,后到达的执行结果不能再把主任务覆盖成 COMPLETED,结果只能按“取消后到达”保存;反过来,如果任务已经原子地进入 COMPLETED 终态,后到的取消请求则应明确返回取消未生效,而不是留下已取消与已出结果并存的矛盾记录。
让取消信号走到每个环节
取消信号要能走到每个环节。一种做法是在任务下发时携带可查询的取消标识,子任务在启动前、调用外部接口前、回写结果前各检查一次,发现主任务已取消就停止,或者转入补偿。外部调用如果提供取消、撤销或终止接口,应优先调用对应机制;如果调用本身确实不可中断,则等待结果返回,并将结果按“取消后到达”隔离处理。取消后才返回的结果即使执行成功,也不能自动晋升为主任务有效结果,更不能继续触发后续 Agent、通知、业务写入或其他自动化动作。已经产生的副作用不能当作没有发生。如果业务系统提供可用的补偿或冲正操作,可以进入补偿流程;对于无法撤销或不存在等价补偿动作的结果,应保留事实记录、阻止后续副作用,并转人工处置。
把取消写进对账与回执
取消还要写进对账与回执。任务进入终态之后,系统应当把取消发生在哪一步、已经完成哪些部分、哪些被回收、哪些需要人工处理整理成一条回执,而不是只把状态从执行中改成已取消。对使用者来说,知道一次取消的实际结果是“什么都没发生”,还是“前半段已执行、后半段没有”,直接决定他接下来要不要手动补一步。界面与回执还应区分取消请求已受理、正在停止和已进入取消终态,避免后台仍在收尾时提前显示“已取消”。
需要说明的是,通用模型或Agent平台通常都能提供任务的启动与停止接口,但取消由谁判定、信号如何穿透子任务、已执行部分如何处置,仍需结合企业自身的业务流程与系统边界来确定。
在青山不语AI工作室的企业AI Agent定制方案中,任务在下发时携带可查询的取消标识,子任务在关键检查点读取取消状态,不可中断的调用按“取消后到达”单独标记,已经产生的副作用进入补偿与人工处置,取消结果随回执一并返回并纳入对账。
这套机制的边界也要讲清楚。它能让取消这个动作在系统内部被一致地执行和记录,却无法让已经发出的请求撤回。外部系统是否接受撤销、已提交的单据能否冲正,取决于对方的系统能力与业务规则。取消的受理范围与副作用处置规则属于业务部门的职责,助手这一侧负责状态传递、检查点判定与留痕。
验收可以用几次受控的操作来完成。在测试环境中提交一个包含多步调用的任务,在执行中途发出取消,观察后续环节是否停止、不可中断的调用是否被单独标注,也检查任务记录里有没有状态与结果互相矛盾的情况。
在我看来,取消功能常被当成一个界面细节,实际上它是一道系统边界的考题。企业在评估AI定制服务时,可以问一句很具体的话,任务取消之后,谁能说清它到底做了什么、没做什么。这个问题能被清楚地回答,任务的执行边界才算真正被管住。