职责概述

解决的问题:竞争对手可以通过批量调用 Claude API,收集大量"输入→输出"对,然后用这些数据训练自己的模型(蒸馏)。反蒸馏系统通过在输出中注入噪声、脱敏推理过程、验证客户端真实性等手段,使得收集到的输出无法有效用于训练竞品模型。

应用场景:① 竞品通过中间代理批量采集 CC 输出 → 网关检测识别并标记 ② 逆向工程者分析 CC 输出模式 → 假工具注入让蒸馏数据包含噪声 ③ 思维链被截获 → 脱敏为不可读的占位符 ④ 伪造 CC 客户端发送请求 → Native Attestation 验证失败拒绝服务。

一句话理解:就像钞票的防伪——水印、安全线、变色油墨、微缩文字,每一层都让造假更难。蒸馏者拿到的数据就像假钞上印着"样本"两个字。

架构设计

Layer 1 — API 侧输出变换(服务端执行)
Fake Tool Injection — 注入虚假工具定义
Thinking Redaction — 思维链脱敏
Connector Text Summarization — 文本摘要+签名
Layer 2 — 客户端侧输出处理
Streamlined Output — 蒸馏抵抗输出格式
Strip Signature Blocks — 签名块清理
Layer 3 — 请求认证与追踪
Native Client Attestation — 密码学客户端认证
Request Fingerprint — SHA256 请求指纹
Attribution Header — 归因头
Layer 4 — 环境检测
Gateway Fingerprinting — AI 代理检测
Undercover Mode — 隐身模式

Layer 1: Fake Tool Injection — 假工具注入 ★ 核心

这是反蒸馏最核心的机制。当启用时,API 在模型的上下文中注入不存在的工具定义。模型输出的 tool_use 调用中会包含对这些假工具的引用,使得蒸馏出的学生模型学到的是包含噪声的工具调用模式,无法正常工作。

anti_distillation: ['fake_tools'] 请求参数
客户端只发送 flag,假工具注入在 API 侧执行
// services/api/claude.ts:301-313
// Anti-distillation: send fake_tools opt-in for 1P CLI only
if (
  feature('ANTI_DISTILLATION_CC')
    ? process.env.CLAUDE_CODE_ENTRYPOINT === 'cli' &&
      shouldIncludeFirstPartyOnlyBetas() &&
      getFeatureValue_CACHED_MAY_BE_STALE(
        'tengu_anti_distill_fake_tool_injection',
        false,
      )
    : false
) {
  result.anti_distillation = ['fake_tools']
}
三个前置条件缺一不可:① 构建时 ANTI_DISTILLATION_CC feature flag 开启 ② 运行时是 CLI 入口(非 SDK) ③ GrowthBook 远程配置 tengu_anti_distill_fake_tool_injection 为 true。这种"编译期 + 运行期 + 远程配置"三重门控确保了精确的灰度控制。

Layer 2: Connector Text Summarization (Slate Prism)

POC 级别的反蒸馏机制。API 在工具调用之间缓存 assistant 文本,用摘要替换原文,并附加加密签名。原文不再出现在 transcript 中,但后续轮次可以通过签名恢复完整上下文。蒸馏者拿到的是摘要而非原始输出。

Beta Header: summarize-connector-text-2026-03-13
服务端摘要 + 密码学签名,客户端透传
// utils/betas.ts:279-298 — POC: server-side connector-text summarization
// When enabled, the API buffers assistant text between tool calls,
// summarizes it, and returns the summary with a cryptographic signature
// so the original text can be restored on subsequent turns.

// 启用条件:内部用户 + CONNECTOR_TEXT feature flag + GrowthBook flag
if (process.env.USER_TYPE === 'ant'
    && feature('CONNECTOR_TEXT')
    && (getFeatureValue_CACHED_MAY_BE_STALE('tengu_slate_prism', false)
        || isEnvTruthy(process.env.USE_CONNECTOR_TEXT_SUMMARIZATION))) {
  result.betas.push('summarize-connector-text-2026-03-13')
}

// services/api/claude.ts:2055-2134 — 流式处理 connector_text 块
// 累积文本和签名,在 stripSignatureBlocks 中按需清理
签名块的清理在 utils/messages.ts:5060-5078stripSignatureBlocks 函数中执行:当凭证变更时(用户切换账号),thinking、redacted_thinking、connector_text 三种签名块会被统一清除,防止旧凭证的签名数据泄露。

