本文为作者学习Learn claude code过程中记下的笔记。
Learn Claude Code仓库:https://github.com/shareAI-lab/learn-claude-code


1. 工具的设计

只有 bash 时, 所有操作都走 shell。cat 截断不可预测, sed 遇到特殊字符就崩, 每次 bash 调用都是不受约束的安全面。专用工具 (read_file, write_file) 可以在工具层面做路径沙箱。

这句话是在解释 为什么需要专门的工具,而不是什么都用 bash(shell命令)。

1.1 问题:只用 Bash 的缺点

假设只有 bash 工具,LLM 想读文件时会这样:

cat myfile.txt           # 问题1: 文件大时输出超长
sed -i 's/old/new/g' f   # 问题2: 特殊字符会破坏命令 (如 `&`, `/`, `$`)
rm important.txt         # 问题3: bash 就是个安全漏洞,任何危险命令都能执行
ls / | some_command      # 问题4: 可以访问任意目录
缺点 例子 问题
输出不可控 cat 大文件 可能刷爆终端
特殊字符崩溃 sed $var $ 被 shell 解析出错
不安全 rm -rf / 没有限制,LLM 可能误操作
路径逃逸 cat /etc/passwd 可以访问工作目录外的文件

1.2 解决方案:专用工具 + 路径沙箱

def safe_path(p: str) -> Path:
    path = (WORKDIR / p).resolve()           # 把相对路径转为绝对路径
    if not path.is_relative_to(WORKDIR):      # 检查是否在允许范围内
        raise ValueError(f"Path escapes workspace: {p}")
    return path

def run_read(path: str, limit: int = None) -> str:
    text = safe_path(path).read_text()        # 只能在 WORKDIR 内读取
    # ...

这样 即使 LLM 被诱导 执行 read_file(path="/etc/passwd"),也会被 safe_path 拦截,因为 /etc/passwd 不在 WORKDIR 内。

1.3 总结

方案 优点 缺点
只用 bash 灵活强大 危险、不可预测、路径不可控
专用工具 安全、可控、可限制范围 需要事先定义好要暴露的功能

核心思想:给 LLM 的能力要"最小化原则"——只给它完成目标需要的工具,而不是把所有权限都放开。


2. ToDoWrite

2.1 问题

多步任务中, 模型会丢失进度 -- 重复做过的事、跳步、跑偏。对话越长越严重: 工具结果不断填满上下文, 系统提示的影响力逐渐被稀释。一个 10 步重构可能做完 1-3 步就开始即兴发挥, 因为 4-10 步已经被挤出注意力了。

通过 TodoWrite 让计划可见

我们不让模型在思维链中默默规划,而是强制通过 TodoWrite 工具将计划外化。每个计划项都有可追踪的状态(pending、in_progress、completed)。这有三个好处:(1) 用户可以在执行前看到 agent 打算做什么;(2) 开发者可以通过检查计划状态来调试 agent 行为;(3) agent 自身可以在后续轮次中引用计划,即使早期上下文已经滚出窗口。

同一时间只允许一个任务进行中

TodoWrite 工具强制要求任何时候最多只能有一个任务处于 in_progress 状态。如果模型想开始第二个任务,必须先完成或放弃当前任务。这个约束防止了一种隐蔽的失败模式:试图通过交替处理多个项目来'多任务'的模型,往往会丢失状态并产出半成品。顺序执行的专注度远高于并行切换


3. 子 Agent 上下文隔离

"大任务拆小, 每个小任务干净的上下文" -- 子agent用独立 messages[], 不污染主对话

agent工作越久, messages 数组越胖。每次读文件、跑命令的输出都永久留在上下文里。"这个项目用什么测试框架?" 可能要读 5 个文件, 但父agent只需要一个词: "pytest。

  1. 父agent有一个 task 工具。子agent拥有除 task 外的所有基础工具 (禁止递归生成)。
  2. 子agent以 messages=[] 启动, 运行自己的循环。只有最终文本返回给父agent。
  3. 子agent可能跑了 30+ 次工具调用, 但整个消息历史直接丢弃。父agent收到的只是一段摘要文本, 作为普通 tool_result 返回。

