我在生产环境中为数十个 AI Agent 项目设计过记忆存储架构,从 Redis 缓存到专用向量数据库,从 PostgreSQL + pgvector 到 Pinecone,用户量从日活几千到日活千万级别都经历过。今天我把实战经验系统整理成这篇选型指南,帮助你避免我踩过的那些坑。
长期记忆是 AI Agent 实现连贯对话、跨会话上下文保持的关键能力。选择错误的存储方案,轻则 Token 成本暴涨 3 倍,重则出现会话混淆、数据丢失等严重事故。先说结论:没有银弹,但有最适合你业务场景的方案。
为什么 AI Agent 需要长期记忆
大模型的上下文窗口是有限的,GPT-4o 的 128K Token 看似很大,但面对一个持续服务 1 年的用户,其交互历史可能达到数百万 Token。没有长期记忆的 Agent 就是每次都"失忆"的白纸,无法理解用户的长期偏好、历史行为、进行性对话。
长期记忆的核心需求包括:
- 语义检索:根据当前对话语义,从历史中找回相关内容
- 结构化存储:用户画像、偏好设置、关键状态
- 高效更新:实时追加新记忆、修改过期信息
- 成本可控:存储和检索的 Token/API 成本要可接受
主流存储方案对比表
| 方案 | 语义搜索 | 写入性能 | 查询延迟 | 月成本估算* | 维护复杂度 | 扩展性 |
|---|---|---|---|---|---|---|
| Redis + Vector | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | <5ms | $50-500 | 中 | 集群模式优秀 |
| PostgreSQL + pgvector | ⭐⭐⭐⭐ | ⭐⭐⭐ | 10-50ms | $20-200 | 低 | 中等 |
| Pinecone | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 30-100ms | $70-2000+ | 极低 | 自动扩展 |
| Weaviate | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 20-80ms | $100-1500 | 中 | 良好 |
| Milvus | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 10-60ms | $60-800 | 高 | 优秀 |
| 文件系统/S3 + 外部向量化 | ⭐⭐⭐ | ⭐⭐ | 100ms+ | $10-100 | 低-中 | 优秀 |
*月成本基于 100 万条记忆条目、每天 10 万次检索估算,不含计算资源
方案一:Redis + RediSearch/RedisVL
Redis 是我接触最多的方案,特别适合需要毫秒级响应的场景。我在一个电商 AI 助手项目中用它存储用户会话和商品偏好,延迟稳定在 3-5ms,用户体验非常好。
核心优势
- 亚毫秒级读写延迟,业界领先
- 支持多种数据结构(String、Hash、Sorted Set、JSON)
- RediSearch 模块支持全文和向量混合检索
- 生态成熟,社区活跃,问题容易排查
- 支持集群模式,水平扩展无压力
实战代码:Redis Vector 存储与检索
import redis
import json
import numpy as np
from typing import List, Dict, Any
class RedisMemoryStore:
def __init__(self, host='localhost', port=6379, password=None):
# HolySheep API Key 配置(示例)
# REDIS_MEMORY_HOST = os.getenv('REDIS_MEMORY_HOST')
# REDIS_MEMORY_PASSWORD = os.getenv('REDIS_MEMORY_PASSWORD')
self.redis = redis.Redis(
host=host,
port=port,
password=password,
decode_responses=True
)
# 向量维度(OpenAI text-embedding-3-small)
self.embedding_dim = 1536
def store_memory(
self,
user_id: str,
memory_id: str,
content: str,
embedding: List[float],
metadata: Dict[str, Any]
) -> bool:
"""存储单条记忆"""
key = f"memory:{user_id}:{memory_id}"
data = {
"content": content,
"embedding": np.array(embedding).astype(np.float32).tobytes(),
"metadata": json.dumps(metadata),
"created_at": redis_client.time()[0]
}
pipe = self.redis.pipeline()
pipe.hset(key, mapping=data)
# 设置 30 天过期,实际按需调整
pipe.expire(key, 60*60*24*30)
# 添加到用户记忆索引
pipe.zadd(f"user_index:{user_id}", {memory_id: data["created_at"]})
pipe.execute()
return True
def search_similar(
self,
user_id: str,
query_embedding: List[float],
top_k: int = 5,
threshold: float = 0.7
) -> List[Dict]:
"""向量相似度搜索 - 使用余弦相似度"""
query_vec = np.array(query_embedding).astype(np.float32)
# 获取用户所有记忆
memory_ids = self.redis.zrange(f"user_index:{user_id}", 0, -1)
results = []
for mid in memory_ids:
key = f"memory:{user_id}:{mid}"
data = self.redis.hgetall(key)
if not data:
continue
stored_vec = np.frombuffer(data["embedding"], dtype=np.float32)
# 计算余弦相似度
similarity = np.dot(query_vec, stored_vec) / (
np.linalg.norm(query_vec) * np.linalg.norm(stored_vec)
)
if similarity >= threshold:
results.append({
"memory_id": mid,
"content": data["content"],
"similarity": float(similarity),
"metadata": json.loads(data["metadata"])
})
# 按相似度排序返回 Top-K
results.sort(key=lambda x: x["similarity"], reverse=True)
return results[:top_k]
def batch_store(self, user_id: str, memories: List[Dict]) -> int:
"""批量写入记忆,提升吞吐量"""
pipe = self.redis.pipeline()
import time
ts = int(time.time())
for mem in memories:
key = f"memory:{user_id}:{mem['id']}"
pipe.hset(key, mapping={
"content": mem["content"],
"embedding": np.array(mem["embedding"]).astype(np.float32).tobytes(),
"metadata": json.dumps(mem.get("metadata", {}))
})
pipe.zadd(f"user_index:{user_id}", {mem["id"]: ts})
pipe.execute()
return len(memories)
使用示例
redis_client = RedisMemoryStore()
存储记忆
redis_client.store_memory(
user_id="user_12345",
memory_id="mem_001",
content="用户偏好深色模式,上次咨询时间是2026-01-15",
embedding=[0.123, -0.456, ...], # 1536维向量
metadata={"type": "preference", "source": "conversation"}
)
性能基准测试
我在 4 核 8G 云服务器上做了基准测试,结果如下:
| 操作 | 延迟(P50) | 延迟(P99) | QPS |
|---|---|---|---|
| 单条写入 | 1.2ms | 3.8ms | 8000+ |
| 批量写入(100条) | 45ms | 120ms | 2200 |
| 向量检索(Top-5) | 3.5ms | 8.2ms | 280 |
| 混合检索 | 5.1ms | 12ms | 190 |
方案二:PostgreSQL + pgvector
如果你团队已经有 PostgreSQL 运维能力,这个方案性价比极高。我在一个内部知识库 Agent 项目中用它,存储了 2000 万条文档切片,月成本控制在 $80 以内。
核心优势
- 一块数据库同时处理事务和向量搜索
- SQL 的表达能力让复杂查询变得简单
- pgvector 0.5+ 版本性能大幅提升
- Cloudflare D1、Supabase 等托管方案降低运维负担
- 强一致性,ACID 事务支持
实战代码:pgvector 存储与检索
import psycopg2
from psycopg2.extras import execute_values
import numpy as np
from typing import List, Dict, Tuple
class PGVectorMemoryStore:
def __init__(self, connection_string: str):
self.conn = psycopg2.connect(connection_string)
self.conn.autocommit = True
self._init_table()
def _init_table(self):
"""初始化向量存储表"""
with self.conn.cursor() as cur:
cur.execute("""
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE IF NOT EXISTS agent_memories (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id VARCHAR(64) NOT NULL,
session_id VARCHAR(64),
content TEXT NOT NULL,
embedding vector(1536),
memory_type VARCHAR(32) DEFAULT 'conversation',
importance_score FLOAT DEFAULT 0.5,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
metadata JSONB DEFAULT '{}'
);
-- 创建索引加速查询
CREATE INDEX IF NOT EXISTS idx_memories_user_id
ON agent_memories(user_id);
CREATE INDEX IF NOT EXISTS idx_memories_session
ON agent_memories(user_id, session_id);
-- HNSW 索引适合需要快速召回的场景
CREATE INDEX IF NOT EXISTS idx_memories_embedding_hnsw
ON agent_memories USING hnsw (embedding vector_cosine_ops);
""")
def store_memory(
self,
user_id: str,
content: str,
embedding: List[float],
session_id: str = None,
memory_type: str = "conversation",
importance: float = 0.5,
metadata: dict = None
) -> str:
"""存储单条记忆"""
with self.conn.cursor() as cur:
cur.execute("""
INSERT INTO agent_memories
(user_id, session_id, content, embedding, memory_type, importance_score, metadata)
VALUES (%s, %s, %s, %s, %s, %s, %s)
RETURNING id
""", (
user_id, session_id, content, embedding,
memory_type, importance, psycopg2.extras.Json(metadata or {})
))
return cur.fetchone()[0]
def batch_store(self, user_id: str, memories: List[Dict]) -> int:
"""批量写入 - 使用 execute_values 提升性能 10x"""
values = [
(
user_id,
m.get("session_id"),
m["content"],
m["embedding"],
m.get("type", "conversation"),
m.get("importance", 0.5),
psycopg2.extras.Json(m.get("metadata", {}))
)
for m in memories
]
with self.conn.cursor() as cur:
execute_values(
cur,
"""INSERT INTO agent_memories
(user_id, session_id, content, embedding, memory_type, importance_score, metadata)
VALUES %s""",
values,
template=None,
page_size=1000
)
return len(memories)
def search_similar(
self,
user_id: str,
query_embedding: List[float],
session_id: str = None,
memory_types: List[str] = None,
top_k: int = 10,
min_importance: float = 0.0
) -> List[Dict]:
"""混合检索:向量相似度 + 元数据过滤"""
# 构建查询
sql = """
SELECT id, content,
1 - (embedding <=> %s::vector) as similarity,
memory_type, importance_score, metadata, created_at
FROM agent_memories
WHERE user_id = %s
AND importance_score >= %s
"""
params = [query_embedding, user_id, min_importance]
if session_id:
sql += " AND session_id = %s"
params.append(session_id)
if memory_types:
sql += f" AND memory_type = ANY(%s)"
params.append(memory_types)
sql += f" ORDER BY embedding <=> %s::vector LIMIT {top_k}"
params.append(query_embedding)
with self.conn.cursor() as cur:
cur.execute(sql, params)
columns = [desc[0] for desc in cur.description]
return [dict(zip(columns, row)) for row in cur.fetchall()]
def get_recent_memories(
self,
user_id: str,
session_id: str = None,
limit: int = 50
) -> List[Dict]:
"""获取最近的记忆(时间序)"""
sql = """
SELECT id, content, memory_type, importance_score, created_at
FROM agent_memories
WHERE user_id = %s
"""
params = [user_id]
if session_id:
sql += " AND session_id = %s"
params.append(session_id)
sql += f" ORDER BY created_at DESC LIMIT {limit}"
with self.conn.cursor() as cur:
cur.execute(sql, params)
columns = [desc[0] for desc in cur.description]
return [dict(zip(columns, row)) for row in cur.fetchall()]
def update_memory_importance(self, memory_id: str, score: float) -> bool:
"""更新记忆重要性评分"""
with self.conn.cursor() as cur:
cur.execute("""
UPDATE agent_memories
SET importance_score = %s, updated_at = NOW()
WHERE id = %s
""", (score, memory_id))
return cur.rowcount > 0
def delete_old_memories(self, user_id: str, days: int = 90) -> int:
"""清理过期记忆"""
with self.conn.cursor() as cur:
cur.execute("""
DELETE FROM agent_memories
WHERE user_id = %s
AND created_at < NOW() - INTERVAL '%s days'
AND importance_score < 0.3
""", (user_id, days))
return cur.rowcount
性能调优:HNSW vs IVFFlat
def benchmark_pgvector():
"""基准测试 pgvector 两种索引模式"""
import time
conn_string = "postgresql://user:pass@localhost:5432/agent_memory"
store = PGVectorMemoryStore(conn_string)
# 生成 10 万条测试数据
test_embeddings = [np.random.rand(1536).tolist() for _ in range(100000)]
start = time.time()
store.batch_store("benchmark_user", [
{"content": f"test content {i}", "embedding": emb}
for i, emb in enumerate(test_embeddings)
])
print(f"批量写入 100K 条: {time.time() - start:.2f}s")
# 查询测试
query_vec = test_embeddings[50000]
start = time.time()
for _ in range(100):
store.search_similar("benchmark_user", query_vec, top_k=10)
print(f"100 次向量检索: {time.time() - start:.2f}s")
使用示例
pg_store = PGVectorMemoryStore("postgresql://user:pass@localhost:5432/agent_memory")
pg_store.store_memory("user_001", "用户上次购买了运动鞋", [0.1, 0.2, ...])
方案三:Pinecone 专用向量数据库
Pinecone 是我用过最省心的方案,特别适合不想运维基础设施的团队。通过 立即注册 HolySheep AI,你可以获得稳定的 API 调用渠道,配合 Pinecone 使用效果很好。
import pinecone
from pinecone import ServerlessSpec
import os
class PineconeMemoryStore:
def __init__(
self,
api_key: str = None,
index_name: str = "agent-memories",
dimension: int = 1536
):
pinecone.init(api_key=api_key or os.getenv("PINECONE_API_KEY"))
self.index_name = index_name
self._ensure_index(dimension)
def _ensure_index(self, dimension: int):
"""确保索引存在"""
if self.index_name not in pinecone.list_indexes():
pinecone.create_index(
self.index_name,
dimension=dimension,
metric="cosine",
spec=ServerlessSpec(
cloud="aws",
region="us-east-1"
)
)
self.index = pinecone.Index(self.index_name)
def store_memory(
self,
user_id: str,
memory_id: str,
content: str,
embedding: List[float],
metadata: dict = None
) -> dict:
"""Upsert 单条记忆"""
vector_id = f"{user_id}_{memory_id}"
response = self.index.upsert(
vectors=[{
"id": vector_id,
"values": embedding,
"metadata": {
"user_id": user_id,
"memory_id": memory_id,
"content": content,
**(metadata or {})
}
}]
)
return response
def batch_store(self, vectors: List[dict]) -> dict:
"""批量写入(最多 2MB/1000条 per request)"""
# Pinecone 推荐每批 1000 条
vectors_formatted = [
{
"id": v["id"],
"values": v["embedding"],
"metadata": v.get("metadata", {})
}
for v in vectors
]
return self.index.upsert(vectors_formatted)
def search_similar(
self,
user_id: str,
query_embedding: List[float],
top_k: int = 10,
filter_dict: dict = None
) -> List[dict]:
"""向量检索"""
query_filter = {"user_id": {"$eq": user_id}}
if filter_dict:
query_filter.update(filter_dict)
results = self.index.query(
vector=query_embedding,
top_k=top_k,
filter=query_filter,
include_metadata=True
)
return [
{
"id": match["id"],
"score": match["score"],
"content": match["metadata"]["content"],
"metadata": match["metadata"]
}
for match in results["matches"]
]
def delete_user_memories(self, user_id: str) -> dict:
"""删除用户所有记忆"""
return self.index.delete(filter={"user_id": {"$eq": user_id}})
使用示例
pinecone_store = PineconeMemoryStore(
api_key="YOUR_PINECONE_API_KEY"
)
pinecone_store.store_memory(
user_id="user_123",
memory_id="mem_001",
content="用户订阅了年度会员",
embedding=[0.123, -0.456, ...], # 1536维向量
metadata={"subscription": "annual", "since": "2025-12"}
)
方案四:混合架构实战
单一方案往往不够,我在生产环境中用得最多的是"Redis + PostgreSQL"混合架构。热数据放 Redis,冷数据归档到 PostgreSQL,定期同步和淘汰。
import asyncio
import redis.asyncio as aioredis
import psycopg2
from datetime import datetime, timedelta
from typing import List, Dict, Optional
class HybridMemoryStore:
"""
热数据:Redis(最近 7 天、高频访问)
温数据:PostgreSQL(7-90 天)
冷数据:归档表(90 天以上,支持删除)
"""
def __init__(
self,
redis_url: str,
pg_connection_string: str,
hot_threshold_days: int = 7,
warm_threshold_days: int = 90
):
self.redis = aioredis.from_url(redis_url)
self.pg_conn = psycopg2.connect(pg_connection_string)
self.pg_conn.autocommit = True
self.hot_days = hot_threshold_days
self.warm_days = warm_threshold_days
# 初始化 PostgreSQL 表
self._init_tables()
def _init_tables(self):
"""初始化归档表"""
with self.pg_conn.cursor() as cur:
cur.execute("""
CREATE TABLE IF NOT EXISTS memory_archive (
id UUID PRIMARY KEY,
user_id VARCHAR(64),
content TEXT,
embedding BYTEA,
metadata JSONB,
archived_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_archive_user ON memory_archive(user_id);
""")
async def store(
self,
user_id: str,
memory_id: str,
content: str,
embedding: List[float],
metadata: dict = None
):
"""统一写入入口 - 自动分层"""
import json
import numpy as np
key = f"memory:{user_id}:{memory_id}"
data = {
"content": content,
"embedding": np.array(embedding).astype(np.float32).tobytes(),
"metadata": json.dumps(metadata or {}),
"created_at": datetime.now().isoformat()
}
# 同时写入 Redis 和 PostgreSQL
async with self.redis.pipeline(transaction=True) as pipe:
await pipe.hset(key, mapping=data)
await pipe.expire(key, 60*60*24*30) # Redis 30 天 TTL
await pipe.execute()
# 异步写入 PostgreSQL(用于长期归档)
loop = asyncio.get_event_loop()
await loop.run_in_executor(None, self._pg_insert,
memory_id, user_id, content, embedding, metadata)
def _pg_insert(self, memory_id, user_id, content, embedding, metadata):
import numpy as np
with self.pg_conn.cursor() as cur:
cur.execute("""
INSERT INTO agent_memories (id, user_id, content, embedding, metadata)
VALUES (%s, %s, %s, %s, %s)
ON CONFLICT (id) DO NOTHING
""", (
memory_id, user_id, content,
np.array(embedding).astype(np.float32).tobytes(),
psycopg2.extras.Json(metadata or {})
))
async def retrieve(
self,
user_id: str,
query_embedding: List[float],
include_archive: bool = True
) -> List[Dict]:
"""统一检索入口 - Redis 优先,PostgreSQL 兜底"""
import json
import numpy as np
# 1. 先查 Redis 热数据
pattern = f"memory:{user_id}:*"
keys = []
async for key in self.redis.scan_iter(match=pattern, count=100):
keys.append(key)
results = []
if keys:
pipe = self.redis.pipeline()
for key in keys:
pipe.hgetall(key)
raw_results = await pipe.execute()
query_vec = np.array(query_embedding)
for i, data in enumerate(raw_results):
if not data:
continue
stored_vec = np.frombuffer(data["embedding"], dtype=np.float32)
similarity = np.dot(query_vec, stored_vec) / (
np.linalg.norm(query_vec) * np.linalg.norm(stored_vec)
)
results.append({
"memory_id": keys[i].decode().split(":")[-1],
"content": data["content"],
"similarity": float(similarity),
"metadata": json.loads(data["metadata"]),
"tier": "hot"
})
# 2. 如果需要归档数据,查 PostgreSQL
if include_archive and len(results) < 10:
loop = asyncio.get_event_loop()
pg_results = await loop.run_in_executor(
None, self._pg_search, user_id, query_embedding, 10 - len(results)
)
results.extend(pg_results)
# 3. 热度加权:提升 Redis 结果的相似度
for r in results:
if r["tier"] == "hot":
r["similarity"] *= 1.1 # 热数据权重 +10%
results.sort(key=lambda x: x["similarity"], reverse=True)
return results[:10]
def _pg_search(self, user_id, query_embedding, limit):
import numpy as np
with self.pg_conn.cursor() as cur:
cur.execute("""
SELECT id, content, embedding, metadata,
1 - (embedding <=> %s::vector) as similarity
FROM agent_memories
WHERE user_id = %s
ORDER BY embedding <=> %s::vector
LIMIT %s
""", (query_embedding, user_id, query_embedding, limit))
return [
{
"memory_id": row[0],
"content": row[1],
"metadata": row[3],
"similarity": float(row[4]),
"tier": "warm"
}
for row in cur.fetchall()
]
async def archive_old_memories(self, days: int = 30):
"""归档旧记忆 - 定时任务调用"""
loop = asyncio.get_event_loop()
await loop.run_in_executor(None, self._archive_task, days)
def _archive_task(self, days: int):
with self.pg_conn.cursor() as cur:
cur.execute("""
INSERT INTO memory_archive
SELECT id, user_id, content, embedding, metadata, NOW()
FROM agent_memories
WHERE created_at < NOW() - INTERVAL '%s days'
ON CONFLICT (id) DO NOTHING
""", (days,))
print(f"归档了 {cur.rowcount} 条记忆")
使用示例
async def main():
store = HybridMemoryStore(
redis_url="redis://localhost:6379/0",
pg_connection_string="postgresql://user:pass@localhost:5432/agent_memory"
)
# 存储
await store.store(
user_id="user_001",
memory_id="mem_100",
content="用户喜欢周杰伦的音乐",
embedding=[0.1, 0.2, 0.3] * 512,
metadata={"topic": "music", "artist": "jay_chou"}
)
# 检索
results = await store.retrieve(
user_id="user_001",
query_embedding=[0.1, 0.2, 0.3] * 512,
include_archive=True
)
for r in results:
print(f"[{r['tier']}] {r['content'][:50]}... (相似度: {r['similarity']:.3f})")
asyncio.run(main())
架构设计最佳实践
记忆压缩与摘要
不要存储原始对话,而是定期做摘要压缩。我见过太多项目因为存储量爆炸导致成本失控。
import httpx
import asyncio
from typing import List, Dict
class MemoryCompressor:
"""使用 LLM 对记忆进行摘要压缩"""
def __init__(self, api_key: str, base_url: str = "https://api.holysheep.ai/v1"):
self.client = httpx.AsyncClient(
base_url=base_url,
headers={"Authorization": f"Bearer {api_key}"},
timeout=60.0
)
async def summarize_conversation(
self,
messages: List[Dict[str, str]]
) -> str:
"""将多轮对话压缩为单条摘要"""
# 构建压缩 Prompt
compression_prompt = f"""请将以下对话提炼为简洁的记忆摘要,保留关键信息和用户偏好:
对话记录:
{chr(10).join([f"用户: {m['user']}\n助手: {m['assistant']}" for m in messages])}
摘要要求:
- 保留关键事实和偏好
- 删除冗余表达
- 长度控制在 200 字以内
- 用第三人称叙述
"""
response = await self.client.post("/chat/completions", json={
"model": "gpt-4.1",
"messages": [
{"role": "system", "content": "你是一个记忆压缩助手,负责将对话提炼为关键信息。"},
{"role": "user", "content": compression_prompt}
],
"max_tokens": 300,
"temperature": 0.3 # 低温度保证摘要一致性
})
result = response.json()
return result["choices"][0]["message"]["content"]
async def batch_compress(
self,
conversation_groups: List[List[Dict]]
) -> List[str]:
"""批量压缩多个对话组"""
tasks = [self.summarize_conversation(group) for group in conversation_groups]
return await asyncio.gather(*tasks)
使用示例
compressor = MemoryCompressor(api_key="YOUR_HOLYSHEEP_API_KEY")
conversations = [
[
{"user": "我想买一双跑步鞋", "assistant": "好的,您平时跑步的距离是多少?"},
{"user": "一般跑5公里左右", "assistant": "推荐您选择缓震性能好的款式。"},
{"user": "预算在500元左右", "assistant": "这个价位可以看看某某品牌。"}
],
# ... 更多对话组
]
summaries = asyncio.run(compressor.batch_compress(conversations))
print(f"压缩了 {len(summaries)} 组对话")
记忆生命周期管理
from datetime import datetime, timedelta
from enum import Enum
from typing import Optional
class MemoryTier(Enum):
HOT = "hot" # 最近 7 天,高频访问
WARM = "warm" # 7-30 天,正常访问
COLD = "cold" # 30-90 天,低频访问
ARCHIVE = "archive" # 90 天以上,压缩存储
class MemoryLifecycleManager:
"""记忆生命周期管理"""
def __init__(self, store: HybridMemoryStore):
self.store = store
def calculate_tier(self, created_at: datetime) -> MemoryTier:
"""根据创建时间计算记忆层级"""
age_days = (datetime.now() - created_at).days
if age_days <= 7:
return MemoryTier.HOT
elif age_days <= 30:
return MemoryTier.WARM
elif age_days <= 90:
return MemoryTier.COLD
else:
return MemoryTier.ARCHIVE
def should_compress(self, memory: dict, current_tier: MemoryTier) -> bool:
"""判断是否需要压缩"""
if current_tier in [MemoryTier.COLD, MemoryTier.ARCHIVE]:
# 冷数据定期压缩节省空间
return memory.get("compression_count", 0) < 3
return False
def should_delete(self, memory: dict, current_tier: MemoryTier) -> bool:
"""判断是否需要删除"""
importance = memory.get("importance_score", 0.5)
if current_tier == MemoryTier.ARCHIVE and importance < 0.2:
return True
if current_tier == MemoryTier.COLD and importance < 0.1:
return True
return False
async def run_maintenance(self):
"""执行维护任务 - 建议每日执行"""
# 1. 压缩低重要性冷记忆
# 2. 删除过期不重要记忆
# 3. 将热数据迁移到温数据层
# 4. 更新访问统计
pass
常见报错排查
错误 1:Redis 连接池耗尽
# 错误信息
redis.exceptions.ConnectionError: Error 99: Cannot assign requested address
或
redis.exceptions.ConnectionLimitError: Too many connections
原因分析
连接池未正确配置,高并发时创建了过多连接
解决方案
import redis
from redis import ConnectionPool
方案 A:使用连接池
pool = ConnectionPool(
host='localhost',
port=6379,
max_connections=50, # 根据服务器配置调整
decode_responses=True
)
redis_client = redis.Redis(connection_pool=pool)
方案 B:Async Redis + 连接复用
import redis.asyncio as aioredis
class AsyncRedisStore:
def __init__(self):
# 复用单一连接
self.redis = aioredis.from_url(
"redis://localhost:6379/0",
max_connections=100,
decode_responses=True
)
async def batch_ops(self, operations: List):
"""批量操作使用 pipeline 减少连接数"""
async with self.redis.pipeline(transaction=True) as pipe:
for op in operations:
if op["type"] == "set":
pipe.set(op["key"], op["value"])
elif op["type"] == "get":
pipe.get(op["key"])
return await pipe.execute()
方案 C:检查连接泄漏
async def check_connections