【系列:手搓自主 AI Agent:Hermes 架构原理剖析 · 第 6 篇】

记忆与技能:让 Agent 跨会话变聪明、把经验沉淀成文件

导读: 用户第三次纠正同一个错误,你的 Agent 依然"失忆"。这是所有自主 Agent 的痛点:跨会话记忆缺失。本文拆解 Hermes 架构的 s07 记忆系统与 s08 技能系统------记忆解决"跨会话记住",技能解决"把做法沉淀成文件"。读完你将掌握:该存什么、不该存什么、如何用 2200 字符上限 + 冻结快照 + 后台审查机制,让 Agent 真正越用越聪明。

为什么你的 Agent 每次都像第一次合作

假设你正在用 Agent 写代码。第一次,你告诉它"这个项目用 pytest"。第二次,你纠正它"别用 mock,写真实集成测试"。第三次,它又用 mock 了。第四次,你差点把电脑砸了。

不是 Agent 笨,是它真的不记得。

每次新会话,系统 prompt 从头组装,上下文归零。用户偏好、纠正过的错误、项目约定、外部资源位置,全部丢失。

阶段 1 的 Hermes 已经能跑:循环、工具、持久化、压缩、容错。现在进入阶段 2(智能层),先解决记忆与技能。

记忆的边界:不是什么都存

先说最重要的原则。memory 不是"把一切有用信息都记下来"。

只有那些跨会话仍然有价值,而且不能轻易从当前项目状态直接推出来的信息,才适合进入 memory。否则 memory 变成垃圾堆,Agent 开始依赖过时记忆。

这个原则必须最先立住。很多初学者把 memory 当垃圾桶,什么都往里扔,结果 Agent 被自己的记忆污染。

两个文件:MEMORY.mdUSER.md

s07 的真实代码里,记忆系统拆成两个文件:

python 复制代码
MEMORY_DIR = HERMES_HOME / "memories"
MEMORY_FILE = MEMORY_DIR / "MEMORY.md"            # 通用知识(项目/环境)
USER_FILE = MEMORY_DIR / "USER.md"                # 用户画像
ENTRY_SEP = "\n\n§\n\n"                      # 用罕见的 § 做分隔符
MEMORY_CHAR_LIMIT = 2200                          # 字符上限
USER_CHAR_LIMIT = 1375

MEMORY.md 是 Agent 的工作笔记本,记"这个项目/这个环境是怎么回事"------项目用 pytest、机器是 macOS ARM、auth 重写是合规要求。

USER.md 是用户画像,记"这个人是谁、怎么和他合作最顺"------偏好 tabs、讨厌 mock、回答要简洁。

分隔符用 § 而不是常见的 ---。为什么?因为 --- 可能出现在正文里,而 § 几乎不会。这是细节,但决定了解析的稳定性。

字符限制 2200/1375 不是随便定的。memory 会进每次会话的 system prompt,无限增长会吃掉上下文窗口。空间不够时,Agent 自己决定删哪条------FIFO pop 末尾。

判断存哪里的规则很简单:问"如果换了一个完全不同的项目,这条信息还有用吗?" 有用 → USER.md(描述用户);没用 → MEMORY.md(描述当前环境)。

边界案例:"用户讨厌 mock 测试"存 USER.md。这是用户偏好,换项目依然成立。

该存 vs 不该存:一张清单

该存:

  • 用户偏好("回答要简洁")
  • 用户纠正("别用 mock")
  • 项目约定("commit 前必须跑 lint")
  • 外部资源位置("部署文档在 wiki 的 deploy 页")

不该存:

  • 文件结构/函数签名------可重读代码
  • 当前任务进度------这是 task/plan 的职责
  • 当前分支/PR 号------很快过时
  • 修 bug 的具体代码------代码和提交记录才准确
  • 密钥密码------安全红线

这条边界很关键。很多人把"当前任务进度"存进 memory,结果 Agent 在新会话里拿着过时的进度继续干,越干越偏。

最小实现:四个动作搞定记忆

s07 的核心实现非常克制,只有四个函数加上一个 action 分发:

python 复制代码
parse_entries / render_entries / load_memory / save_memory

handle_memory 负责 action 分发,支持 add / remove / read 三个操作,target 区分 memory 和 user。

真实行为:

  • read=== MEMORY (N entries) === + 条目列表
  • add → 追加条目,超限自动 pop 末尾 + 返回 "Trimmed to N entries to stay within X char limit"
  • remove → 关键字(不区分大小写)匹配删除,返回删除数量

