☰
别再往 System Prompt 里塞东西了!深度拆解 Hermes Agent 的“五层记忆“黑科技
2026/10/2 11:01:01 网站建设 项目流程

1. 从 System Prompt 膨胀说起:Hermes Agent 五层记忆架构到底解决什么问题

如果你写过稍微复杂一点的 Agent,大概率经历过这个循环:第一版跑得挺好,第二版加两条业务规则,第三版加用户偏好,第四版加历史决策记录……等到某天你打开prompt_builder.py,发现 System Prompt 已经膨胀到八千多 token,而模型在第五轮对话之后依然开始胡言乱语。这不是模型不行,是架构选错了方向。

Hermes Agent 的五层记忆架构,本质上是在回答一个问题:Agent 的记忆应该放在哪里,才既便宜又准确又可追溯?它的答案不是"把上下文窗口撑到 200K",而是把记忆拆成五个层次,每一层用不同的存储介质和检索策略,让 System Prompt 只承载真正需要每轮都出现的那部分内容。

这套架构适合谁?如果你正在做本地 Agent、CLI 编码助手、或者任何需要跨会话保持状态的 LLM 应用,并且已经被 token 账单和"失忆"问题折磨过,那这套分层思路值得完整复现一遍。它不依赖特定云服务,核心存储就是本地 SQLite,检索用 FTS5 全文索引,压缩用辅助模型做摘要,整套东西在单机上就能跑起来。

我试过把原来一个 6000 token 的 System Prompt 拆成"冻结偏好 + 技能索引 + 会话检索"三部分之后,单轮输入 token 直接降到 1800 左右,而且模型对历史决策的引用准确率反而上升了。原因很简单:信息密度降低了,注意力不再被稀释。

下面按三条主线展开:System Prompt 怎么瘦身、SQLite FTS5 怎么建表和查询、上下文压缩前后 token 怎么对比验证。每一段都给出可复制的配置和命令,你可以边看边在本地跑。

2. TaoToken 前置准备:给 Hermes Agent 配一个稳定的模型入口

Hermes Agent 本身是运行时框架,它需要调用外部 LLM 来完成推理和摘要。压缩中间轮次、生成技能描述、做用户建模,这些都会产生额外的模型调用。如果模型入口不稳定,压缩过程会频繁失败,血缘链就断了。所以第一步是把模型接入层配好。

TaoToken 在这里的角色是统一的模型接入网关。你不需要在 Hermes 的配置里分别填 Anthropic、OpenAI 的 key,而是通过一个 Base URL 加一个 Key,就能在多个模型之间切换。对于 Hermes 这种"主模型 + 廉价摘要模型"的组合场景特别合适——主推理用强模型,压缩摘要用便宜模型,两者走同一个入口。

先拿到 API Key。访问 https://taotoken.net/api-keys 创建,注意这个页面是控制台里的密钥管理入口,创建后复制保存,后面配置要用。

然后确认你要用的模型 ID。打开 https://taotoken.net/models 可以看到当前可用的模型列表,记下你打算用作主推理和摘要的两个 Model ID。Hermes 的压缩器默认会调用配置里指定的compression.model,所以这里要提前想好。

如果你更习惯在对话界面里先验证模型连通性,可以打开 https://taotoken.net/chat 直接发一条测试消息,确认返回正常再往下走。这一步能排除掉大部分"Key 填错"或"模型名写错"的低级问题。

对于长期跑编码 Agent 的场景,建议了解一下 Coding Plan,它针对高频调用做了额度优化:https://taotoken.net/coding-plan 。Hermes 的压缩和技能扫描会产生不少小请求,用包月方案比按量计费更可控。

接入文档在 https://taotoken.net/doc ,里面有各语言 SDK 的调用示例和 Base URL 说明。Hermes 用的是 OpenAI 兼容协议,所以配置里填https://taotoken.net/api作为 base_url 即可,注意这个地址不带任何查询参数。

配好之后,你的 Hermes 配置里应该出现类似这样的结构:一个base_url指向 TaoToken 的 API 地址,一个api_key是你刚创建的密钥,然后model字段填你在模型列表里选定的 ID。这三件套(Base URL + Key + Model ID)是后面所有配置的基础,缺一个都会在启动时报认证错误。

3. 可复制配置:五层记忆的 settings 与 FTS5 建表语句

这一节给出可以直接粘贴的配置片段。Hermes 的配置分两部分:一部分是config.yaml里的运行时参数,另一部分是 SQLite 数据库的 schema。两者要对应上,否则 FTS5 索引不会自动同步。

先看config.yaml的记忆相关段落。路径按你的实际安装位置调整,这里用~/.hermes/profiles/default/config.yaml作为示例:

