Install
openclaw skills install @abc1317679842-ui/workbuddy-token-tracker在每次回答后显示真实 token 消耗与耗时。【仅适配 WorkBuddy 桌面客户端(Windows),不适用于其他 AI 工具/平台——数据源是 WorkBuddy 每轮调用后落盘的 trace 文件,依赖其 hooks 机制】WorkBuddy 客户端不显示 token(内置模式只显示积分、自有 API 模式也不显示),但每轮 LLM 调用结束都会把真实 token/耗时落盘成一个新 trace 文件(~/.workbuddy/traces/<pid>/trace_*.json)。本技能读取该数据,在每次最终回复的最后单独一行附上「耗时 + 输入/输出 token」。当用户说「显示 token」「看消耗」「这次用了多少 token」「统计用量」或任何希望看到每次回答成本时触发。
openclaw skills install @abc1317679842-ui/workbuddy-token-tracker~/.workbuddy/traces/<pid>/trace_*.json(每轮模型调用结束自动生成)+ 客户端 hooks 挂载点——其他 AI 工具/平台(Claude Code、Cursor、ChatGPT 桌面版、其他 OpenClaw 客户端等)没有这套机制,装上也不会工作,请勿在其他环境安装。从技能市场安装 = 文件拷入 skills 目录,不会自动挂 hook。请让 WorkBuddy 助手帮你把下面配置合并进 settings.json(或手动添加):
{
"hooks": {
"UserPromptSubmit": [
{ "matcher": ".*", "hooks": [ { "type": "command", "command": "node <技能目录>/token-tracker.js --hook" } ] }
],
"Stop": [
{ "matcher": ".*", "hooks": [ { "type": "command", "command": "node <技能目录>/token-tracker.js --stop" } ] }
]
}
}
<技能目录> 替换为实际安装路径(如 C:/Users/你的用户名/.workbuddy/skills/token-usage-tracker)。效果:
Stop hook → 每轮回答结束自动弹「本条消耗」Windows 通知(核心体验)UserPromptSubmit hook → 下轮提问时自动注入上一轮用量node <技能目录>/token-tracker.js --stop 查看最近一轮消耗(方式 A)Stop 段即可,其余功能不受影响WorkBuddy 客户端 UI 不显示每轮对话的 token 用量:内置模型只显示「积分」,自有 API 模式也不展示 token。但平台在每次模型调用整轮结束后都会把真实用量写进一个新的 traces/<pid>/trace_*.json(含 totalTokens / totalInputTokens / totalOutputTokens / totalCachedTokens / duration / startedAt / endedAt)。本技能把这些数据读出来,让你每轮都能看到真实消耗。
在生成最终可见回复时,先运行读数脚本,把其输出作为回复的最后单独一行:
node ~/.workbuddy/skills/token-usage-tracker/token-tracker.js
脚本输出形如:
GLM-4.6V | 耗时 3m 43s | 输入 287.9万 / 输出 1.2万 tokens(该轮累计 289.1万,缓存命中 287.2万)
模型名完整显示(去括号说明,不截断);未识别到模型名时省略模型段。数字自动用「万/亿」单位(保留 1 位小数),低于 1 万显示原值,方便速读。
把这行原样贴在回复最末尾(独占一行,前面空一行与其它内容隔开)。2026-08 起以系统通知(方式 C)为准:每次回答结束 Stop hook 已自动弹「本条 Token 消耗」通知,回复末尾的「上一轮」行时效性差且冗余——默认省略;仅在用户明确要求("贴一下用量/这次用了多少")时才运行脚本贴出。
settings.json 的 hooks.UserPromptSubmit 已挂接本技能的 --hook 模式,会在你提交下一轮时自动把「上一轮」的 token 注入上下文,无需手动跑脚本。
WorkBuddy 是 Claude Code fork,支持 Stop 事件(回答结束后触发)。settings.json 的 hooks.Stop 已挂接本技能 --stop 模式:回答结束时本轮 trace 已落盘(实测 Stop 比落盘早 ~15ms,脚本会轮询等待最多 3 秒),读到最新文件即为本条回答的精确统计,然后:
v2.19(2026-08-06 修复):旧逻辑只在
sameRound || !snap时等待;若入口文件恰是"另一个旧文件"(如会话起标题的 terminalTitleGenerator 小 trace)且 ≠ 快照文件,会被误判为"本条"直接弹 toast(曾把 744 tokens 当成本轮展示,真实 122.3 万)。现改为:入口文件非"刚落盘"(≤1s)时一律轮询等待"比入口更新的有效 trace"(≤3s);超时且入口明显是旧文件(落盘 >3s 前)→ 标"上一轮"不冒充本条。备份:token-tracker.js.bak-20260806。
v2.20(2026-08-06,完整消耗聚合):一个用户轮次会落盘多个 trace(起标题内部调用 + 主任务,实测 744 + 122.3 万),v2.19 仍只取最新一个——用户明确要求完整数据。现增加整轮聚合:
--hook(用户提交时)把本轮起点时间戳lastUserMsgAt写入快照;--stop聚合「起点之后、同 pid 目录、(无 sessionId 的内部调用 或 sessionId 与 Stop payload 一致)」的全部有效 trace,累加 in/out/cached/total,耗时 = 窗口内最早 startedAt → 最新 endedAt;无起点记录(手动运行)退化为单 trace(v2.19 行为)。快照保存时保留 lastUserMsgAt。备份:token-tracker.js.v2.19-20260806。
v2.21(2026-08-06,多会话并发快照隔离):v2.20 的
.snapshot.json是全局单文件、不带 sessionId,hook matcher 为.*全局触发——同时开多个会话时,后提交的会话会把lastUserMsgAt覆盖成自己的时间 → 先结束的会话 Stop 聚合起点错乱(用户问"多任务并发时 token 计算是不是没用了")。现改为快照按session_id拆分:hook/stop 都从 payload 读session_id(WorkBuddy 所有 hook payload 均带此字段,实测/官方文档确认),有 sid →.snapshot-<sid>.json(各会话隔离);无 sid(手动运行)→ 全局.snapshot.json(行为不变)。sid 来自外部 payload,只留[a-zA-Z0-9_-]防路径注入。备份:token-tracker.js.v2.20-20260806。验证:隔离测试 7/7 通过(A/B 双会话互不覆盖、起点先后正确、手动全局快照兼容、单会话聚合回归)。已知限制(P1 沿用):无 sessionId 的内部调用(起标题等)在同一 pid 目录同时活跃多会话时无法归属会话,理论上会串入窗口内的其他会话——仅限"同进程多会话真并发",日常单会话/顺序多会话不受影响。
v2.22(2026-08-06,内部调用按最近主任务归属):v2.21 遗留 P1——无 sessionId 的内部调用(起标题等)只看"起点之后",B 会话在 A 任务中途提交时,A 的内部调用时间戳落在 B 起点之后会被 B 误收。用户洞察:"各会话任务结束时间不可能在同一秒"。现改为最近主任务归属:对无 sessionId 的内部调用,找到时间距离最近的主任务 trace(有 sessionId 的,窗口内距离=0,否则取端点最近者),归属该会话;只有归属本会话的才累加。这样利用任务时间线天然分隔并发会话,比"±N 秒容差窗口"精确。备份:
token-tracker.js.v2.21-20260806。验证:隔离测试 10/10 通过(含核心 S4:真并发交错时 A 的内部调用归 A、B 不误收;S5 反向验证)。剩余极限:两个会话的内部调用与各自主任务时间线完全重合(同秒级真并发)时仍无法区分——数据源无 sessionId 标记,属平台限制。
DeepSeek-V4 Flash | 高峰 | 4m 26s 余额¥2.77
输入 391.2万 / 输出 1.9万|缓存99.74%|¥0.25
TOAST_ROW1_MAX_W=47(此前 42u 是保守估算,低估 5u;放宽后分隔符两侧可加空格提升可读性)。时段标注 periodNote():模型声明 peak_multiplier>1(DeepSeek 原厂系=2)且当前在高峰时段(工作日 9-12/14-18)→ 高峰(两字,v2.14 用户拍板:不带 ×N;v2.12「峰×N」太简被否、v2.13「高峰×N」实测 1 分钟+耗时余额必丢被否);其余模型统一定价不显示;预留 night_discount 夜间折扣字段。耗时保持 1m 40s 单位格式(用户要求,不压缩;分钟可为 2-3 位如 12m/123m,fmtDur 上限 999m 59s=9u)。v2.17 分隔符规则(定稿):半角 |(dispWidth 1u,禁止全角「|」2u),两侧各 1 空格——模型名 | 时段 | 耗时(用户反馈"模型名和分隔符挨太近像一体",加空格后 DeepSeek-V4 Flash | 高峰 | 12m 34s 余额¥123.45);无时段 → 模型名 | 耗时;余额带「¥」符号(v2.18 恢复:实测上限 47u 后空间充裕,峰值场景 45u+1u=46u 仍有富余;v2.15 曾去符号省宽度)。极限宽度矩阵(v2.18 定稿验证,上限 47u):高峰场景 12 组合 9 个 ≤47u 含余额(用户峰值场景"12m 34s 几十分钟+余额¥123.45 上百块"=46u ✅;1m 40s+万位=47u 卡线;仅"长耗时+万位余额"丢,现实不存在);无高峰最坏 42u。行2 = 输入 X / 输出 Y|缓存NN.NN%(两位小数)|¥费用(不带「约」字,未收录显示「未收录」;行2 保持全角「|」正文小字视觉统一,有空间)。余额放行1:行2 在 输入/输出+缓存+费用 后已无空间(实测约 49u/上限 52u),追加余额会折叠变 3 行。布局演进(用户多轮实测纠正):v2.8 行2 追加 → 折叠;v2.9 行1 对齐行中线 → base 长时 pad=1 贴死;v2.10 剩余空间居中 → 标题大字超宽变 3 行;v2.11 紧跟时间 + 行1 上限 42u(保守);v2.15 去「¥」;v2.16 全角「|」→半角 |(省 2u);v2.17 用户弹 5 通知实测极限 47u → 上限放宽 + 分隔符两侧加空格;v2.18 恢复「¥」符号(用户:极限有富余就加回)。宽度压力测试(用户要求算极端,含时间变长维度):行1 标题大字上限 47u(v2.18 高峰 43-47u / 无高峰 34-42u);行2 正文双千万级(1e8 以下最坏 10000万)输入+输出 + 缓存99.99% + 价格 ¥3000 级 = 49u / 上限 52u,余 3u + 保护兜底(丢缓存占比);行1 超宽(长模型名)余额自动让位不折叠。说明:Windows toast 第二行默认即「正文小字号」(ToastText02=标题大字+正文小字);更小字号(Caption)需 AdaptiveGroup+HintStyle 自定义 XML(Win10 周年更新+),兼容性有风险,暂未采用。这是当前唯一确认有效的"本条可见"通道(UI 内 systemMessage 通道实测不显示,已放弃)。peak_multiplier:1,提示里会要求搜索核验时段折扣策略。hookSpecificOutput.systemMessage(保留,若未来平台支持即生效,无副作用)。.stop-probe.json 记录每次触发:sameRound=false+waited=ok = 成功拿到本条;waited=timeout = 3 秒内 trace 未落盘,退化为「上一轮」且不弹通知。settings.json 中 hooks.Stop 配置(--hook/手动模式不受影响)。trace.modelInfo.models[0](空壳 trace 从 spans 的 toolOutput[].model 取);带厂商前缀的模型名(如 moonshotai/kimi-k2.7-code、deepseek/deepseek-v4-flash)由 findModel() 做包含匹配,自动落到本地 key。~/.workbuddy/skills/token-usage-tracker/pricing.json(官方人民币价:输入/缓存命中输入/输出,元每百万 tokens;peak_multiplier 高峰倍率;or_id 关联 OpenRouter 模型 id;usd_input_price/usd_output_price 为自动刷新写入的 USD 参考价)。token-tracker.js 自动执行:
openrouter.ai/api/v1/models,无需 key)按模型名精确/包含匹配;pricing.json(auto_converted: true,缓存价按输入 10% 估算,标 note "待人工核验官方价"),同时 hook/手动输出附提示「已自动补录估算价」;pricing._lookedup_models(同一模型当天不再重复联网),输出提示「请用 unified-search 搜官方定价页补录」;未命中输入×输入价 + 命中输入×缓存价 + 输出×输出价,按当前时段取倍率;结果不足 ¥0.01 显示 ¥<0.01。模型名 | 时段标注 | 耗时[ 空格]余额¥X(半角 | 两侧各 1 空格),行2 输入 X / 输出 Y|缓存NN.NN%|¥费用(详见方式 C)。GET https://api.deepseek.com/user/balance + Authorization: Bearer <API key> 即可查询账户余额,无需网页登录——这就是 NIX 等 DeepSeek 客户端"只给 API key 就能显示余额"的原因。~/.workbuddy/models.json 里存在 url 指向 api.deepseek.com 的模型(即用户自己的 DeepSeek API key)时启用;无 key → 不显示余额,不影响其他功能。Deepseek-V4-Flash(与自定义 id 同名);trace/hook payload/transcript 无模式标记;进程级探测(tasklist/wmic/netstat)被本机安全策略禁用。"密钥是否在用"信号抓不到,但 余额变化 = 账户在真实消耗 是其等价信号(有密钥才有消耗)。判定规则:每次查询与上次观测对比(toFixed(2) 字符串比较避免浮点陷阱)——
余额¥X(自定义 API 模式,或其他处用同一 key,显示的是真实余额);余额¥2.77(金额两位小数,带 ¥ 符号——v2.18 恢复:实测行1 上限 47u 后空间充裕,峰值场景 46u 仍有富余)。放行1 的原因:行2 已满(实测约 49u/上限 52u),追加余额会折叠变 3 行。行1 是标题大字,宽度上限 TOAST_ROW1_MAX_W=47u(v2.17 实测:47u 不换行 / 48u 换行);超宽(长模型名)时余额自动让位,不折叠。v2.17 定稿:行1 分隔符用半角 |(全角「|」dispWidth 算 2u 太浪费)且两侧各 1 空格——模型名 | 高峰 | 耗时 余额¥X,模型名与分隔符不再贴死。.balance.json(含 history 数组,保留最近 20 条观测),15 秒 TTL(v2.18 从 60s 压短:用户要求实时,接口实测 300ms 级,正常轮询间隔 >15s 即每轮拿实时数;15s 内连发 toast 才复用缓存秒回)——查询失败降级用旧缓存,无缓存则隐藏余额(不报错、不影响 toast)。api.deepseek.com 发请求,缓存文件只存余额数值与观测历史、不存 key;脚本不打印、不上传 key。token-tracker.js 时检查 pricing.json 的 date——过期才同步调用 refresh-prices.js 联网拉 OpenRouter 免费 API(openrouter.ai/api/v1/models,无需 key,每 12 小时更新)更新所有 or_id 匹配模型的 USD 参考价 + 补未收录模型的估算价,写回 date=今天;当天已刷新则直接跳过、不联网;拉取失败保留本地价、date 不变(次日重试),stderr 如实报错。pricing.json(自动刷新只动 USD 参考字段,不覆盖本地权威主价)。model_prices_and_context_window.json(BerriAI/litellm,静态聚合)、Portkey https://configs.portkey.ai/pricing/<provider>.json(免费 API 每日更新)、厂商官方定价页(权威)。--hook 模式读到的永远是最新已完成的一轮(即上一条回答,精确但滞后一轮)。回答末尾贴出的行不可能显示本条(本条尚未落盘),必须如实标注「上一轮/最近完成轮」,不许冒充"本条"。Stop 事件在本轮回答完全结束后触发,此时(等待后)本轮 trace 已写完,--stop 模式能拿到本条精确数据,通过 Windows 系统通知展示——这是唯一能显示「本条」的通道。traces/<pid>/trace_*.json 的 trace.modelInfo / trace.duration。~/.workbuddy/skills/token-usage-tracker/.snapshot.json 记录「上次已统计的 trace 文件路径 + 该轮统计」,只用于去重;不做总量 diff,因此换会话 / 清空上下文导致总量变小也不会出现负数或 0。该轮累计 是该 trace 内多次模型调用的合计;duration 为平台记录的该轮总耗时(含工具调用等)。