Yoda
参考Agent 设计指南模型与账户

用量信息同步与管理

配额看服务端(CC /api/oauth/usage、Codex /api/codex/usage),token 消耗看本地 transcript——两层数据源各取所长

用量信息同步与管理

结论

用量数据有两层,harness 必须分开对待:配额/限流层只有服务端知道——CC 通过 OAuth 专属接口 /api/oauth/usage 返回 5 小时与 7 天窗口的利用率百分比,Codex 把 RateLimitSnapshot 直接嵌在每个 token_count 流事件里、另有 /api/codex/usage 可主动拉取;token 消耗层则完整落在本地 transcript——CC 每条 assistant 消息带增量 message.usage,Codex rollout 持久化累计值 total_token_usage。harness 聚合消耗只需解析本地文件(注意 CC 要按 message.id 去重、Codex 要做相邻差分),而配额数据绝不应自行估算——要么调真实接口、要么不显示,这是 Yoda 的两条实现路线。

研究问题

  • 各 CLI 的用量数据落在哪里?transcript token 字段还是账户接口?
  • 限流窗口(5h / 7d / credits)如何暴露?字段语义是什么?
  • harness 如何不双重计数、不伪造数据地聚合多账户用量?

各 Agent 设计与实现

Claude Code

CC 源码为社区重建版(src_2026-03-31),以下仅作架构描述与短引用。

配额接口fetchUtilization() GET {BASE_API_URL}/api/oauth/usage,仅当 isClaudeAISubscriber() && hasProfileScope() 时调用(即订阅 OAuth 专属,API Key 用户拿不到)[一手源码 services/api/usage.ts:33-63]。返回结构:

type Utilization = {
  five_hour?: RateLimit | null        // { utilization: 0-100, resets_at: ISO8601 }
  seven_day?: RateLimit | null
  seven_day_opus?: RateLimit | null   // Opus 单独的 7 天窗口
  seven_day_sonnet?: RateLimit | null
  seven_day_oauth_apps?: RateLimit | null
  extra_usage?: ExtraUsage | null     // { is_enabled, monthly_limit, used_credits, utilization }
}

[一手源码 services/api/usage.ts:12-31]。/usage 命令即基于此,且 availability: ['claude-ai']——API Key 模式下命令本身不可见 [一手源码 commands/usage/index.ts:3-9]。

限流头:429/进行中响应携带 anthropic-ratelimit-unified-* 头族:-representative-claim(哪个窗口触顶)、-overage-status-reset-overage-reset-overage-disabled-reason [一手源码 services/api/errors.ts:471-512services/api/withRetry.ts:276, 815]。

订阅档位fetchProfileInfo() 把 OAuth profile 的 organization.organization_type 映射为 max/pro/enterprise/team,并返回 rate_limit_tierhasExtraUsageEnabledbillingType [一手源码 services/oauth/client.ts:355-401]。

