
这篇文章会帮你理解一个关键问题:用户只发了一条消息,为什么 Hermes 可能连续调用模型和工具很多次?
答案藏在Conversation Loop(对话循环)里。
它让模型负责判断下一步,让 Hermes 负责实际执行工具,再把真实结果交回模型。这个过程不断重复,直到模型能够给出最终回答。
先记住一句话:模型负责决定,Hermes 负责执行;执行结果再交回模型继续决定。
一、它在整个流程的什么位置?
我们已经知道,System Prompt 像 Agent 的长期岗位说明书,Turn Context 则负责在每个用户轮次开始前整理材料。
Conversation Loop 接过这些材料,开始真正处理任务:
System Prompt长期身份与规则↓Turn Context准备这一轮的上下文↓Conversation Loop模型决策与工具执行不断循环↓Turn Finalizer保存结果并结束本轮
Turn Context 回答:
第一次调用模型前,要准备什么?
Conversation Loop 回答:
模型返回以后,Hermes 下一步做什么?
二、把它想象成“主管和执行人员”
可以把模型看成主管,把工具看成执行人员。
用户说:
帮我检查测试为什么失败。模型看到任务后,可能判断:
我需要先运行测试。但模型自己不会真的打开终端。它只能提交一张工具调用单:
工具:terminal任务:运行 pytest
Hermes 收到调用单后:
检查工具和参数; 实际运行测试; 得到真实输出; 把输出写回消息历史; 再次询问模型下一步怎么办。
模型可能继续要求读取配置文件,也可能认为证据已经足够,直接给出结论。

模型不会亲自执行工具;Hermes 执行后,将真实结果重新交给模型。
三、最简循环只有四步
去掉流式输出、重试和 Provider 差异后,Conversation Loop 可以压缩为:
while 还有预算:response = 调用模型(messages, tools)if response 包含 tool_calls:执行工具把结果加入 messagescontinue保存最终回答break
对应的四步是:
模型决策↓是否需要工具?↓ 是Hermes 执行工具↓结果写回 messages↓再次调用模型
如果模型不再要求工具,而是返回普通文本,循环就有机会结束。
四、一个 Turn 不等于一次模型调用
用户发出一条消息,到 Hermes 最终回答,叫做一个turn(用户轮次)。
但一个 turn 内可以调用模型很多次:
用户发来消息↓Turn Context 准备一次↓模型调用 1:要求运行测试↓工具执行,结果回填↓模型调用 2:要求读取配置↓工具执行,结果回填↓模型调用 3:给出最终回答
简单区分:
所以,“用户只问了一次”和“模型只调用一次”并不是一回事。
五、模型究竟返回什么?
第一遍理解时,可以把模型返回分为三类。
1. 普通文本
模型认为已经有足够信息,返回最终说明。
如果通过完成性检查,Hermes 会保存回答并退出循环。
2. 工具调用
模型认为需要真实证据,例如:
{”name”: ”terminal”,”arguments”: {”command”: ”pytest”}}
这只是结构化意图,不表示命令已经执行。
Hermes 还要检查工具名、参数、安全规则和执行顺序,然后才会真正调用工具。
3. 异常或不完整结果
例如空响应、无效工具名、参数 JSON 不完整、上下文过长或 Provider 暂时失败。
Hermes 会根据错误类型选择修复、重试、压缩、fallback,或者受控结束,不会无限循环。
六、为什么工具调用要保存两类消息?
假设模型要求运行测试,消息历史里必须同时保存:
assistant 消息└── 模型决定调用 terminaltool 消息└── terminal 实际返回 2 failed, 18 passed
概念结构类似:
[{"role": "assistant","tool_calls": [{"id": "call_123","function": {"name": "terminal","arguments": "{...}",},}],},{"role": "tool","tool_call_id": "call_123","content": "2 failed, 18 passed",},]
tool_call_id 把调用和结果对应起来。
下一次调用模型时,模型既能看到自己要求做了什么,也能看到真实执行结果。
七、模型并没有直接操作终端
真实过程是:
模型生成工具调用意图↓Hermes 解析并验证↓工具在真实环境中执行↓Hermes 生成 tool 消息↓模型读取执行结果
因此,工具调用失败时要分四层排查:
模型有没有生成正确调用; Hermes 有没有正确解析; 工具有没有成功执行; 结果有没有正确写回消息历史。
这四个环节可能出现完全不同的问题。
八、为什么执行完工具还要再问模型?
工具输出往往只是原始材料。
例如终端返回:
FAILED tests/test_config.py::test_loadFileNotFoundError: config.yaml
它还不是适合直接交给用户的完整答案。模型仍需判断:
这个错误意味着什么; 是否还要读取配置; 是否需要修改; 修改后怎样验证; 最终如何解释原因。
所以工具执行后,Hermes 会把新证据加入 messages,再调用模型。
Conversation Loop 本质上是一个不断获取、验证和解释证据的过程。
九、什么时候继续,什么时候结束?
继续循环
以下情况通常会继续:
模型返回工具调用; 工具调用错误可以让模型自行修正; 上下文过长,但压缩后还能重试; 修改已经发生,但还缺少必要验证。
结束循环
以下情况会结束或受控停止:
模型给出不含工具调用的最终文本; 用户要求中断; 达到模型调用或共享迭代预算; 多次重试后仍然无法恢复; 安全 guardrail 要求停止。
这套边界避免 Agent 在失败时无限调用模型。
十、Grace Call:最后一次收尾机会
假设最后一份正常预算刚好用来执行工具:
预算即将耗尽↓工具已经执行完成↓模型还没来得及总结
如果立即停止,用户可能只看到工具做完了,却没有最终说明。
因此 Hermes 可以给模型一次受控的grace call:预算虽然已经耗尽,但允许再调用一次模型完成收尾。
它不是无限续杯。使用后标记会被消费,防止循环继续扩张。
可以把它理解为:会议已经到点,但允许负责人再用一分钟汇总结论。
十一、用一个完整例子串起来
用户发送:
帮我检查测试为什么失败。第一次模型调用
模型要求:
运行 pytestHermes 执行并回填:
FileNotFoundError: config.yaml第二次模型调用
模型看到错误后要求:
查找实际配置文件Hermes 回填:
配置位于 config/config.yaml第三次模型调用
模型继续读取测试配置,确认测试仍然引用旧路径。
第四次模型调用
模型不再要求工具,给出结论:
测试失败是因为测试配置仍然引用根目录下的 config.yaml,但文件已经移动到了 config/config.yaml。
这时 Conversation Loop 保存最终回答并退出,Turn Finalizer 再负责持久化和清理本轮状态。
最后,用一张检查表收尾
如果下面四点都能解释清楚,就已经理解了 Conversation Loop:
一个用户 turn 内为什么可能调用模型很多次; 模型为什么只决定工具调用,而不亲自执行; assistant 工具调用和 tool结果怎样进入消息历史;什么情况下继续循环,什么情况下结束。
最后浓缩成一句话:
Conversation Loop 让模型根据不断增加的真实证据持续决策,直到形成最终回答或触发受控停止。
夜雨聆风