会话

Hermes Agent 自动将每次对话保存为一个会话(session)。会话支持对话恢复、跨会话搜索和完整的对话历史管理。

会话如何工作

每次对话——无论是来自 CLI、Telegram、Discord、Slack、WhatsApp、Signal、Matrix、Teams 还是任何其他消息平台——都会作为会话存储,包含完整的消息历史。会话通过以下方式追踪:

  1. SQLite 数据库~/.hermes/state.db)——结构化会话元数据,支持 FTS5 全文搜索,以及完整的消息历史

SQLite 数据库存储以下内容:

  • 会话 ID、来源平台、用户 ID
  • 会话标题(唯一的、人类可读的名称)
  • 模型名称和配置
  • 系统提示词快照
  • 完整消息历史(角色、内容、工具调用、工具结果)
  • Token 计数(输入/输出)
  • 时间戳(开始时间、结束时间)
  • 父会话 ID(用于压缩触发的会话拆分)

哪些计入上下文

Hermes 存储会话历史以便恢复对话,但不会在每个轮次都重新发送所有历史字节。在每个轮次中,模型看到的是选定的系统提示词、当前对话窗口以及 Hermes 为该轮次显式注入的任何内容。

媒体附件作为轮次作用域的输入处理:

  • 图片可以原生附加到下一次模型调用,或在当前模型不支持原生视觉时预分析为文本描述。
  • 音频在配置了语音转文本时被转录为文本。
  • 文本文档可以包含其提取的文本;其他类型的文档通常通过保存的本地路径和简短说明来表示。
  • 附件路径和提取/派生的文本可能出现在记录中,但原始图像、音频或二进制文件字节不会重复复制到未来的提示词中。

例如,如果用户发送一张图片并要求 Hermes 用它制作一个表情包,Hermes 可能使用视觉检查图片一次,然后运行一个图像处理脚本。未来的轮次不会自动携带原始 JPEG 到上下文中。它们只携带写入对话的内容,如用户的请求、简短的图片描述、本地缓存路径或最终的助手回复。

上下文增长的最常见原因不是媒体文件本身,而是冗长的文本:粘贴的脚本、完整的日志、大型工具输出、长 diff、重复的状态报告和详细的证明转储。优先使用摘要、文件路径、聚焦的摘录和基于工具的查询,而不是将大型制品复制到聊天中。

:::tip 当会话变长时使用 /compress,需要新线程时使用 /new,并且只在你想从存储中删除旧的已结束会话时才使用 hermes sessions prune。压缩减少活动上下文;它不是隐私删除。 向 /new 传递名称(例如 /new payments-refactor)可以提前设置新会话的初始标题——便于稍后使用 /resume <name> 或在 /sessions 选择器中找到它。 :::

会话来源

每个会话都标记有其来源平台:

来源描述
cli交互式 CLI(hermeshermes chat
telegramTelegram 消息应用
discordDiscord 服务器/DM
slackSlack 工作空间
whatsappWhatsApp 消息应用
signalSignal 消息应用
matrixMatrix 房间和 DM
mattermostMattermost 频道
email电子邮件(IMAP/SMTP)
sms通过 Twilio 的短信
dingtalk钉钉消息应用
feishu飞书消息应用
wecom企业微信
weixin微信(个人微信)
bluebubbles通过 BlueBubbles macOS 服务器的 Apple iMessage
qqbotQQ 机器人(腾讯 QQ)通过官方 API v2
homeassistantHome Assistant 对话
webhook传入的 Webhook
api-serverAPI 服务器请求
acpACP 编辑器集成
cron定时任务
batch批处理运行

CLI 会话恢复

使用 --continue--resume 从 CLI 恢复之前的对话:

继续上次会话

# 恢复最近的 CLI 会话
hermes --continue
hermes -c
 
# 或使用 chat 子命令
hermes chat --continue
hermes chat -c

这会从 SQLite 数据库中查找最近的 cli 会话,并加载其完整的对话历史。

按名称恢复

如果你已为会话设置了标题(参见下面的会话命名),可以按名称恢复:

# 恢复一个命名会话
hermes -c "my project"
 
# 如果有谱系变体(my project、my project #2、my project #3),
# 这会自动恢复最近的变体
hermes -c "my project"   # → 恢复 "my project #3"

恢复特定会话

# 按 ID 恢复特定会话
hermes --resume 20250305_091523_a1b2c3d4
hermes -r 20250305_091523_a1b2c3d4
 
# 按标题恢复
hermes --resume "refactoring auth"
 
# 或使用 chat 子命令
hermes chat --resume 20250305_091523_a1b2c3d4

会话 ID 在退出 CLI 会话时显示,也可以通过 hermes sessions list 找到。

恢复时的对话回顾

当你恢复一个会话时,Hermes 会在输入提示之前以样式化面板显示之前对话的紧凑回顾:

恢复 Hermes 会话时显示的前对话回顾面板的样式化预览。

恢复模式会显示一个紧凑的回顾面板,其中包含最近用户和助手的对话轮次,然后返回实时提示。

回顾:

  • 显示用户消息(金色 )和助手回复(绿色
  • 截断长消息(用户 300 字符,助手 200 字符 / 3 行)
  • 折叠工具调用为带有工具名称的计数(例如 [3 tool calls: terminal, web_search]
  • 隐藏系统消息、工具结果和内部推理
  • 限制最多最后 10 轮对话,带有”… N earlier messages …”指示符
  • 使用暗淡样式以区别于活跃对话

要禁用回顾并保持最小单行行为,在 ~/.hermes/config.yaml 中设置:

display:
  resume_display: minimal   # 默认:full

:::tip 会话 ID 格式为 YYYYMMDD_HHMMSS_<hex>——CLI/TUI 会话使用 6 字符十六进制后缀(例如 20250305_091523_a1b2c3),网关会话使用 8 字符后缀(例如 20250305_091523_a1b2c3d4)。你可以按 ID(完整或唯一前缀)或按标题恢复——-c-r 都支持。 :::

跨平台交接

使用 /handoff <platform> 从 CLI 会话将实时对话转移到消息平台的家频道。agent 从 CLI 离开的地方继续——相同的会话 ID、完整的角色感知记录、工具调用等所有内容。

# 在 CLI 会话中
/handoff telegram

执行过程:

  1. CLI 验证 <platform> 已启用且设置了家频道(在目标聊天中运行一次 /sethome 进行配置)。

  2. CLI 将会话标记为待处理并阻塞轮询网关。如果 agent 正在回复中则拒绝——等待当前响应完成。

  3. 网关监视器认领交接并请求目标适配器创建一个新线程:

    • Telegram——打开一个新的论坛主题(如果 Bot API 9.4+ 主题模式在聊天中启用,则为 DM 主题,否则为论坛超级群组主题)。
    • Discord——在家文本频道下创建一个 1440 分钟自动归档的线程。
    • Slack——发布一条种子消息并将其 ts 作为线程锚点。
    • WhatsApp / Signal / Matrix / SMS——无原生线程,直接回退到家频道。
  4. 网关将目标密钥重新绑定到你现有的 CLI 会话 ID,然后伪造一个合成用户轮次,要求 agent 确认并总结。回复会出现在新线程中。

  5. 网关确认成功时,CLI 打印一个 /resume 提示并干净退出:

    ↻ Handoff complete. The session is now active on telegram.
      Resume it on this CLI later with: /resume my-session-title
    
  6. 从那时起,对话在平台上继续。在新线程中回复——该频道中任何被授权的用户共享同一个会话,并且线程中任何后续的真实用户消息都能无缝加入,因为线程会话的键不包含 user_id

恢复回 CLI: 当你想回到桌面时,只需运行 /resume <title>(或从 shell 运行 hermes -r "<title>")并从平台离开的地方继续。

失败模式:

  • 未配置家频道 → CLI 拒绝并提示 /sethome
  • 平台未启用 / 网关未运行 → CLI 在 60 秒后超时并显示清晰消息,CLI 会话保持完整。
  • 线程创建失败(权限、主题模式关闭)→ 直接回退到家频道并仍完成交接;没有线程隔离但交接本身成功。
  • adapter.send 失败(速率限制、临时 API 错误)→ 交接标记为失败并注明原因;行数据清除后你可以重试。

值得注意的限制: 对于不支持线程的平台上的多用户群组家频道,合成用户的键为 DM 风格的会话。这对于自 DM 家频道(典型设置)有效,但对于真正共享的群聊并不理想。线程覆盖了 Telegram / Discord / Slack——迄今为止最常见的场景——因此大多数设置永远不会遇到这个问题。

会话命名

为会话设置人类可读的标题,便于查找和恢复。

自动生成标题

Hermes 在首次交流后为每个会话自动生成一个简短的描述性标题(3–7 个词)。这在一个后台线程中使用快速的辅助模型运行,因此不增加延迟。在浏览会话时,你会看到 hermes sessions listhermes sessions browse 中显示自动生成的标题。

自动标题仅对每个会话触发一次,如果你已手动设置标题则跳过。

手动设置标题

在任何聊天会话(CLI 或网关)中使用 /title 斜杠命令:

/title my research project

标题会立即应用。如果会话尚未在数据库中创建(例如,你在发送第一条消息之前运行 /title),它会被排队并在会话开始时应用。

你也可以从命令行重命名现有会话:

hermes sessions rename 20250305_091523_a1b2c3d4 "refactoring auth module"

标题规则

  • 唯一——不能有两个会话共享同一个标题
  • 最多 100 字符——保持列表输出整洁
  • 已清理——控制字符、零宽字符和 RTL 覆盖会自动被剥离
  • 普通 Unicode 没问题——表情符号、CJK、重音字符都可以使用

压缩时的自动谱系

当会话的上下文被压缩时(手动通过 /compress 或自动),Hermes 会创建一个新的延续会话。如果原始会话有标题,新会话会自动获得一个编号标题:

"my project" → "my project #2" → "my project #3"

当你按名称恢复(hermes -c "my project")时,它会自动选择谱系中最近的会话。

消息平台中的 /title

/title 命令在所有网关平台(Telegram、Discord、Slack、WhatsApp)上都可以使用:

  • /title My Research——设置会话标题
  • /title——显示当前标题

会话管理命令

Hermes 通过 hermes sessions 提供全套会话管理命令:

列出会话

# 列出最近的会话(默认:最近 20 个)
hermes sessions list
 
# 按平台过滤
hermes sessions list --source telegram
 
# 显示更多会话
hermes sessions list --limit 50

当会话有标题时,输出显示标题、预览和相对时间戳:

Title                  Preview                                  Last Active   ID
────────────────────────────────────────────────────────────────────────────────────────────────
refactoring auth       Help me refactor the auth module please   2h ago        20250305_091523_a
my project #3          Can you check the test failures?          yesterday     20250304_143022_e
—                      What's the weather in Las Vegas?          3d ago        20250303_101500_f

当没有会话有标题时,使用更简单的格式:

Preview                                            Last Active   Src    ID
──────────────────────────────────────────────────────────────────────────────────────
Help me refactor the auth module please             2h ago        cli    20250305_091523_a
What's the weather in Las Vegas?                    3d ago        tele   20250303_101500_f

导出会话

# 将所有会话导出到 JSONL 文件
hermes sessions export backup.jsonl
 
# 从特定平台导出会话
hermes sessions export telegram-history.jsonl --source telegram
 
# 导出单个会话
hermes sessions export session.jsonl --session-id 20250305_091523_a1b2c3d4

导出的文件每行包含一个 JSON 对象,包含完整的会话元数据和所有消息。

删除会话

# 删除特定会话(带确认)
hermes sessions delete 20250305_091523_a1b2c3d4
 
# 不确认直接删除
hermes sessions delete 20250305_091523_a1b2c3d4 --yes

重命名会话

# 设置或更改会话的标题
hermes sessions rename 20250305_091523_a1b2c3d4 "debugging auth flow"
 
# CLI 中多词标题不需要引号
hermes sessions rename 20250305_091523_a1b2c3d4 debugging auth flow

如果标题已被另一个会话使用,会显示错误。

修剪旧会话

# 删除超过 90 天(默认)的已结束会话
hermes sessions prune
 
# 自定义时间阈值
hermes sessions prune --older-than 30
 
# 仅修剪来自特定平台的会话
hermes sessions prune --source telegram --older-than 60
 
# 跳过确认
hermes sessions prune --older-than 30 --yes

:::info 修剪仅删除已结束的会话(已显式结束或自动重置的会话)。活跃会话永远不会被修剪。 :::

会话统计

hermes sessions stats

输出:

Total sessions: 142
Total messages: 3847
  cli: 89 sessions
  telegram: 38 sessions
  discord: 15 sessions
Database size: 12.4 MB

对于更深入的分析——Token 使用量、成本估算、工具分解和活动模式——请使用 hermes insights

会话搜索工具

Agent 有一个内置的 session_search 工具,使用 SQLite 的 FTS5 引擎对所有历史对话执行全文搜索——并让 agent 滚动浏览找到的任何会话。无需 LLM 调用,无需摘要,无需截断。每种调用模式都从数据库返回实际消息。

三种调用模式

该工具根据你设置的参数推断你的意图。没有 mode 参数。

1. 发现——传递 query

session_search(query="auth refactor", limit=3)

运行 FTS5,按会话谱系去重,返回前 N 个会话。每个结果包含:

  • session_idtitlewhensource
  • snippet——FTS5 高亮的匹配摘录
  • bookend_start——会话的前 3 条用户+助手消息(目标/启动)
  • messages——FTS5 匹配周围 ±5 条消息,锚点消息已标记(上下文中的命中点)
  • bookend_end——会话的最后 3 条用户+助手消息(结果/决策)
  • match_message_idmessages_beforemessages_after

书签 + 窗口共同重构目标 → 匹配 → 结果,而无需支付整个记录的代价。典型耗时:在实际会话数据库上 15–50ms。

2. 滚动——传递 session_id + around_message_id

session_search(session_id="20260510_174648_805cc2", around_message_id=590803, window=10)

返回以锚点为中心的 ±window 条消息窗口。无 FTS5,无书签——只是切片。在发现调用后使用,当你需要比默认 ±5 窗口更多的上下文时。

  • 向前滚动:将 messages[-1].id 传回为 around_message_id
  • 向后滚动:将 messages[0].id 传回为 around_message_id
  • 边界消息出现在两个窗口中作为方向标记
  • messages_beforemessages_after 小于 window 时,表示你已到达会话的开头或结尾

典型耗时:每次滚动调用 1–2ms。

3. 浏览——无参数:

session_search()

按时间顺序返回最近的会话(标题、预览、时间戳)。当用户问”我之前在做什么”而未指定主题时很有用。

FTS5 查询语法

关键词模式支持标准的 FTS5 查询语法:

  • 简单关键词:docker deployment(FTS5 默认为 AND)
  • 短语:"exact phrase"
  • 布尔:docker OR kubernetespython NOT java
  • 前缀:deploy*

可选参数

  • sort——newestoldest,基于 FTS5 排名之上。省略则仅按相关性排序(默认;适用于探索性回忆)。使用 newest 回答”我们在 X 上停在哪里了”的问题,使用 oldest 回答”X 是如何开始的”的问题。
  • role_filter——要包含的角色(逗号分隔)。发现默认使用 user,assistant(工具输出通常是噪音)。传递 user,assistant,tool 以包含工具输出(调试工具行为),或传递 tool 以仅搜索工具输出。

何时使用

Agent 被提示自动使用会话搜索:

“当用户引用过去对话中的内容,或你怀疑存在相关的先前上下文时,使用 session_search 来回忆,而不是要求他们重复自己。”

典型触发词:“we did this before”、“remember when”、“last time”、“as I mentioned”,或任何对当前窗口中不存在的项目/人员/概念的引用。

按平台追踪会话

网关会话

在消息平台上,会话通过从消息源构建的确定性会话键来标识:

聊天类型默认键格式行为
Telegram DMagent:main:telegram:dm:<chat_id>每个 DM 聊天一个会话
Discord DMagent:main:discord:dm:<chat_id>每个 DM 聊天一个会话
WhatsApp DMagent:main:whatsapp:dm:<canonical_identifier>每个 DM 用户一个会话(当映射存在时,LID/电话别名折叠为一个身份)
群组聊天agent:main:<platform>:group:<chat_id>:<user_id>群组内按用户(当平台暴露用户 ID 时)
群组线程/主题agent:main:<platform>:group:<chat_id>:<thread_id>所有线程参与者共享会话(默认)。设置 thread_sessions_per_user: true 则按用户。
频道agent:main:<platform>:channel:<chat_id>:<user_id>频道内按用户(当平台暴露用户 ID 时)

当 Hermes 无法获取共享聊天的参与者标识符时,会回退到该房间的一个共享会话。

共享 vs 隔离的群组会话

默认情况下,Hermes 在 config.yaml 中使用 group_sessions_per_user: true。这意味着:

  • Alice 和 Bob 可以在同一个 Discord 频道中与 Hermes 对话,而不共享记录历史
  • 一个用户长时间的工具密集型任务不会污染另一个用户的上下文窗口
  • 中断处理也按用户独立,因为运行中的 agent 键与隔离的会话键匹配

如果你想要一个共享的”房间大脑”,请设置:

group_sessions_per_user: false

这将群组/频道恢复为每个房间的单个共享会话,这保留了共享的对话上下文,但也共享了 Token 成本、中断状态和上下文增长。

会话重置策略

网关会话根据可配置的策略自动重置:

  • idle(空闲)——在 N 分钟不活动后重置
  • daily(每日)——在每天特定时间重置
  • both(两者)——以先到者为准(空闲或每日)
  • none(无)——从不自动重置

在会话自动重置之前,会给 agent 一个轮次来从对话中保存任何重要的记忆或技能。

具有活动后台进程的会话永远不会自动重置,无论策略如何。

存储位置

内容路径描述
SQLite 数据库~/.hermes/state.db所有会话元数据 + 带 FTS5 的消息
网关消息~/.hermes/state.dbSQLite——所有会话消息的规范存储
网关路由索引~/.hermes/sessions/sessions.json将会话键映射到活跃会话 ID(来源元数据、过期标志)

SQLite 数据库使用 WAL 模式支持并发读取器和单个写入器,这很好地适用于网关的多平台架构。

:::note 旧的 JSONL 记录 在 state.db 成为规范存储之前创建的会话可能留有 ~/.hermes/sessions/ 中的 *.jsonl 文件。Hermes 不再写入或读取它们。在验证对应的会话存在于 state.db 后可以安全删除。 :::

数据库 Schema

state.db 中的关键表:

  • sessions——会话元数据(id、source、user_id、model、title、时间戳、Token 计数)。标题具有唯一索引(允许 NULL 标题,仅非 NULL 值必须唯一)。
  • messages——完整消息历史(role、content、tool_calls、tool_name、token_count)
  • messages_fts——用于消息内容全文搜索的 FTS5 虚拟表

会话过期与清理

自动清理

  • 网关会话根据配置的重置策略自动重置
  • 重置前,agent 从即将过期的会话中保存记忆和技能
  • 选择加入的自动修剪:当 sessions.auto_prunetrue 时,在 CLI/网关启动时修剪早于 sessions.retention_days(默认 90)的已结束会话
  • 在实际删除行后进行修剪后,state.db 会执行 VACUUM 以回收磁盘空间(SQLite 在执行普通 DELETE 时不会缩小文件)
  • 修剪每 sessions.min_interval_hours(默认 24)最多运行一次;上次运行的时间戳记录在 state.db 本身中,因此在同一个 HERMES_HOME 中的每个 Hermes 进程共享

默认为关闭——会话历史对 session_search 回忆很有价值,静默删除可能会让用户感到意外。在 ~/.hermes/config.yaml 中启用:

sessions:
  auto_prune: true          # 选择启用——默认为 false
  retention_days: 90        # 将已结束会话保留这么多天
  vacuum_after_prune: true  # 在修剪后回收磁盘空间
  min_interval_hours: 24    # 不要频繁运行修剪超过此间隔

活跃会话永远不会自动修剪,无论其年龄如何。

手动清理

# 修剪超过 90 天的会话
hermes sessions prune
 
# 删除特定会话
hermes sessions delete <session_id>
 
# 在修剪前导出(备份)
hermes sessions export backup.jsonl
hermes sessions prune --older-than 30 --yes

:::tip 数据库增长缓慢(典型情况:数百个会话占用 10-15 MB),会话历史支持跨过去对话的 session_search 回忆,因此自动修剪默认关闭。如果你运行大量的网关/cron 工作负载,state.db 显著影响性能(观察到的故障模式:384 MB 的 state.db,约 1000 个会话,FTS5 插入和 /resume 列表变慢),请启用它。使用 hermes sessions prune 进行一次性清理,无需打开自动扫描。 :::