4. Skill Loader

这是 两层 Skill 注入 的设计。让我逐步解释:

4.1 核心设计思想

不要把所有 skill 内容都塞进 system prompt(太长了)
只在 system prompt 放 skill 名称列表(Layer 1)
等 LLM 需要时,再通过 tool_result 把完整内容注入给 LLM(Layer 2)

4.2 Layer 1:System Prompt(只放元数据)

SYSTEM = f"""You are a coding agent at {WORKDIR}.
Skills available:
{SKILL_LOADER.get_descriptions()}"""

输出长这样:

You are a coding agent at C:\Users\wsmdm\Desktop\learn-claude-code.
Skills available:
  - pdf: Process PDF files
  - code-review: Review code changes

只占几十个 token,告诉 LLM"有哪些技能可以用"。

4.3 Layer 2:按需加载(完整内容)

当 LLM 调用 load_skill("pdf") 时:

"load_skill": lambda **kw: SKILL_LOADER.get_content(kw["name"]),

返回的是 完整的 skill 文档,被包装成 <skill> 标签:

<skill name="pdf">
## PDF Processing

1. Use pymupdf to read PDF
2. Extract text with...
3. ...
</skill>

这通过 tool_result 机制注入:

results.append({"type": "tool_result", "tool_use_id": block.id, "content": str(output)})
messages.append({"role": "user", "content": results})

4.4 为什么这样设计?

问题 System Prompt 全塞进去 按需加载(两层)
token 消耗 巨大(10个skill可能几万元) 极低
LLM 能用到的 skill 全部(但可能上下文不够) 只有用到的那些
灵活性

4.5 流程图

LLM 说:"我要处理 PDF"
         ↓
调用 load_skill(name="pdf")
         ↓
返回 <skill>pdf 完整文档</skill> 作为 tool_result
         ↓
这条 tool_result 附加到 messages 里发回给 LLM
         ↓
LLM 现在"看到了"完整的 PDF 处理指南
         ↓
继续执行任务

简单说:System Prompt 只告诉你"有哪些工具箱",等你真的要开某个工具时,才把工具箱里的说明书给你看。


5. 上下文压缩

5.1 三层压缩策略

  • 每轮运行的微压缩:几乎零成本:它截断旧消息中的 tool_result 块,去除不再需要的冗长命令输出。
  • token 数超过阈值时触发的自动压缩:调用 LLM 生成对话摘要,代价高但能大幅缩减上下文。
  • 用户触发的手动压缩:用于明确的'重新开始'场景。

分层意味着低成本操作持续运行(保持上下文整洁),而高成本操作很少触发(仅在真正需要时)。

5.2 摘要替换全部消息,而非保留部分历史

自动压缩触发时,生成摘要并替换全部消息历史,不会在摘要旁保留最近的 N 条消息。

这避免了一个微妙的连贯性问题:如果同时保留近期消息和旧消息的摘要,模型会看到重叠内容的两种表示。摘要可能说'我们决定使用方案 X',而近期消息仍在展示讨论过程,产生矛盾信号。

干净的摘要是一个连贯的单一叙述。

5.3 完整对话以 JSONL 格式归档到磁盘

完整的未压缩对话仍会追加到磁盘上的 JSONL 文件中。每条消息、每次工具调用、每个结果都不会丢失。

压缩对内存上下文是有损操作,但对永久记录是无损的。事后分析(调试 agent 行为、计算 token 用量、提取训练数据)始终可以基于完整记录进行。JSONL 格式仅追加写入,对并发写入安全,易于流式处理。


6. 任务系统

"大目标要拆成小任务, 排好序, 记在磁盘上" -- 文件持久化的任务图, 为多 agent 协作打基础。

写入磁盘,建立依赖关系,实现任务持久化

s03 的 TodoManager 问题:

todos = [
    {"content": "任务 A"},
    {"content": "任务 B"},
    {"content": "任务 C"},
]

6.1 只有内容 + 是否完成,没有结构

真实目标的样子:

