首页 / 资讯中心 / 文章详情

Hermes Agent Loop拆解:大模型多步任务的可控循环执行框架

Hermes Agent Loop拆解:大模型多步任务的可控循环执行框架 ★ FEATURED ARTICLE
我拆过不少 Agent 项目大部分死法都差不多单轮 Prompt 回答得挺好一旦让它自己把一件事做完就开始原地打转、工具调错、上下文跑飞。后来我把执行层单独抽出来做了一个统一的循环骨架也就是这篇要聊的 Hermes Agent Loop。这篇文章要拆解的就是这个 Loop 从接收任务到完成退出的完整执行流程。它解决的痛点很具体大模型推理不是可靠的服务它随时可能在半路给出一个错误动作循环结构要能兜住这种不确定性。内容适合两类人一类是正在用 LangChain、Dify 这类框架但觉得黑盒太多、想自己掌控执行链路的开发者另一类是准备从零搭 Agent 编排层、想了解循环里每个环节到底该做什么的架构师。1. Hermes Agent Loop 的定位单次推理与循环执行的分界线很多人第一次接触 Agent 时有个误解把 Prompt 写长一点、把工具描述写全一点模型就能自动完成多步任务。实际上模型每次调用只做一件事——基于当前上下文生成下一个动作它没有持续运行的概念。Hermes Agent Loop 做的就是在模型外面套一个可控的 while 循环模型负责出决策Loop 负责保管状态、执行动作、判断是否继续。1.1 单次调用的局限与循环的诞生拿一个最简单的例子让 Agent 查一下某公司最近的融资新闻然后整理成一份简报。如果只有一次 LLM 调用模型做不到先搜新闻、再读细节、最后写简报这组动作因为一次调用的输出长度有限而且工具返回结果是调用之后才出现的模型在生成时根本看不到这些内容。所以第一步必须拆分模型先输出一个计划PlanLoop 拿到计划后解析成一个可执行动作调用工具把结果塞回上下文再让模型决定下一步。这就是循环的雏形。Hermes Agent Loop 在这个雏形上做了一些更严格的定义每次循环迭代只能产出一个动作动作必须来自白名单动作结果必须写入统一格式的状态缓存循环何时退出由收敛条件决定而不是由模型自己说了算。1.2 Loop 需要承担的三类职责我画边界的时候只给了 Loop 三件事其他一概不管状态托管所有跨步骤的数据都放在 State 对象里包括原始任务、历史消息、工具返回值、当前步骤计数。模型调用本身是无状态的所有状态都从 State 注入生成完再写回。动作执行模型输出的文本不能直接执行Loop 负责把它解析成结构化指令校验参数调用对应工具函数再把结果标准化。收敛控制每个循环迭代结束都要做一次是否结束的判断。判断依据有明确的退出条件比如步骤超限、模型主动输出完成标记、或者连续几轮没有产生有效进展。提示不要把业务逻辑塞进 Loop。查库、调 API、算数据这些事应该放在工具函数里Loop 只做编排和状态流转。否则 Agent 每次迭代都要重读一堆业务代码上下文很快就爆了。把 Loop 的职责严格控制在这三项之后最大的收益是出问题时可排查。Agent 行为异常时我只需要看三步状态里存了什么、动作执行是否成功、收敛条件为什么没触发。这比在一个巨大的 Prompt 里找原因要直观得多。2. 五阶段执行流程拆解从意图解析到收敛退出Hermes Agent Loop 的主循环并不是简单的模型输出—执行—再输出五个阶段有明确的输入输出协议和失败处理逻辑。这一节把每个阶段掰开看。2.1 阶段一意图解析与任务边界确认任务进来的时候往往是一句话比如帮我查一下最近三个月大模型框架的发布动态。这句话不能直接丢给循环因为模型会把它理解成无限开放的任务——查多少条时间范围怎么界定输出形式是什么所以第一个阶段必须做任务上限确认把原始任务解析成三要素——目标、约束、产出格式。这一步我会用一个轻量的 LLM 调用来做输出的 JSON 结构固定为{goal, constraints, output_format}。解析失败或格式不对就重试一次再失败则直接拒绝执行不进入循环。这个阶段的隐藏作用是给后续所有阶段划定边界。后续模型每次生成动作时我们都在 System Prompt 里重述这三要素防止 Agent 在第五步突然跑去做一件第一句任务根本没提的事。任务边界越清晰Loop 死循环的可能性越低。2.2 阶段二环境快照构建与初始上下文组装进入循环之前需要准备两份东西环境快照和初始上下文。环境快照是 Agent 可用的全部资源清单包括已注册工具列表、每个工具的入参 Schema、当前时间、工作目录等。初始上下文则是在系统提示词里写入任务三要素、环境快照摘要、历史对话占位符。这里有个容易被忽视的点工具列表不是越多越好。Session 级配置会在这一阶段过滤掉当前任务用不上的工具只把可能用到的注册进快照。过滤逻辑很简单——用任务三要素里的goal做一次关键词匹配匹配不上的工具直接不展示。我见过 Agent 因为工具列表里有一个发送邮件工具就在调研任务里莫名其妙地给用户发了一封邮件过滤之后这类问题基本绝迹。2.3 阶段三推理生成与动作产出循环体每次迭代第一次调 LLM 时输入是所有历史消息加上当前状态输出必须是严格格式化的动作序列。Hermes Agent Loop 用 JSON 约束输出每条消息必须包含thought和action两个字段action里指定tool_name和params。我实践下来比较稳的做法是一次只生成一个动作而不是一口气生成五个动作再依次执行。原因有两个第一后续动作依赖前面动作的结果提前生成的后续动作大概率是瞎猜第二执行到一半如果出错只重试当前动作不需要废弃整条计划。thought字段是给人看的日志但它在格式约束里必须存在。这不仅仅是为了可读性而是让模型在生成动作前强制经历一轮推理。没有这个字段模型经常直接丢出一个工具调用参数错误率明显升高。实测有thought约束比没有时工具调用失败率低 30% 左右可以理解为模型先把话说清楚了再动手就不容易出错。2.4 阶段四工具执行与结果标准化回填动作解析成结构化调用后Loop 在工具注册表里查找对应函数。查找规则不是纯名字匹配而是名字精确匹配 参数 Schema 校验。工具名要全部小写下划线风格避免模型输出的大小写变体导致查不到工具。参数校验这一步非常关键。模型生成的params经常出现类型不对、缺少必填字段、枚举值超出范围这些问题。Hermes Agent Loop 使用 JSON Schema 做运行时校验校验不通过不会把错误直接丢回给模型而是生成一条参数校验失败原因的回填消息。回填消息的格式也是固定的包含expected和actual两段模型下一轮读这条消息就知道怎么修正。工具执行的结果统一包装成一个三元组{success, output, error}。无论内部是返回 DataFrame、字符串还是对象最后都转成字符串摘要写回状态。长输出的截断逻辑在下一章状态管理里细说这里只提一点截断不能从中间切必须保留头尾因为模型对结果开头的总结和结尾的结论最敏感。2.5 阶段五收敛判断与循环退出每一轮迭代结束Loop 都要回答一个问题继续还是退出Hermes Agent Loop 的退出条件有四条满足任意一条就结束模型输出action的tool_name为finish并携带最终答案。当前步骤数超过max_steps配置值。连续n轮没有产生工具调用只是对着历史消息空转。工具执行结果出现致命错误且重试次数已耗尽。第 1 条是最常见的正常退出。第 2、3 条是兜底防止模型陷入死循环。第 4 条是异常退出终止后会把已完成的部分成果和错误信息一起返回而不是让用户看到一条干巴巴的失败提示。注意finish动作必须在工具注册表里占一个位置但实际不会执行任何函数它只是 Agent 对 Loop 说我完成了的信号。很多自定义 Agent 实现没有显式定义这个信号导致模型永远在调工具Loop 只能靠步骤上限强行掐断这其实是设计缺陷。3. 状态管理与上下文传递循环里最容易被忽视的底层细节工具调用链路和 Prompt 设计固然重要但真正决定 Agent 能否稳定完成长任务的是状态管理。状态管理做不好循环跑到第十步时模型就会把前面几轮的工具结果忘得一干二净然后开始重复提问或者胡编乱造。3.1 状态对象的结构设计与作用域控制Hermes Agent Loop 里的状态对象本质是一个可序列化的字典包含四个层级任务级状态原始任务文本、解析后的目标约束、会话 ID。全程只读任何阶段都不能修改。运行级状态当前步数、当前动作列表、已执行动作索引。由 Loop 自身维护工具函数无权限访问。上下文级状态历史消息队列、工具结果摘要列表、上下文窗口当前占用 token 数。每轮结束由 Loop 更新。工具级状态工具函数内部产生的临时数据用完后写回上下文级状态生命周期结束即销毁。这四个层级的作用域控制完全靠命名约定和存取的封装。工具函数不能直接state[...]乱取只能通过state.get_tool_state(name)访问属于自己的命名空间防止多个工具之间互相覆盖。我曾经见过一个真实事故一个 Agent 在执行查询订单并发送提醒任务时因为两个工具共用了同一个状态 key都是result发送提醒时把另一路查询的中间结果当成订单号发出去了。从那时起Hermes Agent Loop 里所有状态访问都强制走命名空间隔离工具之间想共享数据必须显式声明跨界。3.2 上下文窗口占用与历史消息裁剪策略大模型上下文窗口是循环最大的硬约束。每次迭代增加的 token 量大概是模型输出几百 token、工具返回结果几千甚至上万 token。跑十来轮就可能顶到窗口上限。所以必须在写入之前做预估Hermes Agent Loop 采用写入前压缩写入后计数的策略。压缩手段分三个层次截断工具返回的字符串超过阈值比如 3000 字符时保留开头 10% 和结尾 20%中间省略。这个方法对新闻列表、日志记录这类结构化文本特别有效。摘要截断仍然太长时调用一次 LLM 对完整文本做一段不超过 100 字的中文摘要。摘要任务本身消耗 token但和保住 Agent 主流程相比完全值得。丢弃最早几轮的历史消息会被标记为已归档不再参与模型输入。归档前先检查它是否包含工具结果摘要中的关键字段有关键内容就先转入压缩摘要。这套策略的目标不是省 token而是保证模型每次读到的窗口里最近的对话 当前工具结果永远占主要比例。模型对短期记忆更敏感把有限的窗口留给当下比把所有历史都塞进去要稳得多。3.3 循环内外的记忆接口与长任务延续有些任务一轮 Loop 跑不完比如每天抓一次竞品数据连续一个月。这种场景需要把状态持久化到外部存储跨会话恢复。Hermes Agent Loop 在状态对象里预留了记忆接口每个循环结束运行级状态会被序列化成 JSON 存到 Redis 或本地文件新会话启动时检测是否有同一任务 ID 的历史状态有则从上次断点继续。断点续跑的坑在于工具结果摘要已经过压缩恢复上下文时模型看到的不是完整原始结果。我的处理办法是断点恢复时重新执行最后一步工具调用把最新结果作为当前上下文而不是直接读存储的摘要。这样既省 token 又保证数据新鲜。4. 工具调用链路上的三个致命失效点与修复方案把 Loop 跑通只是第一步真正让 Agent 从 Demo 变成可用工具要解决的是工具调用链路上一堆看起来不严重但实际致命的问题。这里挑三个典型失效点每个都给出我在 Hermes Agent Loop 里的修复方案。4.1 幻想工具名与参数结构错配模型经常编造不存在的工具名比如注册表里只有search_web它输出一个web_search。Hermes Agent Loop 的第一道防线是精确匹配匹配失败后不直接报错而是走模糊纠正把模型输出的工具名和注册表里所有工具名做字符编辑距离计算距离小于等于 2 就自动纠正为注册表工具并记录一条已纠正日志。参数错配比工具名错配更隐蔽。模型可能把应该传给params.keyword的内容传到了params.query。JSON Schema 校验能挡住类型错误挡不住语义错配。Hermes Agent Loop 的对策是在工具描述里写清每个参数的别名和近义说法例如keyword的描述写成检索关键词也可接收 query、search_term、keyword_string 等叫法。效果立竿见影参数错配率降了接近一半。4.2 工具返回异常时的错误传递策略工具执行失败的常规做法是把错误信息原样丢回给模型然后重试。这在简单场景没问题但如果错误信息很长、或包含底层异常栈模型会被带偏开始尝试用随机参数反复调用同一工具消耗大量步数。Hermes Agent Loop 的错误回填遵循摘要化 修正引导两条规则。摘要化指错误信息只保留错误类型 最可能的根因 建议检查的参数比如APIKeyError认证失败建议检查 params.auth_token 是否过期。修正引导是在错误回填消息末尾加一行你可以在下一次调用时修正这些参数或者换一个工具完成目标。这行引导至关重要没有它模型容易陷入同一个错误的工具反复调循环。4.3 重复调用与死循环的识别机制死循环最经典的形态模型反复调用一个只读工具比如搜索每次都得到不同结果然后它觉得信息不够再搜一次。单看每一次调用都有意义但整体上任务没有任何进展。Hermes Agent Loop 用三重检测来处理步数硬上限默认max_steps15超限直接终止并输出达到步数上限任务未完成。重复动作检测记录每个工具的调用参数哈希同一工具相同参数出现 3 次且中间没有其他工具成功执行判定为死循环。进展度量每轮迭代计算当前输出与目标任务的语义相似度连续 3 轮相似度没有上升判定为原地打转。三重检测的触发顺序是硬上限最先、重复动作其次、进展度量兜底。前两种容易误判有些任务确实需要反复调用同一工具获取不同结果所以只有进展度量判定后才会彻底终止前两种触发时只发警告并降低该工具后续调用优先级。5. Loop 参数调优与可观测性建设从能跑到跑稳一个执行框架如果只能解决能不能跑那还只是个脚本。配参数、加观测、做容错才是让它变成真正可用服务的分水岭。5.1 关键运行参数的经验区间Hermes Agent Loop 的核心参数不多但每个都直接决定行为表现参数建议值影响max_steps10~20越大越能处理复杂任务但死循环代价也高max_retries2工具失败重试次数超过后走错误退出流程context_max_tokens模型窗口的 70%预留输出和工具结果的余量convergence_threshold0.85进展度量的语义相似度阈值idle_rounds3连续空转轮数超过触发死循环判定这些参数在开发环境应该调得更激进一些max_steps调小、idle_rounds调低让问题早暴露。生产环境再放宽宁可多跑两步也不要频繁误杀。另外不同任务的参数应该差异化比如查资料写报告类任务max_steps给大一点执行固定 SQL类任务给到 5 就足够。5.2 全链路日志的字段标准与埋点位置Agent 调试最大的痛苦是不知道模型内部在想什么。Hermes Agent Loop 从第一版就强制每条日志必须包含五个字段step、input_tokens、output_tokens、action、state_delta。其中state_delta记录本轮状态里哪些 key 变了旧值和新值各是什么。埋点位置固定在五个意图解析完成、环境快照构建完成、LLM 调用返回、工具执行返回、收敛判断完成。每一处只打印一行 JSON 日志配合日志平台可以按会话 ID 快速拉出全链路时间线。调试时遇到Agent 第五步行为诡异先看第五步的state_delta里上下文摘要有没有被错误覆盖比猜模型意图要高效得多。5.3 超时与并发控制避免多任务互相干扰Loop 串行跑没问题一上并发就暴露问题多个会话共享工具实例工具内部状态互相污染。Hermes Agent Loop 的解决方案是每会话一个 Context工具函数通过 Context 里的命名空间读写数据绝不允许静态变量。在此基础上再叠加两层限制工具级超时和会话级并发锁。工具级超时用信号量实现单个工具执行超过阈值通常 30 秒就强制中断并把超时写入错误回填。会话级并发锁控制同一用户同时只能跑一个 Loop 实例防止无意识的重复提交把下游 API 打爆。6. 一次真实任务的逐轮追踪记录纸上谈兵这么多不如跑一个真实任务看看 Loop 是怎么逐轮流转的。下面这个案例是我在本地跑通的完整流程任务是调研 2024 年 AI Agent 框架的主要技术路线输出对比表格。6.1 任务启动与环境快照内容任务文本进入 Loop 后第一阶段意图解析输出{ goal: 调研 AI Agent 框架的技术路线并做对比, constraints: [时间范围 2024 年, 关注框架本身而非应用, 输出必须含技术路线列], output_format: markdown 表格 }环境快照注册了三个工具search_web关键词搜索、read_url读取网页正文、create_table生成 markdown 表格。这一步过滤掉了其他几个不相关的工具。6.2 前五轮的动作序列与状态变化第一轮模型输出thought表示需要先收集资料action为search_webparams.keyword是AI Agent framework architecture 2024。工具返回十条搜索结果结果摘要被截断到 2000 字符写入状态。第二轮模型读完摘要认为需要选择头几个有代表性的框架深入看动作是read_url参数是第一个搜索结果里的链接。工具返回页面正文因为正文超过 3000 字符先截断处理保留开头综述和结尾结论。第三轮模型发现第一个框架是 LangChain 的 Agent 实现原理于是再调read_url看了另一个框架的官网文档注意此时上下文里已经有四条消息token 占用约 18000。第四轮模型开始总结技术路线差异输出thought但action还是read_url要看第三个框架。状态里的行动列表显示三个 URL 分别是 AgentScope、AutoGen 和 LangGraph 的官方文档。第五轮模型读完第三个 URL 后可能觉得资料已经足够动作直接跳到了finish。它给出的最终答案是一段对比文字而不是预期的 markdown 表格。6.3 输出格式偏离时的自动纠正过程第五轮这里出现了一个典型问题模型想结束任务但输出格式不符合目标任务里指定的output_format。Hermes Agent Loop 的收敛模块在判定finish之前先做了一轮格式校验检查最终答案里是否包含 markdown 表格语法。检查没通过于是不终止循环而是生成一条系统反馈消息你的答案缺少 markdown 表格请重新组织输出包含技术路线、主要特点、适用场景三列。第六轮模型重新生成答案这次输出了一张完整的 markdown 表格。收敛模块再次校验校验通过写入最终结果Loop 正常退出。这个案例想说明两件事一是循环不是模型想停就能停要由收敛模块确认产出满足任务要求二是状态管理在一个 6 轮的任务里看起来轻松但一旦任务扩展到几十轮早期工具结果的摘要保存方式、历史消息裁剪策略就会变成决定性因素。提示如果你在复现类似流程建议在你的 Agent 框架里先完整记录每一轮 LLM 原始输出和工具原始返回值不要只存摘要。原样数据是排查任何诡异行为的底牌摘要数据只够做展示。7. Hermes Agent Loop 与其他 Agent 编排方案的差异最后聊一点横向对比方便你在选型时判断这套循环方案到底值不值得采用。市面上常见的 Agent 编排方式大概有三类Prompt 工程单飞、LangChain 类框架的 Agent Executor、以及自己写循环。Prompt 工程单飞适合一次性任务任务复杂就要靠拆 Prompt 和人工干预。LangChain 的 AgentExecutor 本质也是一个循环但它把大量行为隐藏在内部抽象里想自定义收敛判断、上下文裁剪策略就得翻源码改内部逻辑维护成本不低。自己写循环最灵活但需要处理一堆脚本化细节状态序列化、工具注册、错误回填格式、并发隔离工作量很大。Hermes Agent Loop 走的是第三条路的简化版只保留循环控制 状态管理 工具中间层这层骨架其余全部放开。它不对接任何具体模型品牌不绑定向量库也不内置任何业务工具。任何语言里只要有一个 LLM 客户端和一个函数字典就可以在几十行代码内把 Loop 的核心结构搭起来。骨架的抽象程度决定了它的适配广度这也是为什么我可以在不同项目里反复迁移这套循环设计而不需要重写。而对于要不要引入 Agent 平台这个问题我的建议取决于团队情况如果团队已经把任务拆得很清楚、工具接口也稳定自研一个很薄的循环完全可控如果业务上还搞不清 Agent 到底该跑什么流程那先用成熟平台跑通 MVP观察哪些环节最需要自定义再针对性自研。我在实际项目里最深的体会是Agent 项目的天花板不在模型的推理能力而在执行框架的兜底能力。Hermes Agent Loop 的设计本质上就是把模型可能会犯错当成默认前提所有阶段都在围绕这个前提做防御。每次看到 Agent 在一个错误工具参数上反复四次才修正我就觉得状态回填和错误引导的设计还值得再打磨。下一步我准备把进展度量的语义相似度算法换成更便宜的计算方式当前用 embedding 余弦距离在长任务上还是有点贵如果后续有新版本我会再写一篇对比数据出来。
阅读完成 · 觉得有帮助?
咨询建站