乐于分享
好东西不私藏

从源码看Claude Code:代码 Agent 的架构、循环与扩展机制

从源码看Claude Code:代码 Agent 的架构、循环与扩展机制

目录

  1. 1. 整体架构与系统定位
  2. 2. 核心抽象与代码组织
  3. 3. 一次请求的完整执行链路
  4. 4. Prompt、Agent Loop 与决策机制
  5. 5. 工具系统与环境交互
  6. 6. 文件编辑、上下文管理与检索机制
  7. 7. Skill机制:任务方法的封装
  8. 8. Compact机制:长任务中的上下文压缩
  9. 9. Hooks机制:执行生命周期的扩展点
  10. 10. 安全、鲁棒性与工程设计取舍
  11. 11. 总结

1. 整体架构与系统定位

Claude Code的本质不是“带终端的聊天程序”,而是一个以模型决策为中心、以工具调用为执行手段、以代码仓库和本地环境为操作对象的交互式代码 Agent。

它和普通 LLM 应用的差别在于:

  • • 输入不只是自然语言,还包括 cwd、git 状态、CLAUDE.md、历史消息、权限模式、MCP 服务、技能与 hooks。
  • • 输出不只是文本,还可能是读文件、搜索代码、修改文件、运行命令、连接远端会话、调用 MCP 工具。
  • • 核心流程不是一次生成,而是持续执行 观察 -> 决策 -> 行动 -> 再观察

从分层上看,这个系统大致可以分成:

  • • 用户交互层:CLI、REPL/TUI、headless/SDK、remote session viewer。
  • • 会话与状态层:session id、消息历史、AppState、转录存储、任务与后台 agent 状态。
  • • Agent 核心层:prompt 构造、query() 主循环、QueryEngine、工具调度。
  • • 能力扩展层:skills、hooks、compact、memory、MCP、agents/swarm。
  • • 工具执行层:Bash、文件读写编辑、搜索、Web、任务、MCP tools。
  • • 模型接入层:Anthropic API 请求构造、流式返回、token/cost/usage 统计。
  • • 外部环境层:本地文件系统、shell、git、网络、远端 session、MCP server。

一句话概括:它不是模型外壳,而是围绕“代码任务闭环”构建的 Agent 执行系统。

2. 核心抽象与代码组织

2.1 源码阅读地图

