告别 Moltbook:如何搭建私有的 Agent 知识库
Moltbook 的数据泄露让很多人意识到了"数据主权"的重要性。
我们希望 Agent 能记住我们的喜好、历史任务、常用指令。我们希望它越用越懂我们,变成真正的"私人助理"。但我们不希望这些记忆存在别人的服务器上,更不希望这些记忆和 150 万人的数据一起被泄露。
好消息是:OpenClaw 完全可以搭配本地知识库使用。你不需要上传任何数据,也能拥有一个"懂你"的贾维斯。
这篇文章会手把手教你搭建一套私有的 Agent 记忆系统。
为什么需要知识库?
先理解一下问题。
LLM(大语言模型)本身是"无状态"的。每次对话,它都是从零开始。昨天你告诉它你喜欢吃川菜,今天它就忘了。上周你让它帮你整理了一份报告,这周问它要,它一脸懵逼。
解决这个问题的方法是给 Agent 加一个"外部记忆"——知识库。
知识库的工作原理:
- 存储:把每次对话、重要信息转换成向量(embedding),存到数据库里
- 检索:下次对话时,根据当前话题,从数据库里找出相关的历史信息
- 注入:把检索到的信息作为上下文,和当前问题一起发给 LLM
这就是所谓的 RAG(Retrieval-Augmented Generation)。
方案选择:本地向量数据库
市面上有很多向量数据库,我们要选择可以完全本地部署的:
| 方案 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|
| ChromaDB | 轻量、Python 原生、易上手 | 功能相对简单 | 个人使用、快速原型 |
| Qdrant | 功能强大、性能好、有 Web UI | 配置稍复杂 | 中大规模部署 |
| Milvus | 企业级、高性能、分布式 | 重量级、资源消耗大 | 大规模生产环境 |
| LanceDB | 新项目、嵌入式、零配置 | 生态较新 | 嵌入式应用 |
| pgvector | PostgreSQL 扩展、熟悉的 SQL | 需要 PostgreSQL | 已有 PG 的环境 |
对于个人用户,我推荐 ChromaDB 或 Qdrant。下面分别介绍。
方案一:ChromaDB(最简单)
ChromaDB 是一个轻量级的向量数据库,特别适合 Python 生态。
安装和运行
# 方式一:直接用 Docker
docker run -p 8000:8000 chromadb/chroma
# 方式二:Python 包(嵌入式使用)
pip install chromadb
基础使用示例
import chromadb
from chromadb.config import Settings
# 创建客户端,数据持久化到本地目录
client = chromadb.Client(Settings(
chroma_db_impl="duckdb+parquet",
persist_directory="./chroma_data"
))
# 创建或获取一个 collection
collection = client.get_or_create_collection(
name="agent_memory",
metadata={"description": "OpenClaw Agent 的记忆存储"}
)
# 添加记忆
def add_memory(content, metadata=None):
import uuid
doc_id = str(uuid.uuid4())
collection.add(
documents=[content],
metadatas=[metadata or {}],
ids=[doc_id]
)
return doc_id
# 搜索相关记忆
def search_memory(query, n_results=5):
results = collection.query(
query_texts=[query],
n_results=n_results
)
return results
# 示例:存储用户偏好
add_memory(
"用户喜欢吃川菜,尤其是麻婆豆腐和水煮鱼。不能吃太辣的。",
metadata={"type": "preference", "category": "food", "date": "2026-02-01"}
)
add_memory(
"用户的工作是软件工程师,主要使用 Python 和 TypeScript。",
metadata={"type": "profile", "category": "work", "date": "2026-02-01"}
)
# 示例:检索
results = search_memory("推荐一家餐厅吃饭")
print(results)
# 会返回关于饮食偏好的记忆
持久化配置
# 确保数据持久化
client = chromadb.Client(Settings(
chroma_db_impl="duckdb+parquet",
persist_directory="/path/to/your/data", # 指定持久化目录
anonymized_telemetry=False # 关闭遥测
))
# 定期调用 persist() 确保数据写入磁盘
client.persist()
Docker Compose 部署
# docker-compose.yml
version: '3.8'
services:
chroma:
image: chromadb/chroma:latest
container_name: chroma
ports:
- "127.0.0.1:8000:8000"
volumes:
- ./chroma_data:/chroma/chroma # 数据持久化
environment:
- ANONYMIZED_TELEMETRY=false
restart: unless-stopped
openclaw:
image: openclaw/openclaw:latest
depends_on:
- chroma
environment:
- MEMORY_BACKEND=chroma
- CHROMA_HOST=chroma
- CHROMA_PORT=8000
# ...其他配置
方案二:Qdrant(更强大)
Qdrant 是一个用 Rust 写的向量数据库,性能出色,功能完善。
Docker 部署
docker run -p 6333:6333 -p 6334:6334 \
-v ./qdrant_data:/qdrant/storage \
qdrant/qdrant
基础使用示例
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct
import uuid
# 连接到 Qdrant
client = QdrantClient(host="localhost", port=6333)
# 创建 collection
client.create_collection(
collection_name="agent_memory",
vectors_config=VectorParams(size=1536, distance=Distance.COSINE)
# 1536 是 OpenAI text-embedding-ada-002 的维度
)
# 生成 embedding(使用 OpenAI 或本地模型)
def get_embedding(text):
import openai
response = openai.Embedding.create(
model="text-embedding-ada-002",
input=text
)
return response['data'][0]['embedding']
# 添加记忆
def add_memory(content, metadata=None):
point_id = str(uuid.uuid4())
embedding = get_embedding(content)
client.upsert(
collection_name="agent_memory",
points=[
PointStruct(
id=point_id,
vector=embedding,
payload={
"content": content,
**(metadata or {})
}
)
]
)
return point_id
# 搜索
def search_memory(query, limit=5):
query_embedding = get_embedding(query)
results = client.search(
collection_name="agent_memory",
query_vector=query_embedding,
limit=limit
)
return [
{
"content": hit.payload["content"],
"score": hit.score,
"metadata": {k: v for k, v in hit.payload.items() if k != "content"}
}
for hit in results
]
使用本地 Embedding 模型
如果你不想把文本发给 OpenAI 来生成 embedding,可以用本地模型:
from sentence_transformers import SentenceTransformer
# 加载本地模型
model = SentenceTransformer('all-MiniLM-L6-v2') # 384 维
# 或者中文模型
# model = SentenceTransformer('shibing624/text2vec-base-chinese')
def get_local_embedding(text):
return model.encode(text).tolist()
# 注意:创建 collection 时要改成对应的维度
client.create_collection(
collection_name="agent_memory",
vectors_config=VectorParams(size=384, distance=Distance.COSINE)
)
Docker Compose 完整配置
version: '3.8'
services:
qdrant:
image: qdrant/qdrant:latest
container_name: qdrant
ports:
- "127.0.0.1:6333:6333" # REST API
- "127.0.0.1:6334:6334" # gRPC
volumes:
- ./qdrant_data:/qdrant/storage
environment:
- QDRANT__SERVICE__GRPC_PORT=6334
restart: unless-stopped
openclaw:
image: openclaw/openclaw:latest
depends_on:
- qdrant
environment:
- MEMORY_BACKEND=qdrant
- QDRANT_HOST=qdrant
- QDRANT_PORT=6333
volumes:
- ./data:/app/data
ports:
- "127.0.0.1:3000:3000"
restart: unless-stopped
与 OpenClaw 集成
假设 OpenClaw 支持自定义记忆后端(或者你可以修改代码),以下是集成思路:
# memory_adapter.py - OpenClaw 的记忆适配器
class LocalMemoryAdapter:
def __init__(self, backend="chroma"):
if backend == "chroma":
import chromadb
self.client = chromadb.Client(...)
self.collection = self.client.get_or_create_collection("agent_memory")
elif backend == "qdrant":
from qdrant_client import QdrantClient
self.client = QdrantClient(...)
def store(self, content, metadata=None):
"""存储一条记忆"""
# ...
def recall(self, query, limit=5):
"""检索相关记忆"""
# ...
def forget(self, memory_id):
"""删除一条记忆"""
# ...
def summarize_and_compress(self):
"""定期总结和压缩旧记忆"""
# 防止记忆库无限增长
# ...
# 在 OpenClaw 的配置中使用
# config.json
{
"memory": {
"enabled": true,
"backend": "local",
"adapter": "memory_adapter.LocalMemoryAdapter",
"options": {
"backend": "qdrant",
"host": "localhost",
"port": 6333
}
}
}
记忆管理最佳实践
1. 分类存储
不要把所有东西混在一起。按类型分 collection:
collections = {
"conversations": "对话历史",
"user_preferences": "用户偏好",
"knowledge": "知识库(文档、笔记)",
"tasks": "任务记录",
"feedback": "用户反馈"
}
for name, description in collections.items():
client.get_or_create_collection(
name=name,
metadata={"description": description}
)
2. 设置 TTL(过期时间)
旧的记忆可能不再相关,设置过期机制:
import time
def add_memory_with_ttl(content, ttl_days=30):
expiry = time.time() + ttl_days * 86400
add_memory(content, metadata={"expiry": expiry})
def cleanup_expired():
# 定期清理过期记忆
current_time = time.time()
# 查询并删除过期的记录
# ...
3. 定期备份
#!/bin/bash
# backup_memory.sh
BACKUP_DIR="/backup/agent_memory"
DATE=$(date +%Y%m%d_%H%M%S)
# 停止服务(可选,确保数据一致性)
docker-compose stop qdrant
# 备份数据目录
tar -czvf "${BACKUP_DIR}/qdrant_${DATE}.tar.gz" ./qdrant_data
# 重启服务
docker-compose start qdrant
# 保留最近 7 天的备份
find ${BACKUP_DIR} -name "qdrant_*.tar.gz" -mtime +7 -delete
echo "Backup completed: qdrant_${DATE}.tar.gz"
加入 crontab:
# 每天凌晨 3 点备份
0 3 * * * /path/to/backup_memory.sh >> /var/log/memory_backup.log 2>&1
4. 加密敏感记忆
对于特别敏感的信息,存储前先加密:
from cryptography.fernet import Fernet
# 生成密钥并安全保存
key = Fernet.generate_key()
cipher = Fernet(key)
def add_encrypted_memory(content, is_sensitive=False):
if is_sensitive:
encrypted = cipher.encrypt(content.encode()).decode()
add_memory(encrypted, metadata={"encrypted": True})
else:
add_memory(content, metadata={"encrypted": False})
def search_and_decrypt(query, limit=5):
results = search_memory(query, limit)
for result in results:
if result.get("metadata", {}).get("encrypted"):
result["content"] = cipher.decrypt(result["content"].encode()).decode()
return results
完整的部署示例
# docker-compose.yml
version: '3.8'
services:
# 向量数据库
qdrant:
image: qdrant/qdrant:latest
container_name: agent-memory
ports:
- "127.0.0.1:6333:6333"
volumes:
- qdrant_data:/qdrant/storage
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:6333/"]
interval: 30s
timeout: 10s
retries: 3
# Embedding 服务(可选,如果用本地模型)
embedding:
image: ghcr.io/huggingface/text-embeddings-inference:cpu-1.0
container_name: embedding-service
ports:
- "127.0.0.1:8080:80"
volumes:
- embedding_cache:/data
environment:
- MODEL_ID=BAAI/bge-small-en-v1.5
restart: unless-stopped
# OpenClaw
openclaw:
image: openclaw/openclaw:latest
container_name: openclaw
depends_on:
- qdrant
- embedding
ports:
- "127.0.0.1:3000:3000"
volumes:
- openclaw_data:/app/data
- ./config:/app/config:ro
environment:
- MEMORY_ENABLED=true
- QDRANT_URL=http://qdrant:6333
- EMBEDDING_URL=http://embedding:80
restart: unless-stopped
volumes:
qdrant_data:
embedding_cache:
openclaw_data:
总结
搭建私有知识库的要点:
- 选择合适的向量数据库:个人用户推荐 ChromaDB 或 Qdrant
- 数据永不离开本地:配置持久化目录,绑定到 localhost
- 考虑使用本地 Embedding:避免把文本发给 OpenAI
- 做好备份:定期备份数据目录
- 必要时加密:敏感信息存储前先加密
这样做的体验差异几乎为零,但安全性天差地别。你的 Agent 依然聪明,记得你上次让它写的代码风格,记得你讨厌吃香菜。但这些信息物理上从未离开过你的控制范围。
对于那些敏感的笔记、代码片段、私人对话,本地知识库是唯一的归宿。既然选择了 Local Agent 这条路,那就把"Local"贯彻到底吧。Moltbook 的悲剧,不应该在你身上重演。