Killer Code

手把手教你逆向Claude Code:如何监控AI每一次'内心独白'?

通过技术手段逆向分析Claude Code的API交互过程,揭秘AI编程之王的多模型协作策略、精巧的系统提示词设计和智能工具调用机制。

手把手教你逆向Claude Code:如何监控AI每一次"内心独白"?

来源: 掘金文章
作者: 子昕AI编程
发布时间: 2025-01-15

文章摘要

通过技术手段逆向分析Claude Code的API交互过程,揭秘AI编程之王的多模型协作策略、精巧的系统提示词设计和智能工具调用机制,手把手教你监控AI的每一次"内心独白"。

正文内容

大家好,我是子昕,一个干了10年的后端开发,现在在AI编程这条路上边冲边摸索,每天都被新技术追着跑。

最近几天我发现一个有趣的现象:作为 Claude Pro 订阅用户,我明显感觉到 Claude Code 有点降智了,不如之前那么聪明。

这让我突然想起一个经典问题——我们能否偷看一下 AI 的"小抄"?

作为一个有着强烈好奇心的程序员,我决定对 Claude Code 来一场"开膛破肚"式的逆向分析。毕竟,既然它被誉为"AI编程工具之王",那我就要看看它的王座到底是怎么坐稳的。

经过一番折腾,我发现了 GitHub 上一个对Claude Code进行逆向的项目:claude-code-reverse

github.com/Yuyz0112/claude-code-reverse

这个项目可以让我们实时拦截和分析 Claude Code 与服务器的所有通信,相当于给 AI 装了个窃听器

为什么要逆向 Claude Code?

在开始动手之前,先说说为什么要这么做:

  1. 技术好奇心:Claude Code 凭什么能做到比其他 AI 编程工具更强?
  2. 成本透明度:作为 Pro 用户,我想知道每次对话到底消耗了哪个模型,用了多少 tokens
  3. 学习借鉴:了解顶级 AI Agent 的设计思路,对我们自己开发 AI 应用有巨大价值
  4. 质量监控:当感觉 AI 表现异常时,可以通过日志分析找到原因

准备工作:工具箱清单

在开始这场"技术侦探"之旅前,你需要准备:

  • Claude Code:废话,没有目标怎么逆向
  • Node.js 环境:用于安装 js-beautify
  • 一颗不怕搞坏东西的心:记得备份,万一搞砸了别哭

第一步:定位"目标"

首先要找到 Claude Code 的真身。在命令行执行:

which claude

通常会得到类似这样的结果:

/opt/homebrew/bin/claude

但这只是个"替身"!在 Mac 上,这通常是一个软链接。我们需要找到真正的 cli.js 文件:

ls -l /opt/homebrew/bin/claude

你会看到它指向了真正的安装位置:

/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js

这就是我们要"动手脚"的地方!

第二步:美化代码,让它"可读"

Claude Code 的代码是压缩过的,就像一团乱麻。我们需要先让它变得人类可读:

# 进入 Claude Code 安装目录
cd /opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/

# 备份原文件(这一步很重要!)
mv cli.js cli.bak

# 安装代码美化工具
npm install -g js-beautify

# 美化代码
js-beautify cli.bak > cli.js

现在 cli.js 就变成了格式良好、可读性强的代码。

第三步:植入"间谍代码"

这是整个过程中最关键的一步。我们要在 cli.js 中植入监控代码,让它把所有与 LLM 的对话都记录下来。

3.1 添加基础监控模块

在文件开头 #!/usr/bin/env node 这行之后,添加我们的"间谍模块":

代码植入过程截图

间谍代码如下,直接复制粘贴即可:

// ============= 间谍模块开始 =============
import fs from "fs";
import path from "path";
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const LOG_PATH = path.resolve(__dirname, 'messages.log');

// 每次启动时创建新的日志会话
fs.writeFileSync(
  LOG_PATH,
  `---Session ${new Date()}---\n`
);

function isAsyncIterable(x) { 
  return x && typeof x[Symbol.asyncIterator] === 'function'; 
}

const ts = () => new Date().toISOString();

function uid() {
  return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
}

// 这个函数负责拦截流式响应,记录工具调用的详细信息
function tapIteratorInPlaceWithTools(inner, onFinal) {
  if (!inner) return inner;

  const TAPPED = Symbol.for('anthropic.tap.iterator');
  if (inner[TAPPED]) return inner;
  Object.defineProperty(inner, TAPPED, { value: true, configurable: true });

  const byteLen = s =>
    typeof Buffer !== 'undefined'
      ? Buffer.byteLength(s, 'utf8')
      : new TextEncoder().encode(s).length;

  const makeWrapper = getOrigIter => function() {
    const it = getOrigIter();
    let text = '';

    const open = new Map();
    const done = [];
    const PREVIEW_CAP = Infinity;

    const start = (id, name) => {
      if (id == null || open.has(id)) return;
      open.set(id, { 
        id, 
        name: name||'unknown', 
        startedAt: Date.now(), 
        inputBytes: 0, 
        preview: '' 
      });
    };
    
    const delta = (id, chunk) => {
      if (id == null) return;
      if (!open.has(id)) start(id);
      const rec = open.get(id);
      if (!rec) return;
      const s = typeof chunk==='string' ? chunk : JSON.stringify(chunk||'');
      rec.inputBytes += byteLen(s);
      if (rec.preview.length < PREVIEW_CAP) {
        rec.preview += s.slice(0, PREVIEW_CAP - rec.preview.length);
      }
    };
    
    const stop = id => {
      const rec = open.get(id);
      if (!rec) return;
      open.delete(id);
      const finishedAt = Date.now();
      done.push({ 
        ...rec, 
        finishedAt, 
        durationMs: finishedAt - rec.startedAt 
      });
    };
    
    const finalizeDangling = err => {
      for (const rec of open.values()) {
        done.push({
          ...rec,
          finishedAt: Date.now(),
          durationMs: Date.now() - rec.startedAt,
          errored: err ? (err.stack||String(err)) : undefined
        });
      }
      open.clear();
    };

    return (async function*() {
      try {
        for await (const ev of it) {
          // 记录每个事件
          const logEntry = {
            timestamp: ts(),
            event: ev,
            type: ev.type || 'unknown'
          };
          
          fs.appendFileSync(LOG_PATH, JSON.stringify(logEntry, null, 2) + '\n');
          
          yield ev;
        }
      } catch (err) {
        finalizeDangling(err);
        throw err;
      }
    })();
  };

  return makeWrapper(() => inner);
}
// ============= 间谍模块结束 =============

