长期记忆与向量数据库
引言
当信息超出上下文窗口的容量,Agent 需要一种持久化的长期记忆机制。向量数据库(Vector Database)通过将文本转化为高维向量并进行相似性搜索,成为当前 Agent 长期记忆的核心基础设施。
派系视角:向量检索(embedding similarity + ANN)是 Domingos 五学派中类比派 (Analogizers) 在大模型时代的复兴 —— RAG = 现代 CBR。从派系视角看推荐系统、协同过滤、矩阵分解、HNSW/IVF/LSH 算法对比、RAG vs 经典 CBR,见 The Master Algorithm — 推荐系统与现代检索。
向量嵌入(Embedding)
什么是嵌入
嵌入是将离散的文本(词、句子、段落)映射到连续的高维向量空间中,使得语义相近的文本在向量空间中距离更近。
\[\text{embed}: \text{Text} \rightarrow \mathbb{R}^d\]
其中 \(d\) 是嵌入维度(通常 384-3072)。
主流嵌入模型
| 模型 | 维度 | 提供者 | 特点 |
|---|---|---|---|
| text-embedding-3-small | 1536 | OpenAI | 性价比高 |
| text-embedding-3-large | 3072 | OpenAI | 精度最高 |
| text-embedding-ada-002 | 1536 | OpenAI | 经典模型 |
| BGE-large-en-v1.5 | 1024 | BAAI | 开源最佳之一 |
| BGE-M3 | 1024 | BAAI | 多语言、多粒度 |
| E5-large-v2 | 1024 | Microsoft | 开源高质量 |
| voyage-3 | 1024 | Voyage AI | 代码/文档专优 |
| Cohere embed-v3 | 1024 | Cohere | 多语言优化 |
嵌入代码示例
from openai import OpenAI
client = OpenAI()
def get_embedding(text, model="text-embedding-3-small"):
response = client.embeddings.create(input=text, model=model)
return response.data[0].embedding
# 示例
embedding = get_embedding("Agent 的记忆系统非常重要")
print(f"维度: {len(embedding)}") # 1536
# 使用开源模型(sentence-transformers)
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("BAAI/bge-large-en-v1.5")
embeddings = model.encode(["memory systems for agents", "向量数据库"])
相似性度量
余弦相似度
最常用的相似性度量:
\[\cos(\theta) = \frac{\mathbf{a} \cdot \mathbf{b}}{\|\mathbf{a}\| \|\mathbf{b}\|} = \frac{\sum_{i=1}^{n} a_i b_i}{\sqrt{\sum_{i=1}^{n} a_i^2} \cdot \sqrt{\sum_{i=1}^{n} b_i^2}}\]
- 值域:\([-1, 1]\),1 表示完全相同,0 表示正交,-1 表示完全相反
- 优势:对向量长度不敏感,适合文本相似度
其他度量
| 度量 | 公式 | 适用场景 |
|---|---|---|
| 欧氏距离 | \(d = \sqrt{\sum(a_i - b_i)^2}\) | 绝对距离敏感 |
| 点积 | \(s = \sum a_i b_i\) | 已归一化的向量 |
| Manhattan 距离 | \(d = \sum \|a_i - b_i\|\) | 高维稀疏数据 |
近似最近邻搜索(ANN)
精确的最近邻搜索复杂度为 \(O(nd)\)(\(n\) 为向量数,\(d\) 为维度),在大规模数据下不可行。ANN 算法牺牲少量精度换取大幅加速。
HNSW(Hierarchical Navigable Small World)
当前最流行的 ANN 算法:
graph TB
subgraph "HNSW 多层图结构"
subgraph "Layer 2(稀疏)"
A2((A)) --- B2((B))
end
subgraph "Layer 1(中等)"
A1((A)) --- B1((B))
A1 --- C1((C))
B1 --- D1((D))
end
subgraph "Layer 0(密集)"
A0((A)) --- B0((B))
A0 --- C0((C))
B0 --- D0((D))
C0 --- E0((E))
D0 --- F0((F))
E0 --- F0
end
end
Q[查询向量 q] -.->|从顶层开始| A2
A2 -.->|下沉到下层| A1
A1 -.->|细化搜索| E0
原理:
- 构建多层图结构,上层稀疏、下层密集
- 搜索时从顶层入口开始,贪心搜索最近邻
- 逐层下沉,在每层细化搜索结果
- 搜索复杂度:\(O(\log n)\)
IVF(Inverted File Index)
- 先用聚类(如 K-Means)将向量空间分区
- 搜索时只在最近的几个分区(nprobe)内搜索
- 搜索复杂度:\(O(n \cdot \text{nprobe} / K)\)
Product Quantization(PQ)
将高维向量分段量化,压缩存储空间(可压缩 4-64 倍),同时支持近似距离计算。
主流向量数据库
对比表
| 数据库 | 类型 | ANN 算法 | 特点 | 适用场景 |
|---|---|---|---|---|
| Pinecone | 云托管 | 自研 | 全托管、简单易用 | 快速上手、生产级 |
| Weaviate | 开源/云 | HNSW | 多模态、GraphQL | 复杂查询、混合搜索 |
| ChromaDB | 开源 | HNSW | 嵌入式、Python 优先 | 原型开发、本地 |
| Milvus | 开源/云 | IVF/HNSW | 大规模、高性能 | 十亿级数据 |
| Qdrant | 开源/云 | HNSW | Rust 实现、过滤优 | 需要复杂过滤 |
| pgvector | 扩展 | IVF/HNSW | PostgreSQL 扩展 | 已有 PG 基础设施 |
| FAISS | 库 | 多种 | Meta 开源库 | 研究、自定义 |
ChromaDB 示例(本地开发)
import chromadb
# 创建客户端
client = chromadb.Client()
# 创建集合
collection = client.create_collection(
name="agent_memory",
metadata={"hnsw:space": "cosine"}
)
# 添加记忆
collection.add(
documents=[
"用户偏好:喜欢简洁的回答",
"任务记录:完成了数据分析报告",
"学习笔记:掌握了 Python 装饰器",
],
ids=["mem_1", "mem_2", "mem_3"],
metadatas=[
{"type": "preference", "timestamp": "2024-01-01"},
{"type": "task", "timestamp": "2024-01-02"},
{"type": "knowledge", "timestamp": "2024-01-03"},
]
)
# 检索相关记忆
results = collection.query(
query_texts=["用户喜欢什么样的回答风格?"],
n_results=2,
where={"type": "preference"} # 可选的元数据过滤
)
print(results["documents"])
Pinecone 示例(生产环境)
from pinecone import Pinecone
pc = Pinecone(api_key="your-api-key")
index = pc.Index("agent-memory")
# 写入(upsert)
index.upsert(vectors=[
{
"id": "mem_1",
"values": embedding_vector, # 1536维向量
"metadata": {
"text": "用户偏好:简洁回答",
"type": "preference",
"timestamp": 1704067200
}
}
])
# 查询
results = index.query(
vector=query_embedding,
top_k=5,
include_metadata=True,
filter={"type": {"$eq": "preference"}}
)
pgvector 示例(PostgreSQL)
-- 启用扩展
CREATE EXTENSION vector;
-- 创建表
CREATE TABLE agent_memory (
id SERIAL PRIMARY KEY,
content TEXT,
embedding vector(1536),
memory_type VARCHAR(50),
created_at TIMESTAMP DEFAULT NOW()
);
-- 创建 HNSW 索引
CREATE INDEX ON agent_memory
USING hnsw (embedding vector_cosine_ops);
-- 相似性搜索
SELECT content, 1 - (embedding <=> query_embedding) AS similarity
FROM agent_memory
WHERE memory_type = 'preference'
ORDER BY embedding <=> query_embedding
LIMIT 5;
向量搜索管线
graph LR
A[原始文本] --> B[文本分块<br/>Chunking]
B --> C[嵌入模型<br/>Embedding]
C --> D[(向量数据库<br/>Index)]
E[查询] --> F[查询嵌入]
F --> G[ANN 搜索]
D --> G
G --> H[Top-K 结果]
H --> I[重排序<br/>Reranking]
I --> J[最终结果]
混合搜索(Hybrid Search)
单纯的向量搜索可能遗漏关键词匹配的结果。混合搜索结合向量搜索和传统关键词搜索:
\[\text{score}_{\text{hybrid}} = \alpha \cdot \text{score}_{\text{vector}} + (1-\alpha) \cdot \text{score}_{\text{keyword}}\]
# Weaviate 混合搜索示例
result = client.query.get("Memory", ["content", "type"]).with_hybrid(
query="Python 装饰器",
alpha=0.7 # 0=纯关键词, 1=纯向量
).with_limit(5).do()
实践建议
嵌入模型选择
- 通用场景:OpenAI text-embedding-3-small(性价比最佳)
- 高精度:OpenAI text-embedding-3-large 或 Cohere embed-v3
- 私有部署:BGE-M3 或 E5-large-v2
- 代码场景:Voyage Code 或 CodeBERT
分块策略
- 固定大小:简单但可能切断语义
- 语义分块:基于段落/章节分割
- 递归分块:先按大结构分,再递归细分
- 推荐 chunk size:256-512 tokens,overlap 50-100 tokens
性能优化
- 使用 batch 操作减少 API 调用
- 对热点数据建立缓存层
- 选择合适的 ANN 参数(如 HNSW 的 M 和 efConstruction)
- 考虑向量维度压缩(Matryoshka embeddings)
延伸阅读
- RAG 增强记忆 - 基于向量检索的完整 RAG 管线
- Johnson, J., Douze, M., & Jégou, H. (2019). "Billion-scale similarity search with GPUs" (FAISS)
- Malkov, Y. A., & Yashunin, D. A. (2020). "Efficient and robust approximate nearest neighbor search using HNSW"