如何把整个会话事件日志折叠成上下文时间线?dsh-context 的 fold 设计与有界状态技巧
2026/8/24 8:50:31 网站建设 项目流程

如何把整个会话事件日志折叠成上下文时间线?dsh-context 的 fold 设计与有界状态技巧

【免费下载链接】dsh-contextThe best DeepSeek Harness plugin for context insight and management, with context dashboard / browser and context command, for context statistics, composition, breakdown, evolution details, understanding how the context is made of, and how it evolves. 一站式 DeepSeek Harness 上下文可视化插件,Context 面板及浏览器与 Context 命令,透视上下文组成、演进、压缩、剪枝等事件与动作。项目地址: https://gitcode.com/gh_mirrors/ds/dsh-context

dsh-context 是 DeepSeek Harness 的一站式上下文可视化插件。它的核心是一座"折叠器"(fold):把整条会话的持久化事件日志逐条重放,折叠成一条按请求划分的上下文时间线(context timeline),同时用一套有界状态(bounded state)技巧,保证状态体积恒定——会话再长,内存与存储占用也不会失控。本文带你读懂这套设计背后的 5 个关键技巧。

为什么需要上下文时间线:事件日志的三个问题

DeepSeek Harness 会把每一次用户消息、助手回复、工具调用、注入、压缩(compaction)和剪枝(prune)都写进一份只追加的事件日志。这份日志是"事实之源",但直接读它有三个痛点:

  1. 太碎:一次对话产生上百条事件,看不出"每次请求时上下文由什么组成";
  2. 太长:日志只增不减,全量载入内存不可行;
  3. 会"缩水":压缩事件把旧消息替换成新摘要,事后重建"当时的上下文"变得很困难。

dsh-context 的解法不是解析日志全文,而是维护一份折叠状态:每来一条事件,就把状态向前推一步;状态本身记录"当前模型看到的表面(surface)"、"每个请求的 token 组成"和"压缩/注入等事件"。最终这份状态被投影成 UI 里的上下文时间线。

fold 如何工作:把事件日志逐步重放成上下文状态

在源码 src/host/fold.ts 中,折叠器是一个纯函数三件套(init / apply / view),注册为会话投影单元contextTimeline

  • init:创建一个空状态(createTimelineState)——表面列表为空、各类 token 和为 0;
  • apply:框架每提交一条session/event,就驱动一次applyTimeline,把状态向前推一步;
  • view:会话结束或推送时,用buildTimelineView把状态快照成浏览器渲染的时间线数据。

apply 步骤中几个巧妙的设计:

  • 无关事件返回同一引用。turn 边界、流式 chunk 等不影响时间线的事件,apply 直接返回原状态对象;框架靠"引用是否变化"判断是否要推送更新,避免无谓的界面刷新。
  • 变更时做惰性浅克隆。只有真正修改某条路径时才复制相关数组,未动的部分与旧状态共享,兼顾了"不可变"契约与性能。
  • 影子序号协议。压缩事件先"武装"一批将被遮蔽的节点序号(pendingShadowedSeqs),紧随其后的替换事件按序号精确移除——保证我们的分类记账与官方 token 计量的口径分毫不差。项目还用差分测试(tests/diff-fold.ts)在真实会话日志上验证折叠结果与官方投影完全一致。

有界状态技巧:让状态永远保持"小而全"

折叠状态会被持久化,所以它必须永远是纯 JSON 且有上界。dsh-context 用了五道上限(src/host/config.ts中可配置):

按整轮(turn)裁剪,绝不截断半轮

请求记录超过maxKeptTurns(默认 300 轮)时,折叠器从新到旧数"轮次运行",整轮地丢弃最旧的部分,永远保留"最近 N 个完整轮次"。测试里 400 轮 × 4 步 = 1600 条请求会被裁到最近 300 轮的 1200 条,且边界正好落在整轮起点(见tests/host.spec.ts的 400 轮用例)。这样按"轮"浏览时永远能展示完整的近期轮次,不会出现半截轮。

软限 + 硬限,双保险

整轮裁剪之上还有maxRequestSteps(默认 1500 步)作为硬底限,防"单轮几百步"的病态会话;事件列表maxEvents(默认 400)与归档maxArchiveNodes(默认 400)同样保留"最新尾部"。裁剪只截断历史明细,不改变状态形状,所以调整这些配置无需迁移旧状态。

