Linguista

Claude Code 是如何构建的「Rosetta」

独家揭秘:这款热门新开发工具的构建过程,及其对 AI 软件开发的未来意义。

自 5 月份全面开放(GA)以来,Claude Code 席卷了开发者世界。该工具目前的年化营收(ARR)已超过 5 亿美元,且自发布以来的三个月内,使用量暴增了 10 倍以上。

最近,我与 Claude Code 核心团队的三位成员坐下来聊了聊:Boris Cherny(提出最初原型创意的工程师,也是该项目的创始工程师)、Sid Bidasaria(Claude Code 的 2 号工程师,Claude Code 子智能体/Subagents 的创造者),以及创始产品经理 Cat Wu

我了解到了 Claude Code 是如何构建的,并洞察了一个成功的“AI 优先(AI-first)工程团队”是如何运作的;这就像是透过水晶球,窥探了未来快速发展的初创公司可能的运作模式。好消息是,软件工程师在这个未来中似乎非常抢手……

今天,我们将涵盖以下内容:

  1. 一切的起源。 Claude Code 的想法源于一个命令行工具,工程师最初用它通过 Claude 来显示自己在工作时听的音乐。在获得文件系统访问权限后,这个工具在 Anthropic 内部像野火一样蔓延。如今,Claude Code 已经拥有了一个编制齐全的团队。
  2. 技术栈与架构。 TypeScript, React, Ink, Yoga 和 Bun。选择这些技术栈是为了确保留在“分布内(on distribution)”,并发挥模型的长处。趣闻:Claude Code 90% 的代码是由它自己编写的!
  3. 以天为单位(而非周)构建和发布功能。 团队以极快的速度工作,每位工程师每天大约发布 5 个版本。原型设计的速度惊人地快:我们会为一个新功能快速迭代 10 个以上的实际原型。看起来 AI Agent(智能体)确实加速了迭代过程。
  4. 重新设计终端用户体验(UX)。 Claude Code 为终端用户体验带来了许多创新功能,在我们需要与 LLM 驱动的终端交互之前,这些功能是不被需要的。我们将一探究竟。
  5. “AI 优先”的软件工程是什么样的。 使用 AI Agent 进行代码审查和测试、测试驱动开发(TDD)的复兴、自动化事故响应以及谨慎使用功能开关(Feature flags)。这是否预示着未来“AI 优先”工程团队的工作方式?
  6. 构建子智能体(Subagents)。 详细介绍子智能体功能是如何在短短三天内构建完成的——其中两天的成果还被废弃了。
  7. AI 辅助工程团队的未来? 工程团队可以从 Anthropic 的运作方式中学到什么,以及需要注意哪些可能不太容易复制的独特之处。

1. 一切的起源

Boris Cherny 于 2024 年 9 月加入 Anthropic,并开始使用 Claude 3.6 模型构建一系列不同的原型。当时,他想更熟悉 Anthropic 的公共 API。Boris 回忆那段时期:

“我开始在终端里胡乱捣鼓使用 Claude。第一个版本非常简陋:它不能读取文件,不能使用 bash,也根本做不了任何工程方面的事情。但它可以与计算机交互。

我把这个原型连接到了 AppleScript 上:它可以告诉我工作时正在听什么音乐。然后它还可以根据我的输入切换播放的音乐。

这是一个很酷的演示,但并不那么有趣。”

与此同时,Cat Wu 正在研究 AI Agent 的计算机使用情况,以及由此产生的新能力。在与 Cat 交谈后,Boris 萌生了一个想法:赋予终端比仅仅使用 AppleScript 更多的能力。他说:

“我尝试给它一些工具来与文件系统和批处理命令(batch)交互;它可以读取文件、写入文件并运行批处理命令。

突然间,这个 Agent 变得非常有趣。我在我们的代码库中运行它,并开始向它提问。Claude 随后开始探索文件系统并读取文件。它会读取一个文件,查看导入(imports),然后读取导入中定义的文件!它继续这样做,直到找到满意的答案。Claude 探索文件系统的能力让我大为震撼,因为我从未见过这样的工具。

在 AI 领域,我们经常谈论‘产品滞后(product overhang)’,这就是我们在原型中发现的东西。 产品滞后意味着模型有能力做某件特定的事情,但运行 AI 的产品并没有以捕捉这种能力的方式构建。我发现 Claude 探索文件系统的能力就是纯粹的‘产品滞后’。模型已经可以做到这一点,但当时还没有围绕这一能力构建的产品!”

产品与市场的契合(Product-market fit)

Boris 开始每天在工作中使用他的原型。随后他将其分享给了后来的 Claude Code 核心团队,同事们也开始每天使用它。

