Skip to content

编程思想要点:编程正在从"告诉计算机怎么做"演进为"告诉计算机做什么",而 Claude Code 正是这场革命的活化石和催化剂。

1.1 编程思想的三次浪潮

让我们从一个简单的问题开始:当你在终端里输入 claude 并按下回车时,究竟发生了什么?

这个问题看似简单,但它的答案横跨了七十年的编程思想史。从第一台电子计算机 ENIAC 的诞生到今天你手中的 AI 编程助手,编程经历了三次根本性的范式转换。理解这三次浪潮,是理解 Claude Code 的前提。

1.1.1 第一次浪潮:指令式编程——控制机器

1945年,冯·诺依曼架构奠定了现代计算机的基础。程序员通过穿孔卡片、汇编语言,最终是高级语言来告诉计算机一步步做什么

c
// 第一次浪潮的典型思维:逐步指令
int main() {
    int a = 5;
    int b = 10;
    int sum = a + b;
    printf("Sum = %d\n", sum);
    return 0;
}

这个时代的核心隐喻是食谱。程序员是厨师,计算机是忠实的帮手,而代码是一份精确到每一步操作的菜谱。if-else 是"如果蛋不新鲜就用牛奶替代",for 循环是"重复搅拌三次"。

这种范式的力量在于其确定性——给定相同的输入,程序总是产生相同的输出。它的局限在于,你必须提前知道所有可能的路径。

Fortran、C、Pascal——这些语言都是这个时代的产物。它们让人类得以用更接近数学的方式指挥机器,但本质上,程序员仍然在以机器的视角思考问题。

设计思想:指令式编程的核心假设是"程序员全知"。你必须预见每一条执行路径、处理每一个边界条件、应对每一个错误。这是一种完备性思维——好的程序就是覆盖了所有情况的程序。

1.1.2 第二次浪潮:对象式编程——组织复杂性

到了1970年代,随着软件系统变得越来越大、越来越复杂,指令式编程的"意大利面条代码"问题日益严重。Simula 67、Smalltalk,最终是 C++ 和 Java 带来了对象式编程革命。

cpp
// 第二次浪潮的典型思维:对象封装与消息传递
class FileSystem {
private:
    std::vector<File> files;
    
public:
    void createFile(const std::string& name, const std::string& content);
    File readFile(const std::string& name);
    void deleteFile(const std::string& name);
};

// 使用者不需要知道文件系统如何存储文件
FileSystem fs;
fs.createFile("hello.txt", "Hello, World!");

这个时代的核心隐喻从食谱变成了城市。程序员不再直接操作每一个细节,而是设计一个个"建筑"(对象),它们通过"街道"(接口)相互连接。封装是城墙,继承是家族谱系,多态是外交协议。

面向对象编程(OOP)的核心贡献不是语法,而是一种组织思维——它教会我们如何通过抽象来管理复杂性。你不需要理解发动机的内部构造就能开车,同理,你不需要理解 FileSystem 的内部实现就能使用它。

Bruce Eckel 在《C++编程思想》中精辟地指出:"对象式编程让我们从'怎么做'走向了'谁来做'。"

设计思想:对象式编程的核心假设是"世界由对象组成"。好的设计就是找到正确的抽象边界,让每个对象只关心自己的职责。这是一种分工思维——将复杂系统拆分为可管理的协作单元。

1.1.3 第三次浪潮:Agent式编程——意图驱动的自主行为

2020年代,大语言模型(LLM)的崛起带来了第三次范式转换。这一次,变革的不是语法,而是谁在做决策

bash
# 第三次浪潮的典型思维:意图描述
$ claude "帮我重构这个项目的认证模块,从 Session 切换到 JWT"

你不再写 createFile()readFile()——你告诉 Agent 你想要什么,它自己决定怎么做。这就是 Agent 式编程的核心区别:

维度指令式对象式Agent式
决策者程序员程序员AI Agent
表达方式步骤序列对象交互自然语言意图
执行模型确定性多态分派自主规划+执行
错误处理预定义逻辑异常层次自适应策略
复杂性管理分解封装上下文感知

这个时代的核心隐喻从城市变成了公司。程序员变成了 CEO(或者更准确地说,是产品经理),而 Claude 是你雇来的工程师团队。你通过自然语言描述需求,AI Agent 自主规划任务、选择工具、执行操作、处理错误,并在需要时向你请示。

设计思想:Agent 式编程的核心假设是"意图可以编码为智能行为"。好的 Agent 系统不是覆盖所有情况,而是理解意图并在不确定中做出合理决策。这是一种涌现思维——从简单规则中产生复杂行为,从自然语言中涌现工程实践。