graph TD
    A[A 完成] --> B
    A --> C
    B & C --> D & E
    D & E --> F
    style A fill:#90EE90
    style B fill:#fff
    style C fill:#fff
    style D fill:#fff
    style E fill:#fff
    style F fill:#fff
  • A (完成) → B 和 C 可以并行
  • D 和 E 都完成后才能做 F

为什么"agent分不清"

  1. 不知道什么能做
Agent: "还有啥任务?"
系统:  [A, B, C, D, E, F]
  • Agent 不知道 B 被 A 卡住,也不知道 C 和 D 可以同时跑 → 只能瞎猜或者一股脑全做
  1. 不知道什么被卡住
    A 没做完时,B 实际上"被阻塞"了,但清单里 B 就是一条记录,状态还是 "todo",Agent 以为能做,其实做了也白做(等 A 成果)

  2. 不能表达并行

  • C 和 D 独立,没有任何依赖
  • 但清单里跟 B 排在一起,看不出谁和谁相关
  • → Agent 可能串行做 C、D,白白浪费时间
  1. 上下文压缩就丢了

6.2 TaskManager: JSON 文件持久化

S03: 内存中的列表
self.todos = []

S06 的 micro_compact 会压缩消息
如果任务状态只存在于 Messages 列表里,压缩后任务清单就消失了!

S07 的改进
{
    "id": 1, "subject": "A", "status": "completed", "blockedBy": [], "blocks": [2, 3]
}
{
    "id": 2, "subject": "B", "status": "pending", "blockedBy": [1], "blocks": [4]
}
{
    "id": 3, "subject": "C", "status": "pending", "blockedBy": [1], "blocks": [4]
}
{
    "id": 4, "subject": "D", "status": "pending", "blockedBy": [2, 3], "blocks": []
}
  • 文件持久化: .tasks/task_1.json 在磁盘上,s06 压缩不影响
  • blockedBy 字段: 显式表达"被谁阻塞"
  • blocks 字段: 双向维护,D 知道自己在等谁
  • scan_unclaimed_tasks(): 能筛选出 pending + 无 owner + 无 blockedBy 的任务
  • 并行检测: 多个任务同时 blockedBy: [] → 可以并行

6.6 核心区别

维度 s03 扁平清单 s07+ 有向图
依赖关系 blockedBy/blocks
状态 done/not done pending/in_progress/completed
持久化 内存 JSON 文件
可执行判断 不知道 blockedBy 为空才能做
并行识别 不知道 无依赖的任务可并行

6.7 后台任务

"慢操作丢后台, agent 继续想下一步" -- 后台线程跑命令, 完成后注入通知。

启动后台任务不等待,任务完成后放队列,LLM调用前取走结果,队列保证不重复通知

6.8 Agent Team

6.8.1 实现架构
sequenceDiagram
    participant User as 用户
    participant Agent as 主线程 (Agent)
    participant BG as 后台线程
    participant LLM as LLM API

    Note over Agent: agent_loop()
    Agent->>Agent: drain_notifications()
    Note over Agent: 从通知队列取结果,注入到 messages

    Agent->>LLM: client.messages.create()
    LLM-->>Agent: response

    loop for block in response.content
        Agent->>BG: BG.run(command)
        BG-->>Agent: 立即返回 (启动线程)
    end

    Note over BG: subprocess.run(command)
    Note over BG: 命令执行中... (30分钟)
    BG->>BG: 执行完毕/超时/出错
    BG->>BG: with lock: queue.append(结果)

6.8.2 关键组件
6.8.2.1 守护线程 (daemon=True)
thread = threading.Thread(
    target=self._execute,      # 线程执行的函数
    args=(task_id, command),   # 函数参数
    daemon=True                 # 守护模式
)
thread.start()

守护线程 = 主进程退出时会被强制终止的线程

sequenceDiagram
    participant User as 用户
    participant Main as 主进程
    participant BG as 守护线程

    User->>Main: quit
    Main->>Main: 退出
    Note over Main: 守护线程被杀死 (不管任务跑没跑完)

为什么用守护线程?

  • 用户 quit 后,不需要后台任务结果了
  • 避免主进程卡住等慢任务
