Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help


Hermes Agent 配置

所有设置均存储在 ~/.hermes/ 目录中,便于随时访问。

::提示 如何最便捷地生成可用的 config.yaml 文件 运行 hermes setup --portal 即可——只需完成一次 OAuth 认证,即可同时获得模型提供方以及四种 Tool Gateway 工具,而无需手动编辑 YAML 文件。Portal 用户还能享受按令牌计费的服务 10% 的折扣。详情请参阅 Nous Portal。

::

目录结构

~/.hermes/
├── config.yaml     # Settings (model, terminal, TTS, compression, etc.)
├── .env            # API keys and secrets
├── auth.json       # OAuth provider credentials (Nous Portal, etc.)
├── SOUL.md         # Primary agent identity (slot #1 in system prompt)
├── memories/       # Persistent memory (MEMORY.md, USER.md)
├── skills/         # Agent-created skills (managed via skill_manage tool)
├── cron/           # Scheduled jobs
├── sessions/       # Gateway sessions
└── logs/           # Logs (errors.log, gateway.log — secrets auto-redacted)

配置管理

hermes config              # View current configuration
hermes config edit         # Open config.yaml in your editor
hermes config get KEY      # Print a resolved value
hermes config set KEY VAL  # Set a specific value
hermes config unset KEY    # Remove a user-set value
hermes config check        # Check for missing options (after updates)
hermes config migrate      # Interactively add missing options

# Examples:
hermes config get model
hermes config set model anthropic/claude-opus-4
hermes config set terminal.backend docker
hermes config unset terminal.backend
hermes config set OPENROUTER_API_KEY sk-or-...  # Saves to .env
:::提示
hermes config set 命令会自动将配置值路由到正确的文件中——API 密钥会被保存到 .env 文件中,而其他所有配置则保存到 config.yaml 中。
::

配置优先级

配置项的解析顺序如下(优先级从高到低):

  1. CLI 参数——例如 hermes chat --model anthropic/claude-sonnet-4(每次调用时均可覆盖现有设置);
  2. ~/.hermes/config.yaml——所有非敏感配置的主要配置文件;
  3. ~/.hermes/.env——环境变量的备用存储位置;对于 API 密钥、令牌、密码等敏感信息,此文件是必需的;
  4. 内置默认值——当未设置任何其他配置时,将使用硬编码的安全默认值。
:::信息提示
敏感信息(如 API 密钥、机器人令牌、密码)应存储在 .env 文件中;而模型类型、终端后端、压缩设置、内存限制、工具集等其他配置则应保存在 config.yaml 中。若两者都设置了相同配置,则非敏感配置以 config.yaml 中的值为准。

::

::提示 组织级部署
管理员可通过系统级托管目录,锁定某些特定的配置和敏感值,防止普通用户对其进行覆盖。详情请参阅托管作用域。

::

运行时限制

对于长时间运行的 Hermes 服务器(包括网关以及通过 hermes serve --isolated 启动的实例),只要操作系统支持,其在启动时会应用所配置的 RLIMIT_NOFILE 软限制值。

runtime:
  nofile_soft_limit: 4096

默认值为 4096。Hermes 会将该数值限制在操作系统的硬限制范围内,且不会降低那些已具有更高软限制的进程的值。如需禁用此调整功能,可将该值设置为 0、false 或 null。在 Windows 系统以及无法修改限制值的沙箱环境中,程序仍会继续启动,而不会更改该限制值。

数据库设置

database: 部分用于控制 Hermes 如何打开其 SQLite 状态数据库(即 state.db),该数据库用于存储会话信息、消息内容以及网关路由配置:

database:
  # Journal mode for state.db: wal (default) or delete.
  # Use delete on filesystems where WAL is unsafe (network mounts, some
  # virtiofs setups). Note: an existing on-disk WAL database is never
  # live-downgraded — Hermes keeps WAL and logs an error telling you the
  # configured delete did not apply. To convert an existing database, stop
  # every process using it and run a one-time offline
  # `PRAGMA journal_mode=DELETE` on the file.
  journal_mode: wal

  # Durability level for every state.db connection: OFF, NORMAL, FULL,
  # EXTRA (or 0-3). Unset leaves SQLite's compile-time default, which
  # differs between interpreter builds. On macOS this is a floor, not a
  # pin: values below FULL are refused to protect against Darwin fsync
  # reordering; EXTRA is honored.
  # synchronous: FULL

  # Optional WAL sizing pragmas (integers). Unset = SQLite defaults.
  # wal_autocheckpoint: 1000     # pages between automatic checkpoints
  # journal_size_limit: 67108864 # cap the WAL/journal size in bytes

Hermes还会在每次进程启动、每次数据库连接时发出警告,提示现有数据库的磁盘日志模式在打开时被悄悄更改为WAL模式——例如操作员手动将某个数据库转换为delete模式的情况——并且指出database.journal_mode就是决定该设置生效的关键参数。

环境变量替换

您可以在config.yaml中使用${VAR_NAME}语法来引用环境变量:

auxiliary:
  vision:
    api_key: ${GOOGLE_API_KEY}
    base_url: ${CUSTOM_VISION_URL}

delegation:
  api_key: ${DELEGATION_KEY}

单个值中可包含多个引用,格式如下:url: "${HOST}:${PORT}"。若引用的变量未被设置,占位符将保持原样(如${UNDEFINED_VAR}不会被替换),同时系统会记录警告。单独的 $VAR 格式则不会被展开。

在多路复用多配置文件网关环境下,配置文件 config.yaml 中的引用会针对该配置文件自身的 .env 文件(即其私有变量范围)进行解析,而非共享的进程环境——因此,除非配置文件 B 自身定义了该变量,否则其中的 ${MATRIX_ACCESS_TOKEN} 将保持未解析状态。单配置文件运行模式则不受此影响。

系统也支持类似 Cursor 的 SecretRef 语法:${env:VAR_NAME} 的解析方式与 ${VAR_NAME} 完全相同(env: 前缀会被省略),因此从 Cursor 或 Claude 配置中复制的 MCP 或提供程序代码片段,在 config.yaml 文件及 mcp_servers 块中均可直接使用且无需修改。其他类型的 SecretRef 来源(如 ${file:...}、${vault:...}、${bitwarden:...})则不会在代码行内被解析——外部密钥后端会在启动时通过 secrets: 块将对应值注入环境,因此应使用 ${env:NAME} 的格式来引用这些变量;对于未知的前缀,系统仅会发出一次警告,随后仍会保持原样。

关于 AI 提供程序的设置(包括 OpenRouter、Anthropic、Copilot、自定义端点、自托管大型语言模型以及备用模型等),请参阅AI 提供程序章节。

提供程序超时设置

您可以为整个提供程序设置 providers.<id>.request_timeout_seconds 作为全局请求超时时间,同时为特定模型设置 providers.<id>.models.<model>.timeout_seconds 以实现覆盖。该配置适用于所有传输方式(OpenAI-wire、原生 Anthropic 及兼容 Anthropic 的接口)下的主轮询客户端、备用请求链、凭据更新后的重新构建过程,以及(针对 OpenAI-wire)的每个请求的超时参数——因此所配置的值将优先于旧的 HERMES_API_TIMEOUT 环境变量。

此外,您还可以为非流式过期调用检测器设置 providers.<id>.stale_timeout_seconds,并为特定模型设置 providers.<id>.models.<model>.stale_timeout_seconds 进行覆盖。此配置将优先于旧的 HERMES_API_CALL_STALE_TIMEOUT 环境变量。

若不设置这些参数,则会保留旧有的默认值(HERMES_API_TIMEOUT=1800、HERMES_API_CALL_STALE_TIMEOUT=90,原生 Anthropic 为 900)。当未显式指定时,本地端点会自动禁用非流式过期检测器;而对于非常大的上下文场景,该检测器的阈值可相应提高。目前该功能暂不支持 AWS Bedrock(无论是 bedrock_converse 还是 AnthropicBedrock SDK,均使用带有独立超时配置的 boto3)。相关示例请参见 cli-config.yaml.example 文件中的注释说明。

更新行为

后台检查与 SSH 认证

启动时进行的更新检查会使用与网络请求相同的隔离式 Git 配置来读取源 URL。因此,全局的 url.*.insteadOf 重写规则无法让官方 SSH 远程地址在公共 HTTPS 检查中被隐藏。

Hermes 内部使用的隔离式 Git 命令默认设置为 ssh -o BatchMode=yes:遇到未知的主机密钥、密码或需要输入口令的加密密钥时,系统不会打开终端提示符,而是直接报错。而那些拥有有效密钥或已配置 SSH 代理的受信任主机则仍可正常完成身份验证。这一设置不会更改磁盘上的 Git 或 SSH 配置,也不会影响你在终端工具中运行的命令。

此内部默认设置会覆盖仓库中的 core.sshCommand 配置。不过,显式设置的 GIT_SSH_COMMAND 环境变量仍具有优先级,因此你可以在其中保留自定义的身份验证或传输命令。如果需要保持非交互模式,可在该自定义设置中加入 -o BatchMode=yes;而允许提示输入的覆盖设置仍可能中断正在运行的后台检查。

hermes update 的相关设置位于 config.yaml 文件的 updates 目录下:

updates:
  pre_update_backup: quick       # quick (state snapshot, default) | full (snapshot + HERMES_HOME zip) | off
  backup_keep: 5                 # Keep this many full pre-update backup zips
  non_interactive_local_changes: stash  # stash | discard
  auto_switch_parked_branch: true       # auto-switch a clean, fully merged parked branch back to main

pre_update_backup 是唯一的预更新安全控制选项:quick(默认值)会将关键状态文件(配对数据、定时任务、配置文件、认证信息;超过 1 GiB 的文件将被跳过)快速快照到 state-snapshots/ 目录中;full 模式除了执行快速快照外,还会将整个 HERMES_HOME 目录压缩并存入 backups/ 目录,对于较大的目录而言,此模式可能需要更长的处理时间;off 则会关闭这两种功能。系统也会兼容旧的布尔值设置(true 对应 full,false 对应 off)。

config.yaml 文件本身的时间点副本——即在 hermes setup 重写该文件之前、hermes migrate 修改它之前,或是文件解析失败时生成的副本——会被保存到 backups/config/config.yaml.<reason>.<timestamp> 路径下。系统中会自动跳过重复的副本,仅保留每个原因对应的最新五个副本,因此这些副本不会在 config.yaml 旁边堆积过多。

对于通过 git 安装的版本,Hermes 会在检出更新分支或拉取代码之前,自动暂存已被跟踪的修改过的文件以及未被跟踪的文件。在交互式终端更新过程中,系统会在恢复这些暂存内容之前给出提示。而非交互式更新方式(如桌面/聊天应用、网关模式,或使用 --yes 参数)则会使用 updates.non_interactive_local_changes 参数:stash 模式会在成功拉取代码后恢复本地对源文件的修改;而 discard 模式则会在成功拉取后丢弃更新过程中产生的暂存内容。仅在使用那些不希望保留本地源文件修改记录的托管安装环境中才建议使用 discard 模式。

在执行暂存操作之前,Hermes 还会恢复因 npm install/build 操作而产生的已被跟踪的 package-lock.json 差异文件。在进行更新之前,请先提交或手动暂存那些有意对锁文件进行的修改。

Hermes 支持七种终端后端。每种后端都会决定智能体的 Shell 命令实际在何处执行——可以是您的本地机器、Docker 容器、通过 SSH 连接的远程服务器、Modal 云沙箱(直接连接或通过 Nous 管理的网关)、Daytona 工作空间、Vercel 沙箱,或是 Singularity/Apptainer 容器。

terminal:
  backend: local    # local | docker | ssh | modal | daytona | vercel_sandbox | singularity
  cwd: "."          # Gateway/cron working directory (CLI always uses launch dir)
  temp_dir: ""      # Session temp root; empty = TMPDIR, else ~/.hermes/cache/terminal
  font_family: ""   # Desktop terminal font; e.g. "MesloLGS NF"
  timeout: 180      # Per-command timeout in seconds
  home_mode: auto   # auto | real | profile — subprocess HOME policy
  env_passthrough: []  # Env var names to forward to sandboxed execution (terminal + execute_code)
  singularity_image: "docker://nikolaik/python-nodejs:python3.11-nodejs20"  # Container image for Singularity backend
  modal_image: "nikolaik/python-nodejs:python3.11-nodejs20"                 # Container image for Modal backend
  daytona_image: "nikolaik/python-nodejs:python3.11-nodejs20"               # Container image for Daytona backend

