🤖HermesBlog
Hermes 官方教程 · 第8篇2026/8/9· Easy Understand Hermes Agent

会话管理——和 AI 的每次对话都有记录

Hermes Agent 官方教程第八篇:会话。多平台同步、跨设备接续。

会话管理:AI 的记忆库

会话管理——和 AI 的每次对话都有记录

你有没有过这种经历:跟 AI 聊到一半,不小心关掉了终端,或者切到了别的窗口,回来发现啥都没了?别慌,Hermes Agent 早就帮你把每一句话都记在小本本上了。

所有对话,自动存档

不管你是用命令行、Telegram、Discord、Slack,还是微信、钉钉、飞书……只要是跟 Hermes 说过的话,都会自动保存成一个会话(Session)。这些记录存在本地的 SQLite 数据库里(~/.hermes/state.db),里面不光有完整的聊天记录,还有:

  • 会话标题(方便你认出来这是哪次对话)
  • 用的什么模型、什么配置
  • 系统提示词的快照
  • 输入输出了多少 token
  • 开始和结束的时间

简单说,每次对话的来龙去脉,Hermes 都记得清清楚楚。

不是所有东西都往上下文里塞

你可能会担心:那是不是每次对话都要把之前所有内容重新发给模型?不是的。Hermes 很聪明,它只把当前需要的部分发给模型。

比如你发了一张图片让 AI 做表情包,Hermes 会先看一眼图片、跑个脚本处理一下,但不会把原始图片反复塞进后续的对话。真正让上下文膨胀的,往往是那些大段粘贴的日志、超长的工具输出、反复贴的代码 diff。所以,能用摘要、文件路径、局部摘录解决的,就别整段往聊天里甩。

命令行里怎么恢复会话?

这是最常用的功能,记住这几个命令就够了:

# 继续最近一次 CLI 会话
hermes --continue
hermes -c

# 按名字恢复(比如你给会话起过标题)
hermes -c "我的项目"

# 按 ID 恢复指定会话
hermes --resume 20250305_091523_a1b2c3d4

# 恢复最近一次会话
hermes --resume latest

有个贴心小细节:hermes -c 是感知终端的。你在两个终端窗口里各开一个会话,在哪个窗口执行 -c,就恢复哪个窗口自己的对话,互不干扰。这功能靠的是每个终端留下的“面包屑”文件,如果超过 30 天没用,就会自动回退到“恢复最近会话”的逻辑。

另外,恢复一个闲置会话会采用该 profile 的当前配置:工具集、MCP 服务器、terminal 设置、max_turns、检查点和默认推理强度。会话本身则保留原来的模型、启动参数,以及你做过的 /reasoning 或工作区改动。任何差异都会开启新的提示词缓存前缀,所以恢复后的第一轮会更贵一些。改动 terminal 设置(哪怕只是 terminal.timeout 这种无关项)也会把之前的 cd 重置回会话目录。

恢复会话时,Hermes 还会在输入框上方显示一个对话回顾面板,把之前的用户消息和 AI 回复简要列出来(长消息会截断、工具调用会折叠成数量),让你快速找回上下文。想关掉它,可以在 ~/.hermes/config.yaml 里设置 display.resume_display: minimal。

给会话起个名字

想让会话更好找?用 /new 开新会话的时候,直接带上名字:

/new payments-refactor

