Hermes Agent Harness 架构深度解析:权限控制、上下文管理与经验沉淀
从终端敲下指令到 Agent 吐出最后一个字,背后走过了很长一段链路:
- 消息先被入口收拢,转换成内部统一的消息对象;
- 会话和上下文被重新组织;
- 模型开始多轮推理,必要时调用工具;
- 过程过长时,系统还要整理上下文、保留历史记录,并在任务结束后尝试沉淀经验。
走过这条链路,Hermes‑Agent 的骨架大致浮现出来。但这种方式更像一次源码漫步,遇到什么模块就解释它在消息路径上的职责,撞见某个工程细节就顺手拆解为什么这么做。
问题也恰在于此:跟着流程走容易熟悉框架,却很难真正理解那些散布各处的设计抉择。例如:
- Profile 为什么依赖
HERMES_HOME做隔离? - 系统提示词为什么越稳定的越要靠前放置?
- 记忆为何要在会话开始时冻结成快照?
- 上下文压缩后为什么不覆盖旧会话,而是用父子链新开一个会话?

这些细节单独拆开来看都不算大,甚至只是很小的工程取舍。可一旦放在一起,它们共同指向了一个问题:
Agent 要真正进入工作流,需要的是一套围绕模型构建的工程约束。
上一篇我们沿着消息看见了模块如何运转。这一篇换一个视角:这些模块组合在一起,究竟构成了什么?为什么这些看似零散的设计,最终会汇聚到同一个系统层——Harness。
更进一步:未来的软件是否也要开始为 Agent 做设计和改造?

Harness 是什么
在软件工程里,test harness 是一个成熟的概念:它是指围绕被测代码搭建的基础设施,负责提供输入、捕获输出、管理执行环境,让测试变得可重复、可自动化。被测代码本身不必修改,harness 从外部包裹它,替它处理所有外部依赖和环境问题。
大模型的本质只是一段文字输入,经过推理后输出一段文字。但它不会自己调工具,也不会替你管理上下文、处理错误、记住上一次做过什么。这些能力需要外面有一层系统来补全。
现在我们把这层系统称为 Harness。我更愿意把它看成 Agent 的运行时:模型提供能力,而任务如何推进、工具如何调用、状态如何保存,都交给这层系统。模型是能力源,Harness 决定这股能力能不能从演示走向真实。

几乎所有 Agent 项目的起步阶段都差不多:接入一个模型,写一段系统提示词,挂几个工具函数,再搭一个聊天入口。这个阶段很容易见到效果,尤其针对简单任务、少量工具、模型规模足够时,表现往往相当不错。
但用户的意图没有边界,不会只扔一个孤立问题。今天让 Agent 查资料,明天让它接着昨天的结论修改方案,后天又补充一条新限制。任务执行到一半,会发现文件权限不足、需要确认;运行测试时环境不对;工具返回的错误难以理解。再加上上下文越拉越长、工具列表越来越多,原先“模型加函数”的结构很快就会撑不住。
此时难点已从“会不会调用工具”转换成“能不能在一套可控系统里做事”。如果不在系统层面处理这些问题,就只能把它们塞进 prompt。一开始可能有效,任务一长,这些说明书式的指令很快就会被模型遗忘。这些判断不能全凭模型去猜,必须在模型外面有一层系统把这些事管起来。
起初它们像一堆零散的工程补丁,放在一起看,就是 Harness 的雏形。下面我们沿着 Hermes‑Agent 里几个最典型的设计往下走,每个点都不大,但放到一起,会看到一个 Agent 从聊天壳朝着运行时系统生长的过程。
LLM 与系统
上一篇从一条消息开始。用户输入一句话,表面只是请 LLM 帮忙做事,但系统内部早已发生了多层转换。入口层先把不同来源的消息统一起来:命令行、Telegram、Slack、飞书、Email,这些入口的交互方式截然不同,进入核心后都会被收敛成一套内部的消息对象。
往外走时亦然:模型不需要知道飞书怎么传附件、Discord 怎么带文件,它只需吐出统一的媒体协议,再由网关转换回各自平台的格式。这一步是把平台差异挡在 Agent 核心之外。

