第五章
Agent 执行循环
🎧 语音播放
理解 Hermes 如何"思考"并执行任务
🎯 本章目标
- 理解 Agent 循环的核心概念
- 掌握 run_conversation 的完整执行流程
- 理解 IterationBudget 的作用
📚 前置知识
- 第四章的启动流程
- 知道 LLM 是"根据输入返回输出"的
💡 学完收获
- 能解释 Agent 循环的原理
- 理解为什么 Agent 需要多次调用
- 掌握调试循环的方法
5.1 什么是 Agent 循环?
普通程序:输入 → 处理 → 输出(一次完成)
Agent:输入 → 思考 → 行动 → 观察结果 → 再思考 → 再行动 → ... → 输出
💡 类比:侦探破案
侦探不会看一眼现场就得出结论。他会:
- 问一些问题(调用工具)
- 得到答案(观察结果)
- 再问新问题
- 直到证据链完整
Agent 循环就是"侦探思维"的程序化实现。
5.2 核心流程图
以下是 run_conversation 的完整流程:
Agent 执行循环 Agent Loop
flowchart TD
Start(["🧠 run_conversation() 开始"]) --> Check{"budget\n还有吗?"}
Check -->|还有| Build["📝 构建 Prompt\n系统 + 记忆 + 用户输入"]
Check -->|没了| Fail["❌ 达到最大迭代\n返回失败"]
Build --> Call["📡 调用 LLM API"]
Call --> Parse{"解析响应"}
Parse --> HasCall{"tool_calls?"}
HasCall -->|有| Execute["⚡ 执行工具\nhandle_function_call()"]
Execute --> Log["📝 记录工具结果\n到 messages"]
Log --> Check
HasCall -->|没有| Success["✅ 返回最终响应"]
Success --> End(["🏁 完成"])
style Start fill:#dbeafe,stroke:#2563eb
style Check fill:#ede9fe,stroke:#7c3aed
style Execute fill:#d1fae5,stroke:#059669
style Fail fill:#fee2e2,stroke:#ef4444
style Success fill:#d1fae5,stroke:#059669
5.3 关键组件
5.3.1 IterationBudget
迭代预算控制最大循环次数,防止无限循环:
max_iterations
最大迭代次数(默认 90)
remaining
剩余可用迭代次数
run_agent.py
class IterationBudget:
def __init__(self, max_iterations: int = 90):
self.max_iterations = max_iterations
self.remaining = max_iterations
def use(self) -> bool:
"""消耗一次迭代,返回是否还有剩余"""
self.remaining -= 1
return self.remaining >= 0
def is_exhausted(self) -> bool:
return self.remaining <= 0
⚠️ 为什么是 90?
90 次迭代看起来很多,但一个复杂任务可能需要几十次工具调用。每次工具调用都会消耗一次迭代。如果达到 90 次还没完成,说明任务可能有问题。
5.3.2 消息构建
每次循环,Hermes 都会构建完整的消息列表:
消息构建 Message Building
flowchart LR
A["👤 用户输入"] --> D[Messages]
B["🤖 System Prompt"] --> D
C["💾 记忆"] --> D
D --> E["📋 完整消息列表"]
style A fill:#dbeafe,stroke:#2563eb
style B fill:#ede9fe,stroke:#7c3aed
style C fill:#d1fae5,stroke:#059669
style E fill:#fef3c7,stroke:#f59e0b
5.4 循环执行详解
第一步:检查预算
run_agent.py
async def run_conversation(self, new_message: str):
# 检查迭代预算
if not self.iteration_budget.use():
return "错误:达到最大迭代次数"
# 构建消息
messages = self._build_messages(new_message)
# 调用 LLM
response = await self._call_llm(messages)
# ...处理响应
第二步:调用 LLM
run_agent.py
async def _call_llm(self, messages: list[dict]) -> str:
# 调用 OpenAI 兼容 API
response = self.client.chat.completions.create(
model=self.model,
messages=messages,
tools=self.tool_schemas, # 工具定义
**self._make_extra_kwargs() # temperature, max_tokens 等
)
return response
第三步:解析工具调用
run_agent.py
def _parse_response(self, response) -> tuple[bool, list, str]:
"""解析 LLM 响应"""
if hasattr(response.choices[0].message, 'tool_calls'):
tool_calls = response.choices[0].message.tool_calls
return (True, tool_calls, None)
else:
text = response.choices[0].message.content
return (False, [], text)
5.5 工具执行流程
当 LLM 返回工具调用时:
工具执行 Tool Execution
flowchart TD
A["🔧 tool_calls"] --> B["🔍 查找工具"]
B --> C{"工具存在?"}
C -->|存在| D["⚡ 调用工具"]
C -->|不存在| E["❌ 返回错误"]
D --> F["📝 记录结果"]
F --> G["🔄 下一轮循环"]
E --> G
style D fill:#d1fae5,stroke:#059669
style E fill:#fee2e2,stroke:#ef4444
run_agent.py
async def _execute_tool_calls(self, tool_calls):
results = []
for tool_call in tool_calls:
name = tool_call.function.name
args = json.loads(tool_call.function.arguments)
# 调用工具注册表执行
result = await dispatch_tool_call(name, args)
results.append(result)
# 将工具结果添加到消息历史
self.messages.append(...)
5.6 完整消息流程
一个完整的对话,消息列表如何演变:
第一轮
初始状态
messages = [system, memory, user]
第一轮结束
Tool Call
messages = [system, memory, user, assistant(tool_call), tool_result]
第二轮
继续思考
messages = [system, memory, user, assistant(tool_call), tool_result, assistant(tool_call_2), tool_result_2]
最终
完成
messages = [system, memory, user, ..., assistant(text)]
5.7 为什么需要循环?
看一个具体例子:用户说"帮我分析 GitHub 趋势"
1
搜索
调用 web_search
2
提取
调用 web_extract
3
分析
LLM 分析数据
4
输出
返回分析报告
这 4 步就是 4 次迭代。没有循环,就无法完成复杂任务。
5.8 易错点
⚠️ 无限循环
如果 Agent 反复调用同一个工具,可能是:
- 工具返回的结果没有帮助
- Prompt 没有告诉 Agent 什么时候停止
- 工具本身有 bug
⚠️ 预算耗尽
如果达到最大迭代次数,检查:
- 任务是否太复杂?
- 工具是否正常工作?
- 是否需要增加 max_iterations?
📚 本章总结
本章重点:
- Agent 循环 = 思考 → 行动 → 观察 → 重复
- 每次循环构建完整消息列表发给 LLM
- IterationBudget 防止无限循环
- 工具调用结果加入消息历史
一句话记忆点: Agent 循环就是"AI 的自我对话"——它不是一次问答,而是一场多轮对话
延伸思考
- 如果 LLM 返回了多个工具调用,Hermes 是串行还是并行执行?
- 如何让 Agent 在某些条件下提前结束,而不是等预算耗尽?
- 对比:ReAct、Plan-and-Execute、AutoGPT 的循环策略有什么不同?