Files
deepseek-harness/docs/rfc/implemented/architecture/2026-07-15-replay-token-meter-service.zh.md
T
2026-07-15 16:50:44 +08:00

6.8 KiB
Raw Blame History

RFC: 重放式 token 计量服务

Status: implemented

English | 中文

问题

上下文压力并不只对压缩有用。压缩后端、溢出保护或未来的请求策略插件都可能需要回答同一个问题:持久请求占用了某个模型多少上下文窗口?如果把该折叠逻辑留在 dsh-compact-basic 内部,就会重复实现重放逻辑,使未加载压缩的调用方无法使用计量,并诱使调用方错误复用其他模型的核算结果。

提供方 usage 也不是完整答案。它只描述某个精确请求信封下的一次成功调用,而当前表层之后还可能增长、缩小或被替换。会话也可能切换模型,旧日志可能缺少 chunk 来源,提供方字段还会分别报告输入、缓存读取、缓存写入、输出与推理计数。因此,可用的服务必须把精确锚点与保守的逐模型重新定价结合起来,并公开每个结果已经消费的日志修订号。

决策

一个具体的 LLM 家族服务

@deepseek-ai/dsh-token-meter 是 packages/llm/ 下的单个具体 package,并注册 ctx.tokenMeter。在第二种实现出现之前,它不会被拆成接口与后端。公开入口把精确模型名解析为稳定的 ModelTokenMeter;未知名称抛出带 TOKEN_METER_MODEL_UNCONFIGURED 的 TokenMeterError,而不是继承通用窗口。

内置的 deepseek-v4-flash 与 deepseek-v4-pro profile 都采用 128,000 token 上下文窗口,以及每 token 四个字符的估算密度。models 覆盖按字段合并。自定义名称必须提供 contextWindow,而 charsPerToken 默认为四。直接构造会报告类型化 profile 错误;Loader 挂载则先应用 package 的 Schemastery 形状校验。

绑定模型的重放折叠

每个模型/会话对都有隔离的增量折叠。活跃折叠通过 session/event 前进;每次读取都会追到持久日志尾部,因此监听器顺序、种子会话与服务重载不会改变答案。折叠跟踪规范请求头及其增量、步骤边界、表层追加与替换、assistant usage,以及 assistant chunk 来源。下一个畸形事件会以事务方式失败并保持未读,不会让状态只修改一半。

measure(session, requestHeader?) 返回标量压力。measureSurface(session) 返回用于保留与替换决策的逐位置节点价格。estimateMessage(message) 不依赖会话状态,直接应用该 handle 的 profile。结果是分离且深度不可变的快照,并携带 logRevision;消费者在一次联合决策前比较标量与表层修订号。

只有当 handle 的模型与规范请求信封都等于成功调用锚点时,服务才复用提供方 usage。系统提示词、前缀、工具或调用配置任一变化都会在请求模型下重新定价完整当前请求。表层变化相对匹配锚点保留有符号增量,包括缩小替换后的负值。其他模型的成功调用会改变共享表层,但绝不会覆盖当前模型的锚点。

Usage 会对互不重叠的输入、缓存读取、缓存写入与输出 bucket 求和,不会再次加入推理计数。每次成功模型调用都会记录 assistant/message,包括无内容调用与达到 token 上限的调用,并带上精确的更早 chunk seq。显式空来源列表表示已知为空的提供方流;旧日志中缺失的来源则保守地把持久 assistant 输出视为提供方输出。

compact-basic 消费计量,但不拥有计量

dsh-compact-basic 要求 ctx.tokenMeter;CompactService 不增加 token 方法或类型。后端拆分为配置、自动触发、区域事务与摘要器模块,而 summarize() 仍是唯一的子类 hook。会话模型的 meter 一致用于压力、保留、被遮蔽内容、来源以及非缩小摘要拒绝。

每个已计量模型都会获得默认压缩策略:阈值比例 0.8、保留尾部 floor(contextWindow × 0.16)、摘要模型 ''、摘要最大输出 8192、一次额外压力压缩尝试、一次上下文溢出重试,以及启用自动触发。逐模型压缩覆盖按字段合并 thresholdRatio 与 retainTokens;保留值必须小于最终阈值。空摘要模型先解析最近记录的实际路由模型,再使用 AgentOptions.model。

自动压力检查运行在 agent/post-step,并使用 agent/request 实际选择的模型计量规范持久信封。没有请求头的会话尚无已完成的路由请求可供判断,因此不执行工作;持久记录的未知路由模型仍抛出带精确名称的类型化错误。规范化溢出恢复使用同一 meter 强制选择范围,并且只有在表层替换得到证明后才重试。

测试

单元覆盖固定 profile、按字段覆盖、自定义与未知模型、信封失效、模型切换、有无 usage 的路径、种子追加/替换重放、有符号增量、来源模式、畸形边界、不可变快照、监听器顺序、重载、压缩默认值、实际路由、保留、收敛、强制溢出与事务回滚。真实 Loader/Include YAML fixture 按依赖顺序加载精确的零配置 token-meter 与 compact-basic package 名称。

考虑过的替代方案

  • 把估算保留在 CompactService 内——不予采纳,因为计量拥有独立于压缩的消费者与重放语义;它还会强迫每个压缩器暴露同一套无关 API。
  • 立即把 token meter 拆成接口与启发式后端——不予采纳,因为目前只有一种实现。单个具体服务保留未来接缝,同时避免推测性的 package 与配置。
  • 给未知模型提供 128,000 token 回退——不予采纳,因为看似合理但错误的容量会在错误时点触发破坏性策略。未知路由名称会携带精确名称失败。
  • 在每个标量结果中复制完整历史——不予采纳,因为低于阈值的读取很常见。不可变且带修订号的标量与独立表层快照,在不进行 O(history) 复制的情况下保持一致性。
  • 在模型或信封之间移用提供方 usage——不予采纳,因为分词、上下文容量、工具、前缀与调用配置都是模型/请求事实。不匹配时会重新定价完整当前请求。

后果

  • Token 压力拥有一个可供压缩与未来插件共享的重放感知所有者。
  • 默认值让内置 DeepSeek 组合只需两个零配置插件条目即可使用,而自定义模型必须声明唯一不能安全猜测的事实:上下文容量。
  • 启发式密度与提供方 usage 仍然只是提供方行为的估计。随着模型演进,维护者必须更新内置 profile 与溢出措辞。
  • 遇到畸形持久边界时,计量会明确失败。这会把损坏的重放转化为具名集成错误,而不是让压力静默漂移。
  • post-step 压力检查读取精确记录的路由、工具与前缀边界;对于在成功 usage 锚点出现前就被拒绝的请求,提供方溢出分类仍是由适配器维护的兜底路径。