6.8.2.2 线程锁 (threading.Lock)
self._lock = threading.Lock()

with self._lock:
    self._notification_queue.append({…})

为什么需要锁?

时间线:

sequenceDiagram
    participant Agent as Agent 线程
    participant BG as 后台线程

    Note over Agent: drain() 读队列 (还没数据)
    BG->>BG: 命令执行完,写队列
    BG->>BG: with lock: queue.append(结果)
    Agent->>Agent: 看到空队列,返回 []
    Note over Agent: 结果丢了,没人拿到

加锁保证:读写队列不会同时发生。

6.8.2.3 通知队列
_notification_queue = []

后台线程执行完,写入:

with self._lock:
    _notification_queue.append({
        "task_id": "abc",
        "status": "completed",
        "result": "…"
    })

Agent 线程取出:

with self._lock:
    notifs = list(_notification_queue)
    _notification_queue.clear()  # 清空
return notifs

为什么要清空?

第1轮: 队列 = [结果A]
drain → notifs = [结果A],队列 = []

第2轮: 队列 = []
drain → notifs = [],队列 = []

如果不清空:
第2轮: 队列 = [结果A]
drain → notifs = [结果A] ← 重复通知,LLM 会困惑


6.8.3 完整流程
用户: "编译项目"

Agent:
    │ LLM 调用
    ├─→ 返回 background_run("make")
    │
    BG.run("make")
        │
        ├── task_id = "abc123"
        ├── 创建守护线程
        ├── thread.start()  ← 立即返回
        └── 返回 "后台任务 abc123 已启动"

Agent:
    │ 继续工作…
    │
    │ (编译在后台跑了30分钟)

30分钟后:
Agent:
    │ LLM 调用前
    │  ↓
    drain_notifications()
        │
        ├── 取走 [结果]
        └── 队列清空
    │
    ├─→ 注入 "<background-results>…编译完成…</background-results>"
    │
    │ LLM 调用
    ├─→ LLM 看到 "编译完成了"

6.8.3 完整流程
sequenceDiagram
    participant User as 用户
    participant Agent as Agent
    participant BG as 后台线程

    User->>Agent: "编译项目"

    Agent->>Agent: LLM 调用
    Agent-->>Agent: 返回 background_run("make")

    Agent->>BG: BG.run("make")
    Note over BG: 创建守护线程,返回 task_id
    BG-->>Agent: "后台任务 abc123 已启动"
    Agent->>Agent: 继续工作...

    Note over BG: 命令执行中... (30分钟)

    BG->>BG: 执行完成,写入队列

    Agent->>Agent: LLM 调用前 drain_notifications()
    Note over Agent: 取走结果,队列清空
    Agent->>Agent: 注入 "<background-results>编译完成</background-results>"
    Agent->>LLM: LLM 调用
    Agent-->>User: "编译完成了"

7. Agent Teams

7.1 Teammates + Mailboxes

"任务太大一个人干不完, 要能分给队友" -- 持久化队友 + JSONL 邮箱。

Harness 层: 团队邮箱 -- 多个模型, 通过文件协调

子agent (s04) 是一次性的: 生成、干活、返回摘要、消亡。没有身份, 没有跨调用的记忆。后台任务 (s08) 能跑 shell 命令, 但做不了 LLM 引导的决策。

真正的团队协作需要三样东西:

  • (1) 能跨多轮对话存活的持久agent,
  • (2) 身份和生命周期管理
  • (3) agent之间的通信通道。
s09_agent_teams.py - Agent 团队

持久化命名 Agent,基于文件的 JSONL 收件箱。