# ~/.hermes/profiles/default/config.yaml model: base_url: "https://taotoken.net/api" api_key: "${TAOTOKEN_API_KEY}" model: "claude-sonnet-4-6" compression: enabled: true threshold_tokens: 75000 protect_first_n: 3 protect_last_n: 6 max_passes: 3 model: "gpt-4-turbo" memory: provider: "builtin" memory_file: "~/.hermes/memories/MEMORY.md" user_file: "~/.hermes/memories/USER.md" snapshot_on_init: true database: path: "~/.hermes/hermes.db" wal_mode: true fts5_tokenizer: "unicode61" checkpoint_interval: 50 skills: dirs: - "~/.hermes/skills" - "./skills" - "./optional-skills" cache_snapshot: "~/.hermes/.cache/skills_snapshot"

注意api_key用了环境变量引用,不要把明文密钥写进配置文件。在 shell 里export TAOTOKEN_API_KEY="你的密钥"即可。

接下来是数据库 schema。Hermes 的hermes_state.py在初始化时会建表,但如果你想手动确认或重建,下面是核心的 messages 表和 FTS5 虚拟表:

-- 主消息表 CREATE TABLE IF NOT EXISTS messages ( id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT NOT NULL, role TEXT NOT NULL, content TEXT NOT NULL, timestamp REAL NOT NULL, tool_calls TEXT, tool_results TEXT ); -- 会话表,含血缘字段 CREATE TABLE IF NOT EXISTS sessions ( id TEXT PRIMARY KEY, parent_session_id TEXT, started_at REAL NOT NULL, ended_at REAL, message_count INTEGER DEFAULT 0, end_reason TEXT ); -- FTS5 全文索引虚拟表 CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5( content, tokenize = 'unicode61' ); -- 触发器:保持 FTS5 与主表同步 CREATE TRIGGER IF NOT EXISTS messages_ai AFTER INSERT ON messages BEGIN INSERT INTO messages_fts(rowid, content) VALUES (new.id, new.content); END; CREATE TRIGGER IF NOT EXISTS messages_ad AFTER DELETE ON messages BEGIN DELETE FROM messages_fts WHERE rowid = old.id; END; CREATE TRIGGER IF NOT EXISTS messages_au AFTER UPDATE ON messages BEGIN UPDATE messages_fts SET content = new.content WHERE rowid = old.id; END;

unicode61分词器对中文支持有限,但能正确处理英文和数字。如果你的对话以中文为主,可以在查询时用短语匹配来弥补分词粒度问题,后面查询示例里会演示。

建完表之后,用PRAGMA journal_mode=WAL;开启 WAL 模式,这是并发写入稳定的关键。Hermes 的_execute_with_retry依赖BEGIN IMMEDIATE加随机退避来避免锁冲突,WAL 模式让读操作不阻塞写操作。

配置和 schema 都就位后,启动 Hermes 应该能看到数据库初始化日志。如果报database is locked,检查是不是有另一个进程占着同一个 db 文件。

4. 验证请求:FTS5 查询与上下文压缩前后的 token 对比

配置写完不算完,得亲眼看到记忆被写入、被检索、被压缩。这一节给三个验证动作,每个都有预期输出。

验证一:FTS5 全文检索是否命中历史消息。

先往数据库里塞几条测试消息,或者直接用你之前跑过的会话数据。然后执行:

SELECT m.role, snippet(messages_fts, 0, '[', ']', '...', 30) AS snippet, m.timestamp FROM messages_fts JOIN messages m ON messages_fts.rowid = m.id WHERE messages_fts MATCH 'docker deploy' ORDER BY m.timestamp DESC LIMIT 5;

预期输出里,snippet列会把匹配到的关键词用方括号标出来,比如如何部署 [docker] [deploy] 容器。如果返回空,先确认messages_fts里有数据:SELECT COUNT(*) FROM messages_fts;。计数为 0 说明触发器没生效,检查建表语句是否完整执行。

FTS5 的查询语法值得记几个:docker AND deploy要求两个词都出现,"docker deploy"精确匹配短语,doc*前缀匹配,docker NOT error排除包含 error 的记录。这些在排查历史决策时比向量检索精确得多,因为它是字面匹配,不会"猜"。

验证二:血缘链是否完整。

压缩发生后,旧会话的end_reason应该变成compressed,新会话的parent_session_id指向旧会话。查询:

SELECT id, parent_session_id, message_count, end_reason FROM sessions ORDER BY started_at DESC LIMIT 5;

预期看到类似这样的链:最新会话的parent_session_id指向上一轮被压缩的会话,而那个会话的end_reason是compressed。如果parent_session_id全是 NULL,说明压缩器没有正确调用end_session(),检查compression.enabled是否为 true。

验证三:压缩前后的 token 对比。

这是最直观的一步。在压缩触发前后分别记录estimate_request_tokens_rough()的返回值。你可以在context_compressor.py的compress()入口和出口各加一行日志:

import logging logger = logging.getLogger("hermes.compressor") def compress(self, messages): before = self._estimate_tokens(messages) logger.info(f"compress start: {before} tokens, {len(messages)} messages") for _pass in range(self.max_passes): if self._estimate_tokens(messages) < self.threshold_tokens: break messages = self._compress_middle(messages) after = self._estimate_tokens(messages) logger.info(f"compress done: {after} tokens, {len(messages)} messages") return messages

