Memory 持久记忆 —— 跨会话记忆、Memory 工具、用户画像与环境偏好
简介
上一篇我们深入探讨了 Hermes Agent 的 Skills 技能系统,掌握了如何通过技能市场、本地技能、版本锁定等方式让 Agent 具备专业领域能力。技能让 Agent 从"通用助手"进化为"领域专家",但还有一个关键能力尚未触及——记忆。
想象一下这些场景:
- 你上周告诉 Agent 你的项目使用 TypeScript 5.x + React 18,今天新开会话时它又问你"项目用什么技术栈?"
- 你习惯把编辑器主题设为 Monokai,每次开新会话都要重新说一遍
- 你上次让 Agent 写了一个数据库迁移脚本,这次想回顾但完全找不到当时的对话记录
- 你对 Agent 说"用老样子的方式",但它根本不知道"老样子"是什么
这些问题都指向同一个核心能力:持久记忆。没有记忆的 Agent,就像每次见面都完全失忆的朋友——无论你之前说过多少次,它每次都像第一次认识你。
本文将带你全面掌握 Hermes Agent 的 Memory 系统:
- 跨会话记忆:记忆如何在不同会话间持久化
- Memory 工具:存储、查询、更新记忆的操作接口
- 用户画像:自动构建和维护用户偏好档案
- 环境偏好:项目配置、编码风格、工具链偏好的记忆
核心目标:让 Agent 真正"记住你",而不是每次从零开始。
目录
Memory 系统架构
什么是 Memory?
Hermes Agent 的 Memory 系统是一个多层次的持久化记忆框架,允许 Agent 在不同会话之间保留关键信息。它不是简单地把所有对话都存下来——那样会淹没在噪音中——而是有策略地提取、存储、检索有价值的信息。
Memory 系统架构:
├── 🧠 记忆存储层
│ ├── 短期记忆(当前会话)
│ ├── 长期记忆(跨会话持久化)
│ └── 语义记忆(向量化存储)
│
├── 🔧 记忆工具层
│ ├── memory_write(写入记忆)
│ ├── memory_read(读取记忆)
│ ├── memory_search(搜索记忆)
│ ├── memory_update(更新记忆)
│ └── memory_delete(删除记忆)
│
├── 👤 用户画像层
│ ├── 技术偏好
│ ├── 编码风格
│ ├── 沟通偏好
│ └── 项目上下文
│
└── 🌍 环境配置层
├── 编辑器偏好
├── 终端设置
├── 构建工具配置
└── 部署习惯记忆的三种类型
| 类型 | 持久性 | 范围 | 示例 |
|---|---|---|---|
| 短期记忆 | 会话内 | 当前对话上下文 | 刚才讨论的变量名 |
| 长期记忆 | 永久 | 跨所有会话 | 用户的技术栈偏好 |
| 语义记忆 | 永久 | 可模糊检索 | "上次讨论的数据库优化方案" |
记忆的层次结构
1. 会话级记忆(Session Memory)
会话级记忆是当前对话的上下文窗口,这是所有 LLM 交互的基础能力。但它有容量限制,当对话过长时,最早的信息会被"挤出"上下文窗口。
# 会话记忆的局限性
session_memory = {
"max_tokens": 128000, # 上下文窗口上限
"current_tokens": 125000, # 接近上限
"strategy": "rolling_window", # 滚动窗口策略
"risk": "最早的信息即将丢失"
}2. 用户级记忆(User Memory)
用户级记忆是跨会话持久的,绑定到用户身份。无论何时开启新会话,这些记忆都可用。
# 用户级记忆示例
user_memory:
user_id: "hermes-user-001"
profile:
name: "开发者"
primary_language: "Python"
secondary_language: "TypeScript"
editor: "Neovim"
terminal: "WezTerm"
shell: "zsh"
preferences:
code_style: "black" # Python 格式化
linter: "ruff"
test_framework: "pytest"
commit_style: "conventional_commits"
projects:
- name: "ai-agent-blog"
path: "~/ai-agent-blog"
stack: ["markdown", "nodejs"]
- name: "ml-pipeline"
path: "~/projects/ml-pipeline"
stack: ["python", "pytorch", "fastapi"]3. 项目级记忆(Project Memory)
项目级记忆绑定到特定项目目录,任何访问该项目的会话都能读取。
# .hermes/memory.yaml - 项目级记忆
project:
name: "my-web-app"
language: "TypeScript"
framework: "Next.js 14"
database: "PostgreSQL"
orm: "Prisma"
testing:
unit: "Vitest"
e2e: "Playwright"
conventions:
naming: "camelCase"
imports: "absolute"
styling: "tailwind"
state_management: "zustand"
deployment:
platform: "Vercel"
ci: "GitHub Actions"
notes:
- "API 使用 /api/v2/ 前缀"
- "数据库迁移使用 prisma migrate"
- "组件使用 Server Components 优先"Memory 工具详解
memory_write:写入记忆
用户:记住我的偏好——我习惯用 Prettier 做代码格式化,
tab 宽度设为 2,使用单引号,末尾不加分号。
Agent:[调用 memory_write]
参数:
key: "coding_preferences.formatting"
value:
tool: "prettier"
tab_width: 2
single_quote: true
semi: false
scope: "user" # user | project | session
ttl: null # null = 永久
Agent:✅ 已记住你的代码格式化偏好:Prettier,
2 空格缩进,单引号,末尾不加分号。代码示例:
# Memory 工具调用示例
memory_write(
key="coding_preferences.formatting",
value={
"tool": "prettier",
"tab_width": 2,
"single_quote": True,
"semi": False,
"trailing_comma": "es5"
},
scope="user",
metadata={
"source": "explicit_user_input",
"confidence": 1.0,
"timestamp": "2025-05-20T10:30:00Z"
}
)memory_read:读取记忆
用户:我的格式化偏好是什么?
Agent:[调用 memory_read]
参数:
key: "coding_preferences.formatting"
Agent:[读取记忆]
→ tool: prettier
→ tab_width: 2
→ single_quote: true
→ semi: false
Agent:你的格式化偏好是:使用 Prettier,2 空格缩进,
单引号,末尾不加分号。memory_search:搜索记忆
用户:我之前说过关于数据库的事情吗?
Agent:[调用 memory_search]
参数:
query: "数据库"
scope: "user"
limit: 5
Agent:[语义搜索返回结果]
1. "使用 PostgreSQL 作为主数据库" (2025-04-15)
2. "ORM 偏好 Prisma" (2025-04-15)
3. "数据库迁移习惯手动写 SQL" (2025-05-02)
Agent:关于数据库,你之前提到过:
1. 使用 PostgreSQL 作为主数据库
2. ORM 偏好 Prisma
3. 数据库迁移习惯手动写 SQLmemory_update:更新记忆
用户:改一下,现在我用双引号了,分号也加上。
Agent:[调用 memory_update]
参数:
key: "coding_preferences.formatting"
value:
single_quote: false # 覆盖
semi: true # 覆盖
# 其他字段保持不变
Agent:✅ 已更新:现在使用双引号,末尾加分号。memory_delete:删除记忆
用户:忘了我之前说的数据库迁移习惯。
Agent:[调用 memory_delete]
参数:
key: "coding_preferences.db_migration"
Agent:✅ 已删除关于数据库迁移习惯的记忆。用户画像系统
自动画像构建
Hermes Agent 不仅能被动接受记忆写入,还能主动从对话中提取用户画像信息:
# Agent 自动提取用户画像的流程
class UserProfileExtractor:
"""从对话中自动提取用户偏好"""
def extract(self, conversation):
patterns = {
"tech_stack": [
r"我用 (?:的是)?([\w\s\.]+)",
r"项目用 (?:的是)?([\w\s\.]+)",
r"习惯用 (?:的是)?([\w\s\.]+)"
],
"editor": [
r"(?:编辑器|IDE).*(?:VS ?Code|Vim|Neovim|IDEA|WebStorm)",
r"(?:用|使用).*(?:VS ?Code|Vim|Neovim)"
],
"style": [
r"(?:喜欢|偏好).*(?:2|4)\s*空格",
r"(?:喜欢|偏好).*(?:单引号|双引号)"
]
}
# 提取并写入记忆
for category, regex_list in patterns.items():
for regex in regex_list:
matches = re.finditer(regex, conversation, re.IGNORECASE)
for match in matches:
self.write_to_memory(category, match.group(1))画像数据结构
# 完整的用户画像
user_profile:
identity:
name: "开发者"
timezone: "Asia/Shanghai"
language: "zh-CN"
tech_preferences:
languages:
- name: "Python"
level: "expert"
version: "3.12"
frameworks: ["FastAPI", "Django"]
- name: "TypeScript"
level: "proficient"
version: "5.x"
frameworks: ["React", "Next.js"]
- name: "Go"
level: "intermediate"
frameworks: ["Gin"]
tools:
editor: "Neovim"
terminal: "WezTerm"
shell: "zsh"
package_manager: "uv" # Python
frontend_pm: "pnpm"
formatting:
python: "ruff format"
typescript: "prettier"
tab_width: 2
line_length: 88
testing:
python: "pytest"
typescript: "vitest"
logging:
level: "INFO"
format: "json"
communication:
language: "中文"
detail_level: "detailed" # brief | detailed | verbose
code_comment_language: "zh" # 代码注释用中文
error_explanation: "chinese_with_english_terms"
learned_behaviors:
- pattern: "用户倾向于先解释思路,再给代码"
frequency: 85
confidence: 0.92
- pattern: "用户对错误信息很敏感,喜欢详细的错误分析"
frequency: 78
confidence: 0.88画像应用场景
场景 1:新会话自动加载画像
═══════════════════════════════════════
Agent:[会话启动]
→ 读取用户画像
→ 检测到 primary_language = Python
→ 检测到 editor = Neovim
→ 检测到 detail_level = detailed
Agent 自动调整:
- 使用 Python 示例代码
- 提及 Neovim 相关配置
- 提供详细的解释
──────────────────────────────────────
场景 2:智能推荐
═══════════════════════════════════════
用户:这个项目该怎么测试?
Agent:[查询画像]
→ testing.python = "pytest"
Agent:根据你的偏好,我推荐使用 pytest。
你的项目使用 FastAPI,可以配合 httpx 做异步测试:
```python
import pytest
from httpx import AsyncClient
@pytest.mark.asyncio
async def test_read_main():
async with AsyncClient(app=app, base_url="http://test") as ac:
response = await ac.get("/")
assert response.status_code == 200
```
需要我帮你配置 pytest.ini 和 conftest.py 吗?环境偏好记忆
编辑器偏好
# 编辑器配置记忆
editor_preferences:
neovim:
theme: "tokyonight"
font: "JetBrainsMono Nerd Font"
font_size: 13
tab_size: 2
line_numbers: "relative"
cursor_style: "block"
plugins:
- "telescope.nvim"
- "nvim-treesitter"
- "lsp-zero.nvim"
- "nvim-cmp"
- "gitsigns.nvim"
lsp_servers:
python: "pyright"
typescript: "typescript-language-server"
go: "gopls"终端偏好
# 终端配置记忆
terminal_preferences:
emulator: "WezTerm"
shell: "zsh"
prompt: "starship"
theme: "Catppuccin Mocha"
font: "JetBrainsMono Nerd Font"
font_size: 14
opacity: 0.95
keybindings:
new_tab: "Ctrl+Shift+T"
split_horizontal: "Ctrl+Shift+H"
split_vertical: "Ctrl+Shift+V"
aliases:
- "ll = ls -la"
- "gs = git status"
- "gc = git commit"
- "gp = git push"构建和部署偏好
# 构建和部署记忆
build_deploy_preferences:
python:
packaging: "uv build"
virtual_env: "uv venv"
dependency_management: "uv sync"
deployment: "docker + k8s"
typescript:
bundler: "vite"
package_manager: "pnpm"
deployment: "Vercel / Cloudflare Pages"
go:
build: "go build -ldflags='-s -w'"
deployment: "二进制 + systemd"
ci_cd:
platform: "GitHub Actions"
triggers: ["push to main", "pull_request"]
cache: true
notify_on_failure: true环境偏好自动应用
# Agent 根据环境偏好自动调整行为
def apply_environment_preferences(self):
"""根据记忆的环境偏好调整 Agent 行为"""
prefs = self.memory_read("environment_preferences")
# 代码生成适配
if prefs.get("editor") == "neovim":
self.add_note("提示:在 Neovim 中,你可以用 :w 保存后运行")
# 命令适配
if prefs.get("python_packaging") == "uv":
self.use_command("uv run", instead_of="python")
self.use_command("uv add", instead_of="pip install")
# 终端提示适配
if prefs.get("shell") == "zsh":
self.suggest_zsh_plugins()
# 字体和显示适配
if "Nerd Font" in prefs.get("font", ""):
self.use_unicode_icons = True # 可以使用图标跨会话记忆实战
实战 1:记住项目技术栈
═══════════════════════════════════════
会话 A(第一次讨论)
═══════════════════════════════════════
用户:我有一个新项目,打算用 FastAPI + PostgreSQL +
Prisma Client(Python 版),帮我想想项目结构。
Agent:[记忆写入]
memory_write(
key="project.new_project.stack",
value={
"framework": "FastAPI",
"database": "PostgreSQL",
"orm": "prisma-client-py",
"created_at": "2025-05-15"
},
scope="user"
)
Agent:好的,基于你的技术栈选择,我建议以下结构:
[给出项目结构]
═══════════════════════════════════════
会话 B(3 天后,新会话)
═══════════════════════════════════════
用户:上次说的那个项目,我现在要加用户认证模块。
Agent:[自动检索记忆]
memory_search("用户认证 项目")
→ 匹配: "new_project" (FastAPI + PostgreSQL)
Agent:你说的是那个 FastAPI + PostgreSQL 的项目对吧?
好的,基于已有的技术栈,我推荐用 JWT + OAuth2:
[给出与已有技术栈一致的认证方案]实战 2:记住编码风格
═══════════════════════════════════════
会话 A(发现偏好)
═══════════════════════════════════════
用户:函数命名用蛇形,不要用驼峰。注释写中文。
Agent:[自动记忆]
memory_write(
key="coding_style.naming",
value="snake_case",
scope="project"
)
memory_write(
key="coding_style.comment_language",
value="zh-CN",
scope="user"
)
═══════════════════════════════════════
会话 B(自动应用)
═══════════════════════════════════════
用户:写一个用户注册的函数。
Agent:[读取记忆]
naming → snake_case
comment_language → zh-CN
Agent:好的:
```python
def create_user(
username: str,
email: str,
password: str
) -> User:
"""创建新用户
参数:
username: 用户名
email: 邮箱地址
password: 密码(会自动哈希)
返回:
创建好的 User 对象
"""
# 检查用户名是否已存在
existing = db.query(User).filter(
User.username == username
).first()
if existing:
raise ValueError("用户名已存在")
# 创建新用户
user = User(
username=username,
email=email,
password_hash=hash_password(password)
)
db.add(user)
db.commit()
return user
```实战 3:记住部署习惯
═══════════════════════════════════════
会话 A(设定习惯)
═══════════════════════════════════════
用户:我的项目都部署到 Vercel,环境变量用 .env.local 管理。
Agent:[记忆写入]
memory_write(
key="deployment.preferences",
value={
"platform": "Vercel",
"env_management": ".env.local",
"env_pattern": "NEXT_PUBLIC_* for client, others for server"
},
scope="user"
)
═══════════════════════════════════════
会话 B(自动适配)
═══════════════════════════════════════
用户:这个项目需要接入一个第三方 API,怎么配置密钥?
Agent:[读取记忆]
deployment.platform → Vercel
env_management → .env.local
Agent:根据你的部署习惯(Vercel + .env.local):
1. 开发环境 - 在 .env.local 中添加:
```
THIRD_PARTY_API_KEY=your_key_here
```
2. 生产环境 - 在 Vercel Dashboard 中添加环境变量:
Settings → Environment Variables → Add New
3. 代码中使用:
```typescript
// 只在服务端访问
const apiKey = process.env.THIRD_PARTY_API_KEY;
```记忆的生命周期管理
记忆的创建
记忆可以通过以下方式创建:
- 显式写入:用户直接要求记住某些信息
- 自动提取:Agent 从对话中自动识别和提取
- 系统注入:从项目配置文件(.hermes/memory.yaml)加载
记忆的更新
# 记忆版本管理
memory_versioning:
strategy: "append_with_timestamp"
max_versions: 10
example:
key: "coding_preferences.formatting"
versions:
- value: {semi: false}
timestamp: "2025-01-15"
source: "explicit"
- value: {semi: true}
timestamp: "2025-05-20"
source: "explicit"记忆的过期
# 记忆过期策略
memory_expiry:
default_ttl: null # 默认永久
rules:
- pattern: "session_notes.*"
ttl: "30d" # 会话笔记 30 天后过期
- pattern: "temporary.*"
ttl: "7d" # 临时记忆 7 天后过期
- pattern: "user_profile.*"
ttl: null # 用户画像永久有效
- pattern: "project_context.*"
ttl: "90d" # 项目上下文 90 天后刷新
cleanup_schedule: "weekly"
cleanup_action: "archive_then_delete" # 先归档再删除记忆的归档
# 记忆归档流程
def archive_old_memories():
"""归档过期记忆"""
old_memories = find_expired_memories()
for memory in old_memories:
# 1. 移动到归档存储
archive_store.write(memory)
# 2. 生成归档摘要
summary = generate_summary(memory)
memory_write(
key=f"archive.{memory.key}",
value=summary,
scope="user"
)
# 3. 删除原始记忆
memory_delete(memory.key)隐私与安全
记忆加密
# 记忆安全配置
memory_security:
encryption:
at_rest: "AES-256-GCM"
in_transit: "TLS 1.3"
access_control:
user_memories: "user_only" # 只有用户自己能访问
project_memories: "project_collaborators" # 项目协作者
shared_memories: "configurable" # 可配置
sensitive_data:
auto_detect: true
patterns:
- "password"
- "api_key"
- "token"
- "secret"
action: "encrypt_and_restrict" # 加密并限制访问记忆审计
# 记忆操作审计日志
memory_audit:
enabled: true
log_format:
- timestamp
- action # read/write/update/delete
- key
- scope
- source # explicit/auto/system
- user_id
retention: "180d"
export_format: "json"用户控制
# 用户记忆管理命令
# 查看所有记忆
hermes memory list
# 查看特定记忆
hermes memory get coding_preferences.formatting
# 导出所有记忆
hermes memory export > my-memories.yaml
# 导入记忆
hermes memory import my-memories.yaml
# 清除所有记忆(危险操作)
hermes memory clear --confirm
# 清除特定范围的记忆
hermes memory clear --scope session
hermes memory clear --scope project --path ./my-project
# 设置记忆自动过期
hermes memory set-ttl "project_notes.*" 30d最佳实践
1. 记忆粒度控制
# ✅ 推荐:细粒度的记忆键
coding_preferences:
formatting:
tool: "prettier"
tab_width: 2
naming:
convention: "camelCase"
imports:
order: "alphabetical"
# ❌ 不推荐:一个大块记忆
coding_preferences: "用 prettier,2 空格,驼峰命名,字母排序导入..."2. 合理使用范围
| 范围 | 适用场景 | 示例 |
|---|---|---|
| user | 全局偏好 | 编辑器、语言偏好、编码风格 |
| project | 项目特定 | 技术栈、项目约定、API 地址 |
| session | 临时信息 | 本次讨论的临时方案 |
3. 定期审查记忆
# 每周检查一次记忆
hermes memory list --sort-by=last_accessed
# 清理不常用的记忆
hermes memory prune --unused-since=90d --dry-run4. 不要过度记忆
# ❌ 不要记忆每次对话的细节
# ✅ 只记忆有价值的结论和偏好
good_memories:
- "用户偏好 TypeScript"
- "项目使用 PostgreSQL"
- "部署到 Vercel"
bad_memories:
- "用户今天问了 5 个问题"
- "用户下午 3 点开始会话"
- "用户说了一个然后改主意的方案"总结
本文全面介绍了 Hermes Agent 的 Memory 持久记忆系统:
- Memory 系统架构:短期记忆、长期记忆、语义记忆的三层架构
- 记忆工具:write、read、search、update、delete 五种核心操作
- 用户画像系统:自动从对话中提取偏好,构建完整的用户档案
- 环境偏好记忆:编辑器、终端、构建工具、部署平台的偏好记忆
- 跨会话记忆实战:技术栈记忆、编码风格记忆、部署习惯记忆
- 记忆生命周期管理:创建、更新、过期、归档的完整流程
- 隐私与安全:加密、访问控制、审计日志、用户控制
核心原则:
- 记忆让 Agent 真正"认识你":不再每次从零开始
- 有策略地记忆:记住有价值的结论,忽略噪声
- 细粒度管理:不同范围、不同类型的记忆独立管理
- 用户完全控制:随时查看、修改、删除记忆
- 隐私优先:敏感数据自动检测和保护
📌 下篇预告
Session Search 对话搜索 —— 在海量历史对话中精准定位:关键词搜索、语义搜索、OR/AND 组合查询、上下文回忆。当记忆系统帮你记住了关键信息后,如何快速找到过去的完整对话?下一篇将带你掌握 Hermes Agent 的对话搜索能力,让历史对话成为你的知识宝库。