← 返回首页
方法 · 协作协议

任务单:让 AI 协作可审计的最小协议

靠聊天记录管理 AI 团队,一周就会失控。我用一个九状态的简单状态机, 把「谁在做什么、做到哪一步、凭什么算完成」变成可查询的记录。

2026-10-12约 8 分钟任务编排 / 工程
目录
  1. 一、聊天记录为什么不够用
  2. 二、九个状态,够用了
  3. 三、一条不变量,挡住大部分脏数据
  4. 四、序号从哪来:别去扫表
  5. 五、幂等:重复投递是常态
  6. 六、最小实现

前面写过我在即时通讯群里跑人机混合团队(另一篇)。 群解决了信息可见性,但很快暴露出一个新问题: 群消息是一条扁平的时间线,而任务是有状态的。

一、聊天记录为什么不够用

当同时在跑十几件事,群里会频繁出现这样的对话:

这个昨天不是说完了吗?
没有,还卡在评审。
哪个评审?我记得已经过了。
……往上翻一下,大概三百条之前。

这类沟通每天要发生好几次,每次消耗几分钟,而且没有任何沉淀。 更麻烦的是三件事你说不清:

所以我把任务从消息里抽出来,做成独立实体:任务单。 群消息还是随便聊,但任何要推进的事,必须在任务单里有一个位置。

二、九个状态,够用了

状态机不需要复杂,需要的是每个状态都有明确的责任人和动作。 我的九个状态是这样的:

状态含义谁来动
草稿已创建但还没派出去发起方
已派单任务已下发,等待接手执行方
进行中已接手,正在做执行方
已提交执行方自认完成,等验收验收方
已验收验收通过—
已关闭归档,不再变动—
受阻缺条件,做不下去发起方/协调
退回验收不通过,打回重做执行方
已取消不做了—

前六个是主干流程,后三个是异常分支。异常分支不能省—— 没有"受阻",执行方就会硬扛或者假装完成;没有"退回",验收就变成了橡皮图章。

一个容易被忽略的点:"已提交"和"已验收"必须分开。 执行方说做完,和验收方认可做完,是两件事。合在一起,等于让执行方自己给自己发合格证。

三、一条不变量,挡住大部分脏数据

任务单的当前状态不单独维护,而是由事件推出来。 每次状态变化记一条事件,任务单上的状态永远等于最后一条事件的目标状态。

status == events[-1].to_status

这条不变量看着平淡,但它是整个设计的支点:

反过来,如果既存状态又存事件,两者迟早会打架。而状态一旦和事实脱节, 所有基于它做的统计、看板、催办都会跟着错。

四、序号从哪来:别去扫表

事件需要一个递增序号来保证顺序。最直观的写法是插入时取 MAX(seq) + 1,我一开始也这么干,然后踩了坑:

最后的解法很朴素:序号由任务行上的计数器字段分配。 写路径统一先锁任务行、拿计数器自增、再写事件。串行化点只有一个,锁顺序固定, 既不会重复也不会死锁。

并发问题不要靠"多想一步"解决,要靠"减少一个并发点"解决。

五、幂等:重复投递是常态

我的任务单有两个入口:人手工创建,和外部系统自动同步。 自动同步意味着网络重试、消息重投是常态,同一条派单可能到达两次。

所以每个写入口都要求带一个业务唯一键,重复到达时返回"已存在"而不是再建一单:

这条看起来是工程细节,实际上是协作可信度的基础: 一旦出现重复单,执行方就会去做两遍,验收方会看到两条互相矛盾的记录, 然后所有人开始不信任这个系统,退回到翻聊天记录。

六、最小实现

如果要从零做,最小可用的集合就这么几张表:

task
  id            主键
  task_code     业务编码(唯一)
  title         一句话
  status        当前状态(由事件推导)
  owner_id      当前责任人
  next_seq      事件序号计数器
  created_at / updated_at

task_event
  task_id
  seq           序号(由 task.next_seq 分配)
  from_status
  to_status
  actor         谁做的(人或 AI 的工号)
  reason        为什么
  evidence_ref  证据引用(链接、文件哈希、命令输出摘要)
  created_at

特别提一下 evidence_ref 这个字段。它让"完成"从一个形容词变成一个可点开的链接。 验收方不用相信任何人的口头承诺,点开看就行。

这也是我整套协作机制里,唯一一个必须提前设计好的字段。 其他的都可以后补,这个后补不了——因为历史任务的证据已经丢了。

写于 2026-10-12 · 来自自己那套任务调度系统的设计记录 下一篇:子 Agent 拆与不拆的边界 →