{
  "slug": "agent-design-from-leaked-source",
  "url": "https://kelewrites.com/posts/agent-design-from-leaked-source/",
  "title": "从泄露源码看 Agent 设计：一个本地 Runtime 的工程切片",
  "subtitle": "Claude Code 的全部复杂度，都在回答两个问题——怎么安全可靠，怎么压榨有限上下文",
  "summary": "Anthropic 意外泄露 Claude Code 源码后，可以第一次从代码层面拆开一个真实可用的 Agent Runtime。它的全部复杂度——query 循环、七级错误恢复、Tool 协议、压缩梯子、transcript、多 Agent 分层——其实只在回答两件事：让模型自主行为安全可靠，以及在永远有限的上下文里把任务完成。",
  "date": "2026-05-20",
  "updated": "",
  "tags": [
    "AI",
    "Agent",
    "工程方法"
  ],
  "series": "AI Agent 工程实战",
  "source": {
    "title": "从泄露源码看 Agent 设计",
    "url": "https://www.bilibili.com/video/BV1ybRbBtEgL/"
  },
  "ai": {
    "summary": "Anthropic 意外泄露 Claude Code 源码后，可以第一次从代码层面拆开一个真实可用的 Agent Runtime。它的全部复杂度——query 循环、七级错误恢复、Tool 协议、压缩梯子、transcript、多 Agent 分层——其实只在回答两件事：让模型自主行为安全可靠，以及在永远有限的上下文里把任务完成。",
    "takeaways": [
      "Anthropic 意外泄露 Claude Code 源码后，可以第一次从代码层面拆开一个真实可用的 Agent Runtime。它的全部复杂度——query 循环、七级错误恢复、Tool 协议、压缩梯子、transcript、多 Agent 分层——其实只在回答两件事：让模型自主行为安全可靠，以及在永远有限的上下文里把任务完成。",
      "一、Runtime 内核：CLI 只是入口，所有形态共用一条执行链",
      "二、工具与执行：Tool 不是函数，而是协议对象",
      "三、上下文与状态：在永远有限的窗口里活下来"
    ],
    "audience": [
      "正在做 AI Agent / 工程实践的工程师",
      "关心工程落地与系统设计的人"
    ],
    "quote": "一句话总结：Claude Code 的全部工程复杂度，本质都在解决两个问题——让模型的自主行为安全可靠，以及在有限上下文约束下把长任务跑完。",
    "questions": [
      "这篇文章的核心结论是什么？",
      "Runtime 内核：CLI 只是入口，所有形态共用一条执行链是什么意思？",
      "工具与执行：Tool 不是函数，而是协议对象是什么意思？",
      "上下文与状态：在永远有限的窗口里活下来到底讲了什么？"
    ],
    "citation": {
      "title": "从泄露源码看 Agent 设计：一个本地 Runtime 的工程切片",
      "conclusion": "Anthropic 意外泄露 Claude Code 源码后，可以第一次从代码层面拆开一个真实可用的 Agent Runtime。它的全部复杂度——query 循环、七级错误恢复、Tool 协议、压缩梯子、transcript、多 Agent 分层——其实只在回答两件事：让模型自主行为安全可靠，以及在永远有限的上下文里把任务完成。",
      "points": [
        "一、Runtime 内核：CLI 只是入口，所有形态共用一条执行链",
        "二、工具与执行：Tool 不是函数，而是协议对象",
        "三、上下文与状态：在永远有限的窗口里活下来"
      ],
      "quote": "一句话总结：Claude Code 的全部工程复杂度，本质都在解决两个问题——让模型的自主行为安全可靠，以及在有限上下文约束下把长任务跑完。",
      "url": "https://kelewrites.com/posts/agent-design-from-leaked-source/",
      "attribution": "kele · Article Share"
    }
  },
  "headings": [
    {
      "depth": 2,
      "text": "开场"
    },
    {
      "depth": 2,
      "text": "金字塔总览"
    },
    {
      "depth": 2,
      "text": "一、Runtime 内核：CLI 只是入口，所有形态共用一条执行链"
    },
    {
      "depth": 3,
      "text": "1.1 外壳与内核：一切都汇聚到 query 循环"
    },
    {
      "depth": 3,
      "text": "1.2 query 循环：本质上是一个 while"
    },
    {
      "depth": 3,
      "text": "1.3 流式调度：跑起来丝滑的底层原因"
    },
    {
      "depth": 3,
      "text": "1.4 七级错误恢复：长任务不死的真正秘密"
    },
    {
      "depth": 2,
      "text": "二、工具与执行：Tool 不是函数，而是协议对象"
    },
    {
      "depth": 3,
      "text": "2.1 Tool 接口：把治理写进协议"
    },
    {
      "depth": 3,
      "text": "2.2 执行管线：每一层都有治理点"
    },
    {
      "depth": 3,
      "text": "2.3 并发分批：按声明拆批起跑"
    },
    {
      "depth": 2,
      "text": "三、上下文与状态：在永远有限的窗口里活下来"
    },
    {
      "depth": 3,
      "text": "3.1 三段式 Prompt：稳定的放前面，会变的放后面"
    },
    {
      "depth": 3,
      "text": "3.2 五级压缩梯子：每轮都做上下文治理"
    },
    {
      "depth": 3,
      "text": "3.3 Memory：入口索引，不是正文仓库"
    },
    {
      "depth": 3,
      "text": "3.4 Transcript：append-only 事件流，不是聊天数组"
    },
    {
      "depth": 2,
      "text": "四、安全与扩展：硬边界 + 统一治理"
    },
    {
      "depth": 3,
      "text": "4.1 Sandbox 与 Permission：互补，不是替代"
    },
    {
      "depth": 3,
      "text": "4.2 MCP 与 Skills：统一治理，不开旁路"
    },
    {
      "depth": 3,
      "text": "4.3 多 Agent：三层架构，统一内核"
    },
    {
      "depth": 2,
      "text": "五、收口：所有设计只为两件事"
    },
    {
      "depth": 3,
      "text": "5.1 安全可靠"
    },
    {
      "depth": 3,
      "text": "5.2 有限上下文"
    },
    {
      "depth": 2,
      "text": "一页速览"
    },
    {
      "depth": 2,
      "text": "一句话收束"
    }
  ],
  "text": "> **一句话总结**：Claude Code 的全部工程复杂度，本质都在解决两个问题——**让模型的自主行为安全可靠**，以及**在有限上下文约束下把长任务跑完**。\n\n---\n\n## 开场\n\nClaude Code 的源码不久前被 Anthropic 意外泄露。这是第一次，可以从代码层面去看一个真实生产级 Agent 是怎么搭出来的。\n\n大多数人盯着 prompt 模板、tool 封装、workflow 编排在打转，但代码里展示的工程模式很反直觉：模型不是总指挥，Runtime 才是；Tool 不是函数，而是协议对象；上下文不是\"装得越多越好\"，而是要主动压缩、按需召回；多 Agent 不是为了角色扮演，而是为了**隔离上下文**。\n\n那么这些设计背后，是不是有一条统一的主线？\n\n答案是一句话：**所有设计都在回答两个问题——安全可靠 + 有限上下文**。下面分四层展开。\n\n---\n\n## 金字塔总览\n\n---\n\n## 一、Runtime 内核：CLI 只是入口，所有形态共用一条执行链\n\n### 1.1 外壳与内核：一切都汇聚到 query 循环\n\nClaude Code 的 CLI 只是入口，真正干活的是后面的本地 Agent Runtime。\n启动链：`cli.tsx` → `main.tsx` 主流程调度 → `init/setup` 安全初始化（确认目录可信、加载用户配置）→ `launch repl` → 进入 `query` 函数\n启动参数分流：`--print` 走 headless 后台、`--remote` 走远程模式、默认走完整路径（装配工具池、MCP、内建 skills、Agent 定义）\n**关键事实**：REPL / headless / SDK 嵌入 / sub-agent，**走的都是同一段 query 执行循环，不存在两套实现**\n\n> **金句**：CLI 只是壳，内核是一整套本地 Agent Runtime；主 Agent 派出的子 Agent 跑的也是同一个内核，只是上下文做了隔离。\n\n### 1.2 query 循环：本质上是一个 while\n\n剥掉所有工程外壳，Agent 的核心就是一个反复调用模型的 while：\n\n每一轮，模型决定**继续用工具还是停下来**。Claude Code 在工程上把每一轮拆成了六个阶段：\n\n| 阶段 | 干什么 |\n\n| `pre_api_phase` | 装配 attachment / skill / memory |\n| `api_request_phase` | 流式调用 API，带重试 |\n| `process_response` | 把模型输出解析成 `transition` 状态字 |\n| `execute_tools` | 按并发性分批跑，加权限、加 hooks |\n| `stop_hooks` | 轮次结束后的钩子，可否决退出 |\n| `next_turn` | 判断收工，否则 `turn_count++` |\n\n`transition` 是状态机的语义灵魂：`continue` / `tool_use` / `end_turn` / `stop_sequence`，每个值对应一种\"接下来干嘛\"的决策。\n\n### 1.3 流式调度：跑起来丝滑的底层原因\n\n整条循环是**流式生成**——模型还在生成，界面已经在显示，下游已经开始消化、开始执行工具，不用等整段回答完。这是 Claude Code \"感觉很快\"的真正原因，不是模型快，是**调度在帮模型抢时间**。\n\n### 1.4 七级错误恢复：长任务不死的真正秘密\n\nAgent 跑长任务会失败：网络断、上下文塞满、话说一半被截、token 预算耗尽……Claude Code 把这些按严重程度分了 **L1 ~ L7 七级**，每级一条恢复策略：\n\n| 级别 | 场景 | 策略 |\n\n| L1 | 流式连接中断 | 切非流式模式重试 |\n| L2 | 上下文快塞满 | 预防式折叠旧工具输出（context clipse） |\n| L3 | 上下文已爆 | 完整压缩历史成摘要 |\n| L4 | 输出预算用完 | 提升单轮输出配额 |\n| L5 | 半句被截 | 下一轮明确告诉它\"接着说\" |\n| L6 | stop_hook 拦截 | 外部检查没过，强制再跑一轮 |\n| L7 | token 预算耗尽 | 写好状态，给 resume 接口，不要直接抛 |\n\n> **金句**：七级恢复不是处理一两种异常，是把\"长任务可能死的所有方式\"都铺成了梯子——这才是 Claude Code 能跑半小时长任务的前提。\n\n---\n\n## 二、工具与执行：Tool 不是函数，而是协议对象\n\n### 2.1 Tool 接口：把治理写进协议\n\n很多框架里 Tool 就是\"一个函数 + 一段 description，模型说调就调\"。Claude Code 把 Tool 做成了协议对象，每个 Tool 必须显式声明：\n`name` / `description` / `inputSchema` / `call`\n**是否只读**\n**是否并发安全**\n**权限检查回调**\n\n这些属性直接决定了：能不能并发跑、需不需要走权限弹窗、UI 上如何呈现、能不能修改文件。\n\n**默认值是悲观的**：未声明 = 非并发 + 非只读 + 必走权限。换句话说，**新工具默认走慢路径**——串行执行、加权限检查、绕不开治理。只有显式声明\"我是只读、并发安全\"，才升级到快路径。\n\n> **金句**：安全和并发不是外包给一层管理层去管，而是**写进协议里**，变成每一个工具作者必须面对的设计责任。\n\n### 2.2 执行管线：每一层都有治理点\n\n模型输出一段 `assistant_message`，里面带一个或多个 `tool_use` block。Query 收到后**不是直接调函数**，而是走一整条治理链：\n\n关键不在层多，而在**每一层都有显式的治理点**——可拒绝、可改写、可补充上下文。模型不是想调就调，每一步都能被喊停。\n\n### 2.3 并发分批：按声明拆批起跑\n\n举例：模型一次输出七个 tool_use（A~G）：\n\n| 工具 | 并发性 | 调度 |\n\n| read_a, b, grep_c | 只读，安全 | **并发执行** |\n| file_edit_d | 写操作，不安全 | **独占串行** |\n| glob_e, web_fetch_f | 只读，安全 | **并发执行** |\n| bash_g | 不安全 | **独占串行** |\n\n安全的并发起跑，不安全的退化成单条。底层还有一个 streaming tool executor，用 `queued / executing / completed / yielded` 四状态追踪——边接收 tool_use 边启动，不等模型整段输出完。\n\n---\n\n## 三、上下文与状态：在永远有限的窗口里活下来\n\n这是整个 Runtime 的\"灵魂层\"。所有压缩、记忆、恢复机制，都在回答同一个问题：**上下文永远是有限的，怎么用得最值**。\n\n### 3.1 三段式 Prompt：稳定的放前面，会变的放后面\n\n输入侧分成三类来源：\n\n| 类型 | 内容 | 变化频率 |\n\n| `system_prompt` | 身份、规则、工具使用方式、输出风格 | **几乎不变** |\n| `user_system_context` | CLAUDE.md、当前日期、git 状态 | 每次会变 |\n| `task_specific_prompt` | compact / session memory 等后台任务专用 | 任务相关 |\n\nSystem prompt 内部又切成「静态主干 + 动态边界」两段——前者放前面，后者放后面。**为什么这么排？** 因为服务器端的 prompt cache 按**前缀匹配**，前缀越稳定越长，重复请求命中率越高、token 越便宜。**排序本身就是工程优化**。\n\n身份槽（system_prompt）还有一条优先级链：`override > coordinator > agent > custom > default`。这条链解释了一件事——**sub-agent 不是模型变了，是模型\"看到自己是谁\"变了**。\n\n### 3.2 五级压缩梯子：每轮都做上下文治理\n\n上下文会被工具输出挤爆。Claude Code 的压缩从轻到重分五级：\n\n| 级别 | 名字 | 做什么 |\n\n| 最轻 | `snip` | 把旧的、价值低的工具输出**直接删掉**，不做摘要（因为做摘要本身要花 token） |\n| 轻 | `micro_compact` | 清理旧 tool_result，**保住前缀稳定**不破坏 cache |\n| 中 | `context_clipse` | 把多轮相似操作折叠成一段结构化摘要 |\n| 重 | `auto_compact` | 阈值触发的完整压缩，单独请求模型总结历史 |\n| 最重 | `session_memory_compact` | 复用后台已抽取的 session memory，避免再调一次总结模型 |\n\n两条关键设计：\n**轻量优先**：每轮 API 请求前都按梯子顺序尝试，前面的策略已经把占用降到阈值以下，就不进入完整 compact——因为 auto_compact 自己也会失败、成本更高\n**完整压缩 ≠ 删历史**：是 `summary` + `post_compact_attachment` **重建工作台**（重新塞回工具声明、文件上下文、计划状态），否则模型会忘了自己刚干到哪一步\n\n### 3.3 Memory：入口索引，不是正文仓库\n\n`memory.md` **不是正文仓库，而是入口索引**——每条记忆只记一个链接 + 一行描述，整个文件限制在 200 行 / 25 KB 以内。\n\n为什么？因为如果让 memory 直接进 prompt，几次会话下来 prompt 就被长期状态彻底挤满了。所以走的是另一条思路——**不是全量注入，而是按需召回**：\n\n这个流程叫 `relevant_recall`。Memory 还分三类：`auto_memory`（用户偏好，后台抽取）/ `session_memory`（当前会话滚动摘要）/ `agent_memory`（特定 Agent 长期经验，声明了 memory 字段才有）。\n\n### 3.4 Transcript：append-only 事件流，不是聊天数组\n\nClaude Code 关掉再打开还能接着跑，靠的是 transcript。它**不是聊天记录数组，而是 append-only 事件流**：\n每个事件有唯一 `uuid`，并指向前一条 `parent_uuid`，靠这两个字段连成一条链\n主链有边界：只有四类事件能进——用户输入、模型输出、附加内容、系统消息\n写盘是 append-only + 批量 flush（内存队列 → 后台刷盘）\n文件尾部周期性重挂元数据（标题、模式、worktree），因为 resume 列表页用的是轻量读取器，只扫尾部一小段窗口\n\nResume 是一条四步重建管线：\n**加载日志**：读整个 JSONL，按事件类型分桶\n**重建主链**：从叶子节点沿 `parent_uuid` 一路回溯\n**修复断点**：处理被 snip 删掉的中间节点造成的空洞，往前找还活着的祖先重新挂上\n**恢复运行态**：plan、文件读取历史、context clipse 状态、Agent 模式，全部挂回内存，再把控制权交还给 REPL\n\n---\n\n## 四、安全与扩展：硬边界 + 统一治理\n\n### 4.1 Sandbox 与 Permission：互补，不是替代\n\n很多人会问：既然有 sandbox，为什么还要 permission？答案是**两层各干各的事**：\n**Permission**：回答\"这条命令**能不能**执行\"\n**Sandbox**：处理\"已经允许的命令**怎么被进一步限制**\"\n\n每条 bash 命令到达宿主机之前要走四步：\n**逐条路由**：判断要不要进沙箱，不符合就走普通路径\n**配置翻译**：把用户配置（allowlist、读写目录）**翻译成隔离环境真实可执行的限制**\n**隔离运行**：shell 层把命令包装到隔离环境里跑\n**收尾清理**：清掉临时文件、可能影响后续的残留状态\n\n> **金句**：Sandbox 不是软规则，是硬边界。配置不是拿来对照检查，而是被翻译成系统级限制。\n\n另一个重要细节：**设置文件 / .claude 目录 / skill 目录**列入禁止写入范围——防止一个被攻陷的命令通过修改 Agent 自己的配置来扩大影响。\n\n### 4.2 MCP 与 Skills：统一治理，不开旁路\n\n外部能力接入有两条路径，但**都不开旁路**：\n\n| 入口 | 是什么 | 怎么治理 |\n\n| **MCP** | 外部工具入池 | 标准化成内建 Tool 对象，继续走 schema / permission / tool_result 整套链 |\n| **Skills** | 任务协议注入 | markdown + frontmatter + 可选脚本，**按需激活**而非全量常驻 |\n\nSkills 的激活有三条入口：用户主动调用 / 模型自主选择 / **path 字段条件触发**（比如改 .py 文件时自动激活 Python 相关 skill）。目标只有一个——**避免一堆备用技能把上下文撑爆**。\n\nTool search 还做了**延迟加载**：工具数量多时（一堆 MCP），不会一次性把所有接口描述塞进 prompt，而是先暴露一个轻量发现入口，按任务需要再展开具体工具边界。这直接降低 prompt 体积、提高 cache 命中。\n\n### 4.3 多 Agent：三层架构，统一内核\n\n| 层 | 形态 | 解决什么 |\n\n| **Sub-agent** | 单入子链，复用 Runtime，**隔离 transcript** | 防止主链被工具产物撑爆 |\n| **Coordinator** | 不是多开 sub-agent，而是**把主 Agent 的 system_prompt 换掉**，让它变成派工人 | 任务编排 |\n| **Swarm** | mailbox + task_board + team_file | 团队协作、空闲自找活 |\n\nSub-agent 的本质很容易被误解。看起来像\"不同角色干不同事\"，**实际上它几乎只是为了解决有限上下文**：\nSub-agent 调的还是同一个 API，不是别的模型\n它的 prompt 被大幅精简，中间产生的 tool_use 累积上下文**对主 Agent 完全屏蔽**\n主 Agent 只拿到 sub-agent 返回的一条摘要（典型动画：3 条原始消息 → 1 条摘要）\n\n所有这些多 Agent 入口虽然分了层，但**底下还是同一个 Runtime、同一条 query 执行链**。\n\n---\n\n## 五、收口：所有设计只为两件事\n\n把整套架构压成一句话：**模型意图 → 受治理的本地行动 → 可恢复的长期任务**。\n\n从下到上三层：\n\n再往上抽，所有设计都在回答两个问题：\n\n### 5.1 安全可靠\n\n让大模型的自主行为是相对安全的——不会随便删你的重要文件、不会搞乱系统。Permission 默认放行但走串行慢路径、Sandbox 把配置翻译成硬边界、控制平面禁止写入、MCP/Skills 不开旁路——**这些设计累加起来，才让一个能执行 shell 的 Agent 敢交给普通用户用**。\n\n### 5.2 有限上下文\n\n上下文永远是有限的，**而且这个约束不会消失**——就算未来上下文真的无限了，模型也很难在那么长的输入里找到重点（密集注意力计算撑不住，也没有重心）。所以：\nPrompt 分静态/动态两段是为了 cache 命中\n五级压缩梯子是为了在不丢工作台的前提下腾空间\nMemory 做成索引 + 召回是为了不挤占 prompt\nTranscript append-only 是为了任务可恢复\n**Sub-agent 几乎只是为了上下文隔离**——这点最容易被想成\"多角色协作\"\n\n---\n\n## 一页速览\n\n| 层级 | 内容 | 一句话核心 |\n\n| **核心结论** | 安全可靠 + 有限上下文 | Claude Code 全部复杂度都在回答这两个问题 |\n| **Runtime 内核** | CLI 只是壳 | 所有形态共用 query 执行循环 + 七级错误恢复 |\n| **工具与执行** | Tool 是协议对象 | 治理写进协议，默认走慢路径 |\n| **上下文与状态** | Prompt / 压缩 / Memory / Transcript | 永远有限的窗口里要活下来 |\n| **安全与扩展** | Sandbox + Permission 互补 | MCP/Skills 不开旁路，多 Agent 统一内核 |\n\n---\n\n## 一句话收束\n\n> **模型不是总指挥，Runtime 才是。所有看起来复杂的 Agent 工程，本质都是在用最少的 token 把最多的事情做安全。**"
}