Layer 3: Thinking Redaction — 思维链脱敏

API 返回 redacted_thinking 块而非明文 thinking 块。模型的推理过程在 transcript 中不可见,只保留脱敏后的占位符。CLI 用户端跳过思维链摘要器,直接渲染为 stub。

Beta Header: redact-thinking-2026-02-12
思维链对蒸馏者完全不可见
// utils/betas.ts:266-277
if (getFeatureValue_CACHED_MAY_BE_STALE('tengu_thinking_redaction', true)) {
  result.betas.push('redact-thinking-2026-02-12')
}

// constants/betas.ts:20 — beta header 定义
// 'redact-thinking-2026-02-12' → API 返回 redacted_thinking 块

// CLI 端处理:跳过 thinking summarizer,直接渲染为 stub
// 模型的推理链路在 transcript 中完全不可见
思维链脱敏是一个双刃剑:它保护了推理过程不被蒸馏,但也意味着用户无法看到模型的思考过程来调试问题。CC 通过 GrowthBook 远程配置实现了按用户/场景的灰度控制,而非一刀切。

Layer 4: Streamlined Output — 蒸馏抵抗输出格式

CLAUDE_CODE_STREAMLINED_OUTPUT=trueSTREAMLINED_OUTPUT feature flag 启用时,SDK 输出的消息经过变换:文本消息保留,工具调用只显示累计计数(遇到文本重置),thinking 内容省略,工具列表和模型信息移除。输出变为 streamlined_textstreamlined_tool_use_summary 两种类型。

createStreamlinedTransformer()
有状态变换器:累计工具计数,文本出现时重置
// utils/streamlinedTransform.ts — 蒸馏抵抗输出格式
export function createStreamlinedTransformer(): (
  message: StdoutMessage,
) => StdoutMessage | null {
  let cumulativeCounts = createEmptyToolCounts()

  return function transformToStreamlined(message: StdoutMessage) {
    switch (message.type) {
      case 'assistant': {
        const text = extractTextContent(content, '\n').trim()
        accumulateToolUses(message, cumulativeCounts)

        if (text.length > 0) {
          // 文本消息:输出文本,重置计数
          cumulativeCounts = createEmptyToolCounts()
          return { type: 'streamlined_text', text, ... }
        }
        // 纯工具消息:输出累计摘要
        // 例:"Searched 3 patterns, read 5 files, wrote 2 files"
        return { type: 'streamlined_tool_use_summary', tool_summary, ... }
      }
      case 'result': return message    // 结果消息透传
      default:    return null          // 其他全部丢弃
    }
  }
}
工具分类为 5 个桶:searches(Grep/Glob/WebSearch/LSP)、reads(FileRead/ListMcpResources)、writes(FileWrite/FileEdit/NotebookEdit)、commands(Shell/Tmux)、other。摘要文本用自然语言描述:"Searched 3 patterns, read 5 files, wrote 2 files, ran 4 commands"。

🔒 Layer 5: Native Client Attestation — 客户端认证

Attribution header 中嵌入 cch=00000 占位符。Bun 的 native HTTP 栈(用 Zig 实现)在发送请求前将其替换为密码学哈希。服务端验证这个 token 来确认请求来自真正的 Claude Code 客户端,而非第三方 wrapper 或爬虫。

Attribution Header 构造
cch 占位符在 HTTP 栈中被替换,服务端验证真实性
// constants/system.ts:49-94 — Attribution Header
const header = [
  `cc_version=${VERSION}.${fingerprint}`,    // 版本 + 3字符指纹
  `cc_entrypoint=${entrypoint}`,              // cli | sdk | unknown
  `cch=00000`,                                // ← 占位符
  `cc_workload=${tag}`,                       // QoS 路由
].join('&')

// 当 NATIVE_CLIENT_ATTESTATION feature flag 启用时:
// Bun native HTTP 栈 (bun-anthropic/src/http/Attestation.zig)
// 在发送前将 cch=00000 替换为密码学哈希
// 关键:同长度替换,不改变 Content-Length
同长度替换(same-length replacement)是精妙的实现细节:用哈希值替换 00000 不会改变 HTTP 请求的 Content-Length,避免了触发 HTTP 管道化问题或中间代理的异常行为。实现在 Zig 层而非 TypeScript 层,使得逆向工程者无法在 JS 代码中找到哈希算法。

