一、第一阶段1.1 S01 最小智能体一个工具 一个循环Agent Loop把工具的结果送回模型继续推理。1.1.1 TOOLS 定义里只有一个bash工具TOOLS [{ name: bash, description: Run a shell command., input_schema: { type: object, properties: {command: {type: string}}, required: [command], }, }]1.1.2 循环代码后面章节都在这个循环上叠加机制循环本身始终不变while True: # 把消息历史、system prompt 和工具定义一起发给模型 response client.messages.create( modelMODEL, systemSYSTEM, messagesmessages, toolsTOOLS, max_tokens8000, ) # 加模型回答作下一轮的上下文参考 messages.append({role: assistant, content: response.content}) # 是否调用工具否-结束 if response.stop_reason ! tool_use: return # 执行工具调用收集结果 results [] for block in response.content: if block.type tool_use: # 调用bash output run_bash(block.input[command]) results.append({ type: tool_result, tool_use_id: block.id, content: output, }) # 把工具结果作为新消息追加继续循环 messages.append({role: user, content: results})SYSTEM fYou are a coding agent at {目录}. Use bash to solve tasks. Act, dont explain.1.1.3 CC源码1循环结构差异stop_reason不作为循环继续的唯一依据。流式响应中只要检测到tool_use块就设needsFollowUp为true2State 对象 10 字段教学版只用 messages1.messages: 当前迭代的消息数组 2.toolUseContext:工具、信号、权限上下文3.autoCompactTracking:压缩状态追踪 4.maxOutputTokensRecoveryCount: token 恢复尝试次数上限 35.hasAttemptedReactiveCompact: 本轮是否已尝试响应式压缩6.maxOutputTokensOverride: 8K→64K 的升级覆盖7.pendingToolUseSummary: 后台Haiku 生成的 tool use 摘要8.stopHookActive:停止钩子是否产生阻塞错误9.turnCount: 轮次计数(maxTurns检查)10.transition:上一次继续原因3多条退出和继续路径多条退出和继续路径覆盖 blocking limit、prompt too long、model error、abort、hook stop、max turns、token budget continuation、reactive compact retry 等场景。4流式工具执行和 QueryEngine让工具在模型还在生成时就开始并行执行根据工具是否 concurrency-safe 决定并发或独占。QueryEngine.ts额外加了费用超限、结构化输出验证失败等保护。1.1.4 测试prompta. Create a file called hello.py that prints Hello, World!b. List all Python files in this directoryc. What is the current git branch?1.2 S02 工具箱1.2.1 TOOLS 定义了5个工具bash、read_file、write_file、edit_file、glob文件工具加了safe_path只能操作项目目录里的文件防止智能体越界访问系统文件。1.2.2 循环run_bash被替换为查找分发。results [] for block in response.content: if block.type tool_use: # 查找工具 handler TOOL_HANDLERS.get(block.name) # 找到工具则调用 output handler(**block.input) if handler else fUnknown: {block.name} results.append({type: tool_result, tool_use_id: block.id, content: output})SYSTEM fYou are a coding agent at {目录}. Use tools to solve tasks. Act, dont explain.注意这里的工具是顺序调用的。CC 的做法更复杂按原始顺序切成连续 batchbatch 内并发安全的工具并行执行batch 间严格顺序。1.2.3 CC源码1工具定义方式每个工具是buildTool()创建的独立对象包含 schema、验证、权限、执行。2并发安全判断isConcurrencySafe用isConcurrencySafe(input)判断能否并发把连续的并发安全调用合成同一个batch3分区算法把工具调用按连续块分批4验证管线(5 步验证)1.Zod schema 验证:参数类型/结构检查2.工具级 validateInput:参数值验证3.PreToolUse hooks:钩子可以返回消息、修改输入、阻止执行4.权限检查:canUseTool checkPermissions → allow/deny/ask5.执行 tool.call()5流式工具执行让工具在模型还在生成时就启动6工具结果持久化每个工具有一个maxResultSizeChars字段。结果超过这个值就落盘模型看到的是预览 文件路径。1.2.4 测试prompta. Read the file README.md and tell me what this project is aboutb. Create a file called test.py that prints hello, then read it backc. Find all Python files in this directoryd. Read both README.md and requirements.txt, then create a summary file1.3 S03 工具权限权限系统是为了让 agent 工具调用前先经过一道可靠的安全判断。1.3.1 循环工具执行前插入check_permission——每个工具调用经过三道闸门顺序固定硬拒绝优先拒绝列表软询问次之规则匹配、用户审批都没命中就放行。results [] for block in response.content: if block.type ! tool_use: continue # s03 change: 工具执行前检查权限 if not check_permission(block): results.append({type: tool_result, tool_use_id: block.id, content: Permission denied.}) continue handler TOOL_HANDLERS.get(block.name) output handler(**block.input) if handler else fUnknown: {block.name} results.append({type: tool_result, tool_use_id: block.id, content: output})SYSTEM fYou are a coding agent at {目录}. All destructive operations require user approval.1.3.2 CC源码1PermissionResult不是 3 种是 4 种deny,ask,allow,passthrough(交给通用管线决定)2生产版的验证阶段工具调用不是经过三道闸门而是经过多个阶段3拒绝列表不是一个文件是 8 个来源user project local flag policy加上 cliArg、command、session4isDestructive纯粹是 UI 展示用的5YoloClassifier自动审批先尝试 acceptEdits 模式模拟如果 acceptEdits 允许 → 直接批准再查安全工具白名单最后才调分类器。分类器连续拒绝太多次 → 回退到人工审批。6权限冒泡限弹窗冒泡到父 Agent 的终端而不是在子 Agent 里静默拒绝。1.3.3 测试prompta. Create a file called test.txt in the current directory应该直接通过b. Delete all temporary files in /tmpbash rm 会触发闸门 2c. What files are in the current directory?只读全部通过d. Try to write a file to /etc/something写工作区外触发闸门 21.4 S04 工具调用前后的钩子Hook 是主循环在固定时机对外发出的调用。1.4.1 循环把check_permission从循环体内移到了 hook 上循环不再直接调用任何检查函数这样扩展行为时不需要改动循环体代码。while True: response client.messages.create( modelMODEL, systemSYSTEM, messagesmessages, toolsTOOLS, max_tokens8000, ) messages.append({role: assistant, content: response.content}) if response.stop_reason ! tool_use: # 调用停止钩子 force trigger_hooks(Stop, messages) if force: messages.append({role: user, content: force}) continue return results [] for block in response.content: if block.type ! tool_use: continue # s04 change: 调用工具使用前钩子 blocked trigger_hooks(PreToolUse, block) if blocked: results.append({type: tool_result, tool_use_id: block.id, content: str(blocked)}) continue handler TOOL_HANDLERS.get(block.name) output handler(**block.input) if handler else fUnknown: {block.name} # 调用工具使用后钩子 trigger_hooks(PostToolUse, block, output) # s04: post hook results.append({type: tool_result, tool_use_id: block.id, content: output}) messages.append({role: user, content: results})SYSTEM fYou are a coding agent at {目录}. Use tools to solve tasks. Act, dont explain.1.4.2 CC源码1Hook 事件不止4 个而是 27 个工具相关(3)、会话相关(5)、用户交互(4)、子 Agent(2)、压缩相关(2)、团队相关(3)、其他(8)2HookResult 常用字段摘录14个message、blockingError、outcome、preventContinuation、stopReason、permissionBehavior、updatedInput、additionalContext、updatedMCPToolOutput3关键不变式Hook allow 不能绕过 deny/ask 规则hook 返回 allow 时仍然要检查 settings.json 的 deny/ask 规则4stopHookActive 机制防止无限循环5hook_stopped_continuation退出循环1.4.3 测试prompta. Read the file README.md应该直接通过观察 hook 日志b. Create a file called test.txt通过后观察 PostToolUse 是否触发c. Delete all temporary files in /tmpbash rm 触发权限 hook二、第二阶段2.1 S05 Todos先做计划防止Agent注意力漂移2.1.1 循环工具箱增加了todo_write工具循环体加了todo_write提醒todo_write本身不做任何实际工作不能读文件、不能跑命令只是展示任务每一步的状态。注意SYSTEM的变化。global rounds_since_todo while True: # s05: 3轮没调todo_write则注入一条提醒CC中没有 if rounds_since_todo 3 and messages: messages.append({role: user, content: reminderUpdate your todos./reminder}) rounds_since_todo 0 response client.messages.create( modelMODEL, systemSYSTEM, messagesmessages, toolsTOOLS, max_tokens8000, ) messages.append({role: assistant, content: response.content}) if response.stop_reason ! tool_use: force trigger_hooks(Stop, messages) if force: messages.append({role: user, content: force}) continue return rounds_since_todo 1 results [] for block in response.content: if block.type ! tool_use: continue blocked trigger_hooks(PreToolUse, block) if blocked: results.append({type: tool_result, tool_use_id: block.id, content: str(blocked)}) continue handler TOOL_HANDLERS.get(block.name) output handler(**block.input) if handler else fUnknown: {block.name} trigger_hooks(PostToolUse, block, output) # s05: 当todo_write被调用重置轮次计数器 if block.name todo_write: rounds_since_todo 0 results.append({type: tool_result, tool_use_id: block.id, content: output}) messages.append({role: user, content: results})SYSTEM ( fYou are a coding agent at {目录}. Before starting any multi-step task, use todo_write to plan your steps. Update status as you go. )2.1.2 CC源码有两套任务系统并存。TodoWriteV1一个简单的列表工具数据在内存 AppState 中维护Task SystemV2 s12文件持久化({taskId}.json)、依赖图(blockedBy)、并发锁(proper-lockfile)、ownership2.1.3 测试prompta. Refactor s05_todo_write/example/hello.py: add type hints, docstrings, and a main guard先列 3 步再执行b. Create a Python package under s05_todo_write/example/demo_pkg with __init__.py, utils.py, and tests/test_utils.pyc. Review Python files under s05_todo_write/example and fix any style issues2.2 S06 子智能体 — 大任务拆小小任务有独立的上下文子智能体的核心不是多一个角色而是多一个干净上下文。2.2.1 工具增加了task工具用于生成子智能体TOOLS.append({ name: task, description: Launch a subagent to handle a complex subtask. Returns only the final conclusion., input_schema: {type: object, properties: {description: {type: string}}, required: [description]}, })2.2.2 主循环代码没有变化和S05一样但SYSTEM有变化。SYSTEM ( fYou are a coding agent at {目录}. For complex sub-problems, use the task tool to spawn a subagent. )2.2.3 子Agent循环def spawn_subagent(description: str) - str: Spawn a subagent with fresh messages[], return summary only. messages [{role: user, content: description}] # 新的messages,上下文隔离 for _ in range(30): # 最多30次循环 response client.messages.create( modelMODEL, systemSUB_SYSTEM, messagesmessages, toolsSUB_TOOLS, max_tokens8000, ) messages.append({role: assistant, content: response.content}) if response.stop_reason ! tool_use: break results [] for block in response.content: if block.type tool_use: # Issue 1: subagent保留安全策略 blocked trigger_hooks(PreToolUse, block) if blocked: results.append({type: tool_result, tool_use_id: block.id, content: str(blocked)}) continue handler SUB_HANDLERS.get(block.name) output handler(**block.input) if handler else fUnknown: {block.name} trigger_hooks(PostToolUse, block, output) results.append({type: tool_result, tool_use_id: block.id, content: output}) messages.append({role: user, content: results}) # Issue 5: fallback if safety limit hit during tool_use result extract_text(messages[-1][content]) if not result: # last message is tool_result, look backwards for assistant text for msg in reversed(messages): if msg[role] assistant: result extract_text(msg[content]) if result: break if not result: result Subagent stopped after 30 turns without final answer. return result # 只返回最后的文本结论SUB_SYSTEM ( fYou are a coding agent at {目录}. Complete the task you were given, then return a concise summary. Do not delegate further. )SUB_TOOLS不包含工具task防止子Agent再生子Agent禁止递归。2.2.4 CC源码1不是一种模式是三种Normal subagent、Fork Subagent、General-Purpose2Fork 模式为了共享 Prompt Cache不创建全新上下文目的是让 Anthropic API 的 prompt cache 命中3Context Isolation 的精确粒度子 Agent 不是完全隔离的文件读取状态是共享的。4递归 Fork 防护5Permission Bubbling子 Agent 的权限弹窗冒泡到父终端6Async vs Sync异步子 Agent 完成后通过通知机制告知父 Agent2.2.5 测试prompta. Use a subtask to find what testing framework this project uses子 Agent 去读文件主 Agent 只收结论b. Delegate: read all .py files in agents/ and summarize what each one doesc. Use a task to create s06_subagent/example/string_tools.py with a slugify(text: str) function, then verify it from the parent agent2.3 S08 上下文压缩上下文压缩的核心是让模型在更短的活跃上下文里仍然保住继续工作的连续性。2.3.1 循环增加压缩调用 L3(tool_result_budget落盘保留完整内容) - L1(snip裁掉无关的旧对话裁中间保留头尾)- L2(micro旧工具结果占位) -L4(主动调LLM压缩)reactive_retries 0 while True: # s08 change: 3个预处理0 api调用 # 顺序与 CC 相同: budget → snip → micro messages[:] tool_result_budget(messages) # L3: 大结果落盘 messages[:] snip_compact(messages) # L1: 裁中间 messages[:] micro_compact(messages) # L2: 旧工具结果占位 # s08 change: tokens超过阈值 → LLM 摘要(1 API调用) if estimate_size(messages) CONTEXT_LIMIT: # L4: 调用LLM 压缩 messages[:] compact_history(messages) try: response client.messages.create(modelMODEL, systemSYSTEM, messagesmessages, toolsTOOLS, max_tokens8000) reactive_retries 0 # reset on successful API call except Exception as e: if (prompt_too_long in str(e).lower() or too many tokens in str(e).lower()) and reactive_retries MAX_REACTIVE_RETRIES: # 应急压缩 messages[:] reactive_compact(messages) reactive_retries 1 continue raise messages.append({role: assistant, content: response.content}) if response.stop_reason ! tool_use: return results [] for block in response.content: if block.type ! tool_use: continue # s08: 模型主动调用时触发compact工具 if block.name compact: messages[:] compact_history(messages) results.append({type: tool_result, tool_use_id: block.id, content: [Compacted. Conversation history has been summarized.]}) messages.append({role: user, content: results}) break # 结束当前轮次用压缩后的上下文开始新一轮 blocked trigger_hooks(PreToolUse, block) if blocked: results.append({type: tool_result, tool_use_id: block.id, content: str(blocked)}) continue handler TOOL_HANDLERS.get(block.name) output handler(**block.input) if handler else fUnknown: {block.name} trigger_hooks(PostToolUse, block, output) results.append({type: tool_result, tool_use_id: block.id, content: str(output)}) else: # 正常路径: 无压缩 messages.append({role: user, content: results}) continue # compact was called: results already appended above continue2.3.2 CC源码1执行顺序budget → snip → micro → collapse → auto2read_file 的取舍把Read也放进可 microcompact 的工具集合但同时维护readFileState3contextCollapse独立的上下文管理系统启用时抑制 proactive autocompactsessionMemoryCompactcompact_history 之前先尝试用已有的 session memory做轻量摘要不调 LLM4压缩 prompt 长什么样1.绝对禁止调用工具2.先分析再总结2.3.3 测试prompta. Read the file README.md, then read code.py, then read s01_agent_loop/README.md连续读多个文件观察 L2 压缩旧结果b. Read every file in s08_context_compact/一次性读大量内容观察 L3 落盘c. 反复对话 20 轮观察是否出现 [auto compact] 或 [reactive compact]