再往下是 AI Agent 的主循环:它先决定调用什么工具,系统去执行,然后把结果拿回来,它再根据结果判断下一步,如此反复,直到给出最终回答,或者预算耗尽,又或者用户主动打断。
Agent 做事不会一帆风顺,中间会报错,也需要模型根据工具的执行结果不断思考、重试。一个真正能工作的 Agent,必须允许这种来回调整,否则就只能处理短任务。任务一旦复杂,要么执行不下去,要么直接返回一个看似合理的假结果。
上一篇我们已经把这些环节拆开讲过了。现在回头再看,它们其实都在回答同一个问题:一个 Agent 要怎么进入真实系统,并且长期跑下去? 真正难的不是让模型回答一句话,而是在入口众多、工具复杂、上下文不断膨胀的环境里,还能守住权限边界,失败以后也能继续往前推进。Harness 这个概念并不是为了显得高级而硬造出来的,它是被模型能力的缺口逼出来的,是逐一解决 bug 后沉淀下来的集合。
上下文管理
上下文管理是 Harness 中最关键的模块。模型能否做出合理的决策,很大程度上取决于这一层。用户说过的内容、项目规则、工具结果、记忆中的偏好,都可能影响下一步的判断。
但模型的上下文窗口再大,也不是什么都能往里塞。Hermes‑Agent 里那些压缩、缓存、摘要、快照冻结等设计,指向的是同一个问题:当前推理到底该携带哪些信息?哪些只要存在数据库里?哪些值得沉淀为长期记忆?
短任务 Agent 可以不太在意历史。用户问一句,模型答一句,工具跑一下,就结束了。长期运行的 Agent 则不同——连续对话的历史会累积,项目上下文会变化,工具结果越堆越多,用户还可能在中途插入新需求。
这里最怕两件事:一是把所有东西都塞进上下文。短期看信息很全,长期看成本高、速度慢,旧信息还会污染新判断。二是直接丢弃历史。这样做虽然省钱,但用户回头追问时,Agent 就会“失忆”。
Hermes‑Agent 的处理方式是分层。当前推理只保留必要的上下文,完整历史存入 SQLite,稳定的偏好和项目事实放进记忆,真正能复用的流程沉淀为技能。不同类型的数据,不该堆在同一个地方:
模型上下文 适合放眼前要用信息
数据库 适合放完整记录
记忆 适合放稳定偏好和项目事实
技能 适合放可复用的方法和流程
把它们混在一起,只会越来越乱。

我第一次看到上下文压缩后新开 session、旧 session 作为 parent 保留的设计时,确实停了一下。没想到上下文可以这样处理,也算给了我一个新的尝试方向。
记忆并不是把所有聊天记录都塞回系统提示词,那样很容易失控。Hermes‑Agent 把会话中的记忆写入磁盘,但当前会话的系统提示词快照不变,下次新会话再加载。这一切说到底都是为了控制成本,是用一致性换性能的工程权衡。说实话,我不确定这个取舍在所有场景下都是正确的。如果用户在会话中途发生了明确偏好变化,Agent 可能要等到下一轮会话才能反应过来。
上下文解决的是当前任务怎么做,记忆解决的是下一次能不能少走弯路。
工具编排
很多人谈到工具调用时,讲得很轻巧,好像只是给模型挂几个函数。但一旦面临工具乱用、或明明该用却不用时,就手足无措了。人使用软件时,界面会给出很多暗示:按钮的位置、颜色代表警告、报错后的提示信息,人会自己脑补上下文。但 Agent 不会。它只能看到工具名、参数描述和返回结果,如果这些描述不够清晰,它就只能凭猜测行动。
因此,给 Agent 设计的工具与普通 API 不是一回事。API 最初主要是给人用的:人看不懂可以翻文档,参数传错可以看报错,跑不通就打断点,边试边改。哪怕接口设计得别扭,人也能靠经验兜住。
但工具一旦交给 Agent 使用,情况就变了。Agent 不会像工程师那样慢慢摸索,它需要在一次推理中做出判断:
这个工具到底适不适合当前任务
该传什么参数
参数错了会不会造成严重后果
调用失败后,是重试、换工具,还是直接告诉用户做不了
......
这些若不说清楚,Agent 很容易乱用工具。不是不会调用,而是看起来会用,实际用得一团糟。
因此,为 Agent 设计工具不能只靠一份给人看的 API 文档,更像是在为它量身编写操作指南:
什么时候用
什么时候别用
参数有什么约束
失败后怎么处理
......
写得越清楚,Agent 做判断时就越不容易跑偏。同时,API 返回的错误信息也变得极其重要。如果一个工具只返回“失败”二字,人可以去翻日志,Agent 却很可能卡住,更糟的是它还会自己编造一个解释继续跑。好的错误信息应该能让它看出下一步怎么办:权限不够、路径不存在、网络临时中断,还是这本就不该做。
工具一多,问题会更明显。起初只有两三个工具时,模型还能大概猜到;工具多了以后,你需要按场景分工具集,参数别太啰嗦,危险操作要确认,别让两个工具干冲突的事。Hermes‑Agent 里那些工具注册、并行执行、路径检查等设计,说到底就是为了让 Agent 搞清楚自己能干什么、不能干什么。

