Yoda
参考Agent 设计指南可观测

Telemetry / OTel

遥测导出与团队分析——CC 是「文档完备的 OTel 导出 + 1495 个内部 tengu_* 事件」双平面;Codex 把 OTel 做成内置 crate 但配置面零文档,内部 analytics 默认把指标发给 Statsig

Telemetry / OTel

证据标签:[一手源码] [一手文档] [推断]。CC 源码为重建快照(src_2026-03-31),仅作架构描述与短引用;Codex 源码 codex-rs @ b89ce9a (2026-06-06)。

结论

两个 runtime 都有两套互不相干的遥测平面:一套「给用户看的」(导出到你自己的 OTel 后端),一套「给厂商看的」(内部 analytics 回传厂商)。差别在成熟度与透明度完全倒置——CC 的用户面 OTel 是行业标杆(8 个 metrics、20+ 个 events、beta traces,文档逐字段穷举,含隐私分级开关 OTEL_LOG_*),内部面则是约 1495 个 tengu_* 事件名(仅源码可见)经 Datadog + 第一方 BigQuery 双 sink 回传;Codex 的用户面 OTel 内置成 codex-otel crate(config.toml [otel] 配 OTLP HTTP/gRPC),但在官方 config 文档里零覆盖,且 metrics exporter 的默认值是 Statsig——即默认状态下指标流向 OpenAI 的 Statsig,内部面则是 codex-analytics crate 的 track_* 事件直接 POST 回 Codex backend(仅 ChatGPT 登录时发送)。Harness 想做团队级观测:接 CC 用 OTel 环境变量即可零代码拿到全量指标;接 Codex 要么写 [otel] TOML(无文档,照源码抄),要么放弃推送、回读磁盘 rollout(见 transcript 章)。

研究问题

  • 两边各自导出哪些 metrics / events / traces?怎么开、怎么配?
  • 内部 analytics(厂商回传)各自记什么、发到哪、怎么关?
  • 隐私边界:prompt / 命令 / 文件内容默认露不露,靠什么开关分级?
  • hook 能否作为第三条观测通道?

各 Agent 设计与实现

Claude Code

平面一:用户侧 OTel 导出(opt-in,文档完备)[一手文档] monitoring-usage.md:15-36——CLAUDE_CODE_ENABLE_TELEMETRY=1 总开关 + 标准 OTEL_METRICS_EXPORTER / OTEL_LOGS_EXPORTER(otlp/prometheus/console)+ OTEL_EXPORTER_OTLP_ENDPOINT 等标准 OTel 变量。源码侧实现在 utils/telemetry/instrumentation.ts [一手源码]:325CLAUDE_CODE_ENABLE_TELEMETRY:563 Meter 命名 com.anthropic.claude_code,OTLP exporter 动态 import(:158-170)。

导出内容三层 [一手文档] monitoring-usage.md:

  • Metrics(8 个)(:417-426):claude_code.session.count / lines_of_code.count / pull_request.count / commit.count / cost.usage(USD)/ token.usage / code_edit_tool.decision / active_time.total。counter 创建点 [一手源码] bootstrap/state.ts:955-983,与文档逐名对得上。
  • Events(20+ 个)(:537-997):user_prompttool_resultapi_request(含 cost_usd/4 类 token 数/request_id)、api_errorapi_refusaltool_decision(permission 决策来源六分类)、permission_mode_changedauthmcp_server_connection、hook 生命周期四件套、compaction 等。prompt.id 把一次用户输入触发的所有事件串起来(:525-535)。
  • Traces(beta)(:119-157):再加 CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1,span 树 claude_code.interaction → llm_request / tool → tool.blocked_on_user + tool.execution;Bash 子进程自动注入 TRACEPARENT 实现端到端分布式追踪(:135)。

隐私分级是设计亮点 [一手文档] monitoring-usage.md:87-90、:1161-1173:默认 prompt 文本、工具参数、工具内容全部脱敏,按需逐级打开 OTEL_LOG_USER_PROMPTSOTEL_LOG_TOOL_DETAILSOTEL_LOG_TOOL_CONTENTOTEL_LOG_RAW_API_BODIES(最后者直接落整段 Messages API 请求/响应,文档明示「enabling this implies consent to everything」)。这些数据只去你配置的 OTel endpoint,不去 Anthropic。企业可经 managed settings 强制下发且用户不可覆盖(:44-65)。注意:CC 不向子进程传播 OTEL_* 变量(:67)。

