AI Agent记忆系统设计进阶教程

教程简介

零基础AI Agent记忆系统设计进阶教程,涵盖记忆系统架构(短期/长期/工作记忆)、向量数据库存储方案、MemGPT虚拟内存实现、LangGraph状态管理、多Agent记忆共享、记忆压缩与摘要、持久化存储策略、记忆检索优化、隐私与安全考量、生产级记忆系统架构等核心技能,适合AI架构师和高级开发者系统学习。

AI Agent记忆系统设计进阶教程

从认知科学到工程实现,构建生产级Agent记忆系统的完整指南


目录

  1. 概述与认知科学基础
  2. 记忆系统架构:短期/长期/工作记忆
  3. 向量数据库存储方案
  4. MemGPT虚拟内存实现
  5. LangGraph状态管理
  6. 多Agent记忆共享
  7. 记忆压缩与摘要
  8. 持久化存储策略
  9. 记忆检索优化
  10. 隐私与安全考量
  11. 生产级记忆系统架构

1. 概述与认知科学基础

1.1 为什么Agent需要记忆系统

大语言模型(LLM)本身是"无状态"的——每次推理都是独立的前向传播。虽然通过上下文窗口可以注入历史对话,但这种方式存在根本性限制:

  • 容量限制:即使是200K token的上下文窗口,也无法承载数月甚至数年的交互历史
  • 成本线性增长:上下文越长,推理成本越高
  • 信息检索效率:在长上下文中定位特定信息的效率远低于结构化存储
  • 跨会话断裂:上下文窗口无法跨越独立的会话

记忆系统的本质是为Agent提供持久化、结构化、可检索的经验存储,使其具备跨会话的连续性和学习能力。

1.2 人类记忆的启发

认知科学对人类记忆的研究为Agent记忆系统设计提供了重要参考:

┌──────────────────────────────────────────────────────┐
│                   人类记忆系统                         │
│                                                       │
│  感觉记忆 ──→ 短期记忆 ──→ 长期记忆                   │
│  (<1秒)      (秒~分钟)    (小时~终生)                  │
│               │              │                        │
│               │    ┌─────────┼─────────┐              │
│               │    │         │         │              │
│               ▼    ▼         ▼         ▼              │
│            工作记忆  情景记忆  语义记忆  程序记忆        │
│            (主动处理) (事件)   (知识)   (技能)          │
└──────────────────────────────────────────────────────┘
人类记忆类型 功能 Agent对应
感觉记忆 瞬时输入缓存 输入缓冲区
短期记忆 当前任务相关信息 对话上下文窗口
工作记忆 主动加工和推理 活跃状态/思维链
情景记忆 过去经历的事件 对话历史记录
语义记忆 世界知识和事实 知识库/RAG
程序记忆 技能和操作步骤 工具使用经验/技能库

2. 记忆系统架构

2.1 三层记忆架构

┌─────────────────────────────────────────────────────────────┐
│                      Agent记忆系统                           │
│                                                              │
│  ┌─────────────────────────────────────────────────────┐    │
│  │                 工作记忆 (Working Memory)             │    │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐          │    │
│  │  │当前目标   │  │活跃上下文 │  │推理状态   │          │    │
│  │  │(Goal)    │  │(Context) │  │(State)   │          │    │
│  │  └──────────┘  └──────────┘  └──────────┘          │    │
│  │  容量:有限(受上下文窗口限制)                        │    │
│  │  生命周期:单次推理/单个任务                           │    │
│  └─────────────────────────────────────────────────────┘    │
│                           │ 写入/读取                        │
│                           ▼                                 │
│  ┌─────────────────────────────────────────────────────┐    │
│  │                 短期记忆 (Short-term Memory)          │    │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐          │    │
│  │  │对话历史   │  │当前会话   │  │临时笔记   │          │    │
│  │  │(History) │  │(Session) │  │(Scratch) │          │    │
│  │  └──────────┘  └──────────┘  └──────────┘          │    │
│  │  容量:中等(数千条消息)                              │    │
│  │  生命周期:单次会话                                    │    │
│  └─────────────────────────────────────────────────────┘    │
│                           │ 编码/检索                        │
│                           ▼                                 │
│  ┌─────────────────────────────────────────────────────┐    │
│  │                 长期记忆 (Long-term Memory)           │    │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐          │    │
│  │  │用户画像   │  │知识库     │  │交互模式   │          │    │
│  │  │(Profile) │  │(KB)      │  │(Patterns)│          │    │
│  │  └──────────┘  └──────────┘  └──────────┘          │    │
│  │  容量:无限制(外部存储)                              │    │
│  │  生命周期:持久化                                     │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘

2.2 记忆生命周期

每条记忆从产生到消亡经历以下阶段:

from enum import Enum
from dataclasses import dataclass, field
from datetime import datetime
from typing import Optional, List

class MemoryStage(Enum):
    """记忆生命周期阶段"""
    PERCEPTION = "perception"      # 感知:从输入中提取原始信息
    ENCODING = "encoding"          # 编码:将信息结构化并计算嵌入向量
    CONSOLIDATION = "consolidation" # 整合:与已有记忆关联、去重、合并
    STORAGE = "storage"            # 存储:持久化到长期记忆
    RETRIEVAL = "retrieval"        # 检索:按需从存储中取出
    REFINEMENT = "refinement"      # 精炼:根据新信息更新旧记忆
    DECAY = "decay"                # 衰减:降低不常访问记忆的优先级
    FORGETTING = "forgetting"      # 遗忘:删除不再相关的记忆

@dataclass
class MemoryItem:
    """记忆条目数据结构"""
    id: str
    content: str                          # 原始内容
    embedding: Optional[List[float]] = None  # 嵌入向量
    memory_type: str = "episodic"         # episodic/semantic/procedural
    stage: MemoryStage = MemoryStage.PERCEPTION
    importance: float = 0.5               # 重要性评分 [0, 1]
    access_count: int = 0                 # 访问次数
    last_accessed: Optional[datetime] = None
    created_at: datetime = field(default_factory=datetime.now)
    metadata: dict = field(default_factory=dict)
    associations: List[str] = field(default_factory=list)  # 关联记忆ID

2.3 记忆类型详解

class EpisodicMemory:
    """情景记忆:存储具体的交互事件"""

    def __init__(self, vector_store, llm):
        self.vector_store = vector_store
        self.llm = llm

    async def store_conversation(self, session_id: str, messages: list):
        """将对话存入情景记忆"""
        # 提取关键事件
        events = await self._extract_events(messages)
        for event in events:
            embedding = await self._compute_embedding(event.summary)
            await self.vector_store.upsert(
                collection="episodic",
                id=f"{session_id}_{event.id}",
                vector=embedding,
                metadata={
                    "session_id": session_id,
                    "timestamp": event.timestamp.isoformat(),
                    "summary": event.summary,
                    "participants": event.participants,
                    "emotion": event.emotion_tag,
                    "importance": event.importance,
                }
            )

    async def _extract_events(self, messages: list):
        """用LLM从对话中提取关键事件"""
        prompt = f"""从以下对话中提取关键事件,每个事件包含:
- summary: 一句话摘要
- participants: 参与者
- emotion_tag: 情感标签
- importance: 重要性(0-1)

对话内容:
{self._format_messages(messages)}

输出JSON数组。"""
        response = await self.llm.generate(prompt)
        return self._parse_events(response)

