dsh-qianfan-usage
百度千帆 Token Plan 在 DeepSeek Harness(DSH)里的两个补丁,针对的都是千帆平台特有的问题:
- 用量挂件 —— 在 Web 界面底部那行 token 用量旁边,用一个小 tag 显示「已用百分比」;点一下向上展开明细。
- 正文卫生规则 —— 千帆
DeepSeek-V4.1-Flash-1017在低思考档下会把过程性自述写进正文(Let me…/I'll…),这里在流层把它搬回思考块。
两条都装在一个 bundle 里:一个客户端壳、两个宿主行。
一、用量挂件
收起状态只有一枚小胶囊,不占位置:
● 已用 1.1% ▁▂▁ 26.8M tok 余量 98.9% ▾
点开向上浮出明细面板(套餐/已恢复/已用/剩余、输入·缓存·输出、今日),不推开输入框。 另有完整设置页「千帆用量」:本周期 KPI、按模型的输入/缓存/输出/积分/闲时折扣、补录一条用量、 填写限时恢复额度、重置本周期。
百分比怎么算(对齐官方口径)
控制台「用量」的百分比 = 本周期已消耗积分 ÷ 套餐月度积分额度。积分不是 token 数直接换算, 而是按「模型 × Token 类型」分别折算。官方「积分抵扣逻辑」给的示例(DeepSeek-V4-Pro):
| 计费项 | Tokens | 消耗积分 |
|---|---|---|
| 输入 | 853 | 1 |
| 命中缓存 | 10,240 | 1 |
| 输出 | 427 | 1 |
即 积分 = 输入/853 + 缓存/10240 + 输出/427,且逐模型系数不同——官方只公开了 V4-Pro 这一例。
- 闲时活动:22:00–次日 08:00 调用指定模型按 2 折抵扣(活动至 2026-11-30),插件按事件时间自动判定。
- 系数逐模型校准:实测
deepseek-v4.1-flash-1017是 5 折(官方文档未写), 旧版deepseek-v4.1-flash是 2 折;两者的积分系数也不同。 - 恢复额度:控制台上限 =
floor(套餐额度/3)(Lite 为 3,833)。语义与控制台一致:余量 = 套餐 + 已恢复 − 已用,已使用量 = 已用 − 已恢复,百分比分母仍是套餐额度。
官方档位(月度积分 / token):Mini 1,400/1000万 · Lite 11,500/4200万 · Standard 22,000 ·
Pro 45,000/2.3亿 · Max 165,000/7亿。配置里写 plan: lite 就自动带出额度与显示名。
为什么是本地计量
千帆 Token Plan 没有可编程的用量查询接口:/anthropic/tokenplan/personal/* 与
/v2/tokenplan/personal/* 共 11 条路径实测全部 404,正常推理响应头里也没有配额字段;
控制台数字是登录态页面渲染的。
所以数据来自 DSH 会话日志:每条 assistant/message 都带真实 usage 与 message.source
(provider / model),插件只统计指定 provider 的调用并折算成积分。
它反映「经本实例」的消耗,不是账号全量;最终口径请以控制台为准。
记账链路:
- 激活时用
sessionQuery回填历史; - 运行中监听
session/event增量入账; - 按
(sessionId, seq)精确去重(不是单调水位)——回填与实时是并发路径, 实时事件可能先记下高 seq,若只用高水位会漏掉随后回填到的低 seq(实测漏过 11%); - 按北京自然日分桶写
$DSH_HOME/dsh-qianfan-usage.json; - 前端每 60 秒取
GET /qianfan-usage/summary.json(同源 Cookie 鉴权)。
二、正文卫生规则(hygiene.js)
问题
deepseek-v4.1-flash-1017 在 low 思考档下会稳定地把「过程性自述」写成正文块,例如
GitHub is reachable. / Let me refine the byte-accounting.。同平台的旧版
deepseek-v4.1-flash 完全不会。实测(同一台设备的历史会话):
| 模型 | 消息数 | 有思考块 | 英文正文 |
|---|---|---|---|
| deepseek-v4.1-flash-1017 | 1,225 | 47% | 336,888 字符 |
| deepseek-v4.1-flash(旧版) | 168 | 9% | 0 字符 |
| deepseek-flash(对照) | 440 | 100% | 72 字符 |
已核实网关返回结构正确(非流式 {"type":"thinking",...}、流式 thinking_delta),
适配器也正确映射 thinking → reasoning。所以不是渲染 bug,是模型把旁白写进了正文。
试过但不成立的做法
只靠提示层。 最初只注册一条系统提示分节要求「旁白只写进思考块」。事后核查: 所有近期会话的系统提示快照里规则都在(含规则 = 1),英文正文照样增长 (两个实例:英文 168,701 / 中文 13,328;英文 146,120 / 中文 10,428)。 结论是 1017 不遵守,不是没收到 —— 提示分节保留(对首轮旁白仍有一点约束力),但不足以修好。
客户端搬渲染。 conversation 只有 conversation.chat.reasoning.body 一个接缝,
没有「正文块」槽位,无法把正文挪进思考区。
现在的做法:流层搬迁
在 llm/stream waterfall 上包裹 chunk 流,把后面紧跟工具调用的英文正文块
并进同一消息已有的思考块(块数减一,同时从 replayState.blocks 删掉对应位置,
保持签名表与块数对齐)。
一度被否掉的原因是:千帆走 Anthropic 协议,replayState.blocks 按位置存
thinkingSignature / textSignature,翻转块类型理论上会让签名与块类型错位、回传 400。
实测两条真实请求推翻了这一顾虑:
| 实测 | 结果 |
|---|---|
思考块不带 signature |
HTTP 200 |
思考块带伪造 signature |
HTTP 200 |
该网关根本不校验思考块签名。因此搬迁在千帆上是安全的,但仍然只对白名单
provider/模型启用(默认 qianfan + deepseek-v4.1-flash-1017),换到严格供应商不会误伤。
搬迁分两条路径:
- merge(消息里已有思考块):把旁白并进该思考块,块数减一,签名表同步删掉对应位置;
- synthesize(消息里还没有思考块,例如首轮开口就是旁白):新建一个思考块容纳它。
块索引与块数都不变,签名表只把该位置的类型改成
reasoning。 千帆不校验思考块签名,所以这条路径同样安全。
搬迁的保守边界:
- 只搬「后面紧跟工具调用」的正文 —— 按定义那一段是过程旁白,不是最终回答;
- 中文正文、位于消息末尾的正文(可能是真正的回答)、含代码块的正文,一律不动;
- merge 路径默认只搬 800 字符以内的正文;synthesize 路径上限 8,000 字符, 英文回答缓冲到这个长度仍没等到工具调用,就按原样放行(宁可晚一点输出,不误搬);
- 正文一旦出现中文或代码块,立即当作真回答放行,恢复流式输出;
- 流中断时未决策的正文按原样保留(宁可漏搬,不误搬)。
安装
本仓库是一个 DSH bundle,用 plugin_manager 安装即可(会把目录链接进当前 profile):
# 在 DSH 会话里
plugin_manager { action: "install_bundle", target: "/path/to/qianfan-usage" }
或在 Web 界面的「插件」里从本地目录安装。安装/升级后需要重启 dsh: 运行期安装不会重建客户端模块图,只有启动时挂载的插件才真正生效。
配置
两个 Loader 行,都可在 $DSH_HOME/profiles/<profile>/cordis.patch.yml 里按 id 覆盖。
- id: dsh-qianfan-usage
config:
provider: qianfan # 统计哪个 DSH provider
plan: lite # mini|lite|standard|pro|max(自动带出额度与显示名)
periodResetAt: '2026-12-08T18:37:37+08:00' # 控制台的重置时间
periodDays: 30
tokensPerCreditInput: 853 # 每 1 积分的 token 数(默认 V4-Pro 示例)
tokensPerCreditCache: 10240
tokensPerCreditOutput: 427
modelRates: # 按模型覆盖系数
deepseek-v4.1-flash-1017: { input: 3400, cache: 40960, output: 1700 }
creditScale: 1 # 整体校准 = 本插件积分 / 控制台积分
nightDiscountFactors: # 闲时逐模型系数
deepseek-v4.1-flash-1017: 0.5
deepseek-v4.1-flash: 0.2
nightDiscountStartHour: 22
nightDiscountEndHour: 8
nightDiscountUntil: '2026-11-30'
recoveryCredits: 0 # 本周期已恢复的积分(上限 = 套餐额度/3)
refreshSeconds: 60
keepDays: 400
- id: qianfan-hygiene
config:
enabled: true # 关掉就完全不挂载本行
language: 简体中文 # 正文应当使用的语言
extraRule: '' # 追加在规则末尾的一句额外约束
relocate: true # 是否做流层搬迁(关掉只剩提示分节)
relocateProviders: [qianfan] # 参与搬迁的 provider
relocateModels: [deepseek-v4.1-flash-1017] # 参与搬迁的模型
maxNarrationChars: 800 # 并入已有思考块时,超过这个长度不搬
synthesize: true # 没有思考块时新建一个(旁白不再占正文)
maxSynthesizeChars: 8000 # 新建思考块的长度上限
系数校准:官方只公开 V4-Pro 一例,其它模型默认沿用会高估。 想精确,用控制台实际消耗反推,填
creditScale或modelRates。
自测
node test.mjs # 记账/去重/回填/积分折算/档位/百分比/闲时折扣/管理路由
node test-client.mjs # 客户端:槽位注册、百分比、展开态、无档位提示、无 ESM 顶层导出
node test-hygiene.mjs # 提示分节:名字/顺序/文本/监听器注册/降级不抛错
node test-relocate.mjs # 流层搬迁:用官方 BlockAssembler 真实装配校验(14 项)
另附不依赖 Web UI 的 CLI:node report.mjs(加 --json 出机器可读)。
文件
| 路径 | 说明 |
|---|---|
index.js |
Host:回填、记账、积分折算、账本、summary 路由与管理接口 |
hygiene.js |
Host:注册系统提示分节,约束「思考归思考、正文写结论」 |
client.js |
Client:conversation.composer.dock 的胶囊 + 向上展开面板;settings.section 的用量页 |
widget.js |
兜底挂件(客户端模块不可用时由 index 注入) |
cordis.patch.yml |
两行的挂载声明与默认配置 |
locale/{zh,en}.json |
插件显示名 / 描述 |
report.mjs |
不依赖 UI 的命令行报表 |
test*.mjs |
Host / Client / hygiene / 流层搬迁自测 |
已知限制
- 百分比依赖三件事同时正确:
plan档位、逐模型系数、计费周期起点。缺一个就只是估算。 - 只统计经本 DSH 实例的调用;同 key 的其它客户端、其它设备看不到。
- 客户端挂件必须先安装再启动 dsh;运行期启用不会生效(这是 DSH 的模块加载机制,不是插件问题)。
- 正文卫生的流层搬迁是软改写:只处理「后面紧跟工具调用」的英文正文。 消息末尾的英文旁白(可能是回答)不会被搬,也不会被删除;中文正文一律不动。
- 搬进思考块不等于减少上下文:Anthropic 协议要求把思考块回传,这些 token 仍会 进入请求。它解决的是「旁白占正文、混淆回答」,不是上下文体积。
- 搬迁依赖千帆「不校验思考块签名」这一实测事实。换到校验签名的严格 Anthropic 供应商前,
先把
relocateProviders限定或把relocate关掉。 - 想要零旁白的确定性方案仍是换模型:
deepseek-v4.1-flash(旧版)实测英文正文 0 字符。
No comments yet. Be the first to write one.