已对照 rohitg00/agentmemory 源码核对(npm 最新 0.9.26,引擎锁定 iii v0.11.2,Apache-2.0)。

架构:NAS 跑一个全功能 agentmemory 服务器(数据在 /data 卷)。

  • API(3111):经 Tailscale sidecar 接入 tailnet → 多设备/远程访问,不暴露局域网/公网。
  • viewer(3113):发布到 NAS 局域网 → 同一内网用浏览器直接打开。
  • 压缩 LLM:走 OpenAI 兼容通道接 DeepSeek deepseek-v4-flash
  • 每台开发机只装 Claude Code 插件 + 轻量 MCP shim,经 tailnet 指向 NAS,共享同一份记忆。

⚠️ 安全须知:viewer(3113)是免密特权界面(它代理请求时自动附带你的 AGENTMEMORY_SECRET)。
绑到局域网意味着同一内网任何设备都能全权读写你的记忆。家庭内网可接受;公共/办公网请改用 SSH 隧道(见附录 B)。


下面是整体架构图,帮你快速理解各组件之间的关系:

云端

NAS 局域网

Tailscale Tailnet

NAS(群晖/任意 Linux)

http://NAS_LAN_IP:3113

OpenAI 兼容

agentmemory 服务器
API(3111) + viewer(3113)

Tailscale sidecar
ts-agentmemory

/data 卷
记忆持久化

开发机 A
Claude Code + MCP shim

开发机 B
Claude Code + MCP shim

浏览器
访问 viewer(3113)

DeepSeek API
deepseek-v4-flash

0. 前置准备

# 在 NAS 上(SSH;需 Docker / Container Manager / Portainer 能跑 compose)
cd /volume1/docker            # 群晖示例,换成你的路径
git clone --depth 1 https://github.com/rohitg00/agentmemory.git

Tailscale auth key:登录 https://login.tailscale.com/admin/settings/keys → Auth keys →
Generate auth key,设置:

  • Reusable:(容器重建可再次认证)
  • Ephemeral:(NAS 是常驻节点,别让它离线被清除)
  • Expiration:默认 90 天即可(只在首次认证用一次)
  • Tags:可不打

生成的 tskey-auth-xxxx 一会儿填进 .env
收尾:容器连上后,去 Tailscale 后台 Machines → 找到 agentmemory 节点 → Disable key expiry,
否则约 180 天后节点会因 node key 过期掉线。

DeepSeek API key(用于压缩):登录 https://platform.deepseek.com → API keys → 创建,得到 sk-xxxx
(key 必须你自己申请;别人无法提供。)


1.(推荐)定制 Dockerfile:开启本地向量嵌入 + 中文分词

coolify 的 Dockerfile 默认用 --omit=optional,会跳过本地向量模型,退化成纯 BM25 关键词检索;
且对中文是整段切词。你写中文、要语义召回,就改一行。

编辑 agentmemory/deploy/coolify/Dockerfile,把那条 npm install ... 改成:

RUN printf '{"name":"agentmemory-deploy","version":"1.0.0","private":true,"overrides":{"iii-sdk":"%s"}}\n' "${III_SDK_VERSION}" > package.json \
 && npm install "@agentmemory/agentmemory@${AGENTMEMORY_VERSION}" --no-fund --no-audit \
 && npm install @xenova/transformers @node-rs/jieba tiny-segmenter --no-fund --no-audit \
 && ln -s /opt/agentmemory/node_modules/.bin/agentmemory /usr/local/bin/agentmemory

(去掉了 --omit=optional,显式装了本地嵌入模型 + 中日韩分词器。)

维护提示:你改过这个文件后不要直接 git pull(会被覆盖)。日常升级只改 compose 里的版本号即可(见第 6 节)。


2. docker-compose.yml

存成 docker-compose.yml,和 clone 出来的 agentmemory/ 目录同级