🔑 Layer 6: Request Fingerprint — 请求指纹

基于用户首条消息计算 3 字符 hex 指纹,用于 API 请求归因、OAuth token 验证和计费追踪。算法固定且被 1P/3P API 共同依赖,注释明确警告"不要随意修改"。

computeFingerprint()
SHA256 + 硬编码盐值,取前 3 位 hex
// utils/fingerprint.ts
export const FINGERPRINT_SALT = '59cf53e54c78'  // 硬编码盐值

export function computeFingerprint(
  messageText: string,
  version: string,
): string {
  // 提取索引 [4, 7, 20] 处的字符,越界用 "0"
  const indices = [4, 7, 20]
  const chars = indices.map(i => messageText[i] || '0').join('')

  const fingerprintInput = `${FINGERPRINT_SALT}${chars}${version}`

  // SHA256 哈希,取前 3 个 hex 字符
  const hash = createHash('sha256')
    .update(fingerprintInput).digest('hex')
  return hash.slice(0, 3)
}

// IMPORTANT: Do not change this method without careful coordination
// with 1P and 3P (Bedrock, Vertex, Azure) APIs.

📡 Layer 7: Gateway Detection — AI 网关指纹识别

检测并记录 AI 网关代理,防止通过中间代理批量采集 CC 的输出用于蒸馏。通过匹配响应头前缀和 URL host 后缀识别 7 种主流网关。

网关指纹识别
7 种主流 AI 网关的自动检测
// services/api/logging.ts:65-135
// 检测方式 1:响应头前缀匹配
'x-litellm-'    → LiteLLM
'helicone-'     → Helicone
'x-portkey-'    → Portkey
'cf-aig-'       → Cloudflare AI Gateway
'x-kong-'       → Kong
'x-bt-'         → Braintrust

// 检测方式 2:URL host 后缀匹配
'.cloud.databricks.com' → Databricks

// 检测结果写入分析事件
logEvent('tengu_api_success', { gateway_type })
logEvent('tengu_api_failure', { gateway_type })

🕵 附加: Undercover Mode — 隐身模式

当 Anthropic 内部员工(USER_TYPE=ant)在公开仓库工作时自动激活。移除所有归因信息(Co-Authored-By、Claude Code 标识、模型名称),系统提示不告知模型自己的身份。防止内部代号泄露到公开 commit 中。

isUndercover()
自动检测:不在内部仓库白名单中就激活
// utils/undercover.ts
export function isUndercover(): boolean {
  if (process.env.USER_TYPE === 'ant') {
    if (isEnvTruthy(process.env.CLAUDE_CODE_UNDERCOVER)) return true
    // 自动模式:除非确认在内部仓库白名单中,否则默认激活
    return getRepoClassCached() !== 'internal'
  }
  return false  // 外部用户永远不激活
}

// 系统提示注入(undercover 时):
// "You are operating UNDERCOVER in a PUBLIC repository.
//  NEVER include: internal codenames, version numbers,
//  project names, 'Claude Code', Co-Authored-By, or any
//  hint of what model you are."
Undercover 模式没有强制关闭选项 — 如果不确定是否在内部仓库,就保持隐身。这是安全优先的设计:宁可误杀(在内部仓库也隐身),不可漏放(在公开仓库泄露代号)。外部用户构建的 CC 在编译期通过 USER_TYPE 的 dead code elimination,所有 undercover 函数退化为空返回。

设计模式与亮点

1. 编译期 + 运行期 + 远程配置 三重门控

假工具注入需要三个条件同时满足:构建时 feature flag ANTI_DISTILLATION_CC、运行时 CLI 入口检测、远程 GrowthBook tengu_anti_distill_fake_tool_injection。这种三层控制使得 Anthropic 可以精确控制哪些用户/场景启用反蒸馏,而非一刀切。

2. 服务端执行 + 客户端配合

假工具注入、思维链脱敏、Connector Text 摘要都在 API 侧执行。客户端只发送 flag(如 anti_distillation: ['fake_tools']),不参与实际的输出变换。即使客户端被逆向工程,攻击者也无法绕过服务端的保护。

