跳转至

Hermes 双记忆库并行架构与迁移 SOP

本文目标:解答一个高频疑问——明明把 memory.provider 设成了 openviking,为什么内置记忆库还会满? 并给出官方源码层面的机制考证,以及一套「顶格时做减法而非调限额」的迁移 SOP。

典型误区:以为 provider 是「替换」内置记忆系统,于是满了就调大限额——结果是每个会话的系统提示词固定膨胀,全部上下文为冷门事实买单。

一、根因:provider 是叠加层,不是替换层

翻 hermes-agent 源码(agent/agent_init.py_init_memory),注释写得很直白:

External memory provider plugin (one at a time, alongside built-in)

即 Hermes 实际是两套记忆并行

系统 内容 注入方式 限额
内置记忆库 MEMORY.md(agent notes)+ USER.md(用户画像) 每个会话启动时作为冻结快照全量注入系统提示词 memory_char_limit + user_char_limit
外部 provider(openviking) 知识库注入块 + 语义检索召回(viking_search 按需) 独立注入块,不占内置限额 无对应限额

所以「记忆库满」永远指内置库顶格,与 OpenViking 是否在位毫无关系。官方默认限额 2200/1375 字符(源码注释:约 800 tokens @ 2.75 chars/token),可在 config.yamlmemory 段覆写。

顶格时系统提示词里会标注如 99% — 2992/3000 chars,同时写入会被拒绝,逼 Agent 做「先删后加」的整理。

二、为什么不建议无脑调大限额

内置库的每一字符都是固定常驻成本

每会话成本 ≈ (MEMORY + USER 字符数) × 每字符 token 折算率,随每一轮请求重发
  • 调到 6000/4000,等于给所有会话固定加上约 3600 tokens 底座——哪怕这个会话根本用不到里面 90% 的内容;
  • 会话越长、并发子代理越多,这笔底座被重复支付的次数越多;
  • 而其中大部分内容(项目运维细节、历史排障记录)其实是「低频可检索」的,并不需要每轮都在场。

正确形态是分层:高频必带事实留内置,低频细节进检索层。

2.1 折算率取决于语言,且各厂商分词器不同(2026-09-09 实测修正)

早期文档常用 2.75 chars/token 这一粗略估算,但它既没区分语言、也没区分厂商。实测结果如下:

分词器 中文 英文 中文/英文倍率
G​eminigemini-3.8-flash,countTokens 实测) ≈ 0.59 token/字 ≈ 0.29 token/字符 1.10x
O​penAI o200k_base 1.34x
O​penAI cl100k_base(旧) 2.08x

厂商不可外推:G​emini 1.10x 与 O​penAI 1.34x 相差约 22%。用 tiktoken 估算 G​emini 开销会高估约 20%,务必用厂商自己的计数接口(零配额测法见《提示词成本实测:字符限额与 token 账单的错位陷阱》)。

2.2 由此产生的陷阱:限额按字符,账单按 token

配置里的 memory_char_limit 单位是字符,而计费单位是 token。中文省字符却费 token,英文省 token 却费字符(约需 2.5 倍字符表达同等信息)。

直接后果:把满额中文库整体译成英文,字符数会暴涨约 45% 而直接撑爆限额,写入被拒。

正确顺序是「先减法、后翻译」

分类(高频必带 vs 低频可检索)→ 迁移低频进检索层 → 压缩约 60% → 译英 → 实测字符数校验

2.3 收益要按真值重算

本机 2026-09-09 落地(三文件全量译英 + 压缩):

文件 token 变化 字符占用
MEMORY.md 1525 → 740 2992 / 3000
USER.md 1079 → 464 1996 / 2000

若按 o200k 的 1.34x 估算会得出「省 1426 token/轮」,但按 G​emini 真值 1.10x,实际只省约 200 token/轮(相对每轮约 21.7k 底座约 0.9%)。

所以换语言的真正理由是腾出限额空间(同样预算可容纳更多事实),而非省下那点 token。

2.4 缓存前缀:别为小钱反复改 SOUL.md

SOUL.md 位于系统提示词 cache 前缀最头部,任何改动都会使其后整段前缀失效一次,属一次性重算成本。为省几十 token 反复改它是净亏的——改一次,改对,然后别动

三、分层准则

去向 内容类型 判断标准
内置库(高频必带) 身份与环境拓扑、铁律与拍板决定、工作偏好、正在活跃使用的项目要点 「每个会话开头就该直接知道,检索会打断工作流」
OpenViking(低频可检索) 运维细节、排障全过程、变更历史、版本号/路径/配置值、一次性结论 「用到时搜一下就够了」

一个实用的自检问题:这条事实如果不在场,我会先想到去搜它吗? 会搜 → 检索层;必须直接看见 → 内置库。

四、迁移 SOP(防丢失顺序是关键)

顺序错了会把记忆弄丢,严格按以下四步:

  1. 先迁后删:将低频条目逐条 viking_remember 提交到 OpenViking,确认全部返回 accepted;
  2. 抽验召回:用 viking_search 以不同关键词抽查 2~4 条,确认能召回且内容完整。OpenViking 的记忆抽取是异步的,且可能对来源做合并/改写,不抽验就删内置条目存在丢失风险;
  3. 删内置:用 memory 工具的 batch operations(remove + 对保留条目的 replace 压缩)一次调用完成,避免多次往返;
  4. 收尾:确认双库占用百分比回落,重要定案(如本次架构决定)在内置库留一条精简指针。

五、实测数据(2026-09-07 首轮迁移)

  • 迁移 7 条低频事实:反代运维细节、浏览器扩展治理终局、配额插件架构、TG 频道运营参数、跨端测试规范、桌面 token 双口径、架构杂项归并;
  • 抽验 4 条全部高分召回(如 TG 频道实体条目 0.747、浏览器治理事件文档 0.74);
  • 内置库占用:MEMORY.md 99% → 49%USER.md 99% → 77%
  • 迁移后在内置库新增一条「记忆架构」条目,防止未来会话重新踩「满了就调限额」的坑。

六、速查表

场景 正确动作
内置库顶格(99%) 按分层准则做减法迁移,不是调限额
新增一条事实 默认先想 OpenViking;确认高频必带才进内置
viking_remember 后删内置 必须先 viking_search 抽验召回成功
要调限额 仅当高频必带事实本身超限时;同时评估每会话固定 token 成本
怀疑记忆丢了 viking_search 多关键词交叉检索;OpenViking 抽取可能改写措辞,搜关键词而非原文