terminal.temp_dir 用于控制 Hermes 在本地后端存储会话临时文件的路径——包括后台进程的日志、PID/退出文件、代码执行沙箱以及各种工具的运行结果。当该参数为空(即默认值)时,Hermes 会优先使用环境中指定的 TMPDIR/TMP/TEMP 路径;否则它会使用真实存储中的受管理目录 ~/.hermes/cache/terminal,而非 /tmp。在许多发行版中(尤其是基于 Arch 的系统),/tmp 是一个由内存支持的小型 tmpfs 文件系统,在高负载情况下很容易被 Hermes 的会话临时文件占满。该受管理目录会自动清理:超过 72 小时的旧文件会由网关定期清除,而在仅通过 CLI 安装的系统中,则每个进程的旧文件也会被清除一次。若要将会话临时文件存储到其他位置,可设置 temp_dir 为现有的绝对路径;用户指定的路径不会被自动清理。

terminal.font_family 用于控制 Hermes Desktop 中内置终端的字体。该参数可以接受本地已安装的字体系列名称(例如 MesloLGS NF),也可以接受 CSS 字体堆栈格式。作为备用方案,Hermes 会自动添加其自带的 JetBrains Mono 字体堆栈;若该参数为空,则保持默认设置。您可以在 设置 → 外观 → 终端字体 中修改此同一配置项,无需下载 Google Fonts 字体或申请系统字体权限。

对于 Modal、Daytona 和 Vercel Sandbox 等云端沙箱环境,若设置 container_persistent: true,则表示 Hermes 会尝试在重新创建沙箱时保留文件系统状态。但请注意,这并不保证后续运行时仍会是同一个活跃的沙箱环境、相同的 PID 空间或原有的后台进程。

后端概述

后端类型命令运行位置隔离级别最佳适用场景
本地直接在您的机器上运行无隔离开发及个人使用
Docker单个持久化 Docker 容器(跨会话、/new 模式及子代理共享)完全隔离(通过命名空间与权限限制实现)安全的沙箱环境,CI/CD 流水线
SSH通过 SSH 连接的远程服务器网络层级隔离远程开发,使用高性能硬件的情况
ModalModal 云沙箱环境完全隔离(基于云虚拟机)临时性云计算任务,代码评估
DaytonaDaytona 工作空间完全隔离(基于云容器)受管理的云开发环境
Vercel SandboxVercel Sandbox 平台完全隔离(基于云微虚拟机)具有快照机制的文件系统持久化的云端执行环境
SingularitySingularity/Apptainer 容器通过命名空间实现隔离(–containall 参数)高性能计算集群,共享式机器环境

本地后端

为默认后端选项。命令直接在您的机器上运行,且不进行任何隔离处理。无需特殊配置即可使用。

terminal:
  backend: local

默认情况下,本地工具的子进程会保留您真实操作系统用户目录下的 HOME。这样一来,诸如 git、ssh、gh、az、npm、Claude Code 以及 Codex 等外部 CLI 就能找到其在常规终端中已使用的凭证与配置文件。Hermes 的状态仍通过 HERMES_HOME 来按配置文件进行管理;配置文件选择配置、内存、会话或技能时,并不会依据 HOME 来决定。

Hermes 不会更改您系统级的 HOME、终端启动脚本,或是操作系统账户的主目录。该设置仅用于控制通过 terminal 等工具、后台终端进程、execute_code 功能以及 ACP 辅助进程所启动的子进程所接收的环境。

terminal.home_mode

模式主机环境容器环境权衡点
auto保留真实操作系统用户目录下的 HOME使用 {HERMES_HOME}/home推荐的默认模式。主机上的 CLI 可继续正常工作,同时容器的状态也能得以保留。
real强制使用真实操作系统用户目录下的 HOME若可见则强制使用真实操作系统用户目录下的 HOME当父进程意外以指向配置文件主目录的 HOME 值启动时,此模式非常有用。
profile若存在则使用 {HERMES_HOME}/home若存在则使用 {HERMES_HOME}/home能实现严格的按配置文件隔离 CLI 配置,但除非在配置文件主目录内进行初始化或关联,否则常规的 ~/.ssh、~/.gitconfig、~/.azure、~/.config/gh、Claude/Codex 认证信息、npm 状态等文件将不可见。
默认设置的缺点在于,各主机配置文件会共享位于 ~ 目录下的相同普通用户级 CLI 凭据/配置。如果您需要为某个配置文件设置独立的 Git 身份、SSH 密钥、GitHub CLI 登录信息、npm 配置或云平台 CLI 登录信息,请使用 home_mode: profile,并有意将相关工具初始化到该配置文件的专用目录中。

如果您确实希望实现严格的按配置文件隔离的工具配置,可设置:

terminal:
  home_mode: profile

在该模式下,工具的子进程会将 {HERMES_HOME}/home 作为 HOME 环境变量使用。Hermes 还会设置 HERMES_REAL_HOME,以便脚本在需要时仍能找到真正的用户主目录。而在“自动”模式下,容器后端仍会继续使用 {HERMES_HOME}/home,因为该目录位于持久化的 Hermes 数据卷中。那些需要区分配置文件所在位置与真实用户主目录的脚本,建议将 Hermes 数据路径设置为 HERMES_HOME,而将账户主目录路径设置为 HERMES_REAL_HOME。

from pathlib import Path
import os

hermes_home = Path(os.environ["HERMES_HOME"])
real_home = Path(os.environ.get("HERMES_REAL_HOME", os.environ["HOME"]))
:::warning 该智能体拥有与您的用户账户相同的文件系统访问权限。如需禁用不需要的工具,可使用 hermes tools 命令;若需实现沙箱隔离,则可切换至 Docker 环境。
::

Docker 后端

在经过安全加固的 Docker 容器中执行命令(已移除所有特殊权限,杜绝权限提升风险,并设置了 PID 限制)。

采用单个持久化容器,供所有 Hermes 进程共享。 Hermes 在首次使用时会启动一个长期运行的容器,随后通过 docker exec 命令将所有的终端操作、文件操作以及 execute_code 调用均引导至该容器中——这一机制适用于不同会话、/new 命令、/reset 命令以及 delegate_task 子智能体。工作目录变更、已安装的软件包、/workspace 目录中的文件以及后台进程,都会在多次工具调用之间以及不同的 Hermes 进程之间保持连续性。当您关闭 TUI 会话、执行 /quit 命令或启动新的 hermes 实例时,该容器仍会继续运行,下一个 Hermes 进程会通过标签查找机制重新使用它。具体的容器销毁规则请参见下文的“容器生命周期”部分。

会话隔离模式(container_persistent: false)。在 Docker 后端将 container_persistent: false 设为默认值后,系统会采用“每个会话一个容器”的机制:每一次聊天(桌面应用会话、网关对话、TUI 会话)都会在其首次调用终端或处理文件时创建一个全新的沙箱环境,而当该会话关闭或空闲时间超过 lifetime_seconds 设定的阈值后,该沙箱就会被销毁。不同会话之间完全互不关联——既不存在文件系统状态传递,也没有挂载点或后台进程的延续。若启用 docker_mount_cwd_to_workspace: true,则仅会将当前会话所关联的工作区挂载到 /workspace 目录下;而对于没有关联目录的新会话,则会生成一个空的工作区,而不会继承前一个会话的挂载设置。delegate_task 子代理仍会共享其父会话的容器。当需要将沙箱作为各次对话之间的安全隔离屏障时,可使用此模式;而若希望保持上文所述的长期运行的共享容器机制,则应继续使用默认的 true 值。

terminal:
  backend: docker
  docker_image: "nikolaik/python-nodejs:python3.11-nodejs20"
  docker_mount_cwd_to_workspace: false  # Mount launch dir into /workspace
  docker_run_as_host_user: false   # See "Running container as host user" below
  docker_snap_compat: false        # See "Snap-packaged Docker (AppArmor)" below
  docker_forward_env:              # Host env vars to forward into container
    - "GITHUB_TOKEN"
  docker_env:                      # Literal env vars to inject (KEY=value)
    DEBUG: "1"
    PYTHONUNBUFFERED: "1"
  docker_volumes:                  # Host directory mounts
    - "/home/user/projects:/workspace/projects"
    - "/home/user/data:/data:ro"   # :ro for read-only
  docker_extra_args:               # Extra flags appended verbatim to `docker run`
    - "--gpus=all"
    - "--network=host"
  docker_network: true             # false = air-gap the container (--network=none)

  # Resource limits
  container_cpu: 1                 # CPU cores (0 = unlimited)
  container_memory: 5120           # MB (0 = unlimited)
  container_disk: 51200            # MB (requires overlay2 on XFS+pquota)
  container_persistent: true       # true = persist /workspace + /root, shared container; false = fresh container per session (see below)

  # Cross-process container reuse (defaults match the "one long-lived
  # container shared across sessions" contract — see Container lifecycle).
  docker_persist_across_processes: true   # Reuse container across Hermes restarts
  docker_shared_container_key: ""         # Opt in trusted profiles to one identity
  docker_orphan_reaper: true              # Sweep abandoned Exited containers at startup

  # Cross-backend lifecycle settings (apply to docker as well)
  timeout: 180                     # Per-command timeout in seconds
  lifetime_seconds: 300            # Idle-reaper window; also feeds 2× orphan-reaper threshold

docker_env 与 docker_forward_env 的区别在于:前者会直接注入你在配置中指定的 KEY=value 对(这些值来自 config.yaml 文件,或通过 TERMINAL_DOCKER_ENV='{"DEBUG":"1"}' 以 JSON 字典的形式传递)。而后者则从你的 shell 环境或 ~/.hermes/.env 文件中读取数值,因此敏感信息永远不会出现在配置文件中。对于令牌类数据,建议使用 docker_forward_env;而对于容器所需的静态参数,则可使用 docker_env。

terminal.docker_extra_args(也可通过 TERMINAL_DOCKER_EXTRA_ARGS='["--gpus=all"]' 覆盖)允许你传递那些 Hermes 未将其作为标准键项提供的任意 docker run 参数,例如 --gpus、--network、--add-host,以及自定义的 --security-opt 设置等。每个参数值都必须为字符串形式,该参数列表会被追加到最终生成的 docker run 命令中,从而在必要时覆盖 Hermes 的默认设置。建议谨慎使用此类功能——那些与沙箱安全机制相冲突的参数(如权限降级、--user 设置、工作区绑定挂载等),都可能无形中削弱系统的隔离性。

terminal.docker_network(默认值为 true;环境变量:TERMINAL_DOCKER_NETWORK)——将其设置为 false 可使沙箱容器以 --network=none 的方式运行,从而切断代理命令的所有网络输出。此设置适用于 terminal、execute_code 以及各类文件处理工具所使用的执行容器。由于容器会在 Hermes 的不同进程间保持存在,因此在已有已联网的容器时将此值改为 false 会删除该容器并启动一个全新的隔离容器(系统会记录警告信息);运行在旧容器中的后台进程也将丢失。相较于通过 docker_extra_args 传递 --network=none,建议使用此配置键。

要求: 需安装并运行 Docker Desktop 或 Docker Engine。Hermes 会扫描 $PATH 及 macOS 上常见的 Docker 安装路径(如 /usr/local/bin/docker、/opt/homebrew/bin/docker、Docker Desktop 应用程序包)。Podman 也支持直接使用:若系统中同时安装了两者,可设置 HERMES_DOCKER_BINARY=podman(或其完整路径)来强制使用 Podman。

容器生命周期

每个由 Hermes 管理的容器都会被添加三个标签,以便后续进程及孤儿回收机制能够识别它:

  • hermes-agent=1 — 标识该容器为 Hermes 管理的容器
  • hermes-task-id=<经过处理的 task_id> — 用于标识每个任务对应的重复使用检测机制
  • hermes-profile=<经过处理的 profile 名称> — 默认情况下,该标签用于将重复使用及回收功能限制在当前活跃的 Hermes profile 范围内;若设置了 docker_shared_container_key,则将使用其经过处理后的值。 在启动时,Hermes会执行 docker ps --filter label=hermes-task-id=<id> --filter label=hermes-profile=<identity> 命令,一旦找到对应的容器便会附加到该容器中。除非通过 docker_shared_container_key 明确指定将多个受信任的配置文件合并为同一值,否则该 identity 即为当前活跃的配置文件。如果该容器已“退出”(例如在 Docker 守护进程重启后),系统会重新启动它并再次使用——文件系统状态及已安装的软件包会保留,但容器内的后台进程则不会。

