一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

Claude Code 状态栏实时算费:接入 DeepSeek 峰谷计价

时间:2026-09-13 17:26:01 编辑:袖梨 来源:一聚教程网

在 Claude Code 中接入第三方模型后,状态栏虽然能展示模型、上下文占用和运行时长,却未必能给出可信的费用数据。面对 DeepSeek 的峰谷分时价格,需要从会话 transcript 读取每条消息的 Token 用量和时间戳重新计算,同时处理重复记录、子代理文件与未知模型等容易被忽略的问题。

Claude Code 的状态栏默认能显示模型、目录、上下文占用和时长,但看不到花了多少钱

我给它加上了这一段:

[deepseek-flash] 0911_测试评估软件  git:main ~5
######---- 42%  1m23s  ¥1.11 谷

¥1.11 是这个会话的累计花费, 表示此刻处在 DeepSeek 的空闲(谷时)计费时段——换成高峰时段会变成红色的

看起来挺简单,但真动手会发现三个不看源码就一定会踩的坑。这篇把方案和坑都写出来,也欢迎大家在评论区晒晒自己的状态栏。

一、为什么不能用 Claude Code 自带的 cost 字段

状态栏的 stdin JSON 里其实有个 cost.total_cost_usd。但它在我这个环境下不能用,原因有两层:

第一层,它并不总是按你以为的方式算价。我这边模型走的是 DeepSeek 的 Anthropic 兼容端点:

{
  "env": {
    "ANTHROPIC_BASE_URL": "https://api.deepseek.com/anthropic",
    "ANTHROPIC_DEFAULT_SONNET_MODEL": "deepseek-flash"
  }
}

total_cost_usd 是 Claude Code 按 Anthropic 的价目表匹配 model id 得出的,而 deepseek-flash 显然不在那张表里。

第二层,也是更根本的:DeepSeek 现在是峰谷分时计价,同一个模型峰时和谷时单价差 2 倍。Claude Code 给的是一个聚合标量,结构上就表达不了"哪部分钱是在峰时花的"。就算它能查到价,也只会用单一单价一路算到底。

所以结论很直接:自己从 transcript 算

二、先把计价规则钉死

DeepSeek 在 2026 年 9 月 10 日 12:00(北京时间)调整了 flash 系列定价,单位是元 / 百万 token:

模型输入·缓存命中输入·缓存未命中输出
deepseek-flash¥0.02¥1¥4
deepseek-v4-pro¥0.15¥4.5¥13.5

上表是空闲时段价,高峰时段 = 空闲价 × 2

时段划分(以官方文档为准):

  • 高峰:UTC 周一~周五 01:00–04:0006:00–10:00,即北京时间 09:00–12:0014:00–18:00
  • 空闲:其余所有时间,外加周六周日全天

换算成代码就一个函数:

PEAK_MULTIPLIER = 2
PEAK_HOURS_UTC = ((1, 4), (6, 10))   # [起, 止)

def is_peak(when):
    t = when.astimezone(datetime.timezone.utc)
    if t.weekday() >= 5:              # 周末全天谷时
        return False
    return any(start <= t.hour < end for start, end in PEAK_HOURS_UTC)

注意这里我用的是 UTC,不做本地时区转换——因为时间戳本身就是 UTC,直接按 UTC 的星期几和小时判断,不受机器时区影响,也不会因为夏令时出岔子。

三、数据从哪来:transcript

状态栏的 stdin JSON 里带了 transcript_path,指向本次会话的 JSONL 文件。每条 assistant 消息都带一份 usage:

{
  "type": "assistant",
  "timestamp": "2026-09-13T04:11:53.500Z",
  "message": {
    "id": "msg_xxx",
    "model": "deepseek-flash",
    "usage": {
      "input_tokens": 890,
      "cache_creation_input_tokens": 0,
      "cache_read_input_tokens": 106368,
      "output_tokens": 1710
    }
  }
}

对应关系很清楚:

  • cache_read_input_tokens → 缓存命中价
  • input_tokens + cache_creation_input_tokens → 缓存未命中价
  • output_tokens → 输出价

关键设计:时段按每条消息自己的 timestamp 判断,而不是按"现在几点"。这样跨过峰谷边界的会话会自动拆开算——上午 11:50 那几轮按峰时计价,下午 15:00 的按谷时计价,不会一刀切。

四、三个坑

坑 1:同一条消息会被反复重写,必须去重

这是我第一个撞上的。Claude Code 在流式输出过程中会反复重写同一条 assistant 条目,同一个 message.id 在文件里出现很多次。

我在自己的一个真实会话上数了一下:

usage 行总数 296  →  唯一 message.id 只有 99 个
其中 174 条是重复,有一个 id 重复了 13 次

如果老老实实逐行累加,花费会高估约 2.8 倍

去重很简单,按 message.id 建字典,见过就跳过:

if usage and mid and mid not in seen:
    seen[mid] = (usage, entry.get('timestamp'), msg.get('model'))

坑 2:子代理的花费不在主 transcript 里

这个更隐蔽。我一开始只扫主 transcript,算出来的数字总比感觉少一点。后来翻目录才发现,子代理(subagent)的 usage 是单独成文件的:

~/.claude/projects/<项目>/<session_id>.jsonl          ← 主会话
~/.claude/projects/<项目>/<session_id>/
        └── subagents/agent-xxxxxxxx.jsonl            ← 子代理,单独文件

这些条目都是 isSidechain: true,在主 transcript 里一条都没有。实测漏掉它们会少算约 12%——毕竟我平时没少让子代理去跑搜索。

所以要把子代理目录一起扫进来:

if session_id:
    subagents = os.path.join(os.path.dirname(transcript_path), session_id, 'subagents')
    paths += sorted(glob.glob(os.path.join(subagents, '*.jsonl')))

另外去重要跨文件做(所有文件共用一个 seen 字典),避免万一有重复计入两次。

坑 3:<synthetic> 条目不是真实调用

transcript 里还有一种 model 字段是 <synthetic> 的条目,是 Claude Code 本地生成的消息(比如"No response requested"这类),不是真实的 API 调用

它们也带 usage,但价目表里查不到这个模型。如果代码里直接 PRICES[model] 就会 KeyError 把状态栏搞崩。正确做法是查不到就跳过:

price = PRICES.get(model)
if price is None:
    return None            # 未知模型不计费,而不是报错

顺带说一句,状态栏脚本最忌讳抛异常——崩一次你看到的不是报错,是整个状态栏消失,还很难联想到是算钱算崩的。所以我把整段计价逻辑包在 try/except 里,出了任何问题就静默不显示花费段,绝不连累状态栏本身。

五、性能:状态栏是高频调用的

状态栏刷新很频繁,而 transcript 会长到好几 MB。我实测了一个 5.7MB 的文件:

  • 老老实实逐行 json.loads:103ms
  • 先用子串预筛再 parse:71ms

预筛的道理很简单:文件体积主要是被工具结果撑起来的(读一个大文件、抓一个网页),而那些行根本没有 usage。所以先做一次廉价判断,把绝大多数行直接跳过:

for line in handle:
    if '"usage"' not in line:
        continue            # 工具结果等大行在这里就被丢掉了
    entry = json.loads(line)

再叠一层 3 秒缓存(缓存文件放 TEMP 下、按 session_id 命名),稳态下渲染开销几乎为零。冷渲染 137ms、热缓存 12.5ms。

这里我特意没做增量 offset 解析——复杂度和收益不成正比。3 秒的陈旧度对一个看钱的数字来说完全无所谓。

六、怎么确认算对了

算钱的代码最怕"看起来对"。我用两个办法互相印证:

一是边界单测。时段函数最容易在边界上写错,所以把边界逐个钉了一遍:

周一 00:59 → 谷    周一 01:00 → 峰    周一 03:59 → 峰    周一 04:00 → 谷
周一 05:59 → 谷    周一 06:00 → 峰    周一 09:59 → 峰    周一 10:00 → 谷
周五 10:00 → 谷    周六 02:00 → 谷    周日 06:00 → 谷

二是和一份独立实现逐条对账。我另写了一份逻辑独立、不共享任何代码的求和脚本,在同一时刻读同一个文件,两边结果必须逐位相等。最后差值是 0.00e+00

这一步还纠正了我一个错误。我原本想用"某个会话必须等于 ¥1.0591"当数字,结果怎么都对不上——因为那就是我当时正在用的会话,我一边验证,它一边在长。用会变的东西当基准是没意义的,改成两套实现对账才真正测到了实现本身。

七、最终效果

价目表我单独抽成了一个文件 statusline_pricing.py,只有一张表和两个函数。原因是 DeepSeek 在 8~9 月连着调了两次价——以后改价只需要动这一张表,不用去渲染逻辑里翻。

状态栏第 2 行从:

######---- 42%  1m23s

变成:

######---- 42%  1m23s  ¥1.11 谷

金额的小数位是自适应的(<¥1 显示 4 位,<¥100 显示 2 位),不然新会话一直是 ¥0.00 看不出动静。末尾的 /此刻的时段标记,绿/红配色——写代码前扫一眼就知道现在单价贵不贵。

写在最后

这次改动本身不大,但坑都不在"写代码"上,而在"搞清数据到底长什么样"上。三个坑里有两个(重复条目、子代理单独成文件)光看文档是发现不了的,只能老老实实去数文件里的行。

如果你也在用 DeepSeek 这类第三方端点跑 Claude Code,那自带的 cost 字段基本都指望不上,这套从 transcript 算的思路可以照搬——只要把 PRICES 那张表换成你实际用的模型和价格就行。

最后想收个集:你的状态栏长什么样?

我现在是两行:第一行模型 + 目录 + git 分支和改动数,第二行上下文条 + 时长 + 花费。还想过加的东西有:GitHub CI 状态、当前 todo 进度、机器负载……但状态栏宽度有限,加多了反而看不清。

如果你也改过状态栏,欢迎在评论区贴出来——尤其是你觉得"加了之后再也回不去"的那一两个字段,我挺想抄作业的。代码片段、截图、或者一句话描述都行。


文中 DeepSeek 价格与时段规则来自官方定价页(2026-09-10 生效的调价)。价格会变,照抄前建议先核一遍官网。

热门栏目