#AI#AI Agent#DevOps

多 Agent 任务 Claim:用 Lease 与 Fencing 阻断迟到 Worker

多个 Agent 竞争同一工作时,带 TTL 的领取记录只能帮助回收,不能阻止旧执行者迟到提交。本文用单调 fencing、尝试身份和副作用登记建立可证明的任务所有权。

本文要点

  • 主题聚焦:AI、AI Agent、DevOps
  • 适合人群:独立开发者、全栈工程师、AI 应用开发者
  • 核心价值:快速理解背景、实践路径、常见误区和工程建议
作者 @admin·发布于 2026年3月14日·更新于 2026年7月20日

多 Agent 运行时常把一个待办放进共享队列,由空闲 worker 抢到后执行。最直观的实现是写入 ownerId 和 expiresAt:到期后别人可以接手。然而暂停的进程不会因为数据库时间到了就自动停止。A 的 lease 过期,B 接手并完成后,A 仍可能带着旧结果回来覆盖 B;若任务会发邮件、改仓库或创建云资源,两个执行者甚至都可能产生副作用。

这里的 claim 是协调层对“谁可推进这一项工作”的所有权,不是给工具授权。短期 capability lease 限制某次工具调用能做什么,本篇的 task lease 决定哪个 attempt 能提交。一般的 Agent 长任务工作流 负责检查点,本篇进一步收紧多个 worker 争用同一节点时的迟到写入边界。

冻结 claim.v1 契约

任务保存 taskId、不可变 inputDigest、stateVersion、schedulerEpoch、claimGeneration、attemptId、ownerId、leaseExpiresAt、cancelGeneration 和 resultCommitId。领取必须在一个线性化事务内完成:只允许从 READY 或已过期 CLAIMED 状态领取,将 claimGeneration 单调加一,并创建全新的 attempt。生成号来自任务记录的 CAS,不来自 worker 时钟或随机数。

text
claim(task, expectedStateVersion, schedulerEpoch, owner, ttl)
  -> attemptId, schedulerEpoch, claimGeneration, cancelGeneration,
     leaseExpiresAt, inputDigest

commit(task, attemptId, schedulerEpoch, claimGeneration,
       observedCancelGeneration, inputDigest, resultDigest)
  -> COMMITTED | FENCED | STALE_INPUT | CANCELLED

领取、续租、工具接受和结果提交都要匹配当前 schedulerEpoch 与 cancelGeneration;续租还要匹配 task、attempt、claim generation 和 owner,且只延长有限窗口。worker 不能自行把 TTL 改成无限。etcd 的 Lock/Election 以租约和 revision 形成顺序,Kubernetes Lease 则展示协调记录的资源版本语义。具体存储可以不同,但“过期用于回收,单调代数用于拒绝旧 owner”这两个职责不能混为一谈。

Fencing 必须下沉到提交点

worker 每次开始昂贵步骤前检查剩余 lease,但检查只能减少浪费,不能证明最终所有权。结果提交必须由事实存储重新比较 generation;旧 attempt 可以上传到隔离对象区,却不能更新任务的 canonical result、DAG 后继或共享记忆。多 Agent 共享记忆并发控制 处理事实记录的版本合并,task fencing 则在进入那条写路径之前剔除无主结果。

每个副作用 slot 有唯一持久记录,effectId = H(taskId,nodeId,effectSlot),不含 attempt 或 payload。首次 PREPARED 用 CAS 绑定 payloadDigest;后续不同摘要返回 PAYLOAD_CONFLICT,只有新 task revision 或显式新 slot 才能创建另一效果。所有 generation 向下游复用该 effectId,另传 claimGeneration fencing 与资源 ETag。超时记为 UNKNOWN;接管者只能查询或对账,确认未发生后才按原键执行,不能换键盲发。

若下游既无条件写、可查询幂等结果,也无 reservation,不能宣称 exactly-once;应把节点设为串行、人工批准的 NON_REPLAYABLE。MCP 超时、重试与幂等 的调用语义应成为调度前置条件。

重复执行是允许的,重复生效不是

lease 回收意味着至少一次尝试。调度器应区分 attempt_started、effect 的 PREPARED/UNKNOWN/COMMITTED、result_staged 和 result_committed,而不是用一个 running 布尔值猜测。A 已产生推理成本但被 fencing 时,可以计入废弃 attempt 指标;只有 canonical commit 才释放后继节点。结果摘要还应进入 多 Agent 结果血缘与冲突仲裁,避免“最后返回的文本”自动成为答案。

负向测试让 A 执行 payload X 后丢响应,B 为同一 slot 生成 Y;B 必须得到 PAYLOAD_CONFLICT 并对账 X,第二次外部调用为零。再覆盖旧 owner 恢复、输入改变、取消竞争和 schedulerEpoch 提升,断言 generation 递增、每个 task 最多一个 canonical commit。

指标、发布与安全回滚

核心指标包括 claim 争用率、过期回收延迟、续租失败率、fenced commit 数、同任务 attempt 数、未知副作用积压、废弃计算成本和从 READY 到 canonical commit 的 p95。fenced 为零不一定健康,可能是验证根本没执行;门禁还要统计所有接受点成功校验 epoch 与 generation 的覆盖率,目标为 100%。

上线先双写旧 claim 与 claim.v1,shadow 比较赢家,再从幂等读取扩到条件写。异常时先停止新领取并提高 schedulerEpoch;claim、renew、工具接受点和 commit 都拒绝旧 epoch,旧二进制若不识别该字段便不得回流。确认活跃 lease、UNKNOWN effect 与待提交结果归零后才切池。Agent 故障恢复顺序 可以决定重试或人工接管,但任何恢复路径都不得绕过 epoch 与 generation 比较。

Related

相关推荐