目标:智能体相关学习,侧重研究Claude Code的如何应用智能体实现它的功能
一、什么是 Agent
普通 AI 对话是"一问一答"——你说一句,它回一句,结束。
Agent 不同:它拿到目标后,会自己循环——读文件、思考、调工具、看结果、再思考……直到任务完成才停。
二、整体架构:三层结构
Claude Code 的架构可以拆成三层:
graph TB
subgraph 输入层["① 输入层(Input Router)"]
A1["/命令 slash command"]
A2["! Bash 命令"]
A3["# 记忆模式"]
A4["普通自然语言"]
end
subgraph 核心层["② 核心层(Agent Loop)"]
B["tt() 异步状态机\n每轮经历 6 个阶段"]
end
subgraph 执行层["③ 执行层(Tool Execution)"]
C1["只读工具\nRead / Grep / Glob\n→ 并行执行,最多 10 个"]
C2["写入工具\nEdit / Write / Bash\n→ 串行执行"]
C3["子 Agent\nAgentTool\n→ 独立隔离"]
end
A1 & A2 & A3 & A4 --> B
B --> C1 & C2 & C3
理解要点:
- 只读操作可以并行(读 10 个文件同时进行),速度快
- 写操作必须串行,防止冲突
- 子 Agent 完全隔离,做完返回结果
三、核心状态机:tt() 的六个阶段
每次 Claude Code 处理你的请求,都会跑一遍这 6 个阶段:
flowchart LR
P1["Phase 1\n上下文压缩"] --> P2["Phase 2\n组装 Prompt"]
P2 --> P3["Phase 3\n调用 LLM"]
P3 --> P4["Phase 4\n处理响应流"]
P4 --> P5["Phase 5\n执行工具"]
P5 --> P6{"Phase 6\n需要继续?"}
P6 -- "是(最多 10 轮)" --> P1
P6 -- "否" --> END["返回结果"]
Phase 1:自动压缩上下文
问题:对话越来越长,Token太大
Claude Code 的做法:不是简单地"截断旧消息",而是让 LLM 先把历史总结成摘要,再用摘要替换原始消息。
触发条件(满足任一):
├── 历史 Token 数 > 100,000
├── 消息条数 > 200 条
└── 费用超过阈值
压缩流程:
旧消息(大) → LLM 生成摘要 → 摘要(小)替换旧消息
关键:摘要是语义压缩,不会丢失关键信息,比截断聪明得多。
Phase 2:每轮重新组装 System Prompt
System Prompt 不是固定的,每轮都动态拼接,优先级从高到低:
| 优先级 | 内容 | 说明 |
|---|---|---|
| 1 | 基础行为规则 | 约 2KB,雷打不动 |
| 2 | 模型专属指令 | Opus/Sonnet/Haiku 各有不同 |
| 3 | CLAUDE.md | 项目 > 父目录 > 用户主目录,逐级查找 |
| 4 | Git 上下文 | 当前分支、最近提交等 |
| 5 | 目录结构 | 让模型了解项目布局 |
| 6 | 工具定义 | 约 10-20KB,告诉模型能用什么工具 |
模型差异:
- Opus:加入详细推理引导,适合复杂任务
- Sonnet:均衡型(当前默认)
- Haiku:精简指令,快速响应
对你有用:CLAUDE.md 是你最直接能控制的部分——写清楚项目背景、约定、禁止操作,模型就能更好地理解你的项目。
Phase 3 & 4:LLM 流式调用
建立 SSE 流(Server-Sent Events,就像实时推送),边收数据边处理:
LLM 响应流
├── text_delta → 每个 Token 都触发 UI 更新(你看到的逐字出现)
├── tool_invocation → 收集工具调用参数(等完整参数才执行)
└── completion_signal → 本轮结束信号
Phase 5:工具执行
flowchart TD
TOOLS["本轮工具调用列表"]
TOOLS --> CHECK{"工具类型?"}
CHECK -- "只读\nRead/Grep/Glob" --> PAR["并行执行\n最多 10 个同时跑\n耗时:10-50ms"]
CHECK -- "写入\nEdit/Write/Bash" --> SEQ["串行执行\n一个接一个\n耗时:可能很长"]
PAR & SEQ --> MERGE["结果合并\n进入下一轮"]
Phase 6:循环还是停止?
- 最多循环 10 轮(防止无限运行)
- 每轮工具结果作为下一轮的输入
- 任何轮次 LLM 不再调用工具 → 结束
四、Agentic Loop:四步闭环
这是理解 Agent 行为的最关键图:
graph LR
G["Gather\n收集上下文\nRead/Grep/Glob"]
P["Plan\n推理规划\nReAct 模式"]
A["Act\n执行操作\nEdit/Write/Bash"]
V["Verify\n验证结果\n跑测试/检查"]
G --> P --> A --> V
V -- "不通过,继续修" --> G
V -- "通过,任务完成" --> DONE["完成"]
用大白话解释:
- Gather:先读代码,搞清楚情况
- Plan:想清楚要做什么,怎么做
- Act:动手改文件、跑命令
- Verify:跑测试,看结果对不对
- 不对?回到第 1 步重来。对了?交付。
五、工具系统详解
工具的结构
每个工具都是这样定义的:
{
name: "EditTool",
description: "精确替换文件中的字符串...", // LLM 靠这段话决定用不用这个工具
input_schema: { /* Zod/JSON Schema */ }, // 参数格式校验
execute: async function* (...) { ... }, // 异步生成器,支持流式输出
checkPermission: (...) => boolean, // 权限检查
formatOutput: (...) => string // 输出格式化
}
核心工具一览
| 工具 | 做什么 | 关键设计 |
|---|---|---|
| ReadTool | 读文件 | 支持图片、Jupyter Notebook;大文件自动流式读取 |
| EditTool | 精确修改文件 | 必须先读文件才能编辑;小改显示 diff,大改显示摘要 |
| MultiEditTool | 批量修改文件 | 先模拟所有编辑检测冲突,再真正执行 |
| WriteTool | 创建/覆盖文件 | 全量写入 |
| GrepTool | 搜索文件内容 | 底层用 ripgrep,最多返回 20 个匹配文件 |
| BashTool | 执行 Shell 命令 | 多层安全防护,默认拒绝危险命令 |
| AgentTool | 启动子 Agent | 子 Agent 不能再启动子 Agent(防无限递归) |
EditTool 的安全机制(值得学习)
flowchart TD
REQ["编辑请求"]
REQ --> C1{"文件已读取过?"}
C1 -- "否" --> FAIL["拒绝:必须先读文件"]
C1 -- "是" --> C2{"文件被外部修改?"}
C2 -- "是" --> FAIL2["拒绝:时间戳冲突"]
C2 -- "否" --> C3{"是二进制/符号链接?"}
C3 -- "是" --> FAIL3["拒绝"]
C3 -- "否" --> C4{"路径合法?\n不含 .git/.env"}
C4 -- "否" --> FAIL4["拒绝"]
C4 -- "是" --> EXEC["执行编辑"]
EXEC --> SHOW{"变更幅度?"}
SHOW -- "< 10%" --> DIFF["显示 unified diff"]
SHOW -- "> 50%" --> SUMMARY["显示摘要"]
六、权限系统
四级优先级
优先级(高 → 低)
① DENY 规则 ── 最高优先,永远胜出
② 模式覆盖 ── 当前运行模式(plan/bypassPermissions 等)
③ ALLOW 规则 ── 显式允许的操作
④ 交互式提示 ── 问用户:"这个操作允许吗?"
四种运行模式
quadrantChart
title 四种权限模式
x-axis 限制少 --> 限制多
y-axis 自动化低 --> 自动化高
bypassPermissions: [0.1, 0.9]
acceptEdits: [0.4, 0.6]
default: [0.7, 0.3]
plan: [0.9, 0.1]
| 模式 | 文件编辑 | 执行命令 | 适合场景 |
|---|---|---|---|
plan |
❌ | ❌ | 先规划,不执行 |
default |
需审批 | 需审批 | 日常使用 |
acceptEdits |
✅ 自动 | 需审批 | 信任编辑,谨慎命令 |
bypassPermissions |
✅ 自动 | ✅ 自动 | CI/CD 自动化 |
三层独立防护
第 1 层:权限规则(JIT 编译,支持 glob 通配符)
第 2 层:macOS 沙箱(deny default,选择性 allow)
第 3 层:路径校验(屏蔽 .git、.env、敏感目录)
七、子 Agent 架构
什么时候用子 Agent?
当任务可以独立并行时,拆成多个子 Agent 同时跑:
graph TB
MAIN["主 Agent\n收到复杂任务"]
MAIN --> DECOMPOSE["任务拆解"]
DECOMPOSE --> A["子 Agent A\n分析文件 A\n工具:Read/Grep"]
DECOMPOSE --> B["子 Agent B\n搜索相关代码\n工具:Grep/Glob"]
DECOMPOSE --> C["子 Agent C\n查文档/外部信息\n工具:WebFetch"]
A & B & C --> MERGE["结果合并\n(低温 LLM,确保准确)"]
MERGE --> RESULT["综合结论返回主 Agent"]
style A fill:#e8f4f8
style B fill:#e8f4f8
style C fill:#e8f4f8
关键约束:子 Agent 没有 AgentTool,不能再生子 Agent,防止无限嵌套。
最简可运行架构(从这里开始)
# 伪代码:最小 Agent 骨架
async def agent_loop(goal: str, max_turns: int = 10):
messages = [{"role": "user", "content": goal}]
for turn in range(max_turns):
# Phase 1: 压缩(超限时)
if token_count(messages) > 50_000:
messages = compress(messages)
# Phase 2: 组装 System Prompt
system = build_system_prompt()
# Phase 3: 调用 LLM
response = await llm.call(system=system, messages=messages)
# Phase 4: 解析响应
if response.has_tool_calls:
# Phase 5: 执行工具
results = await execute_tools(response.tool_calls)
messages.append(response)
messages.append(tool_results(results))
else:
# 没有工具调用,任务完成
return response.text
return "达到最大轮次,停止"
十三、延伸阅读
- Southbridge Research:Claude Code 逆向分析
- ReAct 论文:Reason+Act 模式的学术基础
- MCP 协议规范:如何给 Agent 接入外部工具
- Anthropic Agent SDK 文档:官方 Agent 构建指南
相关笔记
- Harness Engineering 实践 — 相关的工程实践
- MCP 协议 — Claude Code 使用的 MCP
- 多智能体协作 — 子 Agent 架构
- AI MOC