饮墨

子安饮墨馀三斗,留与卿儿作赋来

AI Agent 记忆架构设计:从短期对话到长期知识的生产实践

痛点

构建 AI Agent 时,最容易被低估的组件就是 记忆系统(Memory)。一个没有记忆的 Agent 每次对话都是"失忆"状态——无法记住用户偏好、无法从历史任务中学习、无法在多轮交互中保持上下文连贯。

生产环境中常见的记忆痛点:

  • 上下文窗口溢出:对话过长导致 token 超限,早期关键信息被截断
  • 信息检索低效:所有历史一股脑塞进 prompt,成本高且噪声大
  • 跨会话遗忘:用户第二天回来,Agent 对之前的讨论一无所知
  • 多 Agent 协作断裂:子 Agent 完成任务后,上下文无法有效传递给主 Agent

方案:三层记忆架构

借鉴认知科学中人类记忆的分层模型,生产级 AI Agent 的记忆系统可以设计为三层:

层级 类型 存储周期 实现方式 典型场景
L1 工作记忆(Working Memory) 单次会话 上下文窗口 + 滑动摘要 多轮对话上下文
L2 情景记忆(Episodic Memory) 跨会话 向量数据库 + 时间索引 历史对话检索、任务回溯
L3 语义记忆(Semantic Memory) 永久 结构化知识库 + KV Store 用户画像、规则、学到的知识

实操步骤

Step 1:工作记忆 — 滑动窗口 + 递归摘要

工作记忆的核心挑战是在有限的上下文窗口内保留最有价值的信息。

from dataclasses import dataclass, field
from typing import List
import tiktoken

@dataclass
class WorkingMemory:
    """滑动窗口 + 递归摘要的工作记忆实现"""
    max_tokens: int = 8000
    summary_threshold: int = 6000  # 触发摘要压缩的阈值
    messages: List[dict] = field(default_factory=list)
    running_summary: str = ""

    def add_message(self, role: str, content: str):
        self.messages.append({"role": role, "content": content})
        if self._count_tokens() > self.summary_threshold:
            self._compress()

    def _compress(self):
        """将前半部分消息压缩为摘要,保留最近消息"""
        midpoint = len(self.messages) // 2
        old_messages = self.messages[:midpoint]
        self.messages = self.messages[midpoint:]

        # 调用 LLM 生成增量摘要
        self.running_summary = self._summarize(
            self.running_summary, old_messages
        )

    def get_context(self) -> List[dict]:
        """构建发送给 LLM 的上下文"""
        context = []
        if self.running_summary:
            context.append({
                "role": "system",
                "content": f"[对话历史摘要]\n{self.running_summary}"
            })
        context.extend(self.messages)
        return context

    def _count_tokens(self) -> int:
        enc = tiktoken.get_encoding("cl100k_base")
        total = len(enc.encode(self.running_summary))
        for msg in self.messages:
            total += len(enc.encode(msg["content"]))
        return total

    def _summarize(self, existing_summary: str, messages: List[dict]) -> str:
        """调用 LLM 做增量摘要(实际实现替换为 API 调用)"""
        # 生产中调用轻量模型(如 GPT-4o-mini)做摘要
        # 要点:保留关键决策、用户偏好、未完成任务
        ...

关键设计决策: - 摘要压缩比建议 3:1 到 5:1,过度压缩会丢失关键细节 - 摘要使用轻量模型(如 Claude Haiku、GPT-4o-mini),主对话用强模型 - 保留最近 3-5 轮原始消息不压缩,避免上下文断裂

Step 2:情景记忆 — 向量检索 + 时间衰减

情景记忆让 Agent 能够"回忆"过去的对话和任务执行经历。

import time
import numpy as np
from typing import Optional

class EpisodicMemory:
    """基于向量检索 + 时间衰减的情景记忆"""

    def __init__(self, vector_store, embedding_model, decay_factor=0.995):
        self.store = vector_store  # pgvector / Qdrant / Chroma
        self.embedder = embedding_model
        self.decay_factor = decay_factor

    def store_episode(self, content: str, metadata: dict):
        """存储一条情景记忆"""
        embedding = self.embedder.encode(content)
        self.store.upsert(
            id=metadata.get("episode_id"),
            vector=embedding,
            payload={
                "content": content,
                "timestamp": time.time(),
                "session_id": metadata.get("session_id"),
                "importance": metadata.get("importance", 0.5),
                **metadata,
            }
        )

    def recall(self, query: str, top_k: int = 5,
               time_weight: float = 0.3) -> list:
        """带时间衰减的相关记忆检索"""
        query_embedding = self.embedder.encode(query)

        # 向量相似度检索
        candidates = self.store.search(
            vector=query_embedding, limit=top_k * 3
        )

        # 综合评分 = 相似度 × (1 - time_weight) + 时效性 × time_weight
        now = time.time()
        scored = []
        for item in candidates:
            age_hours = (now - item.payload["timestamp"]) / 3600
            recency = self.decay_factor ** age_hours
            importance = item.payload.get("importance", 0.5)

            final_score = (
                item.score * (1 - time_weight) +
                recency * time_weight * 0.7 +
                importance * 0.3
            )
            scored.append((final_score, item))

        scored.sort(key=lambda x: x[0], reverse=True)
        return [item for _, item in scored[:top_k]]

    def summarize_session(self, session_id: str) -> str:
        """会话结束时生成情景摘要并存储"""
        episodes = self.store.filter(
            conditions={"session_id": session_id}
        )
        # 生成该次会话的结构化摘要
        # 包含:任务目标、关键决策、最终结果、待办事项
        ...

生产要点: - 不是每句话都存——设置 importance 评分阈值,过滤掉闲聊 - 会话结束时触发 summarize_session,将整次会话浓缩为一条高质量情景记忆 - 时间衰减因子 0.995 意味着一周前的记忆权重约为原来的 70%

Step 3:语义记忆 — 结构化知识持久存储

语义记忆存储 Agent 从交互中"学到"的知识——用户偏好、业务规则、常见问题解法。

import json
from datetime import datetime

class SemanticMemory:
    """结构化语义记忆:用户画像 + 学到的规则"""

    def __init__(self, kv_store):
        self.store = kv_store  # Redis / DynamoDB / SQLite

    def update_user_profile(self, user_id: str, facts: dict):
        """增量更新用户画像"""
        key = f"user_profile:{user_id}"
        profile = json.loads(self.store.get(key) or "{}")

        for k, v in facts.items():
            profile[k] = {
                "value": v,
                "updated_at": datetime.utcnow().isoformat(),
                "confidence": profile.get(k, {}).get("confidence", 0) + 0.1
            }

        self.store.set(key, json.dumps(profile, ensure_ascii=False))

    def learn_rule(self, rule_type: str, rule: dict):
        """存储 Agent 学到的规则/模式"""
        key = f"rules:{rule_type}"
        rules = json.loads(self.store.get(key) or "[]")

        # 去重:如果已有相似规则,增加置信度
        for existing in rules:
            if existing["pattern"] == rule["pattern"]:
                existing["confidence"] = min(
                    existing["confidence"] + 0.2, 1.0
                )
                existing["last_seen"] = datetime.utcnow().isoformat()
                self.store.set(key, json.dumps(rules, ensure_ascii=False))
                return

        rule["confidence"] = 0.5
        rule["created_at"] = datetime.utcnow().isoformat()
        rules.append(rule)
        self.store.set(key, json.dumps(rules, ensure_ascii=False))

    def get_relevant_knowledge(self, user_id: str, 
                                context: str) -> dict:
        """获取与当前上下文相关的语义知识"""
        return {
            "user_profile": self._get_profile(user_id),
            "applicable_rules": self._match_rules(context),
            "learned_preferences": self._get_preferences(user_id),
        }

语义记忆的数据模型示例:

{
  "user_profile:u_12345": {
    "timezone": {"value": "Asia/Shanghai", "confidence": 0.9},
    "preferred_language": {"value": "zh-CN", "confidence": 1.0},
    "tech_stack": {"value": ["Python", "K8s", "AWS"], "confidence": 0.8},
    "communication_style": {"value": "concise_technical", "confidence": 0.7}
  }
}

Step 4:三层记忆的协同调度

class AgentMemoryOrchestrator:
    """记忆协调器:统一管理三层记忆的读写"""

    def __init__(self, working, episodic, semantic):
        self.working = working  # L1
        self.episodic = episodic  # L2
        self.semantic = semantic  # L3

    def prepare_context(self, user_id: str, 
                        user_message: str) -> List[dict]:
        """为 LLM 调用准备完整上下文"""
        context = []

        # 1. 语义记忆:注入用户画像和规则(最稳定)
        knowledge = self.semantic.get_relevant_knowledge(
            user_id, user_message
        )
        if knowledge["user_profile"]:
            context.append({
                "role": "system",
                "content": f"[用户画像] {json.dumps(knowledge['user_profile'], ensure_ascii=False)}"
            })

        # 2. 情景记忆:检索相关历史(跨会话)
        relevant_episodes = self.episodic.recall(user_message, top_k=3)
        if relevant_episodes:
            episodes_text = "\n".join(
                [ep.payload["content"] for ep in relevant_episodes]
            )
            context.append({
                "role": "system",
                "content": f"[相关历史记忆]\n{episodes_text}"
            })

        # 3. 工作记忆:当前会话上下文(最新鲜)
        context.extend(self.working.get_context())

        # 加入当前用户消息
        context.append({"role": "user", "content": user_message})

        return context

    def post_response(self, user_id: str, 
                      user_msg: str, agent_response: str):
        """响应后更新各层记忆"""
        # 更新工作记忆
        self.working.add_message("user", user_msg)
        self.working.add_message("assistant", agent_response)

        # 异步评估是否值得存入情景记忆
        # (importance > 0.6 的交互才存储)

        # 异步提取用户偏好更新语义记忆
        # (检测到新偏好/规则时触发)

避坑指南

1. 记忆污染问题

:Agent 把错误信息或幻觉写入长期记忆,后续对话被持续误导。

解法: - 语义记忆写入设置 confidence 阈值,单次交互提取的事实 confidence 初始值低(0.3-0.5) - 多次确认后才提升 confidence,低于 0.3 的定期清理 - 关键事实写入前做一次 LLM 验证("这个信息是否可靠?")

2. 检索噪声与上下文污染

:情景记忆检索回来的内容与当前问题相关度低,反而干扰 LLM 推理。

解法: - 设置相似度下限(cosine similarity > 0.75 才纳入) - 检索结果做 reranking,用 Cross-Encoder 二次排序 - 控制注入的记忆总 token 数不超过上下文窗口的 20%

3. 存储成本失控

:所有对话都无差别存储到向量数据库,数据量指数增长,检索延迟上升。

解法: - 分级存储:热数据(7天)在内存向量库,温数据(30天)在磁盘向量库,冷数据压缩归档 - 合并策略:同一主题的多条情景记忆定期合并为一条摘要 - 容量预算:每个用户的情景记忆上限设为 1000 条,超限时按重要性淘汰

总结

生产级 AI Agent 的记忆系统不是简单的"把聊天记录存起来",而是需要:

  1. 分层设计:工作记忆(快/小/易失)→ 情景记忆(中/向量检索)→ 语义记忆(慢/结构化/持久)
  2. 主动遗忘:不是所有信息都值得记住,importance 评分 + 时间衰减是关键
  3. 异步更新:记忆写入不能阻塞主对话流程,后台异步处理
  4. 防污染机制:confidence 阈值 + 定期清理,避免错误信息持久化

技术选型建议:向量库选 pgvector(如果已有 PostgreSQL)或 Qdrant(独立部署),KV 存储用 Redis + 持久化,摘要压缩用轻量模型降低成本。整套方案在 4C8G 的单机上即可支撑千级并发用户的记忆系统。

您还没有登录,请登录后发表评论。