DSH plugin 的 token 计量与 spill:位置回放、修订号与中间产物的外溢机制

插件开发发布于 2026-10-17作者: DeepSeek Plugin 插件市场
DeepSeek HarnessDSH plugintoken-meterspillSpillRef上下文成本
DSH plugin 的 token 计量是一个带 logRevision 的不可变回放快照,节点按位置定价;spill 则把超长文本原样落盘并返回不透明定位符,两者共同决定上下文成本。

上下文成本在 DSH plugin 里由两件事共同决定:一是 token 计量给出的按位置快照,二是 spill 把超长中间产物挪出上下文。 计量的关键是它带 logRevision——你看到的每个数字都对应「消费了多少条持久事件」,所以回放推进后旧快照仍然可解释;而节点的价格分两套:路由定价的 tokens 用于触发与保留决策,固定启发式的 heuristicTokens 用于影子定价保持一致。spill 则只做一件事:把调用方给的文本原样存下来,返回一个不透明定位符——消费方渲染提示,不要解析路径。

DSH plugin 的 token 计量:一个带修订号的回放快照

一句话结论:计量是不可变快照,锚定在消费到的事件数上;回放继续推进,旧计量不会变形。

  1. 核心字段:logRevision(已消费事件数)、baseline(锚点类型)、surfaceDeltaTokens(相对锚点的有符号增减)、totalTokens(请求与响应压力)、surfaceTokens(表层路由定价总量)、nodes(按位置从头部到尾部)。
  2. 锚点两种:baseline.kind === 'usage' 表示最近一次成功调用具有相同规范请求信封,且总量不低于其完整路由定价锚点;estimated 表示没有可复用的保守锚点,服务会自行对完整信封与表层定价。
  3. 锚点会被替换:后续成功请求会替换更早的锚点;两侧按同一路由重新定价,所以增量仍然可比。
  4. 两个总量别混:totalTokens 是压力,surfaceTokens 是表层价格之和。
  5. 示例读法:totalTokens 上涨而 surfaceTokens 持平,说明压力来自响应侧而不是表层内容变多。

DSH plugin 的表层定价:nodes、两种 token 与位置权威

一句话结论:节点顺序是权威的,替换节点的 seq 可能比后面的节点更大——不要按 seq 重排表层。

字段含义用途
seq该表层事件的持久序号定位,不用于排序
tokens该消息在当前路由下的请求压力触发、保留与范围选择
heuristicTokens与路由无关的固定启发式值影子定价替换节点时保持一致
  1. 图片按路由计价:路由的适配器声明了图片定价时,图片按视觉 token 加模型可见文本计价;未声明的路由沿用固定启发式。
  2. 顺序权威:表层顺序是权威的,替换节点的持久 seq 可能高于排在它后面的节点——按 seq 排序会打乱语义。
  3. 快照不可变:它不会随底层回放折叠推进而增长,适合直接缓存与比较。
  4. O(1) 折叠:heuristicTokens 的存在是为了让投影折叠与自己的追加保持一致,不要拿它替代 tokens 做决策。

DSH plugin 怎么读计量:measure 与 estimateMessage

一句话结论:measure 按当前会话表层重算,可传入替换信封;estimateMessage 只做单条消息的启发式估算。

  1. 整会话计量:
ts
const m = ctx.tokenMeter.measure(session, requestHeader?)
// m.logRevision / m.totalTokens / m.surfaceTokens / m.nodes
  1. 替换信封:传 requestHeader 会替换最新记录的请求信封,用于「即将发送的这条请求会花多少」这类预判。
  2. 复杂度:每次调用都会克隆位置节点,因此是 O(surface)——不要在热路径里对超长会话高频调用。
  3. 单条估算:
ts
const n = ctx.tokenMeter.estimateMessage(message)   // 固定启发式
  1. 选型:做成本判断用 measure,做单条替换的影子定价用 estimateMessage 或节点的 heuristicTokens。

DSH plugin 的 spill:saveText 与不透明定位符

一句话结论:spill 只有一个操作 saveText,原样保存文本并返回定位符、字节数与检索提示。

ts
interface SaveTextSpill {
  owner: { sessionId: SessionId }
  source: { kind: 'tool'; toolName: string; callId: ToolCallId; label: string }
        | { kind: 'session-reference'; sessionId: SessionId; label: string }
  suggestedName: string   // 仅命名提示,绝不是路径
  content: string
}
// 返回 SpillRef:{ locator, bytes, retrievalHint }
  1. owner 决定作用域:后端按产生会话分组存储;fork 出的会话继承种子日志里已有的定位符,产物不会被复制或重新归属,fork 之后的新产物才用子会话 id。
  2. source 只描述来源:工具来源带 callId,会话引用来源带被捕获的会话 id;它绝不承担访问控制。
  3. suggestedName 只是提示:后端会把它清洗成单个安全路径段,不当作路径使用。
  4. 定位符不透明:本地后端渲染为文件系统路径,远程或数据库后端可以渲染为 URI 或键;消费方按 retrievalHint 渲染,不要 parse。
  5. 失败即拒绝:真实存储失败(权限、ENOSPC、后端不可用)会 reject,由调用方决定如何降级。
  6. 边界:seam 只管存储,不管保留策略、工具结果替换与检索 API。

