- Copy doc from user manual to docs/ - Quality table: 3383 → 3699 (+692) - Local dev section: 3383 tests → 3699 tests - Version table: add v2.2.1, v2.2.2 entries
19 KiB
Claude Code 的光明和阴影面
副标题: Anthropic 遥测系统的逆向工程、纵深防御与自用改造
作者: James Feng(基于 CC_Pure 代码库逆向分析,2026年6月)
标签:
逆向工程遥测隐私GrowthBookOpenTelemetry数据分析
目录
1. 前言
Claude Code(内部代号 "tengu")是 Anthropic 的终端 AI 编程助手。它不仅仅是一个命令行工具——它是一套完整的数据采集和分析基础设施。每当你输入一个命令、调用一个工具、触发一次 API 请求,几十个遥测事件在后台被捕获、采样、路由和上报。
本文基于对 CC_Pure(Claude Code 反编译还原项目)的深度代码审计,完整拆解这套遥测系统:
- 它收集了什么?
- 数据流向了哪里?
- 我们如何发现并防御?
- 更重要的是,我们如何把它变成自己的利器?
核心结论: Anthropic 的遥测基础设施本身就是一套值得学习的工业级数据工程范例。我们不需要摧毁它——我们需要接管它。
2. 光明面
2.1 工程设计的精妙之处
Claude Code 的遥测系统不是简单的埋点+上报。它是一套分层架构:
logEvent()
├── 本地 JSONL 写入(我们加的防御层)
├── 事件队列(sink 未初始化时的缓冲)
├── GrowthBook 动态采样(云端控制的抽样引擎)
├── Datadog 监控(运维告警)
└── 1P 事件上报(Anthropic 内部 BigQuery 分析)
亮点 1:零依赖入口设计
logEvent() 函数(src/services/analytics/index.ts)本身没有任何模块级依赖。所有事件先进入队列,等 attachAnalyticsSink() 在应用初始化时被调用后才真正路由到后端。这个设计避免了循环依赖,也让测试变得极其容易。
// 精妙:零依赖的入口
export function logEvent(eventName, metadata) {
// ① 本地写入(我们的注入点)
writeLocalEvent(eventName, metadata)
// ② 如果 sink 未就绪,入队;否则直接发送
if (sink === null) {
eventQueue.push({ eventName, metadata, async: false })
return
}
sink.logEvent(eventName, metadata)
}
亮点 2:GrowthBook 动态实验平台
整个项目的 feature flag 系统建立在 GrowthBook 之上。这不是简单的 if (feature_enabled) —— 它是一个完整的 A/B 实验平台:
- 远程评估(remote eval): 服务器预先计算每个 feature 的值,客户端直接使用,无需本地规则引擎
- 磁盘缓存 + 会话内刷新: 首次获取后写
~/.claude.json,后续进程启动用缓存,会话期间通过onGrowthBookRefresh推送更新 - 实验曝光追踪: 每个被访问的 feature 自动记录实验分配事件到 1P 事件管道
- 动态配置(JSON config): 不仅是开关,还支持复杂的 JSON 配置(如事件采样率、批处理参数、sink kill switch)
src/services/analytics/growthbook.ts 文件高达 1256 行,处理了远程评估响应格式的 workaround、env-var override、config override、刷新信号机制等细节。
亮点 3:ToolSearchTool —— RL 数据工厂的核心
ToolSearchTool 不仅是一个工具搜索功能,它是一台强化学习数据收集机器:
// 搜索评分权重(精确调优的参数)
if (parsed.parts.includes(term)) {
score += parsed.isMcp ? 12 : 10 // MCP 工具名精确匹配权重更高
} else if (parsed.parts.some(part => part.includes(term))) {
score += parsed.isMcp ? 6 : 5 // 部分匹配
}
// searchHint 匹配
score += 4
// 描述匹配
score += 2
每一次搜索都上报 tengu_tool_search_outcome 事件,包含:
query:用户的搜索词queryType:select或keywordmatchCount:命中数量totalDeferredTools:延迟工具总数hasMatches:是否有命中
这套数据让 Anthropic 能够量化分析模型如何使用工具,从而持续优化工具描述、搜索算法和评分权重。
亮点 4:多层 PII 防护
代码中随处可见隐私保护设计:
AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS类型标记——强制开发者验证不上传代码/路径sanitizeToolNameForAnalytics()—— MCP 工具名(可能暴露用户配置)被替换为mcp_toolstripProtoFields()—— PII 标记字段只在 1P 特权列中,不进入通用 DatadoggetFileExtensionForAnalytics()—— 只上传文件扩展名,不上传完整路径getUserBucket()—— 用户 ID 哈希分桶,去匿名化计数但不暴露身份
2.2 事件体系全景
通过代码审计,我们统计出 190+ 处 logEvent() 调用,分布在 52 个文件中。主要事件类别:
| 类别 | 事件数 | 代表事件 |
|---|---|---|
| API 查询 | ~20 | tengu_query_error, tengu_api_success, tengu_token_budget_completed |
| 工具使用 | ~15 | tengu_tool_search_outcome, tengu_bash_tool_used |
| 权限决策 | ~10 | tengu_tool_use_granted, tengu_tool_use_rejected |
| 认证/OAuth | ~15 | tengu_oauth_success, tengu_oauth_token_refresh_failure |
| 会话生命周期 | ~10 | tengu_started, tengu_exit, tengu_init |
| 压缩/内存 | ~5 | tengu_auto_compact_succeeded, tengu_orphaned_messages_tombstoned |
| 实验/A/B | ~8 | tengu_willow_mode, GrowthBook assignment |
| Bridge/Remote | ~15 | tengu_bridge_message_received, tengu_ws_transport_reconnected |
| 迁移 | ~8 | tengu_opus_to_opus1m_migration |
| 遥测自监控 | ~3 | analytics_sink_attached |
2.3 数据工厂:四线并行
Anthropic 实质上运行着四条独立的数据管道:
- Datadog(运维):白名单制,只发送 ~40 种预定义事件到 Datadog,用于 API 错误率、OAuth 故障率等 SRE 告警
- 1P Event Logging(分析):基于 OpenTelemetry SDK Logs,所有事件通过
/api/event_logging/batch上报到 Anthropic 的 BigQuery,是核心分析管道 - GrowthBook(实验):Feature flag 赋值 + 实验曝光事件,独立上报,用于 A/B 测试结果评估
- Customer OTLP(客户遥测):可选的企业客户 OTLP 导出(metrics/logs/traces),由
CLAUDE_CODE_ENABLE_TELEMETRY控制
3. 阴影面
3.1 数据收集的广度
让我们诚实地审视:Claude Code 实际收集了什么?
每次启动:
✓ 操作系统版本、终端类型、包管理器列表
✓ Git 仓库远程 URL 的哈希("rh" 字段)
✓ 用户订阅级别(免费/Pro/Max/Team/Enterprise)
✓ 是否为 CI 环境、GitHub Action 类型
每次 API 查询:
✓ 使用的模型名称、beta 列表
✓ token 消耗量、上下文窗口大小
✓ 是否触发了 fallback 模型
✓ 查询前后的 attachment 对比
每次工具调用:
✓ 工具名称、是否成功
✓ 文件扩展名(不是路径,但足以推断工程类型)
✓ Bash 命令类型(diff/grep/sed 等)
✓ 权限决策(always allow / reject / ask)
每次会话:
✓ 启动次数、使用时长
✓ 压缩频率、孤儿消息数量
✓ KAIROS(后台 agent)活跃状态
3.2 技术上的透明度
Anthropic 并不是在偷偷做这件事。代码中的设计模式表明:
- 所有遥测都在
src/services/analytics/下集中管理,模块边界清晰 - 隐私分级明确(
AnalyticsMetadata_I_VERIFIED_...类型标记) - 提供了 opt-out 机制(
DISABLE_TELEMETRY/CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC)
但从"后门"到"数据工厂"的距离并不远。这一套基础设施如果被滥用(或遭遇供应链攻击),可以轻松变成:
- 代码片段收集器(绕过文件路径截断,直接上传内容)
- 用户行为画像(通过 token 消耗模式推断工作习惯)
- 工程结构嗅探(通过文件扩展名统计推断技术栈)
3.3 我们发现的"异常"
在 CC_Pure 的代码审计中,我们注意到几个不寻常的地方:
-
USER_TYPE === 'ant'条件分支: 代码中有 50+ 处检查用户是否为 Anthropic 内部员工。内部版本能看到额外的调试信息、工具(ConfigTool, TungstenTool, REPLTool)、错误日志。这不是安全问题,但说明"内部版本"和"外部版本"的差异比文档披露的更大。 -
ToolSearchTool 的 RL 评分权重:
12/10/6/5/4/3/2的精细评分体系不是手工调整的——它暗示着持续的 A/B 实验和 RL 优化在背后运行。 -
GrowthBook 动态配置的深度: 不仅是 feature flag,还包括事件采样率、批处理大小、sink kill switch、甚至
tengu_max_version_config这种远程杀死特定版本的开关。
4. 解剖:遥测系统的五层架构
第一层:事件生成(Event Generation)
事件在代码各处通过 logEvent('event_name', metadata) 生成。事件名称遵循 tengu_<领域>_<动作> 的命名规范。
// 典型的事件生成点
logEvent('tengu_tool_search_outcome', {
query, queryType, matchCount, totalDeferredTools, maxResults, hasMatches
})
metadata 的类型约束是 { [key: string]: boolean | number | undefined } —— 禁止传递字符串,避免意外上传代码。
第二层:事件增强(Event Enrichment)
在进入 sink 之前,每个事件被 getEventMetadata() 增强,注入:
- 会话上下文: sessionId, clientType, isInteractive
- 环境上下文: 操作系统、终端、包管理器、CI 检测
- 模型信息: 当前使用的模型、betas、provider
- 用户信息: userType, subscriptionType, userBucket
- 进程指标: RSS, heapUsed, cpuUsage(仅在 Datadog 路径)
src/services/analytics/metadata.ts 长达 966 行,是这个增强引擎的核心。
第三层:采样与过滤(Sampling & Filtering)
事件在发送前经过多层过滤:
1. isAnalyticsDisabled() ← 总开关
├── NODE_ENV === 'test'?
├── 3P provider (Bedrock/Vertex/Foundry)?
└── isTelemetryDisabled()?
├── DISABLE_TELEMETRY?
└── CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC?
2. shouldSampleEvent() ← GrowthBook 动态采样
└── tengu_event_sampling_config(JSON 配置,按事件名设置采样率)
3. isSinkKilled('datadog' | 'firstParty') ← 按 sink 单独杀死
└── tengu_frond_boric GrowthBook 配置
4. 对 Datadog 额外:白名单(DATADOG_ALLOWED_EVENTS)+ 非生产环境跳过
第四层:事件路由(Event Routing)
logEventImpl() 在 sink.ts 中将事件分发给两个后端:
logEventImpl(eventName, metadata)
├── shouldTrackDatadog()? → trackDatadogEvent()
│ └── POST https://http-intake.logs.datadoghq.com/api/v2/logs
│ 批次大小: 100, 刷新间隔: 15s
│
└── logEventTo1P() → FirstPartyEventLoggingExporter
└── POST https://api.anthropic.com/api/event_logging/batch
批次大小: 200 (可配置), 刷新间隔: 10s (可配置)
第五层:持久化与重试(Persistence & Retry)
1P 事件导出器(firstPartyEventLoggingExporter.ts,806 行)具有工业级可靠性:
- 磁盘持久化: 发送失败的事件写入
~/.claude/telemetry/1p_failed_events.{sessionId}.{batchId}.json - 二次退避重试:
baseDelay * attempts²,最长达 30s,最多 8 次 - 跨进程恢复: 启动时重试之前会话的失败文件
- 分级失败处理: 一个批次失败 → 短路剩余批次 → 全部入队重试
- 并发安全: 追加写(append)而非全量写,避免覆盖并发事件
5. 防御:我们做了什么
5.1 纵深防御策略
我们的防御策略不是"关掉遥测"——那样会丢失学习这套系统的机会。而是在遥测管道的最前端插入一个本地分支:
logEvent()
│
┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
[本地 JSONL] [Datadog] [Anthropic 1P]
永远执行 可被关闭 可被关闭
自己的数据 运维数据 BigQuery 分析
关键改动(仅 3 个文件,不改任何工具代码):
-
src/services/analytics/localSink.ts(54 行新文件)// 将事件追加写入 ~/.claude/local_analytics.jsonl export function writeLocalEvent(eventName, metadata) { const line = JSON.stringify({ ts: new Date().toISOString(), event: eventName, ...metadata, }) + '\n' fs.appendFileSync(LOCAL_ANALYTICS_FILE, line, 'utf-8') } -
src/services/analytics/index.ts(在logEvent()入口处插入 3 行)// 在所有上游 sink 之前执行 const { writeLocalEvent } = require('./localSink.js') writeLocalEvent(eventName, metadata) -
scripts/analyze_analytics.py(分析脚本)
5.2 为什么这个方案优于直接关掉遥测
| 方案 | 优点 | 缺点 |
|---|---|---|
DISABLE_TELEMETRY=1 |
简单,一键关闭 | 丢失所有数据,学不到东西 |
| 直接删除 analytics 代码 | 彻底 | 破坏代码结构,每次更新需重新修改 |
| 我们的方案:前端分叉 | 保留完整基础设施,数据归自己 | 需额外 200 行代码 + 分析工具 |
5.3 .gitignore 防护
# Local analytics data (never upload)
*.jsonl
.claude/
确保本地遥测数据绝不会被意外提交到仓库。
6. 为己所用
6.1 本地数据文件
~/.claude/local_analytics.jsonl —— 一行一个 JSON 事件:
{"ts":"2026-06-03T10:15:23.456Z","event":"tengu_started","sessionId":"abc123"}
{"ts":"2026-06-03T10:15:24.789Z","event":"tengu_bash_tool_used","toolName":"Bash"}
{"ts":"2026-06-03T10:15:25.012Z","event":"tengu_api_success","model":"claude-sonnet-4-20250514"}
6.2 分析脚本
# 查看事件统计报告
python3 scripts/analyze_analytics.py
# 实时追踪事件流
tail -f ~/.claude/local_analytics.jsonl
# 搜索特定事件
grep "tengu_query_error" ~/.claude/local_analytics.jsonl | python3 -m json.tool
# 按天统计使用次数
grep "tengu_started" ~/.claude/local_analytics.jsonl | wc -l
6.3 你能分析什么
| 分析维度 | 数据来源 | 回答的问题 |
|---|---|---|
| 工具使用频率 | tengu_tool_use_* |
我最常用什么工具?Bash 占比多少? |
| 模型 fallback 率 | tengu_model_fallback_triggered |
我的 API 稳定性如何? |
| 上下文压缩频率 | tengu_auto_compact_succeeded |
我的对话是否经常超出窗口? |
| API 错误类型 | tengu_query_error + http_status |
什么类型的错误最多? |
| 会话时长/频率 | tengu_started / tengu_exit |
我每天用多少次?每次多久? |
| 工具搜索行为 | tengu_tool_search_outcome |
模型是否能正确找到工具? |
6.4 进阶:扩展分析
因为本地 JSONL 包含所有事件的完整 metadata,你可以构建:
- 个人使用画像: 统计最常用的模型、工具组合、操作模式
- 成本分析: 结合 token 消耗事件,估算每日 API 费用
- 效率仪表板: Pandas/Streamlit 可视化,实时监控 CCB 使用
- 异常检测: 监控错误率突增、fallback 异常等
6.5 从 Anthropic 学习的最佳实践
这套遥测系统本身就是一个教科书级的案例:
- 零依赖入口 + 延迟绑定:
logEvent()无依赖,sink 通过attachAnalyticsSink()延迟注入 —— 适合任何需要插拔式后端的系统 - 多层过滤链: 总开关 → 采样 → sink kill switch —— 灵活且可远程控制
- 磁盘兜底 + 指数退避: 即使网络失败也不丢事件
- 隐私类型系统: TypeScript 的
never类型 + 标记模式强制代码审查 - GrowthBook 集成模式: 将 feature flag 变成数据采集工具
7. 附录:事件词典
以下是代码审计中发现的全部遥测事件(部分代表性事件):
API & Query
| 事件名 | 描述 |
|---|---|
tengu_query_error |
API 查询错误 |
tengu_api_success |
API 调用成功 |
tengu_model_fallback_triggered |
触发模型降级 |
tengu_max_tokens_escalate |
Token 上限触发 |
tengu_token_budget_completed |
Token 预算耗尽 |
tengu_query_before_attachments |
查询前 attachment 状态 |
tengu_query_after_attachments |
查询后 attachment 状态 |
tengu_streaming_tool_execution_used |
流式工具执行启用 |
tengu_streaming_tool_execution_not_used |
流式工具执行未启用 |
tengu_post_autocompact_turn |
自动压缩后的对话轮次 |
工具使用
| 事件名 | 描述 |
|---|---|
tengu_tool_search_outcome |
工具搜索结果(RL 数据) |
tengu_bash_tool_used |
Bash 工具被调用 |
tengu_tool_use_success |
工具调用成功 |
tengu_tool_use_error |
工具调用错误 |
tengu_tool_use_granted_in_prompt_permanent |
工具权限永久授予 |
tengu_tool_use_granted_in_prompt_temporary |
工具权限临时授予 |
tengu_tool_use_rejected_in_prompt |
工具权限拒绝 |
会话生命周期
| 事件名 | 描述 |
|---|---|
tengu_started |
启动 |
tengu_init |
初始化完成 |
tengu_exit |
退出 |
tengu_cancel |
用户取消 |
tengu_auto_compact_succeeded |
自动压缩成功 |
tengu_orphaned_messages_tombstoned |
孤儿消息清理 |
OAuth & 认证
| 事件名 | 描述 |
|---|---|
tengu_oauth_success |
OAuth 登录成功 |
tengu_oauth_error |
OAuth 错误 |
tengu_oauth_token_refresh_failure |
Token 刷新失败 |
tengu_oauth_token_refresh_success |
Token 刷新成功 |
tengu_oauth_flow_start |
OAuth 流程启动 |
遥测自监控
| 事件名 | 描述 |
|---|---|
analytics_sink_attached |
遥测 sink 已连接 |
tengu_bridge_message_received |
Bridge 消息接收 |
tengu_ws_transport_reconnected |
WebSocket 重连 |
最后的话: 这套遥测系统的存在本身不是问题——问题在于数据的主权归属。我们的改造方案证明:你可以在不破坏基础设施的前提下,将数据的所有权从云端拉回本地。这套代码本身就是最好的教学材料:学习 Anthropic 的工程实践,掌控自己的数据,然后用这些数据来优化自己的工作流。
光明在于工程的精湛,阴影在于主权的缺失。我们选择照亮阴影,而不是关掉灯光。
文档版本:v1.0 | 最后更新:2026-06-03