更新与卸载,保持你的 Hermes 最新
Hermes Agent 官方教程第四篇:更新与卸载。升级不丢数据。
更新与卸载,保持你的 Hermes 最新
嘿,朋友们!今天咱们来聊聊怎么让 Hermes 始终保持最新状态,以及哪天想卸载了该怎么办。别担心,这比想象中简单多了。
一键更新,就是这么简单
老规矩,先上命令:
hermes update
就这么一行,Hermes 会自动帮你完成所有脏活累活。它会从 main 分支拉取最新代码、更新依赖,还会自动检测新增的配置选项并提示你设置。要是当时手滑跳过了配置提示,别慌,随时可以手动补救:
hermes config check # 看看缺了哪些配置
hermes config migrate # 交互式补全配置
关闭被动更新提示
如果你用的是固定版本,或者在非交互环境里跑 Hermes,可以关掉 CLI 的版本和横幅更新检查:
hermes config set updates.check false
这会同时屏蔽缓存的更新提示和被动的更新检查网络请求,默认值是 true。注意:手动执行 hermes update --check 和 hermes update 仍然照常工作,这个开关也不管桌面应用自己的更新器。
更新时到底发生了什么?
你可能会好奇,这行命令背后到底干了啥?其实 Hermes 做了八件事:
-
更新前快照:默认会保存一份轻量级状态快照(包括配对数据、定时任务、
config.yaml、.env、auth.json等运行时文件),单个超过 1GiB 的大文件会自动跳过,所以不用担心更新变慢。因为代码切换和网关重启会触及每个 profile,所以安装里的每个 profile 都会各存一份快照到自己的state-snapshots/目录。由updates.pre_update_backup控制(默认quick,full会打包整个HERMES_HOME的 zip,off关闭)。想要更全面的备份?用--backup参数开启完整模式。快照是尽力而为的:万一失败,更新会打印⚠ Pre-update snapshot FAILED警告然后继续,不会卡住你。 -
拉取代码:从
main分支拉取最新代码并更新子模块。 -
语法校验 + 自动回滚:这是最贴心的设计!拉完代码后,Hermes 会立刻编译 9 个关键文件。万一发现语法错误(比如合并冲突残留),它会自动
git reset --hard回滚,保证你的终端永远能用。通过校验后,更新器会在新拉下来的代码上重新执行自己(update.log里会看到=== hermes update continued on the pulled code ===),所以后面的步骤不会新旧模块混着跑。要是你一瞬间看到两个hermes update进程,那就是它在交接班。 -
安装依赖:自动运行
uv pip install -e ".[all]"安装新增或变更的依赖。就算代码已经是最新的,只要虚拟环境不健康(核心导入失败),或者装着的hermes-agent还是旧版本(说明上次的依赖安装被中断了),这一步照样会跑——所以✓ Already up to date!不会掩盖一个半更新的环境。 -
配置迁移:检测新配置项并引导你设置。
-
桌面端重建(stage-and-swap):如果 Hermes 桌面应用是从这个 checkout 构建的,它会重新构建,让 GUI 和新代码保持一致。重建会先打包到
apps/desktop/release/旁边的临时暂存目录,验证通过后才重命名覆盖旧版本。任何一步失败(Electron 下载损坏、依赖缺失、磁盘满)都会保留旧应用不动、仍可启动,更新会报告⚠ Update partially complete,之后运行hermes desktop会重试重建。在 macOS 上,重建好的包还会用ditto(保留签名)复制覆盖掉过期的/Applications/Hermes.app或~/Applications/Hermes.app,这样你从 Finder 和 Dock 点开的版本才和后端一致;如果那份已安装的副本正在运行,更新不会动它,只会提示你退出后再跑一次hermes update。 -
网关自动重启:更新完成后,运行中的网关会自动重启。系统服务(systemd/launchd)走服务管理器,手动启动的也会在能对应上 profile 时自动重新拉起。手动启动的
hermes serve/hermes dashboard后端不一样:更新器会让它们继续跑,请它们的主人自己重启。桌面应用自己拉起的后端也归应用自己管。 -
多路复用迁移(多 profile 安装):当新代码验证通过后,如果一个安装有两个或更多 profile、且仍然每个 profile 各跑一个网关,在没有阻碍的情况下会被折叠成单个多路复用的默认网关(等同于
hermes gateway migrate --multiplex --yes);如果存在阻碍(比如两个 profile 共用一个 bot token,或某个次要 profile 绑定了没有/p/<profile>/入口的端口),更新会打印阻碍项和修复方法,不做任何改动。单 profile 安装永远不会被触碰。
更新前先看一眼计划
机器上跑了好几个 profile 或服务?更新前可以先跑:
hermes update --plan
它会只读地打印安装类型、各 profile 下正在运行的 Hermes 服务、它们的监管方式和实际运行的代码版本,以及每个服务会怎么重启。手动启动的 hermes serve / hermes dashboard 后端也会列出来(带记录的绑定地址),但重启交给它们的主人,更新器不会去停或重启。镜像或包管理器安装的会直接告诉你该用哪条外部更新命令。这个命令不改任何东西,在活着的集群上跑也安全。
另外,每次 hermes update 都会在 ~/.hermes/logs/update_receipts/ 写一份机器可读的回执(保留最近 20 份,latest.json 永远指向最新那份),记录更新前的集群计划、每一步做了什么、跳过了什么及原因、网关重启结果和最终的版本矩阵。重启阶段之后,更新器还会把每个活着的网关实际运行的代码和新代码对比,打印一份按 profile 的矩阵——还在跑旧代码的网关会被大声点名并给出确切的重启命令,更新也会以非零状态退出,这样自动化就不会把混版本的集群当成健康的。
为什么网关重启可能要等一会儿
重启是“先排空”的:运行中的网关会拒绝新的回合,然后等待进行中的工作(聊天回合、定时任务、API 运行)完成后再退出,上限由 agent.restart_after_turn_timeout 控制(默认 30 分钟),所以长时间运行的任务不会被中途切断。等待期间,更新器每 30 秒会打印网关还在等什么。想立刻停止等待,可以结束或杀掉列出的工作,或者在 config.yaml 里调低 agent.restart_after_turn_timeout(设为 0 会立即进入强制排空)。
进阶玩法:更新到其他分支
默认跟踪 origin/main,但如果你想体验测试版或候选版本:
hermes update --branch release-candidate
hermes update --check --branch experimental # 只查看落后多少,不实际更新
如果你的本地 checkout 停在别的分支,Hermes 会自动暂存未提交的改动、切换到目标分支再拉取。分支不存在?会自动从 origin 创建跟踪。失败也会干净退出,不会把你晾在半路。非 main 分支上会自动跳过 main 专属的 fork-upstream 同步逻辑。
停在功能分支怎么办?
有时候你可能不小心把 checkout 停在了某个功能分支。Hermes 会智能处理:
- 分支已完全合并:自动切回
main,并告诉你一声。 - 分支有未合并提交但工作区干净:照样切回
main继续更新(桌面端更新按钮、网关/update、定时任务都依赖这个行为),你的提交一个都不会丢,更新后会打印提示和找回命令。 - 工作区有未提交改动:Hermes 不会碰它,更新标记为 SKIPPED 并给出警告和解决命令。
如果你故意维护自定义分支,可以在 config.yaml 里设置:
updates:
parked_branch_strategy: update_in_place
这样更新会直接把 origin/main 合并进你的分支,而不是切走。冲突时干净停止,不会搞乱你的代码。想临时改回切换路径,可以用 hermes update --switch-branch。另外,设置 updates.auto_switch_parked_branch: false 可以完全关掉自动切换(跳过警告还是会照常出现)。
非交互更新时的本地改动
在终端里跑 hermes update 时,Hermes 会暂存未提交的源码改动、拉取,然后询问是否恢复——和以前一样,交互式更新没有任何变化。
自动暂存只管源码树里的改动。在扁平安装(git checkout 根目录同时也是 $HERMES_HOME,比如用 HERMES_INSTALL_DIR=$HERMES_HOME 装的,或者老安装器建的)里,profile 的运行时状态(state.db 及其 WAL/SHM 文件、state-snapshots/、backups/、sessions/、cron/jobs.json、cron/*.db、config.yaml、auth.json、memories/、锁和 pid 文件等)都是 checkout 里的未跟踪文件。这些路径被 git 忽略,所以自动暂存不会碰它们,网关更新期间数据库安然无恙。如果你在扁平安装的根目录放了别的未跟踪文件,最好挪出去或加进 .git/info/exclude;任何未跟踪又没被忽略的文件都会被当成源码改动卷进暂存。
但当更新没有终端时(比如桌面/聊天应用的“更新”按钮,或网关触发的更新),没有提示可回答。这时由 updates.non_interactive_local_changes 决定暂存的改动怎么处理:
# ~/.hermes/config.yaml
updates:
non_interactive_local_changes: stash # 默认:保留并自动恢复
# non_interactive_local_changes: discard # 丢弃本地源码改动
stash(默认)——自动暂存、拉取,然后自动把你的改动恢复到更新后的代码上。不会丢失;如果恢复时冲突,会保留在 git stash 里供手动恢复。discard——自动暂存并在拉取后丢弃 stash,让更新总是落在干净的工作区上。只在你从不打算保留 Hermes 源码本地改动的机器上使用。它用的是 stash-drop(不是git reset --hard+git clean -fd),所以node_modules、venv、构建产物等被忽略的路径永远不会被碰。
在桌面应用里,这个选项位于 Settings → Advanced → In-App Update Local Changes。
桌面端更新从不自动恢复。 桌面更新器调用的是 hermes update --keep-stash:本地源码改动仍会被暂存以便更新进行,但之后不会重新应用——它们留在 git stash 里,更新日志会打印出恢复用的 git stash apply <ref> 命令。
卸载
(注:官方文档暂未提供卸载章节,建议直接删除安装目录和 HERMES_HOME 下的配置即可,后续文档更新会补充详细步骤。)
保持更新,让 Hermes 始终跑在最新状态,享受最好的体验!有问题随时回来翻翻文档,咱们下期见!🚀
📖 官方文档
本文根据 Hermes Agent 官方文档编写,原文见:官方文档 › getting-started/updating