目标 :减少 event 表写入量,不改 Schema,不改 UI,不破坏重放机制
版本 :基于
D:\agent\opencode(dev 分支,v1.17.18)
2026/7/12
一、设计原则
-
不改 Schema --- 不新增事件类型,不修改表结构
-
不改 UI --- Web/CLI 消费端保持完全不动
-
最小改动 --- 聚焦合并写入操作,不做架构层面的重写
二、优化一:合并 step-finish + cleanup
现状
processor.ts 中,每个 LLM turn 至少产生 2 次 updateMessage 调用:
-
step-finish (L456):写入
finish、cost、tokens -
cleanup (L596):写入
time.completed
加上 prompt.ts 中创建 assistant message 的那次写入,同一条消息在 event 表中留下 3 条 message.updated.1 记录。
修改方案
让 step-finish 承担 time.completed 的写入,cleanup 仅在异常场景下兜底:
typescript
// step-finish 分支 (L456):
ctx.assistantMessage.time.completed = Date.now() // 新增
yield* session.updateMessage(ctx.assistantMessage)
// cleanup 分支 (L596):
// 如果已完成则跳过,仅作兜底
if (!ctx.assistantMessage.time.completed) {
ctx.assistantMessage.time.completed = Date.now()
yield* session.updateMessage(ctx.assistantMessage)
}
效果
| 指标 | 改前 | 改后 |
|---|---|---|
| 正常流程写入次数 | 2 次 | 1 次 |
| 异常流程覆盖 | ✅ | ✅(cleanup 兜底) |
风险等级:低 --- cleanup 保留完整兜底逻辑,异常场景不受影响
三、优化二:合并创建 + 首次更新(暂缓)
现状
prompt.ts:1201 创建 assistant message 时触发一次 updateMessage,此时消息内容为空(content 为空,cost=0,tokens=0),仅为占位记录。随后 processor.ts:456 的 step-finish 再次更新,写入实际数据。
改造思路
创建时不立即发布 durable event ,仅在内存中维护消息记录,延迟到 step-finish 阶段再统一写入。
决策
暂不实施 。该改动涉及 prompt.ts 与 processor.ts 之间的交互逻辑调整,改动面较大,收益与优化一重叠,优先级靠后。
四、优化三:PartUpdated 去 durable(需验证后实施)
现状
PartUpdated(message.part.updated.1)在 event 表中占据 260,911 行 / 1,047 MB ,约占总数据量的 68%。
每条 updatePart 调用都会将完整的 part JSON 写入 event 表。streaming 过程中,text-start、text-end、reasoning-start、reasoning-end、step-start、step-finish 等事件都会触发写入。
而 PartDelta(message.part.delta)已经采用了非 durable 的设计,可作为参照。
修改方案
schema/src/v1/session.ts:612 中移除 PartUpdated 的 durable 标记:
typescript
PartUpdated: define({
type: "message.part.updated",
// 去掉 ...options 中的 durable 配置
schema: {
sessionID: SessionID,
part: Part,
time: Schema.Finite,
},
}),
安全性验证
需要确认两点:
-
Web UI 会话重放是否依赖 event 表中的
PartUpdated?检查
packages/app/src/context/global-sync/event-reducer.ts:该文件处理message.updated和session.updated,不处理message.part.updated。 -
V2 sync 是否以 event 表为源?
检查
packages/app/src/context/server-session.ts:786的apply方法:同样只处理message.updated,不处理part.updated。
结论 :PartUpdated 的 event 表行对 Web UI 没有实际消费价值,UI 依赖的是 MessageTable 和 live stream。
效果
| 指标 | 数值 |
|---|---|
| 消除现有行数 | 260,911 行(存量不删,但不再增长) |
| 消除数据量 | 1,047 MB(不再增量) |
风险等级:中高 --- 需充分验证 GlobalBus 推送路径是否依赖 event 表写入(初步判断不依赖)
五、优先级
| 顺序 | 优化项 | 效果 | 风险 | 改动量 | 状态 |
|---|---|---|---|---|---|
| 1 | 优化一:合并 finish+cleanup | 省 50% 的 message.updated.1 |
低 | 2 行 | 立即执行 |
| 2 | 优化三:PartUpdated 去 durable | 省 68% 的 event 行 | 中高 | 1 行 | 先验证后执行 |
| 3 | 优化二:合并创建+首次更新 | 省 33% 的 message.updated.1 |
中 | 较大 | 暂缓 |
六、验证步骤
优化三验证流程
-
grep 全仓搜索
PartUpdated的消费者,确认无 projector 或 reducer 依赖 -
在测试环境将
PartUpdated改为非 durable,运行一个完整 LLM turn -
检查 Web UI 的会话重放是否正常(消息内容、part 顺序、streaming 效果)
-
对比 event 表增量,确认减少量符合预期
验收标准
-
□ 优化一部署后,正常流程每条 assistant 消息从 2 次更新减少为 1 次
-
□ 优化三部署后,event 表日增量减少 60% 以上
-
□ Web UI 所有功能(消息展示、会话重放、streaming 效果)无异常
-
□ CLI 交互无异常
七、执行清单
-
优化一 --- 修改
processor.ts,2 行改动,低风险,立即上线 -
验证优化三 --- grep
PartUpdated消费者,确认无依赖 -
优化三 --- 修改
session.ts,1 行改动,验证后上线
完成后,跑一个完整的 LLM turn,对比 event 表增量即可验证效果。预计三项全部落地后,event 表整体写入量可减少 60%-70%。