平面二:内部 tengu_* analytics(opt-out,仅源码可见)[一手源码] 全源码树 grep 出 1495 个唯一 tengu_* 事件名。架构:services/analytics/index.ts 是零依赖入口(事件先排队、sink attach 后排空,:95-123);sink.ts:48-72 双路 fanout——Datadog(受 Statsig gate tengu_log_datadog_events 控制,:20)+ 第一方 BigQuery exporter(firstPartyEventLoggingExporter.ts)。两个防泄漏机制值得抄:① 类型标记 AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHSindex.ts:19)——metadata 字段类型禁止裸 string,强迫开发者显式声明「这不是代码/路径」;② _PROTO_* 前缀字段只进有特权访问控制的 1P PII 列,发 Datadog 前一律 strip(index.ts:21-58)。关闭方式 [一手文档] data-usage.md:98:DISABLE_TELEMETRY=1;Bedrock/Vertex/Foundry 下默认整体关闭(:108-120);CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC 一键关全部非必要流量。

平面三:hook 即观测探针[一手文档] hooks.md——PreToolUse/PostToolUse/Stop 等 hook 的 stdin JSON 携带 tool_use_id,与 OTel tool_result/tool_decision 事件的 tool_use_id 同值(monitoring-usage.md:567),官方文档明示二者可 join。即不开 OTel 也能用 hook 把工具级遥测推到任意自建管道。

Codex CLI

平面一:内置 codex-otel crate(opt-in,零文档)[一手源码] otel/README.md + otel/src/config.rs:51-61OtelSettings:exporter / trace_exporter / metrics_exporter 三信号独立配置,支持 OtlpHttp/OtlpGrpc/Statsig/None,含 mTLS OtelTlsConfig)。用户配置面是 config.toml [otel][一手源码] config/src/types.rs:513-535OtelConfigTomllog_user_prompt / environment / exporter / trace_exporter / metrics_exporter / span_attributes / tracestate)。默认值是关键 [一手源码] core/src/config/otel.rs:13-23

let exporter = config.exporter.unwrap_or(OtelExporterKind::None);        // 日志:默认不导出
let trace_exporter = config.trace_exporter.unwrap_or(OtelExporterKind::None);
let metrics_exporter = config.metrics_exporter.unwrap_or(OtelExporterKind::Statsig); // 指标:默认发 Statsig