执行控制:预算和中断
Agent 跑起来以后,最怕两件事:一是陷入无限循环烧 token,二是用户按了 Ctrl+C 后台却还在执行。
迭代预算用来解决第一个问题。主循环有一个硬性预算:父 Agent 上限 90 轮,子 Agent 50 轮。模型每推理一轮消耗 1 次,不论这一轮并行调用了几个工具。预算耗尽就强制退出,这是硬上限,防止模型在错误循环或幻觉里把 token 烧光。
级联中断用来解决第二个问题。父 Agent 每 30 秒给子 Agent 发一次心跳。一旦父被用户中断或自己挂掉,心跳断开,子 Agent 就会连锁停下。没有这个机制,用户按了 Ctrl+C 之后,后台仍会有一堆子 Agent 在继续烧 token。
用户中断的处理也值得单独说一下。中断发生时不是 raise 抛异常,而是 break 跳出循环,先持久化已有结果,再返回 interrupted=True。如果前面有工具调用已追加到消息列表但还未执行,系统会补一条伪造的错误 tool result,保证消息结构对 API 合法。这个伪造的错误 tool result 非常实用:做过 Agent 的人都知道,用户中断后工具执行也被截断,没有结果记录,下一轮调用时的消息结构就不完整,API 直接报错。伪造一个结果回去,下次恢复对话就不会被 Provider 拒绝了。

工具让 Agent 触碰到了真实系统,执行控制给了它刹车和油门,但上路后一定会出错。出错以后怎么办?
错误恢复
主循环的每一步都可能出问题:上下文不够用、API 超时、凭证限流、服务器 500。各种错误长相不一,HTTP 状态码不同,报错信息不同,模块来源也不同。Hermes 采用的方式是按错误分类,各走各的恢复路径,而不是用一个大 try/except 一兜了之。
API 调用失败的原因五花八门:认证失败、额度耗尽、限流、上下文超限、模型不存在、网络中断……FailoverReason 枚举将这些归为 14 类。每个错误抛出时,先经过分类器加工,再封装成 ClassifiedError,只带四个布尔恢复标记:
retryable: bool # 能不能直接重试
should_compress: bool # 要不要先压缩上下文再重试
should_rotate_credential: bool # 要不要切换到下一个 API Key
should_fallback: bool # 要不要切到 fallback 模型
主循环拿到 ClassifiedError 后不做字符串匹配,只看这四个标记决定下一步。像 rate_limit、insufficient_funds,或 openai 模块抛出的 BadRequestError 这类脏活儿,全都集中在分类器里。分类器先把错误映射为恢复动作,主循环只负责分发。
为什么分这么细?一个典型的对比是 HTTP 402 和 429。它们表面上都是限额类错误,但处理方式完全不同:
- 429 是临时限流:Provider 告诉你请求太快,需要歇一下再来,退避重试同一个 Key 就能恢复。
- 402 是额度耗尽:账户上的钱已经扣光,同一个 Key 短期内不会恢复,必须立即切到下一个 Key。
不区分清楚的话,Agent 会在一个已没钱的 Key 上反复退避到天荒地老。分类器把这两种错误映射到不同的恢复标记组合,主循环看标记就知道该退避还是该换钥匙。
对做过后端的人来说,这并不陌生——与微服务里的重试、熔断、降级是同一类问题。只是放在 Agent 场景下,错误来源更杂:模型 API、工具执行、文件系统和网络都可能抛出不同类型的失败,分类器的作用就是在这个大杂烩里理出头绪。
错误恢复解决的是出事之后怎么继续走。但 Agent 还有一类问题比出错更麻烦:它做了不该做的事怎么办?