class SemanticMemory:
    """语义记忆:存储抽象的知识和事实"""

    def __init__(self, vector_store, knowledge_graph):
        self.vector_store = vector_store
        self.knowledge_graph = knowledge_graph

    async def store_fact(self, fact: str, source: str, confidence: float):
        """存储一条事实性知识"""
        # 检查是否与已有知识矛盾
        conflicts = await self._check_conflicts(fact)
        if conflicts:
            # 存储为待验证状态,而非直接覆盖
            await self._handle_conflict(fact, conflicts)
            return

        embedding = await self._compute_embedding(fact)
        await self.vector_store.upsert(
            collection="semantic",
            id=self._generate_id(fact),
            vector=embedding,
            metadata={
                "fact": fact,
                "source": source,
                "confidence": confidence,
                "verified": True,
            }
        )
        # 同步更新知识图谱
        entities, relations = await self._extract_knowledge_graph(fact)
        await self.knowledge_graph.add_triples(entities, relations)

3. 向量数据库存储方案

3.1 主流向量数据库对比

数据库 部署方式 最大向量数 索引类型 混合搜索 适用场景
Pinecone 全托管SaaS 10亿+ 专有 快速上线,无需运维
Weaviate 自托管/SaaS 10亿+ HNSW 需要混合搜索
Qdrant 自托管/云 10亿+ HNSW 高性能需求
Milvus 自托管 100亿+ 多种 ⚠️ 超大规模
ChromaDB 嵌入式 百万级 HNSW 开发原型、小规模
pgvector PostgreSQL扩展 千万级 IVFFlat/HNSW 已有PG基础设施

3.2 向量数据库核心操作

from qdrant_client import QdrantClient
from qdrant_client.models import (
    Distance, VectorParams, PointStruct,
    Filter, FieldCondition, MatchValue
)
import numpy as np

class VectorMemoryStore:
    """基于Qdrant的向量记忆存储"""

    def __init__(self, host: str = "localhost", port: int = 6333):
        self.client = QdrantClient(host=host, port=port)
        self._ensure_collections()

    def _ensure_collections(self):
        """初始化记忆集合"""
        collections = {
            "episodic": 1536,    # 情景记忆向量维度
            "semantic": 1536,    # 语义记忆向量维度
            "procedural": 768,  # 程序记忆向量维度(可以用小模型)
        }
        for name, dim in collections.items():
            if not self.client.collection_exists(name):
                self.client.create_collection(
                    collection_name=name,
                    vectors_config=VectorParams(
                        size=dim,
                        distance=Distance.COSINE
                    )
                )

    async def store_memory(
        self,
        collection: str,
        memory_id: str,
        content: str,
        embedding: list[float],
        metadata: dict
    ):
        """存储一条记忆"""
        self.client.upsert(
            collection_name=collection,
            points=[
                PointStruct(
                    id=self._hash_id(memory_id),
                    vector=embedding,
                    payload={
                        "content": content,
                        "created_at": datetime.now().isoformat(),
                        **metadata
                    }
                )
            ]
        )

    async def search_memory(
        self,
        collection: str,
        query_embedding: list[float],
        top_k: int = 5,
        filters: dict = None,
        score_threshold: float = 0.7
    ) -> list[dict]:
        """语义检索记忆"""
        query_filter = None
        if filters:
            conditions = [
                FieldCondition(key=k, match=MatchValue(value=v))
                for k, v in filters.items()
            ]
            query_filter = Filter(must=conditions)

        results = self.client.search(
            collection_name=collection,
            query_vector=query_embedding,
            limit=top_k,
            query_filter=query_filter,
            score_threshold=score_threshold
        )

        return [
            {
                "id": hit.id,
                "content": hit.payload.get("content"),
                "score": hit.score,
                "metadata": {k: v for k, v in hit.payload.items() if k != "content"}
            }
            for hit in results
        ]

3.3 混合搜索策略

单纯依赖向量相似度搜索存在局限——语义相近但关键词不同的查询可能遗漏。混合搜索结合了向量检索和关键词检索的优势:

from dataclasses import dataclass

@dataclass
class HybridSearchConfig:
    """混合搜索配置"""
    vector_weight: float = 0.7    # 向量搜索权重
    keyword_weight: float = 0.3   # 关键词搜索权重
    fusion_method: str = "rrf"    # 融合方法:rrf / weighted

class HybridMemoryRetriever:
    """混合检索器:向量+关键词"""

    def __init__(self, vector_store, bm25_index):
        self.vector_store = vector_store
        self.bm25_index = bm25_index

    async def search(
        self,
        query: str,
        query_embedding: list[float],
        top_k: int = 10,
        config: HybridSearchConfig = None
    ) -> list[dict]:
        config = config or HybridSearchConfig()

        # 向量检索
        vector_results = await self.vector_store.search_memory(
            collection="episodic",
            query_embedding=query_embedding,
            top_k=top_k * 2  # 取更多候选用于融合
        )

        # BM25关键词检索
        keyword_results = self.bm25_index.search(query, top_k=top_k * 2)

        # 融合结果(Reciprocal Rank Fusion)
        if config.fusion_method == "rrf":
            return self._rrf_fusion(
                vector_results, keyword_results,
                config.vector_weight, config.keyword_weight,
                top_k
            )
        else:
            return self._weighted_fusion(
                vector_results, keyword_results,
                config.vector_weight, config.keyword_weight,
                top_k
            )

    def _rrf_fusion(self, r1, r2, w1, w2, k=60):
        """RRF(Reciprocal Rank Fusion)融合算法"""
        scores = {}
        for rank, item in enumerate(r1):
            scores[item["id"]] = scores.get(item["id"], 0) + w1 / (k + rank + 1)
        for rank, item in enumerate(r2):
            scores[item["id"]] = scores.get(item["id"], 0) + w2 / (k + rank + 1)

        # 按融合分数排序
        sorted_ids = sorted(scores.keys(), key=lambda x: scores[x], reverse=True)
        id_to_item = {item["id"]: item for item in r1 + r2}
        return [
            {**id_to_item[rid], "fusion_score": scores[rid]}
            for rid in sorted_ids[:10]
        ]

4. MemGPT虚拟内存实现

4.1 MemGPT核心思想

