跳到主要内容
SUN / 笔记
← 全部文章

从 Claude Code 学习 Agent 工程实现

目标:智能体相关学习,侧重研究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 先把历史总结成摘要,再用摘要替换原始消息。

text
触发条件(满足任一):
├── 历史 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,就像实时推送),边收数据边处理:

text
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["完成"]

用大白话解释:

  1. Gather:先读代码,搞清楚情况
  2. Plan:想清楚要做什么,怎么做
  3. Act:动手改文件、跑命令
  4. Verify:跑测试,看结果对不对
  5. 不对?回到第 1 步重来。对了?交付。

五、工具系统详解

工具的结构

每个工具都是这样定义的:

typescript
{
  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["显示摘要"]

六、权限系统

四级优先级

text
优先级(高 → 低)

① 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 自动化

三层独立防护

text
第 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,防止无限嵌套。


最简可运行架构(从这里开始)

python
# 伪代码:最小 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 "达到最大轮次,停止"

十三、延伸阅读

相关笔记

相关文章

强化学习笔记 →上下文工程 →Mission: 掌握多模态 LoRA 与 QLoRA →