从 Copilot 到 Claude Code:智能体编程的范式转移

Claude Code; Agent; 架构 4779 字 24 min read

这是「Claude Code 系列」的第一篇。这个系列不打算再写一遍”如何安装 Claude Code”——那样的事教程已经够多了。我想做的事情是:带你拆开它。从 Copilot 到 Claude Code 这条演进线里,到底发生了什么范式转移;一个生产级 Agent 工具的骨架是怎么搭的;以及那些”为什么这样设计”的问题,答案远比”怎么用”更值得收藏。

一、AI 编程的三个阶段

在谈架构之前,先把视野拉宽。AI 编程的发展,粗略可以分成三个阶段:

  • 网页对话阶段:以 ChatGPT 为代表。你把需求或代码片段发给模型,模型生成代码,你手动复制粘贴到工程里,手动编译调试。模型的角色是”对话伙伴”——它只能”说话”,不能”做事”。
  • 插件工具阶段:Cursor 横空出世,代码自动补全好用到爆炸,AI 自动识别工程并理解,直接修改本地代码,无需手动复制粘贴。模型从”回答问题的人”变成了”能在编辑器里动手的助手”。
  • Agent 托管阶段:Claude Code、Codex 直接出世,开创全托管编程模式。AI 不仅可以编码提交,更能自动测试、产物发布,配合自定义 Skill、MCP 等扩展,可谓无所不能。

这条演进线的方向始终是同一个:给 LLM 更多的行动能力。从只能看到当前文件,到能看到整个项目;从只能生成建议,到能执行命令;从单步操作,到多步自主规划。

时间线拉长一点看更清楚:

flowchart TD
    A["2021.06 GitHub Copilot 技术预览<br/>首次将 LLM 集成到编辑器"] -->
    B["2022.12 ChatGPT 发布<br/>证明 LLM 的通用对话能力"] -->
    C["2023.03 GPT-4 + Function Calling<br/>LLM 从文本生成器变为指令编排器"] -->
    D["2023.11 Claude 2.1 + Tool Use<br/>200K 上下文窗口"] -->
    E["2024.01 Devin 发布<br/>第一个 AI 软件工程师"] -->
    F["2024.08 Cursor Agent 模式<br/>编辑器集成的 Agent"] -->
    G["2025.02 Claude Code 发布<br/>终端原生 Agent"] -->
    H["2025.11 Anthropic 发布 MCP<br/>标准化 Agent 通信协议"] -->
    I["现在 ← 你在这里"]

    classDef event fill:#f0f7ff,stroke:#3b82f6,stroke-width:1px,color:#1e3a5f
    classDef milestone fill:#dbeafe,stroke:#2563eb,stroke-width:2px,color:#1e40af
    classDef current fill:#fef3c7,stroke:#f59e0b,stroke-width:2px,color:#92400e
    class A,B,C,D,E,F event
    class G,H milestone
    class I current

二、关键转折:LLM 作为推理引擎

2024 年,一个根本性的认知转变开始普及:LLM 的真正价值不在于生成文本,而在于作为推理引擎来编排工具调用。

当 LLM 不再只是”回答问题的人”,而是”决定调用什么工具、传递什么参数、如何处理返回结果的决策中枢”时,它的能力边界被彻底打开了。用更形象的比喻:如果说之前的 LLM 是一个只会口述指令的军事参谋,那么工具调用让 LLM 变成了一个可以直接调度各兵种协同作战的指挥官。参谋的指令需要传令兵逐级传达,而指挥官的命令可以在毫秒级传达到每一个作战单元。

工具调用的突破性意义在于它重新定义了 LLM 与外部世界的边界:

  • 没有工具调用时,LLM 是一个封闭系统。知识截止于训练数据的最后一天,推理局限在上下文窗口之内,输出只能是人类可读的文本。好比你有一位极其博学的朋友,但他被困在一个没有窗户、没有电话的房间里——他知道如何诊断疾病,但无法使用听诊器;知道如何修理汽车,但无法拿起扳手。
  • 有了工具调用之后,LLM 变成了一个开放系统的编排器。它可以调用搜索引擎获取实时信息,可以调用代码解释器执行计算,可以调用文件系统读写代码,可以调用 Git 操作版本控制。每一次工具调用都是 LLM 与真实世界的一次握手。

