DeepSeek Harness 同一条用户消息出现两个气泡:排队回显与落盘各渲染一次的排查

故障排查发布于 2026-10-30作者: DeepSeek Plugin 插件市场
DeepSeek HarnessDSH消息气泡排队session-controller前端渲染
DeepSeek Harness 发送消息后出现两个一模一样的用户气泡,时间戳也相同。拆开看:数据层只有一条 user 事件,是排队回显与落盘消息各渲染了一次,只在 Agent 运行中发送时触发。

「好了吗」这条消息,在对话流里出现了两个一模一样的气泡,时间戳都是 00:17,还各带一个复制按钮——看着就像发了两次。 但会话日志里那次发送只有一条真实的用户消息,其余都是自动注入的上下文(#10808)。把「数据层」与「显示层」分开数,这件事就不神秘了。

DeepSeek Harness 第一层:数据层确实只有一条

日志里 source.kind === "user" 的事件只有一条,其余同角色事件都不是你写的。

  1. 打开会话日志 session.v4.jsonl.zstd,定位那次发送的时间点。
  2. 能看到四条 user/message:kind=user 是「好了吗」。
  3. 另外三条分别是 kind=memmy-memory、kind=runtime-context、kind=time-context。
  4. 后三条 role 也是 user,但它们不是用户输入,是 cadence 自动注入的。
  5. 所以数据层干净:一条输入,不会重复计费,也不会重复回答。
text
00:17:41  user/message  kind=user             "好了吗"
00:17:41  user/message  kind=memmy-memory     "<memmy_l3_world_model ..."
00:17:41  user/message  kind=runtime-context  "Current runtime context ..."
00:17:41  user/message  kind=time-context     "Time sampled ..."

四行同一个时间戳,但只有第一行是你发的。这一点决定了后面所有判断:气泡数量不能用来推断消息条数。

DSH插件 第二层:显示层为什么变成两条

排队回显被从 transcript 里隐藏,但它又被排除在去重集合之外,于是被当成独立气泡渲染。

  1. 前端维护一份待提交账本 pendingSubmissions,记录每条乐观显示消息的位置。
  2. 去重集合 localIds 的来源是:pendingSubmissions.filter(s => s.placement !== "queued").map(s => s.requestId)。
  3. 注意这个过滤条件——queued 被排除在集合之外。
  4. 另一侧,visibleSubmissions 把 queued 的提交从 transcript 里隐藏掉。
  5. 但在 inboxSteering 的渲染逻辑里,只要 source.kind === "user" 有 rpcId,就会去 localIds 里找。
  6. 找不到(因为 queued 不在集合里)→ 直接渲染这一条。
  7. 等消息落盘后,transcript 里再渲染一次 → 两个气泡。

DeepSeek Harness 第三层:为什么只有排队时出现

Agent 空闲时发送走的是下一轮路径,入队与出队几乎同时发生,你看不到重复。

  1. 报告者记录的事件链:Agent 运行中发送时,先是 agent/inbox/spliced 目标是 next-step。
  2. 随后还有一次 splice 带 removedCount: 1。
  3. 之后才出现 user/message kind=user。
  4. 另一次在 Agent 空闲时发送,走的是 next-turn,入队与出队同时发生,只看到一条气泡。
  5. 结论:触发条件是「消息进入排队路径」,与插件无关——报告者逐个排除过第三方插件。

DeepSeek插件 第四层:为什么不用担心数据层

这只是显示层多画了一次,会话内容、模型调用与计费都不受影响。

  1. 模型只读到一条用户消息。
  2. 复制按钮出现两次,但复制的内容相同。
  3. 刷新或重开会话后,气泡数量应回到一条。
  4. 因此它是体验问题,不是数据完整性问题。
  5. 报告里也明确写了「数据没问题,是前端把同一条渲染了两次」。

DeepSeek Harness 版本状态与修法

两个提交修掉了这条路径,但它们都在 0.2.2-alpha.2,不在 rc.4。

  1. 第一个提交 ae07093132d:让 transcript 在回合结束与 steering 落地时保持稳定。
  2. 其中一条说明是「排队回显等待 Inbox 投影,而不是在持久化 splice 时退休,这样队列停靠区不会空一帧」。
  3. 第二个提交 a1659120a38:当排队回显的 Inbox 行被取消时,把回显撤掉。
  4. 两个提交都改了会话状态文件与对应的待提交测试。
  5. 都在 f3803c96(0.2.2-alpha.2)里,不在 504a01a9(rc.4)里。
  6. 数据层不需要改,日志格式也不用动。

DSH插件 怎么确认自己遇到的是这一条

三个特征同时成立就对上了:内容逐字相同、时间戳相同、日志只有一条 kind=user。

  1. 对比两个气泡的正文,是否完全一致(包括标点)。
  2. 对比时间戳,是否精确到同一分钟。
  3. 去日志里数 kind=user 的 user/message 条数。
  4. 回忆发送时机:是不是在 Agent 正在跑工具调用时发的。
  5. 换到 Agent 空闲时再发一条,看是否只有一条气泡。

注意事项

  1. 别按「重复发送」去查网络。日志已经证明只有一条。
  2. 别删会话重来。刷新或重开就能恢复一条的显示。
  3. 别把自动注入事件当成用户消息。它们 role 也是 user。
  4. rc.4 上仍会出现。修复只在 alpha 线。
  5. 报问题时附时间戳与日志条数。这是区分显示问题与真实重复的最快证据。

如果你的两个气泡内容略有差异,那可能是两条真实消息,逻辑完全不同,先去核对日志。

反过来,如果日志里确实有两条 kind=user,那就是真的发了两次,得去查输入通道而不是渲染。相关排查见 DeepSeek Harness 会话显示异常:标题不生成、只跑命令显示空白与删缓存丢会话。

常见问题

DeepSeek Harness 发一条消息出现两个相同的用户气泡,是发了两遍吗?

不是,DeepSeek Harness 只把这条消息发送了一次。会话日志里那次发送只有 1 条 source.kind === "user" 的事件,其余是 cadence 自动注入的 memory、runtime-context、time-context 事件。两个气泡是纯前端显示问题。

为什么这个重复气泡,只在 Agent 运行中发送时才在 DeepSeek Harness 出现?

因为这种发送在 DeepSeek Harness 里走排队路径(placement === "queued")。排队回显会被隐藏于 transcript,但同时又被排除在去重集合之外,于是队列里那条被当成独立气泡渲染;消息落盘后再渲染一次,就成了两个。

两个气泡会造成重复计费或重复回答吗,DeepSeek Harness 的数据层怎么记?

不会,DeepSeek Harness 的数据层只有一条用户消息,模型只处理一次。这个问题只影响显示,不影响会话内容与费用。

DeepSeek Harness 的重复气泡在哪个版本修好了,具体怎么排查?

DeepSeek Harness 的这个显示问题由两个提交 ae07093132d 与 a1659120a38(都是 10-27,rc.4 之后)修掉,它们都在 0.2.2-alpha.2 里,不在 rc.4 里。所以 rc.4 上双气泡仍会出现。

怎么快速确认自己遇到的,就是 DeepSeek Harness 的重复气泡问题?

在 DeepSeek Harness 里看两个气泡的时间戳是否完全一致、内容是否逐字相同,再去会话日志里数 kind=user 的 user/message 只有一条。两者同时成立,就是显示层重复。

相关术语

queued placement
queued placement 是消息在 Agent 运行中提交时采用的排队位置标记。它让消息先进入队列,等当前回合结束或下一步开始时再并入对话。— https://github.com/deepseek-ai/deepseek-harness/discussions/10808
pendingSubmissions
pendingSubmissions 是前端维护的待提交消息账本,记录每条乐观显示的提交及其位置。去重逻辑会从这里取 requestId 组成集合。— https://github.com/deepseek-ai/deepseek-harness/discussions/10808
localIds
localIds 是渲染时的去重集合,取自待提交消息的 requestId。当前实现把 queued 的提交排除在外,因此排队那条不会被去重。— https://github.com/deepseek-ai/deepseek-harness/discussions/10808
agent/inbox/spliced
agent/inbox/spliced 是会话事件,表示一条输入被插入到当前步骤或下一回合。它携带目标位置与移除计数,可用来判断消息走的是排队还是下一轮路径。— https://github.com/deepseek-ai/deepseek-harness/discussions/10808

来源