这样以后就能用 hermes -c "payments-refactor" 秒速找回。如果同名会话有好几个(比如 my project、my project #2),Hermes 会自动恢复最新的那个。

会话太长怎么办?

聊得太多,上下文塞满了?用 /compress 压缩一下,把长会话精简成摘要。想彻底开新话题,就用 /new。要是想清理旧会话,hermes sessions prune 可以删掉已结束的会话。

如果只是数据库文件变大了,先试试无损优化:

hermes sessions optimize

这个命令会合并 FTS5 索引段、压缩数据库文件,但不会动任何会话数据。注意:网关运行时,prune 会请求网关删除它选中的会话;而 optimize,以及没有网关运行时的 prune,在别的 Hermes 进程(桌面端、面板、cron)正占用 state.db 时都会拒绝执行——先停掉那个进程,或者加上 --force。另外记住:压缩是减少上下文,不是删除隐私记录。

会话不会自己过期,但该开新会话时还是要开

网关(gateway)里的会话不会因为闲置或到了某个时间点就自动重置——想开新对话,得自己用 /new 或 /reset。上下文压缩仍然是自动的。

小提示:核心已经不再读取旧的 session_reset 配置、重置策略覆盖项和重置定时器环境变量。如果你的配置里还把 session_reset.mode 设成 idle、daily 或 both,网关启动和 hermes doctor 都会给出警告。想保留基于时间的重置,可以装官方目录插件:hermes plugins install hermes-session-reset-policy,它读的是同一个配置块,行为不变。

不过,正因为会话不会自己结束,很容易一个会话用上好几周。这样虽然能跑,但会悄悄拖累“自我学习”的效果、还会推高成本:

  • 记忆只在会话边界才发挥作用。 MEMORY.md / USER.md 是在会话开始时注入的,session_search 也是用来找回已经掉出上下文的内容。在一个永不结束的会话里,所有东西都还在上下文里,Agent 就没理由去查记忆——那套“自我学习”机制基本跑不起来。记忆蒸馏(重置前的保存)也只有在会话真正结束时才会发生。
  • 成本随历史增长。 压缩能让长会话继续可用,但每一轮仍然要带上一个很大的(压缩过的)前缀。一个带蒸馏记忆的新会话,几乎总比一个月前的老会话更便宜。

实用建议:完成一个任务或话题,就结束这个会话。在自然的停顿点运行 /new(可以带名字,比如 /new payments-refactor)——每天一次或每个项目一次都行。重置前,如果这次工作产生了值得保留的偏好或流程,可以主动让 Agent “把值得记的都记下来”;它会自动从即将过期的会话里保存记忆和技能,但明确提醒一下效果更好。重启机器或网关不算边界——同一个会话会继续。

改了 SOUL.md 或技能,怎么让“老会话”用上新提示词?

一个持续中的会话,每一轮发的都是它第一轮时生成的那份系统提示词——固定不变的字节正是让提供商能复用提示缓存的原因。副作用是:你后来改的 SOUL.md、新装或改过的技能、项目上下文文件(AGENTS.md 之类)都进不到已经在跑的会话里。重启网关也没用,网关会把每个会话存着的提示词原样恢复。以前唯一的办法是 /new,但那连对话一起丢了。

现在可以这样:

# 让当前 profile 的所有会话都换上新提示词(加 -p <profile> 换别的 profile)
hermes sessions refresh-prompts

# 只让某一个会话换(ID 或唯一前缀)
hermes sessions refresh-prompts --session SESSION_ID

这条命令只是在 profile 的 state.db 里记一个刷新请求,并打印有多少会话持有存储提示词。每个会话在下一轮才真正从当前源重建系统提示词,然后才发消息;对话历史、会话 ID 和工具列表都保持原样,重建后的提示词会被存下来,之后每轮照旧逐字节复用。

  • 代价:每个被刷新的会话要付一次提示缓存未命中的钱(就重建的那一轮),之后又恢复缓存。
  • 不用重启:请求在每轮开始时都会读,内存里的 agent 和从数据库恢复的 agent 都认;重启也照样生效。所以更新脚本在替换网关前后调用都行。
  • 重建的是整个系统提示词:记忆和 USER.md 块也会在这一轮同步到最新保存的状态;工具定义不变。请求之后新建的会话本来就带新提示词,不会再重建。
  • 不打这条命令就什么都没变:会话继续回放它存着的提示词。

没人关掉的本地会话会被自动收尾

CLI、TUI 或桌面端会话,如果最后一个查看窗口消失却没正常关闭(终端被杀、连接断了、网关停止或重启时它还开着),网关会在 sessions.orphan_end_grace_s 秒(默认 300,0 表示永不结束)后把它结束掉,结束原因记为 ws_orphan_reap(重启后收尾的是 startup_orphan_reap,且至少要等它最后一次活动满 6 小时)。这些都可以关:dashboard.ws_orphan_reap_grace_s: 0 关掉前者,dashboard.startup_orphan_sweep: false 关掉重启后的清扫。还在跑的回合会跑完再结束;如果它停在被打断的回合上,则要你自己点“放弃”才关。宽限期内重新连上的查看窗口会把它保住。结束是无损的:会话仍可恢复,下一条消息会用同一个 ID 重新打开。

会话来源一览

Hermes 支持超多平台,每个会话都会标记来源:

来源 说明
cli 命令行交互
oneshot 一次性的非交互运行(hermes chat --oneshot -q、-Q、hermes -z 等)。它不会出现在 TUI、桌面端和面板的会话选择器里,但算作 CLI 历史,hermes -c / --resume latest 能接着它继续,hermes sessions list 也看得到
telegram Telegram
discord Discord
slack Slack
whatsapp WhatsApp
weixin 个人微信
wecom 企业微信
dingtalk 钉钉
feishu 飞书
email 邮件
cron 定时任务
api-server API 请求
kanban 看板调度任务(只在看板上出现,会话选择器里隐藏)
tool 第三方集成(--source tool),会话选择器里隐藏
homeassistant Home Assistant 事件(插件)

总共二十多种,基本覆盖了你能想到的所有入口。顺带一提,会话中途被压缩后,来源标签会保持不变——比如 tool 或 oneshot 会话压缩出来的子会话,还是同样的来源、同样的可见性。

把 CLI 会话交接给聊天平台

在 CLI 会话里用 /handoff <platform>,可以把当前对话直接转到某个聊天平台的 home 频道,Agent 会从 CLI 停下的地方接着聊——同一个会话 ID、完整的对话记录、工具调用全都在。

# 在 CLI 会话里
/handoff telegram

前提是目标平台已启用、并且设置过 home 频道(在目标聊天里跑一次 /sethome)。交接完成后,CLI 会提示你用 /resume <标题> 随时把会话拉回桌面端继续。

不同平台落地的方式略有不同:Telegram 会开一个新的论坛话题(如果机器人主人通过 BotFather 启用了 Threaded Mode,就是私聊话题;否则是论坛超级群的话题),Discord 建一个自动归档的线程,Slack 发一条种子消息当线程锚点,Matrix 用事件 ID 作为线程根;而 WhatsApp、Signal、SMS 没有原生线程,就直接落在 home 频道里。另外提醒一句:对于不支持线程、又是多人共享群组 home 频道的平台,交接后的会话会按私聊方式处理——自聊 home 频道(最常见的情况)没问题,但真正共享的群聊就不太理想了。

导出与删除会话

hermes sessions export 可以把会话导出成文件。Markdown/QMD 格式会为每个导出的会话写一个 .md 或 .qmd 文件,外加一个 manifest.jsonl,里面记录文件路径、消息数、血缘 ID 和 SHA-256。批量导出至少要带一个过滤条件,光秃秃的批量导出会被拒绝。--delete-after-verified 只对 --session-id 生效,并且必须加 --yes。因为删除父会话会连带删掉它的委派/子代理会话,这个模式会先把每个委派会话导出到单独文件并校验,然后才动手删除。Markdown/QMD 文件保存的是会话展示的完整历史,包括被就地压缩归档的轮次。删除时会在同一个数据库事务里再次比对这份展示记录和委派集合;期间只要发生追加、重写、回退、压缩或委派变化,删除就会被拒绝。同样的展示历史规则也适用于 --format html、--only user-prompts(Markdown 或 JSONL 输出)以及 /save md|html。

这里有个容易搞混的地方,值得单独说清楚:

  • 完整备份:hermes sessions export(不带 --only)和 /save json(CLI、消息平台、TUI、桌面端)导出的每一行存储记录,包括归档行。也就是说,备份里装着这个会话曾经有过的全部内容——被就地压缩归档的轮次、被回退或编辑删掉的消息,全都在。导出的文件是每行一个 JSON 对象,带完整的会话元数据和每条消息的 active/compacted 标记。把这些文件导回去(面板的会话导入),那些行会作为归档历史恢复,而不会重新变成活跃的模型上下文。
  • 分享用的导出:想分享一段对话,用展示格式(--format md 或 html),它只包含会话当前展示的历史,再配上 --redact 更稳妥。
  • 过滤导出:会包含匹配的置顶和归档会话。置顶是防止会话被清理,而不是把它排除在备份之外。如果显式指定的 --session-id 解析不到,导出会以非零状态退出,并且不生成任何输出文件。
  • --redact:会在写入前清洗消息内容和工具输出里的密钥(API key、token、凭证)——准备分享的导出都建议加上。

另外,每个会话的备份都是在内存里拼出来的,所以存储行数超过 sessions.max_export_messages(默认 20000 条,设成 0 关闭这道闸)的会话会被拒绝导出。这个上限是按会话算的,而且统计的是每一行存储记录(含归档),所以一个被重度压缩、活跃尾巴很短的会话也可能超限。TUI/桌面端的 /save 被这道闸拦下时会返回错误码 4131;hermes sessions export 对这类会话仍然可用,但它的 JSON/JSONL 备份同样要求每个会话不超过这个上限。面板的 Sessions 页面里的导出动作是流式写出的,不受这个上限限制。

还有一点:删除一个还在运行中的聊天里打开的会话,并不会停掉那个聊天:它下一次保存会用同一个 ID 把会话重新建出来,带上完整的内存记录。想让会话彻底消失,先把聊天关掉。

被压缩拆成多个会话的对话会作为一个整体来清理:只要还有较新的分段存在,较早的分段就会保留。归档也是同理——一段被压缩的对话会通过它的活跃末端整体归档,旧的压缩分段不会单独按自己的年龄匹配,所以一个还在活跃的聊天不会因为历史太长而被隐藏。

归档、隐藏与恢复的小规矩

会话的“置顶 / 归档 / 隐藏”这几个标记,在恢复时会被保留:

  • 恢复一个导出(面板导入,或者某个 profile 收养一个“流浪”会话)时,置顶、归档、隐藏标记都会保留,连“这个归档是不是 sessions.auto_archive 清扫出来的”也会一起记住。
  • 恢复回来的置顶会话,依然豁免于保留期清理;收养回来的 Bot Mode 聊天,依然保持隐藏;恢复回来的清扫归档,你恢复它时它还是会回来。
  • 比这些标记更老的导出文件,恢复后就变成普通的、未置顶、可见的会话。

另外,被 sessions.auto_archive 闲置清扫隐藏的聊天,一旦重新活跃就会自己回来——你恢复它,或者新活动把它压进一个新的延续会话时都算。而你自己手动归档的聊天(侧边栏、API 或 hermes sessions archive),就得你自己取消归档才会回来。

修复受损的存储提示词

旧版本受 #122822 影响,可能让网关的清理流程或网关 /compress 把一个分离的维护代理的“精简工具集”系统提示词写到了活跃会话上。装上 PR #122825 的根因修复后,可以用 hermes sessions repair-prompts 找出已经被写坏的行。

这个扫描很保守:只有当存储的提示词缺少 ## Skill Safety 指引并且持久化的 tools[] 固定项里包含 skill_manage(它总会带上那段指引)时,才会提议修复。没有可读固定项、或者只有 memory 固定项(这也是合法的 toolsets: [memory] 配置)的行会被报告为无法验证,扫描不会改动它们;需要的话可以按 SESSION_ID 显式清理。只有 memory 的行也会自己变得可修复:一旦会话被恢复,它的 tools[] 固定项会重新固定完整的工具面,之后扫描就能识别了。

小结

会话管理就是 Hermes 的“记忆系统”——自动存档、按名恢复、跨平台同步。下次再也不用担心聊到一半丢了上下文,hermes -c 一下,接着聊!

官方界面截图

官方会话回顾图

📖 官方文档

本文根据 Hermes Agent 官方文档编写,原文见:官方文档 › user-guide/sessions