反蒸馏与输出保护
Claude Code 的 7 层反蒸馏纵深防御 — 假工具注入、思维链脱敏、输出摘要、客户端认证、请求指纹、网关检测、Undercover 模式的完整实现
职责概述
解决的问题:竞争对手可以通过批量调用 Claude API,收集大量"输入→输出"对,然后用这些数据训练自己的模型(蒸馏)。反蒸馏系统通过在输出中注入噪声、脱敏推理过程、验证客户端真实性等手段,使得收集到的输出无法有效用于训练竞品模型。
应用场景:① 竞品通过中间代理批量采集 CC 输出 → 网关检测识别并标记 ② 逆向工程者分析 CC 输出模式 → 假工具注入让蒸馏数据包含噪声 ③ 思维链被截获 → 脱敏为不可读的占位符 ④ 伪造 CC 客户端发送请求 → Native Attestation 验证失败拒绝服务。
一句话理解:就像钞票的防伪——水印、安全线、变色油墨、微缩文字,每一层都让造假更难。蒸馏者拿到的数据就像假钞上印着"样本"两个字。
架构设计
⚔ Layer 1: Fake Tool Injection — 假工具注入 ★ 核心
这是反蒸馏最核心的机制。当启用时,API 在模型的上下文中注入不存在的工具定义。模型输出的 tool_use 调用中会包含对这些假工具的引用,使得蒸馏出的学生模型学到的是包含噪声的工具调用模式,无法正常工作。
// 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 中,但后续轮次可以通过签名恢复完整上下文。蒸馏者拿到的是摘要而非原始输出。
// 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-5078 的 stripSignatureBlocks 函数中执行:当凭证变更时(用户切换账号),thinking、redacted_thinking、connector_text 三种签名块会被统一清除,防止旧凭证的签名数据泄露。⚔ Layer 3: Thinking Redaction — 思维链脱敏
API 返回 redacted_thinking 块而非明文 thinking 块。模型的推理过程在 transcript 中不可见,只保留脱敏后的占位符。CLI 用户端跳过思维链摘要器,直接渲染为 stub。
// 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 中完全不可见
⇉ Layer 4: Streamlined Output — 蒸馏抵抗输出格式
当 CLAUDE_CODE_STREAMLINED_OUTPUT=true 且 STREAMLINED_OUTPUT feature flag 启用时,SDK 输出的消息经过变换:文本消息保留,工具调用只显示累计计数(遇到文本重置),thinking 内容省略,工具列表和模型信息移除。输出变为 streamlined_text 和 streamlined_tool_use_summary 两种类型。
// 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 // 其他全部丢弃
}
}
}
🔒 Layer 5: Native Client Attestation — 客户端认证
Attribution header 中嵌入 cch=00000 占位符。Bun 的 native HTTP 栈(用 Zig 实现)在发送请求前将其替换为密码学哈希。服务端验证这个 token 来确认请求来自真正的 Claude Code 客户端,而非第三方 wrapper 或爬虫。
// 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
00000 不会改变 HTTP 请求的 Content-Length,避免了触发 HTTP 管道化问题或中间代理的异常行为。实现在 Zig 层而非 TypeScript 层,使得逆向工程者无法在 JS 代码中找到哈希算法。🔑 Layer 6: Request Fingerprint — 请求指纹
基于用户首条消息计算 3 字符 hex 指纹,用于 API 请求归因、OAuth token 验证和计费追踪。算法固定且被 1P/3P API 共同依赖,注释明确警告"不要随意修改"。
// 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 种主流网关。
// 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 中。
// 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."
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 | ~2130 | API 调用核心:anti_distillation 参数注入、connector_text 流式处理 |
utils/betas.ts | ~300 | Beta header 管理:connector text 摘要、thinking redaction 配置 |
constants/betas.ts | ~25 | Beta header 常量定义 |
utils/streamlinedTransform.ts | ~180 | 蒸馏抵抗输出变换器:工具分类 + 累计计数 + 摘要 |
utils/fingerprint.ts | ~75 | 请求指纹:SHA256 + 盐值,3 字符 hex |
constants/system.ts | ~94 | Attribution header:版本、入口、cch 认证、workload |
services/api/logging.ts | ~135 | 网关指纹识别:7 种 AI 代理检测 |
utils/undercover.ts | ~90 | 隐身模式:自动激活、代号保护、commit 归因移除 |
utils/messages.ts | ~5070 | stripSignatureBlocks:签名块统一清理 |
services/api/client.ts | ~129 | x-anthropic-additional-protection 头 |
utils/sanitization.ts | ~100 | Unicode 清理:防御隐藏 prompt 注入 |
utils/commitAttribution.ts | ~300 | Commit 归因:Claude 贡献率计算、Co-Authored-By |