没有向量检索,没有评分机制。一个文件、一个分隔符、一个字符上限,就解决了跨会话记忆。

冻结快照:写入即时,生效延迟

这是 Hermes 最容易被误解的设计。

会话开始时,系统把 MEMORY.md / USER.md 读进来,冻结成 system prompt 的一部分。会话中间修改会立刻写磁盘,但不会改变当前会话的 system prompt。

为什么?因为改了会破坏 Anthropic prompt cache。

prompt cache 是性能命脉。system prompt 只要变一个字符,缓存就失效,每次请求都要重新处理全部 token。所以 Hermes 选择:写入即时、生效延迟。新 memory 下次会话才生效。

这不是 bug,是设计。很多初学者在会话中间改了 memory,期望立刻生效,发现没反应就以为系统坏了。

两套触发机制:nudge 与 flush

Agent 不会主动记忆,需要触发。Hermes 设计了两套机制:

1. 定期后台审查(nudge)

_memory_nudge_interval = 10,每 10 轮用户对话触发。响应已返回用户后,后台线程创建独立"审查 agent"(max_iterations=8、quiet_mode=True、共享 memory store),用 MEMORY_REVIEW_PROMPT 扫描对话:"用户透露了关于自己的事吗?表达了对我行为的期望吗?"

这个设计的好处:不竞争主任务注意力,用户完全无感。

2. 压缩前紧急冲刷(flush)

_memory_flush_min_turns = 6,对话即将压缩/会话重置前,直接在当前对话注入一条消息:"会话即将压缩,保存值得记住的内容------优先用户偏好、纠正、重复模式"。

模型调用 memory 工具保存后,flush 消息从历史删除,不留痕迹。

两套机制配合:平时后台默默审查,关键时刻强制冲刷。保证记忆不遗漏,也不打扰主流程。

另外,多会话同时写 MEMORY.md 时用 fcntl.flock 文件锁保证原子性。外部记忆提供者(向量数据库等)通过 plugin 接入,内容不进 system prompt(破坏缓存),注入 user message。

过渡:知道什么 ≠ 怎么做

memory 解决了"知道什么",但存不了"怎么做"。

代码审查要查哪些项?数据分析怎么处理缺失值?这类程序性知识("做这类任务应该按什么步骤?有哪些注意事项?")塞进 system prompt 会臃肿,不保存每次从零摸索。

这就是技能系统 s08 要解决的问题。

skill vs tool vs memory:三张卡

维度 tool skill memory
本质 硬编码能力 Markdown 文件 声明性事实
谁写 开发者 Agent 自己 Agent
改法 写代码 编辑文件 add/remove
存什么 可执行函数 一套做法 一条事实

一句话:一条事实 → memory;一套做法 → skill。

tool 是硬编码在 Python 里的能力,加新工具要写代码。skill 是 markdown 文件(SKILL.md),Agent 自己可以创建/编辑/删除,不需要改代码。

SKILL.md 格式与目录结构

SKILL.md 用 frontmatter 开头:

markdown 复制代码
---
name: code-review
description: 执行一次完整的代码审查,检查安全、性能、可维护性
version: 1.0.0
---

## 审查流程
1. 先看安全漏洞
2. 再查性能瓶颈
3. 最后检查可维护性
...

目录结构:~/.hermes/skills/<name>/SKILL.md,可带 references/templates/scripts/assets 子目录。

渐进展示:三层加载

技能系统最核心的设计是渐进展示(progressive disclosure),分三层:

目录层 :只显示 name+description,几十个技能几百 token,平时就在 system prompt 里。

正文层 :模型需要时 skill_view 加载 SKILL.md 全文。

附件层:参考文件/模板/脚本,需要时再加载。

平时 system prompt 只放目录。

这意味着什么?20 个技能,每个技能正文 2000 字,如果全塞 system prompt 就是几万 token。渐进展示把常驻成本压到几百 token,其余按需加载。

最小实现:三个函数

python 复制代码
_parse_frontmatter:解析 --- 分隔的 key:value 元数据
discover_skills:只读 frontmatter 不读 body,返回 name/description/path
handle_skill_view:按名加载 SKILL.md 全文
handle_skill_manage:create / edit / delete

discover_skills 只读 frontmatter 不读 body,因为目录扫描要便宜。create 时如果已存在则报错提示用 edit。

稳定层 + 按需层:为什么技能正文不进 system prompt

系统输入分两层:

稳定层 :身份/记忆/项目规则/工具定义/技能目录。每轮都在,决定 prompt cache 命中率。

