AI 智能体记忆系统实战:短期记忆 + 长期记忆 + 工具记忆三位一体设计

阿里云上云采购季 2核2G云服务器99元/年 续费同价

大多数人构建 AI Agent 时,要么完全不考虑记忆(每次对话从零开始),要么把所有上下文一股脑塞进 Prompt(Token 爆炸)。其实,人类的记忆系统有三个层次,Agent 也一样。本文手把手实现一套生产可用的三层记忆架构。


为什么 Agent 需要完善的记忆系统?

没有记忆的 Agent,就像每天上班都不认识同事的员工——效率极低,用户体验极差。

  • 短期记忆缺失:多轮对话中丢失上下文,用户刚说的事就忘了
  • 长期记忆缺失:用户每次都要重新介绍自己,无法个性化
  • 工具记忆缺失:Agent 不知道”上次用这个工具遇到了坑”,重复犯错

三层记忆架构概览

┌─────────────────────────────────────────────────┐
│                  AI Agent                         │
├──────────────────┬──────────────────┬────────────┤
│   短期记忆        │    长期记忆       │  工具记忆  │
│ (Conversation    │  (Vector Store   │ (Tool Use  │
│  Buffer)         │   + SQLite)      │  History)  │
│                  │                  │            │
│ 当前会话上下文    │ 用户偏好/历史摘要 │ 工具调用   │
│ 最近 N 轮对话    │ 跨会话持久化      │ 成功/失败  │
│ 临时工作空间     │ 语义检索          │ 经验积累   │
└──────────────────┴──────────────────┴────────────┘

第一层:短期记忆——对话缓冲区

短期记忆管理当前会话内的上下文,核心挑战是 Token 控制:保留最相关的内容,不撑爆 Context Window。

# memory/short_term.py
from collections import deque
from typing import List, Optional
import tiktoken

class ShortTermMemory:
    """
    滑动窗口式短期记忆
    策略:保留最近 N 轮,同时确保总 Token 数不超过上限
    """

    def __init__(self, max_turns: int = 20, max_tokens: int = 4000, model: str = "gpt-4o-mini"):
        self.max_turns = max_turns
        self.max_tokens = max_tokens
        self.messages: deque = deque(maxlen=max_turns * 2)  # 每轮2条(user + assistant)
        self.encoder = tiktoken.encoding_for_model(model)

    def add_turn(self, user_msg: str, assistant_msg: str):
        """添加一轮对话"""
        self.messages.append({"role": "user", "content": user_msg})
        self.messages.append({"role": "assistant", "content": assistant_msg})
        self._enforce_token_limit()

    def _count_tokens(self, messages: list) -> int:
        """计算消息列表的 Token 数"""
        total = 0
        for msg in messages:
            total += len(self.encoder.encode(msg["content"])) + 4  # 4 是固定开销
        return total

    def _enforce_token_limit(self):
        """强制 Token 上限:从最早的消息开始裁剪"""
        while self._count_tokens(list(self.messages)) > self.max_tokens and len(self.messages) >= 2:
            self.messages.popleft()  # 移除最早的 user 消息
            self.messages.popleft()  # 移除对应的 assistant 消息

    def get_context(self) -> List[dict]:
        """获取当前上下文窗口"""
        return list(self.messages)

    def clear(self):
        """清空当前会话(新会话开始时调用)"""
        self.messages.clear()

    def summarize_if_needed(self, llm_client) -> Optional[str]:
        """当 Token 接近上限时,自动压缩历史对话为摘要"""
        if self._count_tokens(list(self.messages)) > self.max_tokens * 0.8:
            history_text = "\n".join([
                f"{m['role']}: {m['content']}" 
                for m in list(self.messages)[:-4]  # 保留最近2轮原文
            ])
            response = llm_client.chat.completions.create(
                model="gpt-4o-mini",
                messages=[{
                    "role": "user",
                    "content": f"请将以下对话历史压缩为100字以内的摘要,保留关键信息:\n{history_text}"
                }]
            )
            summary = response.choices[0].message.content
            # 用摘要替换早期历史
            early_messages = list(self.messages)[:-4]
            recent_messages = list(self.messages)[-4:]
            self.messages.clear()
            self.messages.append({"role": "system", "content": f"[历史摘要] {summary}"})
            for msg in recent_messages:
                self.messages.append(msg)
            return summary
        return None