MemGPT(2023年,UC Berkeley)提出了一个精妙的类比:将LLM的上下文窗口视为"主内存(RAM)",将外部存储视为"磁盘(Disk)",通过操作系统的虚拟内存管理思想来管理Agent的记忆。

┌─────────────────────────────────────────────┐
│              MemGPT架构                       │
│                                              │
│  ┌─────────────────────────────────────┐    │
│  │        上下文窗口(主内存/RAM)        │    │
│  │                                      │    │
│  │  ┌─────────────────────────────┐    │    │
│  │  │ 系统指令 (System Instructions)│    │    │
│  │  ├─────────────────────────────┤    │    │
│  │  │ 工作上下文 (Working Context) │    │    │
│  │  │ - 用户信息摘要               │    │    │
│  │  │ - 当前任务状态               │    │    │
│  │  ├─────────────────────────────┤    │    │
│  │  │ 对话队列 (FIFO Queue)       │    │    │
│  │  │ - 最近N条消息               │    │    │
│  │  └─────────────────────────────┘    │    │
│  └──────────────┬──────────────────────┘    │
│                 │ 页面换入/换出               │
│                 ▼                            │
│  ┌─────────────────────────────────────┐    │
│  │        外部存储(磁盘/Disk)          │    │
│  │                                      │    │
│  │  ┌──────────┐  ┌──────────┐        │    │
│  │  │ 档案存储  │  │ 回忆存储  │        │    │
│  │  │(Archival)│  │(Recall)  │        │    │
│  │  └──────────┘  └──────────┘        │    │
│  └─────────────────────────────────────┘    │
└─────────────────────────────────────────────┘

4.2 MemGPT核心函数实现

import json
from typing import Callable
from dataclasses import dataclass, field

@dataclass
class MemGPTAgent:
    """MemGPT风格的Agent,实现虚拟内存管理"""

    system_prompt: str
    max_context_tokens: int = 8000
    archival_storage: dict = field(default_factory=dict)  # 长期存储
    recall_storage: list = field(default_factory=list)    # 对话历史存储
    working_context: dict = field(default_factory=dict)   # 工作上下文
    message_queue: list = field(default_factory=list)     # 消息队列

    def get_core_memory_str(self) -> str:
        """生成核心记忆的字符串表示"""
        return f"""[系统指令]
{self.system_prompt}

[工作上下文]
{json.dumps(self.working_context, ensure_ascii=False, indent=2)}

[当前对话队列 - 最近{len(self.message_queue)}条]
{self._format_messages(self.message_queue[-10:])}"""

    def inner_thoughts(self, observation: str) -> dict:
        """Agent的内部思考过程,决定下一步操作"""
        prompt = f"""你是一个具有虚拟内存管理能力的AI Agent。
你有三种记忆存储:
1. 核心记忆(当前上下文,已加载)
2. 回忆存储(全部对话历史,需搜索)
3. 档案存储(长期知识,需搜索)

可用操作:
- send_message(message): 向用户发送消息
- core_memory_append(key, value): 向核心记忆追加信息
- core_memory_replace(key, old_value, new_value): 替换核心记忆中的信息
- archival_memory_insert(content): 向档案存储写入长期记忆
- archival_memory_search(query): 在档案存储中搜索
- conversation_search(query): 在对话历史中搜索

当前状态:
{self.get_core_memory_str()}

用户输入:{observation}

请决定下一步操作(输出JSON格式的function_call)。"""

        # 调用LLM生成操作决策
        response = self.llm.generate(prompt)
        return self._parse_function_call(response)

    async def execute_step(self, user_input: str):
        """执行一步操作(核心主循环)"""
        # 将用户输入加入消息队列
        self.message_queue.append({"role": "user", "content": user_input})

        # 检查上下文窗口是否接近上限
        current_tokens = self._count_tokens(self.get_core_memory_str())
        if current_tokens > self.max_context_tokens * 0.9:
            await self._evict_messages()

        # Agent内部思考并执行操作
        while True:
            action = self.inner_thoughts(user_input)

            if action["function"] == "send_message":
                # 最终动作:向用户回复
                self.message_queue.append({
                    "role": "assistant",
                    "content": action["args"]["message"]
                })
                return action["args"]["message"]

            elif action["function"] == "core_memory_append":
                key = action["args"]["key"]
                value = action["args"]["value"]
                if key in self.working_context:
                    self.working_context[key] += f"\n{value}"
                else:
                    self.working_context[key] = value

            elif action["function"] == "archival_memory_insert":
                content = action["args"]["content"]
                memory_id = f"mem_{len(self.archival_storage)}"
                embedding = await self._compute_embedding(content)
                self.archival_storage[memory_id] = {
                    "content": content,
                    "embedding": embedding,
                    "timestamp": datetime.now().isoformat()
                }

            elif action["function"] == "archival_memory_search":
                query = action["args"]["query"]
                results = await self._search_archival(query)
                # 将搜索结果注入上下文
                self.message_queue.append({
                    "role": "system",
                    "content": f"[档案搜索结果]\n{self._format_search_results(results)}"
                })

    async def _evict_messages(self):
        """消息换出:将旧消息从上下文迁移到回忆存储"""
        # 保留最近的消息在队列中
        keep_count = len(self.message_queue) // 2
        evicted = self.message_queue[:-keep_count]
        self.message_queue = self.message_queue[-keep_count:]

        # 将换出的消息存入回忆存储
        for msg in evicted:
            self.recall_storage.append(msg)

        # 在队列中插入摘要
        summary = await self._summarize_messages(evicted)
        self.message_queue.insert(0, {
            "role": "system",
            "content": f"[以下是之前对话的摘要]\n{summary}"
        })

4.3 MemGPT的优势与局限

维度 优势 局限
上下文管理 动态换入换出,有效利用有限窗口 多次换入换出可能丢失微妙上下文
长期记忆 支持近乎无限的记忆容量 依赖检索质量,可能遗漏重要信息
用户体验 跨会话保持连续性 初始设置复杂,需要调参
成本 减少不必要的长上下文调用 每次换入换出都需要额外的LLM调用

5. LangGraph状态管理

5.1 LangGraph中的记忆概念

LangGraph(LangChain团队开发)是一个基于图的Agent编排框架,其状态管理机制天然适合构建记忆系统:

from typing import TypedDict, Annotated, Sequence
from langgraph.graph import StateGraph, END
from langchain_core.messages import BaseMessage
import operator

class AgentState(TypedDict):
    """Agent状态定义——记忆系统的载体"""
    # 对话消息(自动管理)
    messages: Annotated[Sequence[BaseMessage], operator.add]
    # 长期记忆(跨节点持久化)
    long_term_memory: dict
    # 当前任务上下文
    current_task: str
    # 检索到的相关记忆
    retrieved_memories: list[str]
    # Agent的内部反思
    reflection: str

