Files
deepseek-harness/packages/core/session/README.zh.md
T

12 KiB
Raw Blame History

description, kind
description kind
面向用户与维护者的事件溯源会话日志与内存存储说明,用于构建、检查或扩展每个 agent 交互背后的持久记录。 package-reference

@deepseek-ai/dsh-session

English | 中文

概述

dsh-session 提供仅追加的会话日志,记录 agent(智能体)的完整交互历史——每个模型可见事实都流经的单一真源。LLM(大语言模型)消息历史由日志派生deriveMessages()),从不另行存储,因此回放就是对同一批事件重新派生,压缩(compaction)也可以遮蔽较旧的表层条目而不删除历史。该包还提供内存存储(ctx.sessions)、插件通过声明合并扩展的类型化 SessionEvent 词汇,以及为产生消息的事件排序的 surface 层。持久化刻意是独立关注点:后端订阅 session/event 并在 session/flush 时刷新。作为任何 agent 会话的基础时请选择本包;它本身不运行模型调用。

目录


使用本包

在必须存在会话的任何地方挂载 dsh-session。它在内存中创建并持有事件溯源的 Session 实例;持久存储由订阅 session/event 流的持久化插件叠加。

创建与检查会话

ctx.sessions.create() 构建绑定到调用方 fiber 的实时会话;get(id)list() 查找会话,fork() 从实时会话的稳定前缀创建子会话。

const session = ctx.sessions.create(sessionId, { meta: { cwd: '/workspace' } })
ctx.sessions.get(sessionId)      // the live session
ctx.sessions.list()              // every live session, in creation order

追加与派生

session.append(type, data, opts?) 提交一个类型化事件——它先快照并冻结载荷、校验其为无损 JSON,再通知观察者。session.deriveMessages() 把日志投影为模型看到的 Message[],采用增量且有缓存的方式:

session.append('user/message', { role: 'user', content: [{ type: 'text', text: 'hello' }], source: { kind: 'user' } },
  { surfaceOp: 'append' })
session.deriveMessages()         // the derived model history

表层事件(user/messageassistant/messagetool/result)必须声明如何进入有序 surface;原始分片、边界与其他仅日志事件从不产生消息。

派生会话的 fork

ctx.sessions.fork(source, boundary?, childSessionId?) 选取截至 boundary 事件序号(含该事件)的源事件(默认:当前最后一个事件),要求所选前缀结束时没有开放轮次,再创建带谱系元数据的实时子会话。必须在轮次中途分支的工具时委派会裁剪到已完成前缀。

刷新持久状态

ctx.sessions.flush(session) 分发需等待完成的持久性检查点:每个持久化监听器都会刷新,调用在所有监听器结算后完成。需要立即持久性屏障的生产方应等待它,而不是假定写后刷新已完成。


理解实现

实现细节——点击展开

本节解释该包如何实现上述行为;可观察约定已在使用本包中完整说明。

设计理念

该包建立在事件溯源之上:Session 是类型化 SessionEvent 的仅追加日志,其他一切——模型历史、transcript、遥测、标题、持久化——都从这条流派生。surface 是派生投影:一个增量管理器校验追加候选、根据已提交事件推进有序视图,并跟踪每次已提交重写都会递增的 replaceGeneration。模型可见即已记录:任何到达模型请求的内容都必须能从日志重建。共享的行编解码器在事件序列与紧凑行之间无损转换,逐字保留无法识别的事件,并拒绝形态错误的行。持久化后端决定是否打包写入;有界历史传输可以使用同一种行,同时保留完整逻辑区间,并为需要 token 边界的消费方提供精确解码。

请求 header

request/header 存储非历史请求 envelope 的完整规范快照,原因为 initialresumechangeseries。显式消息序列起点或表层替换会在 envelope 不变时写入 series 快照;同时发生变化时使用 startsSeries: true。同一序列内的步骤、重试与普通后续轮次继承最新快照。adapterDefaults 区分由适配器解析的值与显式设置,foldRequestHeader() 选择最新快照。这种自包含记录以每个消息序列增加存储为代价,支持局部窗口渲染与精确重建;细节由可重建请求 Agent Note负责。

源码地图

文件 职责
src/index.ts 插件入口:SessionStore 服务、存储生命周期、forkflush
src/types.ts SessionEventMapSessionEventUserMessageSessionHeaderTurnEndReasonMap
src/surface.ts 有序 surface 投影、替换校验、deriveEventMessage
src/request-header.ts request/header 折叠与重建
src/json.ts 无损 JSON 校验与快照
src/chunk-rows.ts 供持久化后端使用的共享紧凑行存储编解码器
src/repair.ts 崩溃遗留日志的冷修复
src/invariant.ts 不变式配套:序号、轮次/步骤闭合、工具调用/结果配对