源码透视:Claude Code 中的三次浪潮共存

最有趣的是,Claude Code 的源码本身就是三次浪潮共存的活化石。在它的 16,668 行代码中:

  • 指令式编程的痕迹随处可见:条件判断、循环、错误处理。Vj7() 工厂函数中的每一行都是经典的指令式代码。
  • 对象式编程的结构清晰可见:MessageStream 类(事件流管理)、APIError 层次体系(400/401/403/404/429/5xx 的完整错误分类)。
  • Agent式编程则是它的灵魂:Claude 通过自然语言理解用户意图,自主选择 FileRead、FileEdit、Bash、Agent 等 25+ 种工具来完成任务。

这种共存不是偶然的。每一次浪潮都没有消灭前一次浪潮——指令式编程是对象式编程的基础,对象式编程是 Agent 式编程的基础。Claude Code 的 Agent 能力建立在精心设计的对象抽象之上,而对象抽象又建立在精确的指令式逻辑之上。

1.2 Claude Code 的产品定位

1.2.1 为什么是 CLI?

在 GUI 王道的时代,Anthropic 选择将 Claude Code 定位为命令行工具,这不是怀旧,而是深思熟虑的工程哲学。

打开 cli.js 的第一行:

javascript
#!/usr/bin/env node
// (c) Anthropic PBC. All rights reserved.
// Version: 2.1.88
// Want to see the unminified source? We're hiring!
// https://job-boards.greenhouse.io/anthropic/jobs/4816199008

#!/usr/bin/env node——这个 shebang 行是整个故事的起点。它告诉你,Claude Code 是一个 Node.js 程序,通过命令行启动。但为什么?

CLI 的不可替代性

命令行接口(CLI)有三个 GUI 无法替代的优势:

  1. 可编程性:CLI 可以被管道、脚本、cron 任务、CI/CD 流水线调用。claude "review this PR" --output json 可以嵌入任何自动化流程。
  2. 上下文连续性:在终端中,Claude Code 天然拥有文件系统的访问权限、Git 状态、环境变量——这些是理解"项目"的基础上下文。
  3. 无干扰性:CLI 没有多余的 UI 元素,用户与 AI 的交互集中在文本层面,这正是 LLM 最擅长的模态。

终端作为 Agent 的原生栖息地

更深层的思考是:终端本身就是一种"Agent 协议"。当你输入 git commit -m "fix auth bug" 时,你在向 git 这个"Agent"发出一个指令,它理解你的意图,执行相应的操作,返回结果。Claude Code 只是将这个范式扩展到了一个更强大的"Agent"——一个理解自然语言、能自主规划、能使用多种工具的通用 Agent。

从源码中可以看到,Claude Code 使用了 Commander.js 作为 CLI 框架:

javascript
// cli.js 内嵌的 Commander.js 模式
program
  .name("claude")
  .version("2.1.88")
  .description("Use Claude, Anthropic's AI assistant, right from your terminal")
  .command("mcp")
  .command("auth")
  .command("plugin")
  .command("agents")
  .command("auto-mode")
  // ...更多子命令

Commander.js 本身就是命令行哲学的最佳诠释——它让命令的组织和解析变得像定义对象一样优雅。

1.2.2 12MB 单文件的隐喻

cli.js 有 12.44MB,16,668 行——这是一个单文件 Bundle。

在模块化、微服务盛行的今天,为什么要将一切打包成单个文件?这是一个值得深思的工程决策。

从 package.json 读出的真相

json
{
  "name": "@anthropic-ai/claude-code",
  "version": "2.1.88",
  "bin": { "claude": "cli.js" },
  "engines": { "node": ">=18.0.0" },
  "type": "module",
  "dependencies": {},
  "optionalDependencies": {
    "@img/sharp-darwin-arm64": "^0.34.2",
    "@img/sharp-linux-arm64": "^0.34.2"
  }
}

注意 dependencies 是空对象 {}。零运行时依赖。所有依赖——lodash、Commander.js、chalk、@anthropic-ai/sdk——全部打包进了 cli.js

这意味着什么?自包含性。一个文件就是一个完整的、可运行的系统。

这与 Unix 哲学一脉相承:lsgrepawk——每个都是一个自包含的可执行文件。Claude Code 将自己定位为这个传统的一部分,只不过它是一个能理解自然语言的"超级 grep"。

Bundle 的工程美学

