00|先别看功能,先看问题:Claude Code 到底在解决什么

18 阅读9分钟

这是「Claude Code 第一性原理拆解」的第 1 篇。
我想先不谈功能表,而是先回答一个更底层的问题:一个编程智能体到底在解决什么,以及为什么 Claude Code 会长成今天这样。

摘要

这一篇我主要想讲清 4 件事:

  • Claude Code 真正在解决的,不是“聊天更聪明”,而是“让模型在真实工程环境里持续行动”。
  • 主循环、工具运行时、权限、上下文压缩,不是功能堆砌,而是问题自然外溢出来的架构层。
  • 如果站在大模型应用开发工程师的视角,读 Claude Code 最重要的不是追功能,而是建立问题意识。
  • 这套问题意识,会直接影响我以后怎么设计自己的 Agent 系统。

你会得到什么

读完这一篇,最少应该能回答这几个问题:

  • 为什么我会把 Claude Code 看成 Agent Runtime,而不是编程版聊天工具。
  • 为什么一旦模型开始行动,系统复杂性就会迅速上升。
  • 为什么我读源码时要先抓主问题,而不是先扫目录。

我现在看 Claude Code,不会先去看它支持多少命令、多少模式、多少工具。我会先问一个更底层的问题:

如果不看产品文案,只看工程现实,Claude Code 到底在解决什么问题?

因为一旦这个问题没想清楚,后面很容易陷入一种很表层的阅读方式:

  • 看到 FileReadTool,觉得“哦,就是读文件”
  • 看到 BashTool,觉得“哦,就是跑命令”
  • 看到 AgentTool,觉得“哦,就是多开几个智能体”

这样读,最后很难得到自己的方法论。最多只是知道“这个仓库很大、功能很多”。

但我现在越来越确信,Claude Code 的价值不在“功能多”,而在它很认真地解决了一类特定系统问题:

如何让一个模型在真实工程环境里持续、可控、可恢复地行动。

如果只用一句更工程化的话来说,就是:

Claude Code 不是聊天机器人加几个工具,而是一个面向编程场景的 Agent Runtime。

这篇文章,我就只做一件事:把这个判断从第一性原理讲明白。

一、先从第一性原理问:一个“编程智能体”到底要干嘛

如果我把“编程智能体”这四个字拆开来看,其实它至少要同时满足四个条件:

1. 它不是纯生成系统

也就是说,它不能只是:

  • 听用户说一句话
  • 返回一段文字

如果只是这样,那它本质上还是一个聊天系统,只不过主题是编程。

2. 它必须理解环境

编程不是在真空里进行的。
它总要面对:

  • 当前仓库
  • 当前文件树
  • 当前分支或工作区
  • 当前已有代码和配置
  • 当前用户真正想解决的问题

所以一个编程智能体,第一性原理上一定不是“单轮问答机”,而是“带环境的系统”。

3. 它必须能行动

只会说“你可以改一下这个函数”没有用。
真正的编程智能体必须能:

  • 读文件
  • 搜代码
  • 修改文件
  • 跑测试
  • 看错误输出
  • 必要时继续再行动

这意味着它必须把“语言理解”转成“具体动作”。

4. 它必须对动作后果负责

这是最关键的一点。
一旦它能改文件、跑命令、调用外部系统,它就不再是纯信息系统,而是一个带副作用的系统

而一旦系统有副作用,就会立刻出现这些问题:

  • 动作有没有权限
  • 多个动作能不能并发
  • 失败以后怎么恢复
  • 长会话如何维持上下文
  • 用户如何知道它做了什么

所以如果从第一性原理看,编程智能体天然会长出四层结构:

  1. 环境理解
  2. 行动执行
  3. 状态维持
  4. 风险约束

Claude Code 的整个架构,其实都是这四件事往外长的结果。

二、我为什么说“先别看功能,先看问题”

因为我觉得工程阅读里最常见的误区,就是被功能表带跑。

比如看到 Claude Code 这种项目,最常见的感想是:

  • 它支持编辑文件
  • 它支持终端命令
  • 它支持子智能体
  • 它支持 MCP

这些当然都是真的,但这不是最重要的信息。

更重要的是:

为什么一个认真做的编程智能体,最后几乎一定会长出这些东西?

我会倒着推一遍。

如果它要持续做任务

它就不能把每次模型调用都当成独立结束点。
于是一定需要主循环

如果它要调用工具

它就不能把工具当成普通函数,而要统一管理:

  • schema
  • 执行
  • 错误
  • 回填消息

于是一定需要Tool Runtime

如果它能改真实环境

它就不能只靠 prompt 自觉,而要在运行时做决策。
于是一定需要权限系统

如果它会话会很长

它就不能无限堆消息,而要做:

  • 压缩
  • 记忆
  • 信息分层

于是一定需要上下文工程

如果任务太复杂

它就不能把所有事情都压在一个执行体上。
于是会自然长出子任务 / 多 Agent / 隔离执行

也就是说,从问题出发,Claude Code 的架构其实非常“必然”。

三、Claude Code 的主问题,不是“怎么问模型”,而是“怎么约束复杂性”

这是我读这个项目以后最大的感受之一。

很多人做大模型应用,前两个月最容易关注的是:

  • prompt 够不够强
  • 模型够不够好
  • tool description 写得准不准

这些都重要,但只要系统开始进入“能持续行动”的阶段,问题重心就会迅速移动。

真正困难的东西变成:

  • 会不会把状态搞乱
  • 会不会执行危险操作
  • 会不会在第 10 轮以后忘掉真正任务
  • 会不会在工具失败后陷入死循环
  • 会不会让一个小任务无限膨胀成大系统复杂性

