Agent Loop 永动机与工具扩展机制详解
文章目录
- [Agent Loop 永动机与工具扩展机制详解](#Agent Loop 永动机与工具扩展机制详解)
-
- [一、核心概念:Agent Loop是永动机](#一、核心概念:Agent Loop是永动机)
-
- [1.1 永动机的本质](#1.1 永动机的本质)
- 二、S01:基础永动机(单工具版本)
-
- [2.1 核心结构](#2.1 核心结构)
- [2.2 S01代码分析](#2.2 S01代码分析)
- 三、S02:工具扩展版(多工具版本)
-
- [3.1 核心演进:从硬编码到可扩展](#3.1 核心演进:从硬编码到可扩展)
- [3.2 S02架构图](#3.2 S02架构图)
- [3.3 S02详细流程图](#3.3 S02详细流程图)
- 四、核心要点:永动机与扩展的分离
-
- [4.1 永动机不变性](#4.1 永动机不变性)
- [4.2 扩展性实现](#4.2 扩展性实现)
- [4.3 扩展步骤演示](#4.3 扩展步骤演示)
- 五、架构层次图
- 六、设计模式分析
-
- [6.1 策略模式(Strategy Pattern)](#6.1 策略模式(Strategy Pattern))
- [6.2 开闭原则(Open-Closed Principle)](#6.2 开闭原则(Open-Closed Principle))
- [6.3 单一职责原则(Single Responsibility Principle)](#6.3 单一职责原则(Single Responsibility Principle))
- 七、实际应用场景
-
- [7.1 从S01到S02的能力提升](#7.1 从S01到S02的能力提升)
- [7.2 典型任务对比](#7.2 典型任务对比)
- 八、演进路径总结
- 九、核心洞察
-
- [9.1 设计哲学](#9.1 设计哲学)
- [9.2 实践建议](#9.2 实践建议)
- 十、总结
一、核心概念:Agent Loop是永动机
1.1 永动机的本质
Agent Loop是一个永不停止的循环,直到LLM决定停止为止。这个设计的精髓在于:
python
# 核心永动机模式
while True:
response = LLM(messages, tools)
if response.stop_reason != "tool_use":
return # LLM决定停止,永动机停止
execute tools
append results # 将结果反馈给LLM,继续循环
永动机的特点:
- ⚡ 自驱动: LLM自主决定是否继续
- 🔄 闭环反馈: 工具结果反馈给LLM
- 🛑 智能停止: LLM判断任务完成时主动停止
- ♾️ 无限潜力: 理论上可以处理任意复杂任务
二、S01:基础永动机(单工具版本)
2.1 核心结构
┌─────────────────────────────────────────────────────────────┐
│ S01: 基础Agent Loop │
│ (Single Tool: bash) │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 用户输入: "创建一个test.txt文件并写入hello" │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────┐
│ 永动机启动 │
│ while True: │
└─────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第1轮迭代 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ LLM调用: client.messages.create() │ │
│ │ Input: 用户请求 + SYSTEM提示 │ │
│ │ Output: 决定调用bash工具 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 工具执行 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ bash: echo "hello" > test.txt │ │
│ │ Output: "(no output)" │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 结果反馈 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ messages.append({ │ │
│ │ "role": "user", │ │
│ │ "content": [{"type": "tool_result", ...}] │ │
│ │ }) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第2轮迭代 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ LLM调用: 收到工具结果,决定是否继续 │ │
│ │ 判断: 任务已完成,不需要更多工具调用 │ │
│ │ stop_reason: "end_turn" (不是"tool_use") │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 永动机停止 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ if response.stop_reason != "tool_use": │ │
│ │ return # 退出循环,任务完成 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
2.2 S01代码分析
关键代码段:
python
def agent_loop(messages: list):
while True: # ← 永动机核心
response = client.messages.create(
model=MODEL, system=SYSTEM, messages=messages,
tools=TOOLS, max_tokens=8000,
)
messages.append({"role": "assistant", "content": response.content})
# 智能停止条件
if response.stop_reason != "tool_use":
return # LLM决定停止
# 工具执行(硬编码为bash)
results = []
for block in response.content:
if block.type == "tool_use":
print(f"\033[33m$ {block.input['command']}\033[0m")
output = run_bash(block.input["command"]) # 硬编码bash
print(output[:200])
results.append({"type": "tool_result", "tool_use_id": block.id,
"content": output})
messages.append({"role": "user", "content": results})
S01的限制:
- ❌ 只有一个bash工具
- ❌ 工具处理逻辑硬编码
- ❌ 扩展性差,添加新工具需要修改核心循环
三、S02:工具扩展版(多工具版本)
3.1 核心演进:从硬编码到可扩展
关键变化:
python
# S01: 硬编码工具处理
output = run_bash(block.input["command"]) # 只能处理bash
# S02: 可扩展工具分发
handler = TOOL_HANDLERS.get(block.name) # 根据工具名称查找处理器
output = handler(**block.input) if handler else f"Unknown tool: {block.name}"
3.2 S02架构图
┌─────────────────────────────────────────────────────────────┐
│ S02: 扩展Agent Loop │
│ (Multiple Tools + Dispatch) │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────┐
│ 永动机启动 │
│ while True: │
└─────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 工具定义层(可扩展) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ TOOLS = [ │ │
│ │ {"name": "bash", ...}, │ │
│ │ {"name": "read_file", ...}, │ │
│ │ {"name": "write_file", ...}, │ │
│ │ {"name": "edit_file", ...}, │ │
│ │ ] │ │
│ │ │ │
│ │ TOOL_HANDLERS = { │ │
│ │ "bash": lambda **kw: run_bash(...), │ │
│ │ "read_file": lambda **kw: run_read(...), │ │
│ │ "write_file": lambda **kw: run_write(...), │ │
│ │ "edit_file": lambda **kw: run_edit(...), │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 永动机核心(完全不变!) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ while True: │ │
│ │ response = LLM(messages, tools) ← 结构完全相同 │ │
│ │ if stop_reason != "tool_use": │ │
│ │ return │ │
│ │ execute tools ← 唯一变化点 │ │
│ │ append results │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 工具分发层(新增) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ for block in response.content: │ │
│ │ if block.type == "tool_use": │ │
│ │ handler = TOOL_HANDLERS.get(block.name) │ │
│ │ output = handler(**block.input) │ │
│ │ results.append(tool_result) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
3.3 S02详细流程图
用户输入: "读取config.txt,将port改为8080,然后保存"
↓
┌─────────────────────────────────────────────────────────────┐
│ 第1轮:LLM规划任务序列 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ LLM分析: 需要三个步骤 │ │
│ │ 1. read_file("config.txt") - 读取文件 │ │
│ │ 2. edit_file(...) - 修改port │ │
│ │ 3. (可能需要验证) │ │
│ │ │ │
│ │ 决定先调用: read_file │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 工具分发 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ block.name = "read_file" │ │
│ │ handler = TOOL_HANDLERS.get("read_file") │ │
│ │ = lambda **kw: run_read(kw["path"], kw.get("limit"))│ │
│ │ output = handler(path="config.txt") │ │
│ │ = run_read("config.txt") │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ read_file执行 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ def run_read(path: str, limit: int = None) -> str: │ │
│ │ text = safe_path(path).read_text() │ │
│ │ lines = text.splitlines() │ │
│ │ if limit and limit < len(lines): │ │
│ │ lines = lines[:limit] + [...] │ │
│ │ return "\n".join(lines)[:50000] │ │
│ │ │ │
│ │ Output: "port=80\nhost=localhost\n..." │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 结果反馈给LLM │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ messages.append({ │ │
│ │ "role": "user", │ │
│ │ "content": [ │ │
│ │ { │ │
│ │ "type": "tool_result", │ │
│ │ "tool_use_id": "toolu_01", │ │
│ │ "content": "port=80\nhost=localhost\n..." │ │
│ │ } │ │
│ │ ] │ │
│ │ }) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第2轮:LLM根据结果继续执行 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ LLM分析: 收到文件内容,发现port=80 │ │
│ │ 目标: 改为8080 │ │
│ │ 决定调用: edit_file │ │
│ │ │ │
│ │ edit_file( │ │
│ │ path="config.txt", │ │
│ │ old_text="port=80", │ │
│ │ new_text="port=8080" │ │
│ │ ) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 工具分发 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ block.name = "edit_file" │ │
│ │ handler = TOOL_HANDLERS.get("edit_file") │ │
│ │ = lambda **kw: run_edit(...) │ │
│ │ output = handler( │ │
│ │ path="config.txt", │ │
│ │ old_text="port=80", │ │
│ │ new_text="port=8080" │ │
│ │ ) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ edit_file执行 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ def run_edit(path: str, old_text: str, new_text: str):│ │
│ │ fp = safe_path(path) │ │
│ │ content = fp.read_text() │ │
│ │ if old_text not in content: │ │
│ │ return f"Error: Text not found" │ │
│ │ fp.write_text(content.replace(old_text, new_text, 1))│ │
│ │ return f"Edited {path}" │ │
│ │ │ │
│ │ Output: "Edited config.txt" │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第3轮:LLM判断任务完成 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ LLM分析: 文件已成功修改 │ │
│ │ 判断: 任务完成,无需更多工具调用 │ │
│ │ stop_reason: "end_turn" │ │
│ │ │ │
│ │ 返回文本响应: "已将config.txt中的port从80改为8080" │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 永动机停止 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ if response.stop_reason != "tool_use": │ │
│ │ return # 退出循环 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
四、核心要点:永动机与扩展的分离
4.1 永动机不变性
对比S01和S02的agent_loop:
python
# S01的agent_loop
def agent_loop(messages: list):
while True: # ← 相同
response = client.messages.create( # ← 相同
model=MODEL, system=SYSTEM, messages=messages,
tools=TOOLS, max_tokens=8000,
)
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": # ← 相同
# 唯一不同点:工具执行逻辑
output = run_bash(block.input["command"]) # S01硬编码
# vs
handler = TOOL_HANDLERS.get(block.name) # S02可扩展
output = handler(**block.input)
results.append({"type": "tool_result", "tool_use_id": block.id, "content": output})
messages.append({"role": "user", "content": results}) # ← 相同
不变性统计:
- ✅ 循环结构:100%相同
- ✅ LLM调用:100%相同
- ✅ 停止条件:100%相同
- ✅ 消息管理:100%相同
- ✅ 结果收集:100%相同
- 🔄 工具执行:唯一变化点(从硬编码到可扩展)
4.2 扩展性实现
S02的扩展机制:
python
# 1. 工具定义扩展
TOOLS = [
{"name": "bash", "description": "Run a shell command.", ...},
{"name": "read_file", "description": "Read file contents.", ...},
{"name": "write_file", "description": "Write content to file.", ...},
{"name": "edit_file", "description": "Replace exact text in file.", ...},
# 添加新工具只需在这里添加定义
# {"name": "new_tool", "description": "...", ...},
]
# 2. 工具处理器扩展
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"]),
# 添加新工具处理器只需在这里添加映射
# "new_tool": lambda **kw: run_new_tool(...),
}
# 3. 工具实现扩展
def run_new_tool(param1, param2):
# 实现新工具的逻辑
return result
4.3 扩展步骤演示
添加一个新工具的完整流程:
python
# 步骤1: 定义工具实现
def run_search_files(pattern: str) -> str:
"""搜索匹配模式的文件"""
try:
matches = list(WORKDIR.rglob(pattern))
return "\n".join(str(m.relative_to(WORKDIR)) for m in matches)
except Exception as e:
return f"Error: {e}"
# 步骤2: 添加工具处理器映射
TOOL_HANDLERS = {
# ... 现有工具 ...
"search_files": lambda **kw: run_search_files(kw["pattern"]),
}
# 步骤3: 添加工具定义
TOOLS = [
# ... 现有工具 ...
{
"name": "search_files",
"description": "Search for files matching a pattern.",
"input_schema": {
"type": "object",
"properties": {
"pattern": {"type": "string", "description": "File pattern to search for"}
},
"required": ["pattern"]
}
},
]
# 完成!永动机无需任何修改
五、架构层次图
┌─────────────────────────────────────────────────────────────┐
│ 应用层 │
│ 用户交互、任务描述、结果展示 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 永动机层(不变) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ while True: │ │
│ │ response = LLM(messages, tools) │ │
│ │ if stop_reason != "tool_use": return │ │
│ │ execute tools via dispatch │ │
│ │ append results │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 核心特点: 结构稳定,逻辑简单,无需修改 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 工具分发层(扩展点) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ TOOL_HANDLERS = { │ │
│ │ "tool_name": handler_function, │ │
│ │ } │ │
│ │ │ │
│ │ handler = TOOL_HANDLERS.get(tool_name) │ │
│ │ output = handler(**tool_input) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 核心特点: 动态分发,松耦合,易扩展 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 工具实现层(可扩展) │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ run_bash() │ │ run_read() │ │
│ ├──────────────────┤ ├──────────────────┤ │
│ │ run_write() │ │ run_edit() │ │
│ ├──────────────────┤ ├──────────────────┤ │
│ │ run_search() │ │ run_...() │ │
│ └──────────────────┘ └──────────────────┘ │
│ │
│ 核心特点: 独立实现,专注功能,易于测试 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 系统层 │
│ 文件系统、Shell执行、网络请求等 │
└─────────────────────────────────────────────────────────────┘
六、设计模式分析
6.1 策略模式(Strategy Pattern)
工具分发就是策略模式的应用:
python
# 策略接口
def handler(**kwargs) -> str:
"""所有工具处理器都遵循这个接口"""
pass
# 具体策略
def run_bash(command: str) -> str:
pass
def run_read(path: str, limit: int = None) -> str:
pass
# 策略选择
handler = TOOL_HANDLERS.get(tool_name)
output = handler(**tool_input)
6.2 开闭原则(Open-Closed Principle)
对扩展开放,对修改封闭:
- ✅ 开放扩展: 添加新工具无需修改永动机
- ✅ 封闭修改: 永动机核心逻辑稳定不变
- ✅ 低耦合: 工具与永动机通过接口解耦
6.3 单一职责原则(Single Responsibility Principle)
每层专注自己的职责:
- 永动机层: 负责循环控制和消息管理
- 分发层: 负责工具路由和参数传递
- 实现层: 负责具体功能执行
七、实际应用场景
7.1 从S01到S02的能力提升
| 能力 | S01 | S02 |
|---|---|---|
| Shell命令执行 | ✅ | ✅ |
| 文件读取 | ❌ | ✅ |
| 文件写入 | ❌ | ✅ |
| 文件编辑 | ❌ | ✅ |
| 代码分析 | ❌ | ✅ (组合使用) |
| 项目重构 | ❌ | ✅ (组合使用) |
7.2 典型任务对比
任务:分析一个Python项目并生成文档
S01的处理:
用户: 分析这个Python项目并生成文档
LLM: 我只能用bash,让我试试...
bash: find . -name "*.py" | head -10
bash: cat file1.py | head -20
# ... 困难且有限
S02的处理:
用户: 分析这个Python项目并生成文档
LLM: 我可以使用多个工具
1. read_file("main.py") - 读取主文件
2. read_file("utils.py") - 读取工具文件
3. write_file("docs.md", ...) - 生成文档
# ... 高效且完整
八、演进路径总结
S01: 基础永动机
↓
+ 单工具 (bash)
+ 硬编码处理
+ 有限能力
↓
S02: 扩展永动机
↓
+ 多工具 (bash + read + write + edit)
+ 动态分发
+ 强大能力
↓
S03: 智能永动机 (todo_write)
↓
+ 自我跟踪 (TodoManager)
+ 进度管理
+ 状态感知
↓
S04: 子代理永动机 (subagent)
↓
+ 任务分解
+ 并行执行
+ 层次化
九、核心洞察
"The loop didn't change at all. I just added tools."
这个设计的精髓在于:
- 永动机是核心: Agent Loop是整个系统的心脏,保持稳定
- 工具是四肢: 通过工具扩展能力,不影响心脏
- 分发是神经: TOOL_HANDLERS连接心脏和四肢
- 扩展是进化: 添加新工具就像进化出新能力
9.1 设计哲学
python
# 好的设计
永动机 = 稳定的核心循环
工具 = 灵活的能力扩展
分发 = 智能的路由机制
# 坏的设计
永动机 = 频繁修改的混乱代码
工具 = 硬编码的固定逻辑
分发 = 不存在的if-else地狱
9.2 实践建议
✅ 推荐做法:
- 保持永动机简洁稳定
- 通过工具扩展功能
- 使用分发模式解耦
- 每个工具职责单一
❌ 避免做法:
- 在永动机中添加业务逻辑
- 硬编码工具处理逻辑
- 破坏分层架构
- 让工具相互依赖
十、总结
S01到S02的演进展示了软件设计的核心原则:
- 发现稳定核心: Agent Loop就是那个稳定的核心
- 识别扩展点: 工具处理是自然的扩展点
- 设计扩展机制: TOOL_HANDLERS分发机制
- 保持核心不变: 永动机无需修改
- 通过扩展增强: 添加工具获得新能力
这种设计让系统既保持了简单性,又具备了强大的扩展性,是构建复杂系统的优秀范例。
关键要点:Agent Loop是永动机,工具是扩展。在永动机不改变的时候,只要实现扩展就能得到想要的结果。
下是您提供的相关文章的链接,可直接点击阅读:
- 【claude code 如何构建 agent 实践1】Agent Loop 永动机与工具扩展机制详解[S02]
- 【claude code 如何构建 agent 实践2】TodoWrite 详细执行流程分析:从S02到S03的演进
- 【claude code 如何构建 agent 实践3】Subagent子智能体机制深度解析:从S02到S04的演进
- 【claude code 如何构建 agent 实践4】Skill技能加载机制深度解析:从S02到S05的演进
- 【claude code 如何构建 agent 实践5】Claude Code 上下文压缩机制深度解析: 从S02到S06的演进
- 【claude code 如何构建 agent 实践6】Claude Code 任务管理系统深度解析: 从S02到S07的演进
- 【claude code 如何构建 agent 实践7】后台任务机制深度解析: 从S02到S08的演进
- 【claude code 如何构建 agent 实践8】用纯原生工具反向实现多智能体协作(非langchain、非langgraph): 从S02到S09的演进
- 【claude code 如何构建 agent 实践9】从自由协作到结构化协议: 从S09到S10的演进