Claude Code

用 mem0 为 Claude Code 搭建自托管记忆

October 1, 2026 · 3 分钟阅读

Claude Code 自带一套记忆系统,但它只是按项目目录划分的 markdown 文件。我想要的是一份持久的记忆:跨项目、跨会话跟随我,完全运行在自己的机器上,不依赖任何托管的记忆服务。下面是实现这一点的技术栈 —— 以及一路上踩过的坑。

整体结构

┌─────────────┐   SSE over HTTP   ┌───────────────┐   SQL   ┌──────────────┐
│ Claude Code │ ────────────────▶ │ mem0-open-mcp │ ──────▶ │ pgvector     │
│ (MCP 客户端 │  :8765 + bearer   │  (Docker)     │         │  (Docker)    │
│  + CLAUDE.md)│                  │  mem0ai 库    │         │  mem0_claude │
└─────────────┘                   └──────┬────────┘         └──────────────┘
                                         │ OpenAI 兼容 API
                                         ▼ host.docker.internal:9800/v1
                                  ┌───────────────┐
                                  │ oMLX (macOS)  │
                                  │ 对话 + 嵌入   │
                                  └───────────────┘

三个活动部件:模型服务器、数据库、MCP 服务器。每一个单独看都很平淡。

1. oMLX 提供模型

mem0 需要两类模型调用:一个 LLM 负责判断哪些内容值得记住(从对话中提取事实),一个嵌入模型负责把记忆向量化以便检索。两者都通过 oMLX 在本地运行 —— 这是一个面向 Apple Silicon 的 OpenAI 兼容推理服务器:

因为它提供标准的 /v1 API,mem0 的 openai provider 可以原样对接。Docker 容器通过 http://host.docker.internal:9800/v1 访问它 —— 这是 Docker Desktop 指向宿主机的别名。

2. pgvector 存储记忆

本地克隆的 mem0 仓库提供了一个 compose 项目(server/docker-compose.yaml),其中的 postgres 服务使用 pgvector/pgvector:pg17 镜像。compose 文件里完整的 mem0 API 服务和 dashboard 都用不上 —— MCP 服务器通过 mem0ai Python 库直接访问 Postgres —— 所以只需运行数据库容器。

一个挂载到 /docker-entrypoint-initdb.d/ 的初始化脚本会在首次启动时创建专用的数据库和用户,让 Claude 的记忆不与同一个 Postgres 上的其他数据混用 schema:

#!/bin/bash
set -e
psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB" <<'EOSQL'
   CREATE USER mem0_claude_user WITH PASSWORD '<db-password>' SUPERUSER;
EOSQL
createdb -U "$POSTGRES_USER" mem0_claude -O mem0_claude_user || true
psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d mem0_claude <<'EOSQL'
   CREATE EXTENSION IF NOT EXISTS vector;
EOSQL

该目录下的脚本按字母序执行 —— zz- 前缀保证它在仓库自带的初始化脚本之后运行。

3. mem0-open-mcp 是桥梁

mem0-open-mcp 是开源的 MCP 服务器,把 mem0 暴露为一组工具:add_memories、search_memory、list_memories 等。它运行在一个小型自定义镜像里:

FROM python:3.11-slim
WORKDIR /app
RUN pip install --no-cache-dir "mem0ai<2" "mem0-open-mcp" "mcp>=1.28,<2" "psycopg[binary,pool]"

# 修复 0.2.12 中的一个真实 bug:AsyncMemory.from_config 不是协程,
# 对它 await 会导致服务器在启动时崩溃。
RUN sed -i 's/await AsyncMemory\.from_config/AsyncMemory.from_config/g' \
        /usr/local/lib/python3.11/site-packages/mem0_server/server.py \
        /usr/local/lib/python3.11/site-packages/mem0_server/cli.py

EXPOSE 8765
CMD ["mem0-open-mcp", "serve"]

所有行为都由一个 bind-mount 的 YAML 决定,改配置不需要重新构建镜像:

server:
  host: "0.0.0.0"
  port: 8765
  user_id: "claude_user_01"

auth:
  api_key: "<客户端必须携带的-bearer-token>"

llm:
  provider: "openai"
  config:
    base_url: "http://host.docker.internal:9800/v1"
    model: "gemma-4-e2b-it-4bit"
    api_key: "env:OPENAI_API_KEY"

embedder:
  provider: "openai"
  config:
    base_url: "http://host.docker.internal:9800/v1"
    model: "bge-m3-mlx-8bit"
    api_key: "env:OPENAI_API_KEY"
    embedding_dims: 1024

vector_store:
  provider: "pgvector"
  config:
    host: "postgres"        # compose 服务名,在 docker 网络内解析
    port: 5432
    collection_name: "claude_memories"
    embedding_model_dims: 1024
    extra:
      dbname: "mem0_claude"
      user: "mem0_claude_user"
      password: "<db-password>"

在 mem0 克隆仓库中加一个 compose override,把该服务挂到与 Postgres 相同的网络上:

services:
  mem0-open-mcp:
    build:
      context: ~/.config/mem0-claude
      dockerfile: Dockerfile
    image: mem0-open-mcp:local
    restart: unless-stopped
    volumes:
      - ~/.config/mem0-claude/mem0-open-mcp.yaml:/app/mem0-open-mcp.yaml:ro
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}
    networks:
      - mem0_network
    ports:
      - "8765:8765"
    command: mem0-open-mcp serve --port 8765 --user-id claude_user_01
    depends_on:
      - postgres

4. 教会 Claude Code 使用它

把服务器注册到 user 作用域,让所有项目都能用到:

claude mcp add --scope user --transport sse mem0 \
  http://127.0.0.1:8765/mcp/claude/sse/claude_user_01 \
  --header "Authorization: Bearer <bearer-token>"

URL 路径中编码了客户端名称和用户 id;bearer token 必须与 YAML 中的 auth.api_key 一致。

光有工具还不够 —— Claude 还需要知道什么时候调用它们。我的全局 ~/.claude/CLAUDE.md 里写了这样的规则:mem0 是主要的记忆存储 —— 持久的偏好和上下文保存在那里;当过去的会话可能有帮助时去搜索;只有在服务器不可用时才回退到基于文件的记忆。没有这段话,服务器就只会闲置在那里。

踩过的坑

验证它是否工作

claude mcp list                      # mem0 应显示 ✔ Connected
docker ps | grep -E 'mem0|postgres'  # 两个容器都在运行

然后是真正的测试:告诉 Claude “记住我偏好 X”,开一个新会话,问它对你了解多少。如果答案回来了,这个闭环就完成了。

← 返回所有文章