def memory_retrieval_node(state: AgentState) -> dict:
    """记忆检索节点:从长期记忆中检索相关信息"""
    latest_message = state["messages"][-1].content

    # 语义检索相关记忆
    relevant_memories = vector_store.search(
        query=latest_message,
        collection="long_term",
        top_k=5
    )

    return {
        "retrieved_memories": [m["content"] for m in relevant_memories]
    }

def reasoning_node(state: AgentState) -> dict:
    """推理节点:基于记忆和当前输入进行思考"""
    context = "\n".join(state["retrieved_memories"])
    latest_msg = state["messages"][-1].content

    prompt = f"""基于以下信息回答用户问题:

相关记忆:
{context}

长期记忆摘要:
{state['long_term_memory'].get('user_summary', '暂无')}

用户问题:{latest_msg}"""

    response = llm.generate(prompt)
    return {
        "messages": [AIMessage(content=response)],
        "reflection": f"回答了关于{latest_msg[:30]}的问题"
    }

def memory_consolidation_node(state: AgentState) -> dict:
    """记忆整合节点:将新交互写入长期记忆"""
    latest_interaction = f"""
用户:{state['messages'][-2].content}
助手:{state['messages'][-1].content}
"""

    # 提取值得记忆的信息
    extraction_prompt = f"""从以下交互中提取值得长期记忆的信息:
{latest_interaction}

提取格式:
- 用户偏好:...
- 新知识:...
- 任务状态:...
如果没有值得记忆的内容,返回"无"。"""

    extracted = llm.generate(extraction_prompt)

    if extracted != "无":
        # 更新长期记忆
        updated_memory = state["long_term_memory"].copy()
        updated_memory["recent_extractions"] = extracted

        # 存入向量数据库
        vector_store.upsert(
            collection="long_term",
            content=latest_interaction,
            metadata={"extraction": extracted}
        )

        return {"long_term_memory": updated_memory}

    return {}

# 构建带记忆的Agent图
def build_memory_agent():
    graph = StateGraph(AgentState)

    # 添加节点
    graph.add_node("retrieve_memory", memory_retrieval_node)
    graph.add_node("reason", reasoning_node)
    graph.add_node("consolidate_memory", memory_consolidation_node)

    # 定义边
    graph.set_entry_point("retrieve_memory")
    graph.add_edge("retrieve_memory", "reason")
    graph.add_edge("reason", "consolidate_memory")
    graph.add_edge("consolidate_memory", END)

    return graph.compile()

5.2 状态持久化

LangGraph支持将状态持久化到外部存储,实现跨会话的记忆保持:

from langgraph.checkpoint.sqlite import SqliteSaver

# 使用SQLite作为检查点存储
memory = SqliteSaver.from_conn_string(":memory:")

# 编译时绑定检查点
graph = build_memory_agent()
app = graph.compile(checkpointer=memory)

# 使用时指定thread_id实现会话隔离
config = {"configurable": {"thread_id": "user_123"}}

# 第一次对话
result1 = await app.ainvoke(
    {"messages": [HumanMessage(content="我叫张三,喜欢Python")]},
    config=config
)

# 第二次对话(状态自动恢复)
result2 = await app.ainvoke(
    {"messages": [HumanMessage(content="我之前说我喜欢什么语言?")]},
    config=config
)
# Agent能回忆起用户喜欢Python

6. 多Agent记忆共享

6.1 共享记忆架构

当多个Agent协作时,记忆共享成为关键挑战:

┌──────────────────────────────────────────────────────┐
│                 共享记忆层                             │
│  ┌──────────────────────────────────────────────┐    │
│  │           共享记忆总线 (Shared Memory Bus)     │    │
│  │                                               │    │
│  │  ┌─────────┐  ┌─────────┐  ┌─────────┐      │    │
│  │  │任务状态  │  │共享知识  │  │协作历史  │      │    │
│  │  │(Task)   │  │(KB)     │  │(History)│      │    │
│  │  └─────────┘  └─────────┘  └─────────┘      │    │
│  └──────────────────────────────────────────────┘    │
│        ▲              ▲              ▲                │
│        │              │              │                │
│  ┌─────┴──────┐ ┌─────┴──────┐ ┌─────┴──────┐      │
│  │ Agent A    │ │ Agent B    │ │ Agent C    │      │
│  │ (规划)     │ │ (执行)     │ │ (验证)     │      │
│  │            │ │            │ │            │      │
│  │ 私有记忆   │ │ 私有记忆   │ │ 私有记忆   │      │
│  │ + 共享视图 │ │ + 共享视图 │ │ + 共享视图 │      │
│  └────────────┘ └────────────┘ └────────────┘      │
└──────────────────────────────────────────────────────┘

6.2 实现方案

from enum import Enum
from dataclasses import dataclass, field
from typing import Set
import asyncio

class MemoryScope(Enum):
    """记忆作用域"""
    PRIVATE = "private"      # 仅创建者可见
    SHARED = "shared"        # 所有Agent可见
    DELEGATED = "delegated"  # 指定Agent可见

@dataclass
class SharedMemory:
    """共享记忆条目"""
    id: str
    content: str
    creator: str             # 创建者Agent ID
    scope: MemoryScope
    visible_to: Set[str] = field(default_factory=set)  # DELEGATED模式下可见的Agent列表
    version: int = 1
    lock_holder: str = None  # 当前持有写锁的Agent