追加校验

每次追加都会执行一趟递归处理,对每个嵌套值只读取、校验并复制一次,因此有状态的 getter 无法给校验提供一个值、给存储提供另一个值。非无损 JSON 载荷(BigInt、循环、稀疏数组、-0、特殊原型)会在追加位置被拒绝,先于任何后端刷新。表层事件还会校验标记形态、被引用的源事件 seq,以及替换的完整遮蔽节点覆盖。

派生历史

deriveMessages() 把每个 surface 节点的投影缓存一次,每次调用都返回共享、深度冻结消息之上的新数组;三种 surface 事件类型(user/messageassistant/messagetool/result)各自投影自己的消息种类——user 内容原样、带提供方与模型的组装 assistant 消息,或 user 角色的工具结果。surface 重写会重建投影——不存在原始日志回退,因此 surface 是派生历史的唯一来源。

请求头

循环在每个循环实例边界及变更时记录完整规范 request/header 快照(调用配置、适配器默认值、渲染后的系统提示词、组装后的工具 schema);foldRequestHeader(events) 通过选择最新快照来重建它,使每个对话请求都成为日志的纯函数。路由元数据(request/context)是独立的已记录状态,仅在提供方、模型或容量变化时追加。


进一步探索

包级约定对大多数消费方已经足够;需要周边领域时再阅读以下页面。


模型体验

派生消息历史

模型看到什么

模型会原样接收 user/messageassistant/messagetool/result surface 条目中的完整消息——标识、角色、来源与内容块都与创建时确定的值相同,投影从不生成标识。直接提示词与注入上下文仍是彼此独立的 user/message 事件,各事件的来源会保留其出处。分片、边界、用量与其他仅日志事件不会添加消息。

Token 影响

追加的 surface 条目会在后续步骤中重新发送。replace surface 操作会从未来输入中移除被遮蔽条目,但不删除其原始日志记录。

KV Cache 影响

追加的 surface 条目会保留可复用前缀。即使底层事件日志保持仅追加,replace 操作也会从首条被遮蔽消息起使缓存复用失效。

崩溃修复结果

模型看到什么

如果恢复发现 assistant 工具请求没有持久 tool/call,其合成 TOOL_NOT_STARTED 结果内容为 The tool call was interrupted before the Harness recorded it as started. Retry it if it is still needed.。如果持久 tool/call 没有结果,其 TOOL_OUTCOME_UNKNOWN 结果内容为 The tool call was interrupted after it was recorded, but no result was durably recorded. Its outcome is unknown. Decide whether to retry from the tool semantics: retry only if the operation is read-only or idempotent; if it may have side effects, first verify external state or ask the user. Do not retry blindly.

Token 影响

未受损会话的 token 增量为零。恢复时,每个修复后的调用都会添加保留的、针对具体风险的错误文本。

KV Cache 影响

保持仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。

已记录的请求头

模型看到什么

会话会重建循环实际发送的系统提示词、工具 schema、调用配置与会话前缀。请求头事件不会向消息历史加入第二份副本;前缀在 deriveMessages() 外部前置。

Token 影响

日志记录不产生重复 token。重建的前缀、系统文本与 schema 仍会产生正常的逐请求开销。

KV Cache 影响

记录日志不会导致失效,精确重建会保持请求前缀一致。后续请求头若更改前缀、提示词或 schema,可能从第一处差异开始使复用失效。

已知限制与延期工作

这些限制说明会话存储何时需要特别留意。它们是当前包约束,不是任务积压。

  • fork() 仅在实时会话的稳定边界处切分:所选前缀结束时不得有开放轮次,且源会话必须位于存储中;fork API 不支持对已持久化但未加载的会话进行 fork。
  • SESSION_FORMAT_VERSION 固定为 0:预发布阶段不承诺广泛兼容性;Session 只接受当前 seed 形状,后端拒绝任何其他版本,每个不认识的事件类型也会拒绝重建(机制)。
  • TurnEndReasonMap 不含 ACPAgent Client Protocol)命名的 refusalmax_turn_requests 变体:受生产方约束;只有当适配器或循环首次产生这些变体时才加入。
  • fork 之外没有会话树:基于分支会话的 pi 风格条目树被推迟,除非消费方需要超越基于边界的 forking 的能力。

开发备注

维护者的工作上下文——点击展开

无。