Hermes Agent:记忆模块 & Skill 自动收集原理(完整版)
不用向量数据库,纯文本文件 + FTS5 全文检索。冻结注入保 Prefix Cache,fork 子 Agent 做后台 Skill Review。每个设计决策背后都是明确的工程取舍。
一、为什么不使用向量检索?
1.1 主流方案的痛点
大多数 AI Agent 的记忆系统会用向量数据库(如 Pinecone、Chroma、Milvus)做语义检索:
用户输入 → 文本向量化 → 向量数据库 → 相似度 TopK 检索 → 召回相关记忆 → 注入 context
这套方案的问题:
- 系统复杂度:需要维护向量数据库,增加部署成本
- 调试困难:为什么召回这一条而不是那一条?黑盒不好解释
- 过度设计:个人使用的 Agent,记忆条目通常只有几十到几百条
1.2 Hermes 的选择
Hermes 使用纯文本文件 + FTS5 全文检索:
- 简单透明:用户可以直接打开 MEMORY.md 编辑
- 足够好用:个人规模下,关键词检索配合合理上限,体验不差
- 无外部依赖:不需要向量数据库,开箱即用
二、记忆模块详解
2.1 两层架构
记忆调度中心 MemoryManager
├── 内置记忆 BuiltinMemoryProvider
│ ├── MEMORY.md — Agent 笔记,上限 2200 字符
│ └── USER.md — 用户画像,上限 1375 字符
└── 外部插件 MemoryProvider(最多挂载一个)
└── Honcho / Mem0 / Holographic / ...
为什么只能挂载一个外部 provider? 避免工具命名冲突、简化调度逻辑、减少 token 开销。
2.2 字符上限的设计逻辑
| 文件 | 上限 | 用途 | 设计理由 |
|---|---|---|---|
| MEMORY.md | 2200 字符 | Agent 的环境/项目笔记 | 防止注意力稀释,强迫精炼 |
| USER.md | 1375 字符 | 用户画像、偏好 | 精简的用户快照,快速加载 |
注意力稀释问题:
正常情况下,2000 tokens 的记忆内容中,每条约占 0.26% 的注意力权重。膨胀到 5000 tokens 后,每条只剩 0.02%。上限就是一道硬约束,迫使 Agent 只保留最有价值的信息。
2.3 冻结注入 & Prefix Cache
这是 Hermes 记忆系统最核心的设计决策。
什么是 Prefix Cache?
LLM API(如 Claude、GPT)内部使用 KV Cache 加速:
第一次调用:
Input: [system prompt] + [user message]
→ 遍历所有 token 计算 KV 矩阵(耗时)
→ 保存 KV Cache
后续调用(system prompt 不变):
Input: [system prompt] + [新的 user message]
→ 直接命中 KV Cache ⚡
→ 省 80%+ 计算时间
关键:只要 system prompt 变化,整个 KV Cache 就失效,必须重新计算。
Hermes 的策略
- Session 开始:读取 MEMORY.md 完整内容,制作”快照”,存入 system prompt,首次 API 调用建立 KV Cache
- 对话进行中:system prompt 不变,每次 API 调用命中 KV Cache
- mid-session 写入:调用 memory 工具写入新内容,文件已更新,但 system prompt 里的快照不变
- 下个 Session:重新读取(包含上次写入的内容),制作新快照,重建 KV Cache
取舍:
- ✅ 好处:保住了 prefix cache,大幅降低 API 延迟和成本
- ❌ 代价:mid-session 写入的记忆,本轮对话感知不到
2.4 mid-session 写入的两种场景
| 场景 | 本轮能感知吗? | 原因 |
|---|---|---|
| 用户明确说出”记住我喜欢用 pnpm” | ✅ 能 | 这句话在 messages 历史里,LLM 能看到 |
| Agent 自己推断并写入 MEMORY.md | ❌ 不能 | system prompt 快照没更新 |
示例:
本轮 Session:
用户: "帮我配置项目"
Agent: (推断出用户喜欢 pnpm,写入文件)
文件已更新 ✓
但 system prompt 快照未变
用户: "用什么包管理器?"
Agent: "根据你的项目,推荐 npm" ← ❌ 没记住!
下个 Session:
Agent 重新读取(包含 pnpm 偏好),新快照注入 system prompt
用户: "用什么包管理器?"
Agent: "你喜欢用 pnpm" ← ✅ 记住了!
这是一个明确的工程取舍:性能优先,延迟生效。
三、Skill 自动收集:闭环学习系统
3.1 设计目标
让 Agent 能从每次任务中自动提炼可复用的知识,形成正向循环:
完成任务 → 后台 Review → 提取 Skill → 下次复用 → 更高效完成任务 → ...
3.2 为什么 fork 子 Agent?
不直接让主 Agent 存 skill,有三个原因:
| 主 Agent 直接处理 | fork 子 Agent | |
|---|---|---|
| 响应速度 | ❌ 慢,用户要等 | ✅ 快,后台静默 |
| 上下文污染 | ❌ review 思考污染主对话 | ✅ 独立不污染 |
| 判断客观性 | ❌ 带任务惯性 | ✅ 冷启动旁观者 |
3.3 nudge 触发完整时序
- 复杂任务执行:每次工具调用计数器 +1
- 计数器达阈值(默认 10 次工具调用后),标记
_should_review = True,计数器归零 - 返回最终回答给用户(用户已看到结果)
- 后台 fork 子 Agent:传入对话快照,子 Agent 冷启动执行 Review Prompt
- Review 判断:
- 值得泛化 →
skill_manage(action='create')→ 通知用户 Skill created - 已有 skill 需更新 →
skill_manage(action='patch')→ 通知用户 Skill updated - 不值得 → 静默跳过
- 值得泛化 →
3.4 Review 的判断标准
子 Agent 会审视对话,问自己:
- 有试错过程吗? 不是一帆风顺,而是踩了坑、调整了方案
- 有思路转变吗? 实际做法和用户最初预期不一样
- 用户有特别偏好吗? 比如在多种方案中选了某一种
- 下次还用得上吗? 这个场景会重复出现
触发创建/更新的条件:
- 任一问的回答是”是”,且没有现成 skill 覆盖 → 创建新 skill
- 有类似 skill,但内容需要补充 → patch 更新
3.5 Skill 文件结构
~/.hermes/skills/
├── my-skill/
│ ├── SKILL.md ← 核心文件
│ ├── references/ ← 参考资料
│ ├── templates/ ← 模板文件
│ ├── scripts/ ← 辅助脚本
│ └── assets/ ← 图片等资源
└── category-name/
└── another-skill/
└── SKILL.md
SKILL.md 格式:
---
name: k8s-persistent-storage
description: Configure persistent volumes in Kubernetes
platforms: [linux, macos]
metadata:
hermes:
requires_toolsets: [terminal, web]
config:
- key: k8s.namespace
description: Default namespace
default: "default"
---
# 触发条件
When setting up persistent storage in Kubernetes...
# 步骤
1. Check existing storage classes: `kubectl get sc`
2. ...
# 常见问题
- PVC stuck in Pending → check storage class3.6 Skill 的安全机制
skill_manage 工具有多层防护:
- 命名校验:小写字母、数字、连字符、下划线,最长 64 字符
- 大小限制:SKILL.md 最大 100KB,附件最大 1MB
- 安全扫描:检查注入攻击、数据外泄等恶意模式
- 回滚机制:扫描失败时自动撤销更改
- 只读保护:外部目录(external_dirs)的 skill 不能被修改
四、记忆 vs Skill:本质区别
| 维度 | 记忆(MEMORY.md) | Skill(SKILL.md) |
|---|---|---|
| 存储内容 | 事实性信息 | 过程性知识 |
| 回答的问题 | ”世界是什么样的?" | "下次怎么干?“ |
| 典型内容 | 用户偏好、环境配置、项目状态 | 操作步骤、避坑指南、最佳实践 |
| 触发写入 | 手动 memory 工具 / nudge | 手动 skill_manage / nudge |
| 生效时机 | 下个 session | 下个 session(重建索引后) |
| 更新方式 | 追加、替换、删除条目 | create / edit / patch 操作 |
| 组织形式 | 两段式(Agent 笔记 + 用户画像) | 分类目录 + 独立 skill 包 |
区别:
- 记忆 = 是什么(事实)
- Skill = 怎么做(过程)
五、关键设计决策总结
| 决策 | 取舍 | 原因 |
|---|---|---|
| 冻结注入 | 性能优先,延迟生效 | 保住 prefix cache,省成本 |
| 字符上限 | 精炼优先,放弃完整 | 防注意力稀释,强迫高质量记录 |
| fork 子 Agent | 体验优先,增加复杂度 | 后台 review 不卡用户 |
| 纯文本 vs 向量 | 简单优先,放弃语义检索 | 个人规模够用,可维护性好 |
| 单外部 provider | 简化优先,放弃灵活 | 避免冲突,降低调度复杂度 |
每个设计决策都不是”最优解”,而是在特定约束下的合理取舍。理解这些取舍,比记住实现细节更重要。