每个队友在独立线程中运行自己的 Agent 循环。通过仅追加的收件箱通信。

    子 Agent (s04):  spawn -> execute -> return summary -> destroyed

    队友 (s09):      spawn -> work -> idle -> work -> … -> shutdown

    .team/config.json                   .team/inbox/

    +----------------------------+      +------------------+
    | {"team_name": "default",  |      | alice.jsonl      |
    |  "members": [              |      | bob.jsonl        |
    |    {"name":"alice",        |      | lead.jsonl       |
    |     "role":"coder",        |      +------------------+
    |     "status":"idle"}       |
    |  ]}                        |      send_message("alice", "fix bug"):
    +----------------------------+         open("alice.jsonl", "a").write(msg)

                                      read_inbox("alice"):
    spawn_teammate("alice","coder",…)    msgs = [json.loads(l) for l in …]
         |                                open("alice.jsonl", "w").close()
         v                                return msgs  # drain

    Thread: alice              Thread: bob

    +------------------+       +------------------+
    | agent_loop       |       | agent_loop       |
    | status: working  |       | status: idle     |
    | … runs tools     |       | … waits …        |
    | status -> idle   |       |                  |
    +------------------+       +------------------+

    5 种消息类型 (全部声明,此处未全部处理):

    +-------------------------+-----------------------------------+
    | message                 | 普通文本消息                       |
    | broadcast               | 发送给所有队友                     |
    | shutdown_request        | 请求优雅关闭 (s10)                |
    | shutdown_response       | 批准/拒绝关闭 (s10)                |
    | plan_approval_response  | 批准/拒绝计划 (s10)               |
    +-------------------------+-----------------------------------+

核心思想: "可以相互对话的队友。"

7.2 设计决策

7.2.1 与subagent的区别
  • 在 s04 中,子agent是临时的:创建、执行一个任务、返回结果、销毁。它们的知识随之消亡。
  • 在 s09 中,队友是具有身份(名称、角色)和配置文件的持久化线程。队友可以完成任务 A,然后被分配任务 B,并携带之前学到的所有知识。持久化队友积累项目知识,理解已建立的模式,不需要为每个任务重新阅读相同的文件。

我理解的teamate就是状态不隔离,持久化,可互相通信的subagent

7.2.2 团队配置持久化
  • 团队结构(成员名称、角色、agent ID)存储在 JSON 配置文件中,而非任何 agent 的内存中。
  • 任何 agent 都可以通过读取配置文件发现队友——无需发现服务或共享内存。如果 agent 崩溃并重启,它读取配置即可知道团队中还有谁。
  • 这与 s07 的理念一致:文件系统就是协调层。配置文件人类可读,便于手动添加或移除团队成员、调试团队配置问题。
7.2.3 工具分发
  • 团队组长获得 ALL_TOOLS(包括 spawn、send、read_inbox 等),而队友获得 TEAMMATE_TOOLS(专注于任务执行的精简工具集)。
  • 队友专注于做事(编码、测试、研究)
  • 组长专注于协调(创建任务、分配工作、管理沟通)。

8. team_protocols

"队友之间要有统一的沟通规矩" -- 一个 request-response 模式驱动所有协商。

基于 s09 的团队消息系统,加了 Shutdown 协议 和 Plan Approval 协议,两个协议都通过 request_id 关联请求和响应。


8.1 核心:request_id 关联模式

发请求时生成 ID → 响应时带上同样 ID → 精准匹配

sequenceDiagram
    participant Lead
    participant Alice

    Lead->>Alice: req_id = "abc123"
    Note over Alice: 处理中…
    Alice-->>Lead: 带上 "abc123" 响应
    Note over Lead: 收到响应,查 "abc123",知道是谁在回应

8.2 协议一:Shutdown 关闭协议

目的:优雅关闭队友,而不是强制杀死线程。

流程

  1. Lead 想关闭 alice
  2. 生成 req_id = "abc123"
  3. 把 req_id 存到 tracker
  4. 发送 shutdown_request 给 alice
  5. alice 收到,决定批准还是拒绝
  6. 发送 shutdown_response 带上 req_id
  7. Lead 更新 tracker 状态
  8. alice 线程退出或继续工作

状态机

pending → approved (线程退出)
        → rejected (继续工作)

8.3 协议二:Plan Approval 计划审批协议

目的:队友做重大操作之前,必须获得 Lead 批准。