但工具调用也带来了新的工程挑战。这些挑战不再是模型层面的,而是系统工程层面的:

  1. 工具注册与发现:谁来管理工具的注册表?如何让模型知道有哪些工具可用?如何在不重启系统的情况下动态添加新工具?
  2. 参数校验:谁来校验工具调用的参数?模型可能传递错误类型、缺少必填字段、或传入超出范围的值。校验逻辑放在哪一层?
  3. 权限管控:谁来决定某个工具调用是否应该被执行?模型可能要求执行 rm -rf /,这显然不应该被允许。但有些操作在特定上下文中是安全的——如何平衡安全性与效率?
  4. 错误恢复:工具执行可能失败,API 调用可能超时,LLM 的输出可能不符合预期格式。每一种错误场景都需要有对应的恢复策略,否则 Agent 会陷入”出错-重试-再出错”的死循环。
  5. 状态一致性:多个工具调用可能操作同一份资源。如何在多轮调用之间维护状态一致性?如何避免”读到过期数据”?
  6. 并发与调度:某些工具调用可以并行执行(如同时读取多个文件),某些必须串行(如先创建目录再写文件)。如何智能地调度这些调用以最大化效率?

这六个问题催生了一个新的架构概念:Agent Harness

三、为什么需要 Agent Harness,而不是简单封装

一个常见的误解是:Agent Harness 不过是对 LLM API 的一层封装,加上一些工具定义和调用逻辑。事实远非如此。

假设我们要构建一个能够”修改代码并运行测试”的 Agent:

简单封装的思路是:调用 LLM API,解析输出中的工具调用指令,执行工具,把结果拼回 Prompt,再次调用 LLM API。这是一个典型的 while 循环。

这种思路的问题在于它假设了一个理想化的世界:API 调用不会超时,模型输出永远格式正确,用户不需要实时看到进度,上下文窗口是无限的,所有操作都是安全的。但在真实的生产环境中,每一个假设都会被打破。

Agent Harness 的思路则要回答以下问题:

  1. 流式输出:LLM 的响应是流式的,用户需要实时看到 Agent 的思考过程,而不是等待整个响应完成。如何在不阻塞主线程的情况下实现增量渲染?回调会导致”回调地狱”,Promise 链会失去中途取消的能力,事件发射器会增加内存管理的复杂度。
  2. 权限管控:LLM 可能要求执行 rm -rf /。权限系统应该在哪一层介入?外层统一拦截会丢失工具特定的权限逻辑(如 Bash 命令的风险评估),每个工具内部检查会导致重复代码和策略不一致。
  3. 上下文管理:随着对话进行,上下文窗口会被填满。何时触发上下文压缩?压缩策略如何保证不丢失关键信息?压缩后的上下文如何与缓存系统协同?错误的压缩可能导致 Agent”忘记”关键信息,做出错误决策。
  4. 错误恢复:没有统一的错误恢复框架,每种错误都需要单独处理,代码会迅速膨胀到不可维护。
  5. 状态持久化:用户中断会话后如何恢复?多个子智能体如何共享状态?状态更新如何保证不可变性?
  6. 可扩展性:如何让第三方开发者安全地注册新工具?如何支持 MCP 等外部协议?如果没有清晰的扩展接口,Agent 的能力将永远受限于原始开发者的想象力。

用一个类比总结两者的区别:简单封装就像是给一辆汽车装了一个遥控器——你能让它前进和后退,但没有方向盘助力、没有刹车系统、没有安全气囊。Agent Harness 则是设计一整辆安全、可靠、可扩展的自动驾驶汽车——不仅有引擎,还有悬挂系统、制动系统、安全约束系统和诊断系统。

一句话概括:Agent Harness 是围绕 LLM 构建的运行时框架,它将 LLM 从一个文本生成器提升为一个能够安全、可靠、高效地与外部世界交互的自主智能体。

反模式警告: 如果你正在构建一个 Agent 系统,核心循环只是一个 while (true) { callAPI(); parseResponse(); executeTool(); } 的简单循环,那么你可能正在重复”简单封装”的错误。停下来想想:你的系统如何处理流式输出?如何管理上下文?如何在错误时恢复?如果这些问题的答案是”还没想好”,你需要一个 Agent Harness。

Claude Code 正是这个 Agent Harness 思路的代表实现。

四、Claude Code 全景架构

Claude Code 的 src 目录包含约 1,884 个 TypeScript 文件、50 万行级别代码。对一个终端工具来说,这个体量相当可观——作为对比,VS Code 的核心代码大约也是这个规模。这说明 Agent Harness 的工程复杂度不亚于一个完整的编辑器框架。

但这并不意味着代码是臃肿的。恰恰相反,Claude Code 的代码组织表现出高度的模块化特征:每个工具是一个独立模块,每个子系统有清晰的边界,职责划分遵循单一职责原则。这种模块化程度是 Agent Harness 可维护性的关键保障。