Boris 和 Claude Code 团队在 2024 年 11 月发布了一个可供内部试用(dogfooding)的版本——距离第一个原型仅两个月。第一天,大约 20% 的工程团队使用了它;到第五天,50% 的工程团队都在使用 Claude Code。那时,Boris 非常确信 Claude Code 在外部世界也会大受欢迎。

但在内部,就是否发布该工具存在争论,有人认为应将其保留供内部使用。Boris 回忆道:

“实际上我们甚至不确定是否要公开发布 Claude Code,因为我们认为它可能成为我们的竞争优势,就像我们的‘独门秘籍’:如果它能给我们带来优势,为什么要发布呢?

最终,我们得出了这样的结论:

所以,通过发布这个工具,我们可以学到更多关于模型安全性和能力的知识。”

组建 Claude Code 工程团队

最初,团队只有 Boris 一人,直到 11 月,Sid Bidasaria 加入 Anthropic 并看到了 Claude Code 的早期版本。他喜欢这个想法并加入了 Boris 的项目。

这个两人团队的工作方式非常自由。Sid 告诉我:

“我们要做的绝大部分工作就是极快地构建原型,并打造能展示底层模型强大能力的产品。团队内部没有正式的流程:一切都超级流畅。我们可以研究几乎任何我们想要的东西,所以我们就不断选择最有希望的想法。”

到 7 月份,团队增长到大约 10名工程师,并且招聘仍在继续。如今,它已是一个拥有工程师、产品管理、设计和数据科学人员的成熟产品团队——而且他们还在招聘

Claude Code 不仅仅适用于程序员

如今,Anthropic 超过 80% 编写代码的工程师日常都在使用 Claude Code,但使用它的不仅仅是他们。Boris 说:

“我走进办公室,瞥见一位数据科学家的屏幕。他正在运行 Claude Code。我就问:‘等等,你为什么在用 Claude Code?’他说:‘我发现怎么让这东西跑起来并帮我写查询语句了。’

这些天当我经过数据科学家那一排工位时,他们都在运行 Claude Code——许多人甚至同时运行几个实例——用来跑查询、创建可视化图表以及做其他类型的辅助工作。”

有趣的一点是,Boris 最初只设想软件工程师会使用 Claude Code——所以起了这个名字!——但产品证明了它在其他领域也有广泛用途。

团队规模翻倍的同时增加工程产出

如果我们以“每位工程师的 Pull Request(PR)数量”作为指标;当一个工程团队规模迅速翻倍时,这个指标通常会下降。这是因为现有工程师要花更多时间指导新同事而减少编码,新员工起初也需要时间适应。像任何指标一样,每位工程师的 PR 数并不是一个完美的指标,但它确实能反映迭代的速度。GitHub、Dropbox、Monzo、Adyen 等公司都在衡量 PR 吞吐量。

但 Anthropic 在团队规模翻倍时,PR 吞吐量反而增长了 67%——这要归功于 Claude Code。 按照常理,“平均合并 PR 数”这一指标本应下降,但它实际上上升了!这一功劳被归于 Claude Code:工程师用它能更快地完成 PR。在 Anthropic 工程团队人数翻倍的同时,Claude Code 恰好在整个工程部门被广泛采用,这可能是一系列幸运事件的巧合。

我也注意到,使用 Claude Code 完成一项工作要快得多,并且我在编码任务上取得了更好的进展。当我构建的东西可以通过运行程序并检查输出或运行测试来验证正确性时,Claude Code 的帮助尤为明显。

2. 技术栈与架构

Claude Code 的技术栈:

Ink 框架是一个简洁的组件,允许在终端中创建外观令人愉悦的 UI。例如,要创建这个 UI:

你可以用 React 编写如下代码:

一个在控制台中更新的计数器代码。来源:GitHub 上的 Ink

npm 用于分发 Claude Code。它是 Node 生态系统中最流行的包管理器。要开始使用 Claude Code,你需要安装 Node 18 或更高版本,然后运行:

npm install -g @anthropic-ai/claude-code

完成后,你可以使用 Claude 命令启动该工具。

选择这个技术栈是为了让 Claude 模型处于“分布内(on distribution)”。 在 AI 领域,有“分布内”和“分布外(off distribution)”这两个术语。“分布内”意味着模型已经知道如何处理它,“分布外”意味着模型不擅长处理它。

团队希望为 Claude 选择一个它已经很擅长的“分布内”技术栈。TypeScript 和 React 是模型非常有能力驾驭的两种技术,因此是合乎逻辑的选择。然而,如果团队选择了一个 Claude 不太擅长的更生僻的技术栈,那就会变成“分布外”技术栈。Boris 总结道:

“如果是分布外的技术栈,模型仍然可以学习。但你必须从头教起并投入大量工作。我们要的是一个不需要我们去教的技术栈:一个 Claude Code 可以用来构建它自己的栈。效果非常好;大约 90% 的 Claude Code 是由 Claude Code 自己编写的”。

架构:选择最简单的选项

有趣的是,就客户端的模块、组件和复杂的业务逻辑而言,Claude Code 并没有那么复杂。对于一个能执行遍历文件系统和代码库等复杂操作的工具来说,这有点令人惊讶!但 Claude Code 只是 Claude 模型之上的一个轻量级外壳。这是因为模型完成了几乎所有的工作:

Claude Code 团队试图编写尽可能少的业务逻辑。 Boris 告诉我:

“这听起来可能很奇怪,但我们构建它的方式是希望人们尽可能原始感受这个模型。我们坚信模型能做的比今天产品所允许的要多得多。

当你看许多编码产品时,它们阻碍了模型;它们通过添加 UI 元素和其他杂乱的部分来搭建脚手架,使得在这些工具中运行的模型感觉像是单脚跳一样艰难。旨在帮助用户的功能最终限制了模型。所以,我们试图使 UI 尽可能极简。

每次有新模型发布,我们都会删除一堆代码。 例如,随着 4.0 模型的发布,我们删除了大约一半的系统提示词(system prompt),因为我们不再需要它了。我们试图在模型周围放置尽可能少的代码,这包括最大限度地减少提示词和工具的数量。我们不断删除旧工具并尝试新工具”。

Claude Code 不使用虚拟化——它在本地运行。 一个主要的设计决策是是否在虚拟机中运行 Claude Code——比如在 Docker 容器或云端——从而创建一个沙箱环境。但团队决定采用本地运行的版本,因为:简单!Boris 说:

“对于每一个设计决策,我们几乎总是选择最简单的可能选项。对于‘你在哪里运行批处理命令?’和‘你在哪里读取文件系统?’这类问题,最简单的答案是什么?就是在本地做。

所以我们采用了这个方案:Claude Code 在本地运行批处理命令,并读取和写入文件系统。没有虚拟化”。

权限系统

Claude Code 最复杂的部分是权限系统。在本地运行 Claude Code 的风险在于,Agent 可能会做一些不可逆的、不应该做的事情,比如删除文件。但这如何安全地完成呢?

同样,团队选择了简单性,构建了一个在执行操作前寻求许可的权限系统。用户可以决定:

Claude Code 在删除文件前寻求许可

Boris 告诉我,把权限系统做对花了不少功夫:

“我们最重要的原则是:如果你开始运行 Claude Code,它不应该在未经许可的情况下更改你系统上的东西。那可能是危险的。

然而,我们也应该给人们退出的方式,让他们可以说‘实际上,在我工作的这个上下文中,我想不必每次都给予许可。’

不过,权限系统中有很多细微差别。例如,当一个命令进来时,我们会对其进行静态分析,以检查这是否已经在设置文件(settings.json)中被允许。

设置系统是一个多层系统,可以按项目、按用户和按公司进行配置。 你也可以与团队共享设置。我们观察到团队共享设置文件来将常用命令加入白名单,这样 Claude Code 就不会请求许可,比如提交到源代码控制系统”。

其他功能

Claude Code 在某些方面很简单,但拥有数十项增加了其复杂性的功能。一些已在文档中列出。值得注意的有:

3. 以天为单位(而非周)构建和发布功能

对于一个只有十几名工程师的团队来说,他们的工作速度非常快:

约 60-100 次内部发布/天。 任何时候只要工程师对 Claude Code 进行了更改,他们就会在内部发布一个新的 npm 包。Anthropic 的每个人都使用内部版本,开发团队也能获得快速反馈。

整个夏天,工程师们每天提交大约 5 个 PR——这比大多数科技公司每天 1-2 个 PR 的常态要快得多。

1 次外部发布/天。 几乎每一天,都会有一个新版本的包作为部署的一部分发布出来。

2 天内 20 个原型:构建待办事项列表(Todo lists)

这次开发经历让我惊讶的是,团队使用 Claude Code 进行的原型设计比我通常看到的要多得多。作为一个例子,Boris 向我演示了他如何在两天内的几个小时里构建了大约 20 个新功能“待办事项列表”的原型。

他很好心地分享了他在各种迭代中使用的实际提示词(prompts)。在每次迭代后,Boris 会:

原型 #1:像完成时那样显示待办事项

