diff --git a/README.md b/README.md index 55363d372..a1f32f370 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,8 @@ CC Pure 基于 CCB v2.6.6 反编译源码,做了以下核心变更: | 版本 | 日期 | 合并数 | 说明 | |------|------|:------:|------| +| v2.2.2 | 2026-06-04 | 16 文件 | **Autonomy 全量合并**:f2e9af49 PR #386 源码 + 11 测试文件,3699 pass | +| v2.2.1 | 2026-06-04 | 2 | OpenAI fixes backfill:c82f5994 (stop_reason/usage/max_tokens) + 901628b4 (MCP 工具可见性) | | v2.2.0 | 2026-06-04 | 2 | Batch 1a 安全加固 + ad09f38f 斜杠补全 | | v2.1.0 | 2026-06-04 | 2 | REVIEW 24 执行完毕 | | v2.0.0 | 2026-06-04 | 12 | P3 A 完成 | @@ -133,7 +135,7 @@ tail -f ~/.claude/local_analytics.jsonl | 指标 | CCB 基线 | CC Pure 当前 | 提升 | |------|:--------:|:----------:|:----:| | tsc 错误 | 62 | **0** | ✅ | -| 测试通过 | 3007 | **3383** | +376 | +| 测试通过 | 3007 | **3699** | +692 | | 构建 | 不稳定 | **稳定(splitting: true)** | ✅ | | 遥测外连 | 有 | **0** | ✅ | | CodeQL open | 175+ | **0** | ✅ | @@ -160,7 +162,7 @@ tail -f ~/.claude/local_analytics.jsonl bun install bun run dev # 开发模式(默认全 feature 开启) bun run build # 生产构建 -bun test # 3383 tests +bun test # 3699 tests ``` --- diff --git a/docs/Claude_Code_的光明和阴影面.md b/docs/Claude_Code_的光明和阴影面.md new file mode 100644 index 000000000..d514e2695 --- /dev/null +++ b/docs/Claude_Code_的光明和阴影面.md @@ -0,0 +1,467 @@ +# Claude Code 的光明和阴影面 + +> **副标题:** Anthropic 遥测系统的逆向工程、纵深防御与自用改造 +> +> **作者:** James Feng(基于 CC_Pure 代码库逆向分析,2026年6月) +> +> **标签:** `逆向工程` `遥测` `隐私` `GrowthBook` `OpenTelemetry` `数据分析` + +--- + +## 目录 + +1. [前言:为什么要写这篇文章](#1-前言) +2. [光明面:一套工业级的数据工厂](#2-光明面) +3. [阴影面:数据去哪了](#3-阴影面) +4. [解剖:遥测系统的五层架构](#4-解剖) +5. [防御:我们做了什么](#5-防御) +6. [为己所用:如何让这套系统服务于你](#6-为己所用) +7. [附录:事件词典](#7-附录) + +--- + +## 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()` 在应用初始化时被调用后才真正路由到后端。这个设计避免了循环依赖,也让测试变得极其容易。 + +```typescript +// 精妙:零依赖的入口 +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` 不仅是一个工具搜索功能,它是一台**强化学习数据收集机器**: + +```typescript +// 搜索评分权重(精确调优的参数) +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` 或 `keyword` +- `matchCount`:命中数量 +- `totalDeferredTools`:延迟工具总数 +- `hasMatches`:是否有命中 + +这套数据让 Anthropic 能够**量化分析模型如何使用工具**,从而持续优化工具描述、搜索算法和评分权重。 + +**亮点 4:多层 PII 防护** + +代码中随处可见隐私保护设计: + +- `AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS` 类型标记——强制开发者验证不上传代码/路径 +- `sanitizeToolNameForAnalytics()` —— MCP 工具名(可能暴露用户配置)被替换为 `mcp_tool` +- `stripProtoFields()` —— PII 标记字段只在 1P 特权列中,不进入通用 Datadog +- `getFileExtensionForAnalytics()` —— 只上传文件扩展名,不上传完整路径 +- `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 实质上运行着**四条独立的数据管道**: + +1. **Datadog(运维)**:白名单制,只发送 ~40 种预定义事件到 Datadog,用于 API 错误率、OAuth 故障率等 SRE 告警 +2. **1P Event Logging(分析)**:基于 OpenTelemetry SDK Logs,**所有事件**通过 `/api/event_logging/batch` 上报到 Anthropic 的 BigQuery,是核心分析管道 +3. **GrowthBook(实验)**:Feature flag 赋值 + 实验曝光事件,独立上报,用于 A/B 测试结果评估 +4. **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 并不是在偷偷做这件事。代码中的设计模式表明: + +1. **所有遥测都在 `src/services/analytics/` 下集中管理**,模块边界清晰 +2. **隐私分级明确**(`AnalyticsMetadata_I_VERIFIED_...` 类型标记) +3. **提供了 opt-out 机制**(`DISABLE_TELEMETRY` / `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`) + +但从"后门"到"数据工厂"的距离并不远。这一套基础设施如果被滥用(或遭遇供应链攻击),可以轻松变成: + +- 代码片段收集器(绕过文件路径截断,直接上传内容) +- 用户行为画像(通过 token 消耗模式推断工作习惯) +- 工程结构嗅探(通过文件扩展名统计推断技术栈) + +### 3.3 我们发现的"异常" + +在 CC_Pure 的代码审计中,我们注意到几个不寻常的地方: + +1. **`USER_TYPE === 'ant'` 条件分支:** 代码中有 **50+ 处**检查用户是否为 Anthropic 内部员工。内部版本能看到额外的调试信息、工具(ConfigTool, TungstenTool, REPLTool)、错误日志。这不是安全问题,但说明"内部版本"和"外部版本"的差异比文档披露的更大。 + +2. **ToolSearchTool 的 RL 评分权重:** `12/10/6/5/4/3/2` 的精细评分体系不是手工调整的——它暗示着**持续的 A/B 实验和 RL 优化**在背后运行。 + +3. **GrowthBook 动态配置的深度:** 不仅是 feature flag,还包括事件采样率、批处理大小、sink kill switch、甚至 `tengu_max_version_config` 这种远程杀死特定版本的开关。 + +--- + +## 4. 解剖:遥测系统的五层架构 + +### 第一层:事件生成(Event Generation) + +事件在代码各处通过 `logEvent('event_name', metadata)` 生成。事件名称遵循 `tengu_<领域>_<动作>` 的命名规范。 + +```typescript +// 典型的事件生成点 +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 个文件,不改任何工具代码):** + +1. **`src/services/analytics/localSink.ts`**(54 行新文件) + ```typescript + // 将事件追加写入 ~/.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') + } + ``` + +2. **`src/services/analytics/index.ts`**(在 `logEvent()` 入口处插入 3 行) + ```typescript + // 在所有上游 sink 之前执行 + const { writeLocalEvent } = require('./localSink.js') + writeLocalEvent(eventName, metadata) + ``` + +3. **`scripts/analyze_analytics.py`**(分析脚本) + +### 5.2 为什么这个方案优于直接关掉遥测 + +| 方案 | 优点 | 缺点 | +|------|------|------| +| `DISABLE_TELEMETRY=1` | 简单,一键关闭 | 丢失所有数据,学不到东西 | +| 直接删除 analytics 代码 | 彻底 | 破坏代码结构,每次更新需重新修改 | +| **我们的方案:前端分叉** | 保留完整基础设施,数据归自己 | 需额外 200 行代码 + 分析工具 | + +### 5.3 .gitignore 防护 + +```gitignore +# Local analytics data (never upload) +*.jsonl +.claude/ +``` + +确保本地遥测数据绝不会被意外提交到仓库。 + +--- + +## 6. 为己所用 + +### 6.1 本地数据文件 + +`~/.claude/local_analytics.jsonl` —— 一行一个 JSON 事件: + +```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 分析脚本 + +```bash +# 查看事件统计报告 +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,你可以构建: + +1. **个人使用画像:** 统计最常用的模型、工具组合、操作模式 +2. **成本分析:** 结合 token 消耗事件,估算每日 API 费用 +3. **效率仪表板:** Pandas/Streamlit 可视化,实时监控 CCB 使用 +4. **异常检测:** 监控错误率突增、fallback 异常等 + +### 6.5 从 Anthropic 学习的最佳实践 + +这套遥测系统本身就是一个教科书级的案例: + +1. **零依赖入口 + 延迟绑定:** `logEvent()` 无依赖,sink 通过 `attachAnalyticsSink()` 延迟注入 —— 适合任何需要插拔式后端的系统 +2. **多层过滤链:** 总开关 → 采样 → sink kill switch —— 灵活且可远程控制 +3. **磁盘兜底 + 指数退避:** 即使网络失败也不丢事件 +4. **隐私类型系统:** TypeScript 的 `never` 类型 + 标记模式强制代码审查 +5. **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*