ccusage 的 Qwen Code 数据源适配器:JSONL 解析、Token 计算与用量报告实战
2026/9/21 15:42:27 网站建设 项目流程
  • AI 应用
  • CLI
  • 开发工具

【免费下载链接】ccusage

npx ccusage

项目地址:https://gitcode.com/gh_mirrors/cc/ccusage
点击查看免费下载

ccusage 通过ccusage-adapter-qwen这一专用适配器,把 Qwen Code 本地项目与聊天 JSONL 文件转译为统一的用量条目(usage entries),进而复用整套 daily / weekly / monthly / session 报表视图。本文将结合该适配器的文档与源码实现,完整讲解其数据目录发现规则、JSONL 行级解析原理、推理与缓存 Token 的计价方式、四类报表的汇总逻辑,以及如何通过ccusage qwen命令在实战中查看 Qwen Code 用量。

Qwen 支持目前标记为 experimental,ccusage 与 Qwen Code 都在持续演进,相关行为可能发生破坏性变更。

适配器定位与模块划分

ccusage-adapter-qwen是 ccusage 众多来源适配器(adapter)之一,职责单一:把 Qwen Code 写入本地磁盘的 JSONL 聊天记录,转换成报表渲染所需的LoadedEntry。凡是与 Qwen 无关的通用能力——目录遍历、文件分块、并行读取、日期过滤、表格输出等——都被下沉到ccusage-coreccusage-adapter-common,适配器自身只保留"来源特有"的逻辑。

从 rust/adapters/qwen/README.md 的 "Owns" 一节可以看到清晰的职责边界:

文件职责
loader.rs读取数据源、去重、日期过滤的入口封装
parser.rs原始记录解析、Token 映射、模型命名与成本计算
paths.rs环境变量、默认目录、文件发现规则
report.rs与共享报表形状不同的 JSON / 表格结构

源码实际分为 src/loader.rs、src/parser.rs、src/paths.rs、src/report.rs 四个文件,由 src/lib.rs 统一导出。

对外公开接口(Public surface)

适配器向主 CLI 暴露了如下接口:

  • loader::load_entries— 加载全部用量条目;
  • report::report_from_rows— 由汇总行生成报表 JSON;
  • report::summarize_entries— 按报表类型汇总条目;
  • run— 适配器的命令行入口;
  • has_data— 检测当前环境是否存在可用的 Qwen 数据。

依赖方面,Cargo.toml 声明了ccusage-adapter-commonccusage-corejiff(时区/日期处理)、serdeserde_json,测试则依赖ccusage-test-support。构建上,该适配器属于adaptersCrane artifact 层,所有适配器在同一个 Cargo 调用中并行编译。

快速上手:聚焦报表视图

Qwen 数据源支持与 ccusage 其他来源完全一致的聚焦视图(focused views)。先查看帮助:

# bunx(推荐) bunx ccusage qwen --help # npx npx ccusage@latest qwen --help # pnpm pnpm dlx ccusage qwen --help

可用的报表视图如下(其中weekly同样由 src/report.rs 支持,其通用行为可参考 docs/guide/weekly-reports.md 等文档):

聚焦视图说明参见文档
ccusage qwen daily按日期聚合用量docs/guide/daily-reports.md
ccusage qwen monthly按月聚合用量docs/guide/monthly-reports.md
ccusage qwen session按 Qwen 会话分组用量docs/guide/session-reports.md
ccusage qwen weekly按周聚合用量docs/guide/weekly-reports.md

这些视图通用支持:

  • --json:输出结构化 JSON;
  • --compact:适配窄终端宽度;
  • --offline:使用缓存的定价数据而非在线获取;
  • 从源码看还支持--since / --until日期边界、--jq后处理、--order排序与--no-cost(见 src/lib.rs 中run的调用链)。

run的执行流程为:load_entries加载全部条目 → 若非 session 视图则按--since/--until过滤日期 →summarize_entries按报表类型汇总 → 排序 → 按--json/--jq输出 JSON,或打印标题为 "Qwen Token Usage Report" 的表格(src/lib.rs 的run函数)。需要说明的是,session 视图的日期过滤是在汇总之后基于last_activity进行的,与非 session 视图在条目级过滤不同。

数据目录与文件发现规则

默认目录结构

ccusage 读取 Qwen Code 的聊天 JSONL 文件,默认根目录为~/.qwen,目录结构如下:

~/.qwen/ └── projects/ └── {project}/ └── chats/ └── *.jsonl

即:projects/{project}/chats/*.jsonl这一固定三层结构。适配器 README 中的表达式${QWEN_DATA_DIR:-~/.qwen}/projects/**/*.jsonl是对此的简写。

QWEN_DATA_DIR 环境变量

可以通过QWEN_DATA_DIR覆盖默认根目录,它既可以是单个目录,也可以是逗号分隔的多个目录(例如多份归档根目录),此时多个根会被分别扫描并合并结果:

