Hermes 连修 4 个隐蔽大坑:附件、API Key、更新、搜索,附案例讲解

你有没有遇到过这种“说不清哪里不对”的怪事:让 Hermes 把一份文件发到 Telegram,它回复“已发送”,但你翻遍聊天记录也找不到附件;或者你同时开了好几个 profile,切换模型时发现用的好像不是自己的 API Key;又或者搜历史会话时,明明记得说过的话却怎么都搜不出来。这些问题不是你的错觉——它们都是 Hermes Agent 里藏得很深的 bug,直到 2026 年 8 月 9 日被一口气修复。这篇文章把这 4 个坑逐一拆开,讲讲每个坑长什么样、为什么会发生、现在怎么修的。
坑一:附件悄悄消失——它说发给你了,其实没有
案例:你在 Telegram 上让 Hermes 生成一份 PDF 报告。它很快回复“报告已生成,正在发送”。但你的聊天记录里只有一行诡异的文字:MEDIA:/path/to/report.pdf——没有文件,只有这段路径文本。更糟的情况是,它连这行字都没有,只是说“已确认发送”,然后什么都没发生。
根因:问题出在“排队发送”路径上。当一条回复需要排队发出时(比如上一轮对话还在进行中、子代理任务压缩、连发多张照片,或者开启了队列模式),第一轮的回复会走一条绕过附件处理的分支路径:没有流式传输的回复直接调用 adapter.send() 把原始文本发出去,MEDIA: 标签没有解析成文件;已经流式传输的回复则记录“发送成功”,但附件被悄悄丢弃。于是 Hermes 告诉你文件已生成,文件却从未真正送达。
修复:新增的 _deliver_queued_first_response() 函数统一接管了排队回复的投递——先用 extract_media 把正文和附件拆开,再走标准的 _deliver_media_from_response 附件投递流程,同时保留了路径安全检查。还有一个贴心的兜底:如果第一轮回复本身失败了,它只会发送错误提示文字,不会再尝试上传附件(避免把失败场景伪装成成功)。
你该知道的:如果你曾经在 Telegram、Discord 等消息平台上遇到过“回复里只有 MEDIA: 文字”或“附件莫名消失”,就是这个 bug。修复后附件会正常送达;在更新到包含此修复的版本之前,遇到附件丢失时可以改用文件浏览器或 web_extract 查看内容。
坑二:API Key 串台——切换模型时读到别人的密钥
案例:你在一台机器上配置了多个 profile(比如工作和个人),各自有不同的模型供应商 API Key。某天你切换到个人 profile,用 hermes 切换模型时,却发现列表里“已认证”的供应商显示的是工作 profile 的密钥——你差点用别人的 Key 发起请求。
根因:多 profile 复用(multiplexing)模式下,模型切换时的密钥读取绕过了 per-profile 的密钥作用域。具体来说有两处:一是模型选择器里“哪些供应商已认证”的列表,二是 switch_model 真正解析密钥的逻辑——后者直接读取进程环境变量(${VAR} 展开和 key_env 回退),然后作为显式密钥传给运行时解析。一个 profile 因此可以看到甚至采用另一个 profile 的 API Key。
修复:新增 _scoped_key_env() 辅助函数,把两处读取统一改道 agent.secret_scope.get_secret——多 profile 关闭时行为与 os.getenv 完全一致(单 profile 用户不受影响),开启时则严格限定在当前 profile 的密钥作用域内。关键设计是 fail-closed(失败即拒绝):如果抛出了 UnscopedSecretError,直接报错而不是回退到环境变量,绝不让密钥悄悄串台。
你该知道的:这是本次修复中唯一的 type/security 级别问题——涉及密钥隔离,建议多 profile 用户尽快升级。单 profile 用户行为完全不变,可以放心更新。
坑三:更新后残留孤儿进程——Windows 桌面版更新卡住
案例:你在 Windows 上使用 Hermes 桌面应用,点击更新后,新版怎么也启动不了——旧版本的进程还占着虚拟环境(venv),文件被锁住,更新反复失败。查任务管理器,能看到一堆“没人管的”后端进程(孤儿进程)赖着不走。
根因:桌面应用更新时,releaseBackendLock() 先给主后端进程发了 SIGTERM 终止信号,然后才调用 forceKillProcessTree() 杀掉整个进程树。在 Windows 上这一步顺序是致命的:启动器(launcher)先退出了,taskkill /T 就无法再枚举它的后代进程——那些子进程存活下来,继续占着 venv,形成了孤儿。
修复:新的 stopBackendTreesForUpdate() 先整树杀掉当前存活的根进程(不再预先发信号),再处理进程池。同时孤儿进程的分类逻辑也升级为“树感知”:扫描器返回的孤儿根进程连同它的后代(包括 re-exec 的 uv 解释器工作进程)会被整体识别并清理,taskkill /T 一次性收走整棵子树;而确实有活着的父进程的后端则不会被误杀。
你该知道的:这个修复只影响 Windows 桌面版更新流程。Linux/macOS 用户不受影响;Windows 用户如果之前遇到过“更新后卡住/残留进程”,更新后应该会明显改善。
坑四:搜索静默失败——日常查询搜不出结果
案例:你在 Hermes 里搜索历史会话,想找之前讨论过的 gateway/run.py 这个文件,结果返回“无结果”。你以为自己记错了,又搜 it's、user@host、50%,全部空空如也。明明这些话肯定说过,为什么搜不到?
根因:会话搜索底层用的是 SQLite 的 FTS5 全文搜索,但查询清洗(sanitizer)只过滤了 6 个字符(+{}():"^)。FTS5 语法还会拒绝很多其他字符——单引号、斜杠、@、逗号、问号、等号、分号、感叹号、竖线、波浪号、井号、美元符、方括号、尖括号、反斜杠等——这些字符直接进入 MATCH 查询时会抛 OperationalError,而异常在查询处被静默吞掉,最终返回零结果。所以你搜的不是“没有”,而是“查询坏了”。
修复:清洗逻辑重建为完整的字符剥离集(用 re.escape 修复了字面反斜杠丢失的问题),并聪明地处理了几个特例:带引号的精确短语("exact phrase")保留、点号和连字符的术语不受影响、% 在非中文查询时被剥离(因为 % 是中文查询 LIKE 回退所需的保留字符,而中文查询根本不会走到 FTS5 报错路径)。
你该知道的:修复后,it's、gateway/run.py、user@host、50% 这类日常查询都能正常解析和匹配;中文查询不受任何影响。如果你之前觉得“Hermes 搜索不好用”,很可能就是这个 bug 在作怪。
总结:一次修复,四个教训
| 坑 | 现象 | 严重级别 | 影响范围 |
|---|---|---|---|
| 附件消失 | MEDIA: 文字泄漏 / 附件静默丢弃 |
功能性 | 所有网关平台(Telegram/Discord 等) |
| API Key 串台 | 切换模型读到别的 profile 的密钥 | 安全 | 多 profile 复用用户 |
| 更新残留孤儿进程 | Windows 更新后旧进程锁住 venv | 功能性 | Windows 桌面版 |
| 搜索静默失败 | 含特殊字符的查询返回空结果 | 功能性 | 所有用户 |
这四个 bug 的共同点:失败都是静默的——附件丢了还说“已发送”,查询坏了还显示“无结果”,密钥读错了还不报错。这也是它们藏得深的原因:用户第一反应通常是怀疑自己,而不是怀疑软件。现在这些问题都已修复,如果你之前被它们坑过,值得在更新后重新试试这些操作。
注:以上修复于 2026-08-09 合入 Hermes Agent main 分支,尚未进入正式 release(最新发布版仍为 v0.20.0)。想立即体验可从源码运行或等待下一个版本发布。