【AI】Hermes Agent 提示词优化与闭环机制

Hermes 的长期记忆不是「全量保存一切对话」,而是 分层存储 + 主动策展 + 容量硬限制 。会遗忘、会遗漏细节,而且 很多细节是刻意不记的


一、记忆体系总览(四层)

text 复制代码
┌─────────────────────────────────────────────────────────────────┐
│ Layer 1: 内置策展记忆 (MEMORY.md + USER.md)                      │
│   ~2200 + ~1375 字符,每 session 冻结注入 system prompt           │
├─────────────────────────────────────────────────────────────────┤
│ Layer 2: 会话全量存储 (SQLite + FTS5, state.db)                  │
│   所有对话原文保留,按需 session_search 检索(不占固定 prompt)    │
├─────────────────────────────────────────────────────────────────┤
│ Layer 3: 程序性记忆 (Skills, SKILL.md)                           │
│   工作流/方法论,体量大,按需 skill_view 加载                      │
├─────────────────────────────────────────────────────────────────┤
│ Layer 4: 外部记忆 Provider (Honcho/Mem0/Hindsight 等,最多 1 个) │
│   语义检索、用户建模、自动抽取,与内置记忆并行                       │
└─────────────────────────────────────────────────────────────────┘
层级 存储位置 容量 注入方式 典型内容
MEMORY.md ~/.hermes/memories/ ~2200 字符 每 session 开头 frozen snapshot 环境、项目惯例、工具坑
USER.md 同上 ~1375 字符 同上 用户偏好、沟通风格
Session DB ~/.hermes/state.db 无上限 session_search 按需 完整历史对话
Skills ~/.hermes/skills/ 无硬限 索引在 prompt,正文按需加载 复杂工作流
External Provider 云端/本地 DB 依 provider prefetch → user message 语义相关回忆

二、长期记忆如何实现?

1. 写入路径(四条)

路径 A:Agent 主动写(foreground)

模型在对话中调用 memory 工具:

text 复制代码
memory(action="add", target="user", content="用户偏好 TypeScript")
memory(action="replace", target="memory", old_text="...", content="...")
memory(action="remove", target="memory", old_text="...")

写入后立即落盘,但 不会更新当前 session 的 system prompt(见下文 frozen snapshot)。

路径 B:后台自改进 review(background)

10 个 user turn (默认 memory.nudge_interval: 10)触发一次后台 review:

160:168:hermes-agent/agent/background_review.py 复制代码
_MEMORY_REVIEW_PROMPT = (
    "Review the conversation above and consider saving to memory if appropriate.\n\n"
    "Focus on:\n"
    "1. Has the user revealed things about themselves --- their persona, desires, "
    "preferences, or personal details worth remembering?\n"
    "2. Has the user expressed expectations about how you should behave, their work "
    "style, or ways they want you to operate?\n\n"
    "If something stands out, save it using the memory tool. "
    "If nothing is worth saving, just say 'Nothing to save.' and stop."
)

Fork 一个子 Agent,白名单只有 memory + skill_manage,主对话结束后异步跑,不抢用户任务的注意力。

路径 C:外部 Provider 自动同步

每 turn 结束后(非 interrupted):

text 复制代码
memory_manager.sync_all(user_msg, assistant_response)      # 写入 provider
memory_manager.queue_prefetch_all(user_msg)                # 为下轮预热检索

Session 结束或压缩前还会 commit_memory_session()on_session_end() 做抽取。

路径 D:上下文压缩前的抢救

压缩丢弃中间消息前,调用 on_pre_compress(messages),让 provider 把即将丢失的内容提炼进外部记忆或压缩摘要。


2. 读取路径(三条)

读取 A:Session 启动时 frozen snapshot

text 复制代码
load_from_disk() → 捕获 _system_prompt_snapshot → 注入 volatile tier

整个 session 内 字节级不变,保证 LLM prefix cache 稳定。