class MultiAgentMemoryManager:
    """多Agent记忆管理器"""

    def __init__(self):
        self.shared_store: dict[str, SharedMemory] = {}
        self.agent_stores: dict[str, dict] = {}  # 每个Agent的私有存储
        self._locks: dict[str, asyncio.Lock] = {}

    def register_agent(self, agent_id: str):
        """注册一个Agent"""
        self.agent_stores[agent_id] = {}

    async def write_memory(
        self,
        agent_id: str,
        content: str,
        scope: MemoryScope = MemoryScope.PRIVATE,
        visible_to: Set[str] = None
    ) -> str:
        """写入记忆"""
        memory_id = f"mem_{agent_id}_{len(self.shared_store)}"

        if scope == MemoryScope.PRIVATE:
            # 私有记忆:仅存入Agent自己的存储
            self.agent_stores[agent_id][memory_id] = {
                "content": content,
                "scope": scope.value
            }
        else:
            # 共享/委托记忆:存入共享存储
            self.shared_store[memory_id] = SharedMemory(
                id=memory_id,
                content=content,
                creator=agent_id,
                scope=scope,
                visible_to=visible_to or set()
            )

        return memory_id

    async def read_memory(
        self,
        agent_id: str,
        query: str,
        include_private: bool = True,
        include_shared: bool = True
    ) -> list[dict]:
        """读取记忆——遵守访问控制"""
        results = []

        if include_private:
            # 搜索自己的私有记忆
            for mid, mem in self.agent_stores.get(agent_id, {}).items():
                if self._relevance_score(query, mem["content"]) > 0.3:
                    results.append({"id": mid, **mem, "source": "private"})

        if include_shared:
            # 搜索共享记忆
            for mid, mem in self.shared_store.items():
                if self._can_access(agent_id, mem):
                    if self._relevance_score(query, mem.content) > 0.3:
                        results.append({
                            "id": mid,
                            "content": mem.content,
                            "creator": mem.creator,
                            "scope": mem.scope.value,
                            "source": "shared"
                        })

        # 按相关性排序
        results.sort(key=lambda x: self._relevance_score(query, x["content"]), reverse=True)
        return results[:10]

    def _can_access(self, agent_id: str, memory: SharedMemory) -> bool:
        """检查Agent是否有权访问该记忆"""
        if memory.scope == MemoryScope.SHARED:
            return True
        if memory.scope == MemoryScope.DELEGATED:
            return agent_id in memory.visible_to or agent_id == memory.creator
        return False

    async def sync_shared_context(self, agent_id: str) -> str:
        """同步共享上下文:生成Agent可见的所有共享记忆摘要"""
        visible_memories = [
            mem for mem in self.shared_store.values()
            if self._can_access(agent_id, mem)
        ]

        if not visible_memories:
            return "暂无共享记忆。"

        # 按时间排序,取最近的
        visible_memories.sort(key=lambda m: m.version, reverse=True)
        recent = visible_memories[:20]

        summary = "共享记忆摘要:\n"
        for mem in recent:
            summary += f"- [{mem.creator}] {mem.content[:100]}...\n"
        return summary

    def _relevance_score(self, query: str, content: str) -> float:
        """简单的相关性评分(实际应用中应使用嵌入向量)"""
        query_words = set(query.lower().split())
        content_words = set(content.lower().split())
        overlap = len(query_words & content_words)
        return overlap / max(len(query_words), 1)

7. 记忆压缩与摘要

7.1 为什么需要压缩

随着交互的积累,记忆存储会快速增长。压缩和摘要机制确保:

  • 存储成本可控
  • 检索效率不随时间退化
  • 重要信息不被海量细节淹没

7.2 分层压缩策略

class MemoryCompressor:
    """记忆压缩器——实现分层压缩策略"""

    def __init__(self, llm, embedding_model):
        self.llm = llm
        self.embedding_model = embedding_model

    async def compress_conversation(
        self,
        messages: list[dict],
        target_length: int = 500
    ) -> str:
        """将对话压缩为目标长度的摘要"""
        # 第一层:提取关键信息点
        key_points = await self._extract_key_points(messages)

        # 第二层:生成结构化摘要
        summary = await self._generate_structured_summary(key_points, target_length)

        # 第三层:压缩至目标长度
        if len(summary) > target_length:
            summary = await self._abstractive_compression(summary, target_length)

        return summary

    async def _extract_key_points(self, messages: list[dict]) -> list[str]:
        """从对话中提取关键信息点"""
        conversation = "\n".join(
            f"{m['role']}: {m['content']}" for m in messages
        )
        prompt = f"""从以下对话中提取所有关键信息点,每个点用一句话概括。
重点关注:
1. 用户表达的偏好或需求
2. 做出的决策或达成的共识
3. 待办事项或承诺
4. 重要的事实或数据

对话内容:
{conversation}

输出格式:每行一个关键点,以"- "开头。"""

        response = await self.llm.agenerate(prompt)
        return [line.strip("- ") for line in response.strip().split("\n") if line.startswith("- ")]

    async def _generate_structured_summary(
        self,
        key_points: list[str],
        target_length: int
    ) -> str:
        """生成结构化摘要"""
        points_text = "\n".join(f"- {p}" for p in key_points)
        prompt = f"""将以下关键点整合为一段结构化摘要(目标长度:{target_length}字):

{points_points}

要求:
1. 保留所有重要信息
2. 合并重复或相似的点
3. 按主题分组组织
4. 使用简洁清晰的语言"""

        return await self.llm.agenerate(prompt)

    async def compress_memories_batch(
        self,
        memories: list[dict],
        max_memories: int = 100
    ) -> list[dict]:
        """批量压缩记忆:将相似记忆合并"""
        if len(memories) <= max_memories:
            return memories

        # 按语义聚类
        embeddings = [
            await self.embedding_model.embed(m["content"])
            for m in memories
        ]
        clusters = self._cluster_embeddings(embeddings, n_clusters=max_memories)

        # 每个聚类生成一条合并记忆
        compressed = []
        for cluster_indices in clusters:
            cluster_memories = [memories[i] for i in cluster_indices]
            merged_content = await self._merge_memories(cluster_memories)

            # 保留最高重要性的元数据
            max_importance = max(m.get("importance", 0.5) for m in cluster_memories)

            compressed.append({
                "content": merged_content,
                "importance": max_importance,
                "merged_from": len(cluster_memories),
                "original_ids": [m["id"] for m in cluster_memories]
            })

        return compressed

    async def _merge_memories(self, memories: list[dict]) -> str:
        """将多条相关记忆合并为一条"""
        contents = "\n".join(f"- {m['content']}" for m in memories)
        prompt = f"""将以下相关记忆合并为一条简洁的记忆:

{contents}

要求:保留所有关键信息,去除重复,生成一条综合记忆。"""
        return await self.llm.agenerate(prompt)

7.3 渐进式摘要

class ProgressiveSummarizer:
    """渐进式摘要:按时间层级逐步压缩"""

    LEVELS = {
        "raw": None,          # 原始对话
        "level_1": 0.5,       # 第一层:保留50%
        "level_2": 0.25,      # 第二层:保留25%
        "level_3": 0.1,       # 第三层:保留10%
    }

    async def summarize_timeline(
        self,
        memories: list[dict],
        llm
    ) -> dict[str, list[dict]]:
        """按时间层级进行渐进式摘要"""
        now = datetime.now()
        result = {"recent": [], "day_level": [], "week_level": [], "month_level": []}

        for memory in memories:
            age = now - memory["created_at"]

            if age < timedelta(hours=6):
                # 最近6小时:保留原始内容
                result["recent"].append(memory)
            elif age < timedelta(days=1):
                # 6-24小时:第一层压缩
                memory["content"] = await self._compress(memory["content"], 0.5, llm)
                result["day_level"].append(memory)
            elif age < timedelta(days=7):
                # 1-7天:第二层压缩
                memory["content"] = await self._compress(memory["content"], 0.25, llm)
                result["week_level"].append(memory)
            else:
                # 7天以上:第三层压缩
                memory["content"] = await self._compress(memory["content"], 0.1, llm)
                result["month_level"].append(memory)

        return result

    async def _compress(self, content: str, ratio: float, llm) -> str:
        """按比例压缩内容"""
        target = int(len(content) * ratio)
        if target < 50:
            return content[:50] + "..."

        prompt = f"""将以下内容压缩至约{target}字,保留核心信息:

{content}"""
        return await llm.agenerate(prompt)