QWEN_DATA_DIR="$HOME/.qwen,/backup/qwen" ccusage qwen daily

在 src/paths.rs 中,环境变量按逗号切分、去空白、去重,且仅保留实际存在的目录(path.is_dir());未设置时回退到~/.qwen。也就是说,只要QWEN_DATA_DIR存在,默认目录便不再被扫描。

发现逻辑:严格三层路径

discover_chat_files的规则值得注意:它并非简单地递归收集所有.jsonl,而是只接受恰好三层的相对路径projects/{project}/chats/{file}.jsonl——project非空、第二层必须是字面量chats、文件以.jsonl结尾。路径层级不对的文件会被is_chat_file过滤掉。这意味着数据必须严格按上述目录布局存放。

项目名(project)则通过反向扫描路径窗口提取:找到projects/{project}/chats/模式的窗口,取其中的{project}段作为project_path,用于报表按项目归类;找不到时回退为"unknown"

JSONL 行级解析原理

目标记录结构

Qwen 的聊天文件是 JSONL,每行一条记录。适配器只消费如下字段(其余字段由 serde 直接跳过):

{ "type": "assistant", "model": "qwen3-coder-plus", "timestamp": "2026-02-23T14:24:56.857Z", "sessionId": "session-json", "usageMetadata": { "promptTokenCount": 100, "candidatesTokenCount": 50, "thoughtsTokenCount": 10, "cachedContentTokenCount": 5, "totalTokenCount": 165 } }