跑一轮长对话,触发压缩后看日志。典型结果是:压缩前 78000 token、42 条消息,压缩后 21000 token、11 条消息。中间那 31 条被摘要成了一条高密度文本,而头 3 条和尾 6 条原样保留。

如果你用 TaoToken 的模型对话页面做对照测试,可以把压缩前后的消息分别贴进去,观察模型对历史问题的回答质量。压缩后的版本如果依然能准确引用关键决策,说明摘要质量合格。

5. 常见报错排查:401、local proxy failed、reading choices、OAuth

配置和验证过程中最容易撞上四类报错,逐个说清楚原因和解法。

401 Unauthorized。这是认证失败,九成是 Key 或 Base URL 的问题。先确认TAOTOKEN_API_KEY环境变量在当前 shell 里真的存在:echo $TAOTOKEN_API_KEY。如果为空,说明 export 没生效或者写在了别的 shell 配置里。然后确认base_url填的是https://taotoken.net/api,不要多加斜杠或路径。最后确认model字段的 ID 在模型列表里真实存在,拼错模型名有时也会返回 401 而非 404。

local proxy failed。这个报错通常出现在 Hermes 尝试通过本地代理转发请求时。检查你的环境变量里有没有残留的HTTP_PROXY或HTTPS_PROXY设置,如果有,先unset掉再启动。Hermes 的 HTTP 客户端会读取系统代理配置,一个失效的代理地址会导致连接直接失败。另外确认config.yaml里没有配置proxy字段,除非你确实有本地转发服务在跑。

Error reading choices。这个报错说明请求发出去了,返回体也收到了,但解析响应时找不到choices字段。常见原因是模型返回了错误结构,比如被网关拦截返回了 HTML 错误页。先看完整响应体:在run_agent.py的 API 调用处打印response.text。如果返回的是 HTML,说明 Base URL 或路径不对,请求没打到正确的 API 端点。如果返回的是 JSON 但结构不同,检查你用的模型是否兼容 OpenAI 的 chat completions 格式。

OAuth 相关报错。如果你在配置里启用了需要 OAuth 的模型提供商,但没完成授权流程,会看到 token 过期或 refresh 失败的提示。Hermes 的auth.json里存的是 OAuth token,路径通常在~/.hermes/auth.json。检查这个文件是否存在且未过期。如果用的是 TaoToken 的 API Key 模式,就不需要 OAuth,把配置里的 OAuth 相关字段清空即可。

排查顺序建议:先看日志里完整的错误堆栈,定位是网络层、认证层还是解析层;再用 curl 手动打一次 API,排除 Hermes 自身的问题;最后对照配置逐项检查三件套(Base URL + Key + Model ID)。大部分报错在第二步就能定位。

6. 把记忆分层用起来:从配置到日常编码工作流

配置跑通之后,真正的价值在于日常使用。五层记忆不是摆设,它改变的是你和 Agent 协作的方式。

第一层短期记忆的"头尾保护"意味着你不需要手动清理对话。当 token 逼近阈值,压缩器自动把中间的工具调用和报错修复过程摘要成一段高密度文本,头部的任务定义和尾部的当前语境原样保留。你继续对话就行,血缘链在后台维护。

第二层长期偏好用冻结快照,这个设计要理解清楚:同一个会话内改MEMORY.md不会立即生效,因为 System Prompt 用的是初始化时的快照。但 Gateway 模式每轮新建实例,所以每轮都能读到最新文件。如果你在 CLI 里改完偏好发现没生效,开个新会话即可。

第三层 FTS5 检索是你主动用的。当 Agent 说"我不记得之前怎么配的",你可以直接查数据库:

SELECT content FROM messages_fts WHERE messages_fts MATCH '"nginx config"' ORDER BY rank LIMIT 3;

ORDER BY rank会按 FTS5 的相关性排序,比按时间排更准。找到原始消息后,把内容贴回对话,Agent 就能接着往下做。

第四层技能库的用法是把重复操作固化成SKILL.md。比如你每次部署都要跑一套固定的构建命令,就写一个技能文件,Agent 扫描后会在需要时自动调用。技能目录的 mtime 变化会触发重新扫描,改完文件不用重启。

第五层用户建模目前通过插件实现,适合需要跨会话积累用户画像的场景。如果你只是本地编码助手,前四层已经够用。

日常编码时,我建议把主推理模型设强一点,压缩摘要模型设便宜一点。压缩是高频小请求,用便宜模型能显著降低成本,而摘要质量对最终效果的影响远小于主推理。TaoToken 的模型列表里可以按价格和上下文长度筛选,选一个性价比合适的做摘要。

最后提醒一点:FTS5 索引会随消息增长而变大,10 万条消息大约 50MB,对本地磁盘不是问题。但记得定期跑PRAGMA wal_checkpoint(PASSIVE);或者依赖 Hermes 每 50 次写入的自动 checkpoint,防止 WAL 文件无限膨胀。数据库文件在~/.hermes/hermes.db,备份直接复制这个文件即可,血缘链和索引都在里面。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询