编程思想要点:编程正在从"告诉计算机怎么做"演进为"告诉计算机做什么",而 Claude Code 正是这场革命的活化石和催化剂。
1.1 编程思想的三次浪潮
让我们从一个简单的问题开始:当你在终端里输入 claude 并按下回车时,究竟发生了什么?
这个问题看似简单,但它的答案横跨了七十年的编程思想史。从第一台电子计算机 ENIAC 的诞生到今天你手中的 AI 编程助手,编程经历了三次根本性的范式转换。理解这三次浪潮,是理解 Claude Code 的前提。
1.1.1 第一次浪潮:指令式编程——控制机器
1945年,冯·诺依曼架构奠定了现代计算机的基础。程序员通过穿孔卡片、汇编语言,最终是高级语言来告诉计算机一步步做什么。
// 第一次浪潮的典型思维:逐步指令
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 带来了对象式编程革命。
// 第二次浪潮的典型思维:对象封装与消息传递
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)的崛起带来了第三次范式转换。这一次,变革的不是语法,而是谁在做决策。
# 第三次浪潮的典型思维:意图描述
$ 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 的第一行:
#!/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 无法替代的优势:
- 可编程性:CLI 可以被管道、脚本、cron 任务、CI/CD 流水线调用。
claude "review this PR" --output json可以嵌入任何自动化流程。 - 上下文连续性:在终端中,Claude Code 天然拥有文件系统的访问权限、Git 状态、环境变量——这些是理解"项目"的基础上下文。
- 无干扰性:CLI 没有多余的 UI 元素,用户与 AI 的交互集中在文本层面,这正是 LLM 最擅长的模态。
终端作为 Agent 的原生栖息地
更深层的思考是:终端本身就是一种"Agent 协议"。当你输入 git commit -m "fix auth bug" 时,你在向 git 这个"Agent"发出一个指令,它理解你的意图,执行相应的操作,返回结果。Claude Code 只是将这个范式扩展到了一个更强大的"Agent"——一个理解自然语言、能自主规划、能使用多种工具的通用 Agent。
从源码中可以看到,Claude Code 使用了 Commander.js 作为 CLI 框架:
// 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 读出的真相
{
"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 哲学一脉相承:ls、grep、awk——每个都是一个自包含的可执行文件。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 中有一个引人注目的字段:
"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 的产品定位体现了三个工程哲学:
- 最小入口原则:一个命令
claude,一个入口文件cli.js,用户体验的起始成本为零。 - 自包含性原则:零外部依赖,一个文件就是完整系统,不存在版本冲突或依赖地狱。
- 原生栖境原则:终端是开发者的原生环境,CLI 工具与开发者工作流天然融合。
1.3 Agent 编程的核心特征
如果用一句话概括 Agent 编程与之前所有编程范式的区别,那就是:Agent 编程将"决策"从程序员转移到了 AI。
这不是一个简单的转移,而是引发了一系列根本性的变化。让我们通过 Claude Code 的源码来理解这些变化。
1.3.1 自主决策:从预设分支到动态推理
传统编程中,程序的行为由 if-else 预定义:
# 传统方式:程序员预设所有分支
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 根据理解来动态选择行为:
// 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, ExitWorktreeAgent 不是通过 switch-case 来选择工具,而是通过推理来决定使用哪些工具、以什么顺序、什么参数。这个决策过程是不可预测的、上下文相关的、并且可能随着对话的推进而调整。
源码透视:工具注册表
Claude Code 的 sdk-tools.d.ts 文件定义了所有工具的类型签名。这是一种"契约式"设计——它告诉 AI 每个工具的输入输出格式,但不规定何时使用它们。
export type ToolInputSchemas =
| AgentInput // 子 Agent 调度
| BashInput // Shell 命令执行
| FileEditInput // 文件编辑
| FileReadInput // 文件读取
| GlobInput // 文件搜索
| GrepInput // 内容搜索
| TodoWriteInput // 任务管理
| WebFetchInput // 网页抓取
| WebSearchInput // 网络搜索
| AskUserQuestionInput // 用户交互
// ...更多工具每个工具的接口设计都体现了 Agent 思维:
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 上下文感知:从静态参数到动态理解
传统函数通过参数接收上下文:
# 传统方式:上下文必须显式传递
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 linesAgent 通过对话上下文来理解需求。当用户说"把这个函数重构一下"时,Agent 不需要你指定哪个函数(它可以从当前文件上下文中推断)、不需要你指定重构方式(它可以自主选择)、甚至不需要你指定目标语言(它从文件扩展名和项目结构中推断)。
源码透视:会话状态对象 Vj7()
Claude Code 的全局状态对象是理解其上下文感知能力的关键:
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 的核心运行时是一个消息流系统。从源码中可以看到:
// 概念模型(简化)
class MessageStream {
// 事件类型
"message_start" // 消息开始
"content_block_start" // 内容块开始
"content_block_delta" // 内容块增量
"content_block_stop" // 内容块结束
"message_delta" // 消息增量
"message_stop" // 消息结束
// ...
}每一轮对话都是一个完整的流:用户发送消息 → AI 生成响应 → 执行工具 → 获取结果 → 继续生成 → ...直到任务完成。
这种增量式执行要求一种完全不同的错误处理哲学。传统程序在遇到错误时抛出异常;Agent 在遇到错误时调整策略。
从源码中的错误处理可以看到:
// 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.js 和 sdk-tools.d.ts)。这本书的每一个论断都有源码支撑——你可以随时验证、质疑、或者发现我遗漏的东西。
源码准备
要跟随本书的源码分析,你需要:
- 安装 Claude Code:
npm install -g @anthropic-ai/claude-code@2.1.88 - 找到安装路径:
which claude或npm root -g - 阅读入口文件:
cli.js(12.44MB,minified) - 参考类型定义:
sdk-tools.d.ts(114KB,25+ 工具定义)
虽然源码是 minified 的,但通过本书的引导,你会学会如何在混淆的代码中找到关键结构。这种能力本身就是理解复杂系统的基础技能。
小结
本章我们从历史的视角理解了编程思想的演进:
- 第一次浪潮(指令式):控制机器——食谱隐喻,完备性思维
- 第二次浪潮(对象式):组织复杂性——城市隐喻,分工思维
- 第三次浪潮(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 写出一个接口定义草案。