Skip to Content
Hermes Agent 源码解读第 7 章 执行引擎与多模型路由

第 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 的改进。流程是:

  1. Plan:先让模型对整个任务做一次规划,输出一份步骤清单
  2. Execute:按清单逐步执行,每一步可能是一次工具调用
  3. 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.pyprompt_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-sonnetclaude-3-haikuclaude-3-haikutext-embedding-3-small轻度个人使用,每天 1–3 次对话
$20–40(推荐默认)claude-3-5-sonnetclaude-3-haikuclaude-3-5-sonnettext-embedding-3-small中度个人使用,每天 5–10 次对话
$50–100(质量优先)claude-3-5-sonnetclaude-3-5-sonnetclaude-3-5-sonnettext-embedding-3-large重度个人 / 小团队共享
$200+(专业场景)claude-3-opusclaude-3-5-sonnetclaude-3-opustext-embedding-3-large内容质量比成本更重要的商业场景

按预算调整的核心原则:

  1. 降成本的重点不是主决策,是辅助推理。主决策从 Sonnet 降到 Haiku 会让任务成功率明显下降;辅助推理从 Sonnet 降到 Haiku 几乎看不出差别
  2. 内容生成的模型选 main 同款 —— 用户能直接读到输出质量,省这里会被立刻感知到
  3. Embedding 模型很便宜,不要在这里抠成本
  4. 不确定时选”推荐默认”那档,调整一周再决定加减

同样的配置换成其他家模型也可以。主决策 / 内容生成 = 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(主决策 + 内容生成)1862.1M142K$8.43
claude-3-haiku(辅助推理 + 反思)4233.8M78K$1.04
gpt-4o-mini(备用辅助)91680K22K$0.14
text-embedding-3(embedding)1542890K$0.09
合计22427.47M242K$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 的响应、临时结果)
  • 下一步计划

恢复流程:

  1. 用户(或 cron 定时器)触发 “resume” 命令,指定 trajectory id
  2. Hermes 从 checkpoint 加载状态
  3. 重建 context(这一步很关键:要让 LLM 重新”想起”之前做了什么)
  4. 继续执行

要注意的”恢复陷阱”:外部状态可能已经变了。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 章(安全与失败档案)会以真实事故的形式再出现一次 —— 执行引擎的每一个陷阱,都对应着一个现实中被坑过的团队。

Last updated on