想法:待办事项列表是跟踪 Claude 正在做什么的最简单方法之一,所以他们尝试将列表放在最近一次工具调用(tool call)的下方。

提示词:

> make it so instead of todos showing up as they come in, we hide the tool use and result for todos, and render a fixed todo list above the input. title it "/todo (1 of 3)" in grey

(译:让待办事项不要随进随出,而是隐藏待办事项的工具使用和结果,并在输入框上方渲染一个固定的待办事项列表。标题设为灰色的 "/todo (1 of 3)")

外观效果:

原型 #1

原型 #2:在底部显示进度

另一个变体是行内显示每个待办事项的更新。

提示词:

> actually don't show a todo list at all, and instead render the tool used inline, as bold headings when the model starts working on a todo. keep the "step 2 of 4" or whatever, and add middot /todo to see after in grey

(译:实际上根本不要显示待办事项列表,而是当模型开始处理一个待办事项时,将使用的工具以内联粗体标题的形式渲染。保留 "step 2 of 4" 之类的文字,并在后面添加灰色的圆点 /todo 以供查看)

外观效果:

原型 #2

原型 #3 和 #4:一个“交互式胶囊”

如果待办事项是一个交互式胶囊(控制台底部的矩形),你可以像查看后台任务一样拉起它来查看进度,会怎么样?

提示词和输出:

> also add a todo pill under the text input, similar to bg tasks. it should render "todos: 1 of 3" or whatever

(译:在文本输入框下方也添加一个待办事项胶囊,类似于后台任务。它应该渲染 "todos: 1 of 3" 之类的文字)

原型 #3

> make the pill interactive, like the bg tasks pill

(译:让这个胶囊可交互,就像后台任务胶囊一样)

原型 #4

原型 #5 和 #6:使用“抽屉”

如果我们有一个从侧面滑入并显示待办事项的“抽屉”呢?

提示词和输出:

> actually undo both the pill and headings. instead, make the todo list render to the right of the input, vertically centered with a grey divider. show it when todos are active, hide it when it's done

(译:实际上撤销胶囊和标题。改为让待办事项列表渲染在输入框的右侧,垂直居中并带有一个灰色分隔符。当待办事项激活时显示,完成后隐藏)

原型 #5,待办事项作为右侧的“抽屉”。查看动画版本

> it's a little jumpy, can you also animate it like a drawer

(译:有点跳动,你能不能像抽屉一样给它加个动画)

原型 #6。查看动画版本

原型 #7、#8 和 #9:关于可见性的实验

为了让待办事项列表尽可能显眼,Boris 尝试让它始终显示在输入框上方。

提示词和输出:

> actually what if you show the todo list above the input instead

(译:实际上如果你把待办事项列表显示在输入框上方会怎样)

原型 #7

> truncate at 5 and show "... and 4 more" or whatever

(译:在第 5 项截断并显示 "... and 4 more" 之类的)

原型 #8

> add a heading "Todo:" in grey text

(译:添加一个灰色的 "Todo:" 标题)

原型 #9

原型 #10-20:移动旋转指示器(Spinner)UI 元素

Boris 继续调整待办事项列表应该放在哪里才显眼,在又做了几个原型之后。最终,Boris 将待办事项列表移到了旋转指示器上,这最大化了可见性,并且开始感觉不错。经过几次迭代,他们确定了最终公开发布的版本。

大约在原型 #20,在尝试了可见性和旋转指示器之后

还有一个迭代

团队从社区收到了很多反馈,用户希望能够查看所有的待办事项。所以团队增加了用 CTRL + T 切换它们的功能。这就是今天上线的版本!

这个迭代(大约 #21 左右)目前已投入生产——按 Tab 键可切换正在执行的步骤列表

使用 AI Agent 一天内构建和测试 5-10 个原型创意是可能的。 原型设计过去非常耗时,如果两天内能构建出两个截然不同的原型就算幸运了。但现在,Agent 可以非常快速地构建原型,因此像 Claude Code 团队那样每天测试 5-10 个原型可以轻松实现。

我不建议每个人都如此快速地构建这么多原型,但我认为理应忘掉过去原型设计需要多长时间:这些工具改变了原型设计的速度上限!

这次原型设计的很多内容都是为了让 UI “感觉良好”:你可以在这个讨论串中看到动画原型。我建议观看原型步骤的视频,以感受该功能是如何演变的,以及 Boris 是如何不断尝试新想法,最终将待办事项列表缩减为今天工具中的样子的。

4. 重新设计终端用户体验(UX)

Claude Code 拥有团队带来的许多新颖想法,因为多亏了 LLM 对每个命令的响应,这确实是终端第一次真正实现了交互式体验。几个例子: