上一篇拆了知识(Agent 需要什么"知识"),这一篇拆运行——一个 Agent 的闭环到底长什么样?
先说结论:Agent 的"运行"不是一次调用,是一个闭环——模型产生下一步决策,Runtime 执行决策并收集环境反馈,再把反馈重新交给模型。 Hermes 的实现是 agent/conversation_loop.py 里的一个 while 循环,核心条件一句话:api_call_count < max_iterations。但注意:循环只是闭环的常见实现,不是 Agent 的定义本身。
普通 LLM 调用通常是一次请求—响应:
输入 → 模型 → 输出 → 结束
Agent 不一样——Agent Runtime 在请求—响应之上增加了状态、工具执行和反馈,使系统能够根据中间结果动态决定下一步。它经常需要多次调用才能完成任务——第一次调用是"理解任务",中间几次是"调用工具、观察结果",最后一次才是"给出最终答案"。
注意:普通 LLM API 本身也可以被应用程序循环调用。所以真正的区别不是"LLM 调一次 vs Agent 调很多次",而是调用方有没有形成一个能够根据模型输出和环境反馈动态决定下一步行动的闭环。
举个例子:让 Agent"查一下今天 GitHub 上 Hermes 有什么新 commit 并总结"。
这不是"一次问答",是一个闭环——模型输出、执行工具、把结果喂回去、再让模型决定下一步。
这是理解 Agent 最关键的一层,也是最容易被忽略的。

一句话记住:
LLM 决定"下一步想做什么",Runtime 决定"这一步怎么真正发生"。
工具调用就是最好的例子:模型不直接执行工具,它只是提出 tool_call 请求;由 Runtime 负责真正执行,再把执行结果转换成后续模型可见的消息。

模型负责决策,Runtime 负责执行闭环。
Hermes 的主循环在 agent/conversation_loop.py,核心就一个 while:

最核心的停止机制(决定 Agent 什么时候停):
核心停止机制
├── iteration budget(迭代预算)
├── max_iterations(最大迭代次数)
└── 当前 turn 没有继续的 tool calls(模型返回纯文本)
这里要区分两层控制:
- while 条件控制Runtime 是否还有资格继续进行下一次模型调用"——`max_iterations` 和 `iteration_budget` 是循环的门槛
- 模型返回无 tool call,则是在循环体内部触发当前 turn 的正常结束——这是循环体内的控制逻辑
两者不是同一层。while 门槛决定能不能再调一次模型,体内的 no-tool-call 判断决定该不该结束这一轮。
其中 `max_iterations` 的默认值取决于版本和配置。
注意:Observe / Reason / Act 不是 Hermes 源码里存在的三个函数——它们是抽象建模,用来帮助理解 Runtime 的四个环节。
| 抽象阶段 | Hermes 源码落点 |
| Observe(观察) | 当前 messages + system prompt + memory + tool 结果 + runtime 状态(没有独立的 observe() 函数,是这些共同构成) |
| Reason(推理) | 调模型(生成 assistant_message,可能带 tool_calls) |
| Act(行动) | _execute_tool_calls(执行模型请求的工具) |
| Loop back | 工具结果 append 回 messages,回到 while 顶部 |
关键细节:tool 调用的结果不是"返回给用户",而是"作为新的上下文喂回模型"。Hermes 在 _execute_tool_calls 之后把结果写进 messages,下一次循环模型就能看到"我调了 git log,输出是这些"。
这就是 Agent 能"自主推进"的本质:每一次循环,模型都多了一段"我刚刚做了什么"的上下文,所以它能决定下一步。
五、没有 tool call ≠ 任务完成
当当前模型调用没有产生需要执行的 tool calls 时,Hermes 通常会把这次 assistant 输出视为当前 turn 的最终响应,从而退出这一轮 conversation loop(源码里对应 text_response(finish_reason=...) 分支)。
但这不等于 Runtime 验证了任务"客观完成"。
模型完全可能:判断错误、提前回答、忘记调用工具、生成一个错误答案。"没有下一步 tool call"只是当前循环的终止信号,不是任务完成的证明。 是否真的完成,取决于模型自身判断,以及是否存在额外的验证/评估机制(verification / reflection / evaluation)。
这也是为什么成熟的 Agent 系统要加验证环节——循环本身不保证正确,只保证"能推进"。
写这篇之前我专门确认过:OpenMAIC(课堂生成)、DeepTutor(学习助手)也有类似的闭环结构——但实现方式不同,不能简单等同。
| 系统 | 闭环实现 | 特点 |
|---|---|---|
| Hermes | conversation_loop.py while | 通用 Agent:工具调用 + 记忆 + 预算控制 + 多退出路径 |
| OpenMAIC | lib/generation/ 流水线 | 分阶段路由:大纲→内容→动作,每阶段独立调模型 |
| DeepTutor | AgenticChatPipeline + agent_loop.py | agent-native runtime:max_rounds 预算 + 无 tool 的回合即 finish |
共同点:都是"调模型 → 看结果 → 再调模型"的闭环。区别在于闭环里塞了什么——Hermes 塞了通用工具,OpenMAIC 塞了课堂生成阶段,DeepTutor 塞了教学对话逻辑(它的 agent_loop.py 注释明确写着 "a round that calls NO tools is the finish"——和 Hermes 的终止信号具有相似的控制逻辑;注意这是"这一层控制逻辑相似",不代表整个 Runtime 架构同构)。
这验证了一件事:Agent 的闭环不是某个框架的专利,是 Agent 的通用运行模型。
七、Agent Runtime 的核心设计决策
拆完源码,Agent 的"运行"其实只有几个关键决策点:
- 什么时候停?(Termination)——iteration budget + max_iterations + "无工具请求"信号,加上 interrupt / error / fallback 等 runtime 控制路径
- 当前状态是什么?(State)——迭代次数、消息列表、工具结果、预算、task_id、exit_reason……没有状态,闭环就无法知道"刚刚发生了什么"
- 工具结果放哪里?(Context / Message)——append 回 messages,作为下一轮的上下文
- 上下文太长怎么办?(Compression)——压缩(Hermes 的
context_compressor)、裁剪、摘要 - 失败怎么办?(Recovery)——tool 失败会被检测(
_detect_tool_failure)并以错误结果回传模型,由模型决定下一步;注意 Hermes 当前没有 tool-level 的自动重试层,重试主要靠"把失败暴露给模型"
这五个决策点,就是 Agent Runtime 的工程骨架。Agent Runtime 本质上是在管理一个不断变化的状态,并让"模型决策 → 环境执行 → 状态更新"这个闭环持续运行,直到满足终止条件。
八、Agent ≠ while loop
说了这么多循环,最后必须泼一盆冷水:Agent 不等于 while 循环。
Agent runtime 可以是:while loop、状态机、workflow、事件驱动 runtime、图执行、planner-executor、多 Agent 编排……现代很多 Agent 系统已经不是简单的 Observe → Reason → Act,而是:
Input → Router → Planner → Sub-agent → Tool
→ Observation → Verifier → Planner → ...
Agent 的关键特征不是"用了 while",而是存在一个能够根据中间状态和环境反馈进行多步决策与执行的闭环。 while 只是实现这个闭环最常见的方式之一。
一点反思
拆完 Hermes 的 Runtime,最直接的感受是:Agent 的能力不再只体现在模型的一次输出里,而体现在模型、Runtime、工具和环境反馈形成的闭环中。 一次调用只是"想了一下",闭环才让 Agent 能"做一件事"——查资料、调工具、看结果、修正、再试。
这也是为什么"Agent 到底怎么运行"值得单独一篇:理解了闭环,才理解了 Agent 和 Chatbot 的本质区别——不是"会不会聊天",是"能不能自己推进一件事"。
夜雨聆风