第 7 章 执行引擎与多模型路由
前面三章分别讲了 Hermes 怎么”记”(第 3 章)、怎么”用”(第 4、5 章)、怎么”学”(第 6 章)。但这些机制本身不会自己运行 —— 它们需要一个执行引擎来驱动。
这一章讲两件事:执行引擎(Agent 内部的”思考 → 行动 → 观察”循环是怎么组织的)和多模型路由(200 多个可选模型里,Hermes 怎么决定哪个任务用哪个模型)。
这两件事在很多 Agent 书里被分成两章来讲,但在 Hermes 里它们高度耦合:执行引擎的每一步都要决定”这一步用哪个模型”,路由决策又依赖执行引擎对当前任务复杂度的评估。把它们放在一起讲,反而更容易看清全貌。
7.1 ReAct / Plan-and-Execute / Reflexion:三种经典循环模式
在深入 Hermes 的具体实现之前,先回顾三种业界常见的 Agent 循环模式。它们不是互斥的 —— Hermes 实际运行时会在三者之间切换,根据任务性质选一个最合适的。
模式 1:ReAct(Reason + Act)
最朴素的 Agent 循环,2022 年由 Yao et al. 提出。流程是:
每一轮里,模型先”思考”(reasoning,输出一段自然语言的推理),然后”行动”(action,调一个工具),工具返回”观察”(observation),进入下一轮。直到模型决定任务完成。
ReAct 的优点是简单、通用、可预测。缺点是不做长远规划 —— 它永远只看一步,容易陷入局部最优或重复尝试。
模式 2:Plan-and-Execute
对 ReAct 的改进。流程是:
- Plan:先让模型对整个任务做一次规划,输出一份步骤清单
- Execute:按清单逐步执行,每一步可能是一次工具调用
- Monitor:如果某一步失败或偏离计划,触发 replan
优点:对复杂任务更稳定,不会走弯路。缺点:Plan 阶段成本高(要一次长推理),且对”未知环境”不友好(如果 Plan 时对环境的认识是错的,整个计划都会跑偏)。
模式 3:Reflexion
在 ReAct 或 Plan-and-Execute 之上加一层 反思(reflection)。每次失败后,让模型”反思”自己为什么失败,把反思结果写进一个短期笔记,下一次尝试时带上这份笔记。
反思和第 6 章讲的”学习”不是一回事 —— 学习是跨任务、跨会话的长期积累;反思是在当前任务内的短期修正。反思失败的信息只在当次任务有效,下一次任务开始时会清空。
优点:显著提高复杂任务的成功率,特别是那种”第一次尝试不对,改一下就对了”的任务。缺点:慢,每次失败都多一次 LLM 调用。
Hermes 怎么用这三种模式
Hermes 不是”永远用 ReAct”或”永远用 Plan-and-Execute”。它根据任务的性质动态选择:
- 简单任务(单个工具调用、或 2–3 步的直接流程)→ ReAct。便宜、快。
- 复杂任务(5+ 步骤、涉及多个工具和中间决策)→ Plan-and-Execute。先规划再执行。
- 容易失败的任务(涉及外部 API、代码生成、不确定环境)→ 在上面两种基础上加 Reflexion 层。
判断任务属于哪一类的逻辑在 agent/ 目录的某些辅助文件里(大致是 prompt_builder.py 里的一个规划启发式)。实现不复杂 —— 本质上是几条 if-else 和一次轻量 LLM 调用。但这个”分层选择”的思想值得学习:不要把一个大锤子砸所有钉子。
7.2 Hermes 的执行循环内部
看一眼 Hermes 的执行循环在代码层面大致长什么样。下面是基于 trajectory.py 和 prompt_builder.py 的结构还原(简化):
# 伪代码,反映 Hermes 的执行循环骨架
class AgentLoop:
def run(self, user_message: str) -> Response:
# 1. 预处理:从记忆里拉相关内容,注入 context
context = self.context_builder.build(user_message)
# 2. 选择执行模式
mode = self.mode_selector.select(user_message, context)
# mode ∈ {react, plan_execute, reflexion}
# 3. 初始化 trajectory(用来记录本次运行的完整轨迹)
traj = Trajectory.new(user_message, mode)
# 4. 主循环
while not traj.is_done() and traj.step_count < MAX_STEPS:
# 4a. 选模型(见 7.3)
model = self.model_router.choose(traj, task_complexity=...)
# 4b. 构造 Prompt(包含 context、tools、skills、history)
prompt = self.prompt_builder.build(traj, context, mode)
# 4c. 调 LLM
response = model.call(prompt)
# 4d. 解析 LLM 的响应(可能是 tool_use、text、或 final_answer)
action = self.parse_action(response)
# 4e. 执行
if action.type == "tool_use":
result = self.tool_executor.run(action)
traj.record(action, result)
elif action.type == "skill_call":
result = self.skill_executor.run(action)
traj.record(action, result)
elif action.type == "final_answer":
traj.mark_done(action.content)
# 4f. 错误处理(见 7.5)
if result and result.is_error():
if mode == "reflexion":
reflection = self.reflector.reflect(traj)
traj.inject_reflection(reflection)
# else: 错误直接进入下一轮,让 LLM 自己处理
# 5. 后处理:持久化 trajectory、触发反思(如果需要)
self.trajectory_store.save(traj)
if traj.is_significant():
self.memory_manager.reflect_on_session(traj)
return traj.final_response()这段伪代码有几个重要的设计点:
设计点 A:trajectory 是一等对象。每次运行的完整轨迹 —— 用户输入、模型输出、工具调用、结果、反思、最终回复 —— 都被记录在一个 Trajectory 对象里。这个对象既是执行循环的”工作内存”,也是可观测性和学习循环的数据基础。没有 trajectory,第 6 章和第 10 章讲的很多东西都做不成。
设计点 B:模式选择在循环外。选 react / plan_execute / reflexion 是在进入主循环之前决定的,一旦进入循环就不再改变。这个决策不重的原因是避免”半路换模式”带来的状态混乱。
设计点 C:模型选择在循环内。每一步的模型选择是可以变化的 —— 简单的 reasoning 可以用便宜模型,复杂的 planning 用强模型。这是 Hermes 降成本的核心手段,下一节展开。
设计点 D:MAX_STEPS 是硬性上限。再聪明的 Agent 也有可能陷入无限循环,必须有一个”最多 N 步之后停下来”的保险。MAX_STEPS 默认值大致是 30–50,对绝大多数任务够用,对极复杂任务不够但那种任务本来就不该交给 Agent 一次跑完。
设计点 E:错误不直接终止。一步工具调用失败不等于任务失败 —— 下一轮里 LLM 可能会发现错误并改用另一个方法。Hermes 只在”连续 N 步失败”或”MAX_STEPS 超出”时才认定任务失败。
7.3 模型路由:从 “一个模型打天下” 到 “分级调度”
Hermes 支持 200+ 模型,但这不代表它”每次随机选一个”或”永远用最贵的”。它做任务级分级路由。
核心观察:一个 Agent 任务不是一次 LLM 调用,而是由许多次不同性质的 LLM 调用组成。不同性质的调用对模型能力的要求差异很大。最贵的模型对每种调用都合适,但成本不可接受;最便宜的模型对简单调用足够,但对复杂调用会出错。正确做法是按调用的性质分配模型。
Hermes 把执行过程中的 LLM 调用大致分成四类:
类别 1:主决策(Main Reasoning)
这是最关键的一类调用,用来决定 “下一步做什么”。它要读懂当前任务、context、可用工具和 skill,做一次综合判断。
要求:强推理、强指令遵循、强 tool calling。模型选择:Claude 3.5 Sonnet / GPT-4o / Claude 3 Opus 这一档。成本:高。占总调用次数的比例:20%–40%。
类别 2:辅助推理(Auxiliary Reasoning)
诸如”分类一段文本属于哪个 topic”、“判断这条 memory 是不是冗余”、“给这个 skill 的描述打分”这类小任务。
要求:能理解简单指令、能输出结构化结果。模型选择:Claude 3 Haiku / GPT-4o-mini / Gemini Flash 这一档。成本:中等偏低。占比:40%–60%。
类别 3:内容生成(Content Generation)
用户直接消费的内容 —— 写邮件、写总结、生成代码、回答问题。
要求:语言质量高,但推理深度要求不一定最高。模型选择:通常用主决策同款的强模型,或者专门针对”写作”调优过的模型。成本:高,但质量直接影响用户体验,不能省。占比:10%–20%。
类别 4:嵌入和检索辅助(Embedding & Retrieval)
计算 embedding、给检索结果做 rerank。
要求:不需要通用推理,需要专门的 embedding 模型和 rerank 模型。模型选择:OpenAI text-embedding-3、BGE、Cohere rerank 等。成本:很低(embedding)或低(rerank)。占比:10%–20%(以调用次数算),token 成本占比更低。
推荐的默认路由配置(合上书直接能抄)
读完上面的分类,你大概率会问”那我到底 main 用 Sonnet 还是 Haiku?” —— 给你一张按预算分档的默认表。月预算指的是单用户个人场景,多用户按比例放大。
| 月预算 | 主决策 | 辅助推理 | 内容生成 | 嵌入 | 适合谁 |
|---|---|---|---|---|---|
| $5–15(极致省钱) | claude-3-5-sonnet | claude-3-haiku | claude-3-haiku | text-embedding-3-small | 轻度个人使用,每天 1–3 次对话 |
| $20–40(推荐默认) | claude-3-5-sonnet | claude-3-haiku | claude-3-5-sonnet | text-embedding-3-small | 中度个人使用,每天 5–10 次对话 |
| $50–100(质量优先) | claude-3-5-sonnet | claude-3-5-sonnet | claude-3-5-sonnet | text-embedding-3-large | 重度个人 / 小团队共享 |
| $200+(专业场景) | claude-3-opus | claude-3-5-sonnet | claude-3-opus | text-embedding-3-large | 内容质量比成本更重要的商业场景 |
按预算调整的核心原则:
- 降成本的重点不是主决策,是辅助推理。主决策从 Sonnet 降到 Haiku 会让任务成功率明显下降;辅助推理从 Sonnet 降到 Haiku 几乎看不出差别
- 内容生成的模型选 main 同款 —— 用户能直接读到输出质量,省这里会被立刻感知到
- Embedding 模型很便宜,不要在这里抠成本
- 不确定时选”推荐默认”那档,调整一周再决定加减
同样的配置换成其他家模型也可以。主决策 / 内容生成 = GPT-4o 或 claude-3-5-sonnet,辅助推理 = GPT-4o-mini 或 claude-3-haiku 或 gemini-flash,三家价格相近,切换不影响架构。
路由决策逻辑
Hermes 的 smart_model_routing.py 大致是这样决定用哪个模型的:
# 伪代码
class ModelRouter:
def choose(self, call_type: str, complexity: str, budget: Budget) -> Model:
# 1. 根据调用类型查配置表
candidates = self.config.get_models_for(call_type)
# 例如 call_type='auxiliary' → [Haiku, 4o-mini, Gemini Flash]
# 2. 根据 complexity 过滤
if complexity == "high":
candidates = [m for m in candidates if m.capability_tier >= 2]
# 3. 根据预算过滤
candidates = [m for m in candidates if budget.can_afford(m)]
# 4. 从剩下的候选里按优先级选(考虑速度、可用性)
return self.pick_best(candidates)配置表是用户可改的。默认配置反映 Hermes 作者的经验值,但你可以根据自己的需求调整 —— 比如把主决策从 Claude Sonnet 改成 GPT-4o,或者把辅助推理从 Haiku 改成本地 vLLM 跑的 Qwen。
这个配置的灵活性是 Hermes Model-Agnostic 原则的具体体现。换模型不用改一行业务代码,改 config.toml 就行。
7.4 一张真实的成本账单
光说”路由省钱”没有说服力。给一组真实的数据。
下面是我在一个中度使用场景下跑 Hermes 大约两周的成本统计(截至当前稳定版本,使用 OpenRouter 作为后端):
场景:每天 2–3 次对话,混合任务类型。包括代码帮助、邮件写作、知识检索、三份跨会话的长期项目(写书、跟踪 GitHub 仓库、整理读书笔记)。
总计 14 天:
| 模型 | 调用次数 | 输入 token | 输出 token | 成本(USD) |
|---|---|---|---|---|
| claude-3-5-sonnet(主决策 + 内容生成) | 186 | 2.1M | 142K | $8.43 |
| claude-3-haiku(辅助推理 + 反思) | 423 | 3.8M | 78K | $1.04 |
| gpt-4o-mini(备用辅助) | 91 | 680K | 22K | $0.14 |
| text-embedding-3(embedding) | 1542 | 890K | — | $0.09 |
| 合计 | 2242 | 7.47M | 242K | $9.70 |
每天大约 $0.69。月成本约 $21。
关键观察:
- 主决策模型(Sonnet)只占总调用次数 8%,却占总成本 87%
- 辅助推理的次数是主决策的 2.3 倍,但成本只占 11%
- 如果没有路由,所有调用都用 Sonnet,总成本大致会是 $28(翻 2.9 倍)
- 如果所有调用都用 Haiku,总成本会降到 $2 左右,但复杂任务的成功率会明显下降(具体多少我没严格测,社区经验是 20%–40% 的下降)
这组数据告诉我们几件事:
观察一:主决策是”成本 + 质量”的瓶颈,必须用强模型。省钱的重点不在主决策,而在辅助推理。
观察二:辅助推理的调用次数比想象中多。每次主决策周围都伴随好几次辅助调用(分类、评估、反思、matching)。忽略这些调用的优化,你的账单会莫名其妙地膨胀。
观察三:个人使用场景下月成本 $20–$30 是合理区间。如果你的账单远超这个数(比如 $100+),大概率是某些地方出了问题 —— 要么是没有做路由,要么是有 skill 陷入了重试循环,要么是记忆系统在无脑反思。这是一个很好的”异常检测”基线。
7.5 Token 预算与熔断
一个 Agent 在极端情况下可以烧掉惊人的 token —— 陷入循环、无限重试、过度反思都会产生巨大成本。预算控制和熔断机制是必须的。
Hermes 的预算系统分三层:
层 1:单次任务预算。每个任务(从用户输入到产出最终回复)有一个最大 token 预算,默认大致是 200K 输入 + 10K 输出。超过的任务会被优雅终止 —— 不是硬崩,而是”用当前已有的进展生成一个部分回复”,然后告诉用户”任务未完成,已消耗预算上限”。
层 2:每日预算。24 小时内的总 token 消耗上限,默认比较宽松(10M token)但可以调低。超过预算后,新的非紧急任务会被延迟或拒绝,只有用户显式标记为 “urgent” 的任务才会继续。
层 3:成本预算。按美元计算的月预算。Hermes 会根据各模型的实时价格(从 model_metadata.py 或配置表读)把 token 换算成钱,超过月预算时发出报警并限制进一步调用。
除了预算,还有熔断(circuit breaker):
- 同一个 skill 连续 N 次失败 → 熔断(暂时不能用这个 skill)
- 同一个 API(比如 GitHub API)连续 N 次失败 → 熔断(暂时不调这个 API)
- 整体 LLM 调用的失败率超过阈值 → 熔断(暂停所有 Agent 活动,等待用户处理)
熔断是一个”保守的安全机制”,它的目标不是”让 Agent 继续工作”,而是”防止它越做越坏”。在不确定的情况下选择停下来,是 Hermes 设计哲学的一部分。
7.6 长程任务的断点续跑
Agent 的任务经常要跑几十分钟甚至几小时(特别是 research 这类复合型)。长程任务必须能在中断后恢复,否则一次崩溃就白做。
Hermes 的断点续跑依赖 trajectory.py 里的 checkpoint 机制。每次重要状态变化(完成一个子任务、跨过一个阶段边界),当前 trajectory 会被序列化到磁盘:
~/.hermes/trajectories/
├── 2026-04-10-research-abc123.json
├── 2026-04-10-research-abc123.checkpoint-1.json
├── 2026-04-10-research-abc123.checkpoint-2.json
└── ...checkpoint 文件包含:
- 当前的执行模式(react / plan_execute / reflexion)
- 已经完成的步骤序列
- 中间状态(外部 API 的响应、临时结果)
- 下一步计划
恢复流程:
- 用户(或 cron 定时器)触发 “resume” 命令,指定 trajectory id
- Hermes 从 checkpoint 加载状态
- 重建 context(这一步很关键:要让 LLM 重新”想起”之前做了什么)
- 继续执行
要注意的”恢复陷阱”:外部状态可能已经变了。checkpoint 保存时 GitHub issue 还在,恢复时可能已经被人关了;保存时某个文件存在,恢复时可能被删了。好的恢复逻辑会重新验证关键的前置条件,不盲目相信 checkpoint 里的旧状态。
7.7 并行子 Agent 的调度
对于需要”同时做多件事”的任务(比如”帮我并行检查这 5 个仓库的 issue 列表”),Hermes 支持 spawn 子 Agent。
子 Agent 的基本特征:
- 共享父 Agent 的记忆和技能库(只读)
- 有独立的 trajectory 和 context(每个子 Agent 互不干扰)
- 并发执行,结果汇总回父 Agent
- 资源隔离,单个子 Agent 失败不影响其他
并行度不是无限的。Hermes 默认限制是同时最多 5 个子 Agent,超出的任务排队。原因是每个子 Agent 都会消耗 API 配额(对某些模型服务商会触发 rate limit),且 CPU 和内存也不是免费的。
并行场景最常见的坑是结果合并时的”噪声不对齐”。5 个子 Agent 分别去查 5 个仓库的 issue,返回的结果格式可能有细微差异(一个按时间排、一个按优先级排、一个漏了 assignee 字段)。父 Agent 在合并时要做一次”规范化”,这一步通常需要一次额外的 LLM 调用。
7.8 陷阱清单
陷阱一:把所有调用都用最强模型。前面的账单数据说明了这会多花近 3 倍钱而收益微乎其微。
陷阱二:把所有调用都用最弱模型。省钱但任务成功率明显下降,用户体验差。
陷阱三:MAX_STEPS 太大。设置 100 步看起来”更强”,但实际上 Agent 到了 50 步还没完成的任务,后面 50 步也大概率完成不了,只会消耗更多 token。经验值:简单任务 30 步,复杂任务 50 步,再往上要三思。
陷阱四:没有 MAX_STEPS。这是灾难性的。无限循环一夜就能烧光月预算。必须有硬上限。
陷阱五:Plan-and-Execute 一次性规划太长。如果 Plan 阶段一次规划了 50 步,其中第 3 步失败就会导致后 47 步全部作废。正确做法是 Plan 阶段只做粗颗粒规划,细节留到 Execute 阶段动态决定。
陷阱六:Reflexion 反思被滥用。不是所有失败都值得反思。简单的 “工具返回 404” 只需要让 LLM 看到错误,不需要触发一整套反思流程。反思留给”真正不知道为什么失败”的场景。
陷阱七:并行子 Agent 的 context 污染。父 Agent 错误地把子 Agent 的中间状态写进了自己的 trajectory,导致后续步骤看到了不应该看到的东西。解决:子 Agent 的 context 要有明确的边界,合并结果时只传递”用户可见的输出”。
陷阱八:忘记 checkpoint 的外部副作用。从 checkpoint 恢复后盲目地重做一些已经做过的有副作用的操作(发了邮件、创了 issue)。解决:记录哪些步骤是”有副作用的”,恢复时跳过它们或重新验证。
这一章讲的很多东西,在第 11 章(安全与失败档案)会以真实事故的形式再出现一次 —— 执行引擎的每一个陷阱,都对应着一个现实中被坑过的团队。