一张全景图直观看下模块组织:

flowchart TD
    entry["入口模块 Entry<br/>CLI 解析 · 启动优化 · React/Ink 初始化"]
    query["查询引擎 QueryEngine<br/>会话状态 · 消息历史 · 文件缓存 · 用量统计"]
    loop["对话主循环 AsyncGenerator<br/>预处理 → API 调用 → 工具检测 → 状态构建"]

    subgraph loop_inner[" "]
        direction LR
        p1["预处理管线<br/>压缩/裁剪"] --> p2["API 调用<br/>流式接收"] --> p3["工具检测<br/>权限检查"] --> p4["状态构建<br/>消息回填"]
    end

    tools["工具系统<br/>45+ 工具 · 编排引擎 · 并发分区"]
    perm["权限管线<br/>四阶段检查 · 五种模式 · 规则持久化"]
    ext["扩展层<br/>MCP 协议 · 子智能体调度 · 插件系统 · Hook 机制"]

    entry --> query --> loop
    loop --- loop_inner
    loop --> tools
    loop --> perm
    perm -.->|权限约束| tools
    tools --> ext

    classDef module fill:#f0f7ff,stroke:#3b82f6,stroke-width:2px,color:#1e3a5f
    classDef inner fill:#f8fafc,stroke:#93c5fd,stroke-width:1px,color:#475569
    classDef extmod fill:#fef9f0,stroke:#f59e0b,stroke-width:2px,color:#78350f
    class entry,query,loop,module module
    class p1,p2,p3,p4 inner
    class tools,perm,ext extmod

这张图揭示了 Claude Code 架构的层次性:从顶部的用户入口到底部的扩展层,每一层都有清晰的职责和接口边界。对话主循环是整个系统的”心脏”,它驱动着数据在各个子系统之间流转。

技术栈:用正确的工具解决正确的问题

技术组件选择设计考量
运行时Bun原生 TypeScript 支持、更快的启动速度、原生 fetch
终端 UIReact + Ink组件化 UI 模型、声明式渲染、React 生态复用
CLI 框架Commander.js成熟的命令行参数解析、子命令支持
Schema 验证Zod v4运行时类型安全、工具输入校验、JSON Schema 生成
LLM SDK@anthropic-ai/sdk官方 SDK、流式响应支持

值得特别一提的是 React + Ink。Ink 是一个使用 React 组件模型渲染终端 UI 的框架。这意味着 Claude Code 的界面不是用 console.log 拼出来的,而是用 React 组件树声明式描述的。当你看到工具执行进度条、权限确认对话框、多列结果展示时,它们背后都是 React 组件——和你在 Web 应用中写的组件没有本质区别。这反映了一个设计理念:终端 UI 不应该比 Web UI 低一等。

Zod v4 的选择同样关键。在 Agent 系统中,LLM 生成的工具调用参数是不可预测的——模型可能传递错误类型、缺少必填字段、或传入超出范围的值。Zod 在运行时提供了一道类型安全屏障,确保每一个工具调用都经过严格的参数校验。更重要的是,Zod 可以同时生成 JSON Schema 发送给 API,让模型知道每个参数的含义和约束——这是”类型定义即文档”的完美实践。

五个关键模块

Claude Code 的核心架构可以用五个关键模块来概括:

入口点模块承担三个核心职责:启动优化(在所有模块导入前记录性能节点,并行预取配置)、CLI 解析(处理模型选择、允许的工具列表、权限模式)、React/Ink 初始化。这里有一个精心设计的启动策略:副作用导入被有序排列,确保性能探针最先执行,紧接着是可并行的 I/O 预取,最后才是重量级模块加载。它揭示了一个通用工程原则——启动路径是用户体验的第一印象。一个需要 5 秒才显示提示符的工具,和 0.5 秒就响应的工具,在用户心理上产生的是”重型工具”和”轻量工具”的天壤之别。

QueryEngine 是一个 class,拥有查询生命周期和会话状态,管理对话消息历史、文件缓存、用量统计、权限拒绝记录。它同时服务于交互式 REPL 和无头 SDK 两种运行模式——“一个核心、多种入口”。这种设计确保了无论用户通过终端交互还是 API 调用,都走同一条经过验证的核心路径。

异步生成器对话主循环是最核心的模块。它是一个 AsyncGenerator,实现迭代式的对话过程:构建请求 → 流式调用 API → 解析工具调用 → 权限校验 → 执行工具 → 注入结果 → 决定继续或终止。用 AsyncGenerator 而非普通函数的关键好处是:调用者可以在循环的每一步通过 yield 接收中间状态,而不需要回调或事件发射器。上层代码用 for await...of 就能优雅地消费整个对话过程。