services:
  # Tailscale 节点:持有网络栈,agentmemory 共享它
  ts-agentmemory:
    image: tailscale/tailscale:latest
    hostname: agentmemory                 # → MagicDNS:http://agentmemory:3111
    environment:
      - TS_AUTHKEY=${TS_AUTHKEY}
      - TS_STATE_DIR=/var/lib/tailscale
      - TS_USERSPACE=false                # 内核模式(需 /dev/net/tun)
    volumes:
      - ts-state:/var/lib/tailscale
    devices:
      - /dev/net/tun:/dev/net/tun
    cap_add:
      - net_admin
    ports:
      - "3113:3113"                       # viewer 发布到 NAS 局域网(端口须发布在网络栈持有者上)
    restart: unless-stopped

  agentmemory:
    build:
      context: ./agentmemory/deploy/coolify
      dockerfile: Dockerfile
      args:
        AGENTMEMORY_VERSION: "0.9.26"
        III_VERSION: "0.11.2"
        III_SDK_VERSION: "0.11.2"
    network_mode: service:ts-agentmemory  # 与 Tailscale 容器共享网络
    depends_on:
      - ts-agentmemory
    environment:
      # ---- 压缩 LLM:DeepSeek(OpenAI 兼容)----
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - OPENAI_BASE_URL=${OPENAI_BASE_URL} #https://api.deepseek.com
      - OPENAI_MODEL=${OPENAI_MODEL} #deepseek-v4-flash
      # ---- viewer 局域网访问 ----
      - AGENTMEMORY_VIEWER_HOST=0.0.0.0
      - VIEWER_ALLOWED_HOSTS=${NAS_LAN_IP}:3113
      - VIEWER_ALLOWED_ORIGINS=http://${NAS_LAN_IP}:3113
    volumes:
      - agentmemory-data:/data
    restart: unless-stopped

volumes:
  ts-state:
  agentmemory-data:

.env(同目录,务必加入 .gitignore):

TS_AUTHKEY=tskey-auth-xxxxxxxx
OPENAI_BASE_URL=sk-xxxxxxxx
OPENAI_API_KEY=https://api.deepseek.com
OPENAI_MODEL=deepseek-v4-flash
NAS_LAN_IP=192.168.1.50          # 换成你 NAS 的局域网 IP

端口说明:API(3111)、stream(3112)被 entrypoint 绑到 0.0.0.0,通过 Tailscale 节点在 tailnet 上可达,
发布到局域网;viewer(3113)默认绑 loopback,这里用 AGENTMEMORY_VIEWER_HOST=0.0.0.0 改绑 + 在
ts-agentmemoryports: 3113:3113 发布到局域网。


下面是容器网络与服务依赖关系图:

环境变量

docker-compose.yml 服务栈

共享网络栈

ts-agentmemory
Tailscale sidecar
网络栈持有者

agentmemory
主服务
network_mode: service:ts-agentmemory

ts-state 卷

agentmemory-data 卷

TS_AUTHKEY

DEEPSEEK_API_KEY

NAS_LAN_IP

3. 启动 + 取密钥

docker compose up -d --build
# 首次启动入口会生成一次性密钥,只打印一次,记下来:
docker compose logs agentmemory | grep AGENTMEMORY_SECRET
# 形如 AGENTMEMORY_SECRET=<64位hex>(持久化在 /data/.hmac)

验证:

# API(任意 tailnet 设备)
curl http://agentmemory:3111/agentmemory/health
# viewer(任意局域网设备浏览器)
#   http://192.168.1.50:3113

下面是首次启动的完整流程:

DeepSeek Tailscale agentmemory ts-agentmemory docker compose 管理员 DeepSeek Tailscale agentmemory ts-agentmemory docker compose 管理员 密钥持久化到 /data/.hmac docker compose up -d --build 启动 Tailscale sidecar 使用 TS_AUTHKEY 认证 加入 tailnet(节点: agentmemory) 构建并启动 agentmemory 连接 DeepSeek API(验证) 连接成功 生成 AGENTMEMORY_SECRET docker compose logs agentmemory | grep SECRET AGENTMEMORY_SECRET=<64位hex> curl http://agentmemory:3111/agentmemory/health {"status":"ok"}

4. 客户端 Tailscale + Claude Code 接入

每台开发机(Mac / Windows / Linux):

# 1) 装并登录 Tailscale,加入同一 tailnet
#    https://tailscale.com/download → tailscale up

# 2) 装 Claude Code 插件(注册 hooks + skills + 自动接 MCP)
#    在 Claude Code 里:
#      /plugin marketplace add rohitg00/agentmemory
#      /plugin install agentmemory

# 3) 设两个环境变量(写进 ~/.zshrc / ~/.bashrc;Windows 用系统环境变量)
export AGENTMEMORY_URL="http://agentmemory:3111"     # MagicDNS;或 http://100.x.x.x:3111
export AGENTMEMORY_SECRET="<第3节日志里的密钥>"

# 4) 重启 Claude Code,验证连通 + 鉴权
curl -H "Authorization: Bearer $AGENTMEMORY_SECRET" http://agentmemory:3111/agentmemory/health

