DeepSeek Harness 长会话分页:Load Earlier 与 Trajectory 失效

作者
DeepSeekAgent.io 编辑部
发布
更新

长会话不能一次把全部历史送进浏览器,DeepSeek Harness 会按页加载较早事件。社区在 DSH Desktop 0.2.1-rc.3 中复现了一条稳定故障链:第一次点击 Load Earlier 正常,第二次点击后 Chat 不再追加历史,Trajectory 也无法打开,刷新页面后才恢复。

服务端分页请求仍然返回 HTTP 200。故障发生在客户端把历史事件重新物化为 Chat 节点的阶段,因此“接口成功”并不代表页面已经接受了这一页。

复现边界

出现问题的 Session 约有 122 个 Turn、约 1.3 万条持久事件,并经历过多次上下文压缩和继承历史。相同数据在全新的 Chromium / Playwright 客户端中也能复现,说明故障不依赖 Electron 容器。

第二次分页后,控制台抛出:

conversation Definition "system-message" withdrew materialized target "chat";
return the same key with hidden visibility instead

此后事件订阅器停止继续处理,页面不再 prepend 更早的 Chat 历史,Trajectory 面板也无法读取完整的物化状态。重新加载会重建客户端状态,所以功能暂时恢复。

问题发生在哪一层

关键路径位于 buildTargetUpserts 一类物化逻辑:某个节点曾经以固定 key 生成 Chat target,历史页加入后,同一节点的 builder 却返回了 null。运行时把这解释为“已经物化的 target 被撤回”,于是触发不变量错误。

system-message 在追加模式、替换模式和当前窗口可见性不同的情况下,可能无法在本次窗口中生成目标。对于实时追加,这种严格检查有助于发现状态错乱;对于历史 prepend,同一事件可能只是暂时不在可见窗口中,直接撤回会让整条订阅链失败。

为什么 Chat 和 Trajectory 会一起失效

Chat 与 Trajectory 看起来是两个入口,底层却共享 Session 事件和客户端物化状态。一旦分页事件使订阅器抛错,Chat 无法继续构建历史,Trajectory 也拿不到一致快照。它不是两个独立 bug,也不应分别用“重载 Chat”和“重载 Trajectory”掩盖。

排查时应同时记录:

  • /api/session/page 的游标、事件数量与 HTTP 状态;
  • 每次 Load Earlier 前后的首尾事件 ID;
  • 抛错的 Definition、target key 与可见性;
  • Chat 和 Trajectory 订阅器是否共享同一个失败边界。

修复方向

更安全的处理有三层:

  1. 当同 key 节点暂时不应显示时,返回带隐藏可见性的同一 target,不要返回 null;
  2. 历史 prepend 允许节点从当前可见窗口退出,但不能破坏已经物化的身份;
  3. 增加跨页 system-message 回归样本,覆盖第二页、第三页、压缩边界与继承历史。

测试不能只断言分页接口返回 200,还要断言第二次和第三次加载后 Chat 条数增长、Trajectory 可打开,并且刷新前后事件顺序一致。

当前恢复方法

页面已经进入错误状态时,刷新可重建订阅器;需要查看 Trajectory 时,先在触发第二次 Load Earlier 前打开。新建 Session 可以避免继续扩大故障,但不能替代旧历史恢复。

长期 Session 还应配合上下文压缩与工具结果剪枝使用。如果问题表现为 Session 列表为空或 Workspace 记录不一致,可再看Session 列表恢复指南。分页失效、上下文压缩与 Session Registry 属于不同层,不宜混为一个“长会话太大”。

参考资料