DeepSeek Harness Code Mode 子调用渲染:Trajectory 的 subtool 交错单元与 Waterfall 的真实计时子泳道
2026/9/18 10:01:29 网站建设 项目流程

DeepSeek Harness Code Mode 子调用渲染:Trajectory 的 subtool 交错单元与 Waterfall 的真实计时子泳道

【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness

本文围绕 DeepSeek Harness(下称 Harness)中 Code Mode 子调用的分析视图渲染这一具体功能展开:它记录了 Trajectory 与 Waterfall 两个"结构 + 计时"视图,如何从把一次run_code回合压成单个不透明 Tool 单元格,演进为交错嵌套的subtool单元与带真实墙钟时间(wall time)的子泳道(sub-lane)。读完本文,你可以掌握该功能的完整决策脉络(问题、方案、被否决的备选、后果),并能对照仓库源码逐条核对布局折叠、时长计算、运行中态渲染与测试固化方式。

背景:run_code 回合在分析视图中的"黑盒"问题

Harness 的客户端提供三种查看会话的方式:对话视图(chat)与两个分析视图——Trajectory(轨迹)与 Waterfall(瀑布图)。后者的全部意义在于结构计时:把一次会话拆成可度量时间成本的单元序列。

在 Code Mode 之前的 UI 迭代中,chat 视图已经拿到了嵌套子行(nested sub-rows):run_code回合内部的每一次子工具调用(sub-call)都以嵌套行的形式呈现。但 Trajectory 与 Waterfall 仍然把一个run_code回合渲染成一个不透明的 Tool 单元格 / 一根 node-count 条形,既不展示任何子调用结构,也不展示每个子调用的墙钟耗时——尽管此时底层的 start/settle 时间对(dispatch 时间对)已经在记录。

决策记录特别强调了一个设计纪律:Waterfall 子泳道被刻意推迟到 start/settle 时间对存在之后才实现,因为"没有真实计时的 span 就是一种谎言"(原文:a span without real timing would have been a lie)。这为后文 Waterfall 泳道的三种计时溯源(provenance)标签埋下伏笔。

完整的决策记录见 Code Mode sub-calls in the trajectory and waterfall views(Agent Note),该记录的状态为implemented,归档于 2026-07-28,属于 Code Mode UI 技术栈的最后一个 PR。

决策总览

决策记录给出的最终方案一句话概括:

Trajectory:在父 Tool 单元格之后交错插入subtool单元格。Waterfall:在归属轮次行下方绘制真实计时的子泳道。

两者共享同一数据通道:都通过标准的快照(snapshot)hook 读取子调用索引——不新增任何 wire 数据、不新增 store,因此"回放(replay)与实时(live)按构造完全一致地渲染"。

在决策记录中,该数据源被称为快照的codeDispatches索引。从当前源码结构看,这份索引以子调用树subCalls的形式挂载在两个位置:tool-result节点与运行中(running)的调用记录上;它由 tool 定义对tool/code-dispatch-starttool/code-dispatch两类事件折叠而成,参见 trajectory-tool-definition.ts。Code Mode 的生产侧则位于核心工具包的 ptc.ts(从源码结构看,run_code的派生事件即在此产生)。

Trajectory:父 Tool 单元格之后交错 subtool 单元

布局折叠:三条父单元路径都接子调用

Trajectory 的布局折叠(layout fold)从快照取得子调用索引后,对每一个callId拥有 dispatch 的 Tool 单元格,在其后按启动顺序交错插入每个子派生一个subtool单元格,且索引在整个交错过程中保持连续

决策记录点名的三类父单元——assistant 块内调用、孤儿结果(orphan results)、运行中调用——在当前源码中均可逐一对应,全部汇聚到同一套交错逻辑:

  • assistant 块内调用:assistant 节点展开后经withSubCalls包装,见 layout.ts;
  • 孤儿 tool-result 节点:先铺父 Tool 单元格,再对node.subCalls展开子单元,见 layout.ts;
  • 运行中调用:同样先铺父单元格,再对其call.subCalls展开,见 layout.ts。

交错与重编号的核心实现是withSubCalls:它先给每个单元格写入连续递增的index,紧接着把该单元格的全部子调用推入输出序列,随后单元格的编号自然顺延——这正对应记录中"indexes stay sequential across the interleave"的要求,见 layout.ts。

子调用单元:时长、运行中态与递归嵌套

expandSubCalls负责把一组子调用变成subtool单元格,见 layout.ts。三个关键行为:

  1. 已结算(settled)子调用的时长是 start/settle 时间对timeSeconds: durationSeconds(sub.time, sub.callTime),即结算时刻减启动时刻;
  2. 运行中(unsettled)子调用显示 em dash("—")timeSecondsnull,沿用原生 in-flight 惯例,绝不显示伪造的 0 或空值;源码注释原话是 "a running (unsettled) or pre-pair log entry shows the em dash",见 layout.ts;
  3. 递归扁平化:子调用若自身又是run_code(即子调用之下还有子调用),其孙级调用会紧随其父之后继续展开,callId采用p1:code:1:code:1式的层级命名。

视觉识别:Sub 标签 + 28px 缩进

新的subtool单元格种类佩戴Sub标签(business tint 配色)并有 28px 缩进,使嵌套关系"一眼可读"。样式实现与决策记录一一对应:

/* run_code sub-dispatch cells: the business tint plus an indent so the nesting under the parent Tool cell reads at a glance. */ .tagSubtool { color: color-mix(in srgb, var(--dsw-alias-state-warn-label) 62%, var(--dsw-alias-label-tertiary)); background: color-mix(in srgb, var(--dsw-alias-state-warn-tertiary) 58%, var(--dsw-alias-bg-layer-1)); } .root[data-kind='subtool'] { padding-left: 28px; }

见 TrajectoryCell.module.css。

Waterfall:归属轮次行下的真实计时子泳道

按决策记录,Waterfall 侧的方案是deriveSubSpans:把子调用索引折叠成每个轮次(turn)一组泳道,且全部使用真实计时

  • 父级 dispatch 窗口定义为"最早一次子调用启动 → 最晚一次子调用结算"(first start → last settle);
  • 每条泳道的 offset 与 width 都是该窗口的比例值,因此并行子调用(由该技术栈的第三个 PR 引入)在图上可见地相互重叠
  • 每条泳道携带timing溯源标签,这是"span 不得撒谎"纪律的直接体现:
    • measured:观测到了完整时间对;
    • running:结算尚待(settle pending),泳道以降低不透明度延伸覆盖到窗口末端;
    • unknown:只有结算时刻的重放窗口(callTime: null)——泳道以空心绘制,悬停标题为 "duration unknown",绝不伪造 0 ms
  • 泳道绘制在归属轮次条形行下方,缩放进一个固定的泳道预算(lane budget)。

需要说明的是:在当前仓库快照中,Waterfall 侧的deriveSubSpans实现未能于 client 包内检索到同名符号(后续重构可能已更名或迁移),因此本节内容以决策记录为准描述其设计;而 Trajectory 侧的实现与测试则可在当前源码中逐条核对。这一"turn 级条形仍用 node-count 占位、子泳道却是真实墙钟时间"的刻意的对比,正是后果一节中"首个真实计时渲染"的来源。

备选方案及其否决理由

决策记录完整保留了三条被否决的路径,其否决逻辑值得设计类似分析视图的读者借鉴。

备选一:把子调用并入轮次 span 的节点计数(给现有条形加权)

否决理由:这会恰好隐藏掉本技术栈要展示的结构;且 node-count 加权在偏差台账(deviation ledger)中早已被标记为"占位符"(stand-in,即台账第 3 项),拿占位手段承载结构信息本末倒置。

备选二:用独立的子调用面板替代视图内嵌套

否决理由:该技术栈已经稳定的 UX 是"在所有视图中都嵌套在父单元之下";独立面板会与 chat 视图分叉,并把选中(selection)逻辑的工作量翻倍(double the selection plumbing)。

备选三:把 Waterfall 泳道推迟到 P-III 时长泳道重设计

否决理由:子泳道计时当下就是真实的(start/settle 对已存在),且"窗口比例"渲染方式与轮次级泳道未来如何重设计相互独立;推迟只会搁浅该技术栈的计时回报(timing payoff)。

后果与落地验证

对既有行为的影响

  1. Waterfall 承载了客户端首个"真实墙钟时间"渲染。轮次条形仍是 node-count 占位——这个对比是刻意的,并由悬停标题(hover titles)明确标注,避免用户误读;
  2. Trajectory 单元格索引开始计入子调用,因此 Code Mode 回合的#N编号总量会增长——这是交错渲染的直接副作用,阅读轨迹编号时需知悉。

测试与快照如何固化这些行为

决策记录列出了规格(specs)固化的清单:交错顺序与时长、运行中 em-dash 分支、窗口比例(offsets/widths)、运行中泳道的延伸、unknown-timing(settle-only)泳道、以及轮次行下方渲染出的泳道;构建后客户端的 Code Mode fixture 快照则额外固化两个标签页组装后的完整渲染(带真实 +0.8s 时长的子单元格、measured泳道)。

其中 Trajectory 侧的用例在当前源码中可以直接核对,位于 layout.client.spec.tsx 的run_code sub-dispatch cells组:

  • 交错顺序与真实时长:父run_code单元格(callTime: 6200,结算9000)之后依次插入两个 settled 子调用,断言单元格种类序列为['message', 'tool', 'subtool', 'subtool'],索引序列为[1, 2, 3, 4](跨交错连续),且时长精确到timeSeconds: 1(6300→7300)与0.5(7300→7800);
  • 运行中子调用:一个未结算的grep子调用渲染为subtool单元且timeSeconds: null(即 em dash 分支);
  • 递归扁平化:子run_code之下的叶子调用紧随其父出现,callIdp1:code:1:code:1,索引仍保持[1, 2, 3, 4]

构建后客户端的 Code Mode fixture 快照可参见 snapshots/web/ptc-round,其中包含 Code Mode 回合的工具 schema 期望值;Trajectory 视图自身的组件与快照构建逻辑位于 ui-trajectory 包(含 trajectory-snapshot-builder.ts)。

小结

这篇决策记录展示了 Harness 客户端分析视图演进中一个典型的"数据先行、渲染随后"案例:先有 chat 视图的子调用嵌套与 start/settle 时间对采集,待真实计时就位后,Trajectory 与 Waterfall 才通过同一份快照数据(无新 wire、无新 store)把子调用结构与时机补全到结构与时序视图里。其可复用的工程要点有三:用窗口比例而非绝对值绘制并行泳道使重叠可见;用 measured / running / unknown 三态溯源标签杜绝伪造时长;以快照一致性保证 live 与 replay 渲染相同。这些约束连同被否决方案的论证,都已固化进上述决策记录与测试用例,可直接作为同类 Agent 轨迹可视化的参考实现。

【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness

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

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

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

立即咨询