从源码结构来看,这个 Bundle 不是简单的拼接,而是一个精心设计的模块系统。通过 var y = (q, K) => () => (q && (K = q(q = 0)), K) 这样的惰性初始化模式,代码实现了模块的延迟加载。每个模块只在实际需要时才初始化,避免了启动时的性能损耗。

这种"看起来是一个文件,实际上是一个完整系统"的设计,是分布式系统的微缩版。正如一个操作系统的内核可以编译成一个二进制文件,Claude Code 将整个 AI 编程助手的复杂性压缩到了一个 CLI 入口。

12MB 背后的技术决策

12.44MB 对于一个 CLI 工具来说很大吗?让我们做个对比:

  • Git for Windows: ~250MB
  • Docker CLI: ~60MB
  • Homebrew: ~1MB (但依赖 Ruby)
  • Node.js: ~80MB

Claude Code 的 12MB 包含了完整的 LLM SDK、文本处理库、终端 UI 库、文件系统操作库、MCP 协议栈——一个完整的 AI Agent 运行时。从这个角度看,12MB 是一个了不起的工程成就。

1.2.3 开源哲学

package.json 中有一个引人注目的字段:

json
"scripts": {
  "prepare": "node -e \"if (!process.env.AUTHORIZED) { console.error('ERROR: Direct publishing is not allowed.'); process.exit(1); }\""
}

这行脚本确保只有经过授权的构建流程才能发布这个包。这是 Anthropic 在开源与控制之间找到的平衡——代码是可审计的(你可以阅读 cli.js),但发布流程是受控的。

更深层的信号是那句注释:"Want to see the unminified source? We're hiring!"。Anthropic 在用一种巧妙的方式表达:这不是传统意义上的开源,但我们欢迎有能力阅读和理解这份代码的人加入。

设计思想

Claude Code 的产品定位体现了三个工程哲学:

  1. 最小入口原则:一个命令 claude,一个入口文件 cli.js,用户体验的起始成本为零。
  2. 自包含性原则:零外部依赖,一个文件就是完整系统,不存在版本冲突或依赖地狱。
  3. 原生栖境原则:终端是开发者的原生环境,CLI 工具与开发者工作流天然融合。

1.3 Agent 编程的核心特征

如果用一句话概括 Agent 编程与之前所有编程范式的区别,那就是:Agent 编程将"决策"从程序员转移到了 AI

这不是一个简单的转移,而是引发了一系列根本性的变化。让我们通过 Claude Code 的源码来理解这些变化。

1.3.1 自主决策:从预设分支到动态推理

传统编程中,程序的行为由 if-else 预定义:

python
# 传统方式:程序员预设所有分支
def handle_user_request(request_type, params):
    if request_type == "create_file":
        create_file(params["path"], params["content"])
    elif request_type == "edit_file":
        edit_file(params["path"], params["old"], params["new"])
    elif request_type == "search":
        search(params["pattern"], params["path"])
    # ... 更多分支

在 Agent 编程中,AI 根据理解来动态选择行为:

javascript
// Claude Code 中 Agent 的工具选择机制(概念模型)
// AI 看到用户说"把所有 .js 文件中的 console.log 替换掉"
// 自主决定:
// 1. 先用 Glob 工具找到所有 .js 文件
// 2. 对每个文件用 Grep 搜索 console.log
// 3. 对匹配的行用 FileEdit 替换
// 这是一个多步骤规划,不是预设的代码路径

从源码中,我们可以看到 Claude Code 为 Agent 提供了 25+ 种工具:

Agent, Bash, TaskOutput, ExitPlanMode, FileEdit, FileRead, 
FileWrite, Glob, Grep, TaskStop, ListMcpResources, Mcp, 
NotebookEdit, ReadMcpResource, TodoWrite, WebFetch, WebSearch,
AskUserQuestion, Config, EnterWorktree, ExitWorktree

Agent 不是通过 switch-case 来选择工具,而是通过推理来决定使用哪些工具、以什么顺序、什么参数。这个决策过程是不可预测的、上下文相关的、并且可能随着对话的推进而调整。

源码透视:工具注册表

Claude Code 的 sdk-tools.d.ts 文件定义了所有工具的类型签名。这是一种"契约式"设计——它告诉 AI 每个工具的输入输出格式,但不规定何时使用它们。

typescript
export type ToolInputSchemas =
  | AgentInput      // 子 Agent 调度
  | BashInput       // Shell 命令执行
  | FileEditInput   // 文件编辑
  | FileReadInput   // 文件读取
  | GlobInput       // 文件搜索
  | GrepInput       // 内容搜索
  | TodoWriteInput  // 任务管理
  | WebFetchInput   // 网页抓取
  | WebSearchInput  // 网络搜索
  | AskUserQuestionInput  // 用户交互
  // ...更多工具

