DeepSeek Harness 回放式 Token 计量服务统一上下文压力核算与压缩策略的消费边界【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness本文基于仓库内 Agent Note《Replay token meter service》展开讲解 DeepSeek Harness 如何以deepseek-ai/dsh-token-meter这一零配置单例服务为压缩compaction、溢出保护与未来的请求策略插件提供统一的持久请求消耗多少 token这一答案。读完你将掌握回放式计量的设计动机、固定启发式估算规则、逐会话增量折叠与锚点复用机制、measure()/estimateMessage()的完整语义以及dsh-compaction-basic如何消费而不拥有计量。一、问题背景为什么计量必须独立于压缩上下文压力context pressure的用途远不止压缩。一个压缩后端、一个溢出保护、乃至未来的请求策略插件都需要回答同一个问题当前持久请求到底消耗了多少 token原文档明确指出如果把这套折叠fold逻辑留在dsh-compaction-basic内部会带来三个后果重复实现回放逻辑——每个需要压力的模块都要各自重放一遍会话日志未加载压缩的调用方无法计量——计量能力被错误地绑定在了压缩后端上诱使调用方复用陈旧核算结果——不同时刻、不同路由下的压力口径无法对齐。同时提供方 usage 也不是完整答案因为它只描述某个精确请求信封下的一次成功调用当前会话表层之后可能增长、缩小或被替换会话可能切换提供方与模型旧日志可能缺少构成 assistant 消息的分片 sequsage 字段还会分开报告输入、缓存读取、缓存写入、输出与推理计数。因此一个可用的计量服务必须做到三点结合最新精确锚点provider usage与保守的启发式重新定价heuristic repricing并公开每个结果已经消费的日志修订号logRevision。二、核心决策一个具体的 LLM 家族服务而非接口抽象deepseek-ai/dsh-token-meter是 packages/llm/token-meter 下的单个具体包通过 Cordis 注册为ctx.tokenMeter。关键决策是在第二种实现出现之前不将其拆分为接口与后端。从 src/index.ts 可以看到TokenMeter继承自 Cordis 的Service其 API 面非常克制export class TokenMeter extends Service { static Config: zTokenMeterConfig z.object({}) // 零配置 measure(session: Session, requestHeader?: EpochHeader): TokenMeasurement estimateMessage(message: Message): number }这个服务没有任何配置项。从validateConfigKeyssrc/index.ts可以看到任何传入的键都会被直接拒绝function validateConfigKeys(config: TokenMeterConfig): void { for (const key of Object.keys(config)) { throw new Error(TokenMeterConfig: unknown key ${key} (no settings are supported)) } }原文档特别强调没有模型 profile、容量设置、密度设置、分词器后端或语言专用策略。精确的提供方/模型容量属于路由所属适配器route-owning adapter的职责通过ctx.llm.resolveModelInfo().context查询见 token-meter/README.md而消费方专属的阈值与保留策略则归dsh-compaction-basic所有。这一职责划分对应原文档中路由模型上下文与压缩策略的架构决策。三、固定启发式估算每 token 四字符 结构开销计量服务的估算核心在 src/estimate.ts是一个确定性、与路由无关的固定密度启发式const CHARS_PER_TOKEN 4 // 固定文本密度 const BLOCK_OVERHEAD 4 // 每 block 的 JSON 框架/类型标签开销 export const ROLE_OVERHEAD 4 // 每条消息的角色字段开销各定价分支如下对应 estimate.ts内容类型定价规则text/reasoningceil(文本长度 / 4) BLOCK_OVERHEADtool-callceil(name 长度 / 4) ceil(arguments 长度 / 4) BLOCK_OVERHEADtool-result递归定价contentBLOCK_OVERHEAD未知 blockmerge 扩展与 image 引用保守的结构化 JSON 价格BLOCK_OVERHEAD ceil(JSON 长度 / 4)消息级 APIestimateMessage(message)即estimateContent(message.content) ROLE_OVERHEADestimate.ts。请求信封header侧则拆分为系统提示词与工具 schema 两部分estimateHeader(header) estimateSystemTokens(header) estimateToolsTokens(header)estimate.ts。图片路由定价适配器声明优先固定启发式并非绝对。measure()在定价时会通过可选的llm服务解析有效信封的提供方/模型若该路由所属适配器声明了图片请求定价imageRequestPricing则每个图片出现处使用路由声明的视觉 token 模型可见文本定价其余节点保留固定启发式未声明定价的路由行为不变见 src/route-pricing.ts。priceSurface还会校验返回价格数量与图片出现次数一致数量不匹配即抛错防止节点被静默错价。四、逐会话回放折叠增量、隔离、事务性失败每个会话在WeakMapSession, ReplayState中拥有一个隔离的增量折叠状态src/index.tsinterface ReplayState { consumedEvents: number // 已消费事件游标 header: EpochHeader | undefined // 规范请求头快照 surface: MeterSurfaceNode[] // 带位置的表层节点 stepStart: { turn; step; nodes } | undefined // 步骤边界 anchor: MeasurementAnchor | undefined // 最近一次成功调用锚点 }折叠通过session/event前进活跃会话由服务自身注册的监听器推着走每次读取都会追平到持久日志尾部。这意味着监听器顺序、种子会话seeded session与服务重载都不会改变答案——回放完全由持久日志决定具有确定性src/index.ts。事务性失败畸形事件整体回滚折叠采用plan/commit 两段式src/surface-fold.tsplanSurfaceTokens只读地执行所有可能失败的步骤表层替换范围解析、步骤边界校验、锚点校验commitSurfaceTokens才原地修改。因此下一个畸形事件会事务性失败并保持未读——同一份损坏日志每次重试都以完全相同的方式失败绝不会让状态只改一半、压力静默漂移。具体校验点包括step/start到达时上一步未结束、step/end无匹配的step/start、assistant/message无匹配步骤边界、表层替换范围在现有节点中不存在、sourceEventSeqs引用了不早于 assistant 消息或重复的 seq、引用的源不是同一步骤的assistant/chunksrc/index.ts。五、measure() 语义一次同步、一个快照、O(surface) 成本measure(session, requestHeader?)是计量服务的主入口src/index.ts其行为可概括为同步一次折叠到当前持久尾部解析有效信封未传requestHeader时用折叠中最新的规范 header用该路由的图片定价给当前表层节点重新计价返回一个分离、深度不可变的快照。返回类型定义在 src/types.tsinterface TokenMeasurement { readonly logRevision: number // 已消费的持久事件数等于下一个未读事件 seq readonly baseline: TokenMeasurementBaseline // usage / estimated / none 三种锚点 readonly surfaceDeltaTokens: number // 相对锚点的有符号表层增量 readonly totalTokens: number // 请求响应总压力非负 readonly surfaceTokens: number // 仅表层的路由计价总量等于 nodes[].tokens 之和 readonly nodes: readonly TokenSurfaceNode[] // 按 head-to-tail 顺序的带位置节点 }要点totalTokens是请求与响应压力surfaceTokens是仅表层的启发式总量恒等于nodes[].tokens之和requestHeader覆盖只改变压力定价表层字段永远描述当前会话每个结果携带一个logRevision调用方可据此判断自己消费的是哪一版日志事实每次计量都会克隆当前节点因此成本为O(surface)——即便是低于阈值即可结束的压力检查也不例外原文档后果一节明确承认这一代价换取的是结果一致性消除了分离 API 在调用方侧的竞态窗口。六、锚点与增量提供方 usage 何时被复用折叠会追踪每次成功模型调用的 usage 及其引用的 chunk seq。原文档给出了精确的复用条件只有当待计量的规范请求信封等于最近一次成功调用的锚点时服务才复用提供方 usage。提供方、模型、系统提示词、前缀、工具或调用配置任一变化都会触发完整的启发式重新定价。从源码看src/index.ts锚点判定还有一层保守性约束usage 总额不得低于该次调用按完整路由计价出的启发式价格否则退回纯估算锚点。表层变化则相对匹配锚点保留有符号增量——包括缩小替换后的负值surfaceDeltaTokens可以为负最终totalTokens用Math.max(0, …)钳制为非负。锚点替换规则后续成功请求会替换先前锚点跨提供方或模型切换时同样如此。usage 求和的去重规则usageTokens()src/index.ts对互不重叠的输入、缓存读取、缓存写入与输出 bucket 求和推理计数不会二次加入return usage.inputTokens (usage.cacheReadTokens ?? 0) (usage.cacheWriteTokens ?? 0) usage.outputTokens每次成功模型调用都会记录assistant/message——包括无内容调用与达到 token 上限的调用——并带上精确的更早 chunk seq。sourceEventSeqs的语义分三种src/index.ts显式空列表已知为空的提供方流按 0 token 定价缺失旧日志保守地把持久 assistant 输出视为提供方输出无法区分提供方输出与监听器改写非空列表从精确引用的 chunk seq 用BlockAssembler重组提供方内容后再定价并对 seq 顺序、去重、步骤归属做严格校验。七、compaction-basic 消费计量但不拥有计量dsh-compaction-basic是计量服务的首要消费方。从 packages/compaction/compaction-basic/src/index.ts 可以看到它的依赖注入声明export class BasicCompactionEngine extends CompactionEngine { static inject [llm, tokenMeter, sessions] // ... }架构约束非常清晰CompactionEngine不增加任何 token 方法或类型——计量全部经ctx.tokenMeter完成配置、区域事务与摘要各自留在独立模块config.ts、region.ts、summarizer.tssummarize()仍是唯一的子类定制钩子回放与持久变更策略保持固定从而保证每一次定价决策压力、保留、被遮蔽内容、引用的源事件、非缩小摘要拒绝都使用同一个单例计量器口径一致。区域事务锁定后计量、摘要后复测、比较向量自动压缩的每次阈值与保留联合决策只使用一次统一计量。区域事务region transaction的执行顺序为追加持久compaction/start锁计量一次异步摘要完成后再次计量比较两个分离的表层节点向量。如果期间发生表层变更节点向量不同则阻止替换但logRevision因无关的纯日志事实推进时不会使未变化的选定范围失效——这正是logRevision语义与按节点向量比较配合的价值所在。自动压力检查的时机自动压力运行在agent/pre-step请求派生之前计量的对象是前一个agent/request实际所选提供方/模型产生的规范持久信封。原文档强调无请求头的会话没有已完成的路由请求可评估不产生任何工作任意路由目标都可以使用这个单例估算器规范的溢出恢复使用同一计量结果强制选择范围并且只有在表层替换得到证明后才重试对于在成功 usage 锚点出现前就被拒绝的请求提供方溢出分类仍由适配器维护作为兜底路径。八、压缩策略配置详解默认值、覆盖与校验原文档给出了压缩策略的服务级默认值与 config.ts 完全一致配置项默认值说明thresholdRatio0.8压力触发阈值比例对容量换算retainRatio0.16保留尾部比例summarizationProvider摘要提供方空 未指定summarizationModel摘要模型空 未指定maxTokens8192摘要最大 token 数compactionRetries1压缩重试次数maxOverflowRetries1溢出恢复最大重试次数autotrue是否启用自动压缩配置解析的关键机制对应 config.ts顶层字段适用于每个路由目标modelPolicies中的精确provider/model项可以部分覆盖这些字段resolveTargetPolicy按 providermodel 精确匹配config.ts压力以容量为基准换算比例resolveCompactSpec用适配器解析的contextWindow计算thresholdTokens floor(contextWindow × thresholdRatio)config.ts容量必须是正整数否则抛出TargetPressureConfigErrorretainTokens可以替代retainRatio二者互斥同时配置即报错无论哪种形式保留值必须小于最终阈值违规在插件加载时即失败摘要提供方与模型必须成对同时为空或同时非空validateSummarizationPairconfig.ts空组合先解析最近记录的请求目标再回退到AgentOptions中的组合thresholdRatio必须是(0, 1]区间内的有限数maxTokens为正整数重试次数为非负整数——所有越界与拼写错误的键都在加载期被拒绝防止默认值掩盖配置错误。九、测试与验证原文档列出的测试面与仓库测试文件一一对应固定估算、信封失效与锚点替换、回放边界、不可变快照、已路由压力、收敛、溢出 generation 证明与回滚——对应 tests/token-meter.spec.ts、tests/route-pricing.spec.ts、tests/turn-usage.spec.ts 等真实 Loader/Include fixture验证零配置 token-meter 与 compaction-basic按依赖顺序加载的路径——对应 tests/loader-composition.spec.ts这正是meter 先于 compactor 注册、compactor 通过ctx.tokenMeter消费这一依赖契约的落地验证。十、替代方案与取舍原文档决策记录原文档记录的五条替代方案及其否决理由是理解这套设计边界的关键把估算保留在CompactionEngine内——否决计量拥有独立于压缩的消费方与回放语义还会强迫每个压缩器暴露同一套无关 API立即拆成接口与启发式后端——否决目前只有一种实现单一具体服务既保留了未来的 seam又避免了推测性的包与配置把模型键控窗口与密度 profile 放进 meter——否决回放估算不拥有模型路由或容量事实容量归路由所属适配器阈值与保留策略归 compaction-basic保留独立的标量与表层计量——否决调用方要为一次决策做两次读取并匹配修订号标量只读虽能避免低于阈值时的节点复制但会在调用方引入竞态窗口统一快照以 O(surface) 复制换取一致性在不同信封之间移用提供方 usage——否决模型、工具、前缀与调用配置都是请求事实不匹配时必须重新定价完整当前请求。十一、后果与适用边界原文档后果一节明确划定了这套方案的边界作为使用时的约束Token 压力拥有了一个回放感知的统一所有者压缩与未来插件共享同一核算meter 是零配置组合项部署时在各自路由所属适配器上配置容量含图片定价在 compaction-basic 上配置可选策略覆盖固定启发式只是提供方行为的估计不是精确分词器或请求序列化器——尤其注意 CJK 文本与 JSON schema 在每 token 四字符下会明显低估见 token-meter/README.md 的 Dev Note每次计量都复制带位置信息的表层成本 O(surface)低于阈值即结束的压力检查也不例外遇到畸形持久边界时计量明确失败把损坏的回放转化为具名集成错误而非压力静默漂移步骤后压力检查读取精确记录的路由、工具与前缀边界对无成功 usage 锚点即被拒绝的请求提供方溢出分类由适配器维护兜底。对于想要进一步深入阅读的读者推荐继续查看 token-meter 包文档含tokenUsage、contextPressure、contextBreakdown三个 session 投影的语义、Token meter 子系统文档 与 Compaction 子系统文档。这两个子系统页分别从计量语义与压缩消费方两个视角与本文形成互补。【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