3.2 找到正确的注入点

现在我们需要找到在 cli.js 中注入监控代码的位置。搜索处理 API 调用的函数,寻找类似这样的模式:

async function makeRequest(options) {
  // ... 现有代码
}

在 API 调用之前插入我们的间谍代码:

// 在 API 调用之前添加监控
const monitoredResponse = tapIteratorInPlaceWithTools(response, () => {
  // 记录最终结果
  fs.appendFileSync(LOG_PATH, `---Session End ${new Date()}---\n\n`);
});

第四步:测试我们的"窃听器"

现在让我们测试监控系统:

# 启动 Claude Code
claude

# 问一个简单的问题
Describe the project structure

你应该会在 Claude Code 安装目录中看到一个 messages.log 文件生成。这个文件包含了所有被拦截的通信!

第五步:分析结果

打开日志文件,你会看到关于以下内容的详细信息:

  1. API 请求/响应:完整的 HTTP 通信
  2. 模型选择:每个任务使用了哪个模型
  3. Token 使用:详细的 token 消耗
  4. 工具调用:所有工具调用及其参数
  5. 系统提示词:指导 Claude Code 行为的复杂提示词

关键发现

1. 多模型协作策略

Claude Code 并不只使用一个模型。它会智能地为不同任务选择不同的模型:

  • Opus:用于复杂推理和代码生成
  • Sonnet:用于常规任务和简单查询
  • Haiku:用于快速响应和基本操作

2. 精巧的系统提示词设计

系统提示词非常详细且精心设计,涵盖:

  • 角色定义:Claude Code 能力的明确定义
  • 工具使用指南:每个工具的详细说明
  • 错误处理:全面的错误恢复策略
  • 上下文管理:智能的上下文压缩和保留

3. 智能的上下文管理

当对话变长时,Claude Code 会自动压缩历史上下文,保留关键信息的同时节省 token 消耗。这个功能通过专门的压缩提示词实现。

4. 工具调用的精细化设计

Claude Code 定义了丰富的工具集,包括:

  • 文件系统操作(读取、写入、搜索)
  • 代码执行(Bash、Python 等)
  • 任务管理(TodoWrite)
  • IDE 集成工具
  • 子代理系统(Task)

实际应用:解决"降智"问题

通过日志分析,我发现我感觉到的"降智"现象可能有几个原因:

  1. 模型选择策略变化:可能是为了控制成本,某些任务改用了较轻量的模型
  2. 上下文压缩过于激进:重要信息在压缩过程中丢失
  3. 工具调用链过长:复杂任务的多步骤推理被分散到多个工具调用中

总结

通过这次"技术侦探"之旅,我不仅了解了 Claude Code 强大秘密的冰山一角,还学到了一些 AI Agent 设计的宝贵经验:

  1. 多模型协作比单一模型更高效
  2. 精心设计的系统提示词是关键
  3. 工具系统的丰富性决定了能力上限
  4. 上下文管理策略影响对话质量

这只是开始

需要说明的是,这次分析只是通过一个简单的"描述项目结构"需求,初步了解了如何进行逆向分析。Claude Code 的真正实力远不止于此!

后面我计划通过更复杂的场景来深入挖掘:

  • 复杂编程任务:看它如何处理多文件重构、架构设计等高难度任务
  • 性能优化场景:分析它如何进行代码审查和性能调优
  • 调试和问题解决:观察它的错误诊断和修复策略
  • 项目搭建全流程:从零开始创建一个完整项目的思维过程
  • Sub-Agent 系统:深入了解它的多智能体协作机制

每一个场景都会揭示 Claude Code 更深层的设计哲学和技术细节。如果你对某个特定场景特别感兴趣,欢迎留言告诉我!

如果你也想探索 AI 的内心世界,不妨试试这个方法。记住,好奇心是程序员最宝贵的品质!

相关图片

文章包含多张截图,展示了逆向分析的过程和结果:

  1. 代码植入过程截图
  2. API 拦截代码示例
  3. 监控日志文件生成
  4. 可视化分析界面
  5. 多模型协作流程图
  6. 工具调用序列图
  7. 系统提示词详细内容

这些图片详细展示了整个逆向分析的技术细节和发现结果。


本文基于掘金的子昕AI编程的原创作品。你可以在 https://juejin.cn/post/7535400490835656740 找到原文。