即不写任何配置时,logs/traces 不出门,metrics 默认流向 Statsig(OpenAI 侧);总闸是 analytics.enabled=false——core/src/otel_init.rs:70-77 在 analytics 关闭时把 metrics exporter 强制改成 Nonegrep -i otel codex-rs/config.mdcodex/docs/*.md 均零命中 [一手文档缺失]——整个 [otel] 配置面只能读源码。

导出的业务事件由 SessionTelemetry 发射 [一手源码] otel/src/events/session_telemetry.rs,共 15 个 codex.* 事件名:codex.api_requestcodex.sse_eventcodex.websocket_connect/request/eventcodex.user_promptcodex.tool_decisioncodex.tool_resultcodex.conversation_startscodex.turn_ttftcodex.startup_phasecodex.sandbox_outcomecodex.auth_recoverycodex.plugin_install_*。prompt 文本默认不记(log_user_prompt 默认 false,config/otel.rs:13)。导出过滤器只放行 codex_otel target 的事件(otel_init.rs:97-101 codex_export_filter),不会把全量 tracing 日志倒给你。

平面二:codex-analytics crate(厂商回传,opt-out)[一手源码] analytics/src/client.rs:约 25 个 track_* 方法——track_turn_token_usagetrack_turn_profile(采样耗时分解七字段,facts.rs:108-116)、track_compactiontrack_guardian_reviewtrack_plugin_*track_skill_invocationstrack_hook_run 等。发送目标是 Codex 自家 backend:

// analytics/src/client.rs:416-423
if !auth.uses_codex_backend() { return; }
let url = format!("{base_url}/codex/analytics-events/events");

仅 ChatGPT 登录态发送(API-key/第三方 provider 不发);机器级开关 analytics.enabled [一手源码] core/src/config/mod.rs:1023-1025(注释自述 "disables analytics across Codex product surfaces in this machine"),同样无公开文档。

差异矩阵

维度Claude CodeCodex CLI
用户侧导出协议OTel metrics + logs/events + traces(beta),标准 OTEL_* 环境变量OTel 三信号,config.toml [otel](TOML 配 endpoint/headers/mTLS)
配置面文档monitoring-usage.md 逐字段穷举(1178 行)零文档,只能读 config/src/types.rs:513
默认行为全关(CLAUDE_CODE_ENABLE_TELEMETRY 必须显式置 1)logs/traces 关,metrics 默认 → Statsigconfig/otel.rs:21
导出 metrics8 个 claude_code.*(含 USD cost)MetricsClient counter/histogram + 15 个 codex.* 事件(无 USD)
隐私分级4 级开关 OTEL_LOG_{USER_PROMPTS,TOOL_DETAILS,TOOL_CONTENT,RAW_API_BODIES}1 个 log_user_prompt(默认 false)
内部 analyticstengu_* ×1495,Datadog + 1P BigQuery 双 sinktrack_* ×~25,POST /codex/analytics-events/events
内部 analytics 开关DISABLE_TELEMETRY=1(有文档)analytics.enabled=false(无文档);仅 ChatGPT 登录态才发
防泄漏机制类型标记 + _PROTO_* PII 分列 + 事件采样配置事件白名单(敏感 ServerNotification 显式 return,client.rs:395-399
hook 作观测通道tool_use_id 与 OTel 事件可 join(有文档)hook 有 track_hook_run 回传,但无对外 join 契约
管理员强制下发managed settings(MDM 分发,用户不可覆盖)无对应机制 [推断](cloud-config 层未见 otel 强制项)

最小复现

# CC:console exporter 1 秒级验证(monitoring-usage.md:347-350)
export CLAUDE_CODE_ENABLE_TELEMETRY=1 OTEL_METRICS_EXPORTER=console OTEL_METRIC_EXPORT_INTERVAL=1000
claude   # 终端可见 claude_code.* 指标 dump

# Codex:~/.codex/config.toml(字段名照 config/src/types.rs:513,无文档)
# [otel]
# exporter = { otlp-http = { endpoint = "http://localhost:4318/v1/logs", protocol = "binary" } }
# trace_exporter = { otlp-grpc = { endpoint = "http://localhost:4317" } }
# 关厂商回传:
# [analytics]
# enabled = false

Harness 接入建议(Yoda 实践)

  • Yoda 当前不消费任何一边的 OTel 流——会话级观测全部走磁盘 transcript/rollout 解析(yoda/src/main/core/conversations/,见 transcript 章)、成本走 transcript usage 重算(见 cost 章)。理由:OTel 是机器级配置(环境变量/全局 TOML),harness 注入会污染用户自己的遥测配置;且 Codex 侧无文档、CC 侧 traces 还在 beta。
  • 如果要做团队/机群级看板(多人多机),CC 直接经 managed settings 下发 OTEL_* 是正解,零代码改造;Codex 需要给每台机器写 [otel] TOML,并接受「字段随版本漂移且无 changelog」。
  • 单机 harness 想要 turn 级耗时/token 推送,CC 更轻的路是 statusline + hookstool_use_id join),不必起 OTLP collector。
  • 隐私默认值要尊重:harness 自动开 OTEL_LOG_TOOL_DETAILS 等于替用户决定上报 Bash 全命令——必须显式征得同意(CC 文档同款立场)。
  • 注意 Codex 的 metrics_exporter 默认 Statsig:对隐私敏感客户交付时,harness 应主动写 analytics.enabled=falsemetrics_exporter = "none"

反例 / 边界

  • CC 的两套平面开关互不影响DISABLE_TELEMETRY 只关 Anthropic 回传,不影响你自配的 OTel 导出;反之 CLAUDE_CODE_ENABLE_TELEMETRY 不开也不会阻止 tengu_* 回传 [一手文档] monitoring-usage.md:1163。
  • CC OTel 的 user.email 在 OAuth 登录时总是带上(monitoring-usage.md:1165)——合规场景要在 collector 层脱敏,客户端没有开关。
  • Codex 的 codex.* OTel 事件与 track_* analytics 事件是两份不同 schema(前者 tracing event,后者 JSON POST),字段不要混用。
  • 第三方 provider(CC 走 Bedrock/Vertex、Codex 用 API key)下,内部回传各自默认关闭/不发送——机群统计若混用认证模式会出现系统性漏计。

失效条件

  • CC traces 仍标 beta(CLAUDE_CODE_ENHANCED_TELEMETRY_BETA),span 名/属性可能变更——发版后回归 monitoring-usage.md。
  • CC 源码快照为 2026-03-31 重建版,tengu_* 计数(1495)与 sink 拓扑(Datadog gate 名)随 live 版本漂移。
  • Codex [otel] 默认值(metrics→Statsig)位于 core/src/config/otel.rs:21,任何一次 config 重构都可能翻转该默认——升级后 diff 此文件。
  • Codex analytics 仅 ChatGPT 登录态发送的判断依赖 auth.uses_codex_backend()client.rs:417),认证体系改版需复核。

参考资料

  • [一手文档] claude-code-docs/docs/monitoring-usage.md(OTel 全量字段)、data-usage.md(opt-out 矩阵)、hooks.md
  • [一手源码] CC:utils/telemetry/instrumentation.tsservices/analytics/{index,sink}.tsbootstrap/state.ts:955-983
  • [一手源码] Codex:otel/README.mdotel/src/config.rsotel/src/events/session_telemetry.rscore/src/config/otel.rscore/src/otel_init.rsconfig/src/types.rs:513-560analytics/src/{client,facts,lib}.rs
  • Anthropic 官方 ROI 监控指南:github.com/anthropics/claude-code-monitoring-guide

On this page