当某个 Hermes 进程退出时——无论是通过 /quit 命令、关闭 TUI 会话、网关关闭,还是收到 SIGKILL 信号——在默认模式下,该容器并不会被清理,而是会继续运行。下一个 Hermes 进程会通过标签探测在几毫秒内便附加到该容器上。这正是“跨会话共享同一个长生命周期容器”这一设计要求所规定的行为:只有这样,后台进程(如 npm 监听器、开发服务器、长时间运行的 pytest 等)才能在多个会话之间持续运行。

只有在以下情况下,该容器才会被终止(即先停止,然后再执行 docker rm -f 命令删除):

触发条件触发时机
docker_persist_across_processes: false启用显式的进程级隔离。每次调用 cleanup() 都会执行 stop + rm -f 操作,行为与 #20561 修复版本之前一致。
空闲回收机制(lifetime_seconds,默认 300 秒)仅当环境变量设置为 persist_across_processes=false 时生效。处于持久化模式的环境变量将不会被处理,容器可顺利度过空闲回收周期。
下次启动时的孤儿容器回收会清理当前配置文件范围内、已标记为 hermes 且退出时间超过 2 × lifetime_seconds(默认 600 秒,即 10 分钟)的容器。正在运行的容器绝不会被影响,这体现了进程间的安全性。如需禁用此功能,可设置 docker_orphan_reaper: false。
用户直接操作执行 docker rm -f、docker system prune 或重启 Docker Desktop 均可触发回收。由于我们未设置 --restart=always,因此主机重启后容器状态仍为“已退出”(其写时复制层会保留并在下次启动时被重新使用,但后台进程已消失)。

值得注意的边缘情况:

  • 当容器内的 PID 1 因内存溢出而被强制终止时,该容器的状态会变为 Exited。下次重新启动时会通过 docker start 命令来启动它;此时文件系统状态得以保留,但后台进程则会被清除。
  • 配置文件切换功能能够实现容器间的隔离——标记为 hermes-profile=work 的容器,在运行于 hermes-profile=research 环境下的 Hermes 进程中是不可见的。孤儿进程清理机制同样受配置文件限制,因此跨配置文件的容器不会被意外清理,但除非在原有配置文件下重新启动 Hermes,否则它们也不会被自动处理。
  • 显式跨配置文件共享——对于需要在同一可信工作空间内协同工作的配置文件,可在 terminal: 配置项下设置相同的非空 docker_shared_container_key。此操作仅用于统一容器的标识标签,任务、数据输出及网络兼容性检查依然会正常执行。未设置该键的配置文件之间仍保持隔离状态。容器标识标签是由该键加上简短的哈希后缀生成的,因此外观相似的键(如 team/workspace 与 team_workspace)不会导致容器合并。重要提示:共享容器仅会由最先启动它的配置文件创建一次——该配置文件的 docker_image、卷设置、共享内存大小以及其他不可变的 Docker 配置将占据主导地位,后续启动的配置文件将直接基于这些设置进行连接;它们配置中的不同设置在容器被删除并重新创建之前均会被忽略。选择共享同一键的配置文件时,各方需就镜像及挂载项达成一致。 通过 delegate_task(tasks=[...]) 生成的并行子代理会共享同一个容器——同时进行的 cd 操作、环境变量修改以及对同一路径的写入都可能引发冲突。如果某个子代理需要独立的沙箱环境,就必须通过 register_task_env_overrides() 注册针对该任务的镜像覆盖配置;而强化学习及基准测试环境(如 TerminalBench2、HermesSweEnv 等)则会为其各自的任务专用 Docker 镜像自动完成此项操作。

安全加固措施:

  • 使用 --cap-drop ALL,仅保留 DAC_OVERRIDE、CHOWN、FOWNER 权限
  • 设置 --security-opt no-new-privileges
  • 限制进程数量为 256 个
  • 为 /tmp(512MB)、/var/tmp(256MB)、/run(64MB)设置大小限制的临时文件系统

凭证转发机制: docker_forward_env 中列出的环境变量会首先从用户的 shell 环境中获取,其次再从 ~/.hermes/.env 文件中读取。技能模块也可以声明 required_environment_variables,这些变量也会被自动合并。

环境变量覆盖规则

terminal: 下的每个键值对都对应一个形式为 TERMINAL_<KEY_UPPERCASE> 的环境变量覆盖项。对于 Docker 后端而言,其中最有用的一些包括:

Env varMaps toNotes
TERMINAL_DOCKER_IMAGEdocker_imageBase image
TERMINAL_DOCKER_FORWARD_ENVdocker_forward_envJSON array: '["GITHUB_TOKEN","OPENAI_API_KEY"]'
TERMINAL_DOCKER_ENVdocker_envJSON dict: '{"DEBUG":"1"}'
TERMINAL_DOCKER_VOLUMESdocker_volumesJSON array of "host:container[:ro]" strings
TERMINAL_DOCKER_EXTRA_ARGSdocker_extra_argsJSON array
TERMINAL_DOCKER_MOUNT_CWD_TO_WORKSPACEdocker_mount_cwd_to_workspacetrue / false
TERMINAL_DOCKER_RUN_AS_HOST_USERdocker_run_as_host_usertrue / false
TERMINAL_DOCKER_SNAP_COMPATdocker_snap_compattrue / false — default false
TERMINAL_DOCKER_NETWORKdocker_networktrue / false — default true; false = --network=none
TERMINAL_DOCKER_PERSIST_ACROSS_PROCESSESdocker_persist_across_processestrue / false — default true
TERMINAL_DOCKER_SHARED_CONTAINER_KEYdocker_shared_container_keyExplicit shared identity for trusted profiles; empty by default
TERMINAL_DOCKER_ORPHAN_REAPERdocker_orphan_reapertrue / false — default true
TERMINAL_CONTAINER_CPUcontainer_cpuCPU cores
TERMINAL_CONTAINER_MEMORYcontainer_memoryMB
TERMINAL_CONTAINER_DISKcontainer_diskMB
TERMINAL_CONTAINER_PERSISTENTcontainer_persistenttrue / false — controls the bind-mount workspace dirs, distinct from docker_persist_across_processes
TERMINAL_LIFETIME_SECONDSlifetime_secondsIdle reaper window
TERMINAL_TEMP_DIRtemp_dirSession temp root (local backend)
TERMINAL_TIMEOUTtimeoutPer-command timeout
HERMES_DOCKER_BINARYnoneForce a specific docker/podman binary path

SSH 后端

通过 SSH 在远程服务器上执行命令。该后端采用 ControlMaster 技术实现连接复用,并设置 5 分钟的空闲保持活跃机制。默认情况下已启用持久化Shell,因此命令执行间的工作目录及环境变量状态都会被保留。

terminal:
  backend: ssh
  persistent_shell: true           # Keep a long-lived bash session (default: true)

必需的环境变量:

TERMINAL_SSH_HOST=my-server.example.com
TERMINAL_SSH_USER=ubuntu

可选参数:

变量默认值说明
TERMINAL_SSH_PORT22SSH 端口
TERMINAL_SSH_KEY(系统默认值)SSH 私钥路径
TERMINAL_SSH_PERSISTENTtrue启用持久化 shell

工作原理: 在初始化时会以 BatchMode=yes 和 StrictHostKeyChecking=accept-new 的参数建立连接。持久化 shell 会在远程主机上保持一个 bash -l 进程运行,通过临时文件进行通信。需要 stdin_data 或 sudo 权限的命令会自动切换到一次性执行模式。

技能/配置环境变量传递: 技能在 required_environment_variables 中声明的变量,或用户在 terminal.env_passthrough 中列出的变量,会通过 OpenSSH 的 SendEnv 功能进行传递——变量名会出现在 ssh 命令行中,而其值则通过客户端环境传递,绝不会出现在远程命令文本中。远程端的 sshd 必须能够接收这些变量;可在服务器上的 /etc/ssh/sshd_config 文件中进行相应配置,然后重新加载 sshd 服务:

AcceptEnv NEXTCLOUD_URL NEXTCLOUD_*      # or the names your skills need

若未指定对应的 AcceptEnv,服务器会静默忽略这些变量,从而导致远程 Shell 认为它们未被设置。即便在配置中列出了 Hermes 提供商的凭证(如 OPENAI_API_KEY 等),这些凭证也绝不会被转发。详情请参阅环境变量传递机制。

在 Modal 云沙箱中执行命令。每个任务都会获得一台独立的虚拟机,其 CPU、内存和磁盘资源均可自定义。此外,文件系统支持在不同会话之间进行快照创建与恢复。

terminal:
  backend: modal
  container_cpu: 1                 # CPU cores
  container_memory: 5120           # MB (5GB)
  container_disk: 51200            # MB (50GB)
  container_persistent: true       # Snapshot/restore filesystem

必需项: 要么提供 MODAL_TOKEN_ID + MODAL_TOKEN_SECRET 环境变量,要么提供 ~/.modal.toml 配置文件。

状态持久化: 开启该功能后,沙箱文件系统会在清理时生成快照,并在下次会话时恢复。这些快照会被记录在 ~/.hermes/modal_snapshots.json 文件中。此功能可保留文件系统状态,但无法保存正在运行的进程、PID 空间或后台任务。

凭证文件: 会自动从 ~/.hermes/ 目录加载凭证文件(如 OAuth 令牌等),并在执行每个命令之前进行同步。

Daytona 后端

在 Daytona 托管的工作空间中运行命令。支持暂停/继续操作以实现状态持久化。

terminal:
  backend: daytona
  container_cpu: 1                 # CPU cores
  container_memory: 5120           # MB → converted to GiB
  container_disk: 10240            # MB → converted to GiB (max 10 GiB)
  container_persistent: true       # Stop/resume instead of delete

必需项: DAYTONA_API_KEY 环境变量。

持久化设置: 开启该功能后,沙箱在清理时会暂停运行(而非被删除),并在下次会话时自动恢复。沙箱的命名规则为 hermes-{task_id}。

磁盘容量限制: Daytona 设置的最大磁盘使用量为 10 GiB。超过此限制的请求将会被限制并同时发出警告。

Vercel 沙箱后端

命令将在 Vercel 沙箱 这一云原生微虚拟机中执行。Hermes 会使用常规的终端和文件操作工具,不提供任何针对 Vercel 的专用模型交互工具。

terminal:
  backend: vercel_sandbox
  vercel_runtime: node24          # node24 | node22 | python3.13
  cwd: /vercel/sandbox            # default workspace root
  container_persistent: true      # Snapshot/restore filesystem
  container_disk: 51200           # Shared default only; custom disk is unsupported

必需的安装步骤: 需要额外安装可选的 SDK。

pip install 'hermes-agent[vercel]'

必需的认证方式: 需要同时配置 VERCEL_TOKEN、VERCEL_PROJECT_ID 和 VERCEL_TEAM_ID 三种参数以实现访问令牌认证。这是在 Render、Railway、Docker 以及类似平台上进行应用部署及运行常规长时间运行的 Hermes 进程所支持的配置方式。

对于偶尔的本地开发场景,Hermes 也支持使用有效期较短的 Vercel OIDC 令牌:

VERCEL_OIDC_TOKEN="$(vc project token <project-name>)" hermes chat

若从关联的 Vercel 项目目录中调用,可省略项目名称:

VERCEL_OIDC_TOKEN="$(vc project token)" hermes chat

OIDC令牌的有效期较短,不应作为文档中规定的部署方式。

运行时环境: terminal.vercel_runtime支持node24、node22和python3.13。若未进行设置,Hermes将默认使用node24。

数据持久性: 当设置container_persistent: true时,Hermes会在清理过程中对沙箱文件系统创建快照,并从该快照中恢复同一任务后续使用的沙箱环境。快照内容可包含通过Hermes同步的凭证、技能以及被复制到沙箱中的缓存文件。此功能仅能保留文件系统状态,无法保留活跃沙箱的标识、进程ID空间、shell状态或正在运行的后台进程。

后台命令: terminal(background=true)会使用Hermes通用的非本地后台进程处理机制。在沙箱处于运行状态时,您可以通过常规进程管理工具来启动、轮询、等待、查看日志以及终止进程。不过,Hermes并不提供在清理或重启后恢复Vercel原生分离进程的功能。

