【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 里塞细节。

相关推荐
蓝田~1 小时前
大模型本地部署与远程调用 — 从 API 到 Agent
java·人工智能·claude·claude code
猿的天空1 小时前
AI视觉原生统一!商汤开源视觉任务大统一模型SenseNova-Vision
人工智能·计算机·ai·程序员·大模型·编程·智能体
EAIReport1 小时前
2026开源AI应用平台深度对比:Dify vs AIFlowy 企业落地选型指南
人工智能·开源
神奇小汤圆1 小时前
Loop Engineering 实战:/goal 命令让 AI 自己写完整项目
人工智能
Anova.YJ1 小时前
AI Notebook
人工智能·python·机器学习
秦先生在广东2 小时前
Agent Island:把 Claude Code / Codex 的会话状态塞进刘海儿屏
人工智能
阿里云大数据AI技术2 小时前
EMR Serverless Daft 如何简化多模态数据处理:视频抽帧、清洗、标注全流程与具身智能实践
人工智能·spark
ZZH_AI项目交付2 小时前
端侧大模型跑进 iPhone XR:我为什么没把它做成聊天助手
人工智能·app·ai编程
Alluxio2 小时前
Alluxio + Anyscale Ray框架,实现跨区域训练数据读取速度20倍提升
人工智能·分布式·机器学习·缓存·ai