流程

  1. alice 要做大事 (比如重构整个模块)
  2. 生成 req_id = "xyz789"
  3. 把计划和 req_id 发给 Lead
  4. Lead 看计划,决定批准还是拒绝
  5. alice 收到响应
    • approve=true → 开始执行
    • approve=false → 修改计划,重新提交

8.4 图解

sequenceDiagram
    participant Lead
    participant Alice

    Note over Lead,Alice: request_id 关联请求和响应

    rect rgb(200, 220, 255)
        Note over Lead,Alice: Shutdown 协议
        Lead->>Alice: shutdown_request + req_id
        Alice-->>Lead: shutdown_response + req_id
        Note over Lead: 线程退出
    end

    rect rgb(220, 255, 220)
        Note over Lead,Alice: Plan Approval 协议
        Alice->>Lead: plan_approval + req_id + 计划内容
        Note over Lead: 看到计划,决定
        Lead-->>Alice: plan_approval_response + req_id + approve?
        Note over Alice: 执行/修改计划
    end

8.5 设计决策

JSONL 收件箱文件而非共享内存

收发信息即维护JSON,只加不减。保证并发写入时的安全,不破坏数据;如果写入时崩溃仅留下不完整数据,读取者可以跳过。

五种消息类型覆盖所有协调模式

消息系统恰好支持五种类型:

  • (1) message 用于两个 agent 间的点对点通信;
  • (2) broadcast 用于全团队公告;
  • (3) shutdown_request 用于优雅终止;
  • (4) shutdown_response 用于确认终止;
  • (5) plan_approval_response 用于组长批准或拒绝队友的计划。

这五种类型映射到基本协调模式:直接通信、广播、生命周期管理和审批流程。

每次 LLM 调用前检查收件箱

队友在每次 agent 循环迭代的顶部、调用 LLM API 之前检查收件箱文件。这确保了对传入消息的最大响应性:响应快,成本低(读取小文件消耗token少),可以影响下次调用。


9. 自主Agent

9.1 Scan Board, Claim Tasks

9.2 设计决策

9.2.1 轮询未认领任务而非事件驱动通知
  • 自主队友每隔约 1 秒轮询共享任务板任务。
  • 轮询从根本上比发布/订阅更简单:
    • 没有订阅管理
    • 没有事件路由
    • 没有事件丢失的 bug
  • 在基于文件的持久化下,轮询保证响应速度,降低文件系统开销。
9.2.2 空闲 60 秒后自动终止

不会因为任务完成到新任务创建之间的短暂间隔而导致过早关闭;又足够短,不会让闲置队友浪费资源。

9.2.3 上下文压缩后重新注入队友身份
  • 自动压缩对话时,生成的摘要会丢失关键元数据:队友的名称、所属团队和 agent_id。没有这些信息,队友无法认领任务(任务按名称归属)、无法检查收件箱(收件箱文件以 agent_id 为键)、也无法在消息中表明身份。

  • 因此每次自动压缩后,系统会向对话中重新注入一个结构化的身份块:'你是 [team] 团队的 [name],你的 agent_id 是 [id],你的收件箱在 [path]。'这是队友在记忆丢失后保持功能所需的最小上下文。


10. Worktree + 任务隔离

(codex的worktree应该也是这个思路)

任务管目标, worktree 管目录, 按 ID 绑定

agent已经能自主认领和完成任务。但所有任务共享一个目录。两个agent同时重构不同模块 -- A 改 config.py, B 也改 config.py, 未提交的改动互相污染, 谁也没法干净回滚。

任务板管 "做什么" 但不管 "在哪做"。解法: 给每个任务一个独立的 git worktree 目录, 用任务 ID 把两边关联起来。

用 git worktree 做目录级隔离,让多个任务真正并行执行而互不干扰。

10.1 场景

核心问题

  • 任务 A: 重构登录模块
  • 任务 B: 修复支付 bug

如果都在同一个目录改代码:

A 在改 auth.py  ←──────────────┐
B 也在改 auth.py  ──────────────┼──→ 冲突!代码乱套

结果:谁先提交谁覆盖,谁都可能被卡住等对方


解决方案:目录隔离