消费即删的映射:callNames 不随会话生长

tool/call事件会把"调用 ID → 工具名"暂存进状态;对应tool/result折叠进来时立刻把该条目删掉。映射因此始终只有"未返回的待决调用"大小,而不是整个会话的工具调用总数——对一个每步都要浅拷贝的持久化状态来说,这点至关重要。

永不裁剪的累计量

被裁剪的只有"明细"(请求记录、事件、归档),而成本累计、图片总数这类运行总计永不裁剪——即使明细早已被保留窗口裁掉,会话成本估算依然覆盖完整日志。这是"有界"与"完整"的分工:明细可丢,总数不丢。

纯 JSON 前置条件:一个隐蔽的坑

持久化投影缓存要求状态是纯 JSON:值为undefined的属性会让整个检查点不可序列化,进而让整个会话的缓存写入失败(包括重启后恢复会话列表依赖的标题行)。因此折叠器对所有可选字段(model、turn、step、pendingShadowedSeqs…)采用"有值才写入、用完即delete"的写法,从不留下undefined属性——这也是状态里反复注释强调的规则。

被裁剪的历史去哪了:归档与"近似重建"诚实声明

被压缩/替换移出的表面节点不会直接丢弃,而是打上gone时间戳存进归档(archived),供上下文浏览器逐步重建"任意一次请求当时组装了什么"。归档同样有界:既按条数封顶,也裁掉"早于最旧保留请求"的移除记录,并把被裁掉部分记为archiveFloor——UI 据此明确提示该步骤的重建只是"近似",而不是静默少显示。有界,但从不撒谎。

上图是一次真实会话:48 轮增长到约 563k token,一次压缩(✂ 标记)回收 −535.5k 后从小窗口继续。

切到 Step 粒度悬停任意一根柱,还能立刻看到该步的轮次/步号、时间戳与"估算 vs 服务商上报"的 token 对照:

从状态到界面:时间线如何驱动上下文浏览器

buildTimelineView把状态投影成推送给浏览器的快照,其中两个细节值得注意:

  • 发送切片而非全量:只推送"最新 N 个节点 + 全部注入节点"(注入项如 AGENTS.md 总是排在最前,若只推尾部会"有 token 和、没有条目"),并附droppedNodes/surfaceFloor告知客户端哪些被截;
  • 事件归属:每个压缩/注入/切换事件被归因到它前后的请求("Step 2→3"或"Turn 50 Step 8 → Turn 51 Step 1"),双指针一次遍历完成,正是历史图上 ✂ 标记的由来。

浏览器端拿到快照后,历史图悬停可直接驱动上下文浏览器实时预览该步的组装结果:

延伸阅读:fold 设计相关文件

  • 折叠器主体(apply / view / 归档 / 裁剪):src/host/fold.ts
  • 投影单元注册(contextTimeline):src/host/timeline.ts
  • 五道保留上限的配置与默认值:src/host/config.ts
  • token 计价(消息、工具 schema、图片):src/host/pricing.ts
  • 有界状态与整轮裁剪的测试用例:tests/host.spec.ts
  • 与官方投影的差分校验脚本:tests/diff-fold.ts

小结

dsh-context 的 fold 设计给出了一套通用套路:用纯函数重放事件日志维护可持久化的折叠状态,状态形状固定且全部字段有界,明细可裁剪而累计值不裁剪,被裁部分用 floor 标记让 UI 诚实声明近似性。掌握了"整轮裁剪、消费即删、纯 JSON、运行总计不裁剪"这四板斧,你就能把任何只追加的事件日志,折叠成一条永远轻量的上下文时间线。

【免费下载链接】dsh-contextThe best DeepSeek Harness plugin for context insight and management, with context dashboard / browser and context command, for context statistics, composition, breakdown, evolution details, understanding how the context is made of, and how it evolves. 一站式 DeepSeek Harness 上下文可视化插件,Context 面板及浏览器与 Context 命令,透视上下文组成、演进、压缩、剪枝等事件与动作。项目地址: https://gitcode.com/gh_mirrors/ds/dsh-context

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询