同步设计
羽忆里的“同步”不是文件级双向同步,而是多端围绕同一份服务端权威数据做状态收敛。这个问题如果定义不清楚,CLI、MCP、Web Console 和自动化任务很快就会在并发更新和缓存读取上出现互相打架的情况。
核心原则:服务端权威,客户端缓存
第一阶段明确采用下面这条原则:
服务端是唯一真相来源,客户端可以缓存,但缓存不具备最终裁决权。
这样做的原因很现实:记忆对象的冲突往往带有语义,第一阶段不适合直接引入 CRDT 或复杂本地优先同步模型。
读取同步
为了避免重复传输,读取侧推荐支持:
- ETag 条件读取
since增量拉取- 摘要字段拉取
- 按 Scope 精确同步
典型收益是:客户端可以先判断数据是否变化,再决定是否拉全量内容,从而减少无意义请求。
写入同步
写入侧至少要解决并发覆盖问题,因此推荐:
- 单条写入与批量写入统一经过服务端 Judge
- 更新接口支持
If-Match/ ETag 乐观锁 - 支持局部字段增量更新
这样可以避免两个客户端同时修改同一条稳定记忆时出现“后写入者静默覆盖前写入者”的情况。
删除同步
同步设计里最容易犯的错误,是把“本地看不到了”当成“用户要求删除”。羽忆明确不采用这种推断。
删除必须通过显式 API 完成,并推荐保留 tombstone 窗口,供其他客户端在增量同步时感知对象已经被删除。
冲突处理
Stable Memory
Stable Memory 更适合走乐观并发控制:
- 客户端携带旧版本 ETag 更新
- 服务端发现版本已变化时返回
409 Conflict - 客户端重新读取后再决定是否合并或重试
History Recall
历史材料原则上追加为主,因此并发修改远少于稳定记忆。真的发生冲突时,优先保留多个版本,而不是激进合并。
失败恢复
同步失败需要区分临时失败和永久失败:
| 类型 | 示例 | 是否应重试 |
|---|---|---|
| 临时失败 | 网络超时、429、503 | 是 |
| 永久失败 | 401、403、参数错误 | 否 |
对永久失败做无限重试没有意义,只会制造噪声和误导,因此客户端应进入抑制状态,等待用户修复配置或权限问题。
为什么第一阶段不做双向离线同步
不是因为它永远没价值,而是因为:
- 语义冲突很难做自动合并
- 客户端复杂度会显著上升
- 当前阶段的核心价值在跨 Agent 在线共享,而不是离线优先
先把服务端权威模型做稳,后续再讨论更复杂的同步拓扑,成本更可控。