8. 持久化存储策略

8.1 存储层选型

存储层 用途 推荐方案 读写延迟
热存储 工作记忆、近期对话 Redis / 内存 <1ms
温存储 短期记忆、会话历史 PostgreSQL + pgvector 1-10ms
冷存储 归档记忆、历史数据 S3 + 向量数据库 10-100ms
元数据 记忆索引、用户画像 PostgreSQL / MongoDB 1-5ms

8.2 多层存储实现

import json
import hashlib
from datetime import datetime, timedelta
from typing import Optional

class TieredMemoryStorage:
    """分层存储管理器"""

    def __init__(self, redis_client, pg_pool, s3_client, vector_store):
        self.redis = redis_client
        self.pg = pg_pool
        self.s3 = s3_client
        self.vector_store = vector_store

    async def write(self, memory: dict):
        """写入记忆——根据热度分层存储"""
        tier = self._determine_tier(memory)

        # 1. 写入向量索引(所有层都写)
        await self.vector_store.upsert(
            collection="memory_index",
            id=memory["id"],
            vector=memory["embedding"],
            metadata={
                "tier": tier,
                "created_at": memory["created_at"].isoformat(),
                "importance": memory.get("importance", 0.5),
                "type": memory.get("type", "episodic"),
            }
        )

        # 2. 根据层级写入对应存储
        if tier == "hot":
            await self._write_hot(memory)
        elif tier == "warm":
            await self._write_warm(memory)
        else:
            await self._write_cold(memory)

    async def read(self, memory_id: str) -> Optional[dict]:
        """读取记忆——先热后冷,自动提升热度"""
        # 尝试从热存储读取
        result = await self._read_hot(memory_id)
        if result:
            return result

        # 尝试从温存储读取
        result = await self._read_warm(memory_id)
        if result:
            # 热度提升:写入热存储
            await self._write_hot(result)
            return result

        # 从冷存储读取
        result = await self._read_cold(memory_id)
        if result:
            # 写入温存储
            await self._write_warm(result)
            return result

        return None

    async def search(self, query_embedding: list, top_k: int = 10) -> list[dict]:
        """跨层检索——向量索引定位,分层读取"""
        # 1. 向量索引搜索
        index_results = await self.vector_store.search(
            collection="memory_index",
            query_vector=query_embedding,
            top_k=top_k
        )

        # 2. 从对应层级读取完整内容
        results = []
        for hit in index_results:
            memory_id = hit["id"]
            tier = hit["metadata"]["tier"]

            if tier == "hot":
                content = await self._read_hot(memory_id)
            elif tier == "warm":
                content = await self._read_warm(memory_id)
            else:
                content = await self._read_cold(memory_id)

            if content:
                results.append({
                    **content,
                    "relevance_score": hit["score"],
                    "storage_tier": tier
                })

        return results

    def _determine_tier(self, memory: dict) -> str:
        """判断记忆应存储在哪个层级"""
        age = datetime.now() - memory["created_at"]
        importance = memory.get("importance", 0.5)

        if age < timedelta(hours=24) or importance > 0.8:
            return "hot"
        elif age < timedelta(days=30) or importance > 0.5:
            return "warm"
        else:
            return "cold"

    async def _write_hot(self, memory: dict):
        """写入Redis"""
        key = f"memory:{memory['id']}"
        await self.redis.setex(
            key,
            timedelta(hours=24),
            json.dumps(memory, default=str)
        )

    async def _write_warm(self, memory: dict):
        """写入PostgreSQL"""
        async with self.pg.acquire() as conn:
            await conn.execute("""
                INSERT INTO memories (id, content, metadata, embedding, created_at)
                VALUES ($1, $2, $3, $4, $5)
                ON CONFLICT (id) DO UPDATE SET
                    content = EXCLUDED.content,
                    metadata = EXCLUDED.metadata
            """,
                memory["id"],
                memory["content"],
                json.dumps(memory.get("metadata", {})),
                memory.get("embedding"),
                memory["created_at"]
            )

    async def _write_cold(self, memory: dict):
        """写入S3"""
        key = f"memories/{memory['id']}.json"
        await self.s3.put_object(
            Bucket="agent-memories",
            Key=key,
            Body=json.dumps(memory, default=str),
            ContentType="application/json"
        )

    async def _read_hot(self, memory_id: str) -> Optional[dict]:
        data = await self.redis.get(f"memory:{memory_id}")
        return json.loads(data) if data else None

    async def _read_warm(self, memory_id: str) -> Optional[dict]:
        async with self.pg.acquire() as conn:
            row = await conn.fetchrow(
                "SELECT * FROM memories WHERE id = $1", memory_id
            )
            return dict(row) if row else None

    async def _read_cold(self, memory_id: str) -> Optional[dict]:
        try:
            resp = await self.s3.get_object(
                Bucket="agent-memories",
                Key=f"memories/{memory_id}.json"
            )
            return json.loads(await resp["Body"].read())
        except Exception:
            return None

9. 记忆检索优化

9.1 检索质量评估指标

指标 含义 目标值
Recall@K 前K个结果中包含相关记忆的比例 >0.85
MRR 第一个相关结果的排名倒数均值 >0.7
Precision@K 前K个结果中相关结果的比例 >0.6
NDCG 考虑排名位置的增益累积 >0.75
延迟P95 95分位检索延迟 <100ms

9.2 查询改写

用户输入往往不够精确,查询改写可以显著提升检索效果:

class QueryRewriter:
    """查询改写器:优化检索查询"""

    def __init__(self, llm):
        self.llm = llm

    async def rewrite(self, query: str, context: list[dict] = None) -> list[str]:
        """生成多个改写查询,提高召回率"""
        context_str = ""
        if context:
            recent = context[-3:]  # 最近3轮对话
            context_str = "\n".join(f"{m['role']}: {m['content']}" for m in recent)

        prompt = f"""将以下用户查询改写为3个不同的检索查询,以提高记忆检索的召回率。

用户查询:{query}
对话上下文:
{context_str}

改写要求:
1. 第一个:扩展同义词和相关概念
2. 第二个:提取核心实体和关键事实
3. 第三个:从用户意图角度改写

输出格式(每行一个):
1. ...
2. ...
3. ..."""

        response = await self.llm.agenerate(prompt)
        rewrites = [
            line.split(". ", 1)[1] if ". " in line else line
            for line in response.strip().split("\n")
            if line.strip()
        ]
        return [query] + rewrites  # 包含原始查询

    async def decompose(self, complex_query: str) -> list[str]:
        """复杂查询分解:将多跳问题拆解为子查询"""
        prompt = f"""将以下复杂查询分解为可以独立检索的子查询:

复杂查询:{complex_query}

分解原则:
1. 每个子查询应该能独立从记忆库中检索到答案
2. 保持语义完整性
3. 最多分解为5个子查询

输出格式(每行一个子查询):"""

        response = await self.llm.agenerate(prompt)
        return [line.strip() for line in response.strip().split("\n") if line.strip()]

