为什么 Agent 需要记忆

人类记忆分为短期记忆、工作记忆、长期记忆,各有不同容量与持久性。AI Agent 面临同样的问题:对话窗口有限,但用户期望 Agent 记住偏好、历史决策、学到的技能。

传统 Agent 的记忆方案通常是两种极端:

  1. 全部塞进 context:对话历史越来越长,token 成本爆炸,性能下降
  2. 无持久记忆:每次对话从零开始,用户反复解释需求

Hermes Agent 的解决方案是四层记忆架构,模拟人类记忆的分层机制,在不同层级存储不同生命周期的信息。

四层记忆总览

┌─────────────────────────────────────────────────┐
│ 第一层:核心记忆 (Core Memory)                    │
│ 容量:~800 token | 生命周期:当前会话             │
│ 存储介质:内存 | 检索方式:直接注入 prompt        │
├─────────────────────────────────────────────────┤
│ 第二层:用户画像 (User Profile)                   │
│ 容量:~2000 token | 生命周期:永久                │
│ 存储介质:USER.md 文件 | 检索方式:会话开始注入    │
├─────────────────────────────────────────────────┤
│ 第三层:长期历史 (Long-term History)              │
│ 容量:无限 | 生命周期:永久                       │
│ 存储介质:SQLite + FTS5 | 检索方式:全文检索       │
├─────────────────────────────────────────────────┤
│ 第四层:技能记忆库 (Skill Memory)                 │
│ 容量:无限 | 生命周期:永久 + 版本管理             │
│ 存储介质:SKILL.md 文件集 | 检索方式:语义匹配     │
└─────────────────────────────────────────────────┘

第一层:核心记忆(Core Memory)

核心记忆是 Agent 的"工作记忆",存在于当前会话的 context window 中。Hermes 严格控制其大小在 800 token 以内。

class CoreMemory:
    """核心记忆:当前会话的工作区"""
    
    MAX_TOKENS = 800
    
    def __init__(self):
        self.system_prompt = ""      # 系统指令
        self.user_preferences = ""   # 当前会话发现的用户偏好
        self.active_context = ""     # 当前任务的关键上下文
        self.recent_decisions = []   # 最近的决策记录
    
    def add(self, content, priority=0):
        """添加内容,自动管理容量"""
        self.active_context += f"\n{content}"
        # 超过容量时,按优先级淘汰
        if self.token_count() > self.MAX_TOKENS:
            self._evict_low_priority()
    
    def _evict_low_priority(self):
        """淘汰低优先级内容,保持核心记忆精简"""
        items = sorted(self.recent_decisions, key=lambda x: x.priority)
        while self.token_count() > self.MAX_TOKENS and items:
            evicted = items.pop(0)
            # 被淘汰的内容转存到长期记忆
            long_term_store.archive(evicted)
            self.recent_decisions.remove(evicted)

核心记忆的关键设计原则:

原则说明
严格限容800 token 上限,防止 context 膨胀
优先级淘汰低优先级信息自动转存到长期记忆
实时更新会话中随时写入与更新
会话隔离不同会话的核心记忆互不干扰

第二层:用户画像(User Profile)

用户画像以 USER.md 文件形式持久存储,记录用户的长期偏好、身份信息、使用习惯。每次会话开始时自动注入。

# USER.md 示例

## 基本信息
- 姓名:张三
- 职业:全栈工程师
- 时区:Asia/Shanghai (UTC+8)

## 技术偏好
- 编程语言:TypeScript > Python > Go
- 框架:Next.js, FastAPI, Prisma
- 部署:Docker + Vercel
- 编辑器:VS Code (暗色主题)

## 工作习惯
- 喜欢简洁的代码,不喜欢过多注释
- 习惯先看测试再写实现
- 部署前必须跑 lint

## 沟通风格
- 直接给方案,不需要过多解释
- 中文为主,技术术语用英文
- 不需要客套话

用户画像的更新策略:

class UserProfile:
    """用户画像管理"""
    
    def __init__(self, path="USER.md"):
        self.path = path
        self.content = self._load()
    
    async def update_from_conversation(self, conversation):
        """从对话中自动提取用户偏好"""
        extraction_prompt = f"""
        从以下对话中提取用户的长期偏好信息:
        {conversation.summary}
        
        只提取稳定偏好,忽略一次性需求。
        输出格式:更新后的 USER.md
        """
        updated = await reflection_llm.generate(extraction_prompt)
        
        # 合并而非覆盖
        self.content = self._merge(self.content, updated)
        self._save()
    
    def _merge(self, old, new):
        """智能合并新旧画像,保留已有信息"""
        # 对每个字段,新值优先但保留旧值中未被覆盖的部分
        ...

第三层:长期历史(Long-term History)

长期历史是 Hermes 的" episodic memory",使用 SQLite + FTS5 全文检索存储所有历史对话与任务记录。

-- SQLite 数据库结构
CREATE TABLE conversations (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    session_id TEXT NOT NULL,
    timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
    user_message TEXT,
    agent_response TEXT,
    tools_used TEXT,          -- JSON array
    task_summary TEXT,
    success BOOLEAN,
    tokens_used INTEGER
);

-- FTS5 全文索引
CREATE VIRTUAL TABLE conversations_fts USING fts5(
    user_message,
    agent_response,
    task_summary,
    content='conversations',
    content_rowid='id'
);