读取 B:每 turn 的 prefetch

text 复制代码
memory_manager.prefetch_all(user_query) → 追加到当前 user message(非 system prompt)

Honcho 等 provider 做语义召回,只注入与当前 query 相关的片段。

读取 C:按需 session_search

Agent 主动搜历史:

text 复制代码
session_search(query="auth refactor", limit=3)
session_search(session_id="...", around_message_id=12345)

FTS5 全文检索,不经过 LLM 摘要,返回原始消息。


3. 生命周期示意

#mermaid-svg-Cb1VJjddD4FDVVHe{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Cb1VJjddD4FDVVHe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Cb1VJjddD4FDVVHe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Cb1VJjddD4FDVVHe .error-icon{fill:#552222;}#mermaid-svg-Cb1VJjddD4FDVVHe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Cb1VJjddD4FDVVHe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Cb1VJjddD4FDVVHe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Cb1VJjddD4FDVVHe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Cb1VJjddD4FDVVHe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Cb1VJjddD4FDVVHe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Cb1VJjddD4FDVVHe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Cb1VJjddD4FDVVHe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Cb1VJjddD4FDVVHe .marker.cross{stroke:#333333;}#mermaid-svg-Cb1VJjddD4FDVVHe svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Cb1VJjddD4FDVVHe p{margin:0;}#mermaid-svg-Cb1VJjddD4FDVVHe .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Cb1VJjddD4FDVVHe .cluster-label text{fill:#333;}#mermaid-svg-Cb1VJjddD4FDVVHe .cluster-label span{color:#333;}#mermaid-svg-Cb1VJjddD4FDVVHe .cluster-label span p{background-color:transparent;}#mermaid-svg-Cb1VJjddD4FDVVHe .label text,#mermaid-svg-Cb1VJjddD4FDVVHe span{fill:#333;color:#333;}#mermaid-svg-Cb1VJjddD4FDVVHe .node rect,#mermaid-svg-Cb1VJjddD4FDVVHe .node circle,#mermaid-svg-Cb1VJjddD4FDVVHe .node ellipse,#mermaid-svg-Cb1VJjddD4FDVVHe .node polygon,#mermaid-svg-Cb1VJjddD4FDVVHe .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Cb1VJjddD4FDVVHe .rough-node .label text,#mermaid-svg-Cb1VJjddD4FDVVHe .node .label text,#mermaid-svg-Cb1VJjddD4FDVVHe .image-shape .label,#mermaid-svg-Cb1VJjddD4FDVVHe .icon-shape .label{text-anchor:middle;}#mermaid-svg-Cb1VJjddD4FDVVHe .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Cb1VJjddD4FDVVHe .rough-node .label,#mermaid-svg-Cb1VJjddD4FDVVHe .node .label,#mermaid-svg-Cb1VJjddD4FDVVHe .image-shape .label,#mermaid-svg-Cb1VJjddD4FDVVHe .icon-shape .label{text-align:center;}#mermaid-svg-Cb1VJjddD4FDVVHe .node.clickable{cursor:pointer;}#mermaid-svg-Cb1VJjddD4FDVVHe .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Cb1VJjddD4FDVVHe .arrowheadPath{fill:#333333;}#mermaid-svg-Cb1VJjddD4FDVVHe .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Cb1VJjddD4FDVVHe .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Cb1VJjddD4FDVVHe .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Cb1VJjddD4FDVVHe .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Cb1VJjddD4FDVVHe .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Cb1VJjddD4FDVVHe .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Cb1VJjddD4FDVVHe .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Cb1VJjddD4FDVVHe .cluster text{fill:#333;}#mermaid-svg-Cb1VJjddD4FDVVHe .cluster span{color:#333;}#mermaid-svg-Cb1VJjddD4FDVVHe div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Cb1VJjddD4FDVVHe .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Cb1VJjddD4FDVVHe rect.text{fill:none;stroke-width:0;}#mermaid-svg-Cb1VJjddD4FDVVHe .icon-shape,#mermaid-svg-Cb1VJjddD4FDVVHe .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Cb1VJjddD4FDVVHe .icon-shape p,#mermaid-svg-Cb1VJjddD4FDVVHe .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Cb1VJjddD4FDVVHe .icon-shape .label rect,#mermaid-svg-Cb1VJjddD4FDVVHe .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Cb1VJjddD4FDVVHe .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Cb1VJjddD4FDVVHe .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Cb1VJjddD4FDVVHe :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是


