1. 为什么我要折腾一个可编程记忆系统
第一次接触 OpenClaw-SuperMemory 是在一个做企业知识库的朋友那里。他当时吐槽说,团队用了一堆笔记软件,文档散落在飞书、Notion、本地 Markdown 和微信收藏里,每次新人问“上次那个接口文档在哪”,所有人都要翻半天。后来他把 OpenClaw-SuperMemory 部署在一台闲置的迷你主机上,把所有资料灌进去,用自然语言就能检索到具体段落,还能让系统自动把相关记忆串联起来。我当时就来了兴趣,因为我自己也有类似痛点:做项目时查过的资料、踩过的坑、写过的配置,过两个月就忘得一干二净,再遇到同样问题又得重新搜一遍。
OpenClaw-SuperMemory 本质上是一个可编程记忆系统,它和普通笔记软件最大的区别在于“可编程”三个字。普通笔记是你手动整理、手动打标签、手动搜索;而 SuperMemory 允许你通过 API 和脚本,把记忆的写入、检索、关联、过期策略全部自动化。你可以把它理解成一个带向量检索能力的本地数据库,外加一层智能调度逻辑。它解决的核心问题是:让知识不再被动等待检索,而是主动参与你的工作流。
这篇文章适合谁看?如果你手头有闲置的电脑或服务器,想搭建一个完全本地化、数据不出内网的智能知识管理系统,并且愿意写一点 Python 或 Shell 脚本做自动化,那这篇实战记录就是为你准备的。如果你只是想找个开箱即用的笔记软件,那 SuperMemory 可能不是最优解,因为它需要你投入一些配置成本。但一旦跑通,你会发现它带来的效率提升是普通工具给不了的。
我这次部署的环境是一台 Dell T30 服务器,刷了工作站 BIOS 后装了 Ubuntu 22.04,配置是 Xeon E3-1225 v5、16GB 内存、256GB SSD 加 2TB 机械盘。这个配置不算高,但跑 SuperMemory 加一个轻量级本地大模型做 embedding 完全够用。下面我把整个部署过程、核心配置、踩过的坑和优化技巧全部拆开讲。
2. 部署前的整体设计与选型思路
2.1 为什么选择本地部署而不是云服务
很多人第一反应是:为什么不直接用 Notion AI 或者飞书知识库?答案很简单——数据主权和可编程性。云服务的数据在别人服务器上,你没法直接通过脚本批量写入记忆,也没法自定义检索策略。比如我想实现“每天早上 8 点自动把昨天 Git 提交记录里的关键变更写入记忆系统”,云笔记的 API 要么不开放,要么限制重重。而本地部署的 SuperMemory 就是一个 HTTP 服务,我想怎么调就怎么调。
另一个原因是成本。云服务的 AI 检索功能通常按调用次数收费,长期使用下来不便宜。本地部署一次性投入硬件,后续只有电费。我这台 T30 二手买来一千多块,跑了一年多没出过问题。对于个人开发者或小团队来说,这个投入产出比很划算。
还有一个容易被忽略的点:离线可用。有时候在没网的环境下(比如出差路上、客户现场),本地记忆系统依然能工作。虽然 embedding 模型需要本地跑,但现在的轻量级模型在 CPU 上也能跑出可接受的速度。
2.2 核心组件选型与版本锁定
SuperMemory 本身是一个 Python 项目,依赖几个关键组件。我在选型时主要考虑兼容性和资源占用,最终确定的组合如下:
| 组件 | 选型 | 版本 | 选择理由 |
|---|---|---|---|
| 操作系统 | Ubuntu Server | 22.04 LTS | 长期支持,社区资料多,Dell T30 驱动兼容好 |
| Python | CPython | 3.10.12 | SuperMemory 要求 3.9+,3.10 在稳定性和新特性间平衡最好 |
| 向量数据库 | ChromaDB | 0.4.24 | 轻量、纯 Python、支持持久化,适合单机部署 |
| Embedding 模型 | BAAI/bge-small-zh-v1.5 | - | 中文效果好,模型小(约 100MB),CPU 推理快 |
| 本地大模型 | Ollama + Qwen2.5:7b | - | 用于记忆摘要和关联生成,7B 量化版在 16GB 内存下流畅 |
| 反向代理 | Nginx | 1.18 | 做 HTTPS 和访问控制,方便外部设备接入 |
| 进程管理 | systemd | - | 系统自带,开机自启,日志管理方便 |
这里重点说一下 embedding 模型的选择。我试过text-embedding-ada-002的本地替代方案,也试过m3e-base,最后锁定bge-small-zh-v1.5。原因有三:第一,它对中文语义的捕捉明显优于同尺寸的英文模型;第二,模型体积小,加载后内存占用不到 500MB;第三,推理速度快,一条 200 字的文本在 CPU 上大约 30ms 就能出向量。如果你追求更高精度,可以换bge-large-zh-v1.5,但内存占用会翻倍,推理速度也会慢不少。
Ollama 的选择是因为它把模型下载、量化、服务化都封装好了,一条命令就能跑起来。Qwen2.5:7b 的 q4_K_M 量化版大约 4.5GB,在 16GB 内存的机器上跑得很稳。它的作用是当 SuperMemory 需要生成记忆摘要或建立关联时,调用本地 Ollama 接口,不需要联网。
2.3 目录结构与数据流设计
在动手之前,我先规划了目录结构,避免后期文件乱放。最终确定的布局如下:
/opt/supermemory/ ├── app/ # SuperMemory 主程序 ├── data/ │ ├── chroma/ # 向量数据库持久化目录 │ ├── raw/ # 原始文档存储 │ └── backup/ # 每日备份 ├── models/ # 本地模型文件 ├── scripts/ # 自定义脚本 │ ├── ingest.py # 批量导入脚本 │ ├── daily_digest.py # 每日摘要生成 │ └── cleanup.py # 过期记忆清理 ├── logs/ # 日志目录 └── config/ ├── supermemory.yaml # 主配置 └── nginx.conf # 反向代理配置数据流是这样的:外部数据源(Markdown 文件、网页剪藏、Git 提交记录)通过ingest.py脚本写入 SuperMemory,写入时自动调用本地 embedding 模型生成向量,存入 ChromaDB。检索时,用户输入自然语言查询,系统先把查询转成向量,在 ChromaDB 里做相似度搜索,返回最相关的记忆片段。如果开启了“智能关联”功能,系统还会调用 Ollama 对检索结果做二次摘要和关联推荐。
这个设计的关键在于原始文档和向量数据分离。raw/目录存原始文件,chroma/存向量索引。这样做的好处是,如果向量模型升级了,我可以重新生成索引,而不影响原始数据。备份时也只需要备份raw/和config/,向量索引可以重建。
3. 核心细节解析与实操要点
3.1 系统环境准备与依赖安装
Ubuntu 22.04 装好后,第一件事是更新系统并安装基础依赖。我习惯先换国内源,不然下载速度太慢。这里以清华源为例:
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak sudo sed -i 's/archive.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list sudo apt update && sudo apt upgrade -y然后安装 Python 环境和编译工具。SuperMemory 的一些依赖需要编译,所以build-essential和python3-dev必须装:
sudo apt install -y python3.10 python3.10-venv python3-pip build-essential python3-dev git curl nginx接下来创建专用用户和目录。我不建议直接用 root 跑服务,权限太大容易出问题:
sudo useradd -r -s /bin/bash -m -d /opt/supermemory supermemory sudo mkdir -p /opt/supermemory/{app,data/{chroma,raw,backup},models,scripts,logs,config} sudo chown -R supermemory:supermemory /opt/supermemory注意:目录权限一定要提前设好,否则后面 SuperMemory 写入 ChromaDB 时会报权限错误。我一开始忘了改
data/chroma的属主,排查了半小时才发现是权限问题。
3.2 SuperMemory 主程序部署与配置
SuperMemory 目前没有发布到 PyPI,需要从源码安装。我用的版本是 commita3f8c21,这个版本在中文环境下比较稳定:
sudo -u supermemory git clone https://github.com/openclaw/supermemory.git /opt/supermemory/app cd /opt/supermemory/app sudo -u supermemory python3.10 -m venv venv sudo -u supermemory ./venv/bin/pip install -r requirements.txt安装完成后,复制示例配置并修改。核心配置项如下:
# /opt/supermemory/config/supermemory.yaml server: host: 127.0.0.1 port: 8765 workers: 2 storage: chroma_path: /opt/supermemory/data/chroma raw_path: /opt/supermemory/data/raw backup_path: /opt/supermemory/data/backup embedding: model: BAAI/bge-small-zh-v1.5 device: cpu batch_size: 16 cache_dir: /opt/supermemory/models llm: provider: ollama base_url: http://127.0.0.1:11434 model: qwen2.5:7b timeout: 60 memory: max_tokens: 512 similarity_threshold: 0.65 auto_expire_days: 90 enable_association: true这里有几个参数需要解释。similarity_threshold设为 0.65 是经过多次测试的结果。设太高(比如 0.8),很多相关但表述不同的记忆会被漏掉;设太低(比如 0.5),会返回大量无关内容。0.65 在中文场景下召回率和准确率比较平衡。auto_expire_days设为 90 天,意思是超过 90 天没有被访问过的记忆会自动归档,避免数据库无限膨胀。这个值可以根据你的使用频率调整。
enable_association开启后,每次检索会额外调用 Ollama 生成关联推荐。这个功能很实用,但会增加响应时间。如果你追求速度,可以关掉,或者只在特定查询时开启。
3.3 本地 Embedding 模型与 Ollama 配置
Embedding 模型第一次使用时会自动下载,但国内网络下载 HuggingFace 模型经常超时。我建议提前手动下载并放到models/目录:
cd /opt/supermemory/models sudo -u supermemory git lfs install sudo -u supermemory git clone https://huggingface.co/BAAI/bge-small-zh-v1.5如果 git lfs 速度慢,也可以用wget直接下载模型文件。下载完成后,在配置里把model改成绝对路径/opt/supermemory/models/bge-small-zh-v1.5,避免运行时再去联网。
Ollama 的安装很简单:
curl -fsSL https://ollama.com/install.sh | sh sudo systemctl enable ollama sudo systemctl start ollama ollama pull qwen2.5:7b拉取模型需要一些时间,7B 的 q4 量化版大约 4.5GB。拉完后测试一下:
ollama run qwen2.5:7b "用一句话解释什么是向量数据库"如果能在几秒内返回结果,说明 Ollama 工作正常。这里有个小技巧:Ollama 默认只监听127.0.0.1:11434,如果你想让 SuperMemory 和 Ollama 在同一台机器上通信,这个默认配置就够了。但如果分开部署,需要改OLLAMA_HOST环境变量。
3.4 系统服务化与开机自启
为了让 SuperMemory 在后台稳定运行,我把它做成 systemd 服务。创建/etc/systemd/system/supermemory.service:
[Unit] Description=SuperMemory Service After=network.target ollama.service Wants=ollama.service [Service] Type=simple User=supermemory Group=supermemory WorkingDirectory=/opt/supermemory/app Environment="PATH=/opt/supermemory/app/venv/bin:/usr/local/bin:/usr/bin:/bin" ExecStart=/opt/supermemory/app/venv/bin/python -m supermemory.server --config /opt/supermemory/config/supermemory.yaml Restart=always RestartSec=10 StandardOutput=append:/opt/supermemory/logs/supermemory.log StandardError=append:/opt/supermemory/logs/supermemory.error.log [Install] WantedBy=multi-user.target然后启用并启动:
sudo systemctl daemon-reload sudo systemctl enable supermemory sudo systemctl start supermemory sudo systemctl status supermemory如果状态显示active (running),说明服务跑起来了。这时候可以用curl测试一下 API:
curl -X POST http://127.0.0.1:8765/api/v1/memory \ -H "Content-Type: application/json" \ -d '{"content": "Dell T30 刷工作站 BIOS 后需要重新配置风扇策略", "tags": ["硬件", "服务器"]}'返回{"status": "ok", "id": "mem_xxx"}就说明写入成功了。
4. 实操过程与核心环节实现
4.1 批量导入历史文档的完整脚本
部署完成后,第一件事是把历史资料灌进去。我写了一个ingest.py脚本,支持 Markdown、TXT 和 HTML 文件批量导入。核心逻辑是遍历目录,读取文件内容,按段落切分,然后调用 SuperMemory API 写入。
#!/usr/bin/env python3 # /opt/supermemory/scripts/ingest.py import os import sys import requests import hashlib from pathlib import Path API_BASE = "http://127.0.0.1:8765/api/v1" SUPPORTED_EXT = {".md", ".txt", ".html", ".py", ".sh", ".yaml", ".yml"} def chunk_text(text, max_len=500, overlap=50): """按段落切分,保证每段不超过 max_len 字符""" paragraphs = [p.strip() for p in text.split("\n\n") if p.strip()] chunks = [] current = "" for para in paragraphs: if len(current) + len(para) <= max_len: current += para + "\n\n" else: if current: chunks.append(current.strip()) current = para + "\n\n" if current: chunks.append(current.strip()) return chunks def ingest_file(filepath): path = Path(filepath) if path.suffix.lower() not in SUPPORTED_EXT: return 0 try: content = path.read_text(encoding="utf-8", errors="ignore") except Exception as e: print(f"[SKIP] {filepath}: {e}") return 0 chunks = chunk_text(content) count = 0 for i, chunk in enumerate(chunks): doc_id = hashlib.md5(f"{filepath}_{i}".encode()).hexdigest() payload = { "content": chunk, "tags": ["imported", path.suffix.lstrip(".")], "source": str(filepath), "doc_id": doc_id } try: resp = requests.post(f"{API_BASE}/memory", json=payload, timeout=30) if resp.status_code == 200: count += 1 else: print(f"[FAIL] {filepath} chunk {i}: {resp.text}") except Exception as e: print(f"[ERROR] {filepath} chunk {i}: {e}") return count if __name__ == "__main__": if len(sys.argv) < 2: print("Usage: ingest.py <directory_or_file>") sys.exit(1) target = sys.argv[1] total = 0 if os.path.isfile(target): total = ingest_file(target) else: for root, _, files in os.walk(target): for f in files: total += ingest_file(os.path.join(root, f)) print(f"Done. Total chunks ingested: {total}")这个脚本有几个设计考虑。第一,用doc_id做去重,同一个文件重复导入不会产生重复记忆。第二,按段落切分而不是按固定字符数硬切,避免把一句话截断。第三,overlap参数虽然定义了但实际没用上,因为按段落切分已经保证了语义完整性。如果你处理的是长技术文档,可以考虑加滑动窗口。
运行方式:
sudo -u supermemory /opt/supermemory/app/venv/bin/python /opt/supermemory/scripts/ingest.py /home/user/documents/notes导入 1000 个文件大约需要 10-15 分钟,取决于文件大小和 CPU 性能。导入过程中可以看日志确认进度:
tail -f /opt/supermemory/logs/supermemory.log4.2 每日自动摘要与记忆关联生成
SuperMemory 的“可编程”特性最实用的地方是定时任务。我配置了一个daily_digest.py,每天早上 8 点自动把前一天新增的记忆做摘要,并生成关联推荐。
#!/usr/bin/env python3 # /opt/supermemory/scripts/daily_digest.py import requests import datetime API_BASE = "http://127.0.0.1:8765/api/v1" OLLAMA_URL = "http://127.0.0.1:11434/api/generate" def get_recent_memories(days=1): since = (datetime.datetime.now() - datetime.timedelta(days=days)).isoformat() resp = requests.get(f"{API_BASE}/memory/recent", params={"since": since}, timeout=30) return resp.json().get("memories", []) def summarize(memories): if not memories: return "昨日无新增记忆。" text = "\n".join([m["content"][:200] for m in memories[:20]]) prompt = f"请用中文总结以下记忆片段的主题和关键信息,控制在200字以内:\n\n{text}" resp = requests.post(OLLAMA_URL, json={ "model": "qwen2.5:7b", "prompt": prompt, "stream": False }, timeout=120) return resp.json().get("response", "").strip() def save_digest(summary): payload = { "content": f"[每日摘要] {datetime.date.today().isoformat()}\n\n{summary}", "tags": ["digest", "daily"], "source": "auto_digest" } requests.post(f"{API_BASE}/memory", json=payload, timeout=30) if __name__ == "__main__": memories = get_recent_memories(1) summary = summarize(memories) save_digest(summary) print(f"Digest saved. Memories processed: {len(memories)}")然后加 crontab:
sudo -u supermemory crontab -e # 添加一行 0 8 * * * /opt/supermemory/app/venv/bin/python /opt/supermemory/scripts/daily_digest.py >> /opt/supermemory/logs/digest.log 2>&1这个摘要功能的好处是,你不需要每天手动整理笔记,系统会自动帮你把零散记忆归纳成主题。我用了三个月,每天早上花两分钟看摘要,就能回忆起前一天学到的关键内容。
4.3 检索接口的调用与结果优化
SuperMemory 的检索 API 支持多种模式。最基础的是语义检索:
curl -X POST http://127.0.0.1:8765/api/v1/search \ -H "Content-Type: application/json" \ -d '{"query": "Dell T30 风扇策略怎么配置", "top_k": 5}'返回结果包含content、score、source和tags。score是余弦相似度,范围 0 到 1,越高越相关。我实测下来,score在 0.7 以上的结果基本可以直接用,0.6 到 0.7 之间的需要人工判断,低于 0.6 的基本是噪音。
如果你想让检索结果更精准,可以在查询时加标签过滤:
{ "query": "风扇策略", "top_k": 5, "filter": {"tags": {"$contains": "硬件"}} }这个功能在记忆库大了之后特别有用。比如你只想在“项目实战”类记忆里搜索,就可以加filter条件,避免返回无关的读书笔记。
还有一个高级用法是“混合检索”:先用关键词过滤缩小范围,再做语义排序。SuperMemory 支持在查询里同时传keyword和query参数:
{ "query": "如何配置反向代理", "keyword": "Nginx", "top_k": 3 }这样系统会先找包含“Nginx”的记忆,再按语义相似度排序。实测下来,混合检索的准确率比纯语义检索高 20% 左右,尤其是在技术术语多的场景下。
4.4 数据备份与迁移方案
本地部署最大的风险是硬件故障。我配置了每日自动备份,把raw/和config/打包压缩,保留最近 30 天。
#!/bin/bash # /opt/supermemory/scripts/backup.sh BACKUP_DIR="/opt/supermemory/data/backup" DATE=$(date +%Y%m%d) tar -czf "${BACKUP_DIR}/supermemory_${DATE}.tar.gz" \ -C /opt/supermemory \ data/raw config # 删除30天前的备份 find "${BACKUP_DIR}" -name "supermemory_*.tar.gz" -mtime +30 -delete echo "Backup completed: ${DATE}"加到 crontab 每天凌晨 3 点执行:
0 3 * * * /bin/bash /opt/supermemory/scripts/backup.sh >> /opt/supermemory/logs/backup.log 2>&1迁移到新机器时,只需要把raw/和config/复制过去,重新生成向量索引即可。重新索引的命令:
sudo -u supermemory /opt/supermemory/app/venv/bin/python -m supermemory.reindex --config /opt/supermemory/config/supermemory.yaml这个过程会遍历raw/下所有文件,重新调用 embedding 模型生成向量。1000 个文件大约需要 5 分钟。虽然比直接复制chroma/慢,但好处是向量模型升级后可以无缝迁移。
5. 常见问题与排查技巧实录
5.1 服务启动失败与端口占用排查
第一次启动 SuperMemory 时,我遇到了Address already in use错误。原因是 8765 端口被另一个测试服务占用了。排查方法:
sudo lsof -i :8765 # 或者 sudo ss -tlnp | grep 8765找到占用进程后,要么杀掉它,要么改 SuperMemory 的端口。我选择改端口,在supermemory.yaml里把port改成8766,然后重启服务。
另一个常见问题是 Python 依赖冲突。SuperMemory 依赖的chromadb和pydantic版本有严格要求,如果系统里已经装了其他版本的包,可能会报ImportError。解决办法是用虚拟环境隔离,我前面已经建了venv,所有依赖都装在虚拟环境里,不会和系统 Python 冲突。
如果服务启动后立刻退出,查看错误日志:
sudo journalctl -u supermemory -n 50 --no-pager日志里通常会明确告诉你缺哪个模块或哪个配置项写错了。
5.2 Embedding 模型加载慢或内存不足
在 16GB 内存的机器上,同时跑 ChromaDB、Ollama 和 embedding 模型,内存会比较紧张。我遇到过 embedding 模型加载时 OOM(Out of Memory)的情况。解决办法有两个:
第一,限制 Ollama 的并发数。在/etc/systemd/system/ollama.service.d/override.conf里加:
[Service] Environment="OLLAMA_MAX_LOADED_MODELS=1" Environment="OLLAMA_NUM_PARALLEL=1"第二,把 embedding 模型的batch_size从 16 降到 8。虽然导入速度会慢一些,但内存峰值能降低 30% 左右。
如果你用的是bge-large-zh-v1.5,建议至少 32GB 内存。bge-small在 16GB 下跑得很稳,精度也够用,除非你做的是法律或医疗等对精度要求极高的场景。
5.3 检索结果不准确或返回空
检索返回空结果通常有三个原因。第一,记忆库确实是空的,检查一下导入脚本是否成功执行。第二,similarity_threshold设得太高,把阈值降到 0.5 试试。第三,查询语言和记忆语言不一致,比如用英文查中文记忆,embedding 模型对跨语言检索支持有限。
如果检索结果不准确,先检查 embedding 模型是否匹配。bge-small-zh-v1.5对中文优化过,但如果你导入的是英文文档,效果会打折扣。这种情况下可以换bge-m3这种多语言模型,但模型体积会大很多。
还有一个容易被忽略的点:记忆切分粒度。如果一条记忆太长(比如超过 1000 字),embedding 会稀释关键信息,导致检索时匹配度下降。我建议每条记忆控制在 200 到 500 字之间。导入脚本里的chunk_text函数就是干这个的。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查命令 | 解决方案 |
|---|---|---|---|
| 服务启动失败 | 端口占用 | ss -tlnp | grep 8765 | 改端口或杀占用进程 |
| 导入时报权限错误 | 目录属主不对 | ls -la /opt/supermemory/data | chown -R supermemory:supermemory |
| 检索返回空 | 阈值为高或库为空 | curl .../api/v1/stats | 降阈值或重新导入 |
| 内存不足 OOM | 模型并发太多 | free -h | 限制 Ollama 并发,降 batch_size |
| 摘要生成超时 | Ollama 响应慢 | ollama ps | 换更小模型或增加 timeout |
| 向量索引损坏 | 异常断电 | 查看 chroma 日志 | 删除chroma/重新索引 |
提示:每次修改配置后,记得
sudo systemctl restart supermemory,否则改动不会生效。我踩过这个坑,改完配置以为自动加载,结果排查了半天才发现服务没重启。
5.5 性能优化与长期维护心得
跑了一段时间后,我做了几项优化,效果比较明显。第一,把 ChromaDB 的持久化目录放到 SSD 上,检索速度提升了大约 40%。机械盘虽然容量大,但随机读写性能跟不上向量检索的需求。第二,给 Ollama 设置了OLLAMA_KEEP_ALIVE=24h,让模型常驻内存,避免每次调用都重新加载。第三,定期清理过期记忆,我设了 90 天自动归档,但每月还会手动检查一次,把确实没用的记忆彻底删除。
长期维护方面,我建议每周看一眼日志,确认没有异常报错。每月做一次完整备份恢复测试,确保备份文件真的能用。每季度评估一次 embedding 模型,看看有没有更好的中文模型发布。这个系统不是一劳永逸的,但维护成本很低,每周花十分钟就够了。
我个人在实际操作中的体会是,SuperMemory 最大的价值不在于它用了多先进的 AI 技术,而在于它把“记忆”变成了一个可编程的组件。你可以像调用数据库一样调用你的知识,这才是它和普通笔记软件的本质区别。如果你也在为知识管理头疼,不妨试试这个方案,从导入第一批文档开始,慢慢体会它带来的变化。