每个工具的接口设计都体现了 Agent 思维:

typescript
export interface AgentInput {
  description: string;    // 3-5 词的任务描述
  prompt: string;         // 任务的具体指令
  subagent_type?: string; // 可选的专业 Agent 类型
  model?: "sonnet" | "opus" | "haiku";  // 模型选择
  run_in_background?: boolean;  // 后台运行
  isolation?: "worktree";  // 工作树隔离
}

注意这些设计细节:

  • description 是给"人类"看的摘要,让用户知道 Agent 在做什么
  • isolation?: "worktree" 是一个高级特性——子 Agent 在独立的 Git worktree 中工作,不会影响主分支
  • model?: "sonnet" | "opus" | "haiku" 允许为不同任务选择不同能力的模型

这不是传统的 API 设计。传统 API 设计者会定义 executeAgent(type, config) 这样的函数。而 Claude Code 的设计者定义的是一个能力集——告诉 AI "你可以做什么",让 AI 自己决定"做什么"。

1.3.2 上下文感知:从静态参数到动态理解

传统函数通过参数接收上下文:

python
# 传统方式:上下文必须显式传递
def process_file(file_path, encoding="utf-8", line_start=0, line_end=None):
    content = read(file_path, encoding)
    lines = content.split("\n")[line_start:line_end]
    return lines

Agent 通过对话上下文来理解需求。当用户说"把这个函数重构一下"时,Agent 不需要你指定哪个函数(它可以从当前文件上下文中推断)、不需要你指定重构方式(它可以自主选择)、甚至不需要你指定目标语言(它从文件扩展名和项目结构中推断)。

源码透视:会话状态对象 Vj7()

Claude Code 的全局状态对象是理解其上下文感知能力的关键:

javascript
function Vj7() {
  let q = "";
  // 获取当前工作目录
  if (typeof process !== "undefined" && typeof process.cwd === "function") {
    let _ = process.cwd();
    try { q = realpathSync(_).normalize("NFC"); }
    catch { q = _.normalize("NFC"); }
  }
  
  return {
    originalCwd: q,                    // 原始工作目录
    projectRoot: q,                    // 项目根目录
    totalCostUSD: 0,                   // 总花费
    totalAPIDuration: 0,               // API 调用总时长
    totalToolDuration: 0,              // 工具调用总时长
    sessionId: randomUUID(),           // 会话 ID
    modelUsage: {},                    // 模型使用统计
    isInteractive: false,              // 是否交互模式
    startTime: Date.now(),             // 会话开始时间
    totalLinesAdded: 0,                // 新增行数
    totalLinesRemoved: 0,              // 删除行数
    sessionCronTasks: [],              // 定时任务
    sessionCreatedTeams: new Set,      // 创建的团队
    invokedSkills: new Map,            // 调用的技能
    systemPromptSectionCache: new Map, // 系统提示缓存
    promptCache1hEligible: null,       // 1小时提示缓存
    promptCache1hAllowlist: null,      // 缓存白名单
    // ...100+ 更多字段
  };
}

这个对象有 100+ 个字段,涵盖了会话的方方面面。它是 Agent 的"记忆"——在多轮对话中,这些状态不断更新,为 Agent 提供持续的上下文。

特别注意几个有趣的状态字段:

  • modelUsage: {}:记录每个模型的 token 消耗,支持多模型协同
  • invokedSkills: new Map:记录已调用的技能,避免重复加载
  • sessionCreatedTeams: new Set:支持多 Agent 协作
  • systemPromptSectionCache: new Map:系统提示的缓存,避免重复计算
  • promptCache1hEligible / promptCache1hAllowlist:Anthropic 的 Prompt Cache 机制,1小时缓存窗口

1.3.3 增量执行:从一次性运行到持续交互

传统程序通常是一次性运行的:编译、运行、输出结果、结束。

Agent 编程是增量的:每一步操作都基于上一步的结果,整个过程是一个持续的交互循环。

源码透视:消息流 (MessageStream)

Claude Code 的核心运行时是一个消息流系统。从源码中可以看到:

javascript
// 概念模型(简化)
class MessageStream {
  // 事件类型
  "message_start"     // 消息开始
  "content_block_start" // 内容块开始
  "content_block_delta" // 内容块增量
  "content_block_stop"  // 内容块结束
  "message_delta"       // 消息增量
  "message_stop"        // 消息结束
  // ...
}