用户对话
Agent 是否调用 memory 工具?
写入 MEMORY.md / USER.md
可能不记
sync_all → 外部 Provider
每 10 turn?
Background Review 抽取
磁盘持久化
下个 Session 才进入 system prompt
SQLite 全量存储
session_search 按需召回


三、会遗忘吗?会遗漏细节吗?

会,而且多数是设计使然。

1. 容量硬限制 → 被迫遗忘/合并

Store 默认上限 典型条目数
memory 2200 字符 8--15 条
user 1375 字符 5--10 条

写满时 不会静默截断 ,而是返回错误,要求 Agent 用 replace/remove 合并或删除旧条目后再写。

旧信息被挤掉 = 主动遗忘

2. 系统级「不该记什么」------刻意不记细节

MEMORY_GUIDANCE 和 memory 工具 schema 都明确写了 skip 规则:

151:171:hermes-agent/agent/prompt_builder.py 复制代码
MEMORY_GUIDANCE = (
    ...
    "Do NOT save task progress, session outcomes, completed-work logs, or temporary TODO "
    "state to memory; use session_search to recall those from past transcripts. "
    "Specifically: do not record PR numbers, issue numbers, commit SHAs, 'fixed bug X', "
    "'submitted PR Y', 'Phase N done', file counts, or any artifact that will be stale "
    "in 7 days. If a fact will be stale in a week, it does not belong in memory. "
    ...
    "Procedures and workflows belong in skills, not memory."
)

刻意不记的内容包括:

  • 任务进度、完成日志、临时 TODO
  • PR/Issue 号、commit SHA
  • 7 天内会过期的信息
  • 琐碎/可 Google 的事实
  • 原始数据 dump
  • 可复现的工作流(应进 Skill,不进 Memory)

3. Agent 可能「该记却没记」

记忆依赖 模型是否调用 memory 工具,不是自动全量抽取:

  • 用户没明确说「记住这个」时,可能漏记
  • Background review 可能返回 "Nothing to save."
  • 写满且 consolidate 失败(每 turn 最多 3 次)→ 直接 skip
  • memory.write_approval: true → 写入需人工批准,未批准则不落盘

4. Frozen Snapshot → 同 session 内「看不见刚写的」

text 复制代码
Session 开始: snapshot = load_from_disk()
Session 中:   memory(action="add") → 磁盘更新,但 snapshot 不变
Session 内 LLM: 仍看 session 开始时的旧 memory
下个 Session:  重新 load_from_disk() → 新 memory 才进 prompt

同 session 内 Agent 写 memory 后,system prompt 里的 memory 块不会变;只有 tool 返回能看到最新状态。跨 turn 的「即时记忆更新」在 prompt 层是滞后的。

5. 上下文压缩 → 对话细节丢失

长对话 token 超限时,中间轮次被 LLM 摘要 替换:

  • 保留首尾 N 轮
  • 中间变成一段 summary(有损)
  • 压缩前 on_pre_compress 可抢救一部分到外部 memory
  • 压缩后 commit_memory_session() 触发 session-end 抽取

未写入 MEMORY.md 也未被 provider 抽取的细节,压缩后就找不回来了 (除非还在 SQLite 原文里,需 session_search)。

6. 外部 Provider 也是选择性记忆