本地消耗数据:transcript(~/.claude/projects/<dir>/<sessionId>.jsonl)的 assistant 行带 message.usageinput_tokens / output_tokens / cache_read_input_tokens / cache_creation_input_tokens,外加 service_tierspeed 等(本机实测)。同一条消息按 content block 写多行、重复同一 message.id 与 usage——消费方必须按 message id 去重 [一手源码(Yoda 侧验证)yoda/.../claude-usage-reader.ts:13-24]。子代理(Task 工具)的消耗在 <projectDir>/<sessionId>/subagents/*.jsonl 单独成文件,属真实成本。

企业级导出:除本地文件外,CC 还能以 OTel 形式把用量指标推到组织的可观测栈——CLAUDE_CODE_ENABLE_TELEMETRY=1 + OTEL_METRICS_EXPORTER / OTEL_LOGS_EXPORTER,支持 mTLS 与多团队自定义属性 [一手文档 claude-code-docs/docs/monitoring-usage.md:11-44]。对企业 harness 这是比抓 transcript 更正规的聚合通道,但对个人桌面产品(Yoda 场景)依赖用户开启,不可作为默认数据源。

Codex CLI

事件即数据:协议层 TokenCountEvent { info: Option<TokenUsageInfo>, rate_limits: Option<RateLimitSnapshot> } [一手源码 codex-rs/protocol/src/protocol.rs:1980-1984],且 EventMsg::TokenCount 在 rollout 持久化白名单中 [一手源码 rollout/src/policy.rs:76-94]——用量随会话写进 ~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonlinfo.total_token_usage会话累计值(input/cached_input/output/reasoning_output/total,本机 rollout 实测字段一致)。

限流快照RateLimitSnapshot { limit_id, limit_name, primary, secondary, credits, plan_type, rate_limit_reached_type },窗口为 RateLimitWindow { used_percent: 0-100, window_minutes, resets_at(unix秒) },credits 为 { has_credits, unlimited, balance } [一手源码 protocol.rs:1986-2046]。rate_limit_reached_type 还区分 workspace owner/member 的 credits 耗尽与 usage limit [同文件 :2001-2007]。

主动拉取:backend-client 提供 get_rate_limits(),按 path style 走 GET {base}/api/codex/usage(Codex API)或 {base}/wham/usage(ChatGPT API),多账户限流取 limit_id == "codex" 优先 [一手源码 backend-client/src/client.rs:287-305]。TUI 通过 app-server 同一通道展示(/status)[一手源码 tui/src/app_server_session.rs:1736]。

与 CC 的结构性差异:Codex 把限流快照作为事件流的一等公民下发(每次 token_count 都附带),客户端无需轮询即可实时显示余量;CC 的限流信息散落在响应头和独立 REST 接口两处,/usage 需要主动请求。对 harness 而言,Codex 的 rollout 文件同时是消耗数据和最近一次限流状态的载体——读文件尾部即可同时拿到两者。

差异矩阵

维度Claude CodeCodex CLI
配额接口GET /api/oauth/usage(OAuth 订阅专属)GET /api/codex/usage/wham/usage
配额单位utilization 百分比 + resets_at ISO 时间used_percent + window_minutes + unix resets_at
窗口粒度5h、7d、7d-opus、7d-sonnet、extra_usageprimary/secondary 双窗口 + credits + 个人限额
限流推送响应头 anthropic-ratelimit-unified-*每个 token_count 事件内嵌 rate_limits
本地 token 数据transcript assistant 行 message.usage每消息增量,按 content block 重复行)rollout token_count 事件 total_token_usage会话累计
聚合时的坑message.id 去重;subagents 子目录别漏相邻事件差分;计数回落 = compaction 新基线
套餐探测profile 接口 organization_type → max/pro/team/enterpriserate_limits.plan_type / id_token claim
入口命令/usage(仅 claude-ai 模式)、/status/status

最小复现

# Codex rollout 中的 token_count 事件结构(本机实测,2026-06-03 会话)
f=$(grep -rl '"token_count"' ~/.codex/sessions/2026/06 | head -1)
grep -m1 '"token_count"' "$f" | jq '{usage: (.payload.info.total_token_usage|keys),
                                      rate: (.payload.rate_limits|keys)}'
# => usage: ["cached_input_tokens","input_tokens","output_tokens",
#            "reasoning_output_tokens","total_tokens"]
#    rate:  ["credits","limit_id","limit_name","plan_type","primary",
#            "rate_limit_reached_type","secondary"]

# CC transcript 中的 usage 字段(本机实测)
f=$(find ~/.claude/projects -name "*.jsonl" | head -1)
grep -m1 '"usage"' "$f" | jq '{type, usage_keys: (.message.usage|keys)}'
# => type:"assistant", usage_keys 含 input_tokens / output_tokens /
#    cache_read_input_tokens / cache_creation_input_tokens / service_tier ...

Harness 接入建议(Yoda 实践)

Yoda 用三个互补组件做 usage 同步,全部只读 [一手源码 yoda/src/main/core/stats/core/settings/]:

  1. Transcript 聚合getUsageOverview.ts):把每个会话的 transcript 解析为统一 TokenBuckets(input/output/cacheRead/cacheCreation/reasoning),按日(heatmap)、按模型、按 runtime、按 auth 来源(official-subscription / official-api / yoda-maas)四维聚合;mtime 缓存让首次全量解析后续免费(:67-74)。无 auth 记录的旧会话回填为该 runtime 当前配置的认证模式——标注是估计值而非编造(:131-138)。

  2. 读法差异内置在 reader 里:CC reader 按 message.id 去重、<synthetic> 模型置 null(ccusage 口径对齐)、subagents транscript 并入 [一手源码 claude-usage-reader.ts:13-24];Codex reader 对累计值做相邻差分,重复计数零增量、计数回落视为 compaction 新基线而不是负数 [一手源码 codex-usage-reader.test.ts:17-84]。cached_input_tokensinput_tokens 的子集,归一化时要扣除。

  3. 成本与档位:30 天窗口的成本用本地价格表估算(local-usage-service.ts,未知模型列入 unpricedModels 而非算 0);订阅档位读凭证文件(见 auth-sync 章),不调用 CC 的 /api/oauth/usage——那是 OAuth Bearer 专属接口,harness 冒用 CLI 的 token 调它会增加风控面。Codex 的 rollout 尾部 1MB 即含最新累计值(CODEX_TAIL_BYTES),无需全文件解析。

  4. 辅助目录纳入:用户在 Yoda 之外(research 目录、项目旧位置)跑的 CC 会话也可计入——project settings 的 statsAuxiliaryPaths 配置额外扫描路径,已追踪的 conversation id 自动跳过避免双计 [一手源码 getUsageOverview.ts:208-230]。

原则:消耗数据本地算(可复现、离线可用),配额数据要么用官方接口要么不展示,两者在 UI 上分开标注。多账户聚合的归因键 = runtimeId × authProvider,而不是只按 runtime——同一个 CC 既可能跑订阅也可能跑 BYOK,成本口径完全不同。

失效条件

  • CC Utilization 字段集变化(如新增 per-model 窗口)或 /api/oauth/usage 路径迁移
  • CC /usage 命令的 availability 限制放开到 API Key 模式(当前仅 claude-ai
  • Codex get_rate_limitslimit_id == "codex" 优先规则随多产品限流合并而改变
  • Codex total_token_usage 从累计改为增量语义,或 token_count 退出 rollout 持久化白名单(rollout/src/policy.rs
  • CC transcript 行格式变化(usage 移出 message.usage、message.id 去重假设失效)
  • anthropic-ratelimit-unified-* 头族更名
  • Agent SDK 订阅额度独立计费上线(CC 文档预告 2026-06-15 起 claude -p 走独立 credit),/usage 口径可能拆分

参考资料

  • CC 重建源码:src_2026-03-31/services/api/usage.tsservices/api/errors.tsservices/oauth/client.tscommands/usage/
  • CC 文档:claude-code-docs/docs/costs.mdmonitoring-usage.md(OTel 指标导出)、authentication.md(SDK credit 预告)
  • Codex 源码:codex-rs/protocol/src/protocol.rs(TokenCountEvent/RateLimitSnapshot)、rollout/src/policy.rsbackend-client/src/client.rs
  • Yoda:src/main/core/stats/getUsageOverview.tsstats/transcript-readers/{claude,codex}-usage-reader.tssettings/local-usage-service.ts
  • ccusage(社区口径参照,Yoda reader 注释声明 parity)

On this page