Skip to content
My Blog
Go back

第 05 课 · 会话日志与「模型可见 ⟺ 已记录」

前置:第 04 课 | 预计时长:60 分钟 | 动手环节:必须完成

你将学会

问题引入

第 04 课你看到请求体来自 session.deriveMessages()。这一课回答随之而来的问题:这个日志凭什么能重建请求?如果有人往日志里塞了一个模型看得见但没走正常通道的事件呢? dsh 的答案是三层防御,并且每一层都有具体的代码载体。

正文

5.1 只追加的事件日志

对应 packages/core/session/src/index.ts。内存结构是私有数组,唯一写入点append()

class Session {
  private log: SessionEvent[] = []        // index.ts:424 唯一存储
  append(type, data, opts?) {
    event = { type, seq: this.log.length, time: now(), data: deepFreeze(snapshot(data)) }
    // 类型层:产生模型消息的事件必须带 SurfaceIntent(index.ts:602-606)
    this.log.push(event)                  // index.ts:641 唯一写入点
    emit('session/event', event)          // 持久化等订阅者在此接收(同步入队,不阻塞)
  }
  deriveMessages() {                      // index.ts:724-745
    // 只遍历 surfaceOp 标记维护的有序节点;裸事件不出现在请求里
  }
}

三个契约:

  1. seq = log.length:连续性契约,持久层靠它校验完整性(第 06 课)。
  2. 事件接纳即 deepFreeze + 无损 JSON 快照:对外只给冻结快照,没人能改写历史。
  3. 写入即广播session/event 是 emit 模式——持久化、投影、遥测都是它的订阅者(第 06 课)。

5.2 三层强制:模型可见 ⟺ 已记录

目标:到达模型的任何内容必须能从 session log 重建;反之,新的模型可见输入必须新增一种会话事件。三层防线:

载体拦什么
类型层SurfaceIntent 编译期约束(session/src/index.ts:602-606「会产生模型消息的 append」必须声明 surface 意图,写错编译不过
派生层deriveMessages() 只走 surface 节点(:724-745裸事件天然不在请求里——派生器根本不看它们
运行时层llm/stream 前逐字节比对(第 04 课的 agent-loop/src/invariant.ts:21-52任何绕过前两层的路径,在发请求当场失败

这条不变量连上下文压缩都遵守:摘要替换通过 surfaceOp: replace事件完成,派生历史随之重建(第 06 课与 compaction 相关部分见 packages/compaction/)。

5.3 SessionEvent:按 type 判别的事件

SessionEventtype 判别(packages/core/session/src/types.ts:396-404)。你会遇到的事件类型家族(非穷举):

surface 事件附带 surfaceOp / sourceEventSeqs 字段,驱动派生层的节点维护。

5.4 为什么是「事件溯源」而不是「消息数组」

对比传统 agent 框架的 messages[]

代价是纪律:每个新模型可见输入都要设计事件类型与 surface 语义——这正是三层强制的意义:让「偷懒直发」在类型期或运行期爆炸。

动手环节

  1. 校验一个快照:取第 04 课用的录制会话,写个 10 行脚本(或肉眼)检查:seq 是否从 0 严格递增;每个 tool/resultsourceEventSeqs 是否指向同 step 的 tool/callturn/step 是否开闭配对。
  2. 找到 SurfaceIntentgrep -rn "SurfaceIntent" packages/core/session/src/ | head,找出类型层把什么约束编码在哪里。
  3. 推演题:假设某插件想「只在 UI 上显示一句提示,不让模型看见」。它应该 append 什么?(提示:log-only 事件没有 surface 意图,不会进派生。)反过来,如果它想让模型看见,必须做什么?

自检清单

常见误解

延伸阅读


Share this post:

Previous Post
崩溃的 turn 要关闭,不是截断:dsh 的会话持久化
Next Post
逐文件 100% 覆盖:dsh 的质量门禁长什么样