Skip to content
Agent Loop 与 Tool Use

Agent Loop 与 Tool Use

本章讲解最小 Agent Loop 的实现(while stop_reason == "tool_use")与 Dispatch Map 工具分发模式,以及 learn-claude-code 的 12 层演进路线。

参考:learn-claude-code s01-s02 — “Bash is all you need”

最小 Agent Loop

🔬 核心理念while stop_reason == "tool_use" — 一个循环 + 一个工具 = 一个 Agent。

Agent Loop 本质上是 ReAct 范式的最小实现。核心逻辑约 30 行代码:

def agent_loop(messages):
    while True:
        # ① 调用模型:传入完整 messages + tools 定义
        response = client.messages.create(
            model=MODEL,
            system=SYSTEM,
            messages=messages,
            tools=TOOLS,
            max_tokens=8000,
        )

        # ② 将 assistant 输出追加到上下文
        messages.append({"role": "assistant", "content": response.content})

        # ③ 唯一退出条件:模型不再要求调用工具
        if response.stop_reason != "tool_use":
            return response

        # ④ 遍历 content block,执行所有工具调用
        results = []
        for block in response.content:
            if block.type == "tool_use":
                handler = TOOL_HANDLERS.get(block.name)
                output = handler(**block.input)
                results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": output,
                })

        # ⑤ 工具结果以 user 角色追加回上下文 → 闭环
        messages.append({"role": "user", "content": results})

四个核心步骤

步骤 动作 说明
1 调模型 client.messages.create(...) 传入完整 messages + tools
2 看回复 检查 response.stop_reason 是否为 "tool_use"
3 执行工具 遍历 response.content,对每个 tool_use block 调用 handler
4 结果塞回 封装为 tool_result 追加到 messages,回到步骤 1

关键设计洞察

  • messages 既是上下文也是状态:所有对话历史、工具调用、执行结果都沉淀在统一的消息列表中,无需额外的状态对象
  • stop_reason 驱动控制流:不靠字符串规则判断"像不像最终答案",直接利用 API 的结构化字段
  • Harness 哲学:模型只负责决策(生成 tool_use),外部程序负责真正执行

Tool Use:Dispatch Map 模式

🔑 核心理念:“加一个工具,只加一个 handler” — 循环不用动,新工具注册进 dispatch map 就行。

工具分发表

TOOL_HANDLERS = {
    "bash":       lambda **kw: run_bash(kw["command"]),
    "read_file":  lambda **kw: run_read(kw["path"], kw.get("limit")),
    "write_file": lambda **kw: run_write(kw["path"], kw["content"]),
    "edit_file":  lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
}

加工具 = 加 handler + 加 schema,循环永远不变。

路径沙箱 (safe_path)

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

所有文件操作工具在执行前都经过沙箱校验,这是 Agent 安全的第一道防线

两层不变原则

  1. Agent Loop 自身永远不变:s02-s12 所有机制都在最小循环上"叠加",核心骨架不修改
  2. 加工具不需要改循环:只需注册 handler 函数 + 添加 tool schema

learn-claude-code 的 12 层演进

s01  Agent Loop         ← 地基:LLM → tool → tool_result → LLM
s02  Tool Use           ← 层1:dispatch map + 路径沙箱
s03  TodoWrite          ← 层2:任务规划与进度追踪
s04  Subagents          ← 层3:子 Agent 上下文隔离
s05  Skills             ← 层4:按需知识注入(类似"技能包")
s06  Context Compact    ← 层5:上下文压缩与摘要
s07  Tasks              ← 层6:持久化任务图(跨会话)
s08  Background Tasks   ← 层7:异步并行执行
s09  Agent Teams        ← 层8:多 Agent 分工协作
s10  Team Protocols     ← 层9:团队通信协议
s11  Autonomous Agents  ← 层10:自治任务认领(自主模式)
s12  Worktree Isolation ← 层11:Git worktree 隔离执行

每一层只增加一个机制,层层叠加,最终将"模型调用"演进为"Agent Runtime"。