Technical Guide
06. Agent 主循环:一次消息如何变成模型调用和工具调用
从 run_agent.py 和 agent/conversation_loop.py 看 Hermes 的核心执行链路。
这篇解决什么问题
Hermes 最核心的源码,不是某一个工具,也不是某个平台适配。
真正的主干是:一条用户消息进来后,Hermes 怎么构造上下文、调用模型、执行工具,再把工具结果送回模型。
这条链路看懂了,后面 Tools、Skills、Memory、Cron、Gateway 都会变清楚。
先看哪几个文件
核心文件是:
run_agent.py
agent/conversation_loop.py
agent/tool_executor.py
model_tools.py
当前源码里,agent/conversation_loop.py 文件开头已经说明:它是从 run_agent.AIAgent 里拆出来的主循环,run_agent.py 里仍保留薄转发和兼容入口。
也就是说:
run_agent.AIAgent.run_conversation
→ agent.conversation_loop.run_conversation
读主循环时,重点看 agent/conversation_loop.py。
一轮对话的简化流程
可以先把它理解成这样:
1. 用户消息进入 AIAgent
2. 构建 turn context
3. 拼系统提示词、记忆、技能、项目规则
4. 计算 token 和上下文预算
5. 调用模型
6. 如果模型返回文本,结束本轮
7. 如果模型返回 tool_calls,执行工具
8. 把工具结果作为 tool message 追加回 messages
9. 再次调用模型
10. 直到模型给出最终回复或达到迭代上限
这就是 Agent loop 的基本形状。
为什么不是一次模型调用就结束
因为 Hermes 是 tool-calling agent。
模型可能会先说:我要调用 read_file。Hermes 执行文件读取,把结果塞回上下文。模型再根据文件内容决定下一步,可能继续调用 patch、terminal、web_search。
所以一次用户请求,内部可能对应多次模型调用和多次工具调用。
主循环里真正难的部分
主循环不只是 while loop。
它还要处理:
- provider 报错和 failover;
- 上下文过长;
- 压缩;
- 工具调用参数修复;
- 用户中断;
- usage 统计;
- billing / entitlement 错误提示;
- 多模态消息;
- 记忆和 skill nudges;
- 最大迭代次数。
所以 conversation_loop.py 很长。读的时候不要试图一次看完。
推荐读法
第一遍只找主干:
def run_conversation(...)
模型调用在哪里发生
tool_calls 在哪里被判断
工具执行在哪里被调用
工具结果如何追加回 messages
什么时候 return 最终回复
第二遍再看异常处理和上下文压缩。
第三遍才看 provider 特殊分支。
一个重要边界
Agent loop 不直接实现每个工具。
它只负责调度:
模型说要调什么工具
→ tool_executor 执行
→ model_tools / registry 找到 handler
→ 具体 tool 文件执行真实动作
这个边界很重要。否则你会在主循环里找不到 web_search、terminal 的具体实现。
如果你要改 Agent 行为
先判断你想改的是哪一层:
改模型调用策略 → conversation_loop / provider adapter
改系统提示词 → prompt_builder / system_prompt
改工具执行并发/展示 → tool_executor
改具体工具行为 → tools/<tool>.py
改工具是否暴露 → toolsets.py / registry / tool config
不要一上来就在 run_agent.py 里硬改。
下一篇看什么
下一篇看 Prompt 与上下文。
因为模型能不能做对事,很多时候不是模型能力问题,而是系统提示词、记忆、技能、项目规则和可用工具共同塑造出来的。