cosmic的共享业务状态确实是重点,不过文章里面没有仔细展开,这里你可以展开一下吗?尤其是并发和冲突

比如 Mia 和 Marcus 同时处理一篇教程: 两人都读到第 12 版。Mia 修改标题和正文,提交成第 13 版;Marcus 根据第 12 版修正代码示例,随后把整篇文章写回。 如果接口是“最后一次写入覆盖前面的内容”,Mia 的修改就可能消失。即使他们写的是不同字段,也可能出现另一种问题:Mia 已经把教程改成新版 API,Marcus 补的示例却还是旧版。 所以要分清两件事: 共享业务状态解决“大家围绕哪个事实协作”。 并发控制解决“大家同时行动时,怎样保证事实不会被破坏”。 Cosmic 原文强调了第一件事,但没有充分展开第二件事。它说结构化字段能避免 Agent 相互踩踏,这个表述需要加条件:结构化能缩小冲突范围,不自动保证并发安全。 下面是基于这个场景的工程设计,不是 Cosmic 已公开实现的细节。 一、共享的不是一段记忆,而是几个有明确身份的对象 不要把所有信息都塞进“文章内容”和“团队聊天记录”。至少应该区分: 文章:稳定的文章 ID、当前草稿版本、线上版本。 修订:某一版具体的标题、正文、代码示例,以及它基于哪个旧版本产生。 任务:谁负责什么、依赖哪些修订、当前执行状态。 审核:审核了哪个版本、结论是什么、由谁批准。 发布:准备发布哪个版本、执行是否成功、外部系统返回了什么。 指标:针对哪个页面、哪个时间窗口采集的数据。 这样,“文章已经批准”就不再是一个模糊布尔值,而是: Tony 批准了文章 A 的第 13 版。 之后 Mia 又写出第 14 版,并不意味着第 14 版也获得批准。线上仍然可以是第 13 版,草稿继续迭代。 这也是共享业务状态比共享聊天记录重要的地方:不是大家都记得“Tony 说可以发”,而是系统知道他批准的到底是什么。 二、减少冲突,先靠写入边界,不靠抢锁 以这三个 Agent 为例,我更倾向于: Mia 维护文章草稿。 Marcus 提交技术修正建议,或生成独立的技术修订。 Lisa 写增长分析和内容需求,不直接覆盖正文。 发布由受控执行器完成。 例如 Marcus 的交付物可以是: 针对文章 A 的第 12 版,代码示例存在问题,建议替换某个段落;附修改内容和验证结果。 而不是拿着一份旧文章直接覆盖 CMS。 这种安排把大量“两个 Agent 同时改同一个对象”的问题,转换成“多个 Agent 并行产出提案,由明确的负责人整合”。 但写入边界必须由工具和权限执行,不能只在提示词里写“请不要修改正文”。 而且单一负责人也不是完整的并发保障:同一个 Mia 可能有两个并行任务,或者一次工具调用被重试,所以存储层依然需要保护。 三、短时间提交用乐观并发控制 Agent 生成内容可能花几分钟,但真正提交只需要很短时间。通常不应该让它从开始思考到结束一直持有数据库锁。 更合适的是: 读取第 12 版。 在锁外生成修改。 提交时同时声明“只有当前版本仍是 12,才接受这次修改”。 后端把检查版本和写入作为一个原子操作。 如果 Mia 已经提交了第 13 版,Marcus 的旧提交就被拒绝,并返回版本冲突。不能由 Agent 先查询“还是不是 12”,再单独写入;两步之间仍可能插入其他写入。 这里还有一个很容易踩的坑: 冲突后,不能只把请求里的版本号从 12 改成 13,然后重发旧内容。 那等于绕过保护,继续覆盖别人。 正确动作是读取新版本和差异,判断原来的修改还能不能应用,重新整合后再提交。 因此,提交工具最好接收“基于哪个版本、要改哪些部分”,而不是只接收一整份新正文。 四、字段不重叠,不代表语义不冲突 并发冲突至少有三种。 覆盖冲突:Mia 和 Marcus 同时改正文。版本检查可以发现。 语义冲突:Mia 把标题改成“无需 API Key”,Marcus 在正文中加入“先配置 API Key”。修改字段不同,数据库完全可能接受,但内容自相矛盾。 流程冲突:Tony 批准第 13 版的同时,Mia 提交第 14 版。如果发布工具只检查“approved=true”,就可能发布未经批准的内容。 对应的处理方式也不同: 覆盖冲突靠条件写入、版本检查。 语义冲突靠依赖标记和整合后的校验。例如技术审核绑定具体修订;正文或代码发生相关变化后,旧审核失效,需要重新检查。 流程冲突靠状态转换条件。发布时检查“目标版本是否有有效批准”,而不是只检查文章有没有被批准过。 关键不是所有改动都必须重新审,而是明确哪些改动会使哪些结论失效。初期整版失效虽然保守,但比过早做精细依赖判断更容易保证正确。 五、任务抢占需要租约,但租约本身还不够 假设 Lisa 创建了“改写教程 A”的任务,两个 worker 同时发现它。 不能让它们分别读取“待处理”,然后都宣布自己领取成功。领取必须是原子状态转换:只有一个执行者能把待处理改成执行中。 如果执行者崩溃,还需要允许超时接管,因此通常使用有期限、可续期的租约。 但会出现一个更隐蔽的问题: worker A 卡住。 租约到期,worker B 接手。 A 又恢复了,继续提交自己的结果。 这时仅靠“锁已经过期”挡不住 A。 可以为每次接管发放递增的执行代次,例如 A 是第 7 次,B 是第 8 次。提交结果时,后端检查代次,只接受当前有效执行者的写入,拒绝已经失去所有权的 A。 这就是 fencing 的核心思路:不是相信旧执行者会主动停止,而是让写入端拒绝它。 如果涉及外部发布,而外部平台不支持这种校验,就必须经过能检查执行代次的受控发布入口;不能以为数据库里的租约能自动约束外部 API。 六、重试不能变成重复执行 另一个常见情况: 发布接口实际成功,但返回结果时超时。 Agent 以为失败,再调用一次。 如果没有幂等机制,就可能重复发布、重复通知,甚至重复扣费。 可以给一次明确的业务动作分配稳定的幂等键,例如: 发布文章 A 的第 13 版。 同一个动作重试时沿用这个键,而不是每次工具调用都随机生成一个新键。 但本地存一条“执行过”也不等于万无一失:如果外部已经成功,本地还没记账就崩溃,仍然会出现不确定状态。优先使用外部系统提供的幂等能力;没有的话,需要查询外部结果、对账,不能盲目重发。 七、业务状态和通知要避免各成功一半 例如 Mia 提交了第 13 版,系统还要通知 Marcus 审核。 如果先保存,再发消息,可能保存成功但消息发送失败,文章永远没人审核。 如果先发消息,再保存,Marcus 可能收到一个根本还不存在的版本。 常见解法是 transactional outbox: 在同一个数据库事务中,保存修订,同时写入“需要发送审核通知”的事件。 后台再把事件投递出去。 投递失败可以重试,接收端处理重复事件时保持幂等。 通知最好携带对象 ID 和修订 ID。Marcus 收到后读取权威状态,而不是把消息里的旧摘要当成最新事实。 这也意味着:群聊、Slack 和 Agent 间消息适合提醒“发生了什么”,不适合独自承担“现在究竟是什么状态”。 八、把它串起来,一个可靠的协作过程是什么样 Mia 和 Marcus 都基于第 12 版工作。 Mia 提交第 13 版,条件写入成功。 Marcus 提交基于第 12 版的技术修正,系统发现版本已变化,不允许直接覆盖。 Marcus 读取第 13 版,重新检查技术修正是否适用,形成新的提案;整合后产生第 14 版。 技术检查和 Tony 的批准都绑定第 14 版。 发布器执行“发布 A 的第 14 版”,使用稳定幂等键,并核验批准和任务所有权。 此时 Mia 即使又生成第 15 版,也不会被顺带发布。线上是第 14 版,最新草稿是第 15 版,两种状态都清楚。 Lisa 后续分析时,记录指标的时间窗口,并尽可能关联当时的线上版本。否则“改完之后转化好了”可能混入旧版流量,反馈闭环也会失真。 所以我认为,Cosmic 这条思路真正值得展开的,不是“让所有 Agent 共用一个 CMS”,而是: 并行探索可以宽松,提交业务事实必须受控。 Agent 负责提出修改、处理冲突和做判断;后端负责保证版本、权限、状态转换和副作用边界。不能让“请先检查一下有没有冲突”这种提示词,承担数据库和工作流系统应该提供的正确性。 参考: Cosmic 原文: https://www.cosmicjs.com/blog/how-to-build-an-ai-agent-team AWS 乐观锁与版本检查: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBMapper.OptimisticLocking.html AWS Transactional Outbox: https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/transactional-outbox.html