- AI 应用
- CLI
- 开发工具
【免费下载链接】ccusage
npx ccusage
本文以 ccusage 的 Grok Build CLI 数据源适配器为主线,讲解它如何从本地~/.grok目录读取updates.jsonl会话日志,生成 daily / monthly / session 三类聚焦报告,并深入剖析 token 拆分、costUsdTicks记账成本与定价表回退的底层实现。读完本文,你将掌握ccusage grok系列命令的完整用法、GROK_HOME数据根解析规则,以及成本在display/auto/calculate三种模式下的计算路径,并能在数据缺失、成本为零等常见故障下快速定位原因。
Grok Build CLI:ccusage 的一个一等数据源
ccusage 可以将本地 Grok Build CLI 的会话日志作为受支持的数据源直接读取(对应文档 docs/guide/grok/index.md)。与 Claude Code、Codex、Gemini 等适配器一样,Grok 数据接入后复用 ccusage 统一的报告模型(unified report model)与聚焦报告模型(focused report model),这意味着你不需要学习一套新的报告语法——同一套 daily / monthly / session 视图、JSON 输出、成本模式和终端表格对 Grok 一视同仁。
在命令行入口处,Grok 适配器被挂载为顶层子命令:rust/crates/ccusage/src/adapter/mod.rs中pub(crate) use ccusage_adapter_grok as grok;,rust/crates/ccusage/src/main.rs中Some(Command::Grok(args)) => adapter::grok::run(args)将ccusage grok ...分派到适配器的run函数。适配器本身的代码位于 rust/adapters/grok/,由四个模块组成:
paths.rs:数据根解析与sessions/**/updates.jsonl发现;parser.rs:turn_completed行准入、token 拆分、定价候选生成与成本计算;loader.rs:并行读取、跨会话全局去重与has_data探测;report.rs:daily / monthly / session 汇总形状。
快速上手:聚焦视图命令
大多数用户可以从统一报告(unified reports,例如ccusage daily,会汇总所有已检测数据源)开始。只有在需要把同一报告形状聚焦到 Grok 用量上时,才加grok命名空间:
# 按天汇总 Grok 用量 ccusage grok daily # 按月汇总 Grok 用量 ccusage grok monthly # 按 Grok 会话分组 ccusage grok session三个视图分别聚合不同的维度:
| 聚焦视图 | 描述 | 参见 |
|---|---|---|
ccusage grok daily | 按日期聚合用量 | Daily Usage |
ccusage grok monthly | 按月聚合用量 | Monthly Usage |
ccusage grok session | 按 Grok 会话分组用量 | Session Usage |
从源码看,这几种视图的差异集中在report.rs的summarize_entries:daily 直接按entry.date分组(summarize_by_key);monthly 先算 daily 再按BucketKind::Monthly分桶;session 则用BTreeMap<String, SessionAccumulator>累积每个session_id的条目,从而把活动时间范围(first_activity/last_activity)和项目路径带到会话行上——这正是会话表格的 Last Activity 列与会话 JSON 输出的数据来源。
支持的选项
这些聚焦视图支持--json、--compact、--mode与--offline等共享选项:
# JSON 输出,便于脚本消费 ccusage grok daily --json # 压缩输出 + 离线模式(使用内嵌定价快照) ccusage grok monthly --compact --offline # 强制从 token 数重新计价 ccusage grok session --mode calculate--mode的三档取值(auto/calculate/display)在 Cost Modes 有全局说明;Grok 适配器对它们的特殊处理见下文「成本核算」一节。其他通用选项如--since/--until日期过滤、--timezone时区、--order排序、--breakdown模型细分等也适用于ccusage grok系列,详细清单见 Command-Line Options。
Cache Create 列的动态显隐
聚焦终端表格的一个细节是:当选中的 Grok 行没有记录任何 cache-creation token 时,表格会省略Cache Create列;一旦选中的行出现非零值,该列重新出现。这一行为在 lib.rs 的table_options中实现——show_cache_creation: rows.iter().any(|row| row.cache_creation_tokens > 0),并有对应单测hides_cache_creation_when_selected_rows_are_zero/shows_cache_creation_when_any_selected_row_is_nonzero佐证。无论表格如何显示,JSON 输出 的稳定报告 schema 中始终保留cacheCreationTokens字段,不会因值为零而缺失。
数据源:会话目录布局与发现规则
Grok 适配器读取的是 Grok 主目录下各会话中的updates.jsonl(每行一条会话更新事件),只统计其中「已完成回合」的记录。目录布局如下:
$GROK_HOME/ # 或 ~/.grok └── sessions/ └── <url-encoded-cwd>/ └── <session-uuid>/ ├── updates.jsonl # 主数据源(turn_completed + usage) └── summary.json # 可选元数据要点:
- 主数据源是
updates.jsonl:只会计入sessionUpdate == "turn_completed"且带有可用 usage 拆分的行。进行中的回合(in-progress turns)在完成之前不会出现;中途被 kill 的会话永远不会写入turn_completed,因此其用量无法被报告。 logs/unified.jsonl不被使用:该日志没有逐请求(per-request)的模型 id,token 无法定价或归属到具体模型,故适配器完全忽略它。这一点在loader.rs的测试has_data_is_false_when_the_home_has_no_sessions中也有体现——只有logs/unified.jsonl时has_data()返回false。
根目录解析(优先级从高到低)
- 非空的
GROK_HOME(Grok 官方环境变量,单一根目录); ~/.grok。
paths.rs中resolve_root()的实现完全对应这条规则:先读GROK_HOME,只有在该变量存在且 trim 后非空、且路径是目录时才采用;否则回落到用户主目录下的.grok。配套测试覆盖了空值回退(empty_grok_home_falls_back_to_default_home)、缺失根(missing_roots_yield_empty_discovery)以及GROK_HOME指向非目录(non_directory_grok_home_yields_empty_discovery)等边界情况。
发现过程(discover_session_files)递归扫描根下的sessions目录,收集所有命名为updates.jsonl的 JSONL 文件(忽略events.jsonl等同目录下的其他 JSONL),并顺带探测同目录的summary.json作为可选元数据;结果按路径排序,保证多次运行加载顺序稳定。
# 数据不在默认位置时,显式指定根目录 GROK_HOME="$HOME/.grok" ccusage grok dailysummary.json虽然是可选的,但它能显著改善报告的归属质量:其中的info.id提供规范化会话 id(行内params.sessionId缺失时使用),info.cwd或git_root_dir提供项目路径(优先info.cwd),current_model_id提供默认模型。当updates.jsonl行内没有modelUsage明细时,顶层 usage 字段会以该默认模型命名。若完全没有 summary 和默认模型,顶层 usage 会被标记为模型unknown(见parser.rs的load_session_meta与测试names_top_level_usage_unknown_when_summary_has_no_default_model)。
另一个值得注意的实现细节:当 summary 缺失时,项目路径取自会话目录的父目录名,该目录名是 URL 编码的 cwd(例如D%3A%5Cwork%5Cproj),适配器用url_decode_lightweight做字节级百分号解码(D%3A%5Cwork%5Cproj→D:\work\proj),并正确处理多字节 UTF-8 路径(如%2Fhome%2F%C3%A9projet→/home/éprojet),遇到非法字节时降级为替换字符而非崩溃。
报告计算逻辑:token 拆分、推理 token 与成本
Token 用量:把 OpenAI 风格计数拆成 ccusage 字段
Grok 记录的是 OpenAI 风格的 usage,其中inputTokens已经包含缓存。适配器(parser.rs的split_input_tokens/split_tokens)按以下规则拆分:
| Grok 字段 | ccusage 字段 | 规则 |
|---|---|---|
inputTokens − cachedReadTokens | input_tokens(未缓存输入) | 缓存读数被钳制为 ≤ input |
cachedReadTokens | cache_read_input_tokens | 缓存读取 |
outputTokens | output_tokens | 原样记录 |
reasoningTokens | 丢弃(不计入总额) | 已是outputTokens的子集 |
cacheCreationTokens | cache_creation_input_tokens | 从未缓存余量中再切出 |
拆分的核心约束是保证三个部分之和恰好等于inputTokens:先uncached = input − min(cached, input),再cache_creation = min(cache_creation, uncached),最后uncached -= cache_creation。loader.rs的测试loads_session_tree_with_uncached_split给出了一个直观例子:inputTokens=100, cachedReadTokens=40时,报告为未缓存输入 60 + 缓存读取 40,extra_total_tokens为 0。
modelUsage是一个「模型 id → 该模型 usage」的映射。若一行turn_completed携带了modelUsage,适配器会为其中每个模型各生成一条条目(测试multi_model_turn_emits_one_entry_per_model验证了这一点);只有modelUsage缺失时才回退到顶层 usage 字段。
Reasoning tokens:已经包含在输出中,绝不重复计费
Grok 的reasoningTokens是outputTokens的子集,因此它们已经计入总数。适配器不会把它们加到 output 之上——无论是 token 数还是成本。parser.rs在构造LoadedEntry时硬编码extra_total_tokens: 0,并附注说明:Grok 的totalTokens == inputTokens + outputTokens,若把 reasoning 再次计入总 token 数,就会造成二次计费。测试does_not_add_reasoning_tokens_to_the_total专门验证了「只有 reasoningTokens=42、其余为 0」的回合最终 input/output/extra 全为 0。
预计算成本:costUsdTicks就是账单金额
Grok 在每条turn_completed上记录costUsdTicks,单位为 1e-10 USD(即 1 tick = 1e-10 美元)。parser.rs中常量COST_USD_TICKS_PER_USD: f64 = 1e10,cost_usd_from_ticks将 ticks 换算为美元。注释中说明该换算已用 Grok CLI 1.0.0 的 58 条回合数据验证:每条costUsdTicks / 1e10都能精确还原 xAI 对xai/grok-4.5的列表价。
ccusage 把costUsdTicks视为发票成本(invoice cost),因此:
display模式和默认的auto模式报告的就是 Grok 实际收取的金额(display直接返回 ticks 换算值;auto在 ticks 存在时也优先使用它);calculate模式,以及auto模式下没有记录 ticks 的回合,才回退到定价表估算。
定价回退:候选模型 id 与长上下文分层
当需要按定价表估算时(parser.rs的pricing_candidates),适配器会为原始模型 id(如grok-4.5-build)生成一串候选:
- 去掉可选的
[grok]前缀并 trim; - 保留原始形式:
grok-4.5-build; - 加上
xai/前缀:xai/grok-4.5-build; - 加上
x-ai/前缀:x-ai/grok-4.5-build; - 再对去掉尾部
-build的规范化形式重复上述三种:grok-4.5、xai/grok-4.5、x-ai/grok-4.5。
查找时先做精确匹配(pricing.find_exact,确保用户通过--pricing-override提供的精确覆盖不会被模糊命中遮蔽),全部候选精确匹配失败后才进入子串模糊匹配。测试exact_raw_model_pricing_override_beats_normalized_fallback和prices_via_the_stripped_candidate_when_the_build_form_is_missing分别覆盖了这两种路径。若所有候选都未命中定价表,成本保持为零,并记录missing_pricing_model警告(测试reports_an_unpriced_model_as_missing_pricing_instead_of_dropping_it)。
长上下文分层:xAI 的长上下文费率会在回合的完整上下文(新鲜输入 + 缓存读取 + 缓存写入)超过模型边界时生效——grok-4.5与grok-4.6的边界是 200K token。以 cost.rs 中的注释与测试为例,grok-4.5基础费率为输入 $2 / 输出 $6 / 缓存读取 $0.3(每百万 token),超过 200K 上下文后变为 $4 / $12 / $0.6;分层依据的是请求携带的整个上下文,而不是未缓存输入——因为对提示缓存激进的 Agent 来说,大量上下文以缓存读取形式存在。由于一条turn_completed行聚合了多个 API 请求,分层只能按回合而非按请求选择,因此估算结果只是对账单的近似。
模型标签
显示用的模型标签就是原始的modelUsagekey(例如grok-4.5-build),不做改写。在统一报告中,Agent 列(Agent column)负责标识 Grok 来源,模型列则保留原始 id,便于和 Grok 侧日志一一对应。
环境变量
| 变量 | 描述 |
|---|---|
GROK_HOME | Grok 官方配置/数据主目录(单一根目录) |
LOG_LEVEL | 调整日志详细程度(0 静默 … 5 跟踪) |
GROK_HOME只接受单个根目录(不像CODEX_HOME等支持逗号分隔列表),完整的环境变量清单与默认值见 Environment Variables。LOG_LEVEL的六档取值(0 静默 / 1 警告 / 2 普通 / 3 信息(默认)/ 4 调试 / 5 跟踪)在脚本管道和 CI 场景下很常用,例如LOG_LEVEL=0 ccusage grok daily --json获得干净的纯 JSON 输出。
配置:grok命名空间
与其他聚焦数据源一样,grok命名空间支持同一套共享报告选项,可写入 ccusage 配置文件(如~/.config/claude/ccusage.json或项目级.ccusage/ccusage.json,优先级见 Configuration):
{ "grok": { "defaults": { "offline": true }, "commands": { "session": { "json": true } } } }语义规则:
grok.defaults应用于所有 Grok 报告;grok.commands.daily、grok.commands.monthly、grok.commands.session分别提供报告级覆盖(同名项覆盖 defaults);- 数据根目录只从
GROK_HOME或~/.grok发现,不从 ccusage 配置中读取——配置只能控制报告选项,不能指定数据目录。
故障排查
没有找到 Grok 用量数据
# 确认根目录下存在已完成回合 ls ~/.grok/sessions/**/updates.jsonl # 数据在其他位置时显式指定 GROK_HOME=/path/to/grok-home ccusage grok daily进行中的回合要等turn_completed写入后才会出现。若目录里只有logs/unified.jsonl而没有sessions/**/updates.jsonl,has_data()返回 false,报告自然为空——这是设计行为而非 bug。
成本显示为 $0.00
Grok 开始记录costUsdTicks之前写入的回合没有携带成本,display模式对它们显示为零。此时改用定价表为这些回合计价:
ccusage grok daily --mode calculate若某个模型在定价表中缺失,成本同样保持为零,并可能出现 missing-pricing 警告(可通过--debug或LOG_LEVEL=4观察)。你也可以用--pricing-override为私有或代理模型补充精确价格,适配器会优先做精确匹配。
回合进行中时总数低于预期
v1 只统计已完成的回合。等待回合结束(turn_completed写入)后重新运行报告即可:
ccusage grok daily深入阅读
想继续探索该适配器的实现与验证,可以按需阅读:
- 数据根解析与会话发现:paths.rs(含
GROK_HOME回退、updates.jsonl 筛选、嵌套会话树测试) - 行准入与 token/成本计算:parser.rs(
turn_completed过滤、usage 拆分、costUsdTicks换算、定价候选与长上下文分层) - 加载与去重:loader.rs(并行读取、跨会话按
eventId|model全局去重、按时间戳排序) - 报告形状:report.rs(daily/monthly/session 汇总、会话活动范围)
- 适配器总体说明与测试方式:rust/adapters/grok/README.md
- 成本模式语义:Cost Modes;命令行选项:Command-Line Options;配置方式:Configuration
- AI 应用
- CLI
- 开发工具
【免费下载链接】ccusage
npx ccusage
相关推荐
10分钟出第一张AI图像:SCAIL-2让ComfyUI新手免折腾
10分钟出第一张AI图像:SCAIL 2让ComfyUI新手免折腾 SCAIL 2 是一款专为 ComfyUI 重新打包的扩散模型。扩散模型用大白话说,就是那种
AI 应用CLI开发工具jQuery自动补全插件终极指南:3步搞定输入框智能提示
jQuery自动补全插件终极指南:3步搞定输入框智能提示 每天都有无数人在表单里一遍遍手打重复内容:国家名、城市名、收货地址、产品编号……输错了还得删掉重来。其
人工智能AI AgentAgent 记忆MCP 服务从TextCaps到AI2D:Pix2Struct在8大视觉问答任务中的性能表现分析
从TextCaps到AI2D:Pix2Struct在8大视觉问答任务中的性能表现分析 Pix2Struct是一个功能强大的视觉问答框架,能够处理从图像描述生成到
AI 应用CLI开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考