hooks 和 MCP shim 都读这两个变量:AGENTMEMORY_URL 决定连哪、AGENTMEMORY_SECRETBearer 发送。
两台机器配好即共享同一份记忆。日常正常写代码,hooks 自动捕获;需要时用 /recall/remember/handoff 等 skill。


5. 压缩 LLM(DeepSeek)说明与验证

agentmemory 的 provider 检测:只要 OPENAI_API_KEY 有真实值,就走 provider: "openai",
modelOPENAI_MODELbaseURLOPENAI_BASE_URL。这条通道官方注释支持
“OpenAI, DeepSeek, SiliconFlow, Azure, vLLM, LM Studio”。所以接 DeepSeek 就是把这三个变量指过去(已在第 2 节配好)。

  • deepseek-v4-flash 是 cost-optimized 档,适合做后台高频压缩(源码里专门有针对贵模型的成本告警)。
  • base URL 用 https://api.deepseek.com(OpenAI SDK 习惯的 https://api.deepseek.com/v1 也行)。

验证:

# 变量是否真进了容器
docker compose exec agentmemory printenv | grep -E 'OPENAI_|VIEWER_|TS_'
# provider 是 openai 还是 noop
docker compose exec agentmemory agentmemory doctor

若日志里仍出现 “No LLM provider key found … using no-op provider”,说明 key 没被读到——
检查 .envDEEPSEEK_API_KEY 和 compose 的 environment 引用。


6. 升级

三块各自独立,最常做的是 agentmemory 本身。

6.1 升级 agentmemory(改版本号 + 重建)

cd /volume1/docker
# 1) 先备份(数据唯一不可替代)
curl -s -H "Authorization: Bearer $AGENTMEMORY_SECRET" \
  http://agentmemory:3111/agentmemory/export > backup-$(date +%F).json
#    并对 agentmemory-data 卷做 NAS 快照
# 2) 查新版 + 看 release 是否要求改引擎
npm view @agentmemory/agentmemory version
# 3) 改 docker-compose.yml 的 args.AGENTMEMORY_VERSION
# 4) 重建
docker compose build agentmemory && docker compose up -d
docker compose logs --tail=50 agentmemory   # 确认无 EPIPE/报错

6.2 铁律:III_VERSION 别乱升

保持 0.11.2。升到 0.11.6+ 会出现 EPIPE 重连循环、保存后搜不到。
只有当 agentmemory 某新版 release notes 明确支持新引擎时,才按它给的版本同步改。

6.3 回滚

AGENTMEMORY_VERSION 改回旧值重建即可,数据卷不动。若新版动过状态库结构,
回滚旧二进制可能读不了已迁移数据——这时用第 6.1 步那份 JSON 导出 import 重建。

6.4 Tailscale 边车 / 客户端

docker compose pull ts-agentmemory && docker compose up -d   # 边车

客户端插件在 Claude Code 里重装/更新;尽量让服务端与客户端小版本相近。

你的 AGENTMEMORY_SECRET 存在 /data/.hmac,升级/重建不变,客户端无需重配。


7. 把本地已有记忆迁到 NAS

import 接口要求 { "exportData": <export内容>, "strategy": "merge"|"replace"|"skip" }

# (本地)先起本地 server:agentmemory 或 npx @agentmemory/agentmemory
curl -s http://127.0.0.1:3111/agentmemory/export > export.json
#   语料大、超时就分页:?maxSessions=10&offset=0 逐批拉
# 包一层
jq '{exportData: ., strategy: "merge"}' export.json > import.json
# 推到 NAS
curl -X POST http://agentmemory:3111/agentmemory/import \
  -H "Authorization: Bearer $AGENTMEMORY_SECRET" \
  -H "Content-Type: application/json" --data @import.json

merge 合并 / replace 覆盖 / skip 只补新。首次迁移用 merge
(裸拷贝 ~/.agentmemory 的 SQLite 为备选,要求两端版本一致且停服时操作,优先用 export/import。)


8. 备份 / 维护 / 排错

  • 备份:记忆在 agentmemory-data 卷(/data/state_store.db),纳入 NAS 快照;或定期 export 出 JSON。
  • 密钥轮换:删 /data/.hmac 后重启会重新生成并打印。
  • 只看到 7 个 MCP 工具:shim 没连上 server——查 AGENTMEMORY_URL 可达、health 通。
  • 存了搜不到 / EPIPE:多半 iii 引擎版本不符,保持 III_VERSION=0.11.2
  • viewer 打不开:确认 AGENTMEMORY_VIEWER_HOST=0.0.0.0VIEWER_ALLOWED_HOSTS 里的 host:port
    与你浏览器地址栏完全一致(含端口);改了要 docker compose up -d 重建容器生效。
  • provider 显示 noop:DeepSeek key 没传进容器,见第 5 节。

