用量统计尾注 (Usage Stats Tailnote)
> 关于依赖:本技能不声明 python 硬依赖。核心功能(按规范产出用量卡)不依赖 python,只有「精确取数」这一步需要;python 仅作可选增强,无 python 时见下文降级路径。
工作方式:按需调用,不改动任何文件
本技能默认按需调用——用户问到用量、或明确要一张卡时才运行一次,运行完就结束。
它不会自行修改任何文件:算完即结束,产出就是回复里的一段文本。不写偏好设置,不改动记忆文件(除非你显式加 --install)。
可选:如果你希望以后每次都带上这张卡
这属于用户自己的偏好设置,由你本人决定是否添加。两种做法:
- 自己复制:打开技能目录下的
references/tailnote-rule.md,把里面那段格式说明复制到你自己的偏好笔记里即可。删掉它就等于取消。 - 让脚本打印出来(只打印,不写盘):
python "<技能根目录>/scripts/sync_tailnote_rule.py" --print
> 若你的环境已有统一的偏好文件,把打印结果放进去即可;本技能不判断路径、不自动写入。
在此之前可以自查
python "<技能根目录>/scripts/sync_tailnote_rule.py" --check # 检查已有偏好文本是否过长被截断
用途
按需产出标准化的「用量统计」小结,量化本次任务的 token 用量、任务用时、主耗环节、所用模型、命中率与净用量——用于自我复盘与用量记账(不是对外汇报)。
系统由三部分构成(路径均相对于技能根目录):
| 组件 | 路径 | 作用 |
|---|---|---|
| 卡片脚本 | scripts/task_usage.py |
读取会话 jsonl 的用量与时间字段,算出用量/用时,直出渲染好的尾注卡 |
| 偏好打印 | scripts/sync_tailnote_rule.py |
打印格式说明(--print)、检查偏好文本长度(--check);默认不写盘 |
| 格式规范 | references/tailnote-rule.md |
尾注的完整规范(格式、字段口径、参数说明),供阅读与复制 |
何时使用
- 用户问「这次 / 这个任务用了多少 token」「耗时多久」「哪些环节最耗」;
- 用户要求「给回复加用量统计 / 尾注 / 用量卡」;
- 用户想核对自己已有偏好文本里的用量卡格式是否完整。
不适用:用户只是问「token 是什么」「模型如何计价」,或要做跨月账单分析、多账号成本分摊——本技能只统计本机单次任务的用量,不做账单与财务口径。
首次使用时,先阅读 references/tailnote-rule.md 了解完整规范,再按下方步骤执行。
使用步骤
- 定位技能目录:下文命令里的
<技能根目录>请替换成本技能的实际位置——本机通常为~/.workbuddy/skills/usage-stats-tailnote/,从市场安装的通常为~/.workbuddy/skills/usage-stats-tailnote__skillhub/。命令报「文件不存在」时先确认这一条,这是「装了却没反应」最常见的原因。 - 运行脚本(默认按当前工作区自动定位会话):
bash
python "<技能根目录>/scripts/task_usage.py" --cwd "<工作区根目录>" --format=md --record --compare \
--reason "<本次任务主要耗时/消耗来源,一句话>" --opt "<是否优化>"
- 输出即为可直接粘贴的尾注卡(已含每行行首
> <small>🟢</small>前缀),可选:--record:把本次结账追加进历史档~/.workbuddy/scripts/.task_usage_history.jsonl;--compare:产出与「上个任务」的跨任务环比行(含命中率环比箭头);--reason/--opt:预填卡片最后一行,避免人工转录出错。
3. 追加到回复末尾:把 stdout 原样作为尾注,与正文空一行以上、用分隔线隔开(用>引用块书写,切勿放进`` 代码块——代码块内 HTML 不渲染,<small>` 会失效)。
4. 需要打印格式说明时(只读,不写盘):
bash
python "<技能根目录>/scripts/sync_tailnote_rule.py" --print # 打印格式说明,供自行复制
python "<技能根目录>/scripts/sync_tailnote_rule.py" --check # 检查已有偏好文本是否超长
卡片格式与口径(摘要)
完整卡片示例(4 行):
── 用量统计 ──
当前任务用时 XX | 模型 XX·档位 | 用量 输入 XX / 输出 XX / 合计 XX token(命中 XX% | 净用量 XX token)
主耗 1、XX(XX) 2、XX(XX)
原因 一句话 | 是否优化 XX
上式的 XX 只是字段位。真实数字长什么样、怎么从日志算出来的,见下一节的可验算样例。
关键口径(务必遵守,完整定义见规则模板):
- 主口径 = token 用量(=input + output,含缓存命中,即真实消耗);命中率(cached_read/input)单列作参考。
- 净用量(=(input − cached_read) + output)=真实计费量,紧跟命中率后用括号括注。两个数可能相差几十倍,别把「合计」当账单。
- 当前任务用时 = 本任务切片(最后一条真实用户输入之后的记录)首末 timestamp 之差,真实墙钟;每条请求独立计时、不做向前合并。整会话墙钟以「(会话累计 X)」括注。
- 轻量任务半行版:用量合计 ≤ 15,000 token 且 调用 ≤ 2 次时(两条件同时满足),自动改出单行版。
- 模型占比按用量口径;多模型写
模型名:用量,占比%。 - 脚本只读本机
~/.workbuddy/projects下的会话 jsonl(纯本地读取);不折算金额(定制节点单价未知,硬套会误导)。
完整链路:从一条命令到一张卡
下面是一次真实运行的全过程,数字没做任何改写,你可以拿它验算,确认脚本算得对不对。
第 1 步:跑脚本,先看中间产物。 加 --format=json 是为了看得见每一步;平时直接用 --format=md 出卡。
python "<技能根目录>/scripts/task_usage.py" --cwd "<工作区根目录>" --format=json
第 2 步:脚本从会话 jsonl 里算出的中间结果(只截关键字段):
{
"scope": "turn",
"totals": {
"calls": 7,
"request_scope": { "input": 342134, "output": 2532, "total": 344666 },
"billing_scope": { "uncachedInput": 50422, "output": 2532, "total": 52954 },
"cache": { "cachedRead": 291712, "cacheWrite": 0, "hitRate": 85.3 },
"wall_clock": { "seconds": 35.0, "human": "35.0 秒" }
},
"steps": [
{ "stage": "Bash", "total": 150593 },
{ "stage": "Edit", "total": 146739 },
{ "stage": "Read", "total": 47334 }
]
}
第 3 步:换成 --format=md 后直出的卡片(就是该原样粘贴的东西):
> <small>🟢</small> ── 用量统计 ──
> <small>🟢</small> **当前任务用时** 35.0 秒(会话累计 38 分 22 秒) | **模型** model-x·正常 | **用量** 输入 342,134 / 输出 2,532 / 合计 344,666 token(命中 85.3% | **净用量** 52,954 token)
> <small>🟢</small> **主耗** 1、Bash(150,593) 2、Edit(146,739)
> <small>🟢</small> **原因** {{原因(一句话)}} | **是否优化** {{是否优化}}
第 4 步:三个数字当场对得上,说明链路没算错。
| 卡片上的数 | 怎么来的 | 验证 |
|---|---|---|
| 合计 344,666 | 7 条 step 的 total 相加 |
150,593 + 146,739 + 47,334 = 344,666 ✓ |
| 净用量 52,954 | input − cachedRead + output |
342,134 − 291,712 + 2,532 = 52,954 ✓ |
| 主耗 前二 | 按 stage 聚合后取最大两项 |
Bash 150,593 / Edit 146,739 ✓ |
第 5 步:这一趟花了多少。 脚本是纯本地读取,自身不产生任何模型调用,跑一次的开销就是读一个 jsonl 的时间和磁盘 IO,token 消耗为 0。若由智能体代为执行,额外开销只有一次工具调用(约几百 token 的输出);卡片本身是脚本渲染好的文本,不需要模型再生成一遍——这也是它比「让模型随手写个用量小结」更省、且数字不会编的原因。
> 第 3 步里 {{原因}} / {{是否优化}} 是留给人的填空位:用 --reason / --opt 传入后就会被替换掉,手填容易漏行首绿点,建议走参数。模型名按你的会话实际所用输出,这里以 model-x 代称,避免把具体商品名写死进文档。
常用参数
| 参数 | 说明 |
|---|---|
--cwd <工作区> |
指定会话所属工作区(默认取当前目录);传入工作区子目录会自动上溯到所属工作区并给出提示;上溯后仍无会话 → 退出码 3,不擅自换数据源 |
--any-workspace |
不限工作区,取全局最近会话 |
--format=json\|md |
直出渲染好的尾注卡(默认输出 JSON);md 自动选完整版 / 半行版 |
--reason "…" / --opt "…" |
预填卡片最后一行 |
--effort <档位> |
模型档位 lite / 正常 / reasoning(默认「正常」) |
--record / --compare |
留档 / 与上个任务环比(基线取最近一条跨会话记录) |
--whole-session |
用时/用量按整会话算(默认只算「当前任务」) |
--last-turn |
显式声明当前任务口径(等于默认) |
--since <epoch_ms> |
只统计某时间点之后(会关闭「当前任务」自动切片) |
--bare |
卡片不带 > 引用前缀(只去 >,不要省略 <small>🟢</small>) |
--no-subagents |
不合并子代理用量 |
--canon <路径> |
(同步脚本)指定格式说明来源;默认 ~/.workbuddy/tailnote-rule.md,无则回退本技能自带模板 |
退出码与故障处理
| 退出码 | 含义 | 处理 |
|---|---|---|
| 0 | 正常输出 | 直接粘贴卡片 |
| 1 | 仅 sync_tailnote_rule.py --check:偏好文本中有内容被截断 |
按提示把过长的段落改短或拆分,再跑一次 --check 到 0 |
| 2 | 参数错误(未知参数 / 缺取值 / 取值非法) | 按 --help 修正参数重跑 |
| 3 | 会话未定位(--cwd 上溯后仍无会话,或全局无任何 jsonl) |
不要手算数字顶上:stderr 会给出「相近目录 / 最近活跃目录」候选,按候选修正 --cwd 重跑;仍取不到就在回复里明说「尾注缺失 + 原因」,不要凭记忆拼一张卡 |
> 两个脚本的退出码语义一致:0 正常、1 检查未通过、2 参数错误、3 数据源未定位。写脚本调用时可直接按码分支,不必解析文案。
应避免的用法
| 不要这样做 | 为什么 | 应该这样做 |
|---|---|---|
把 --format=md 的输出塞进 ``` 代码块 |
代码块内 HTML 不渲染,<small> 会失效,绿点看不见 |
用 > 引用块原样粘贴 |
用 --bare 顺手连绿点一起删 |
绿点是「确实跑过脚本」的唯一凭证,删了无法区分手打与直出 | --bare 只去 >,保留 <small>🟢</small> |
| 拿不到会话就按印象写个数 | 编造的数字比没有更糟——它会进历史档,污染后续环比 | 明确说「用量数据不可得」并给下一步 |
| 把「合计」当账单 | 合计含缓存命中,净用量才是真实计费量 | 两个数并列展示,注明口径 |
| 用本技能做跨月账单 / 多账号分摊 | 它只统计本机单次任务,没有财务口径 | 交给账单系统;本技能只做逐任务记账 |
| 默认按整会话算 | 会把历史轮次算进「本次任务」,越往后越虚高 | 默认即当前任务切片,需要时才 --whole-session |
数据边界:支持什么、不支持什么
先把能做的和做不了的并列写清——下面这张表就是它的能力边界,越界请直接换工具,不要硬套本技能。
| 维度 | 支持 | 不支持 | 越界时脚本怎么反应 |
|---|---|---|---|
| 日志来源 | 本机 ~/.workbuddy/projects/**/*.jsonl 的会话日志;~/.workbuddy/scripts/.task_usage_history.jsonl 的历史汇总 |
平台服务端账单、其他客户端或其他设备的日志、云端同步记录 | 一条 jsonl 都没有 → 退出码 3,stderr 说明「全局无任何会话」,不出卡 |
| 会话规模 | 单工作区、单会话、单任务;整会话用 --whole-session |
跨工作区汇总(--any-workspace 只取最近一个,不是求和)、多账号合并、跨月累计 |
请求了不存在的维度 → 参数错误,退出码 2 |
| 时间口径 | 本任务切片(末条真实用户输入之后);--since 指定起点;整会话墙钟作括注参考 |
按自然日 / 自然月切分、按时区换算、跨设备的时钟对齐 | 时间窗内无记录 → 合计为 0,卡片照出但数字为 0,不编造 |
| 数据完整性 | 已落盘的记录;客户端一旦写入即可读到 | 当前回合尚未落盘的部分——会话 jsonl 是延迟写入的,回合进行中取数通常只能读到已 flush 的尾部 | 数字偏小属正常,建议在回合结束时取数,别在回合中途反复重跑 |
| 口径换算 | token 计数、缓存命中率、净用量(三者同源可互算) | 金额折算(定制节点单价未知,硬套必然误导)、跨模型单价比较 | 不做折算,也不会给「约合多少钱」这类数字 |
| 运行环境 | Windows / macOS / Linux;有 Python 3 时可精确取数 | 无 Python 时无法精确取数(可退回按规范手写,但数字不可信) | 无 python → 见文首依赖说明的降级路径,并明确告知取数失败,不做无声失败 |
| 对文件的写入 | 默认不写任何文件 | 自动改写其他位置的配置 | 只有显式 --record 才向历史档追加一行聚合数字 |
配套的数据处理事实(与上表互补,都是可核查的实现细节):
- 读什么:仅
usage(token 计数)、model、timestamp三类字段,以及历史档里的汇总数字。 - 正文怎么处理:为定位「本次任务从哪条消息开始」,脚本只判断记录里有没有
<user_query>标记(用于排除压缩摘要造成的重复计时)。该判断只在内存里做一次布尔运算,不落盘、不输出、不参与任何计算。 - 输出内容:卡片里只有聚合数字、模型名与时间。
- 运行方式:两个脚本只用 Python 标准库做本地文件读取,运行过程不需要网络。
- 包内构成:纯文本与脚本文件;脚本按
~与当前工作目录定位路径,不依赖固定绝对路径。
常见问题速查
先按症状定位,比从头读快:
| 你遇到的症状 | 直接看 |
|---|---|
| 装了以后什么都没发生 | 本节 Q1;「使用步骤」第 1 条(路径) |
| 命令报「文件不存在」/「不是内部或外部命令」 | 本节 Q4;「使用步骤」第 1 条 |
| 出来的数字比预期小 | 「数据边界」表的「数据完整性」行(回合未结束,日志还没落盘) |
| 数字和账单差很多 | 本节 Q2;「卡片格式与口径」的净用量条 |
| 卡片只有一行 | 本节 Q3(半行版是自动降级,不是出错) |
| 卡片没有绿点 / 格式乱了 | 「应避免的用法」前两行(代码块内 HTML 不渲染) |
| 退出码不为 0 | 「退出码与故障处理」表,按码查 |
| 想把卡片变成每次都带 | 文首「可选」一节 |
Q:装了为什么没立刻看到卡片?
A:本技能是按需调用的——直接说「出个用量卡」或「这个任务用了多少 token」就会出来。若不希望每次都开口,见文首「可选」一节,自己复制一段格式说明到偏好里。
Q:数字和平台账单对不上?
A:本技能读的是客户端本地记录的 token 计数,口径是「本机本次任务」,不做金额折算(定制节点单价未知,硬套会误导)。账单以平台结算为准。
Q:为什么有时出来是半行版?
A:用量合计 ≤ 15,000 token 且调用 ≤ 2 次时自动切半行版,避免为一个小任务写四行。
Q:命令报「文件不存在」,是不是技能坏了?
A:多半是没把 <技能根目录> 换成本机真实路径,见「使用步骤」第 1 条。这是环境差异,不代表技能有问题。