尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

ccusage Pi 适配器深度解析:从 pi-agent 会话存储到用量报表的完整链路

ccusage Pi 适配器深度解析:从 pi-agent 会话存储到用量报表的完整链路 ccusage Pi 适配器深度解析从 pi-agent 会话存储到用量报表的完整链路【免费下载链接】ccusagenpx ccusage项目地址: https://gitcode.com/gh_mirrors/cc/ccusage本指南以 ccusage 仓库中的 Pi 适配器ccusage-adapter-pi为主体系统讲解它如何将 pi-agent 的会话存储含配置文件声明的命名存储转换为报表所需的用量条目usage entries涵盖数据源定位、模块架构、回放去重、Token 解析计价与报告输出的完整链路。读完本文你将掌握 Pi 数据源目录约定、pi.stores配置规则、去重与父子会话回放抑制原理以及ccusage pi系列命令的底层实现依据。Pi 适配器的定位与职责边界Pi 适配器位于 rust/adapters/pi是 ccusage 众多 agent 源适配器之一。它的职责非常聚焦把 pi-agent 的存储store转换成报表渲染所消费的用量条目。其中存储既包括 pi-agent 默认写入的会话目录也包括用户在ccusage.json中通过pi.stores[]声明的其他命名目录。从源码结构看该 crate 严格遵循仅处理本源特有逻辑的边界任何与 Pi 源无关的通用能力时间日期分组、价格计算、模型别名解析等都归属于ccusage-core或ccusage-adapter-common适配器只保留本源的差异部分。这一点在 rust/adapters/pi/README.md 的 Owns 一节有明确声明也是理解后续模块划分的总纲。四个自有模块的分工Pi 适配器将自身逻辑拆分为四个文件各司其职模块职责对应源码loader.rs数据源读取、去重dedupe、日期过滤rust/adapters/pi/src/loader.rsparser.rs原始记录解析、Token 映射、模型命名rust/adapters/pi/src/parser.rspaths.rs环境变量、默认目录、文件发现rust/adapters/pi/src/paths.rsreport.rs与共享形状不同的 JSON 与表格输出rust/adapters/pi/src/report.rs对外公开的 API 面适配器通过 rust/adapters/pi/src/lib.rs 暴露以下公共函数供上层 CLI 与统一加载器all-agent 报表调用loader::load_entries— 加载默认 Pi 存储的条目loader::load_entries_for_store_path— 按单个存储路径加载loader::load_entries_for_store_paths— 按多个存储路径加载命名存储走此路径paths::named_store_paths— 解析配置中的命名存储路径paths::paths as default_paths— 解析默认 Pi 会话路径report::report_from_rows— 由汇总行生成 JSON 报表report::summarize_entries— 将条目汇总为日报/月报/周报/会话报告行run— 适配器级入口串联定价加载、加载、过滤、汇总、输出全流程数据源环境变量、默认目录与命名存储Pi 适配器的数据源定位遵循一个清晰的优先级链实现在 rust/adapters/pi/src/paths.rs${PI_AGENT_DIR:-~/.pi/agent/sessions} ccusage.json 中的 pi.stores[] 条目具体解析顺序paths::paths命令行参数优先若传入--pi-path且非空则将其作为路径列表使用环境变量次之读取PI_AGENT_DIR环境变量非空则使用其值默认目录兜底取~/.pi/agent/sessions通过 ccusage 的 home 目录工具定位仅当该目录存在时返回。值得注意的细节--pi-path与PI_AGENT_DIR都刻意不展开~沿用其历史语义而命名存储路径则会像其他配置文件路径一样展开~。路径列表支持逗号分隔且会自动去重、过滤不存在的目录——例如 /a, /b, /a, /missing 最终只得到[/a, /b]。命名存储pi.stores[]配置文件ccusage.json中可声明额外的 pi 格式会话目录。该配置项的 schema 定义在 rust/crates/ccusage-config/src/config_schema.rs结构为{ pi: { stores: [ { name: omp, path: ~/.omp/agent/sessions }, { name: o3-fork, path: /data/pi-o3/sessions } ] } }每个条目必须包含字符串字段name与path校验规则见 rust/crates/ccusage-config/src/config.rs 的parse_named_pi_storesname必须匹配^[a-z][a-z0-9_-]{0,31}$小写字母开头最长 32 字符name不能与内置 agent 名称冲突如pi、codex、all等均为保留名name不能重复path必须是非空字符串。空数组stores: []视为无操作。校验错误只对真正读取这些存储的命令生效command_uses_named_pi_stores门控不会拖累其他 agent 的命令执行。命名存储的模型命名与去重身份当通过命名存储加载时解析出的模型名会被加上[store_name]前缀例如原始模型gpt-5在omp存储中显示为[omp] gpt-5同时去重身份entry_id_for_store也会把 store 名纳入拼接键因此同名会话、相同时间戳的记录在不同存储之间不会被误判为重复rust/adapters/pi/src/parser.rs 中entry_id_for_store与对应测试includes_named_store_in_dedupe_identity可验证。加载链路并行读取、去重与日期过滤加载的核心实现在loader.rs的load_entries_from_paths整体流程为解析时区parse_tz支持--timezone对每个路径递归收集.jsonl文件collect_files_with_extension过滤掉subagent-artifacts/路径段下的派生调试转录文件通过read_files_parallel并行读取受--single-thread开关控制读取失败的文件仅记录 debug 日志不中断整体加载计算回放抑制计划PiReplayPlan按计划跳过子会话中与父会话回放匹配的前缀按 entry id 首见去重first-wins最后按时间戳排序输出。回放抑制Replay Suppression原理pi-agent 的分叉会话forked session文件可能会把父会话的用量历史完整回放进自己的文件里如果直接全部计数会造成严重重复。loader.rs中的PiReplayPlan专门处理这一问题其核心逻辑值得展开父候选路径对 pi 的树形格式会话父候选按root 到 leaf 的活跃分支确定即从最终物理条目当前叶子沿parentId链回溯而不是简单地按 JSONL 物理顺序取上一行被废弃的兄弟分支、断开的根分支仍然留在父会话中计数候选范围只取父会话中时间戳不晚于子会话 fork 时间戳fork_timestamp的原始记录抑制粒度只删除与候选前缀完全匹配的前缀匹配内容包括时间戳、模型、全部 token 字段、有效的 total-token 回退值以及 Display/Auto 模式下的有效计费成本第一个不匹配的条目及其后的所有记录都保留在子会话中宽松失败父文件缺失、损坏、自引用或成环的血统关系保持原样不投机地丢弃任何用量对应测试fails_open_for_missing_malformed_and_unrelated_same_token_sessions与fails_open_for_self_referential_and_cyclic_sessions。此外replay_billed_cost_bits将成本编码为位模式参与签名比较Calculate 模式下不计入签名Display/Auto 模式下0.0与-0.0视为相等缺失与 0 成本也视为相等避免浮点符号差异引发误判。跳过 subagent 派生转录pi-subagents 会在会话树内写入subagent-artifacts/目录存放派生调试转录文件。这些文件是主会话文件中已记录调用的纯拷贝无会话头、无 entry id既无法被回放抑制覆盖也无法靠 entry id 去重因此加载器直接跳过任何路径段包含subagent-artifacts/的.jsonl文件。而run-*/全新上下文fresh-context子会话是这些子代理的主要记录必须继续计入用量——skips_subagent_artifact_transcripts_but_keeps_fresh_context_child_sessions测试明确验证了这一区分。解析与计价Pi 记录结构、Token 字段与成本模式parser.rs定义了 Pi 会话记录的 serde 结构。ccusage 只声明自己消费的字段其余字段由 serde 自动忽略见PiLine、PiMessage、PiUsage、PiCost。一条典型的 pi 用量记录形如{type:message,timestamp:2026-01-02T00:00:00.000Z,message:{role:assistant,model:gpt-5,usage:{input:1000,output:2000,cacheRead:300,cacheWrite:50,totalTokens:3350,cost:{total:0.05}}}}解析与计价的关键规则行级预过滤只有同时包含usage与message两个子串的行才进入 JSON 解析LinePrefilter大幅降低非用量行的解析开销有效记录判定type必须为message、message.role必须为assistant且携带usage对象同时时间戳可解析、总 token 数非零total_usage_tokens extra_total_tokens 0Token 字段映射input→ 输入 token、output→ 输出 token、cacheRead→ 缓存读取、cacheWrite→ 缓存写入对应核心的 cache-creation 语义、totalTokens→ 总量。当各分项缺失时使用totalTokens作为回退apply_total_token_fallback测试falls_back_to_total_tokens_when_pi_parts_are_missing验证了仅含totalTokens:333的记录被解析为 output 333 的行为宽松容错token 字段用lenient_u64解析cost字段用lenient_object解析——即使cost不是对象如cost:0记录也不会整行失败只是把显示成本视为缺失keeps_record_when_cost_is_not_an_object成本来源可选的cost.total作为显示成本display cost。三种成本模式的取舍在calculate_store_cost/source_cost_for_mode中实现Display直接使用源文件中的显示成本Auto显示成本为有限且非负时使用之否则按 token 与定价重新计算calculate_store_cost_from_tokensCalculate忽略显示成本始终按定价从 token 计算。缺失定价提示在需要定价却找不到模型的场景下设置missing_pricing_model默认存储记[pi] model形式命名存储记原始模型名仅在 Display 模式或 Auto 模式已有显示成本时不告警。项目归属推断会话文件的目录结构决定了条目的 project 归属extract_project扫描路径中sessions段之后的第一个目录段作为项目名命名存储则优先基于存储根目录取相对路径首段失败时回退到默认推断。会话 ID 取自文件名形如agent_session-a.jsonl的文件取下划线后段session-aextract_session_id。报表输出四种粒度的汇总与 JSON 形状report.rs负责把加载后的条目汇总为报表行再组装成 JSONsummarize_entries按AgentReportKind分派Daily按entry.date聚合时区已在前置阶段应用Monthly先按日聚合再按BucketKind::Monthly、WeekDay::Sunday周期桶化Weekly同样基于日汇总做周桶化Session按session_id分组用SessionAccumulator累积每条会话的元数据与用量。report_from_rows输出统一形状{ daily|weekly|monthly|sessions: [...], totals: {...} }其中totals汇总inputTokens、outputTokens、cacheCreationTokens、cacheReadTokens、totalTokens与totalCost空报表也保证输出零值 totals见empty_report_has_zero_totals_object测试。完整的命令入口在lib.rs的run加载定价覆盖 → 加载条目 → 按共享参数过滤日期 → 汇总 → 排序--order与时间周期键→ 按需输出 JSON支持--jq后处理与--no-cost或渲染pi-agent Token Usage Report表格。使用方式与依赖边界pi-agent 源的标准命令见 rust/adapters/pi/src/README.mdccusage pi daily ccusage pi monthly ccusage pi session ccusage pi daily --json ccusage pi daily --pi-path /path/to/sessions其中--pi-path对应配置中的pi.defaults.piPath见 rust/crates/ccusage-config/src/config_schema.rs支持逗号分隔的多个路径。若某模型未收录在默认定价表中可像 ccusage.example.json 那样按带前缀的模型名配置覆盖例如{ pricingOverrides: { [pi] gpt-5.4: { input_cost_per_token: 0.000002, output_cost_per_token: 0.000008 } } }依赖方面rust/adapters/pi/Cargo.toml 声明了四个运行时依赖ccusage-adapter-commonJSONL 解析、文件遍历、并行读取、ccusage-core类型、定价、汇总、输出、jiff时区/时间处理与serde/serde_json。构建上该适配器位于adaptersCrane 产物层与其余适配器在同一次 Cargo 调用中并发编译。测试保障适配器的行为由 rust/adapters/pi/src/loader.rs 与 rust/adapters/pi/src/parser.rs 内置的测试覆盖重点场景包括父子回放前缀抑制与活跃分支识别含嵌套 fork、废弃兄弟分支、多断开根分支回放签名的字段级比较任一 token 字段、模型、total 回退变化都不再抑制显示成本差异不影响回放判定Display/Auto 模式保留子会话成本签名 ±0、缺失/0 成本视为等价自引用与循环血统宽松失败、命名存储跨目录匹配父文件、subagent-artifacts 跳过等边界。这些测试同时以single_thread开关验证并行与单线程路径结果一致保证了去重顺序的确定性。【免费下载链接】ccusagenpx ccusage项目地址: https://gitcode.com/gh_mirrors/cc/ccusage创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表