主仓库
├── auth.py (原始版本)
│
├── .worktrees/
│    ├── auth-refactor/     ← A 的工作目录 (wt/auth-refactor 分支)
│    │    └── auth.py        (重构版本)
│    │
│    └── payment-fix/        ← B 的工作目录 (wt/payment-fix 分支)
│         └── auth.py        (支付修复版本)

A 和 B 在各自目录改代码
互不干扰,可以同时工作
完成后可以选择:
  - keep: 保留这个 worktree,合并到主分支
  - remove: 删除 worktree,代码丢弃

10.2 三个核心组件

  1. TaskManager - 控制平面

    任务 A (id=1): "重构登录" → worktree: "auth-refactor"
    任务 B (id=2): "修复支付" → worktree: "payment-fix"

    • 任务是"想法",存在 .tasks/ 目录
    • 记录谁在做,状态是什么
  2. WorktreeManager - 执行平面

    创建 worktree → 绑定到某个任务
    在 worktree 里执行命令
    完成后 keep 或 remove

  3. EventBus - 可观测性

    事件 说明
    worktree.create.before 创建前
    worktree.create.after 创建后
    worktree.remove.before 删除前
    worktree.remove.after 删除后
    task.completed 任务完成

    用于追踪整个生命周期,出问题了能查日志


10.3 工作流程

  1. 创建任务

    task_create("重构登录模块")
         ↓
    任务写入 .tasks/task_1.json
    status: pending
    
  2. 创建 worktree 并绑定

    worktree_create("auth-refactor", task_id=1)
         ↓
    git worktree add -b wt/auth-refactor .worktrees/auth-refactor
         ↓
    任务状态: pending → in_progress
    worktree 绑定到任务
    
  3. 在 worktree 里工作

    worktree_run("auth-refactor", "git status")
    worktree_run("auth-refactor", "python test.py")
         ↓
    所有命令在隔离目录里执行
    
  4. 完成后选择

    worktree_keep("auth-refactor")
        → 保留,继续在 auth-refactor 分支工作
    
    worktree_remove("auth-refactor", complete_task=True)
        → 删除 worktree,任务标记完成
    

10.4 架构图

graph TB
    subgraph 控制平面
        TM[TaskManager]
        TM -->|"管理"| T1[task_1.json<br/>subject: 重构<br/>worktree: auth-refactor]
        TM -->|"管理"| T2[task_2.json<br/>subject: 支付<br/>worktree: payment-fix]
    end

    TM -->|"绑定"| WM

    subgraph 执行平面
        WM[WorktreeManager]
        WM -->|"创建"| WT1[wt/auth-refactor/]
        WM -->|"创建"| WT2[wt/payment-fix/]
    end

    subgraph 主仓库
        ROOT[auth.py 原始]
    end

目录结构:

.git/                           # 主仓库
├── auth.py                     # 原始文件
├── .worktrees/
│   ├── index.json             # 工作树索引
│   ├── events.jsonl           # 生命周期事件日志
│   ├── wt/
│   │   ├── auth-refactor/     # 任务 A 的 worktree (独立分支)
│   │   └── payment-fix/       # 任务 B 的 worktree (独立分支)

总结

这篇文章梳理了 Claude Code 的 10 个核心设计:

章节 设计 核心价值
1 专用工具 + 路径沙箱 安全可控
2 TodoWrite 计划可见、状态可追踪
3 子 agent 上下文隔离 避免污染、保持简洁
4 Skill Loader 两层注入 按需加载、节省 token
5 上下文压缩 平衡成本与历史
6 任务系统 + 后台任务 持久化、并行化
7 Agent Teams 多 agent 协作
8 Team Protocols 统一的沟通协议
9 自主 agent 自动认领任务
10 Worktree 隔离 任务级目录隔离

核心思想

  • 可控性:最小权限原则,只暴露必要的工具
  • 持久化:磁盘文件作为协调层,重启不丢失
  • 隔离性:子 agent 独立上下文、任务独立目录
  • 按需加载:Skill 按需注入,控制 token 成本

这些设计的共同目标是让 agent 在长期、多任务、复杂场景下保持稳定、可控、可扩展。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