DSH plugin 的本地 spill 后端:目录、权限与防符号链接

一句话结论:本地后端写进私有目录的会话子目录,用排他且仅所有者可访问的方式写入,防止预置符号链接改道。

  1. 路径形状:<root>/session-<hash>/<random>-<safeName>,会话子目录用 sha256(sessionId)。
  2. 目录权限:根目录是配置或延迟创建的私有目录(0700)。
  3. 写入方式:open(path, 'wx', 0o600)——排他且仅所有者可访问,避免预先植入的符号链接把写入重定向。
  4. 检索提示:本地后端的 retrievalHint 会告诉模型在该路径上用 read 或 grep。
  5. 单实现约束:每个上下文只允许一个 ctx.spillStore 实现,加载第二个会按 Cordis 的重复服务规则报错。

DSH plugin 排查注意事项

一句话结论:计量问题先对修订号,spill 问题先看定位符是否被当成路径解析。

  1. 数字对不上 → 对比 logRevision,不同修订号的计量本来就不该相同。
  2. 表层顺序像是乱的 → 你在按 seq 排序;表层顺序才是权威。
  3. 成本突然涨 → 看是 totalTokens 还是 surfaceTokens 变,前者偏响应、后者偏内容。
  4. spill 定位符打不开 → 不要拼路径,按 retrievalHint 操作;远程后端的定位符可能根本不是路径。
  5. 保存失败导致工具报错 → 检查消费方是否把 reject 当成 isError,官方策略是保留内联结果。
  6. 修复状态如实:本文的计量字段、锚点规则与 spill 契约均取自官方「Token 计量」与「spill 存储」页面;接口签名以对应 Cordis API 区块与你所用版本为准。

来源:Token 计量、spill 存储、dsh-token-meter 源码。

常见问题

DSH plugin 的 TokenMeasurement 里 logRevision 表示什么?

DSH plugin 的 logRevision 是生成这份计量时消费掉的持久事件数量,等于下一个未读事件的 seq。它让计量与日志位置一一对应,回放推进后旧快照仍然可解释。

DSH plugin 里,totalTokens 和 surfaceTokens 有什么区别,该看哪个?

DSH plugin 的 totalTokens 是请求与响应的当前压力,surfaceTokens 是当前表层按路由定价的总量、等于各节点价格之和。判断上下文压力看 totalTokens,判断表层内容值多少看 surfaceTokens。

DSH plugin 里,为什么同一个会话的计量里会出现 heuristicTokens 和 tokens 两个值?

DSH plugin 的节点同时带路由定价的 tokens 与固定启发式的 heuristicTokens。前者用于触发、保留与范围选择,后者用于影子定价协议替换节点时保持 O(1) 折叠与追加一致。

DSH plugin 里,spill 保存超长文本后,模型拿到的是什么?

DSH plugin 的 spill 返回 SpillRef,包含不透明定位符、精确字节数与检索提示。模型看到的是定位符与提示,消费方不得解析定位符,应按 retrievalHint 决定用 read 还是 grep。

DSH plugin 里,spill 保存失败时,工具调用会变成错误吗?

不会,DSH plugin 的 spill 策略是尽力而为:保存失败时保留原始内联结果,不会把一次成功的工具调用改成 isError。spill seam 只负责存储,降级方式由消费方决定。

相关术语

TokenMeasurement(token 计量)
TokenMeasurement 是 DSH plugin 在某个已消费日志修订号上的不可变请求压力与表层快照,含修订号、锚点、增量、总量与按位置排列的节点。它不会随回放推进而增长。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/token-meter
logRevision
logRevision 是生成计量时消费的持久事件数量,等于下一个未读事件的 seq。它把计量锚定到日志位置,使统计结果可复现、可比较。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/token-meter
SpillRef(外溢引用)
SpillRef 是一次 spill 保存的结果,包含不透明定位符、精确字节数与后端提供的检索提示。它是消费方与模型之间传递外溢产物的唯一凭据。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/spill
SpillLocator(定位符)
SpillLocator 是后端返回的品牌化、面向模型的不透明句柄,本地后端渲染为文件系统路径,远程或数据库后端可以渲染为 URI 或键。消费方只能渲染它,不能解析它。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/spill

来源