-- 检索示例
SELECT c.* FROM conversations c
JOIN conversations_fts f ON c.id = f.rowid
WHERE conversations_fts MATCH '部署 Vercel'
ORDER BY rank
LIMIT 5;

检索接口设计:

class LongTermMemory:
    """长期记忆:SQLite + FTS5"""
    
    def __init__(self, db_path="memory.db"):
        self.db = sqlite3.connect(db_path)
    
    def search(self, query, limit=5):
        """全文检索历史记忆"""
        # FTS5 查询
        results = self.db.execute("""
            SELECT c.*, bm25(conversations_fts) as rank
            FROM conversations c
            JOIN conversations_fts f ON c.id = f.rowid
            WHERE conversations_fts MATCH ?
            ORDER BY rank
            LIMIT ?
        """, (query, limit)).fetchall()
        
        return [self._format(r) for r in results]
    
    def search_by_time(self, start, end):
        """按时间范围检索"""
        return self.db.execute("""
            SELECT * FROM conversations
            WHERE timestamp BETWEEN ? AND ?
            ORDER BY timestamp DESC
        """, (start, end)).fetchall()
    
    def search_by_tools(self, tool_name):
        """按使用的工具检索"""
        return self.db.execute("""
            SELECT * FROM conversations
            WHERE tools_used LIKE ?
            ORDER BY timestamp DESC
        """, (f'%"{tool_name}"%',)).fetchall()

长期历史的容量是无限的,但检索结果会被压缩后注入核心记忆:

def retrieve_and_compress(query, max_tokens=200):
    """检索长期记忆并压缩到适合核心记忆的大小"""
    results = long_term_memory.search(query, limit=10)
    
    # 用复盘 LLM 压缩检索结果
    compressed = reflection_llm.summarize(
        results,
        max_tokens=max_tokens,
        focus=query
    )
    
    return compressed

第四层:技能记忆库(Skill Memory)

技能记忆库是 Hermes 最独特的记忆层,存储系统从经验中自动提炼的可复用技能。每个技能是一个 SKILL.md 文件。

skills/
├── csv-data-cleaning/
│   ├── SKILL.md          # 技能定义
│   ├── examples/         # 示例数据
│   └── tests/            # 自动生成的测试
├── nextjs-vercel-deploy/
│   ├── SKILL.md
│   └── scripts/
├── api-rate-limit-handling/
│   ├── SKILL.md
│   └── examples/
└── docker-compose-setup/
    ├── SKILL.md
    └── templates/

技能检索使用语义匹配而非关键词匹配:

class SkillMemory:
    """技能记忆库:语义检索 + 版本管理"""
    
    def __init__(self, skills_dir="skills/"):
        self.skills_dir = skills_dir
        self.embeddings = self._build_index()
    
    def find_relevant(self, task_description, top_k=3):
        """语义检索相关技能"""
        task_emb = embed(task_description)
        
        scores = []
        for skill_id, skill_emb in self.embeddings.items():
            sim = cosine_similarity(task_emb, skill_emb)
            # 加入技能成功率权重
            skill = self.load(skill_id)
            success_rate = skill.success_count / max(skill.total_count, 1)
            final_score = sim * 0.7 + success_rate * 0.3
            scores.append((skill_id, final_score))
        
        scores.sort(key=lambda x: -x[1])
        return [self.load(sid) for sid, _ in scores[:top_k]]

跨会话记忆检索流程

当用户开始一个新会话时,Hermes 的记忆检索流程:

用户消息进入
    ├──→ 1. 加载 USER.md(用户画像)→ 注入 system prompt
    ├──→ 2. 语义检索技能库 → 找到相关技能 → 注入 system prompt
    ├──→ 3. FTS5 检索长期历史 → 找到相关历史 → 压缩后注入核心记忆
    └──→ 4. 初始化核心记忆(800 token 预算)
              ├── 系统指令:~300 token
              ├── 用户画像摘要:~200 token
              ├── 相关技能:~200 token
              └── 历史检索摘要:~100 token
async def initialize_session(user_message):
    """会话初始化:跨层记忆检索"""
    core = CoreMemory()
    
    # 1. 加载用户画像
    profile = UserProfile.load()
    core.add(profile.to_summary(), priority=10)
    
    # 2. 检索相关技能
    skills = skill_memory.find_relevant(user_message, top_k=2)
    for skill in skills:
        core.add(skill.to_prompt(), priority=8)
    
    # 3. 检索长期历史
    history = long_term_memory.search(user_message, limit=5)
    if history:
        compressed = await compress_history(history, max_tokens=100)
        core.add(compressed, priority=5)
    
    # 4. 剩余空间留给当前对话
    return core

与其他记忆方案对比

方案持久性检索精度Token 效率可扩展性
Hermes 四层架构永久高(FTS5+语义)极高(分层压缩)
纯 Context 窗口会话级N/A低(全量注入)
向量数据库 RAG永久中(向量)
简单摘要永久

小结

Hermes 的四层记忆架构解决了 Agent 记忆的核心矛盾:既要记住一切,又不能让记忆拖慢思考。通过分层存储、按需检索、智能压缩,Hermes 在 800 token 的核心记忆约束下,实现了对无限历史的高效利用。这是支撑自进化机制的底层基础设施。

加入讨论

这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。

碳基与硅基的智慧碰撞,认知差异创造无限可能。