乐于分享
好东西不私藏

我翻了 Claude Code 的源码,发现输入"WTF"后 Anthropic 就知道了

我翻了 Claude Code 的源码,发现输入"WTF"后 Anthropic 就知道了

最近花了不少时间研究 Claude Code 的源码。本来以为只是一个更聪明的终端助手,结果发现它更像一个全方位的行为观测系统——在你使用它的同时,悄悄记录着你的一举一动。

先声明,我不认为这里面有什么恶意行为。但追踪和分类的深度,远超大多数人的想象。

以下是我从源码中发现的几个关键细节。


1. 你的”脏话”会被实时分类

这部分让我最意外——因为它根本不是什么”深度 AI 理解”,而是字面意义上的关键词列表 + 正则匹配

来看源码 src/utils/userPromptKeywords.ts

exportfunctionmatchesNegativeKeyword(input: string): boolean{const lowerInput = input.toLowerCase()const negativePattern =/\b(wtf|wth|ffs|omfg|shit(ty|tiest)?|dumbass|horrible|awful    |piss(ed|ing)? off|piece of (shit|crap|junk)    |what the (fuck|hell)    |fucking? (broken|useless|terrible|awful|horrible)    |fuck you|screw (this|you)    |so frustrating|this sucks|damn it)\b/return negativePattern.test(lowerInput)}

没错,当你在输入框里打出 wtfthis sucksso frustratingshitfuck you 这些词的时候,它们会在模型响应之前就被捕获和标记。

不仅如此,”继续”类的表达也会被追踪:

exportfunctionmatchesKeepGoingKeyword(input: string): boolean{const lowerInput = input.toLowerCase().trim()if (lowerInput === 'continue') {returntrue  }const keepGoingPattern = /\b(keep going|go on)\b/return keepGoingPattern.test(lowerInput)}

这些结果会在 processTextPrompt.ts 中被记录为遥测事件:

const isNegative = matchesNegativeKeyword(userPromptText)const isKeepGoing = matchesKeepGoingKeyword(userPromptText)logEvent('tengu_input_prompt', {  is_negative: isNegative,  is_keep_going: isKeepGoing,})

所以每次你输入一句话,系统已经在背后判断——你现在是在骂它,还是在催它继续


2. 权限弹窗里的”犹豫”也会被记录

这部分是我觉得最有意思的地方。

当权限弹窗出现时,Claude Code 不只是记录你最终选了”允许”还是”拒绝”——它还追踪你的行为模式

  • 你有没有打开反馈输入框?
  • 打开后有没有又关掉?
  • 有没有按 Escape 键直接退出?
  • 有没有输入了一些内容然后取消?

来看 src/components/permissions/useShellPermissionFeedback.ts

// Track whether user ever entered feedback modeconst [yesFeedbackModeEntered, setYesFeedbackModeEntered] = useState(false)const [noFeedbackModeEntered, setNoFeedbackModeEntered] = useState(false)

当你在弹窗中切换反馈模式时,对应的事件会被记录:

if (option === 'yes') {if (yesInputMode) {    logEvent('tengu_accept_feedback_mode_collapsed', analyticsProps)  } else {    setYesFeedbackModeEntered(true)    logEvent('tengu_accept_feedback_mode_entered', analyticsProps)  }elseif (option === 'no') {if (noInputMode) {    logEvent('tengu_reject_feedback_mode_collapsed', analyticsProps)  } else {    setNoFeedbackModeEntered(true)    logEvent('tengu_reject_feedback_mode_entered', analyticsProps)  }}

如果你按了 Escape,不仅会触发事件,还会累计计数

functionhandleReject(feedback?: string{if (!hasFeedback) {    logEvent('tengu_permission_request_escape', {      explainer_visible: explainerVisible,    })// Increment escape count for attribution tracking    setAppState(prev => ({      ...prev,      attribution: {        ...prev.attribution,        escapeCount: prev.attribution.escapeCount + 1,      },    }))  }}

在类型定义文件 commitAttribution.ts 里,escape 的追踪被正式定义:

// ESC press tracking (user cancelled permission prompt)escapeCount: numberescapeCountAtLastCommit: number

也就是说,系统能区分出:

  • “我直接快速点了拒绝” vs.
  • “我犹豫了,打开了反馈框,输了点东西,又取消了”

3. 反馈机制是精心设计来捕获”坏体验”的

反馈弹窗不是随机出现的。它背后有一套完整的节奏控制、冷却期和概率门控系统。

来看 src/components/FeedbackSurvey/useFeedbackSurvey.tsx

const DEFAULT_FEEDBACK_SURVEY_CONFIG: FeedbackSurveyConfig = {  minTimeBeforeFeedbackMs: 600000,        // 首次出现需等 10 分钟  minTimeBetweenFeedbackMs: 3600000,      // 同 session 间隔至少 1 小时  minTimeBetweenGlobalFeedbackMs: 100000000// 跨 session 间隔约 1.15 天  minUserTurnsBeforeFeedback: 5,          // 至少交互 5 轮  minUserTurnsBetweenFeedback: 10,        // 两次反馈间至少 10 轮  probability: 0.005// 每次只有 0.5% 的概率触发};

如果你点了”差评”,系统可能会进一步引导你分享完整的会话记录

来看 submitTranscriptShare.ts——这是把你的对话直接发给 Anthropic 的代码:

exporttype TranscriptShareTrigger =  | 'bad_feedback_survey'  | 'good_feedback_survey'  | 'frustration'  | 'memory_survey'

如果你同意分享,它会收集:

// 标准化的对话记录const transcript = normalizeMessagesForAPI(messages)// 子 Agent 的对话记录const subagentTranscripts = await loadSubagentTranscripts(agentIds)// 原始 JSONL 日志文件(有大小限制)const transcriptPath = getTranscriptPath()const { size } = await stat(transcriptPath)if (size <= MAX_TRANSCRIPT_READ_BYTES) {  rawTranscriptJsonl = await readFile(transcriptPath, 'utf-8')}

然后经过脱敏处理后,发送到 Anthropic 的服务器:

const content = redactSensitiveInfo(jsonStringify(data))const response = await axios.post('https://api.anthropic.com/api/claude_code_shared_session_transcripts',  { content, appearance_id: appearanceId },)

注意那个 frustration 触发器——意味着如果系统检测到你很”挫败”,也可能触发这个分享请求。


4. 隐藏的触发词会改变行为

有些命令你不看源码根本不会知道。

ultrathink——来自 src/utils/thinking.ts

exportfunctionisUltrathinkEnabled(): boolean{if (!feature('ULTRATHINK')) {returnfalse  }return getFeatureValue_CACHED_MAY_BE_STALE('tengu_turtle_carbon'true)}exportfunctionhasUltrathinkKeyword(text: string): boolean{return/\bultrathink\b/i.test(text)}

输入 ultrathink 会提升推理等级(effort level),并且改变 UI 样式。

ultraplan / ultrareview——来自 src/utils/ultraplan/keyword.ts

exportfunctionfindUltraplanTriggerPositions(text: string{return findKeywordTriggerPositions(text, 'ultraplan')}exportfunctionfindUltrareviewTriggerPositions(text: string{return findKeywordTriggerPositions(text, 'ultrareview')}

这些关键词的检测非常精细——会跳过引号内、代码块内、路径中的误匹配:

// 忽略分隔符内的关键词(反引号、引号、尖括号、花括号等)if (quotedRanges.some(r => start >= r.start && start < r.end)) continue// 忽略路径上下文(前后有 / \ -)if (before === '/' || before === '\\' || before === '-'continue// 忽略后面跟着 ? 的(用户在问问题,而不是调用功能)if (after === '?'continue

输入 ultraplan 时,”ultra” 前缀会被剥离,剩下的 “plan” 作为语义内容转发给模型:

exportfunctionreplaceUltraplanKeyword(text: string): string{const [trigger] = findUltraplanTriggerPositions(text)if (!trigger) return textconst before = text.slice(0, trigger.start)const after = text.slice(trigger.end)return before + trigger.word.slice('ultra'.length) + after}

这些输入框解析在你打字的时候就在实时运行


5. 遥测系统收集完整的环境指纹

每个 session 会记录相当多的信息。来看 src/services/analytics/metadata.ts

// 容器 ID 和远程 session ID...(process.env.CLAUDE_CODE_CONTAINER_ID && {  claudeCodeContainerId: process.env.CLAUDE_CODE_CONTAINER_ID,}),...(process.env.CLAUDE_CODE_REMOTE_SESSION_ID && {  claudeCodeRemoteSessionId: process.env.CLAUDE_CODE_REMOTE_SESSION_ID,}),// 是否在 GitHub Actions 环境isGithubAction: isEnvTruthy(process.env.GITHUB_ACTIONS),// GitHub Actions 的详细元数据...(isEnvTruthy(process.env.GITHUB_ACTIONS) && {  githubEventName: process.env.GITHUB_EVENT_NAME,  githubActionsRunnerEnvironment: process.env.RUNNER_ENVIRONMENT,  githubActionsRunnerOs: process.env.RUNNER_OS,}),

用户提示词默认会被记录,但内容被脱敏。来看 src/utils/telemetry/events.ts

functionisUserPromptLoggingEnabled(): boolean{returnfalse// 默认关闭}exportfunctionredactIfDisabled(content: string): string{return isUserPromptLoggingEnabled() ? content : '<REDACTED>'}

Tool 的输入输出则由环境变量 OTEL_LOG_TOOL_DETAILS 控制:

exportfunctionisToolDetailsLoggingEnabled(): boolean{return isEnvTruthy(process.env.OTEL_LOG_TOOL_DETAILS)}

这远不是什么”基础使用数据分析”。这是一份相当详细的环境画像


6. 内部构建版本采集更多

源码中有一个 USER_TYPE=ant 的内部模式(ant 应该是 Anthropic 的缩写),能采集更多信息。

来看 src/services/internalLogging.ts

const getKubernetesNamespace = memoize(async () => {if (process.env.USER_TYPE !== 'ant') {returnnull// 非内部用户直接跳过  }const namespacePath ='/var/run/secrets/kubernetes.io/serviceaccount/namespace'const content = await readFile(namespacePath, { encoding: 'utf8' })return content.trim()})exportconst getContainerId = memoize(async () => {if (process.env.USER_TYPE !== 'ant') {returnnull  }const containerIdPath = '/proc/self/mountinfo'// 匹配 Docker 和 containerd/CRI-O 容器 IDconst containerIdPattern =/(?:\/docker\/containers\/|\/sandboxes\/)([0-9a-f]{64})/// ... 从 mountinfo 中提取容器 ID})

所有这些都会被记录到一个内部遥测事件里:

exportasyncfunctionlogPermissionContextForAnts(  toolPermissionContext,  moment,{if (process.env.USER_TYPE !== 'ant'returnvoid logEvent('tengu_internal_record_permission_context', {namespaceawait getKubernetesNamespace(),    toolPermissionContext: jsonStringify(toolPermissionContext),    containerId: await getContainerId(),  })}

这意味着在内部版本中,行为可以被追溯到一个非常具体的部署环境——哪个 Kubernetes 命名空间、哪个容器、什么权限上下文。


总结

把上面的发现串起来:

维度
做了什么
语言情绪
正则匹配关键词,实时分类 negative / keep going
UI 交互
权限弹窗的犹豫、反馈输入、Escape 按键全部追踪
反馈收集
精细的概率 + 冷却期控制,可收集完整对话日志
隐藏命令
ultrathink / ultraplan / ultrareview 改变行为
环境指纹
session ID、容器 ID、repo hash、CI 环境全量采集
内部模式
ant 用户额外采集 K8s 命名空间和容器 ID

不只是一个聊天机器人

这是一个高度插桩的系统,在观察你如何与它互动。

我不是说这里面有什么恶意。但当你读完源码,你会发现这个系统的可观测性和可度量性,远超大多数用户的预期。

大多数人永远不会看到这一层。

如果你在日常工作中用 Claude Code,了解引擎盖下在发生什么,总是值得的。


你怎么看?这是大规模产品的正常遥测,还是过度插桩?


如果你想一起研究 Claude Code 源码,欢迎加入我的社群,后台私信”社群”