所以我现在更愿意把 Claude Code 看成“复杂性约束系统”。

它不是在炫耀模型多聪明,而是在努力回答:

当模型开始行动以后,系统怎样还能保持清楚、稳定、可控。

四、源码主线:我会先看哪几条

如果我只保留最少的源码主线,我会锁定这几条:

1. 入口和主控

  • src/entrypoints/cli.tsx
  • src/main.tsx
  • src/QueryEngine.ts
  • src/query.ts

我看这组文件,不是为了背命令参数,而是为了理解:

  • 请求如何进入系统
  • 会话如何开始
  • 回合如何推进

2. 工具层

  • src/Tool.ts
  • src/tools.ts
  • src/services/tools/toolOrchestration.ts
  • src/services/tools/toolExecution.ts

我看这组文件,是为了理解:

  • 模型说“我要执行工具”以后,系统到底怎么把这件事做实

3. 权限与约束

  • src/utils/permissions/permissions.ts

我看这个文件,是为了回答:

  • 模型有行动冲动,但谁来裁决可不可以行动

4. 上下文与长期状态

  • src/services/compact/compact.ts
  • src/services/SessionMemory/sessionMemory.ts

我看这组文件,是为了理解:

  • 为什么 Agent 的长期会话绝不是“把聊天记录一直往后拼”

5. 子任务与多 Agent

  • src/tools/AgentTool/AgentTool.tsx
  • src/tasks/LocalAgentTask/LocalAgentTask.tsx

我看这组,是为了回答:

  • 任务复杂以后,系统怎么拆,怎么追踪,怎么回填

五、用一张架构图把 Claude Code 压到最简

如果不用产品语言,只用系统语言,我会把 Claude Code 压成下面这张图:

flowchart TD
    U[用户输入] --> S[会话状态]
    S --> Q[主循环 Query Loop]
    Q --> P[系统提示与上下文构造]
    Q --> M[模型推理]
    M --> T{是否发起工具动作}
    T -->|否| R[生成最终回复]
    T -->|是| X[Tool Runtime]
    X --> G[权限与风险裁决]
    G --> E[执行工具]
    E --> S
    Q --> C[上下文压缩与记忆]
    E --> A[子任务 / 子 Agent]
    A --> S

我特别喜欢这张图,因为它说明了一个很重要的事实:

Claude Code 的骨架不是“聊天框 + 工具栏”,而是:

  • 一个回合循环
  • 一组受控动作
  • 一套状态回流机制

这和很多人脑海里对 AI 助手的想象非常不一样。

六、我会怎么把这套思路转成 Python 伪代码

我每次读这种 TypeScript 项目,都会主动把它压回 Python 风格伪代码,因为这样最容易看清主语义。

我心里的 Claude Code 最小骨架,大概长这样:

class CodingAgentRuntime:
    def __init__(self, tools, permissions, memory):
        self.tools = tools
        self.permissions = permissions
        self.memory = memory
        self.messages = []

    def handle_user_turn(self, user_input):
        self.messages.append(user_message(user_input))

        while True:
            prompt = self.build_system_prompt()
            assistant = self.call_model(prompt, self.messages)
            self.messages.append(assistant)

            tool_calls = assistant.extract_tool_calls()
            if not tool_calls:
                return assistant

            tool_results = []
            for call in self.partition_and_run_tools(tool_calls):
                if not self.permissions.allow(call):
                    tool_results.append(permission_denied(call))
                    continue
                result = self.tools.execute(call)
                tool_results.append(result)

            self.messages.extend(tool_results)

            if self.should_compact():
                self.messages = self.compact(self.messages)

            if self.should_update_memory():
                self.memory.update(self.messages)

这段伪代码不求贴近每个实现细节,但它足够让我看清:

  • 为什么它是循环
  • 为什么它一定会有工具层
  • 为什么它一定会有权限层
  • 为什么它一定会有 compact / memory

七、这件事和“大模型应用开发”到底有什么关系

这个问题我很在意,因为我不是为了“研究某个产品”才读这个项目。

我读它,是为了反过来校准我自己对大模型开发的理解。

如果我只做过最简单的应用,我对系统的理解可能停在:

  • prompt
  • 模型
  • 检索
  • API 封装

但 Claude Code 会逼我承认:

真正一旦走到“智能体”阶段,工程重点会整体迁移到这些问题上:

  • Runtime 怎么设计
  • 工具怎么托管
  • 状态怎么回流
  • 权限怎么治理
  • 长上下文怎么管理
  • 复杂任务怎么拆解

这其实就是一种认知升级。

八、我的个人判断:Claude Code 最值得学的不是实现,而是问题意识

我不觉得所有团队都要复制 Claude Code 的体量。
很多团队根本不需要那么大的系统。

但我觉得所有认真做 Agent 的团队,都应该学它的一件事:

不要把问题想得太轻。

一旦模型开始读写环境,它面对的就不再只是“生成质量”问题,而是:

  • 系统一致性问题
  • 风险边界问题
  • 长期状态问题
  • 调度问题

如果这些问题在脑子里不存在,再优雅的框架都会被你用成临时脚手架。

九、这一篇我最后想留下的结论

如果我只能留一句话给以后写 Agent 的自己,我会写:

Claude Code 最值得学习的地方,不是它能做多少事,而是它非常诚实地面对了“模型一旦开始行动,系统就必须承担哪些复杂性”这个问题。

而这也是我认为,大模型应用开发从“会接 API”走向“会做系统”时,最关键的一道坎。

系列串联