9.3 重排序(Reranking)

from sentence_transformers import CrossEncoder

class MemoryReranker:
    """记忆重排序器:使用交叉编码器精排"""

    def __init__(self, model_name: str = "BAAI/bge-reranker-large"):
        self.model = CrossEncoder(model_name)

    async def rerank(
        self,
        query: str,
        candidates: list[dict],
        top_k: int = 5
    ) -> list[dict]:
        """对候选记忆进行精排"""
        if not candidates:
            return []

        # 构造(query, candidate)对
        pairs = [(query, c["content"]) for c in candidates]

        # 计算相关性分数
        scores = self.model.predict(pairs)

        # 按分数排序
        scored_candidates = [
            {**candidate, "rerank_score": float(score)}
            for candidate, score in zip(candidates, scores)
        ]
        scored_candidates.sort(key=lambda x: x["rerank_score"], reverse=True)

        return scored_candidates[:top_k]

class RetrievalPipeline:
    """完整检索管道:查询改写 → 向量检索 → 重排序"""

    def __init__(self, rewriter, vector_store, reranker, compressor):
        self.rewriter = rewriter
        self.vector_store = vector_store
        self.reranker = reranker
        self.compressor = compressor

    async def retrieve(
        self,
        query: str,
        context: list[dict] = None,
        top_k: int = 5
    ) -> list[dict]:
        # Step 1: 查询改写
        queries = await self.rewriter.rewrite(query, context)

        # Step 2: 多查询向量检索
        all_candidates = []
        seen_ids = set()
        for q in queries:
            embedding = await self._compute_embedding(q)
            results = await self.vector_store.search(
                collection="memory_index",
                query_vector=embedding,
                top_k=top_k * 2
            )
            for r in results:
                if r["id"] not in seen_ids:
                    all_candidates.append(r)
                    seen_ids.add(r["id"])

        # Step 3: 重排序
        reranked = await self.reranker.rerank(query, all_candidates, top_k)

        # Step 4: 上下文压缩(只保留与查询相关的部分)
        compressed = []
        for mem in reranked:
            relevant_part = await self.compressor.extract_relevant(
                query, mem["content"]
            )
            compressed.append({**mem, "content": relevant_part})

        return compressed

10. 隐私与安全考量

10.1 威胁模型

威胁 描述 风险等级 防御措施
记忆泄露 Agent向非授权方暴露私有记忆 访问控制+加密
记忆注入 恶意输入写入有害记忆 输入验证+内容过滤
记忆投毒 通过多轮交互逐渐污染记忆 记忆审计+版本控制
推断攻击 通过行为模式推断敏感信息 差分隐私
存储泄露 持久化存储被非法访问 加密存储+访问控制

10.2 安全实现

from cryptography.fernet import Fernet
import hashlib
import re

class SecureMemoryManager:
    """安全记忆管理器"""

    def __init__(self, encryption_key: bytes, content_filter):
        self.cipher = Fernet(encryption_key)
        self.content_filter = content_filter
        self.audit_log = []

    async def secure_write(self, user_id: str, content: str, memory: dict) -> dict:
        """安全写入记忆"""
        # 1. 内容过滤:检测并屏蔽敏感信息
        sanitized = await self._sanitize_content(content)

        # 2. 加密存储
        encrypted_content = self.cipher.encrypt(sanitized.encode()).decode()

        # 3. 审计日志
        self.audit_log.append({
            "action": "write",
            "user_id": user_id,
            "memory_id": memory["id"],
            "timestamp": datetime.now().isoformat(),
            "content_hash": hashlib.sha256(sanitized.encode()).hexdigest(),
        })

        return {**memory, "content": encrypted_content}

    async def secure_read(self, user_id: str, memory: dict) -> str:
        """安全读取记忆"""
        # 1. 权限验证
        if not self._check_access(user_id, memory):
            raise PermissionError("无权访问该记忆")

        # 2. 解密
        decrypted = self.cipher.decrypt(memory["content"].encode()).decode()

        # 3. 审计
        self.audit_log.append({
            "action": "read",
            "user_id": user_id,
            "memory_id": memory["id"],
            "timestamp": datetime.now().isoformat(),
        })

        return decrypted

    async def _sanitize_content(self, content: str) -> str:
        """敏感信息脱敏"""
        # 手机号
        content = re.sub(r'1[3-9]\d{9}', '[手机号已脱敏]', content)
        # 身份证号
        content = re.sub(
            r'[1-9]\d{5}(?:19|20)\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])\d{3}[\dXx]',
            '[身份证已脱敏]', content
        )
        # 邮箱
        content = re.sub(
            r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}',
            '[邮箱已脱敏]', content
        )
        # 银行卡号
        content = re.sub(r'\d{16,19}', '[银行卡号已脱敏]', content)

        # LLM辅助检测更复杂的敏感信息
        if await self.content_filter.contains_sensitive(content):
            content = await self.content_filter.redact(content)

        return content

    def _check_access(self, user_id: str, memory: dict) -> bool:
        """检查用户是否有权访问该记忆"""
        # 记忆所有者可以访问
        if memory.get("owner") == user_id:
            return True
        # 共享记忆的授权用户可以访问
        if user_id in memory.get("shared_with", []):
            return True
        # 管理员可以访问(需要额外验证)
        if memory.get("admin_accessible") and self._is_admin(user_id):
            return True
        return False

    async def memory_retention_policy(self, user_id: str):
        """记忆保留策略:定期清理过期记忆"""
        retention_days = 90  # 默认保留90天
        cutoff = datetime.now() - timedelta(days=retention_days)

        expired = await self._find_expired_memories(user_id, cutoff)
        for memory in expired:
            # 根据策略决定删除还是归档
            if memory.get("importance", 0) > 0.8:
                await self._archive_memory(memory)
            else:
                await self._delete_memory(memory)

        self.audit_log.append({
            "action": "retention_cleanup",
            "user_id": user_id,
            "expired_count": len(expired),
            "timestamp": datetime.now().isoformat(),
        })

11. 生产级记忆系统架构

11.1 完整架构图

