灾难场景
想象这个场景:
meeting-copilot分析完会议,往people/alice/log.md写了一条"Alice 负责下周评审"chat-analyst分析完群聊,往同一个文件写了"Alice 在群里抱怨排期紧张"- 用户手动编辑,在同一个文件加了"Alice 想转岗"
三个写入源,同一个文件,不同时间,不同格式。
如果没有协议,你会得到:
- 格式混乱(有的用表格、有的用列表、有的用纯文本)
- 时间乱序(后写的在前面)
- 来源不明(分不清哪条是会议产出、哪条是聊天信号、哪条是主观判断)
- 互相覆盖(最后写入的人"赢了",之前的内容消失)
核心框架:R1-R4 规则
经过 3 次数据不一致事故,我最终收敛出 4 条规则:
graph TB
subgraph "R1-R4 跨源写入协议"
R1["R1: 追加不覆盖\n只 append,从不 overwrite"]
R2["R2: 源头标签\n每条写入标明来源和时间"]
R3["R3: 综合分离\n不同源的观察各自独立存在"]
R4["R4: 时间排序\n按 evidence_date 排列"]
end
R1 --- R2
R2 --- R3
R3 --- R4
style R1 fill:#27ae60,color:#fff
style R2 fill:#3498db,color:#fff
style R3 fill:#9b59b6,color:#fff
style R4 fill:#e74c3c,color:#fff
R1: 追加不覆盖
最重要的一条。 所有技能向共享文件写入时,只能 append,不能 overwrite。
<!-- 正确:追加新块 -->
### [meeting-copilot] 2026-04-15 — 周会信号
- decision: 确定下周灰度
- evidence: meetings/2026-04-15-weekly/
### [chat-analyst] 2026-04-15 — 群聊信号
- risk: 排期压力增大
- evidence: comms/groups/team-chat/2026-04-15.md
<!-- 错误:覆盖了之前的内容 -->
## 最新状态
当前进展正常,下周开始灰度。
为什么 append?因为信息不互斥。会议说的和聊天里表现的可能矛盾——但这个矛盾本身就是有价值的信号,不应该被"最新版本"覆盖掉。
R2: 源头标签
每条写入必须标明:
- 谁写的(哪个技能/人)
- 什么时候的事(evidence_date,不是写入时间)
- 证据在哪(指向原始数据的路径)
### [chat-analyst] 2026-04-15 — 某群信号
- type: risk
- content: 排期压力增大,组长明确表示"来不及"
- confidence: high
- evidence: comms/groups/project-alpha/2026-04-15.md L23-L28
为什么需要 evidence 路径? 因为 AI 会犯错。当你质疑某条信号时,能直接跳到原文确认——而不是"信就信,不信就删"。
R3: 综合分离
不同源的观察各自独立存在,不要试图在写入时就做综合判断。
<!-- 正确:各自独立 -->
### [meeting-copilot] 2026-04-15 — 项目评审
- signal: 组长表示"进展顺利,无阻塞"
### [chat-analyst] 2026-04-15 — 组长私聊
- signal: "说实话有点赶,但会上不好说"
<!-- 错误:写入时就综合 -->
### 综合判断
组长表面乐观但实际有担忧(meeting vs chat 不一致)
综合判断是消费者的事,不是写入者的事。reality-sync 在做晨报时会综合两条信号——但 log 文件本身只存事实。
R4: 时间排序
所有条目按 evidence_date 排列(不是写入时间)。
如果今天回填了上周三的会议分析,那条记录应该插入到上周三的位置,而不是出现在文件末尾。
SoT 原则:写入点唯一
R1-R4 解决了"怎么写"——但还有一个更根本的问题:"谁有权写什么"。
graph TB
subgraph "SoT 分配"
direction TB
MC["meeting-copilot\n拥有: meetings/{entity}/README.md"]
PT["project-tracker\n拥有: domains/{project}/README.md"]
TP["team-pulse\n拥有: people/{account}/README.md 状态字段"]
CA["chat-analyst\n拥有: comms/signals/"]
end
subgraph "共享空间 (R1-R4 适用)"
LOG_P["people/{account}/log.md"]
LOG_D["domains/{project}/log.md"]
end
MC -.->|"R1 追加"| LOG_P
MC -.->|"R1 追加"| LOG_D
CA -.->|"R1 追加"| LOG_P
TP -.->|"R1 追加"| LOG_D
style MC fill:#27ae60,color:#fff
style PT fill:#3498db,color:#fff
style TP fill:#e74c3c,color:#fff
style CA fill:#9b59b6,color:#fff
style LOG_P fill:#f1c40f,color:#333
style LOG_D fill:#f1c40f,color:#333
规则:
README.md的 frontmatter 只有SoT 所有者能修改log.md是共享追加空间,所有有关技能都能按 R1-R4 写入- 没有人能修改别人的 SoT 字段
实际案例:team-pulse 管 people/alice/README.md 的 status: active 字段。即使 meeting-copilot 分析出"Alice 似乎要离职",它也不能改 status——只能追加到 log.md,等 team-pulse 做状态评估时读取并决策。
反模式:"可见但未持久化"
R1-R4 的最大敌人不是"格式不对"——是压根没写。
graph LR
ANALYZE["技能分析\n产出信号"]
VIEW["展示在\n消费视图中"]
SOT["写入\nSoT 文件"]
ANALYZE --> VIEW
ANALYZE --> SOT
VIEW -.->|"常见反模式"| SKIP["❌ 只展示\n不写入"]
style SKIP fill:#e74c3c,color:#fff
style SOT fill:#27ae60,color:#fff
最常见的反模式:AI 分析出了信号,在 daily summary 的表格里展示了——但没有回写到 people/log.md。
结果:你在对话里"看到了",但 5 天后 reality-sync 做全景同步时,完全找不到这条信息。因为消费视图不是持久化。
实施检查清单
如果你也在做多技能 Agent 的数据协作,这些是底线:
| 检查项 | 说明 |
|---|---|
| 每个技能声明写入权 | 它能写哪些文件的哪些部分 |
| 共享文件只追加 | 绝不 overwrite |
| 每条写入有源头标签 | 能 grep 到"谁写的" |
| 证据可追溯 | evidence 路径指向原始数据 |
| SoT 所有权明确 | 谁拥有 frontmatter,谁只能追加 log |
| 有机器验证 | 脚本检查是否真的写了(不靠 AI 自报告) |
总结:多 Agent 协作的第一难题不是"让它们更聪明"——是"让它们不打架"。R1-R4 不复杂,但它的价值在于把隐性冲突变成显性规则。没有规则时,5 个 Agent 的协作复杂度是 O(n²);有了规则,是 O(n)。