附录 A:Option — 不用 sidecar,改 NAS 原生 Tailscale

若 NAS 容器拿不到 /dev/net/tun:套件中心装 Tailscale(NAS 得到 100.x.x.x),compose 去掉 sidecar,
agentmemory 直接发布端口:ports: ["100.x.x.x:3111:3111", "192.168.1.50:3113:3113"](API 走 tailnet IP、
viewer 走局域网 IP)。客户端 AGENTMEMORY_URL=http://100.x.x.x:3111

附录 B:viewer 的三档访问方式(按安全性从高到低)

共同前提:viewer 是免密特权界面(代理时自动带 AGENTMEMORY_SECRET)。能打开它 = 全权读写记忆,无需令牌。
按你的暴露面选最小的一档。三档可按需共存(白名单逗号分隔),但暴露越广风险越大。

B1 — SSH 隧道(最安全,viewer 不出现在任何网络)

把第 2 节 ts-agentmemoryports: ["3113:3113"] 改成 ["127.0.0.1:3113:3113"](只绑 NAS 本机回环),
并设:

  agentmemory:
    environment:
      - AGENTMEMORY_VIEWER_HOST=0.0.0.0                 # 端口转发到容器内 loopback 收不到,必须 0.0.0.0
      - VIEWER_ALLOWED_HOSTS=localhost:3113,127.0.0.1:3113
      - VIEWER_ALLOWED_ORIGINS=http://localhost:3113,http://127.0.0.1:3113

然后 ssh -L 3113:127.0.0.1:3113 <用户>@<NAS>,浏览器开 http://localhost:3113
只有能 SSH 进 NAS 的人能用。

B2 — 局域网(主文档默认,家庭内网用)

见第 2 节:ports: ["3113:3113"] + VIEWER_ALLOWED_HOSTS=${NAS_LAN_IP}:3113
同一局域网设备开 http://<NAS局域网IP>:3113

B3 — 全域 / tailnet(你的任意 Tailscale 设备、任意地点可访问)

“全域”= 你 tailnet 内任意设备、任意网络位置可达;不是公网。
viewer 已绑 0.0.0.0、与 Tailscale 边车共享网络栈,所以它已在 tailscale 接口监听——
只需把 tailnet 主机加进白名单,无需再发布任何端口:

  agentmemory:
    environment:
      - AGENTMEMORY_VIEWER_HOST=0.0.0.0
      # 可与局域网并存,逗号分隔;host:port 必须与地址栏完全一致
      - VIEWER_ALLOWED_HOSTS=${NAS_LAN_IP}:3113,agentmemory:3113,${NAS_TS_IP}:3113
      - VIEWER_ALLOWED_ORIGINS=http://${NAS_LAN_IP}:3113,http://agentmemory:3113,http://${NAS_TS_IP}:3113

.env 补一行 NAS_TS_IP=100.x.x.x(NAS 的 Tailscale IP)。docker compose up -d 生效。
任意 tailnet 设备开 http://agentmemory:3113(MagicDNS)或 http://100.x.x.x:3113

单人 tailnet 到此即可。若你的 tailnet 有别人,务必在 Tailscale 后台 Access controls 用 ACL
把 viewer 端口锁到只有你自己:给 NAS 节点打 tag(如 tag:agentmemory),并仅放行你这个用户访问该端口——

{
  "tagOwners": { "tag:agentmemory": ["autogroup:owner"] },
  "acls": [
    // 你自己的设备:全放行
    { "action": "accept", "src": ["autogroup:owner"], "dst": ["*:*"] }
    // 不要给其他用户/tag 添加指向 tag:agentmemory:3113 的规则,默认即拒绝
  ]
}

(打 tag 需让 TS_AUTHKEY--advertise-tags=tag:agentmemory,或在后台给该节点手动加 tag。)

不推荐 — 公网直曝

viewer 无登录,真要从公网开必须在前面套一层带认证的反向代理(basic auth / OAuth)。
鉴于其特权性质,强烈建议改用 B3 的 tailnet 全域访问,而不是把它挂到公网。

附录 C:Headscale(连协调层都自托管)

NAS 的 ts-agentmemory 与各客户端 tailscale up 都加 --login-server=https://你的headscale;
auth key 由 headscale preauthkeys create 签发。数据平面行为不变。

更多推荐