工具类型系统定义了所有工具必须遵循的类型契约——“接口即架构”。类型定义中蕴含了丰富的架构决策:权限检查被内嵌到工具执行流程中而非外部拦截;并发安全声明影响调度策略;破坏性标记是权限系统的重要输入。

工具注册中心是工具系统的”单一事实来源”。三个设计细节值得注意:条件注册(某些工具通过 Feature Flag 控制,同一份代码服务不同产品形态)、延迟加载(动态导入避免循环依赖和启动开销)、工具过滤(发送给 LLM 前根据权限过滤,确保模型甚至无法”看到”它不该使用的工具)。

五、Agent Harness 的五大设计原则

拆解 Claude Code 的架构,可以提炼出五个贯穿始终的设计原则。每一个原则都回答了一个核心的”为什么不选更简单的方案”:

flowchart TD
    A["异步流式优先<br/>Async Generator"]
    B["不可变状态流转<br/>Immutable State"]
    C["缓存感知设计<br/>Cache-Aware"]
    D["安全边界内嵌<br/>Security at the Perimeter"]
    E["渐进式能力扩展<br/>Progressive Capability"]

    A --> B
    A --> C
    B --> D
    D --> C
    B --> E

    classDef principle fill:#f0f7ff,stroke:#3b82f6,stroke-width:2px,color:#1e3a5f
    class A,B,C,D,E principle

原则一:异步流式优先。 整个对话循环建立在 AsyncGenerator 之上。Agent 的交互模式和传统请求-响应根本不同——一个用户请求可能执行多轮工具调用,每轮都可能产生需要实时展示的中间状态。AsyncGenerator 完美匹配这个需求:增量输出(yield 逐步产出事件)、可中断性(随时 return() / throw() 终止)、背压控制(消费者跟不上时自动暂停)。

原则二:不可变状态流转。 跨迭代状态封装在一个不可变的 State 对象中,每次迭代时通过整体替换而非逐字段修改。这种模式让状态的每次变化都是可追溯的,也是后续能够实现 Fork(子智能体继承父级上下文)的基础。

原则三:缓存感知设计。 Agent 系统的成本高度依赖 prompt cache 命中率。工具定义、系统提示、消息前缀这些稳定部分放在缓存窗口前部,动态部分放在后部。一个错误的上下文压缩策略可能打乱缓存边界,让成本暴涨。

原则四:安全边界内嵌。 权限检查不是外层统一拦截,而是内嵌到工具执行流程——Bash 工具的权限逻辑和文件工具的权限逻辑本质不同,必须分别处理。这就引出了后文要讲的”四阶段权限管线”。

原则五:渐进式能力扩展。 从工具到 Skill,从 Skill 到 MCP,从 MCP 到插件,能力是分层渐进的。同一份核心代码,通过条件注册和延迟加载,可以服务从最小 Agent 到完整产品形态的不同配置。

六、本系列的路线图

理解了这一篇,你就有了 Claude Code 的宏观心智模型。接下来的七篇会分别钻进这些子系统:

  • 上手篇:安装配置与每一项功能,把”能用”这一关过了。
  • 配置篇:六层配置优先级链、四阶段权限管线、26 个生命周期事件的钩子系统。
  • 扩展篇:Skills 技能系统与插件架构——给 Agent 装”外挂”。
  • 编排篇:子智能体 Fork 模式与协调器模式——让多个 Agent 协同。
  • 工作流篇:Plan 模式与结构化工作流——先想后做。
  • 对比篇:Claude Code vs Codex 深度对比——两个 Agent Harness 的设计差异。
  • 收尾篇:构建你自己的 Agent Harness——把这套心智模型迁移到任何框架。

读懂 Claude Code 的设计决策,你就拥有了一套可迁移到任何 Agent 框架的心智模型。无论你之后用 LangChain、AutoGen、CrewAI 还是从零构建,这套骨架都适用。


主要参考

  • 《御舆:解码 Agent Harness —— Claude Code 架构深度剖析》第 1 章「智能体编程的新范式」,作者 lintsinghua,CC BY-NC-SA 4.0 协议,GitHub 仓库
  • 《AI 编程新阶段 Claude Code 上手指南》,Linux.Do 社区帖
  • 时间线中 2026.03 源码意外公开事件:安全研究员 Chaofan Shou (@Fried_rice) 披露