权限与安全
一提到权限,用起来其实挺烦的。我们在使用 Claude Code 时,经常会被弹窗打断:是否允许它执行某个操作?从体验上说,每多一次确认,自动化就少一点流畅感。尤其是做演示时,一个 Agent 从头跑到尾,全程不打断,看起来当然最厉害。
但问题在于,Agent 不只是在聊天框里生成文字。它开始影响真实环境了:改文件、跑命令、调外部 API、发消息,甚至触发一整套业务流程。虽然我们心里也不想要那么多权限确认,但它确实是真实运行时中非常重要的部分。
回想当年做信息化转型的时候,财务数据无论如何都不肯松口,财务部门的人咬着不放,就是这个道理。
写文件这件事本身不一定危险。写到 /tmp 目录,很多情况下可以直接放行;但如果要修改 .env、配置文件、启动脚本,那就不是一回事了。跑命令也一样:ls、cat、git status 这类读状态的命令,通常没必要每次都拦;但一旦涉及删除、覆盖、部署、提交变更,就必须让人确认。外部 API 更明显:查一条订单信息和取消一笔订单,虽然都叫调用接口,风险完全不在一个级别。
所以权限控制最好绑定到具体动作和上下文,而不是只绑定到某个工具名。Agent 真正跑起来后,最麻烦的不是会不会调用工具,而是它可以自己做什么,什么必须停下来问人。
读一个文件,可以直接放行;
删一个目录,就不能这么随便;
发一封邮件、下一个订单、改一段共享记忆,这些动作更不能全交给模型自己判断。
边界说不清,Agent 看起来是在自主执行,实际上就是靠默认策略硬撑。跑对了叫智能,跑错了就是事故。Hermes‑Agent 这块当然还谈不上终局方案,但它至少已经把边界放进了运行时,而不是只停留在提示词里。比如:
工具集可以启停,危险命令要审批;
子 Agent 不能无限套娃式地继续委托,也不能直接写共享记忆、不能随便对外发消息;
父 Agent 一旦被中断,下面的子 Agent 也要跟着停;
工具调用被打断了,系统也要补一个结果回去,避免下一轮消息结构直接乱掉。
这些设计看起来都很细,但解决的是同一个问题:Agent 可以有自主性,但不能乱跑乱撞。

权限解决的是 Agent 能不能做、做到哪里。可任务做完以后,还有另一个运行时问题:这次执行带来的经验,能不能留给下一次。
经验沉淀
Agent 完成一次任务后,到底留下了什么?聊天记录、工具调用日志、模型请求日志……如果答案只是聊天记录,那这套系统并没有变聪明。下次遇到类似问题,它还得重新摸索,最多是用户自己去翻历史,把上次的经验再喂给它。我们和 AI 交互时经常这么干:把有用的提示词、排查步骤、项目习惯记到自己的小本本里,下次再复制进去。
Hermes‑Agent 有一个很有意思的主张:经验保存到技能,越用越聪明。 技能不是什么神奇能力,说白了就是一份可复用的操作笔记。某个项目怎么跑测试,某类问题以前怎么排查,某个工具有什么坑,这些东西若只散落在对话里,Agent 下次未必找得到。沉成 skill 后,它就从一次性的过程,变成了系统可以再次调用的经验。
核心动作是:任务结束后,另起一个安静的 mini Agent 回看这段对话,判断有没有值得保存的方法。这个动作放在用户收到回复之后,不抢主任务的注意力,也不增加用户等待时间。
这一点很像团队的经验沉淀。一个团队不会因为项目做完就自然变强。真正让团队变强的是项目之后留下来的东西:复盘结论、更新过的文档、补齐的工具、调整过的流程。没有这些沉淀,忙完一轮,下一次还是从头摸索。
Agent 也一样。一次任务跑完,过程再精彩,如果执行轨迹没有留下来,下次遇到类似问题,它还是要重新试一遍。这时候,所谓自主执行其实更像一次性的劳动。Hermes 用 Skills 机制完成了这一切,但我不太愿意把这叫“自进化”——这个词太大,也太容易把事情讲玄。更准确地说,它是在做持续积累:Agent 不只把眼前这件事做完,还要尽量把有用的经验带到下一轮。