对应 src/parser.rs 中的QwenLine结构体(#[serde(rename_all = "camelCase")])。其中usageMetadata是 Gemini 风格(Gemini-style)的用量块,五个 Token 字段的含义如下:

字段含义映射结果
promptTokenCount输入 Tokeninput_tokens
candidatesTokenCount输出 Tokenoutput_tokens
thoughtsTokenCount推理(思考)Tokenextra_total_tokens,计入总 Token 并按输出 Token 计价
cachedContentTokenCount缓存命中读取 Tokencache_read_input_tokens
totalTokenCount总 Token(可选兜底)见下文 fallback

三道性能与健壮性设计

  1. 行级预过滤(LinePrefilter):每个可用的 Qwen 行都携带usageMetadata键,因此读取文件时先用LinePrefilter::all(&[br#""usageMetadata""#])基于memmem子串匹配跳过不含该标记的行,在 JSON 解析之前就淘汰大量无效行(如纯用户消息),见 src/parser.rs 的read_chat_file
  2. 宽容反序列化:所有字段都使用ccusage-adapter-common提供的 lenient 反序列化器(lenient_u64non_empty_string等)。Token 数若不是合法的非负整数会被视为0而不是让整行解析失败;字符串会被 trim 且空串视为缺失。这些行为在 src/jsonl.rs 中有完整测试佐证。
  3. 类型化反序列化:存活的行直接反序列化为QwenLine类型化结构,避免中间serde_json::Value树的分配,未使用字段被跳过。

解析过滤与字段兜底

parse_line的过滤与兜底规则如下:

  • 仅接受type == "assistant"的行;缺usageMetadata的行直接放弃;
  • 若输入、输出、缓存读取、推理 Token 全部为 0,则该行被丢弃(零用量记录无意义);
  • totalTokenCount 兜底:当各分项缺失而totalTokenCount存在时,通过apply_total_token_fallback把总数作为输出 Token 兜底。parser 的单元测试falls_back_to_total_token_count_when_qwen_parts_are_missing验证了仅含{"totalTokenCount": 321}的记录会被解析为output_tokens = 321
  • 时间戳兜底timestamp可解析则原样使用,否则回退到文件的修改时间(file_timestamp),文件元数据不可用时再回退到当前系统时间;
  • sessionId 兜底:缺失时生成{project}-{文件名去掉扩展名}
  • 模型名兜底:缺失时使用常量unknown

Token 计算与成本核算

计价口径

  • 推理 Token 按输出计价billable_usageoutput_tokens = display_usage.output_tokens + extra_total_tokens(即thoughtsTokenCount会计入可计费输出),同时保留独立的extra_total_tokens用于展示"总 Token"。
  • 缓存 TokencachedContentTokenCount被当作缓存读取 Token;Qwen 日志目前不暴露缓存创建(cache creation)Token,因此该值恒为 0(见 src/parser.rs 的TokenUsageRaw构造)。

成本计算与模型候选

成本计算遵循"原始模型名 + Qwen 供应商前缀候选"的策略,qwen_model_candidates依次尝试:

  1. 原始模型名(如qwen3-coder-plus);
  2. qwen/{model}
  3. alibaba/{model}

Display模式下不加载定价表、成本展示为 0;在计算模式下加载 LiteLLM 定价数据(支持--offline缓存与--pricing-overrides覆盖),一旦某个候选命中定价表中的条目即按其单价计算成本。若所有候选都未命中,成本为$0.00,并通过missing_qwen_pricing标记缺失定价的模型,便于后续向项目提交 alias 支持请求。

这里有一个细节:显式价格为 0 的条目与"无定价条目"是不同的——单元测试calculate_qwen_cost_returns_explicit_zero_price验证了当free-model显式定价为 0 而qwen/free-model定价为 1 时,原始名命中 0 价条目,成本正确返回0.0而非回退到前缀候选。

汇总与报表生成

汇总逻辑(report.rs)

summarize_entries按报表类型走不同的聚合路径(src/report.rs):

  • Daily:以entry.date为键直接聚合;
  • Weekly / Monthly:先聚合出 daily,再按周(以周日为边界)/ 按月分桶二次聚合;
  • Session:以session_id为键,用SessionAccumulator累积每条记录,最后生成会话汇总。

报表 JSON 形状

report_from_rows生成{ "daily" | "weekly" | "monthly" | "sessions": [...], "totals": {...} }的结构:行数组通过ccusage_core::agent_summary_json序列化,totals通过totals_json计算,无数据时totalsnull

lib.rs 的测试builds_qwen_daily_json_report_with_reasoning_in_total给出了一个可验证的 daily JSON 输出示例(对应上文示例记录,100 输入 + 50 输出 + 10 推理 + 5 缓存读取 = 165 总 Token):

{ "daily": [ { "date": "2026-02-23", "outputTokens": 50, "cacheReadTokens": 5, "totalTokens": 165, "modelsUsed": ["qwen3-coder-plus"], "modelBreakdowns": [ { "modelName": "qwen3-coder-plus", "inputTokens": 100, "outputTokens": 50, "cacheCreationTokens": 0, "cacheReadTokens": 5, "cost": 0.0 } ] } ], "totals": { } }

注意totalTokens(165)包含推理 Token,这正是"推理 Token 计入总 Token"设计的直接体现。

并行读取与去重

文件读取复用ccusage-adapter-commonread_files_parallel:按文件大小做负载均衡分块(size-balanced chunking)后并行读取,结果按原始文件顺序归位;--single-thread可强制单线程。去重采用"首胜"策略:按发现顺序对每个条目计算entry_id(由 session id、时间戳、模型、输入/输出/缓存读取 Token、推理 Token 构成的 JSON 数组字符串),相同 id 只保留第一个。该设计保证并行读取与单线程读取的存活记录一致(src/parser.rs 的entry_id)。

与主 CLI 的接线

Qwen 适配器通过 rust/crates/ccusage/src/adapter/mod.rs 以pub(crate) use ccusage_adapter_qwen as qwen的方式注册,主入口 rust/crates/ccusage/src/main.rs 中Command::Qwen(args)直接调用adapter::qwen::run(args)。此外:

  • last_window.rstimezone.rs会把 Qwen 命令纳入与其余来源一致的"最近窗口"与时区处理逻辑;
  • has_data通过paths::discover_chat_files判断当前环境是否有可读取的 Qwen 数据,用于多来源的自动检测。

环境变量与故障排查

环境变量

变量说明
QWEN_DATA_DIR覆盖数据根目录,支持逗号分隔的多个根目录
LOG_LEVEL调整日志详细程度(0 静默 … 5 trace)

常见问题

  • "No Qwen usage data found":确认数据位于~/.qwen/projects/{project}/chats/这一严格三层结构下;若数据在其他位置或有多个归档根,请通过QWEN_DATA_DIR指定。
  • 成本全部显示为 $0.00:模型不在 LiteLLM 定价数据库中,或--offline缓存中缺失对应条目。此时适配器会尝试qwen/{model}alibaba/{model}前缀候选,仍未命中则成本为 0,可向项目提交 issue 请求 alias 支持。

测试保障

适配器的正确性由三处测试共同保障:

  • src/lib.rs 的集成级测试:加载真实 fixture 目录(通过EnvVarGuard注入QWEN_DATA_DIR)验证条目字段映射、daily JSON 中推理 Token 计入总 Token、以及 session 视图的 ISO 日期边界过滤;
  • src/parser.rs 的单元测试:验证显式 0 价、totalTokenCount 兜底、含冒号字段的 entry_id 唯一性;
  • src/jsonl.rs 的通用测试:验证预过滤跳过无标记行、宽容反序列化在异常类型下不丢整行。

这些测试既是适配器的行为契约,也是理解"Qwen 记录如何一步步变成报表行"的最佳注释。如果你想深入报表的通用形状与配置项,可继续阅读 docs/guide/configuration.md 与 docs/guide/json-output.md。

  • AI 应用
  • CLI
  • 开发工具

【免费下载链接】ccusage

npx ccusage

项目地址:https://gitcode.com/gh_mirrors/cc/ccusage
点击查看免费下载

相关推荐

上一篇:Langchain-Chatchat API 服务解析:FastAPI 路由挂载、Prompt 模板接口与一键启动指南
下一篇:解决gpt4free服务中断:Liaobots与You提供商深度故障分析与修复方案

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询