按需层:技能正文/附件。进 tool_result,不进 system prompt。

技能正文不塞 system prompt 的原因和冻结快照一样:稳定层越固定,缓存越有效。

这是一以贯之的设计哲学:凡是能被缓存的东西尽量稳定,凡是需要变化的东西尽量走 tool_result。

Hermes 独特:Agent 自己创建技能

大多数框架的 skill 是只读的,开发者定义好,Agent 只能调用。Hermes 反其道而行:Agent 自己可以创建、编辑、删除技能。

工作流变成:"上次这样做效果好 → 存成技能 → 下次直接用。"

但 Agent 创建的技能和 Hub 安装的一样,要经过安全检查------扫描可疑命令注入、恶意指令。同名时用户技能优先。

技能改进也有 nudge:每隔一定轮数提醒审视是否有技能值得创建/改进。nudge 不是自动操作,只是提醒。

避坑:两章 10 错精选

记忆系统的 5 个坑:

  1. 把代码结构存进 memory------可重读代码,别存
  2. 把任务进度存进 memory------这是 task/plan 的职责
  3. 把 memory 当绝对真相------可能过时,优先相信眼前真实状态
  4. 不设字符限制------无限增长吃掉上下文窗口
  5. 会话中间改了 memory 期望立刻生效------冻结快照是设计不是 bug

技能系统的 5 个坑:

  1. 所有 skill 正文永远塞 system prompt------20 个技能几万 token
  2. 把 skill 和 memory 混成一类------怎么做 vs 知道什么
  3. Agent 创建的技能没有安全检查------这是安全红线
  4. 技能目录描述太弱------模型不知道何时加载
  5. 把 skill 当绝对规则------是推荐做法不是铁律

小结与预告

记忆系统和技能系统,让 Agent 从"能跑"走向"聪明"。

记忆解决"跨会话记住":两个文件、一个分隔符、字符上限,加上冻结快照和两套触发机制,让 Agent 越用越懂你。

技能解决"把做法沉淀成文件":渐进展示三层、稳定层加按需层、Agent 自主创建,让经验不流失。

这两个系统的设计逻辑是相通的:能缓存的尽量固定,能按需的绝不常驻。

下一篇(第 7 篇):安全审批与子 Agent 委派。当 Agent 能自己创建技能、自己改记忆,安全问题就浮出水面------怎么防止 Agent 被恶意指令劫持?怎么让子 Agent 安全地执行委派任务?


如果你正在构建自己的 Agent,最想先解决哪个问题------跨会话记忆,还是经验沉淀?欢迎在评论区聊聊你的方案。

参考文献

  • Hermes Agent 教学仓库:agents/s07_memory_system.py(本文代码素材,25116 字节真实可运行)
  • Hermes Agent 教学仓库:agents/s08_skill_system.py(本文代码素材,29756 字节真实可运行)
  • Hermes Agent 教学仓库:docs/zh/s07-memory-system.mddocs/zh/s08-skill-system.md(冻结快照、nudge/flush、渐进展示详解)

📥 源码获取 :如需本系列全部源码,请在以下链接克隆:

https://gitcode.com/ganxin7932508/learn-hermes-agent.git

相关推荐
卷无止境7 分钟前
在 awesome-fastapi 里,哪些库值得一看?
后端·python
zhanghaha131430 分钟前
Python进阶教程:6_JSON 数据解析 —— 新手完全指南
开发语言·python·json
Python私教42 分钟前
API 输出模型怎么设计:从 model_dump() 到显式展示层
python·fastapi
Python私教1 小时前
本地 AI 工具服务该绑定 127.0.0.1 还是 0.0.0.0?
python·fastapi
卷无止境1 小时前
FastAPI 的Admin面板生态
后端·python
小田学Python2 小时前
重新定义 Agent:为什么大模型不能直接干活,需要一层“壳”
大模型·api·ai agent
ctlover2 小时前
Streamlit 框架
python
ι:2 小时前
MATLAB 与 Python 搭建无人机地面站:优势、劣势与选型逻辑
python·matlab·无人机
船厂电气自动化ai大模型2 小时前
AI大模型与数学 第32课 函数凹凸性与二阶导数:拐点求解、凹凸区间计算(10道二阶导数计算题)
数据结构·人工智能·python·深度学习·算法
jufeng13073 小时前
【系列:手搓自主 AI Agent:Hermes 架构原理剖析 · 第 7 篇】
python·ai agent·权限系统