磁盘容量: 目前Vercel Sandbox不支持Hermes的container_disk配置选项。请将container_disk保持未设置状态或使用默认值51200;非默认值会导致诊断失败及后端创建失败,而不会被静默忽略。

Singularity/Apptainer后端

在Singularity/Apptainer容器中执行命令。该后端专为那些无法使用Docker的高性能计算集群及共享机器设计。

terminal:
  backend: singularity
  singularity_image: "docker://nikolaik/python-nodejs:python3.11-nodejs20"
  container_cpu: 1                 # CPU cores
  container_memory: 5120           # MB
  container_persistent: true       # Writable overlay persists across sessions

系统要求: $PATH 环境变量中需包含 apptainer 或 singularity 可执行文件。

镜像处理: Docker 链接(docker://...)会自动转换为 SIF 文件并缓存起来;现有的 .sif 文件则可直接使用。

临时目录路径: 依次按照以下顺序确定:TERMINAL_SCRATCH_DIR → TERMINAL_SANDBOX_DIR/singularity → /scratch/$USER/hermes-agent(高性能计算领域的常用路径)→ ~/.hermes/sandboxes/singularity。

隔离机制: 通过 --containall --no-home 参数实现完整的命名空间隔离,同时不会挂载主机上的用户主目录。

常见的终端后端问题

如果终端命令立即失败,或系统提示相关终端工具已禁用,则可能遇到以下问题:

  • 本地模式(Local) — 无特殊要求,是入门时最安全的默认选择。
  • Docker 模式 — 运行 docker version 命令以确认 Docker 正常运行。若命令失败,请修复 Docker 环境,或执行 hermes config set terminal.backend local 将后端切换回本地模式。
  • SSH 模式 — 必须同时设置 TERMINAL_SSH_HOST 和 TERMINAL_SSH_USER 参数。若缺少任意一个参数,Hermes 会输出明确的错误提示。
  • Modal 模式 — 需要 MODAL_TOKEN_ID 环境变量或 ~/.modal.toml 配置文件。可运行 hermes doctor 命令进行检查。
  • Daytona 模式 — 需要 DAYTONA_API_KEY。Daytona SDK 会负责处理服务器地址的配置。
  • Singularity 模式 — $PATH 环境变量中需包含 apptainer 或 singularity 可执行文件,此模式在高性能计算集群中较为常见。

如有疑问,建议先将 terminal.backend 设置回 local 模式,确认命令在该模式下能够正常运行。

终止时实现远程端与主机端的状态同步

对于 SSH、Modal 和 Daytona 后端,Hermes 会在会话期间将用户位于 ~/.hermes/ 目录下的状态数据(包括凭证文件、技能模块及缓存内容)推送到远程沙箱中;而在会话结束时,则会将这些发生变动的状态文件同步回其原始所在位置。那些通过内容哈希校验后发现与最初推送的版本存在差异的文件会被重新应用到对应位置;而在已同步目录下新生成的远程文件(例如代理在远程端创建的技能模块),则会映射到相应的主机路径上。仅用于上传的凭证文件则绝不会被覆盖在主机上。

  • 同步回放功能会最多尝试 3 次,并采用退避策略;同时,它不会尝试提取大小超过 2 GiB 的远程归档文件。
  • Docker 和 Singularity 使用绑定挂载方式(即实时查看主机文件系统),因此无需此功能。
  • 该机制仅适用于 Hermes 状态数据(~/.hermes/ 目录下的内容),不涵盖沙箱内的任意其他工作目录文件——建议用户在销毁沙箱之前,通过 scp 或 modal volume put 等方式手动将重要文件复制出来。

Docker 卷挂载

在使用 Docker 后端时,docker_volumes 功能允许用户将主机目录与容器共享。每个配置项均采用标准的 Docker -v 语法:host_path:container_path[:options]。

terminal:
  backend: docker
  docker_volumes:
    - "/home/user/projects:/workspace/projects"   # Read-write (default)
    - "/home/user/datasets:/data:ro"              # Read-only
    - "/home/user/.hermes/cache/documents:/output" # Gateway-visible exports

此功能适用于以下场景:

  • 向智能体提供文件(数据集、配置文件、参考代码);
  • 从智能体接收文件(生成的代码、报告、导出内容);
  • 共享工作空间,使您与智能体能够访问相同的文件。

如果您使用消息网关,并希望让智能体通过 MEDIA:/... 发送生成的文件,建议使用主机可访问的专用导出挂载路径,例如 /home/user/.hermes/cache/documents:/output。

  • 在 Docker 容器中,将文件写入 /output/...;
  • 在 MEDIA: 中指定主机路径,例如:MEDIA:/home/user/.hermes/cache/documents/report.txt;
  • 除非网关进程在主机上确实存在该路径,否则请勿使用 /workspace/... 或 /output/...。
:::warning
YAML 文件中重复的键会自动覆盖之前的值。如果您已存在 docker_volumes: 块,请将新的挂载项合并到同一列表中,而非在文件后添加另一个 docker_volumes: 键。
::

该设置也可通过环境变量配置:TERMINAL_DOCKER_VOLUMES='["/host:/container"]'(JSON 数组格式)。

Docker 凭证转发

默认情况下,Docker 终端会话不会继承主机的任意凭证。如果您需要在容器内使用特定令牌,请将其添加到 terminal.docker_forward_env 中。

terminal:
  backend: docker
  docker_forward_env:
    - "GITHUB_TOKEN"
    - "NPM_TOKEN"
Hermes 首先会解析您当前 shell 中列出的各个变量,如果这些变量是通过 hermes config set 保存的,则会回退到 ~/.hermes/.env 文件中查找。

::warning docker_forward_env 中列出的任何内容都会被容器内运行的命令所访问。请仅转发那些您愿意在终端会话中公开的凭据。

::

以主机用户身份运行容器

默认情况下,Docker 容器是以 root(UID 0)身份运行的。在 /workspace 或其他绑定挂载目录中创建的文件,其所有权最终属于主机上的 root 用户。因此,在会话结束后,您需要先使用 sudo chown 命令更改文件所有权,才能通过主机上的编辑器对其进行修改。terminal.docker_run_as_host_user 参数可解决这一问题:

terminal:
  backend: docker
  docker_run_as_host_user: true   # default: false

启用该功能后,Hermes 会在 docker run 命令中追加 --user $(id -u):$(id -g) 参数,这样写入绑定挂载目录(如 /workspace、/root 以及 docker_volumes 中的任何目录)的文件将归属于宿主机的用户而非 root 用户。不过这样做会带来一个弊端:容器将无法执行 apt install 操作,也无法向 root 所拥有的路径(如 /root/.npm)写入数据。如果需要同时满足这两点,可使用 HOME 目录归属于非 root 用户的基础镜像(或是在构建镜像时添加所需的工具)。

若希望保持向后兼容性,可将其保持为默认值 false。只有当您的工作流程主要是“编辑宿主机上的挂载文件”,并且已经厌倦了频繁使用 sudo chown -R 命令时,才建议启用此功能。

Snap 包装的 Docker(AppArmor)

在通过 snap 方式安装 Docker 的主机上(常见于 Ubuntu 云镜像,例如 Azure 虚拟机),snap 的 AppArmor 约束机制会拒绝 sandbox 的两项强化配置选项,从而导致容器在启动时即崩溃:

exec /sbin/docker-init: operation not permitted     # --init
exec /usr/bin/sleep: operation not permitted        # --security-opt no-new-privileges

这是 Snapd 的固有限制(LP#1908448),Hermes 无法对此进行干预。您可以选择从 Docker 的 apt 仓库安装 Docker 而非使用 Snap 版本(这是最佳方案,因为所有安全加固措施依然有效),或者选择启用相应功能:

terminal:
  docker_snap_compat: true   # drops --init and no-new-privileges; cap-drop, tmpfs, PID limits stay

启用该功能后,沙箱内的僵尸进程将不会被 init 进程回收,且容器内的 setuid 二进制文件也能重新获取权限;容器启动时会记录一条警告信息。

可选:将启动目录挂载到 /workspace

Docker 沙箱默认处于隔离状态。除非您明确选择,否则 Hermes 不会将主机上的当前工作目录传递给容器。

可在 config.yaml 中开启该功能:

terminal:
  backend: docker
  docker_mount_cwd_to_workspace: true

启用该功能时:

  • 若从 ~/projects/my-app 启动 Hermes,该主机目录将会被绑定挂载到 /workspace;
  • Docker 后端将在 /workspace 中启动;
  • 文件操作工具与终端命令都能访问同一个已挂载的项目目录。

若禁用该功能,则 /workspace 仍由沙箱拥有,除非您通过 docker_volumes 显式挂载其他目录。

相应的安全权衡为:

  • 设置为 false 可保持沙箱隔离边界;
  • 设置为 true 则允许沙箱直接访问用于启动 Hermes 的目录。

仅当您确实希望容器能够操作宿主机上的实时文件时,才应启用此功能。

持久化 Shell

默认情况下,每个终端命令都在独立的子进程中运行——工作目录、环境变量及 shell 变量会在不同命令之间重置。当启用持久化 Shell后,一个长期运行的 bash 进程将在多次 execute() 调用之间保持存活,从而使命令间的状态得以保留。

这一功能对SSH 后端尤为有用,因为它还能消除每次命令执行时的连接开销。SSH 后端默认启用持久化 Shell,而本地后端则默认禁用该功能。

terminal:
  persistent_shell: true   # default — enables persistent shell for SSH

如需禁用:

hermes config set terminal.persistent_shell false

在多个命令之间保持不变的项:

  • 工作目录(执行 cd /tmp 后,该设置会在后续命令中依然有效)
  • 导出的环境变量(如 export FOO=bar)
  • Shell 变量(如 MY_VAR=hello)

优先级规则:

级别变量名默认值
配置文件设置terminal.persistent_shelltrue
SSH 覆盖设置TERMINAL_SSH_PERSISTENT以配置文件值为准
本地覆盖设置TERMINAL_LOCAL_PERSISTENTfalse

后端特定的环境变量具有最高优先级。如果您希望在本地后端也实现持久化 Shell 环境,可进行相应设置:

export TERMINAL_LOCAL_PERSISTENT=true
:::note 由于持久化 shell 的标准输入已被 IPC 协议占用,因此那些需要 stdin_data 或 sudo 权限的命令会自动切换到单次执行模式。
::

如需了解各后端的详细信息,请参阅 代码执行功能 以及 README 中的终端相关部分。

技能设置

技能可通过其 SKILL.md 前置标记定义自身的配置选项。这些为非机密值(如路径、偏好设置、领域配置),会存储在 config.yaml 文件的 skills.config 命名空间下。

skills:
  config:
    myplugin:
      path: ~/myplugin-data   # Example — each skill defines its own keys

技能设置的工作原理:

  • hermes config migrate 会扫描所有已启用的技能,找出未配置的设置,并提示您进行设置。
  • hermes config show 会在“技能设置”选项下显示所有技能设置及其所属的技能名称。
  • 当某个技能加载时,其解析后的配置值会自动注入到该技能的上下文中。

手动设置值:

hermes config set skills.config.myplugin.path ~/myplugin-data

如需了解在自定义技能中声明配置设置的详细信息,请参阅创建技能 — 配置设置。

对智能体创建的技能内容的写入进行防护

当智能体使用 skill_manage 功能来创建、编辑、修改或删除某个技能时,Hermes 可以选择性地扫描新生成或更新的内容,以检测其中是否存在危险的关键词模式(如凭证窃取、明显的提示词注入、数据外传指令等)。该扫描功能默认处于关闭状态——因为那些确实需要访问 ~/.ssh/ 目录或包含 $OPENAI_API_KEY 的真实智能体工作流,常常会频繁触发误报。如果您希望在智能体写入技能内容之前收到警告,请将其重新开启:

skills:
  guard_agent_created: true   # default: false

当该功能处于开启状态时,所有被标记为 skill_manage 类型的写入操作都会以包含扫描器分析理由的审批提示形式呈现。获批的写入操作将会成功执行,而被拒绝的写入操作则会向智能体返回说明性错误信息。

技能写入操作的审批机制

独立于上述内容扫描器之外,skills.write_approval 会要求对所有智能体技能相关的写入操作(创建、编辑、修补、删除及相关文件)进行手动审批——其审批/拒绝机制与处理危险命令的机制完全相同。

skills:
  write_approval: false   # false = write freely (default) | true = stage every write for review

开启该功能后,技能写入操作将被暂存于 ~/.hermes/pending/skills/ 目录中,用户可通过 CLI 或任何消息平台,使用 /skills pending、/skills diff <id>、/skills approve <id>、/skills reject <id> 等命令对它们进行审核。运行时也可通过 /skills approval on|off 来切换此功能状态。内存相关的操作同样遵循相同的审批机制(即下文的 memory.write_approval)。完整操作指南请参阅:限制技能写入操作。

内存配置

memory:
  memory_enabled: true
  user_profile_enabled: true
  memory_char_limit: 2200   # ~800 tokens
  user_char_limit: 1375     # ~500 tokens
  write_approval: false     # true = require approval before any memory write

当设置 memory.write_approval: true 时,所有内存写入操作在生效前都需要经过您的批准:交互式 CLI 会直接在提示语中显示相关操作;而消息交流会话及后台自我优化阶段则会将写入内容暂存为 /memory pending,随后提交至 /memory approve <id> 或 /memory reject <id> 进行审批。您可以通过 /memory approval on|off 在运行时切换此功能。详情请参阅控制内存写入。

上下文文件截断设置

该选项用于控制 Hermes 在应用开头/结尾截断功能之前,从每个自动上下文文件中加载的内容量。此设置适用于注入到系统提示语中的文件,如 SOUL.md、.hermes.md、AGENTS.md、CLAUDE.md 以及 .cursorrules。它不会影响 read_file 工具。

context_file_max_chars: null  # default — dynamic cap scaled to the model's context window (floor 20K, ceiling 500K chars)

设置一个正整数,即可固定上限而非采用动态调整机制:

context_file_max_chars: 25000

每次读取的上下文文件也受到 context_file_read_timeout(单位:秒,默认值为 5.0)的限制。对于那些读取速度较慢的文件——通常是指存储在 iCloud Drive、OneDrive 或 NFS 等基于网络的文件系统中的文件——系统会发出警告并跳过该文件的读取,以确保系统的其余提示内容仍能正常加载。

context_file_read_timeout: 5.0

文件读取安全性

用于控制单次 read_file 调用可返回的内容量。超出限制的读取请求将会被拒绝,并给出错误提示,建议智能体使用 offset 和 limit 参数来指定更小的读取范围。这样一来,就能避免一次性读取压缩后的 JS 包或大型数据文件导致上下文窗口被过度占用。

file_read_max_chars: 100000  # default — ~25-35K tokens

如果您使用的是上下文窗口较大的模型且经常需要读取大型文件,请将其值调高。而对于上下文窗口较小的模型,为提升读取效率,建议将此值调低。

# Large context model (200K+)
file_read_max_chars: 200000

# Small local model (16K context)
file_read_max_chars: 30000

该智能体还会自动对文件读取操作进行去重处理——如果同一个文件区域被读取了两次且文件内容并未发生变化,它就会返回一个轻量级的占位符,而无需重新发送实际内容。当上下文被压缩后,这一机制会重置,从而使智能体能够在文件内容被总结后再次读取这些文件。

工具输出截断限制

有三个相关的上限规则控制着工具在Hermes对其内容进行截断之前可以返回的原始输出量:

tool_output:
  max_bytes: 50000        # terminal output cap (chars)
  max_lines: 2000         # read_file pagination cap
  max_line_length: 2000   # per-line cap in read_file's line-numbered view
  • max_bytes — 当某个 terminal 命令生成的 stdout 和 stderr 合计字符数超过此值时,Hermes 会保留前 40% 和后 60% 的内容,并在两者之间插入 [OUTPUT TRUNCATED] 的提示。默认值为 50000(使用常见分词器时约相当于 12,000–15,000 个标记)。
  • max_lines — 单次 read_file 调用中 limit 参数的上限值。超过此限制的请求会被限制,以防止单次读取操作占用过多的上下文窗口空间。默认值为 2000。
  • max_line_length — 当 read_file 以带行号的形式输出内容时,每行内容的最大长度限制。超过此长度的行会被截断为指定字符数后加上 ... [truncated] 的提示。默认值为 2000。

对于那些具备较大上下文窗口、且每次调用能够处理更多原始输出的数据模型,可适当提高这些限制值;而对于上下文窗口较小的模型,则应降低这些限制,以确保工具输出的结果更为精简。

# Large context model (200K+)
tool_output:
  max_bytes: 150000
  max_lines: 5000

# Small local model (16K context)
tool_output:
  max_bytes: 20000
  max_lines: 500

工具结果溢出预算

除内容截断外,过大的工具输出结果会直接写入磁盘而非被截断:完整输出会保存在$/HERMES_HOME/cache/spillover/目录下,而上下文中的相关内容则会被预览信息及该文件的路径所替代(可通过read_file函数结合offset/limit参数读取,或使用execute_code函数进行处理)。默认的每条结果溢出阈值为100,000个字符,对于上下文容量较小的模型,该阈值会自动降低。

MCP工具的结果(名称为mcp_*的工具)的默认溢出阈值更为严格,为50,000个字符:由于MCP服务器通常会返回大量未分页的大体积数据(如工具发现目录、批量执行结果),这些数据原本可能处于通用阈值范围内,从而导致后续每次对话时上下文容量持续增加。不过所有数据都不会丢失——完整结果仍会保存在磁盘中。如需更改该阈值,可通过以下方式操作:

tool_budget:
  mcp_result_size_chars: 50000   # per-result spillover threshold for mcp_* tools

MCP 的阈值始终受到(可能根据上下文动态调整的)通用单条结果阈值限制,因此即便提高该阈值,也不得超出当前模型上下文窗口允许的范围。

Hermes 还会标记提供方端的截断现象:当 MCP 或 Web 工具的结果中包含自身的截断标识(如“…还有 N 项”、“"has_more": true”以及“已保存至沙箱”之类的提示)时,系统会在结果后附加一行说明,警告用户当前显示的数据并不完整,在将任何列举视为完整之前需进行分页查询或进一步获取数据。

全局禁用工具集

若需在一处同时禁用 CLI 及所有网关平台上的特定工具集,只需在 agent.disabled_toolsets 下列出这些工具集的名称即可:

agent:
  disabled_toolsets:
    - memory       # hide memory tools + MEMORY_GUIDANCE injection
    - web          # no web_search / web_extract anywhere

该配置会在各平台工具设置(由 hermes tools 生成的 platform_toolsets)之后生效,因此此处列出的工具集将会被移除——即便该平台的已保存配置中仍保留有相关设置。当您希望通过一个简单操作即可“在所有地方关闭X功能”,而无需在 hermes tools 的界面中编辑15行以上的平台配置时,可使用此方法。

若将列表留空或省略该键,则不会产生任何影响。

Git工作树隔离

启用Git工作树隔离功能,即可在同一个仓库上并行运行多个Agent。

worktree: true    # Always create a worktree (same as hermes -w)
# worktree: false # Default — only when -w flag is passed

启用该功能后,每个 CLI 会话都将在 .worktrees/ 目录下创建一个包含独立分支的新工作树。各 Agent 可以独立编辑文件、执行提交、推送操作以及创建 Pull Request,而互不干扰。退出时会自动清除干净的工作树,而状态未同步的工作树则会保留以便后续手动恢复。

默认情况下,新工作树会从最新拉取的远程分支尖端(即当前分支的上游分支,若不存在则使用远程仓库的默认分支)创建分支,这样就能确保工作树与项目状态保持一致,而非基于本地克隆中可能已过时的 HEAD 分支。这样一来,Pull Request 的差异对比就能准确反映实际变更内容,而不会受到本地克隆滞后状态的干扰。如需以本地 HEAD 分支作为基础,可设置 worktree_sync: false —— 这在离线环境下或需要以克隆版本的精确当前状态为基准时非常有用。如果无法连接到远程仓库,系统会自动回退到使用本地 HEAD 分支。

worktree_sync: true    # Default — branch from the fetched remote tip
# worktree_sync: false # Branch from local HEAD (offline / pinned base)

您还可以在仓库根目录中使用 .worktreeinclude 文件,列出需要复制到工作树中的被 Git 忽略的文件。

# .worktreeinclude
.env
.venv/
node_modules/

上下文压缩功能

Hermes 会自动对较长的对话内容进行压缩,以确保其长度在模型上下文窗口允许范围内。该压缩功能通过独立的 LLM 调用实现——您可以将它指向任意提供商或接口端点。

所有压缩相关设置均保存在 config.yaml 文件中(无需使用环境变量)。

完整参考文档

compression:
  enabled: true                                     # Toggle compression on/off
  progress_notices: false                           # Opt-in: deliver routine compression progress notices to chat platforms — see below
  threshold: 0.50                                   # Compress at this % of context limit
  threshold_tokens: null                            # Absolute token cap (optional) — takes lower of ratio vs absolute
  target_ratio: 0.20                                # Fraction of threshold to preserve as recent tail
  tail_mode: lean                                   # Tail retention: "lean" (default — clamped 2.5% tail, 10K-25K, with a detailed session log + anchor index + session_search recovery pointers in the summary, all from ONE auxiliary summarizer call; ~3x fewer retained tokens after compaction) or "legacy" (0.20×threshold verbatim tail)
  protect_last_n: 20                                # Min recent messages to keep uncompressed
  protect_first_n: 3                                # Non-system head messages pinned across compactions (0 = pin nothing)
  in_place: true                                    # Compact on the same session id (no rotation) — see below
  idle_compact_after_seconds: 0                     # Opt-in idle compaction (0 = disabled) — see below
  hygiene_hard_message_limit: 5000                  # Gateway safety valve — see below
  hygiene_timeout_seconds: 30                       # Max seconds of NO summary-model output before hygiene compression is cut off
  hygiene_total_ceiling_seconds: 600                # Absolute cap on the hygiene wait even while tokens are still streaming
  hygiene_max_turn_hold_seconds: 10                 # Max wall-clock the incoming turn waits on hygiene compression before proceeding uncompressed — see below
  hygiene_failure_cooldown_seconds: 300             # First rung of the per-session hygiene-failure backoff (x1/x3/x9, capped at 1h)
  context_timeout_seconds: 120                      # Inactivity budget for in-agent compress_context (loop /compress / preflight) — see below
  context_total_ceiling_seconds: 600                # Absolute cap on the *pre-commit* in-agent compress_context wait even while tokens are still streaming (an already-started SessionDB commit is never abandoned; overruns are logged + surfaced)
  proactive_prune_tokens: 0                         # Opt-in tokens trigger for the no-LLM tool-result prune (0 = off; see below)
  proactive_prune_min_result_chars: 8000            # Prune's summarize pass only touches tool results larger than this (clamped >= 200)
  proactive_prune_min_reclaim_tokens: 4096          # Prune only commits when it reclaims at least this many tokens (0 = commit any)

# The summarization model/provider is configured under auxiliary:
auxiliary:
  compression:
    model: ""                                       # Empty = use main chat model. Override with e.g. "google/gemini-3-flash-preview" for cheaper/faster compression.
    provider: "auto"                                # Provider: "auto", "openrouter", "nous", "codex", "main", etc.
    base_url: null                                  # Custom OpenAI-compatible endpoint (overrides provider)
:::info 旧配置迁移说明
对于包含 compression.summary_model、compression.summary_provider 和 compression.summary_base_url 字段的旧配置,在首次加载时(配置版本 17 及以上)会自动迁移为 auxiliary.compression.* 格式,无需手动操作。

::
progress_notices(默认值为 false)用于控制是否在聊天平台(如 Telegram、Discord、Slack 等)上显示常规压缩进度状态。按照设计,自动压缩过程在聊天界面中不会产生任何提示——它仅在后台运行,并仅生成服务器端的日志记录。若将 progress_notices 设置为 true,即可在聊天平台上查看完整的压缩流程信息:包括“正在压缩上下文…”的启动提示、预处理/预 API 压缩触发信号、空闲状态下的压缩操作、重试进度提示(如“已压缩 30 → 12 条消息,正在重试…”)以及“上下文压缩完成”提示。该功能仅针对压缩状态生效——与压缩无关的其他操作信息(如辅助模型故障、服务提供商的速率限制或重试提示等)在任何情况下都不会显示。无论此设置如何,压缩失败提示以及手动执行的 /compress 操作反馈始终会显示。在正在运行的系统中修改此值,其效果将在处理下一条消息时体现。

hygiene_hard_message_limit 是一种仅适用于网关的预压缩安全机制。其存在旨在打破恶性循环:当会话规模过大导致 API 调用频繁中断时,网关将无法接收到令牌使用情况数据,因而基于令牌数量的阈值机制便无法触发,结果转录内容持续累积,连接中断问题也会愈发严重。该基于消息计数的阈值仅依据消息总数来判定(这一数值始终可知,不受 API 故障影响),以此强制启动压缩操作并恢复会话。默认值为 5000,远高于任何正常会话的需求,即便是那些需要处理数千条短轮次对话、且上下文长度达 100 万字符以上的大型模型,也早在达到该阈值之前就会因令牌限制而自动开始压缩。对于某些特殊平台,可适当提高该数值;若希望更积极地实施压缩,则可降低该值。在正在运行的网关上修改此值后,变更将于下一条消息开始时生效(详见下文)。

hygiene_timeout_seconds 则代表了网关在本次预压缩处理过程中的无活动时间阈值——并非一个固定的总时长上限。压缩摘要信息会以流式方式从模型端传输而来,每接收到一个令牌即视为一次进度推进:那些推理速度较慢但仍在持续生成内容的模型,会不断延长自身的处理截止时间,因此即便摘要生成速度缓慢但状态正常,也不会在生成过程中被强制中断。只有当摘要模型在指定时间内完全不产生任何输出(可能是后端故障、连接挂起或服务端无响应)时,网关才会向用户发出警告,继续接收未经过压缩的新消息,并将此次会话标记为临时故障状态,而非显示为卡住状态。

hygiene_total_ceiling_seconds(默认值为 600)用于限制总等待时间,即便在令牌仍在传输的过程中也会起到约束作用,从而防止流量极小的持续流无限期地占用处理轮次。该值至少会被限制在 hygiene_timeout_seconds 的水平。

hygiene_max_turn_hold_seconds(默认值为 10)则是网关的轮次等待预算——即进入的消息在等待卫生压缩处理时,网关允许其等待的最大时间长度,超过此时间后网关将停止等待并继续处理未压缩的转录内容。设置这一参数是因为仅依靠 hygiene_total_ceiling_seconds,可能会导致通信链路在远超聊天传输协议空闲超时的时间内保持沉默:由于持续生成摘要模型的令牌流会不断重置无活动状态计时,若没有轮次等待预算,等待时间可能会接近上限,而用户却收不到任何数据——此时 Telegram(及类似传输协议)会断开连接,导致该轮次看似被冻结。将轮次等待时间限制在此预算范围内(远低于通常约30秒的传输空闲超时),可确保消息能得到及时处理。即使达到此预算上限,压缩处理也不会丢失:工作进程会继续独立运行,且在其提交操作被水印机制保护时(使用会话数据库时的常规情况),其提交记录依然有效,因此完整的摘要会在下一个安全节点被采纳,而在等待被放弃后仍存在的轮次内容也会以原样作为并发尾部内容保留下来。对于思考/推理总结模型(如 DeepSeek、QwQ 等),这一点尤为重要——因为仅其推理阶段就可能耗尽预算限制,从而导致总结结果延迟生成一轮,而非完全无法生成。如果无法安全地设置预算限制,延迟生成的结果将会被丢弃(通过 CompressionCommitFence 机制),并且不能覆盖后续的回复。如果您希望在同一轮内完成压缩处理且您的传输系统能够承受等待时间,可提高预算限制;而对于响应速度极慢的后端,降低该限制则有助于更快恢复。

hygiene_failure_cooldown_seconds 用于控制每次卫生压缩超时或中断后,在单个会话内的冷却时间。在冷却期间,网关会跳过对同一超大会话的重复处理尝试,从而避免每条新消息都因同一个出问题的辅助后端而受阻。通过执行 /compress、/reset 操作或等待后续正常的回复,仍可恢复该会话。

该数值并非固定间隔,而是逐步递增机制中的第一级阈值:同一会话连续出现故障时,等待时间分别为该数值的 1 倍、3 倍,再到 9 倍,最终以一小时为上限。因此,当某个会话的总结模型永久损坏时,系统会停止按固定间隔无限重试,而是转为退避策略;而一旦有某次处理真正缩小了文本长度,系统又会将该数值重置为第一级。这种递增机制是针对单个会话及进程内部生效的——重启网关可将其重置为第一级,但冷却截止时间本身依然有效。

context_timeout_seconds(默认值为 120)与代理内的 compress_context 功能所使用的空闲时间限制相同——这一限制同样适用于对话循环、预处理压缩以及手动执行的 /compress 操作——从而避免因摘要模型卡住而导致会话无限期停滞。流式摘要生成会延长等待时间;只有处于静默状态的 Worker 才会被中断。超时后,Hermes 会针对 auxiliary.compression.fallback_chain 中的第一个条目重新尝试生成摘要(若该条目指定了自身超时时间,则会使用该时间值)——由于卡住的流程不会触发异常,因此辅助客户端自身的回退处理机制无法检测到这一问题。只有当此次尝试也失败,或未配置任何回退链时,Hermes 才会跳过压缩操作,保留现有消息,并向用户发出警告。将其设置为 0 即可禁用该功能。网关会话管理机制拥有独立的 hygiene_timeout_seconds 参数,不会被双重封装处理。

context_total_ceiling_seconds(默认值为 600)用于限制智能体在预提交阶段(即摘要生成/流式处理阶段)的等待时间,即便此时数据仍在传输中也是如此。该值至少会被限制在 context_timeout_seconds 的水平。具体保障机制为:摘要生成阶段的时间会受到此上限约束;而若提交阶段耗时超过该上限,系统将会记录相关日志并予以显示。 一旦工作进程进入压缩提交阶段且 SessionDB 的数据修改操作正在执行中,提交过程绝不会在半途中止——因为那样做可能会导致转录内容出现偏差——但此时系统不会再保持沉默:如果提交耗时超出上限,Hermes 会记录超时情况(首次为警告级别,多次发生则会升级为错误级别),并通过用户可见的警告渠道发送一次性提醒,随后以固定的时间间隔持续等待,直到提交完成。如果在摘要生成阶段该时间上限已到期,所有辅助通道(如 chat.completions、Codex Responses、Anthropic Messages)上的摘要生成流都会在瞬间被关闭——对于没有人在等待的连接,这种被中止的摘要生成不会产生任何费用,其会话租约也会被释放,以便后续再次尝试。

protect_first_n 用于控制每次压缩操作中会固定保留多少条非系统的对话开头消息。默认值为 3——即最初的用户与助手交流内容会在每次摘要生成过程中都被保留,从而确保原始目标始终可见。在那些持续时间较长的滚动压缩会话中,如果开头的对话内容已不再重要,可将 protect_first_n 设为 0,这样仅系统提示语、摘要以及对话结尾内容会被固定保留。无论此设置为何,系统提示语本身始终会被保留。

in_place(默认值为 true)用于控制压缩操作发生时会话标识的处理方式。当该值为 true 时,压缩操作会重写消息列表并重新生成系统提示语,但不会更改会话标识——整个对话在其生命周期内都将使用同一个稳定的标识(不存在 parent_session_id 链,也不会在会话列表中出现 name #2/#3 这类的重新编号)。这种压缩方式是非破坏性的:实时对话内容会被压缩,但压缩前的对话记录仍会以相同标识被软归档(标记为“已停用”或“已压缩”状态),依然可以通过 session_search 检索并恢复,而不会被删除。钩子函数可通过 session:compress 事件中的 in_place 字段获取该模式信息。若将 in_place 设为 false,则会恢复旧有的行为,即每次压缩后都会生成一个新的会话标识,并与旧标识保持关联。

threshold_tokens用于为压缩触发机制设置一个可选的绝对token上限。一旦启用,压缩操作将在这两个值中较小的那个达到时触发——即无论当前使用的是哪种模型,压缩都绝不会在超过用户设定的token数量时才执行。这一设计解决了因切换不同上下文窗口大小的模型(例如从100万token变为40万token)而导致触发阈值变动的问题。该上限会被限制在模型本身的上下文长度范围内,因此将其设置得高于模型支持的最大值也是安全的,系统会自动转而使用基于比例的阈值。默认值为null(已禁用,仅采用基于比例的阈值)。即便在模型切换或回退到备用模型时,此上限设置依然有效。

idle_compact_after_seconds则是一种可选的、基于时间间隔的触发机制,可作为基于大小的threshold的补充。默认值为0(已禁用)。当该值设置为大于0时,若会话在停止活动至少相应秒数后重新启动,系统会在首次回复之前先对累积的历史内容进行压缩处理。这样一来,那些长期运行的对话(例如数小时后才重新查看的Telegram聊天记录)就无需在后续每次回复时都重新读取全部过时的上下文信息。此外,当上下文长度已经达到或低于压缩目标值(threshold × target_ratio)时,该机制不会触发;同时它也遵循与其他自动压缩功能相同的故障冷却、防频繁触发以及会话级锁定保护机制。例如:若设置为idle_compact_after_seconds: 1800,则意味着在30分钟无活动后就会触发压缩。

proactive_prune_tokens功能可实现一种确定性的、无需依赖LLM的旧工具结果数据清理机制,且该操作独立于threshold参数运行。在支持大上下文窗口的模型中,由于threshold设定的压缩阈值(约等于窗口大小的50%)很少被触发,因此那些体积庞大的工具输出内容(如终端输出、文件读取结果、网页提取内容)会一直保留在历史记录中,并在后续的每一次对话轮次中被重复发送。当这些被重复发送的历史记录数量超过proactive_prune_tokens设定的阈值时(默认值为0,表示关闭该功能;可尝试设置为48000以启用),系统会自动对重复内容进行去重处理,对过大的旧结果进行摘要压缩,并截断过长的工具调用参数——同时会保留最新的protect_last_n条消息,且不会再次向模型发送数据。所有完整的输出内容仍可从会话存储中恢复。proactive_prune_min_result_chars参数(默认值为8000,最低限制为200字符)用于设定工具结果在低于此长度时将不会被清理。而proactive_prune_min_reclaim_tokens参数(默认值为4096)则确保只有在清理操作能回收至少这么多令牌时才会真正执行——因为一旦完成清理,就会重写已发送的历史记录,并使提供方的提示词缓存前缀失效,所以这一限制机制能让缓存失效的情况仅发生在特定的节点(如压缩边界处),而非每次工具调用时都会发生。该功能仅在内置的compressor引擎下生效;其他上下文引擎则只会执行无实际作用的操作。

::提示:网关的压缩设置与上下文长度的热重载功能
在最新版本中,无需重启网关、无需执行 /reset 操作,也无需切换会话,只需在正在运行的网关上修改 config.yaml 文件中的 model.context_length 或任何 compression.* 键值,更改即可在下一条消息处理时立即生效。由于缓存中的代理签名已包含这些键值,因此网关会在检测到变化时自动重新构建代理模型。而 API 密钥以及工具/技能相关配置仍需通过常规的重载方式来应用。

::

常见配置方案

默认方案(自动检测)——无需任何配置:

compression:
  enabled: true
  threshold: 0.50

该功能会使用您指定的主提供商和主模型。如果您希望在成本较低的模型上实现压缩功能,而非使用主聊天模型,可针对特定任务进行覆盖设置(例如:auxiliary.compression.provider: openrouter + model: google/gemini-2.5-flash)。

强制指定提供商(基于 OAuth 或 API 密钥):

auxiliary:
  compression:
    provider: nous
    model: gemini-3-flash

可与任意提供商配合使用:nous、openrouter、codex、anthropic、main 等。

自定义端点(自托管环境、Ollama、zai、DeepSeek 等):

auxiliary:
  compression:
    model: glm-4.7
    base_url: https://api.z.ai/api/coding/paas/v4

指向自定义的 OpenAI 兼容端点。认证时使用 OPENAI_API_KEY。

三个参数的交互方式

auxiliary.compression.providerauxiliary.compression.base_url最终效果
auto(默认值)未设置自动检测最优的可用服务提供商
nous / openrouter 等未设置强制使用指定的服务提供商,并采用其认证方式
任意值已设置直接使用自定义端点(忽略服务提供商设置)
:::warning 摘要模型上下文长度要求 摘要模型必须拥有至少与主智能体模型相当大的上下文窗口。压缩器会将对话的中间完整部分发送给摘要模型——如果该模型的上下文窗口小于主模型,摘要生成操作将会因上下文长度不足而失败。出现这种情况时,中间的对话内容会被直接丢弃而不会生成摘要,从而导致对话上下文在无声无息中丢失。如果您自行指定模型,请务必确认其上下文长度不低于主模型。
::

网关轮次租约超时时间

网关会通过已确定的会话 ID 对各轮次进行序列化处理,因此两个路由键无法同时读取和写入相同的对话记录。请独立于常规的智能体无活动超时时间来配置最大租约等待时间:

agent:
  gateway_turn_lease_timeout: 5

当该预算耗尽时,若仍有其他轮次持有会话租约,Hermes 将直接终止处理:它不会加载对话记录,也不会对等待中的消息运行模型。用户会收到拒绝通知,需重新发送消息。Hermes 不会自动重新将消息加入队列,因为在没有持久排序和幂等性保障的情况下这样做可能会导致消息被处理两次。非正数值将采用 5 秒的默认值。

会话停滞监视器

网关会运行一个仅用于通知的停滞监视器(参数为 agent.session_stall_timeout,默认值为 300 秒,值为 0 表示禁用)。当某个处于忙碌状态的会话存在待处理的入站后续消息,且该智能体的共享活动时钟已空闲至少此时长时,网关会记录一条警告信息,并向用户发送一次性通知:

⚠️ Agent session appears stalled (last activity N min ago). Try /new to reset.

语义说明:

  • 仅发送通知。 监控机制不会强制终止当前轮次——这与agent.gateway_timeout不同,后者会在长时间无响应后取消任务运行。停滞通知仅用于告知用户代理似乎出现了卡顿,由用户自行决定后续操作(如发送新指令、停止任务或继续等待)。
  • 每次停滞事件仅触发一次通知。 当待处理的请求处理完毕或代理恢复活动时,锁定状态即会被解除;因此,如果一个会话在恢复后又再次停滞,系统仍会再次发送通知。
  • 进度信息仅通过共享的活动快照提供(如工具调用记录、API处理进度、压缩心跳信号)。待处理的请求仅用于触发通知,而非作为进度计时的依据。
agent:
  session_stall_timeout: 300   # seconds; 0 disables the watchdog

关注度升级:重新连接处理机制

当平台适配器无法建立连接时(如网络中断、机器人令牌被撤销、Sidecar组件故障),网关会以指数退避策略无限次尝试重新连接——由于不会停止重试,因此临时性故障总能自动恢复,无需人工干预。但缺点是,永久性故障(如Telegram令牌被撤销、Discord缺少必要权限意图)也会表现为持续的“重试”状态,与临时故障无法区分。

有两种机制可用于识别永久性故障:

  • 终端分类机制:对于那些从异常类型上就能判断出无法自动恢复的故障——例如被拒绝或已撤销的令牌(telegram_auth_error、discord_auth_error、email_auth_error)、缺失的必要权限意图(discord_intents_required)、依赖项无法安装的Photon Sidecar组件(SIDECAR_DEPS_MISSING)或节点二进制文件缺失的情况(SIDECAR_NODE_MISSING)——系统会将其标记为致命故障,而非放入重试队列。该分类完全基于异常类型,对于含义模糊的错误则仍会持续尝试重试。
  • 需要关注度升级机制:如果某个平台在超过agent.reconnect_attention_after设定时间后(默认值为7200秒,即2小时;设为0则禁用此功能)仍持续处于重试队列中,网关会在运行状态信息(可通过hermes status命令查看)中标记该平台为“需要关注:true”,并记录下“开始重试的时间戳”,同时输出警告日志。此时重试行为不会改变——这只是一个提示信号,而非断路保护机制。一旦成功重新连接,该标记便会自动清除。
agent:
  reconnect_attention_after: 7200   # seconds; 0 disables the escalation flag

网关代理缓存

网关会为每个对话会话保留一个代理实例,这样在对话进行过程中即可复用已缓存的提示前缀,而无需在每一轮对话中都重新生成系统提示。该缓存中的代理还会存储整个对话会话的完整记录——包括所有工具的输出内容;在包含上百次工具调用的会话中,这些数据的体积可达数十兆字节。因此,在繁忙的多平台网关系统中,缓存是占用内存最多的组件。

agent:
  agent_cache:
    max_size: 128            # LRU entry cap
    idle_ttl_secs: 3600      # evict an agent idle this long
    memory_high_mb: auto     # anon-RSS budget; number, "auto", or 0/off
    max_evictions_per_pass: 16
    protect_recent: 8

max_size与idle_ttl_secs分别从数量和时间维度对缓存进行限制。由于这两种方式都无法掌握缓存中存储的字节数,因此memory_high_mb提供了第三种限制机制:一旦网关自身的匿名内存使用量超出设定阈值,它就会丢弃最近最少使用的转录内容,这些内容会在下次处理时从已存储的会话中重新加载。如果网关需要与其他服务共享内存,则应降低该值;而若希望保留所有转录内容,可提高该值(或将其设为0以关闭此功能)。

auto模式会根据网关实际运行的内存限制来确定阈值——对于容器而言是cgroup限制,对于systemd服务则是总RAM容量——因此无需再单独设置同步数值,系统便会自动遵循单元配置中的MemoryMax/MemoryHigh设置。

正在处理的会话、受protect_recent保护的最近期会话,以及转录内容尚未完全写入磁盘的会话,均不会被丢弃。发生内存淘汰时,系统会以WARNING级别记录相关日志,同时显示当前的实际内存使用量及被丢弃的会话信息。

Agent cache pressure: anon RSS 6802MB over budget 6656MB — evicting 5 LRU session(s): ...

上下文引擎

上下文引擎用于在对话接近模型token限制时,控制对话的管理方式。内置的 compressor 引擎采用有损摘要技术来实现这一功能(详见上下文压缩)。插件引擎则可以选用其他策略来替代它。

context:
  engine: "compressor"    # default — built-in lossy summarization

若要使用插件引擎(例如用于实现无损上下文管理的 LCM):

context:
  engine: "lcm"          # must match the plugin's name

插件引擎绝不会自动激活——您必须明确将 context.engine 设置为插件名称。可通过 hermes plugins → Provider Plugins → Context Engine 来查看并选择可用的引擎。

有关内存插件对应的单选系统,请参阅 Memory Providers 文档。

迭代预算

当智能体处理需要多次工具调用的复杂任务时,其迭代预算(默认为 500 次轮次)可能会被耗尽。Hermes不会在任务进行过程中发出压力警告——早期版本会在预算使用达到 70%/90% 时向模型发出警告,但这会导致模型过早放弃复杂任务,该功能已于 2026 年 4 月被移除。

取而代之的是,当预算真正耗尽(500/500)时,Hermes 会发送一条消息要求模型完成当前任务,并允许其进行一次补足调用以输出最终响应。如果这次补足调用仍无法生成文本,系统会要求智能体总结其已完成的工作。

agent:
  max_turns: none              # Iterations per conversation turn (default: none = unlimited)
                               # Set a positive integer to cap; "none"/"null"/
                               # "unlimited"/"inf"/"infinity"/"infinite"/0/-1 = no limit
  budget_warning_ratio: null   # Optional one-time checkpoint warning, e.g. 0.75
  api_max_retries: 3           # Retries per provider before fallback engages (default: 3)

默认情况下,agent.max_turns 的值是无限制的——设置轮次上限反而会引发更多问题(例如在任务执行过程中突然中断)。因此,Hermes 在启动时会一直持续对话,直到任务完成。若需设定上限,可输入一个正整数。若要明确表示“无限制”,可使用以下任意不区分大小写的写法:"none"、"null"、"unlimited"、"infinite"、"infinity"、"inf"、0 或 -1(这些值会对应 sys.maxsize 的特殊值,从而确保循环不会因达到轮次上限而终止)。

对于普通对话和委托对话,默认情况下 agent.budget_warning_ratio 是关闭状态。当该参数被设置为介于 0 和 1 之间的数值,并且同时指定了有限的 max_turns 值时,Hermes 会在达到阈值后,在最新的工具响应中添加一条模型可见的检查点提示。该提示会在每个对话轮次中重新触发,并消耗每个智能体自身的迭代预算。它只会添加到当前工具响应的末尾,而不会影响之前的轮次记录;同时也不会新增虚拟的用户/系统消息,也不会更改现有的超时处理机制。对于由调度器管理的看板工作节点,默认情况下在资源使用率达到 90% 时会收到完成检查点提示(可通过指定具体比例来调整该阈值),且此时相关工具仍可继续使用。该检查点要求提供已验证的完成状态或详细的进度说明,而非提前宣布任务成功。

agent.api_max_retries 用于控制 Hermes 在触发备用提供者切换之前,针对临时性错误(如速率限制、连接中断、5xx 错误)会重试调用提供者 API 多次。默认值为 3,即总共进行 4 次尝试。如果您已配置了备用提供者并希望更快地实现故障转移,可将该值设为 0,这样主提供者遇到首次临时错误时就会立即切换到备用提供者,而无需继续向那个不稳定的接口发送重试请求。

实时运行时间预算

除了迭代预算之外,您还可以为每次对话运行设置可选的实时运行时间预算。该功能专为那些受到严格外部时间限制的一次性任务或评估任务设计(例如每项任务最多 900 秒):如果不设置此预算,即使任务已基本完成,运行仍可能因超时而中断——要么无法生成最终答案,要么陷入某个挂起的提供者调用中无法退出。

agent:
  run_budget_seconds: null     # Optional; unset/null = feature fully off (default)

或者通过 CLI 按每次调用来执行:

hermes chat --run-budget 850 -q "..."

当预算被设定后,将会发生以下两件事:

  1. 达到80%时的结束提示。 当预算使用量达到80%时,Hermes会发送一条一次性的提示信息(以缓存安全的方式传递,并附加在最新的工具响应结果中,类似 /steer 消息),告知模型停止新的探索或验证工作,基于当前已有的状态生成最终成果。该提示在每次运行过程中最多触发一次,其机制与现有的迭代预算结束逻辑一致——不会反复发出警告。

  2. 按截止时间缩放的过期超时机制。 那些默认为数分钟级的隐式非流式过期超时设置(以及推理模型的最低限制,例如DeepSeek推理模型为600秒),其上限会被设定为 max(60, remaining_budget × 0.5)。这样一来,单个长时间挂起的提供者调用就绝不会占用掉整个运行过程的剩余时间。该上限只会让超时时间变得更短,而不会延长;同时,明确配置的 stale_timeout_seconds 值(来自提供者或模型配置,或通过 HERMES_API_CALL_STALE_TIMEOUT 参数设置)始终会优先生效,不受影响。

预算是按每次“对话轮次”来计算的(每收到一条用户消息就会重置),而如果未设置该功能,则完全处于休眠状态——不会进行任何时间读取、不会发送任何提示,也不会调整超时时间。

停止时验证(代码验证)

一旦该功能被启用,当智能体在工作区中修改了代码,但却未能提供任何新的验证结果(如测试通过、构建成功、代码检查无错误等)时,Hermes 将拒绝接受其给出的最终答案。系统会自动插入一个虚拟的后续问题,要求智能体进行验证或解释为何无法完成验证。仅涉及文档、Markdown 内容或技能相关修改的情况不会触发此机制,且该循环设有上限,因此绝不会让智能体陷入无限循环之中。

agent:
  verify_on_stop: false        # true | false | "auto" (surface-aware: on for CLI/TUI/desktop, off for messaging)
  verify_guidance: true        # Append creative-UI / clean-diff guidance to the missing-evidence nudge
  max_verify_nudges: 3         # Cap on consecutive continue nudges per turn (built-in + pre_verify hooks)
  coding_instructions: ""      # Standing project-wide coding rules appended to the coding brief

verify_on_stop 参数支持 true(始终启用)、false(关闭——默认值)或 "auto"(旧版的表面感知模式:对于交互式编码界面,如 CLI、TUI、桌面端以及通过编程方式调用的场景则保持启用;而对于 Telegram/Discord 等消息类平台,则会关闭该功能,因为在此类场景下进行验证信息提示会被视为聊天干扰)。默认情况下在所有场景下均为关闭状态:新安装的版本初始值为 false,而配置迁移也使得现有版本的该功能处于关闭状态,因此只有用户明确选择才会在其上启用该功能。若设置了 HERMES_VERIFY_ON_STOP 环境变量,其值将优先于配置文件中的设置。

用于触发此保护机制的相关证据——包括执行了哪些测试、lint 或构建命令,以及自上次之后修改了哪些文件——均存储在 ~/.hermes/verification_evidence.db 文件中。该日志文件仅在保护机制处于启用状态时才会被创建或写入内容;当 verify_on_stop 设置为 false 时,不会记录任何信息,现有的文件也可以被随时删除。

若需在同一位置实现基于用户/插件策略的自定义检查以保持智能体持续运行,请参阅 pre_verify 钩子。

持续目标(/goal)

当某个持续目标处于激活状态时,Hermes 会逐一判断每个助手回复是否满足该目标。若不满足,则会将继续处理的提示语反馈至同一会话中,并持续工作,直到目标完成、对话轮次预算耗尽,或是用户暂停/清除该目标为止。实际上,对话轮次预算才是真正的保障机制——对于判定失败的案例,系统会选择“继续处理”而非终止当前流程,从而避免因某个不可靠的判定机制而阻碍整体进度。

goals:
  max_turns: 20   # Max continuation turns before Hermes auto-pauses the goal (default: 20)

max_turns 参数用于限制目标在触发 Hermes 自动暂停并要求用户执行 /goal resume 前可进行的续传轮数。该设置旨在避免判定错误(即目标实际上已完成,但系统仍指示继续处理),同时防止模型在处理模糊或无法实现的目标时无限制地消耗资源。如需了解该功能的详细信息,请参阅 目标管理。

API 超时设置

Hermes 为流式请求提供了独立的超时机制,同时针对非流式请求设计了过期检测功能。仅当您将相关参数设置为默认值时,本地提供程序的过期检测功能才会自动生效并进行调整。

超时类型默认值本地提供程序配置/环境变量
套接字读取超时120秒自动提升至1800秒HERMES_STREAM_READ_TIMEOUT
流式数据过期检测180秒上限提升至900秒(agent.local_stream_stale_timeout)HERMES_STREAM_STALE_TIMEOUT
非流式数据过期检测90秒若保持默认值则自动禁用providers.<id>.stale_timeout_seconds 或 HERMES_API_CALL_STALE_TIMEOUT
API调用(非流式)1800秒不变providers.<id>.request_timeout_seconds / timeout_seconds 或 HERMES_API_TIMEOUT
套接字读取超时时间用于控制httpx等待来自服务提供商的下一批数据的时长。对于本地大型模型,其在预加载大量上下文后才能生成第一个token,这一过程可能需要数分钟,因此Hermes在检测到本地端点时会将此超时时间延长至30分钟。无论是否检测到端点类型,只要您显式设置了HERMES_STREAM_READ_TIMEOUT,系统将始终使用该值。

过期流检测功能会主动断开那些仅收到SSE保持连接信号却未接收到实际内容的连接。对于在预加载阶段不会发送保持连接信号的本地服务提供商,系统的默认超时时间将从180秒提升至900秒——您可以通过agent.local_stream_stale_timeout或环境变量HERMES_LOCAL_STREAM_STALE_TIMEOUT来调整这一数值。

过期非流式请求检测则用于终止那些长时间无响应的非流式调用。为避免在长时间预加载过程中出现误判,Hermes默认会在本地端点上关闭此功能。不过,如果您显式设置了providers.<id>.stale_timeout_seconds、providers.<id>.models.<model>.stale_timeout_seconds或HERMES_API_CALL_STALE_TIMEOUT,那么即便在本地端点上,系统也会优先使用您设定的超时时间。

上述各项超时限制共同构成了对所有非流式请求的管控机制。对于那些接收了请求却毫无响应的服务提供商——即连接保持开启状态,既无数据传输也无错误提示——系统会在达到对应过期超时时间后自动终止该连接并重新发起请求,而不会一直等待较长的套接字读取超时时间(或者,在无人监控的定时任务中,直到外部因素强制终止进程)。

只有在至少60秒的静默状态之后,才会出现周期性的提供者等待提示。Codex响应中的等待状态指的是静默时长,而非完整的生成时间——活跃的流事件(包括推理过程)会维持该状态的“静默”表现。一旦这些事件停止,系统会记录无流事件的时间,而不会判定没有收到任何响应;一旦事件恢复,该提示便会消失。当重新连接启动一个新的首个事件监视阶段时,等待状态也会随之变化。这种显示机制不会影响独立的实时时钟超时设置,也不会改变监视器的超时时间。同样地,聊天完成流在数据块恢复传输后也会立即消除静默警告,但不会替代本地的模型加载状态显示。

Cron作业和委托的子代理也会进行流式处理。它们会在自己的线程中直接处理请求(其他会话通过网关嵌套线程池中的中断工作线程来执行相关操作),但由于请求的stream: true属性,上述陈旧流检测规则依然适用于它们——每个令牌都会计入活跃度统计,因此即便推理模型需要数分钟才能完成计算,也不会被误认为是提供者卡住了,而那些会主动断开静默连接的边缘代理则仍能持续接收到数据字节。

禁用API流式传输

model.streaming: false 会强制整个会话中的所有请求——包括父代理和子代理——都采用非流式处理方式。对于那些自托管的、兼容 OpenAI 的服务器而言,这是解决 流式 工具调用路径出现故障时的应急方案(例如,使用 --tool-call-parser qwen3_xml 并搭配推理解析器的 vLLM 可能会将工具调用相关的标记泄露为纯文本,从而导致返回的 tool_calls 数量为零,进而使委派的任务无法正常执行)。其默认值为 true;除非遇到了此类故障,否则建议保持该默认设置,因为非流式调用会失去上述提到的实时性特性。需要注意的是,此参数与 display.streaming 是相互独立的,后者仅用于控制终端中的令牌显示方式。

model:
  streaming: false

上下文压力警告

除了迭代预算压力之外,上下文压力还会监测对话距离压缩阈值的接近程度——即触发上下文压缩以总结旧消息的临界点。这有助于您和智能体及时了解对话何时变得过长。

进度等级后果
距离阈值 ≥ 60%信息提示CLI界面会显示青色进度条;网关会发送信息通知
距离阈值 ≥ 85%警告CLI界面会显示加粗的黄色进度条;网关会警告即将进行上下文压缩

在CLI中,上下文压力会以进度条的形式显示在工具输出流中:

  ◐ context ████████████░░░░░░░░ 62% to compaction  48k threshold (50%) · approaching compaction

在消息平台中,系统会发送一条纯文本通知:

◐ Context: ████████████░░░░░░░░ 62% to compaction (threshold: 50% of window).

如果禁用了自动压缩功能,系统会发出警告,提示上下文内容可能会被截断。

上下文压力检测是自动进行的——无需任何配置。它仅作为面向用户的通知而触发,不会修改消息流,也不会向模型的上下文中注入任何内容。

凭证池策略

当您为同一提供方拥有多个 API 密钥或 OAuth 令牌时,可配置相应的轮换策略:

credential_pool_strategies:
  openrouter: round_robin    # cycle through keys evenly
  anthropic: least_used      # always pick the least-used key

选项包括:fill_first(默认值)、round_robin、least_used、random。详细文档请参阅凭证池。

提示词缓存

当当前使用的提供方支持跨会话提示词缓存时,Hermes会自动启用该功能——无需用户进行任何配置。

对于运行在原生Anthropic平台、OpenRouter以及Nous Portal上的Claude,Hermes会在系统提示词及技能模块中添加cache_control缓存控制指令,设置1小时的过期时间(ttl: "1h")。在当前小时内的首次发送将按正常费率计费;而在同一小时内不同会话间的后续发送则可从缓存中读取,享受较低的缓存读取费率。这意味着系统提示词、加载的技能内容以及长上下文对话的前半部分,在初始1小时内可在多个hermes会话之间,甚至在不同子代理之间重复使用。

Qwen Cloud(阿里达斯阔)上游平台的缓存过期时间被限制为5分钟,因此Hermes在该平台也会采用5分钟的缓存过期时间。其他通过第三方实现的Claude版本(如AWS Bedrock、Azure Foundry)则遵循相应提供方的默认缓存设置。xAI Grok则采用独立的会话绑定对话ID机制——详情请参见xAI提示词缓存。

目前不存在关闭此功能的选项——缓存功能始终处于开启状态,即便在单轮对话中也能节省成本,因为仅系统提示词所占的输入Token数量就已相当可观。

在基于 Anthropic 风格的断点设置中,Hermes 所请求的唯一一个明确可调整的参数,便是缓存 TTL 等级。

prompt_caching:
  cache_ttl: "5m"   # "5m" or "1h" (Anthropic-supported tiers); other values are ignored

cache_ttl 用于指定 Hermes 通过原生 Anthropic API、OpenRouter 以及 Nous Portal 为 Claude 设置的断点缓存时间。仅支持 Anthropic 所认可的两种时间值("5m" 和 "1h"),其他任何数值都将被忽略。那些有自身时间限制的服务提供商(例如最大缓存时间为 5 分钟的 Qwen Cloud)仍需遵循上游服务允许的时间设置。

辅助模型

Hermes 会使用“辅助”模型来处理诸如图像分析、浏览器截图分析、会话标题生成以及上下文压缩等辅助任务。默认情况下(auxiliary.*.provider: "auto"),Hermes 会将所有辅助任务转发给您的主聊天模型——即您在 hermes model 中选择的同一服务提供商/模型。起步时无需进行任何配置,但请注意,在那些计算成本较高的推理模型(如 Opus、MiniMax M2.7 等)上,执行辅助任务会显著增加费用。如果您希望无论使用何种主模型,都能以低成本快速完成辅助任务,可以明确指定 auxiliary.<task>.provider 和 auxiliary.<task>.model 的值(例如,可在 OpenRouter 上使用 Gemini Flash 处理图像相关任务)。(网页内容提取不属于辅助任务:web_extract 功能以及浏览器快照会以确定性的方式截取长文本内容,并将完整文本保留下来以便通过 read_file 功能分页查看——此过程不涉及大型语言模型。)

::note 为何“auto”模式会使用主模型 在早期版本中,聚合器用户(OpenRouter、Nous Portal)会被分配到成本较低的默认提供方。这一做法令人费解——那些购买了聚合器订阅的用户,其辅助流量却由不同的模型处理。现在,“auto”模式会对所有用户统一使用主模型,不过仍可通过config.yaml中的任务级配置进行覆盖(详见下方的完整辅助配置参考)。

::

交互式配置辅助模型

无需手动编辑YAML文件,只需运行hermes model,然后从菜单中选择**“配置辅助模型”**。系统会提供一个交互式的任务级选择界面:

$ hermes model
→ Configure auxiliary models

[ ] vision               currently: auto / main model
[ ] title_generation     currently: openrouter / google/gemini-3-flash-preview
[ ] tts_audio_tags       currently: auto / main model
[ ] compression          currently: auto / main model
[ ] approval             currently: auto / main model
[ ] triage_specifier     currently: auto / main model
[ ] kanban_decomposer    currently: auto / main model
[ ] profile_describer    currently: auto / main model
[ ] delegation           currently: auto / inherit main agent

选择任务后,接着挑选提供方(OAuth流程会自动打开浏览器;使用API密钥的提供方则会弹出提示),最后选定模型。这些设置将会保存到config.yaml文件中的auxiliary.<task>.*路径下。其操作方式与选择主模型的流程相同——无需学习额外的语法。

Delegation选项较为特殊:它用于指定delegate_task子代理所使用的模型,并将相关设置保存在顶层的delegation.*部分(即delegation.provider/delegation.model),而非auxiliary.*目录下。这是因为子代理属于独立的完整代理,而非简单的侧边LLM调用。其auto选项的含义为“继承父代理的提供方、模型及认证信息”。

如果您不希望Hermes在首次交互后自动生成标题,可设置auxiliary.title_generation.enabled: false。不过仍可通过/title命令或“hermes sessions rename”功能手动设置标题。

仅支持流式处理的端点

某些兼容OpenAI的端点会直接拒绝非流式聊天请求(例如Tencent Copilot会返回HTTP 400错误信息“当前不支持非流式聊天请求”)。虽然交互式聊天本身是流式处理,但辅助任务(如标题生成、压缩、图像处理等)使用的是非流式调用,因此每次尝试都会失败。Hermes始终将copilot.tencent.com视为仅支持流式处理的端点;对于其他类似的端点,则需在auxiliary.stream_only_base_urls下列出对应的URL子串:

auxiliary:
  stream_only_base_urls:
    - "my-stream-only-proxy.example.com"

匹配到的辅助调用会通过 stream=True 的方式发送,且各数据块(包括工具调用差异信息)都将在客户端进行聚合处理——其他端点的相关行为保持不变。

视频教程