如果按理解优先级来读,建议顺序是:

  1. 1. src/main.tsx
  2. 2. src/setup.ts
  3. 3. src/query.ts
  4. 4. src/Tool.ts 与 src/tools.ts
  5. 5. src/services/tools/*
  6. 6. src/QueryEngine.ts
  7. 7. src/constants/prompts.tssrc/utils/systemPrompt.tssrc/utils/queryContext.ts
  8. 8. src/skills/*src/utils/hooks.tssrc/services/compact/*
  9. 9. src/services/mcp/*src/remote/*src/bridge/*

2.2 关键目录职责

  • • src/main.tsx:CLI 主入口,负责参数解析、模式切换、初始化和启动 REPL 或其他运行路径。
  • • src/setup.ts:进程级与会话级初始化,加载配置、session、hooks、memory、插件、工作区状态。
  • • src/query.ts:单轮请求的核心 Agent loop。
  • • src/QueryEngine.ts:面向 SDK/headless 模式的会话封装,对外暴露稳定的提交消息接口。
  • • src/Tool.ts:工具抽象、工具上下文、权限上下文等核心类型定义。
  • • src/tools.ts:内建工具目录和最终工具池组装。
  • • src/services/tools/*:工具编排、执行、hook 集成、流式调度。
  • • src/constants/prompts.tssrc/utils/systemPrompt.ts:系统提示词的模块化装配。
  • • src/skills/*:skill 加载、技能目录扫描、MCP skill 对接。
  • • src/utils/hooks.ts:hook 框架,负责在生命周期节点执行外部命令或函数回调。
  • • src/services/compact/*:长上下文压缩、自动 compact、session memory 结合。
  • • src/services/mcp/*:MCP 连接、鉴权、资源/工具发现与封装。
  • • src/remote/*src/bridge/*:远程 session 和 bridge 通道。
  • • src/screens/REPL.tsx:交互式主界面上的高层调度器。

2.3 核心抽象

Claude Code最重要的抽象不是 UI 组件,而是以下对象:

  • • Session / Conversation:由消息序列、session id、转录存储、usage 和当前上下文共同构成,QueryEngine 是 headless 形态下的主要承载体。
  • • Message:统一表示 user、assistant、tool_result、system、attachment、hook result 等事件。
  • • Tool:系统能力的统一描述,定义在 src/Tool.ts,内建工具和 MCP 工具都落到这一抽象上。
  • • ToolUseContext:工具运行时所需的共享环境,包含工具池、模型、MCP 连接、AppState、文件缓存、消息列表、权限上下文等。
  • • Agent / Runner:query() 是最核心的执行循环,QueryEngine 和 REPL 在其外层做会话管理。
  • • Model Prompt:由默认 prompt、agent prompt、用户自定义 prompt、skills/memory/MCP 指令共同拼装。
  • • Edit / Patch:虽然编辑行为分散在具体工具里,但“对工作区施加可验证变更”是整个系统的核心能力之一。

2.4 最关键的五个对象

如果只抓五个骨架点,应优先理解:

  • • query()
  • • QueryEngine
  • • Tool / ToolUseContext
  • • getTools() / assembleToolPool()
  • • buildEffectiveSystemPrompt() 与 getSystemPrompt()

3. 一次请求的完整执行链路

可以用“请修复当前仓库里的失败测试”来理解这条链路。

3.1 请求进入系统

用户从 CLI 或 SDK 提交请求后:

  • • src/main.tsx 解析命令行与模式。
  • • src/setup.ts 建立当前 session 环境。
  • • 交互模式下交给 src/screens/REPL.tsx
  • • headless 模式下交给 src/QueryEngine.ts

3.2 初始上下文收集

在真正调用模型前,系统会收集:

  • • 当前工作目录与 git 状态,见 src/context.ts
  • • 用户上下文,如 CLAUDE.md 与日期,见 src/context.ts
  • • 当前工具集合,见 src/tools.ts
  • • skills、命令、MCP 工具与资源
  • • 会话历史消息和文件读取缓存
  • • 权限模式、沙箱、AppState

3.3 Prompt 构造

系统提示词不是一段静态文本,而是多段拼接:

  • • 默认系统 prompt 由 src/constants/prompts.ts 动态生成
  • • 最终生效 prompt 由 src/utils/systemPrompt.ts 决定
  • • src/utils/queryContext.ts 同时抓取 defaultSystemPrompt + userContext + systemContext
  • • query.ts 中再把 userContext 前置到消息里,把 systemContext 追加到 system prompt

这一步还会受以下因素影响:

  • • main thread agent 的专用 prompt
  • • --system-prompt / --append-system-prompt
  • • memory prompt
  • • MCP server instructions
  • • skill 发现结果与 attachment 注入

3.4 模型第一次决策

query() 发起 API 调用后,模型可能:

  • • 直接输出答案
  • • 请求读取文件
  • • 搜索代码
  • • 执行命令
  • • 调用 skill
  • • 调用 MCP tool

这说明系统是 function-calling / tool-using agent,而不是一次性回答器。

3.5 工具执行

当 assistant message 中出现 tool_use block 后:

  • • runTools() 会先按 isConcurrencySafe() 分批
  • • 只读工具可并发
  • • 修改类工具串行执行
  • • runToolUse() 负责解析工具名、校验 schema、权限检查、执行 hooks、调用真正的 tool.call()

这条路径主要落在:

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

3.6 循环推进

工具结果会被重新写回消息流,然后进入下一轮 query() 内部循环。于是形成典型闭环:

  1. 1. 跑测试
  2. 2. 读取报错文件
  3. 3. 搜索相关实现
  4. 4. 修改代码
  5. 5. 再跑测试
  6. 6. 最后总结

这就是仓库里最核心的 Agent 行为模式。

3.7 终止条件

一次 turn 在以下情况结束:

  • • 模型给出 final answer,没有新的 tool_use
  • • 达到 turn/token/budget 限制
  • • 发生不可恢复错误
  • • 用户中断
  • • compact/recovery 之后仍无法继续

4. Prompt、Agent Loop 与决策机制

4.1 System prompt 的角色

Claude Code把很多“产品行为”写进了 system prompt,而不是硬编码成流程机:

  • • 行为规范
  • • 工具使用约束
  • • 输出风格
  • • 安全限制
  • • 长任务策略
  • • memory 与 MCP 使用说明

因此,源码中的能力很大程度是“prompt + tool runtime”协同得到的。

4.2 用户指令如何被增强

用户看到的是一句请求,但模型看到的是:

  • • 默认系统 prompt
  • • agent 或自定义 prompt
  • • 当前日期、CLAUDE.md、git status
  • • 可用工具 schema
  • • MCP instructions
  • • skill discovery/system reminders
  • • 历史消息与附件

所以“用户原话”和“模型真实输入”之间有一层很厚的运行时增强。

4.3 Agent Loop 的控制方式

src/query.ts 体现了该系统的主循环控制:

  • • 每一轮先调用模型
  • • 解析 assistant 输出中的 tool_use
  • • 执行工具并追加 tool_result
  • • 必要时自动 compact 或做 max token recovery
  • • 继续下一轮,直到满足终止条件

它不是传统 planner-executor 两阶段系统,而更接近混合式 ReAct/function-calling:边做边修正。

4.4 自我修正机制

系统的纠错能力主要来自环境反馈:

  • • 工具错误会回流给模型
  • • 权限拒绝会回流给模型
  • • prompt too long 会触发 compact/recovery
  • • patch/命令/测试失败后会继续读文件并重新决策

这让系统不是“第一次必须做对”,而是通过闭环逼近正确结果。

4.5 上下文膨胀控制

长对话会遇到上下文膨胀,因此系统内置:

  • • token 预算跟踪
  • • tool output 裁剪
  • • auto compact
  • • session memory compact
  • • micro compact / post-compact cleanup
  • • attachment 与 reinjection 策略

这部分对应后面的 compact 章节,是整个工程能跑长任务的关键。

5. 工具系统与环境交互

5.1 Tool 抽象

src/Tool.ts 定义的 Tool 接口统一了:

  • • 名称与 schema
  • • call()
  • • 只读/并发安全属性
  • • 权限检查
  • • MCP 元信息
  • • 延迟加载/总是加载等策略

这使得 Bash、文件编辑、MCP tool 在调度层面拥有统一协议。

5.2 工具注册与组装

工具不是散落加载的,核心入口在 src/tools.ts

  • • getAllBaseTools() 定义内建工具全集
  • • getTools() 根据模式、feature flag、deny 规则做过滤
  • • assembleToolPool() 把内建工具和 MCP 工具合并成最终工具池

这种集中式工具池设计让 prompt、权限、调度都能基于统一视图工作。

5.3 工具执行管线

完整执行链可以概括成:

tool_use -> 找工具 -> schema 校验 -> 权限判断 -> hooks -> 真正调用 -> tool_result -> 回写消息

其中最重要的执行点是 checkPermissionsAndCallTool()

5.4 权限、沙箱与 Bash

工具系统最复杂的工程点集中在 Bash:

  • • 规则匹配和权限模式在 src/utils/permissions/*
  • • UI/交互式授权在 src/hooks/useCanUseTool.tsx
  • • Bash 自己还有更细的命令分类、危险操作识别、沙箱决策
  • • 真正的 shell 进程由 src/utils/Shell.ts 启动

这说明仓库并不是“模型随便跑命令”,而是把 Bash 视为高风险能力进行特殊治理。

5.5 MCP 的地位

MCP 不是旁路系统,而是被标准化成普通 Tool:

  • • 连接和发现由 src/services/mcp/client.ts
  • • 远端工具被包装成 MCPTool
  • • 最后进入同一个 tool pool、同一套权限/编排路径

这是该仓库可扩展性的关键设计之一。

6. 文件编辑、上下文管理与检索机制

6.1 文件操作能力

仓库内建的核心工作能力包括:

  • • FileReadTool
  • • FileEditTool
  • • FileWriteTool
  • • NotebookEditTool
  • • GlobTool
  • • GrepTool

这些工具组合起来,形成了“读代码 -> 定位 -> 修改 -> 验证”的最小闭环。

6.2 文件与上下文缓存

ToolUseContext 内部有 readFileState 等缓存结构,用于:

  • • 避免重复读取大文件
  • • 维持当前会话对文件的理解
  • • 支持 compact 后的部分恢复

此外,QueryEngine 还维护消息历史、文件历史和 attribution 状态,说明这个系统把“连续任务”而不是“单轮问答”当作一等公民。

6.3 上下文注入机制

这里的上下文管理不是单纯堆消息,而是分层处理:

  • • userContext:CLAUDE.md、日期等,以用户消息形式前置
  • • systemContext:git 状态等,以系统 prompt 尾部附加
  • • memory prompt:作为 system prompt section 注入
  • • attachments:把技能发现、delta 信息等作为系统提醒加入

这是一种“多通道上下文拼接”设计,而不是只有 messages 一条总线。

6.4 检索机制

仓库对代码检索至少有三层:

  • • 文件系统级搜索:Glob/Grep
  • • 工具层搜索:ToolSearch,用于延迟暴露工具
  • • MCP/LSP/远端资源检索

这意味着系统并不假设“所有能力都必须一开始暴露给模型”,而是支持按需发现。

7. Skill 机制

Skill 是Claude Code里非常重要的“方法级扩展单元”,本质上是可被模型调用的专业化操作说明和工作流模板。

7.1 Skill 的数据来源

skills 主要由 Markdown + frontmatter 描述,src/skills/loadSkillsDir.ts 负责:

  • • 扫描用户、项目、managed、plugin、bundled skill 目录
  • • 解析 frontmatter
  • • 生成运行时 Command
  • • 去重、过滤、路径匹配激活

frontmatter 可表达:

  • • 名称与描述
  • • 适用场景
  • • 允许工具
  • • 参数
  • • hooks
  • • 执行上下文,如 inline/fork

7.2 Skill 与命令系统的关系

在Claude Code里,skill 并非完全独立于命令系统,而是会被融合到 commands/slash-command 可见层里。因此它既是“提示模板”,也是“运行时能力入口”。

7.3 Skill 的动态发现

一个很有代表性的设计是:读文件、写文件、编辑文件时都可能触发 skill discovery。也就是说,技能发现不是只靠用户显式输入,而是和当前正在操作的文件路径绑定。

这使得系统能根据工作区上下文主动提示更合适的工作流。

7.4 Skill 的实际作用

skill invocation 并不只是把一段文本喂给模型,它还会:

  • • 加载 skill 正文
  • • 注册 skill 附带 hooks
  • • 记录已调用 skill,以便 compact 后重注入
  • • 更新 command permissions 等状态

所以 skill 实际上会改变运行时行为。

8. Compact 机制

Compact 是Claude Code支撑长时会话的核心工程能力之一。

8.1 为什么需要 compact

Agent 任务往往是多轮的:

  • • 工具输出很多
  • • 历史消息长
  • • skill 内容也会占上下文
  • • MCP 指令和 memory 也占空间

如果没有 compact,任务很容易在上下文窗口里“自我撑爆”。

8.2 Compact 的实现思路

src/services/compact/compact.ts 负责对历史消息做总结和压缩。关键思路包括:

  • • 去掉图像/文档等对总结无帮助的块
  • • 根据 API round 分组截断旧消息
  • • 生成 compact boundary
  • • 把精简后的摘要重新放回会话

8.3 Auto Compact

src/services/compact/autoCompact.ts 负责:

  • • 根据模型 context window 计算阈值
  • • 判断是否应自动 compact
  • • 在失败时做 circuit breaker,避免反复无效 compact

这说明 compact 不是一个按钮功能,而是 query loop 的一部分。

8.4 Compact 之后如何保住关键信息

仓库用了几个补偿机制:

  • • session memory first
  • • post-compact cleanup
  • • 已调用 skill 的限额重注入
  • • 必要 attachment 的重建

这套设计的重点不是“摘要历史”,而是“在压缩后仍保留继续工作的关键操作上下文”。

9. Hooks 机制

Hooks 是系统对外部流程的另一条扩展总线。

9.1 Hook 的角色

src/utils/hooks.ts 支持在多个生命周期节点执行用户定义逻辑,例如:

  • • session start / end
  • • setup
  • • pre tool use / post tool use
  • • permission denied
  • • pre compact / post compact
  • • stop / stop failure

因此 hooks 不是单一“命令前后回调”,而是贯穿整个 Agent 生命周期。

9.2 Hook 的执行方式

hook 可以通过 shell 命令、函数或 HTTP 等方式触发。它们有自己的:

  • • 输入 schema
  • • 环境变量注入
  • • 后台执行模式
  • • timeout
  • • 事件上报和 tracing

这使 hooks 更像一个轻量自动化框架。

9.3 Hook 与权限的关系

一个关键工程细节是:hook 的 allow 结果并不能越过 deny/ask/safety check。也就是说,hook 可以辅助决策,但不会破坏底层安全边界。

9.4 Hook 与 Skill 的关系

skill frontmatter 可以带 hooks,调用 skill 时会把这些 hooks 注入 session 级注册表。于是 skill 提供方法论,hook 提供执行时机,两者组合成更强的流程编排能力。

10. 安全、鲁棒性与工程设计取舍

10.1 安全边界

Claude Code在安全上不是靠“相信模型”,而是靠多层约束:

  • • 工具 schema 校验
  • • rule-based permissions
  • • ask/deny/allow 模式
  • • Bash 专项安全分类
  • • sandbox runtime
  • • subprocess env 清洗
  • • MCP 鉴权与会话失效处理

10.2 鲁棒性设计

系统做了很多工程上的防御:

  • • 工具并发与串行分离
  • • max output / prompt too long 恢复
  • • auto compact 失败熔断
  • • session transcript 持久化
  • • background/remote session 支持
  • • tool output 截断与存储

这些机制一起保证系统能在真实仓库里跑较长任务,而不是只适合 demo。

10.3 设计取舍

Claude Code的一些明显取舍是:

  • • 重 prompt 编排,而非重规则引擎
  • • 重统一 Tool 抽象,而非为不同能力写多套执行框架
  • • 重运行时扩展,skills/hooks/MCP 都尽量接到同一主循环
  • • 在 Bash 上投入大量治理,因为它是最强也最危险的工具

代价也很明显:

  • • 入口和模式较多,main.tsx 很重
  • • feature flag 较多,理解成本高
  • • prompt、attachments、hooks、memory 共同作用后,行为路径较复杂

11. 总结

11.1 总体判断

从源码组织看,Claude Code的核心不是 UI,也不是单个模型 API 封装,而是一个统一的 Agent runtime:

  • • 以 query() 为核心回路
  • • 以 Tool 为统一执行接口
  • • 以 QueryEngine / REPL 为两种主要承载形态
  • • 以 skills、hooks、compact、MCP 为扩展层
  • • 以 permissions/sandbox 为安全底盘

11.2 这个架构最成功的地方

  • • 把 built-in tools 和 MCP tools 统一进同一抽象
  • • 把长任务问题通过 compact/memory 机制工程化解决
  • • 把 skills/hooks 做成运行时扩展,而不是散落脚本
  • • 让 REPL、SDK、remote session 共用核心 query runtime