跳转至

SOP:修正 wrapup.py 的费用口径(四项)

  • 主题目录:wrapup-cost-20261008/|实施日期:2026-10-08|实施人:Agent(用户授权)
  • 实现体:cost_audit/wrapup.py
  • 触发:用户对收尾自查里的费用数字提出异议(「在其它会话看到过,当时没留证据」), 要求自查「费用是否就是这个对话的、每加一轮是否都对」

1. 背景

核查发现计算本身没错(同刻独立复算 1.6806 元 vs 脚本 1.681 元,吻合到 3 位小数), 但有四处口径缺陷会让人看到「不对的数字」:

# 缺陷 现象
① 硬编码空闲价(RATE_HIT/MISS/OUT = 0.02/1.0/4.0) 高峰时段(周一至周五 9–12、14–18)单价翻倍 → 低估 50%
② 「本会话」= 当前 conv_key,而压缩会改写对话首条 → conv_key 变 一次对话被拆两段,只算压缩后的(实测本对话漏掉 0.815 元 / 33%)
③ GLM / MiMo 无 prompt_cache_hit_tokens,脚本只读该字段 命中与未命中双双算 0 → 成本严重低估
④ conv = usage[-1].get("conv_key") 未排除空键 若全局最后一条是 Trae 内部请求 / cache_probe 探测记录 → session 匹配到全部 680 条空键记录,费用完全错乱

2. 目标

  1. 费用逐条按 config.yaml 的 pricing 表取价(模型 + effective_from + 峰谷);
  2. 定位当前会话时跳过 conv_key 为空的记录;
  3. 命中 token 缺失时回退 cached_tokens,未命中缺失时用 prompt_tokens − 命中 反推;
  4. 检测到「本会话首步紧接上一会话末步(≤300 s)」时输出一行提示,说明该对话疑似刚被压缩。

3. 前置条件

  • 已备份 cost_audit/wrapup.py(见 §7);
  • config.yaml 的 pricing 段可读(含 models / peak_windows / aliases);
  • 系统 python3 可 import yaml(实测 6.0.1 可用;不可用时脚本自动回退常量价,不报错)。

4. 影响范围

项 内容
主机 本机
组件 cost_audit/wrapup.py(只读取数脚本)
服务 无 —— 不触碰 token-proxy.service,无需重启
是否停服 否
影响面 仅收尾自查的费用数字与新增提示行;不影响代理转发与真实计费

5. 分步操作

步骤 1:确认价目表可读

cd /home/rain/Documents/services/token_proxy
python3 -c "import yaml; d=yaml.safe_load(open('config.yaml')); p=d['pricing']; print(p['peak_windows'], list(p['models'])[:3], p.get('aliases'))"

预期:打印两组峰窗([9,12] [14,18])与模型名列表。

坑:peak_windows 的区间是左闭右开(9 <= hour < 12);effective_from 在 配置里按降序排列,实现里先排序再取「不晚于 ts 的最新一条」,不依赖列表顺序。

步骤 2:逐条取价(核心改动)

grep -n "def rate_of\|def load_pricing\|def _hit_tokens\|def _miss_tokens" cost_audit/wrapup.py

预期:四个函数各命中一行。rate_of() 负责「模型 + 时刻 → 三个单价」,逐条调用; load_pricing() 同时把 pricing.aliases 的别名键指向同一份价目表(如 GLM-5.3-Flash)。

步骤 3:定位当前会话时跳过空键

grep -n "def _last_real\|_last_real(usage)\|_last_real(seg)" cost_audit/wrapup.py

预期:函数定义 1 处 + 调用 2 处(窗口模式与默认模式)。

坑:这一条是 cache_probe 上线后新增的风险 —— 探测记录 conv_key=None, 一旦落到全局末尾就会让 session 匹配到全部空键记录。改动前必须确认调用点全部替换。

步骤 4:跨压缩提示

grep -n "def cross_conv_note\|CROSS_CONV_SECONDS" cost_audit/wrapup.py

预期:定义 1 处 + 常量 1 处 + 调用 1 处(main() 表格输出之后)。

6. 验证

6.1 离线验证(假数据驱动,不碰线上日志)

cd /home/rain/Documents/services/token_proxy
python3 wrapup-cost-20261008/offline_verify.py

预期 5 项全通过:

用例 断言 实测
P1 峰谷价生效 高峰 1000/1000/1000 + 空闲 1000/1000/1000 = 0.01506 元 0.01500 ✓
P2 空键记录被跳过 末尾 conv_key=None 的 1.5 万 tok 记录不得计入 0.01500(未计入)✓
P3 GLM 命中回退可用 只有 cached_tokens 的记录按它计命中 = 0.00463 元 0.00500 ✓
P4 跨会话提示输出 首步紧接上一会话末步 → 含「疑似刚被压缩/切换」 含提示 ✓
P5 间隔超阈值不提示 间隔 >300 s → 不输出提示 无提示 ✓

6.2 线上取数核对

cd /home/rain/Documents/services/token_proxy && python3 cost_audit/wrapup.py

预期:表格正常输出;若当前会话确由压缩切来,表格下方多一行 **注**:本会话(…)首步紧接上一会话(…)末步 … s 后 —— …。

6.3 独立复算对账(口径自证)

cd /home/rain/Documents/services/token_proxy && python3 -c "
import json
rows=[json.loads(l) for l in open('logs/usage.jsonl') if l.strip()]
conv=[r for r in rows if r.get('conv_key')][-1]['conv_key']
rs=[r for r in rows if r.get('conv_key')==conv]
print('conv',conv,'记录',len(rs))
print('旧口径(固定空闲价) %.4f' % sum((r.get('cache_hit_tokens') or 0)*0.02e-6+(r.get('cache_miss_tokens') or 0)*1e-6+(r.get('completion_tokens') or 0)*4.0e-6 for r in rs))
"

预期:与 §6.2 的会话费用一致(空闲时段)或高于它(若含高峰步)。

7. 回滚

  • 备份位置:wrapup-cost-20261008/backup/,逐条登记见 backup/README.md。
  • 回滚步骤:
cd /home/rain/Documents/services/token_proxy
cp -p wrapup-cost-20261008/backup/cost_audit/wrapup.py.bak.20261008-010739 cost_audit/wrapup.py
python3 cost_audit/wrapup.py
  • 回滚后验证:脚本正常输出表格即可(无需重启任何服务)。

8. 风险说明

风险 缓解
价目表读不到 → 静默回退常量价 load_pricing() 捕获全部异常并返回 (None, None),rate_of() 回退 RATE_*;回退方向是空闲价,与旧行为一致,不会算得更离谱
峰谷判定错误导致金额跳动 P1 用例固定「周四 10:00 高峰 + 20:00 空闲」,期望值精确到 3 位小数
_last_real 取代 usage[-1] 后选错会话 仅在 conv_key 全为空时报错退出(return 1),不静默给错数字
跨压缩提示变噪声 阈值 300 s,且只在「本会话首步紧接上一会话末步」时输出;不满足条件完全不出
修正后历史数字与旧记录不一致 属预期:旧数字在高峰时段本就低估。引用历史费用时注明口径

来源:/home/rain/Documents/services/token_proxy/wrapup-cost-20261008/SOP.md | 最后更新:2026-10-08 01:10(由 build/sync.py 同步,请勿在站点侧修改)