每一轮对话都是一个完整的流:用户发送消息 → AI 生成响应 → 执行工具 → 获取结果 → 继续生成 → ...直到任务完成。

这种增量式执行要求一种完全不同的错误处理哲学。传统程序在遇到错误时抛出异常;Agent 在遇到错误时调整策略。

从源码中的错误处理可以看到:

javascript
// Anthropic SDK 的错误层次体系
class APIError extends Error {
  constructor(status, error, message, headers) { /*...*/ }
  static generate(status, error, message, headers) {
    if (status === 400) return new BadRequestError(/*...*/);
    if (status === 401) return new AuthenticationError(/*...*/);
    if (status === 429) return new RateLimitError(/*...*/);
    if (status >= 500) return new InternalServerError(/*...*/);
    // ...
  }
}

Agent 的错误处理不是简单的 try-catch,而是一个策略网络——根据错误类型调整后续行为。

1.4 本书阅读路线图

本书共分为三卷,每卷对应一个理解层次:

卷一:认知篇(第1-3章)——你现在正在读的部分

  • 第1章:编程思想的演进历史,以及 Agent 编程的本质特征
  • 第2章:Claude Code 的全景架构——12MB 单文件中的工程美学
  • 第3章:Agent 编程思维——超越传统编程范式的思维模型

卷二:实现篇(第4-7章)

深入 Claude Code 的核心实现机制:工具系统、会话管理、MCP 协议、权限模型等。

卷三:实践篇(第8-10章)

从 Claude Code 的设计哲学出发,探讨如何构建自己的 Agent 系统。

阅读建议

如果你是经验丰富的工程师,建议按顺序阅读——理解"为什么"比理解"怎么做"更重要。

如果你是学生或初学者,可以先跳到卷三看实践案例,遇到不理解的地方再回到前面的章节。

无论哪种阅读路径,我都建议你同时打开 Claude Code 的源码(cli.jssdk-tools.d.ts)。这本书的每一个论断都有源码支撑——你可以随时验证、质疑、或者发现我遗漏的东西。

源码准备

要跟随本书的源码分析,你需要:

  1. 安装 Claude Code:npm install -g @anthropic-ai/claude-code@2.1.88
  2. 找到安装路径:which claudenpm root -g
  3. 阅读入口文件:cli.js(12.44MB,minified)
  4. 参考类型定义:sdk-tools.d.ts(114KB,25+ 工具定义)

虽然源码是 minified 的,但通过本书的引导,你会学会如何在混淆的代码中找到关键结构。这种能力本身就是理解复杂系统的基础技能。

小结

本章我们从历史的视角理解了编程思想的演进:

  1. 第一次浪潮(指令式):控制机器——食谱隐喻,完备性思维
  2. 第二次浪潮(对象式):组织复杂性——城市隐喻,分工思维
  3. 第三次浪潮(Agent式):意图驱动的自主行为——公司隐喻,涌现思维

Claude Code 作为第三次浪潮的代表性产品,具有三个核心特征:

  • 自主决策:AI 根据推理选择工具和策略
  • 上下文感知:通过 100+ 字段的全局状态理解项目
  • 增量执行:持续的交互循环,自适应错误处理

在下一章中,我们将深入 Claude Code 的内部架构,看看这 12MB 的单文件中究竟藏着什么。

思考与练习

1. 列举三个你日常使用的 CLI 工具,分析它们分别属于哪次编程浪潮的产品。如果你能选择,你会希望哪个工具具备 Agent 能力?为什么?

2. 阅读你的 ~/.claude/ 目录结构(如果已安装 Claude Code),找出 settings.json 文件,理解其中的配置项与 Vj7() 工厂函数输出的字段之间的对应关系。

★★ 3. 找到 Claude Code 安装路径中的 cli.js,用你喜欢的编辑器打开前 100 行。尝试识别出:(a) 哪些是 Node.js 标准库的导入;(b) 哪些是第三方库的代码;(c) 哪些是 Claude Code 自身的业务逻辑。

★★ 4. 运行 claude --help,列出所有子命令。尝试推断每个子命令的职责,并画出 CLI 命令树的结构图。

★★★ 5. 思考一个假想的场景:你需要在 Claude Code 中实现一个新的工具 DatabaseQuery,用于执行 SQL 查询。你需要修改哪些文件(或哪些模块)?需要定义哪些类型?这个工具的输入输出应该是什么样的?用 TypeScript 写出一个接口定义草案。


第2章:Claude Code 的全景架构 — 12MB 单文件的工程美学

基于 MIT 许可发布