Skip to content

Latest commit

 

History

History
103 lines (76 loc) · 6.39 KB

File metadata and controls

103 lines (76 loc) · 6.39 KB

架构解析 — Claude Code 是怎么工作的,我们复现哪些部分

这是本项目的核心学习文档。先读懂这篇,再去看 roadmap 里的里程碑,你会知道每一步在复现什么。

1. 一句话本质

Agent = LLM + 工具 + 循环。

Claude Code、Codex CLI、Cursor Agent 这些产品,剥掉外壳后核心都是同一个循环:

用户输入
   │
   ▼
┌─────────────────────────────────────────┐
│  while True:                            │
│      response = LLM(messages, tools)    │◄─── system prompt(性格+操作手册)
│      if 模型没有要调工具:                 │
│          break   # 说完了,还话筒给用户    │
│      for 每个工具调用请求:                │
│          权限检查(allow/ask/deny)       │
│          result = 本地执行工具            │
│          messages += 工具结果            │
└─────────────────────────────────────────┘
   │
   ▼
显示回复,等下一次用户输入

关键洞察(这是初学者最容易想复杂的地方):

  1. 模型从不"执行"任何东西。 模型只输出"我想调用 read_file,参数是 {...}"这样的结构化意图,真正执行的是你写的本地 Python 代码,执行结果再作为消息喂回去。
  2. 消息列表是唯一的状态。 没有状态机、没有工作流图。对话历史(用户消息、模型回复、工具调用、工具结果)就是 agent 的全部记忆。
  3. 智能在模型里,不在框架里。 循环代码不到 100 行;agent 显得聪明,是因为模型本身会规划。工程代码的职责是把工具、权限、上下文伺候好。

2. Claude Code 的分层架构 → 我们的对应实现

┌────────────────────────────────────────────────┐
│ 交互层    终端 REPL / 流式渲染 / slash 命令       │ → cli.py (M0, M8)
├────────────────────────────────────────────────┤
│ 上下文层  system prompt / CLAUDE.md 记忆 /       │ → prompts.py (M4)
│          token 预算 / compaction 压缩           │ → context.py (M6)
├────────────────────────────────────────────────┤
│ 核心层    agent 循环(唯一的调度中枢)            │ → agent.py (M1) ⭐
├────────────────────────────────────────────────┤
│ 权限层    工具执行前的统一守门人                  │ → permissions.py (M3)
├────────────────────────────────────────────────┤
│ 工具层    Read/Edit/Write/Bash/Glob/Grep/Task   │ → tools/ (M1,M2,M5,M7)
└────────────────────────────────────────────────┘

3. 各层的设计要点

3.1 工具层:description 就是 prompt

一个工具由四部分组成:namedescriptioninput_schema(JSON Schema)、本地执行函数。前三个会随每次 API 请求发给模型——模型选不选、会不会用对一个工具,几乎完全取决于 description 写得好不好。Claude Code 的每个工具 description 都是几百词的精心之作,写明了何时该用、何时不该用、和其他工具怎么配合。

同理,工具的错误信息也是 promptedit_file 匹配失败时返回「old_str 匹配到 3 处,请提供更多上下文使其唯一」,模型下一轮就会自我纠正。错误信息写得好,agent 就显得"会自愈"。

3.2 权限层:策略与机制分离

工具自己不做权限判断(机制),统一由 permissions 模块决策(策略):每个工具声明危险级别(read/write/execute),权限模式(default / accept-edits / yolo)+ 会话白名单决定 allow / ask / deny。用户拒绝时,拒绝理由作为 tool_result 返回模型,模型会换个做法——拒绝也是对话的一部分

3.3 上下文层:上下文窗口是稀缺资源

这是 coding agent 最核心的工程问题。三个手段:

  • 入口截断:工具输出限长(bash 截 2000 字符、read 分页)——脏东西不进上下文
  • compaction:接近窗口上限时,用模型把旧历史总结成摘要替换掉,保留任务目标和最近几轮
  • 子 agent 隔离:见 3.4

3.4 子 Agent:花 token 买上下文空间

搜索类任务("全项目找 X")会产生大量中间垃圾(几十次 grep/read 的输出)。Task 工具把这种任务派给一个全新消息列表的子 agent,它在自己的上下文里翻箱倒柜,只把最终结论一段话带回主上下文。两条铁律:子 agent 不能再派子 agent(防递归爆炸);子 agent 通常只给只读工具。

3.5 记忆分层

Claude Code 我们 生命周期
系统提示词 内置 prompts.py 每次请求
项目记忆 CLAUDE.md AGENT.md 跨会话,随项目
会话历史 messages messages 单会话(可持久化)

4. 我们刻意简化了什么(学习时要知道差距在哪)

Claude Code 有 我们的取舍
20+ 个工具、每个 description 数百词 8 个左右核心工具
并行工具调用、后台任务 串行执行,够学习用
MCP、hooks、插件生态 M9 可选
精细的 allowlist 规则语法 三档模式 + 会话白名单
多模型路由(Haiku 干杂活) 单模型
IDE 集成、GitHub Actions

简化的原则:保留每一层的"思想",砍掉每一层的"规模"

5. 建议的学习路径

  1. 读完本文 → 做 M1,亲手跑通循环(这一步价值最大)
  2. 每完成一个里程碑,回来重读对应小节,对比理解
  3. M6 做完后,重点复盘:没有上下文管理时 agent 是怎么"变笨"的
  4. 全部做完后,读一遍 Claude Code 官方文档的 agent loop 章节,看看真实实现多了什么