第二层:长期记忆——跨会话持久化

长期记忆存储用户的偏好、历史交互摘要和关键事实,通过向量语义检索按需召回,不占用 Prompt 空间。

# memory/long_term.py
import sqlite3
import json
from datetime import datetime
from typing import List, Dict, Any
from openai import OpenAI

class LongTermMemory:
    """
    长期记忆系统
    - SQLite 存储结构化用户信息
    - 向量嵌入支持语义检索
    """

    def __init__(self, user_id: str, db_path: str = "./agent_memory.db"):
        self.user_id = user_id
        self.client = OpenAI()
        self.db_path = db_path
        self._init_db()

    def _init_db(self):
        """初始化数据库表结构"""
        with sqlite3.connect(self.db_path) as conn:
            conn.execute("""
                CREATE TABLE IF NOT EXISTS memories (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    user_id TEXT NOT NULL,
                    memory_type TEXT NOT NULL,   -- 'fact', 'preference', 'summary'
                    content TEXT NOT NULL,
                    embedding BLOB,              -- 序列化的向量
                    importance REAL DEFAULT 0.5, -- 重要性评分 0~1
                    access_count INTEGER DEFAULT 0,
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
                )
            """)
            conn.execute("CREATE INDEX IF NOT EXISTS idx_user_id ON memories(user_id)")

    def _embed(self, text: str) -> List[float]:
        """生成文本的向量嵌入"""
        response = self.client.embeddings.create(
            model="text-embedding-3-small",
            input=text
        )
        return response.data[0].embedding

    def _cosine_similarity(self, v1: List[float], v2: List[float]) -> float:
        """计算余弦相似度"""
        import math
        dot = sum(a * b for a, b in zip(v1, v2))
        norm1 = math.sqrt(sum(a * a for a in v1))
        norm2 = math.sqrt(sum(b * b for b in v2))
        return dot / (norm1 * norm2) if norm1 * norm2 > 0 else 0

    def store(self, content: str, memory_type: str = "fact", importance: float = 0.5):
        """存储一条记忆"""
        embedding = self._embed(content)
        embedding_blob = json.dumps(embedding).encode()

        with sqlite3.connect(self.db_path) as conn:
            conn.execute(
                "INSERT INTO memories (user_id, memory_type, content, embedding, importance) VALUES (?, ?, ?, ?, ?)",
                (self.user_id, memory_type, content, embedding_blob, importance)
            )

    def retrieve(self, query: str, top_k: int = 5, memory_type: str = None) -> List[Dict]:
        """语义检索最相关的记忆"""
        query_embedding = self._embed(query)

        sql = "SELECT id, memory_type, content, embedding, importance, access_count FROM memories WHERE user_id = ?"
        params = [self.user_id]
        if memory_type:
            sql += " AND memory_type = ?"
            params.append(memory_type)

        with sqlite3.connect(self.db_path) as conn:
            rows = conn.execute(sql, params).fetchall()

        # 计算相似度排序
        scored = []
        for row in rows:
            stored_embedding = json.loads(row[3].decode())
            similarity = self._cosine_similarity(query_embedding, stored_embedding)
            # 综合相似度和重要性评分
            final_score = similarity * 0.7 + row[4] * 0.3
            scored.append({
                "id": row[0],
                "type": row[1],
                "content": row[2],
                "score": round(final_score, 4)
            })

        # 更新访问计数
        top_results = sorted(scored, key=lambda x: x["score"], reverse=True)[:top_k]
        with sqlite3.connect(self.db_path) as conn:
            for r in top_results:
                conn.execute("UPDATE memories SET access_count = access_count + 1 WHERE id = ?", (r["id"],))

        return top_results

    def get_user_profile(self) -> str:
        """获取用户画像摘要(用于 System Prompt)"""
        with sqlite3.connect(self.db_path) as conn:
            preferences = conn.execute(
                "SELECT content FROM memories WHERE user_id = ? AND memory_type = 'preference' ORDER BY importance DESC LIMIT 10",
                (self.user_id,)
            ).fetchall()

        if not preferences:
            return ""

        profile_items = [row[0] for row in preferences]
        return "用户偏好:" + ";".join(profile_items)

第三层:工具记忆——经验积累系统

工具记忆记录 Agent 调用各种工具的成功/失败经验,避免重复踩坑,提升工具调用成功率。

# memory/tool_memory.py
import sqlite3
import json
from datetime import datetime
from typing import Optional, Dict, Any

class ToolMemory:
    """
    工具调用经验记忆
    记录哪些参数格式有效,哪些场景下工具会失败
    """

    def __init__(self, db_path: str = "./agent_memory.db"):
        self.db_path = db_path
        self._init_db()

    def _init_db(self):
        with sqlite3.connect(self.db_path) as conn:
            conn.execute("""
                CREATE TABLE IF NOT EXISTS tool_experiences (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    tool_name TEXT NOT NULL,
                    input_params TEXT,           -- JSON序列化的输入参数
                    output_summary TEXT,         -- 输出摘要
                    success INTEGER DEFAULT 1,   -- 1=成功, 0=失败
                    error_message TEXT,          -- 失败时的错误信息
                    execution_time_ms INTEGER,   -- 执行耗时
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
                )
            """)

    def record(self, tool_name: str, params: dict, success: bool,
               output: str = None, error: str = None, exec_time_ms: int = 0):
        """记录一次工具调用经验"""
        with sqlite3.connect(self.db_path) as conn:
            conn.execute(
                """INSERT INTO tool_experiences 
                   (tool_name, input_params, output_summary, success, error_message, execution_time_ms)
                   VALUES (?, ?, ?, ?, ?, ?)""",
                (tool_name, json.dumps(params, ensure_ascii=False), 
                 output[:200] if output else None,
                 1 if success else 0, error, exec_time_ms)
            )

    def get_tool_tips(self, tool_name: str) -> str:
        """获取工具使用提示(注入到 Agent 的工具调用指令中)"""
        with sqlite3.connect(self.db_path) as conn:
            # 查询失败案例
            failures = conn.execute(
                """SELECT input_params, error_message, COUNT(*) as cnt 
                   FROM tool_experiences 
                   WHERE tool_name = ? AND success = 0
                   GROUP BY error_message 
                   ORDER BY cnt DESC LIMIT 3""",
                (tool_name,)
            ).fetchall()

            # 查询成功案例
            successes = conn.execute(
                """SELECT input_params, output_summary, execution_time_ms
                   FROM tool_experiences 
                   WHERE tool_name = ? AND success = 1
                   ORDER BY created_at DESC LIMIT 3""",
                (tool_name,)
            ).fetchall()

        tips = []
        if failures:
            tips.append("⚠️ 注意避免以下错误:")
            for f in failures:
                tips.append(f"  - 参数 {f[0]} 曾导致错误:{f[1]}(出现{f[2]}次)")

        if successes:
            tips.append("✅ 成功调用示例:")
            for s in successes:
                tips.append(f"  - 参数 {s[0]} → {s[1]}")

        return "\n".join(tips) if tips else "暂无使用记录"

整合:三层记忆的 Agent 完整实现

# agent.py - 集成三层记忆的 Agent
from openai import OpenAI
import time
from memory.short_term import ShortTermMemory
from memory.long_term import LongTermMemory
from memory.tool_memory import ToolMemory

class MemoryAwareAgent:
    """三层记忆架构的 AI Agent"""

    def __init__(self, user_id: str):
        self.client = OpenAI()
        self.user_id = user_id

        # 三层记忆初始化
        self.short_term = ShortTermMemory(max_turns=15, max_tokens=3000)
        self.long_term = LongTermMemory(user_id=user_id)
        self.tool_memory = ToolMemory()

    def chat(self, user_message: str) -> str:
        """处理用户消息,综合三层记忆生成回复"""

        # 1. 从长期记忆检索相关信息
        relevant_memories = self.long_term.retrieve(user_message, top_k=3)
        memory_context = "\n".join([f"- {m['content']}" for m in relevant_memories])

        # 2. 获取用户画像
        user_profile = self.long_term.get_user_profile()

        # 3. 构建 System Prompt(包含用户画像和相关记忆)
        system_prompt = f"""你是一个智能助手,具备完善的记忆能力。

{f"用户背景:{user_profile}" if user_profile else ""}

{f"相关历史记忆:\n{memory_context}" if memory_context else ""}

请结合以上信息为用户提供个性化、连贯的服务。"""

        # 4. 构建完整消息列表(System + 短期记忆 + 当前消息)
        messages = [{"role": "system", "content": system_prompt}]
        messages.extend(self.short_term.get_context())
        messages.append({"role": "user", "content": user_message})

        # 5. 调用 LLM
        response = self.client.chat.completions.create(
            model="gpt-4o-mini",
            messages=messages
        )
        assistant_reply = response.choices[0].message.content

        # 6. 更新短期记忆
        self.short_term.add_turn(user_message, assistant_reply)

        # 7. 异步更新长期记忆(提取关键信息存储)
        self._extract_and_store_memory(user_message, assistant_reply)

        return assistant_reply

    def _extract_and_store_memory(self, user_msg: str, assistant_reply: str):
        """从对话中提取值得长期记忆的信息"""
        extraction_prompt = f"""分析以下对话,提取值得长期记忆的用户信息(偏好、重要事实、需求等)。
如果没有值得记录的信息,返回空列表。

用户:{user_msg}
助手:{assistant_reply}

以JSON格式返回:{{"memories": [{{"type": "preference/fact", "content": "...", "importance": 0.0-1.0}}]}}"""

        try:
            response = self.client.chat.completions.create(
                model="gpt-4o-mini",
                messages=[{"role": "user", "content": extraction_prompt}],
                response_format={"type": "json_object"}
            )
            result = json.loads(response.choices[0].message.content)
            for memory in result.get("memories", []):
                if memory.get("importance", 0) > 0.4:  # 只存重要度 > 0.4 的记忆
                    self.long_term.store(
                        content=memory["content"],
                        memory_type=memory.get("type", "fact"),
                        importance=memory.get("importance", 0.5)
                    )
        except Exception:
            pass  # 记忆提取失败不影响主流程


# 使用示例
import json

agent = MemoryAwareAgent(user_id="user_001")

# 第一轮:介绍自己
reply1 = agent.chat("你好,我是一名Python后端开发,正在研究大模型部署")
print(f"Agent: {reply1}")

# 第二轮:具体问题(Agent 会记住用户是Python开发者)
reply2 = agent.chat("我想用 vLLM 部署 Qwen,有什么建议吗?")
print(f"Agent: {reply2}")

# 新会话(跨会话记忆生效)
new_agent = MemoryAwareAgent(user_id="user_001")
reply3 = new_agent.chat("上次我们聊的 vLLM 部署,你记得吗?")
print(f"Agent: {reply3}")

三层记忆效果对比

场景 无记忆 Agent 仅短期记忆 三层记忆 Agent
跨会话个性化 ❌ 每次重新介绍 ❌ 会话结束即忘 ✅ 永久记住用户偏好
长对话连贯性 ❌ Token 超限截断 ✅ 滑动窗口保留 ✅ 压缩+保留关键信息
工具调用成功率 50~70% 50~70% ✅ 80~90%(经验积累)
个性化回答质量 低 中 高
推理 Token 消耗 低 中 中(按需检索)

生产部署注意事项

  1. 记忆隐私保护:用户有权删除自己的长期记忆,需提供 forget_all() 接口
  2. 记忆遗忘策略:设置 TTL(如90天未访问自动清除),防止数据库无限膨胀
  3. 向量库选型:数据量 < 10万条用 SQLite + 内存计算即可;> 10万条换 Chroma 或 Milvus
  4. 异步处理:记忆提取和存储应异步执行,不阻塞主流程响应时间

👤 作者简介

一枚在大中原腹地(河南)卖公有云的从业者,主营腾讯云/阿里云/华为云,曾踩坑无数,现专注AI大模型应用落地。关注公众号「AI热点日报」,围观AI前沿动态~

博客:y1cloud.cn

发表评论