背景:AI Agent 的"失忆症"
最近在用 AI Agent(比如 Claude、GPT 这类)帮我写代码,发现一个很烦的问题------每次开新对话,它完全不记得我之前是谁、做什么项目、用什么技术栈。每次都要重新自我介绍一遍,效率极低。
市面上的方案要么贵(按月付费),要么数据不在自己手里(云端存储)。我想找个自托管、免费、数据本地、支持多 Agent 共享的记忆系统。
调研了一圈,选了 Honcho------一个用 Python + FastAPI 写的自托管记忆服务,支持多 Agent、向量检索、方言推理。刚好我手头有 WSL2 + Docker Desktop,LLM 用的是 DeepSeek(便宜)。
下面是我从头搭建到跑通的完整过程,重点是我踩的 5 个坑。
整体架构
css
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Hermes │ │ Claude │ │ 其他 Agent │
│ (你) │ │ Code │ │ │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└──────────────────┼──────────────────┘
│
┌──────▼──────┐
│ Honcho │ ← 自托管 Docker
│ API :8000 │
└──────┬──────┘
│
┌───────────────┼───────────────┐
│ │ │
┌─────▼─────┐ ┌──────▼──────┐ ┌─────▼─────┐
│ Deriver │ │ Embedding │ │ Postgres │
│ (DeepSeek)│ │ (jina-v2) │ │ +pgvector │
└───────────┘ └─────────────┘ └───────────┘
三个核心组件:
- Honcho API:接收消息、管理会话
- Deriver:用 LLM 从对话中提取关键信息
- Embedding 服务:把文本转成向量,支持语义搜索
环境
- Windows 11 + WSL2(Ubuntu)
- Docker Desktop(WSL2 集成模式)
- DeepSeek API(deepseek-chat 模型)
- 本地 embedding 服务(jina-embeddings-v2-base-zh,768 维)
坑 1:Docker 镜像拉不下来
第一个命令就跑不通:
bash
git clone https://github.com/plastic-labs/honcho.git
cd honcho
docker compose up -d # ❌ 卡住不动
Docker Hub 在国内基本不可达。解决:在 Docker Desktop → Settings → Docker Engine 里加上镜像源:
json
{
"registry-mirrors": [
"https://docker.1ms.run",
"https://docker.xuanyuan.me"
]
}
改完记得 wsl --shutdown 重启 WSL,否则不生效。
另外如果开了代理(比如 clash-verge,本地端口 10808),还可以在 Docker Desktop Settings → Resources → Proxies 里配上 HTTP/HTTPS 代理,双保险。
坑 2:DeepSeek 当 LLM + Embedding 的"分裂"配置
Honcho 默认用 OpenAI,但我要用 DeepSeek。.env 配置如下:
bash
# LLM 配置
LLM_MODEL_CONFIG__TRANSPORT=deepseek
LLM_MODEL_CONFIG__MODEL=deepseek-chat
LLM_MODEL_CONFIG__API_KEY=sk-xxxxxxxx
LLM_MODEL_CONFIG__BASE_URL=https://api.deepseek.com
# 关键:DeepSeek 没有 embedding API!
# 必须显式配置 embedding provider,否则会走 OpenAI 的 text-embedding-3-small
EMBEDDING_MODEL_CONFIG__TRANSPORT=openai
EMBEDDING_MODEL_CONFIG__MODEL=jina-embeddings-v2-base-zh
EMBEDDING_MODEL_CONFIG__API_KEY=not-needed
EMBEDDING_MODEL_CONFIG__BASE_URL=http://localhost:8080/v1
核心教训:DeepSeek 有 Chat API 但没有 Embedding API 。如果不显式配 EMBEDDING_MODEL_CONFIG,Honcho 会默认走 OpenAI,但又没有 OpenAI key,直接挂掉。
我选的是 jina-embeddings-v2-base-zh,用 sentence-transformers 在本机 8080 端口起了一个兼容 OpenAI 格式的 embedding 服务。768 维,中文友好,单条 51ms,内存占用 ~1.2GB。
坑 3:pgvector 维度不匹配------最隐蔽的坑
Honcho 默认用 PostgreSQL + pgvector 存向量,默认维度是 1536(OpenAI text-embedding-3-small 的输出维度)。
但 jina-v2 输出的是 768 维。
直接写入会报错:
yaml
ERROR: expected 1536 dimensions, not 768
解决分两步:
第一步:改数据库
sql
-- 连接数据库(注意数据库名是 postgres,不是 honcho)
docker exec honcho-database-1 psql -U postgres -d postgres
-- 改两表的向量列
ALTER TABLE documents ALTER COLUMN embedding TYPE vector(768);
ALTER TABLE message_embeddings ALTER COLUMN embedding TYPE vector(768);
-- 重建 HNSW 索引
DROP INDEX IF EXISTS documents_embedding_idx;
CREATE INDEX documents_embedding_idx ON documents USING hnsw (embedding vector_cosine_ops);
DROP INDEX IF EXISTS message_embeddings_embedding_idx;
CREATE INDEX message_embeddings_embedding_idx ON message_embeddings USING hnsw (embedding vector_cosine_ops);
第二步:改源码
Honcho 有个向量迁移策略:schema 里的 Vector(1536) 是硬编码的,并且有个 MIGRATED 环境变量控制是否走新代码路径。但代码里用的是 or not MIGRATED,导致 MIGRATED=false 时永远走旧路径。
需要把 6 个文件里的 or not MIGRATED 改成 and not MIGRATED:
python
# 改前
if not MIGRATED or not migrated:
# 改后
if not MIGRATED and not migrated:
涉及文件:
src/config.pysrc/crud/message.pysrc/crud/document.pysrc/search.pysrc/sync_vectors.py
改完重建镜像:
bash
docker compose build --no-cache api deriver
docker compose up -d api deriver
坑 4:DeepSeek json_object 兼容性------deriver 直接罢工
最坑的一个。
Honcho 的 deriver 组件负责用 LLM 从对话中提取关键信息(观察)。它会调用 DeepSeek API,设置 response_format: json_object。
但 DeepSeek 有一条奇葩要求:prompt 里必须包含单词 "json",否则 400 错误。
vbnet
ERROR - Error code: 400 - "Prompt must contain the word 'json' in some form
to use 'response_format' of type 'json_object'."
Honcho deriver 的系统提示词是 "Analyze messages from {peer_id} to extract explicit atomic facts..."------你仔细看,全篇没有一个 "json" 单词。
deriver 重试 3 次后直接放弃,队列里堆了 49 条消息没人处理,整个推理功能不可用。
解决: 在 src/llm/backends/openai.py 两处注入 "Respond in JSON.":
python
# 位置 1:_build_params() 中 json_mode 分支
if extra_params.get("json_mode"):
params["response_format"] = {"type": "json_object"}
# 注入这一行
system_msg["content"] += "\nRespond in JSON."
# 位置 2:_create_structured_response() 中 json_schema 回退路径
# 类似地,在设置 json_object 后向消息追加 "\nRespond in JSON."
改完同样的 docker compose build --no-cache api deriver && docker compose up -d api deriver。
坑 5:FLUSH_ENABLED------短消息会永久卡在队列
Honcho 有一个消息 flush 机制:新消息会先进队列,等待攒够一定量后再由 deriver 批量处理。但如果你发了一条很短的消息(比如 "你好"),可能永远凑不够批量阈值,消息就一直卡着。
加上这行到 .env:
bash
FLUSH_ENABLED=true
这样每条消息都会触发即时处理,不再等待攒批。
代价是多消耗一点 LLM token(每次都调一次 API),但对个人使用来说完全可忽略。
端到端验证:别信容器状态
容器全部 healthy 不代表功能正常。我总结了一套三步验证法:
bash
# 1. 写入测试
curl -X POST http://localhost:8000/v3/workspaces/hermes/sessions/test/messages \
-H "Content-Type: application/json" \
-d '{"content": "用户用 Python 写后端,喜欢 FastAPI", "peer_name": "sanlin"}'
# 2. 搜索验证(等 30 秒让 deriver 处理)
curl http://localhost:8000/v3/workspaces/hermes/peers/sanlin/search \
-d '{"query": "Python"}'
# 3. 推理验证
# 使用 honcho CLI 或 API 调用推理接口
三步全通过才算"正常运行"。
成本
| 项目 | 费用 |
|---|---|
| DeepSeek API | ~0.1 元/天(个人使用,对话量不大) |
| jina-v2 embedding | 免费(本地跑,1.2GB 内存) |
| Honcho 服务 | 免费(自托管) |
| Docker Desktop | 免费(个人版) |
每月花销不到 3 块钱。
总结
五个坑按隐蔽程度排序:
- ⭐⭐⭐⭐⭐ pgvector 维度不匹配------既要改数据库,又要改源码
- ⭐⭐⭐⭐ json_object 兼容性------DeepSeek 特有要求,报错信息明确但不明显
- ⭐⭐⭐ Embedding 配置分离------DeepSeek 无 embedding API,必须显式配
- ⭐⭐ Docker 镜像拉取------国内通用问题,有标准解法
- ⭐ FLUSH_ENABLED------文档有提但不醒目
如果你也想自托管 Honcho + DeepSeek,照着这五个坑提前填好,应该能少走很多弯路。
最后安利一下 Honcho 的 Peer 模型设计:同一个用户画像(sanlin)可以被多个 Agent(Hermes、Claude Code、自写脚本)共享,但每个 Agent 的行为和记忆又是隔离的。这比简单的"全局记忆池"灵活太多。