到这里,Harness 的边界就从 Agent 自身往外扩了一步。Agent 想沉淀经验,前提是它能读懂自己操作过的系统;而软件想被 Agent 稳定使用,也得开始把动作、状态和失败原因讲清楚。
软件要适应 Agent
如果 Harness 持续成熟,软件本身也会被反向改造。过去做软件,默认使用者是人。界面上的按钮、菜单和流程,都是围绕人的理解方式组织的。人能看懂上下文,也能靠经验判断什么时候该点、出错该怎么查、什么操作会影响线上数据。
这个前提不会消失。人仍然要看界面、做判断、承担责任,UI 依旧重要。但 Agent 进入工作流以后,软件会多出另一种使用者。
Agent 可以看页面、点按钮,甚至模拟浏览器操作,但这并不是最稳定的方式。页面里有太多默认语境:一个提交按钮,人知道它大概意味着什么;一句“操作失败”,人会去查日志、问同事、回想业务规则。Agent 没有这些经验,只能猜。所以未来的软件能力,不能全藏在 UI 里,也不能只停留在传统 API 层。
很多系统早就有 API,但那些 API 主要是给工程师集成用的。字段名可能只有内部人懂,错误信息可能只方便查日志,权限规则也可能散在业务代码里。工程师可以一边调试一边补理解,Agent 不行。它要稳定调用一个能力,需要系统把运行时语义说清楚。比如提交审批这个动作,光有接口还不够。Agent 需要知道当前状态能不能提交,提交前要补齐什么字段,失败后是缺字段、没权限,还是流程状态不允许。它还需要知道这个动作会造成什么后果,是否需要先停下来等人确认。
能把这些信息表达出来,才算是在为 Agent 设计。这并不是说以后软件都会变成聊天框。界面不会消失,人也不会退出流程。变化在于,软件除了给人看、给人点,还要把一部分能力整理成 Agent 能理解、能判断、能安全调用的形式。
这个变化会先影响内部系统。以前做内部系统,一个功能能点到、能看懂、别太反人类,基本就能交差。Agent 进来以后,标准会多一层:这个功能能不能被它接住,条件有没有写清楚,失败信息够不够它判断下一步,而不是只返回一句“操作失败”。
一旦这个标准成立,软件设计的重心就会慢慢偏移。功能入口不再只是页面上的按钮,也可能成为 Agent 编排任务时的一块积木。流程也不一定非要按页面一步步走。只要权限、状态、前置条件和风险边界足够清楚,Agent 就可以在边界内重新组合动作。日志也会变得更重要——它不只是给工程师排错,还会成为 Agent 理解执行过程的依据:刚才发生了什么,哪一步失败了,失败之后还能不能继续。
所以不是 UI 消失了。更准确地说,是软件多了一层新的读者:以前主要写给人看,现在还要让 Agent 看得懂、接得住、用得稳。

结语
在我们真正做 Agent 开发的时候,会发现半年前的模型和半年后的模型对工程能力的要求是不一样的。模型当然重要,没有模型能力,Harness 只是一个空架子。但长期来看,只讨论模型,很容易把问题讲窄。
模型会变,能力边界也会变。今天是某个模型工具调用更稳,明天可能是另一个模型上下文更长,后天又冒出一个新的推理模型。系统如果把自己绑死在某一个模型上,迟早会被动。
更值得投入的,是模型外面那层东西。上下文不是越多越好,工具也不是挂上去就完事。权限要落到具体动作上,失败以后要能接着走,历史和经验也得能被下一次任务找回来。这些没有模型发布会那么热闹,但它们决定 Agent 能不能在真实工作里长期跑下去。
Hermes‑Agent 给我的启发就在这里。它当然不是标准答案,也不是所有 Agent 系统都该照着它长。但它像一个很好的早期切片:你能看到一个 Agent 从聊天壳往运行时系统长出来的过程。
上一篇文章,我们跟着一条消息走了一遍,看到的是 Hermes‑Agent 怎么把一次对话跑完:

这一篇看的是另一层:如果把这条链路放远一点,它背后正在成形的一套 Harness。它关心的不是某一次回答够不够聪明,而是模型能力能不能在真实工作流里稳定、安全、可持续地运行。
再往前看一步,Harness 还会反过来影响软件本身。未来的软件不会只被人使用,Agent 也会使用它。系统不能只给 Agent 一个接口地址,还得把动作的语义说清楚:什么时候能用,当前处在什么状态,越界了怎么办,失败之后还能不能补救,什么地方必须停下来等人确认。
这些听起来都是工程细节,但新一代软件的入口,很可能就是从这些不起眼的地方长出来的。
我现在对 Agent 的判断也更保守了一点。一个 Demo 能跑通,说明不了太多。真正值得看的,是它连续跑十几轮之后还稳不稳;出错的时候,会不会停下来,而不是继续硬冲;任务做完以后,有没有把过程里的经验留下来。
还有一个更容易被忽略的问题:它接入的那个软件,本身有没有给 Agent 留出清楚的动作层。如果这些问题回答不上来,再强的模型也只能撑一阵。如果回答得上来,Agent 才可能真的从聊天框走出来,成为软件系统里一层新的执行能力。