第10章 Agent Loop —— Harness的心跳
目录
上一篇我们全景扫描了开源 Harness 生态。本章我们深入核心——Agent Loop,理解 observe→think→act→feedback 的持续循环。
Agent Loop 是什么
一句话定义:Agent 的核心心跳——持续循环的感知-推理-行动机制。
Agent Loop = observe → think → act → feedback 定位:Harness 的第一层,驱动整个 Agent 运行的心跳
图表
代码
flowchart LR
O[observe
观察环境
接收 messages] --> T[think
推理决策
Model 推理]
T --> A[act
执行行动
调用工具]
A --> F[feedback
收集反馈
工具输出]
F -->|循环| Oflowchart LR
O[observe
观察环境
接收 messages] --> T[think
推理决策
Model 推理]
T --> A[act
执行行动
调用工具]
A --> F[feedback
收集反馈
工具输出]
F -->|循环| Oflowchart LR
O[observe
观察环境
接收 messages] --> T[think
推理决策
Model 推理]
T --> A[act
执行行动
调用工具]
A --> F[feedback
收集反馈
工具输出]
F -->|循环| Oflowchart LR
O[observe<br/>观察环境<br/>接收 messages] --> T[think<br/>推理决策<br/>Model 推理]
T --> A[act<br/>执行行动<br/>调用工具]
A --> F[feedback<br/>收集反馈<br/>工具输出]
F -->|循环| O核心机制:
- observe:接收 messages 数组(对话历史 + 工具输出)
- think:Model 推理,决定下一步行动(工具调用或结束对话)
- act:执行工具调用(Bash、Read、Write、Edit)
- feedback:收集工具输出,追加到 messages 数组
Agent Loop 的核心组件
1. messages 数组 —— 对话历史存储
一句话定义:存储完整对话历史的数组——用户消息 + Model 响应 + 工具输出。
图表
代码
graph LR
subgraph MessagesArray["messages 数组"]
M1[用户消息1
role: user]
M2[Model 响应1
role: assistant
tool_use]
M3[工具输出1
role: user
tool_result]
M4[Model 响应2
role: assistant
tool_use]
M5[工具输出2
role: user
tool_result]
M6[Model 响应3
role: assistant
end_turn]
end
M1 --> M2 --> M3 --> M4 --> M5 --> M6graph LR
subgraph MessagesArray["messages 数组"]
M1[用户消息1
role: user]
M2[Model 响应1
role: assistant
tool_use]
M3[工具输出1
role: user
tool_result]
M4[Model 响应2
role: assistant
tool_use]
M5[工具输出2
role: user
tool_result]
M6[Model 响应3
role: assistant
end_turn]
end
M1 --> M2 --> M3 --> M4 --> M5 --> M6graph LR
subgraph MessagesArray["messages 数组"]
M1[用户消息1
role: user]
M2[Model 响应1
role: assistant
tool_use]
M3[工具输出1
role: user
tool_result]
M4[Model 响应2
role: assistant
tool_use]
M5[工具输出2
role: user
tool_result]
M6[Model 响应3
role: assistant
end_turn]
end
M1 --> M2 --> M3 --> M4 --> M5 --> M6graph LR
subgraph MessagesArray["messages 数组"]
M1[用户消息1<br/>role: user]
M2[Model 响应1<br/>role: assistant<br/>tool_use]
M3[工具输出1<br/>role: user<br/>tool_result]
M4[Model 响应2<br/>role: assistant<br/>tool_use]
M5[工具输出2<br/>role: user<br/>tool_result]
M6[Model 响应3<br/>role: assistant<br/>end_turn]
end
M1 --> M2 --> M3 --> M4 --> M5 --> M6消息结构:
| 消息类型 | role | content | 示例 |
|---|---|---|---|
| 用户消息 | user | 文本或结构化内容 | {"role": "user", "content": "帮我写一个测试"} |
| Model 响应(工具调用) | assistant | tool_use 信息 | {"role": "assistant", "tool_use": {"name": "Read"}} |
| 工具输出 | user | 工具执行结果 | {"role": "user", "content": "文件内容..."} |
| Model 响应(结束) | assistant | 最终回答 | {"role": "assistant", "content": "测试已写好"} |
2. Model 推理 —— 决策引擎
一句话定义:Model 根据 messages 数组推理,决定下一步行动。
图表
代码
graph LR
Messages[messages 数组
对话历史] --> Model[Claude/GPT
推理引擎]
Model --> Decision{决策}
Decision -->|需要工具| ToolUse[tool_use
调用工具]
Decision -->|任务完成| EndTurn[end_turn
结束对话]
ToolUse -->|追加| Messagesgraph LR
Messages[messages 数组
对话历史] --> Model[Claude/GPT
推理引擎]
Model --> Decision{决策}
Decision -->|需要工具| ToolUse[tool_use
调用工具]
Decision -->|任务完成| EndTurn[end_turn
结束对话]
ToolUse -->|追加| Messagesgraph LR
Messages[messages 数组
对话历史] --> Model[Claude/GPT
推理引擎]
Model --> Decision{决策}
Decision -->|需要工具| ToolUse[tool_use
调用工具]
Decision -->|任务完成| EndTurn[end_turn
结束对话]
ToolUse -->|追加| Messagesgraph LR
Messages[messages 数组<br/>对话历史] --> Model[Claude/GPT<br/>推理引擎]
Model --> Decision{决策}
Decision -->|需要工具| ToolUse[tool_use<br/>调用工具]
Decision -->|任务完成| EndTurn[end_turn<br/>结束对话]
ToolUse -->|追加| Messages推理流程:
- Model 接收 messages 数组作为输入
- Model 推理当前任务进展
- Model 判断下一步需要什么(工具调用或结束)
- Model 输出 stop_reason(tool_use 或 end_turn)
3. stop_reason —— 循环控制信号
一句话定义:Model 输出的控制信号——决定继续循环还是结束对话。
| stop_reason | 含义 | Agent 行为 |
|---|---|---|
| tool_use | 需要工具调用 | 执行工具,收集结果,追加到 messages,继续循环 |
| end_turn | 任务完成 | 返回最终结果,结束对话 |
图表
代码
graph LR
Model[Model 推理] --> Stop{stop_reason}
Stop -->|tool_use| Loop[继续循环
执行工具]
Stop -->|end_turn| End[结束对话
返回结果]
Loop --> Messages[messages 数组]
Messages --> Modelgraph LR
Model[Model 推理] --> Stop{stop_reason}
Stop -->|tool_use| Loop[继续循环
执行工具]
Stop -->|end_turn| End[结束对话
返回结果]
Loop --> Messages[messages 数组]
Messages --> Modelgraph LR
Model[Model 推理] --> Stop{stop_reason}
Stop -->|tool_use| Loop[继续循环
执行工具]
Stop -->|end_turn| End[结束对话
返回结果]
Loop --> Messages[messages 数组]
Messages --> Modelgraph LR
Model[Model 推理] --> Stop{stop_reason}
Stop -->|tool_use| Loop[继续循环<br/>执行工具]
Stop -->|end_turn| End[结束对话<br/>返回结果]
Loop --> Messages[messages 数组]
Messages --> Model4. 工具执行 —— 行动落地
一句话定义:根据 Model 的 tool_use 决策,执行具体工具。
图表
代码
sequenceDiagram
participant Loop as Agent Loop
participant Model as Model 推理
participant ToolExec as 工具执行器
participant Env as 环境
Loop->>Model: 发送 messages
Model-->>Loop: 返回 tool_use
Loop->>ToolExec: 执行工具
ToolExec->>Env: 调用环境(文件系统/终端)
Env-->>ToolExec: 返回结果
ToolExec-->>Loop: 返回工具输出
Loop->>Loop: 追加到 messagessequenceDiagram
participant Loop as Agent Loop
participant Model as Model 推理
participant ToolExec as 工具执行器
participant Env as 环境
Loop->>Model: 发送 messages
Model-->>Loop: 返回 tool_use
Loop->>ToolExec: 执行工具
ToolExec->>Env: 调用环境(文件系统/终端)
Env-->>ToolExec: 返回结果
ToolExec-->>Loop: 返回工具输出
Loop->>Loop: 追加到 messagessequenceDiagram
participant Loop as Agent Loop
participant Model as Model 推理
participant ToolExec as 工具执行器
participant Env as 环境
Loop->>Model: 发送 messages
Model-->>Loop: 返回 tool_use
Loop->>ToolExec: 执行工具
ToolExec->>Env: 调用环境(文件系统/终端)
Env-->>ToolExec: 返回结果
ToolExec-->>Loop: 返回工具输出
Loop->>Loop: 追加到 messagessequenceDiagram
participant Loop as Agent Loop
participant Model as Model 推理
participant ToolExec as 工具执行器
participant Env as 环境
Loop->>Model: 发送 messages
Model-->>Loop: 返回 tool_use
Loop->>ToolExec: 执行工具
ToolExec->>Env: 调用环境(文件系统/终端)
Env-->>ToolExec: 返回结果
ToolExec-->>Loop: 返回工具输出
Loop->>Loop: 追加到 messagesAgent Loop 的完整流程
流程详解
图表
代码
flowchart TB
subgraph Loop["Agent Loop 完整流程"]
A[用户消息] --> B[初始化 messages]
B --> C[Model 推理]
C --> D{stop_reason}
D -->|tool_use| E[解析工具调用]
E --> F[权限检查]
F -->|deny| G[❌ 禁止执行
返回错误]
F -->|ask| H[⚠️ 请求审批
等待确认]
F -->|allow| I[执行工具]
G --> J[错误信息追加]
H --> J
I --> K[工具输出追加]
J --> L[上下文检查]
K --> L
L -->|溢出| M[上下文压缩]
L -->|正常| N[继续循环]
M --> N
N --> C
D -->|end_turn| O[返回最终结果]
endflowchart TB
subgraph Loop["Agent Loop 完整流程"]
A[用户消息] --> B[初始化 messages]
B --> C[Model 推理]
C --> D{stop_reason}
D -->|tool_use| E[解析工具调用]
E --> F[权限检查]
F -->|deny| G[❌ 禁止执行
返回错误]
F -->|ask| H[⚠️ 请求审批
等待确认]
F -->|allow| I[执行工具]
G --> J[错误信息追加]
H --> J
I --> K[工具输出追加]
J --> L[上下文检查]
K --> L
L -->|溢出| M[上下文压缩]
L -->|正常| N[继续循环]
M --> N
N --> C
D -->|end_turn| O[返回最终结果]
endflowchart TB
subgraph Loop["Agent Loop 完整流程"]
A[用户消息] --> B[初始化 messages]
B --> C[Model 推理]
C --> D{stop_reason}
D -->|tool_use| E[解析工具调用]
E --> F[权限检查]
F -->|deny| G[❌ 禁止执行
返回错误]
F -->|ask| H[⚠️ 请求审批
等待确认]
F -->|allow| I[执行工具]
G --> J[错误信息追加]
H --> J
I --> K[工具输出追加]
J --> L[上下文检查]
K --> L
L -->|溢出| M[上下文压缩]
L -->|正常| N[继续循环]
M --> N
N --> C
D -->|end_turn| O[返回最终结果]
endflowchart TB
subgraph Loop["Agent Loop 完整流程"]
A[用户消息] --> B[初始化 messages]
B --> C[Model 推理]
C --> D{stop_reason}
D -->|tool_use| E[解析工具调用]
E --> F[权限检查]
F -->|deny| G[❌ 禁止执行<br/>返回错误]
F -->|ask| H[⚠️ 请求审批<br/>等待确认]
F -->|allow| I[执行工具]
G --> J[错误信息追加]
H --> J
I --> K[工具输出追加]
J --> L[上下文检查]
K --> L
L -->|溢出| M[上下文压缩]
L -->|正常| N[继续循环]
M --> N
N --> C
D -->|end_turn| O[返回最终结果]
end循环计数
问题:一个任务需要多少次循环?
| 任务复杂度 | 循环次数 | 示例 |
|---|---|---|
| 简单任务 | 2-5 次 | 创建一个文件 |
| 中等任务 | 5-10 次 | 修改代码 + 测试 |
| 复杂任务 | 10-20 次 | 重构模块 + 测试 + 修复 |
| 大型任务 | 20-50 次 | 完整项目开发 |
循环计算:
循环次数 = 工具调用次数 + 最终响应
例如:Read + Edit + Bash + Write + end_turn = 5 次最小 Agent Loop 实现
Python 伪代码
# 最小 Agent Loop 实现
def agent_loop(user_input, model, tools):
messages = [{"role": "user", "content": user_input}]
while True:
# observe: 发送 messages 到 Model
response = model.chat(messages)
# think: Model 推理并返回 stop_reason
if response.stop_reason == "end_turn":
# 任务完成,返回最终结果
return response.content
if response.stop_reason == "tool_use":
# act: 解析工具调用
tool_name = response.tool_name
tool_args = response.tool_args
# 执行工具
if tool_name in tools:
tool_result = tools[tool_name].execute(tool_args)
else:
tool_result = f"Error: Unknown tool {tool_name}"
# feedback: 追加结果到 messages
messages.append({
"role": "assistant",
"content": response,
"tool_use": {"name": tool_name, "args": tool_args}
})
messages.append({
"role": "user",
"content": tool_result
})关键设计点
图表
代码
graph TB
subgraph Design["最小 Loop 设计要点"]
D1[while True 循环
持续运行直到 end_turn]
D2[messages 数组
追加历史,形成上下文]
D3[stop_reason 判断
Model 决定继续或结束]
D4[工具执行
根据 tool_use 执行具体工具]
D5[结果追加
工具输出追加到 messages]
endgraph TB
subgraph Design["最小 Loop 设计要点"]
D1[while True 循环
持续运行直到 end_turn]
D2[messages 数组
追加历史,形成上下文]
D3[stop_reason 判断
Model 决定继续或结束]
D4[工具执行
根据 tool_use 执行具体工具]
D5[结果追加
工具输出追加到 messages]
endgraph TB
subgraph Design["最小 Loop 设计要点"]
D1[while True 循环
持续运行直到 end_turn]
D2[messages 数组
追加历史,形成上下文]
D3[stop_reason 判断
Model 决定继续或结束]
D4[工具执行
根据 tool_use 执行具体工具]
D5[结果追加
工具输出追加到 messages]
endgraph TB
subgraph Design["最小 Loop 设计要点"]
D1[while True 循环<br/>持续运行直到 end_turn]
D2[messages 数组<br/>追加历史,形成上下文]
D3[stop_reason 判断<br/>Model 决定继续或结束]
D4[工具执行<br/>根据 tool_use 执行具体工具]
D5[结果追加<br/>工具输出追加到 messages]
end设计格言:
“One loop & Bash is all you need” —— 一个工具 + 一个循环 = 一个 Agent
Agent Loop 的扩展机制
管理层插入
权限检查:
# 权限检查插入 Loop
def agent_loop_with_permissions(user_input, model, tools, permissions):
messages = [{"role": "user", "content": user_input}]
while True:
response = model.chat(messages)
if response.stop_reason == "end_turn":
return response.content
if response.stop_reason == "tool_use":
tool_name = response.tool_name
tool_args = response.tool_args
# 权限检查(管理层插入)
permission = check_permission(tool_name, tool_args, permissions)
if permission == "deny":
tool_result = "Error: Operation denied"
elif permission == "ask":
tool_result = ask_user_confirmation(tool_name, tool_args)
else: # allow
tool_result = tools[tool_name].execute(tool_args)
messages.append({"role": "assistant", "content": response})
messages.append({"role": "user", "content": tool_result})Hooks 插入
PreToolUse / PostToolUse:
# Hooks 插入 Loop
def agent_loop_with_hooks(user_input, model, tools, hooks):
messages = [{"role": "user", "content": user_input}]
while True:
response = model.chat(messages)
if response.stop_reason == "end_turn":
return response.content
if response.stop_reason == "tool_use":
tool_name = response.tool_name
tool_args = response.tool_args
# PreToolUse Hook
modified_args = run_hooks("PreToolUse", tool_name, tool_args, hooks)
# 执行工具
tool_result = tools[tool_name].execute(modified_args)
# PostToolUse Hook
modified_result = run_hooks("PostToolUse", tool_name, tool_result, hooks)
messages.append({"role": "assistant", "content": response})
messages.append({"role": "user", "content": modified_result})设计格言:
“挂在循环上, 不写进循环里” —— 在工具前后留插口,不改主循环也能扩展
Agent Loop 的上下文管理
上下文溢出问题
问题:messages 数组持续增长,超过 Model 的上下文窗口限制。
| Model | 上下文窗口 | messages 数组增长 |
|---|---|---|
| Claude 4.x | 200K tokens | 每 5-10 个循环可能溢出 |
| GPT-4 | 128K tokens | 更容易溢出 |
图表
代码
graph LR
Messages[messages 数组
持续增长] -->|接近上限| Overflow[上下文溢出
超出窗口限制]
Overflow -->|触发| Compression[上下文压缩
释放空间]
Compression -->|继续| Loop[继续循环]graph LR
Messages[messages 数组
持续增长] -->|接近上限| Overflow[上下文溢出
超出窗口限制]
Overflow -->|触发| Compression[上下文压缩
释放空间]
Compression -->|继续| Loop[继续循环]graph LR
Messages[messages 数组
持续增长] -->|接近上限| Overflow[上下文溢出
超出窗口限制]
Overflow -->|触发| Compression[上下文压缩
释放空间]
Compression -->|继续| Loop[继续循环]graph LR
Messages[messages 数组<br/>持续增长] -->|接近上限| Overflow[上下文溢出<br/>超出窗口限制]
Overflow -->|触发| Compression[上下文压缩<br/>释放空间]
Compression -->|继续| Loop[继续循环]上下文压缩触发
触发时机:
- messages 数组长度超过阈值
- Token 计数接近上下文窗口上限
- 用户手动触发压缩(/compact 命令)
压缩策略:详见第十三章(上下文管理)。
Agent Loop 的并发执行
多工具并发调用
问题:Model 可以在一次响应中调用多个工具,如何并发执行?
图表
代码
graph TB
Model[Model 响应
多个 tool_use] --> Parse[解析工具列表]
Parse --> Parallel[并发执行]
Parallel --> T1[工具1]
Parallel --> T2[工具2]
Parallel --> T3[工具3]
T1 --> Collect[收集所有结果]
T2 --> Collect
T3 --> Collect
Collect --> Append[追加到 messages]graph TB
Model[Model 响应
多个 tool_use] --> Parse[解析工具列表]
Parse --> Parallel[并发执行]
Parallel --> T1[工具1]
Parallel --> T2[工具2]
Parallel --> T3[工具3]
T1 --> Collect[收集所有结果]
T2 --> Collect
T3 --> Collect
Collect --> Append[追加到 messages]graph TB
Model[Model 响应
多个 tool_use] --> Parse[解析工具列表]
Parse --> Parallel[并发执行]
Parallel --> T1[工具1]
Parallel --> T2[工具2]
Parallel --> T3[工具3]
T1 --> Collect[收集所有结果]
T2 --> Collect
T3 --> Collect
Collect --> Append[追加到 messages]graph TB
Model[Model 响应<br/>多个 tool_use] --> Parse[解析工具列表]
Parse --> Parallel[并发执行]
Parallel --> T1[工具1]
Parallel --> T2[工具2]
Parallel --> T3[工具3]
T1 --> Collect[收集所有结果]
T2 --> Collect
T3 --> Collect
Collect --> Append[追加到 messages]并发示例:
# Model 返回多个 tool_use
response = {
"stop_reason": "tool_use",
"tool_uses": [
{"name": "Read", "args": {"file_path": "/src/a.ts"}},
{"name": "Read", "args": {"file_path": "/src/b.ts"}},
{"name": "Grep", "args": {"pattern": "function"}}
]
}
# 并发执行
results = parallel_execute(response.tool_uses)
# 追加结果
for result in results:
messages.append({"role": "user", "content": result})并发限制
| 维度 | 限制 | 原因 |
|---|---|---|
| 最大并发数 | 10-20 | 避免资源耗尽 |
| 文件并发 | 5-10 | 文件系统限制 |
| Shell 并发 | 1-2 | 避免 Shell 冲突 |
Agent Loop 的错误处理
工具执行错误
错误类型:
| 错误类型 | 原因 | Model 处理 |
|---|---|---|
| 工具不存在 | tool_name 未注册 | Model 收到错误,推理替代方案 |
| 参数错误 | tool_args 格式错误 | Model 收到错误,修正参数重试 |
| 执行失败 | 工具执行抛出异常 | Model 收到错误,调整策略 |
图表
代码
graph LR
ToolCall[工具调用] --> Execute[执行]
Execute -->|成功| Success[返回结果]
Execute -->|失败| Error[返回错误]
Error --> Model[Model 推理]
Model -->|重试| ToolCall
Model -->|换路子| Alternative[替代方案]graph LR
ToolCall[工具调用] --> Execute[执行]
Execute -->|成功| Success[返回结果]
Execute -->|失败| Error[返回错误]
Error --> Model[Model 推理]
Model -->|重试| ToolCall
Model -->|换路子| Alternative[替代方案]graph LR
ToolCall[工具调用] --> Execute[执行]
Execute -->|成功| Success[返回结果]
Execute -->|失败| Error[返回错误]
Error --> Model[Model 推理]
Model -->|重试| ToolCall
Model -->|换路子| Alternative[替代方案]graph LR
ToolCall[工具调用] --> Execute[执行]
Execute -->|成功| Success[返回结果]
Execute -->|失败| Error[返回错误]
Error --> Model[Model 推理]
Model -->|重试| ToolCall
Model -->|换路子| Alternative[替代方案]设计格言:
“错误不是终点, 是重试的起点” —— 出错时会重试、腾空间、换路子
Loop 崩溃恢复
崩溃场景:
- Model API 超时
- 工具执行崩溃
- 系统异常
恢复机制:
- 检查点(保存 messages 数组)
- 重试机制(最多 3 次)
- 错误报告(记录崩溃原因)
Agent Loop 的性能考量
循环延迟
| 延迟来源 | 延迟时间 | 优化方法 |
|---|---|---|
| Model API | 1-5 秒 | 缓存、模型选择 |
| 工具执行 | 0.1-30 秒 | 并发执行、后台执行 |
| 权限审批 | 5-60 秒(用户响应) | 批量审批、预设权限 |
总延迟计算:
总延迟 = 循环次数 × (Model延迟 + 工具延迟 + 权限延迟)
例如:10次循环 × (2秒 + 1秒 + 0秒) = 30秒循环优化
| 优化方法 | 效果 | 代价 |
|---|---|---|
| 并发工具执行 | 减少 50% 循环时间 | 需并发支持 |
| 后台执行 | 用户感知延迟降低 | 需后台机制 |
| 上下文压缩 | 减少 Model 延迟 | 可能丢失历史 |
| 子 Agent 隔离 | 减少主 Loop 复杂度 | 增加通信开销 |
本章小结
- Agent Loop 定义:observe→think→act→feedback 的持续循环
- 核心组件:messages数组、Model推理、stop_reason、工具执行
- 完整流程:用户消息→Model推理→stop_reason判断→工具执行→结果追加→循环
- 最小实现:while True循环 + messages数组 + stop_reason判断 + 工具执行
- 扩展机制:权限检查、Hooks、上下文压缩插入Loop
- 并发执行:Model可返回多个tool_use,并发执行提升效率
- 错误处理:工具错误→Model推理→重试或换路子
- 性能考量:循环延迟 = Model延迟 + 工具延迟 + 权限延迟
预告:下一篇我们深入工具系统——从Bash到MCP的工具分发机制。
相关内容
- 第01章 Agent 的 70 年简史 —— 从符号推理到深度学习
- 第02章 LLM 时代的 Agent 革命 —— 2023-2026 爆发期
- 第03章 Harness 概念溯源 —— "模型是大脑,Harness是身体"
- 第04章 Harness 核心架构 —— 四层模型详解
- 第05章 Harness vs Agent vs Model —— 三者关系辨析
如果这篇文章对你有帮助,欢迎请我喝杯咖啡 ☕
支付宝
微信