Honcho/Mem0 等会做语义抽取和去重,不是 verbatim 存储;sync_allinterrupted turn 时跳过,避免污染:

3389:3391:hermes-agent/run_agent.py 复制代码
        if interrupted:
            return

四、三层记忆的职责分工

text 复制代码
MEMORY.md / USER.md     → 「永远该知道的关键事实」(极小、高信号)
session_search (SQLite) → 「某次对话里说过什么」(全量、按需、免费)
Skills                  → 「怎么做某类事」(流程、步骤、reference)
External Provider       → 「跨 session 语义关联 + 用户建模」(智能但有损)

官方对比(memory.md):

持久 Memory Session Search
容量 ~1300 tokens 固定 无限
速度 每 turn 都在 prompt 里 ~20ms FTS5
成本 占 prompt token 按需、无 LLM 成本
适用 关键事实常驻 「上周讨论过 X 吗?」

五、记忆闭环如何运转

text 复制代码
用户纠正/表达偏好
    ↓
MEMORY_GUIDANCE 引导 Agent 主动 memory(action="add")
    ↓
或每 10 turn → Background Review 抽取
    ↓
写入 MEMORY.md / USER.md(受字符上限约束,需合并旧条目)
    ↓
下个 Session → frozen snapshot 注入 system prompt
    ↓
用户不必重复纠正(若 Agent 记对了)

复杂工作流
    ↓
SKILLS_GUIDANCE → skill_manage 创建/修补 Skill
    ↓
Skills Index 在 prompt 里,正文按需 skill_view 加载

历史细节
    ↓
不写入 Memory(按设计)
    ↓
留在 SQLite → session_search 按需找回

六、结论

问题 答案
如何实现长期记忆? 文件策展 (MEMORY/USER) + SQLite 全量 + Skills + 可选外部 Provider + 后台 review
会遗忘吗? :容量挤占、explicit remove、压缩摘要、7 天过期规则
会遗漏细节吗? :只记「 durable facts」,任务细节/进度/PR 号等 刻意不记
细节去哪了? 完整对话在 SQLite,用 session_search 按需查;流程进 Skills
是 bug 还是设计? 主要是设计:Memory 是「高信号摘要」,不是 transcript 备份

如果关心的是「Hermes 会不会像人一样记住每次对话的所有细节」------不会 。它更像:小笔记本(MEMORY)+ 无限档案室(SQLite)+ 操作手册(Skills)+ 可选的长期语义记忆(Provider),并且 notebook 有严格页数限制,Agent 被明确 instruct 不要往 notebook 里塞细节。

相关推荐
奔跑中的小象17 分钟前
统信UOS + 天数AI卡部署SGLang服务手册
人工智能·uos·sglang·天数智芯
DevSecOps选型指南20 分钟前
中国版Mythos,为何是悬镜安全灵脉CodeAI?
人工智能·安全
Shockang30 分钟前
LangGraph 状态机实战
人工智能
刹那芳华199242 分钟前
循环神经网络的从零开始实现(RNN)
人工智能·rnn·深度学习
阿童木写作1 小时前
跨境图片翻译工具多合一,批量图片视频字幕翻译加智能抠图
人工智能·python·音视频·语音识别
科技之门1 小时前
AI3D从建模到贴图、绑骨和动画的完整流程怎么做?V2Fun完整工作流指南
人工智能·3d·贴图
宇宙第一小趴菜1 小时前
二、机器学习的应用领域和发展史
人工智能·机器学习
前端开发江鸟1 小时前
我写过 MCP Server,却一直以为 MCP 只有 Tool
人工智能
天国梦1 小时前
自习室智能化升级避坑指南:天学网AI智习室方案实测与选型建议
大数据·人工智能
阿里云云原生2 小时前
可用性从 99.9% 跃升至 99.995%:畅捷通如何用 AI 重塑运维底座?
运维·网络·人工智能