┌─────────────────────────────────────────────────────────────────────┐
│                      生产级Agent记忆系统                              │
│                                                                      │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │                    接入层 (API Gateway)                      │    │
│  │  认证 → 限流 → 路由 → 请求/响应日志                          │    │
│  └──────────────────────────┬──────────────────────────────────┘    │
│                              │                                       │
│  ┌──────────────────────────▼──────────────────────────────────┐    │
│  │                   记忆服务层 (Memory Service)                │    │
│  │                                                              │    │
│  │  ┌───────────┐  ┌───────────┐  ┌───────────┐              │    │
│  │  │写入管道    │  │检索管道    │  │压缩管道    │              │    │
│  │  │Write      │  │Retrieve   │  │Compress   │              │    │
│  │  │Pipeline   │  │Pipeline   │  │Pipeline   │              │    │
│  │  │           │  │           │  │           │              │    │
│  │  │·内容过滤  │  │·查询改写  │  │·定时触发  │              │    │
│  │  │·向量计算  │  │·向量检索  │  │·聚类合并  │              │    │
│  │  │·存储路由  │  │·重排序    │  │·摘要生成  │              │    │
│  │  │·索引更新  │  │·上下文压缩│  │·过期清理  │              │    │
│  │  └───────────┘  └───────────┘  └───────────┘              │    │
│  └──────────────────────────┬──────────────────────────────────┘    │
│                              │                                       │
│  ┌──────────────────────────▼──────────────────────────────────┐    │
│  │                   存储层 (Storage Layer)                     │    │
│  │                                                              │    │
│  │  ┌────────┐  ┌────────────┐  ┌────────┐  ┌──────────┐    │    │
│  │  │ Redis  │  │ PostgreSQL │  │Qdrant  │  │   S3     │    │    │
│  │  │ 热存储  │  │ 温存储     │  │向量索引 │  │ 冷存储   │    │    │
│  │  └────────┘  └────────────┘  └────────┘  └──────────┘    │    │
│  └─────────────────────────────────────────────────────────────┘    │
│                                                                      │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │                   监控层 (Observability)                     │    │
│  │  Prometheus指标 → Grafana仪表板 → 告警规则                   │    │
│  └─────────────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────────────┘

11.2 关键设计决策

决策1:嵌入模型选型

模型 维度 中文效果 延迟 推荐场景
OpenAI text-embedding-3-large 3072 优秀 通用场景,质量优先
OpenAI text-embedding-3-small 1536 良好 成本敏感场景
BAAI/bge-large-zh-v1.5 1024 优秀 中文为主,自托管
sentence-transformers/all-MiniLM-L6-v2 384 一般 极低 原型开发

决策2:向量数据库选型

def choose_vector_db(requirements: dict) -> str:
    """根据需求选择向量数据库"""
    if requirements.get("managed"):
        return "Pinecone"  # 全托管,零运维
    if requirements.get("scale") == "billion+":
        return "Milvus"    # 超大规模
    if requirements.get("hybrid_search"):
        return "Weaviate"  # 原生混合搜索
    if requirements.get("low_latency"):
        return "Qdrant"    # 高性能
    if requirements.get("simple"):
        return "ChromaDB"  # 开发原型
    return "Qdrant"        # 默认推荐

决策3:压缩时机

COMPRESSION_TRIGGERS = {
    "time_based": {
        "description": "定时压缩,如每天凌晨",
        "schedule": "0 2 * * *",
        "suitable": "对话量稳定的系统"
    },
    "threshold_based": {
        "description": "记忆数量超过阈值时触发",
        "threshold": 10000,
        "suitable": "对话量波动大的系统"
    },
    "event_based": {
        "description": "特定事件触发,如用户登出、会话结束",
        "events": ["session_end", "user_logout"],
        "suitable": "实时性要求高的系统"
    }
}

11.3 性能基准

以下是一个生产级记忆系统的典型性能指标:

PERFORMANCE_BENCHMARKS = {
    "写入延迟": {
        "P50": "5ms",    # 热存储写入
        "P95": "20ms",   # 含向量计算
        "P99": "50ms",   # 含压缩和索引
    },
    "检索延迟": {
        "P50": "15ms",   # 向量检索
        "P95": "50ms",   # 含重排序
        "P99": "100ms",  # 含跨层查询
    },
    "吞吐量": {
        "写入": "1000 QPS",   # 单节点
        "检索": "5000 QPS",   # 单节点
        "压缩": "100 条/秒",   # 批量压缩
    },
    "存储效率": {
        "向量索引": "~6KB/条 (1536维 float32)",
        "元数据": "~1KB/条",
        "压缩率": "原始对话的10-20%",
    }
}

11.4 部署建议

# docker-compose.yml - 记忆系统部署配置
version: "3.8"

services:
  memory-service:
    build: ./memory-service
    ports:
      - "8080:8080"
    environment:
      - REDIS_URL=redis://redis:6379
      - PG_URL=postgresql://postgres:5432/memories
      - QDRANT_URL=http://qdrant:6333
      - EMBEDDING_MODEL=BAAI/bge-large-zh-v1.5
      - ENCRYPTION_KEY_FILE=/run/secrets/encryption_key
    secrets:
      - encryption_key
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: "2"
          memory: 4G

  redis:
    image: redis:7-alpine
    volumes:
      - redis-data:/data
    command: redis-server --maxmemory 2gb --maxmemory-policy allkeys-lru

  postgres:
    image: pgvector/pgvector:pg16
    volumes:
      - pg-data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: memories
      POSTGRES_PASSWORD_FILE: /run/secrets/pg_password

  qdrant:
    image: qdrant/qdrant:latest
    volumes:
      - qdrant-data:/qdrant/storage
    ports:
      - "6333:6333"

volumes:
  redis-data:
  pg-data:
  qdrant-data:

secrets:
  encryption_key:
    external: true
  pg_password:
    external: true

总结

构建生产级Agent记忆系统需要在多个维度做出平衡:

维度 权衡点 建议
容量 vs 成本 记忆越多越好,但存储和计算成本线性增长 分层存储+定期压缩
检索精度 vs 延迟 精排提升精度但增加延迟 向量粗排+交叉编码器精排
隐私 vs 功能 完全加密影响语义检索能力 存储加密+检索时解密
共享 vs 隔离 共享记忆提升协作效率,但增加泄露风险 细粒度访问控制
持久化 vs 实时性 持久化保证可靠性,但增加写入延迟 异步写入+缓存

核心原则:

  1. 记忆即状态:将记忆系统视为Agent的核心状态管理器,而非附属功能
  2. 分层设计:不同温度的记忆使用不同的存储和检索策略
  3. 可观测性:每条记忆的生命周期都应可追踪、可审计
  4. 渐进演化:从简单的对话历史开始,逐步添加向量检索、压缩、共享等高级功能
  5. 安全优先:在设计阶段就纳入隐私和安全考量,而非事后补救

记忆系统是Agent从"无状态工具"进化为"有记忆的伙伴"的关键。掌握本教程中的架构思想和实现方案,你将能够构建出真正具有持久记忆、跨会话连续性和持续学习能力的AI Agent系统。


本教程最后更新:2024年。记忆系统技术仍在快速演进,建议持续关注学术论文和开源项目(如MemGPT、LangGraph、Mem0等)的最新进展。

内容声明

本文内容为AI技术学习教程,仅供学习参考。如涉及技术问题,欢迎通过 xurj005@163.com 与我们交流。

目录