3. Dead Code Elimination 保护

Undercover 模式通过 process.env.USER_TYPE === 'ant' 的编译期常量折叠,在非 Anthropic 构建中所有内部函数退化为 return false / return ''。外部用户看不到任何 undercover 相关的逻辑。

4. 同长度替换避免 HTTP 副作用

Native Attestation 使用 cch=00000 占位符,在 Zig 层替换为等长哈希,不改变 Content-Length。这种底层实现避免了中间代理的异常行为。

5. 签名块统一管理

thinking、redacted_thinking、connector_text 三种签名块在 stripSignatureBlocks 中统一清理。凭证变更时一次性清除所有签名数据,防止跨用户的签名泄露。

Agent 实践借鉴 — 垂直 Agent 的输出保护

场景映射:客服 Agent 的输出保护需求

客服 Agent 的对话输出包含敏感信息:业务逻辑决策路径、工具调用链路、内部系统架构。这些输出如果被采集用于蒸馏竞品 Agent,等同于泄露核心竞争力。

借鉴 CC:三层输出保护策略

// 客服 Agent 输出保护 — 借鉴 CC 的反蒸馏架构
class OutputProtection {
  // 策略 1:服务端输出变换(对应 CC 的 Fake Tool Injection)
  // 在返回给客户端前,对工具调用链路做脱敏
  transformForDelivery(rawResponse: AgentResponse): DeliveryResponse {
    return {
      text: rawResponse.text,                    // 文本保留
      toolSummary: this.summarizeTools(rawResponse.tools), // 工具只返回摘要
      thinking: undefined,                        // 推理过程不暴露
    }
  }

  // 策略 2:请求指纹(对应 CC 的 Fingerprint)
  computeRequestFingerprint(sessionId: string, userId: string): string {
    const input = `${SALT}${sessionId.slice(0, 8)}${userId.slice(0, 4)}`
    return crypto.createHash('sha256').update(input).digest('hex').slice(0, 8)
  }

  // 策略 3:网关检测(对应 CC 的 Gateway Detection)
  detectProxy(headers: Record<string, string>): string | null {
    const proxySignatures = {
      'x-litellm-': 'LiteLLM',
      'cf-aig-': 'Cloudflare',
      'x-custom-proxy-': 'CustomProxy',
    }
    for (const [prefix, name] of Object.entries(proxySignatures)) {
      if (Object.keys(headers).some(h => h.startsWith(prefix))) return name
    }
    return null
  }
}

落地清单

必须抄的:
  • 服务端执行输出变换:工具调用脱敏、推理过程隐藏,全部在服务端完成。客户端只拿摘要。
  • 请求指纹:每个会话计算唯一指纹,用于归因和异常检测。盐值硬编码,算法与多方协调。
  • 网关检测:通过响应头特征识别中间代理,检测结果写入审计日志。
不需要抄的:
  • Fake Tool Injection:这是 LLM 输出保护特有的,客服 Agent 输出的是结构化数据,不存在"假工具"的场景。
  • Undercover Mode:内部员工专用,外部 Agent 不需要。
  • Native Attestation:需要 Zig 层的客户端认证,客服场景用 OAuth token 就够了。

代码索引

文件行数说明
services/api/claude.ts~2130API 调用核心:anti_distillation 参数注入、connector_text 流式处理
utils/betas.ts~300Beta header 管理:connector text 摘要、thinking redaction 配置
constants/betas.ts~25Beta header 常量定义
utils/streamlinedTransform.ts~180蒸馏抵抗输出变换器:工具分类 + 累计计数 + 摘要
utils/fingerprint.ts~75请求指纹:SHA256 + 盐值,3 字符 hex
constants/system.ts~94Attribution header:版本、入口、cch 认证、workload
services/api/logging.ts~135网关指纹识别:7 种 AI 代理检测
utils/undercover.ts~90隐身模式:自动激活、代号保护、commit 归因移除
utils/messages.ts~5070stripSignatureBlocks:签名块统一清理
services/api/client.ts~129x-anthropic-additional-protection 头
utils/sanitization.ts~100Unicode 清理:防御隐藏 prompt 注入
utils/commitAttribution.ts~300Commit 归因:Claude 贡献率计算、Co-Authored-By