# memory-tencentdb-ctl.sh — memory_tencentdb运维脚本 > 配合 [`install_hermes_memory_tencentdb.sh`](./install_hermes_memory_tencentdb.sh) 使用。 > 先跑安装脚本部署好插件 + Node 依赖,之后日常的启停 / 配置全部通过 `memory-tencentdb-ctl.sh` 完成。 ## 1. 运行模式 脚本有两种模式,**默认是独立模式**,完全不触碰 `~/.hermes`: | 模式 | 激活方式 | 做什么 | 不做什么 | |---|---|---|---| | `standalone`(默认) | 无需任何参数 | 启停 Gateway;写 `$TDAI_DATA_DIR/tdai-gateway.json`;日志落 `$TDAI_DATA_DIR/logs/` | 不写 `$HERMES_HOME/env.d/`,不改 `$HERMES_HOME/config.yaml`,不读 hermes 相关 env | | `hermes` | 命令行追加 `--hermes`,或环境 `MEMORY_TENCENTDB_MODE=hermes` | standalone 的全部 + `config llm` 同步写 `$HERMES_HOME/env.d/memory-tencentdb-llm.sh`;日志落 `$HERMES_HOME/logs/memory_tencentdb/`;开放 `enable-hermes-memory` 子命令 | — | > **为什么 hermes 模式要多写 env 文件?** > 因为 hermes 进程会托管式地把 Gateway 以子进程拉起来(supervisor 用 `os.environ.copy()` 传环境),此时 Gateway 读不到 `tdai-gateway.json` 所在的 shell 环境,必须通过 `$HERMES_HOME/env.d/*.sh` 让 hermes 自身 `source` 才能把凭据传进去。独立模式下 Gateway 自己读 JSON,无此需要。 ## 2. 路径 > **路径变量约定**:本节及后续示例统一用 `$HERMES_HOME` 指代 hermes 的家目录,**默认 `~/.hermes`**,但你可以通过环境变量覆盖(例如 `export HERMES_HOME=/srv/hermes`),脚本和 hermes 自身都遵守这个变量。 > > 自 0.4.x 起,所有 tdai 相关数据/代码默认收纳到统一根目录 `$MEMORY_TENCENTDB_ROOT`(默认 `~/.memory-tencentdb`)之下: > > - `$TDAI_INSTALL_DIR` 默认 `$MEMORY_TENCENTDB_ROOT/tdai-memory-openclaw-plugin`(即 `~/.memory-tencentdb/tdai-memory-openclaw-plugin`) > - `$TDAI_DATA_DIR` 默认 `$MEMORY_TENCENTDB_ROOT/memory-tdai`(即 `~/.memory-tencentdb/memory-tdai`) > > 下文出现这些变量时**先 `export` 一份覆盖**,再跑命令即可全局生效。 > 旧版本使用 `~/tdai-memory-openclaw-plugin` 与 `~/memory-tdai`;`install_hermes_memory_tencentdb.sh` 在升级时会自动迁移这两个旧目录到新位置。 | 路径 | standalone | hermes | 作用 | |---|---|---|---| | `$TDAI_INSTALL_DIR` | ✅ | ✅ | 插件源码 + `node_modules` + `src/gateway/server.ts` | | `$TDAI_DATA_DIR/tdai-gateway.json` | ✅ | ✅ | Gateway 主配置:`llm` / `memory.embedding` / `memory.tcvdb` / `memory.storeBackend`,权限 `0600` | | `$TDAI_DATA_DIR/logs/` | ✅ 日志 | — | `gateway.stdout.log` / `gateway.stderr.log` / `gateway.pid` | | `$HERMES_HOME/logs/memory_tencentdb/` | — | ✅ 日志 | 同上,换目录 | | `$HERMES_HOME/env.d/memory-tencentdb-llm.sh` | — | ✅ | hermes 启动前 `source`,给 supervisor 托管的 Gateway 子进程注入 LLM 凭据 | | `$HERMES_HOME/config.yaml` | — | ✅ | `enable-hermes-memory` 修改其 `memory.provider` | | Gateway 监听 | `127.0.0.1:8420` | `127.0.0.1:8420` | 可被 `MEMORY_TENCENTDB_GATEWAY_HOST/PORT` 覆盖 | 所有路径都能用同名环境变量覆盖(再次列出便于对照):`MEMORY_TENCENTDB_ROOT`(默认 `~/.memory-tencentdb`)、`TDAI_INSTALL_DIR`(默认 `$MEMORY_TENCENTDB_ROOT/tdai-memory-openclaw-plugin`)、`TDAI_DATA_DIR`(默认 `$MEMORY_TENCENTDB_ROOT/memory-tdai`)、`HERMES_HOME`(默认 `~/.hermes`)、`MEMORY_TENCENTDB_LOG_DIR`、`MEMORY_TENCENTDB_GATEWAY_HOST/PORT`。 依赖:`bash`、`python3`、`node >= 22`、`npx`、`lsof` 或 `ss`。 ## 3. 安装 & 调用 脚本随 npm 包发布到 `node_modules/.../scripts/` 下,但**没有**注册为 `bin` 命令。要用全局命令名调用,必须自己做一次软链。 ### 3.1 从 npm 包里直接运行(无需任何配置) ```bash npm install @tencentdb-agent-memory/memory-tencentdb # 项目内安装,路径可由 npm root 动态算出 "$(npm root)/@tencentdb-agent-memory/memory-tencentdb/scripts/memory-tencentdb-ctl.sh" --help # 全局安装则用 npm root -g "$(npm root -g)/@tencentdb-agent-memory/memory-tencentdb/scripts/memory-tencentdb-ctl.sh" --help ``` `npm root` / `npm root -g` 会在所有包管理器(npm / pnpm / yarn)和不同的 prefix 配置下返回正确目录,避免硬编码 `node_modules/` 路径。适合一次性、临时使用的场景。 ### 3.2 软链到 PATH(推荐给运维 / 长期使用) 不论脚本来源(git clone 出来的仓库 / `npm install` 装的包 / 自定义部署目录),先把脚本路径**算出来存到一个变量里**,再统一做软链。这样无需关心你把仓库放在 `~/code/`、`/opt/`、还是别的什么地方。 ```bash # 第一步:定位 memory-tencentdb-ctl.sh 的真实路径(任选一种来源) # (a) 从 git 仓库(在仓库根目录或任意子目录里执行) SCRIPT="$(git -C "$(git rev-parse --show-toplevel)" ls-files | \ grep -E 'scripts/memory-tencentdb-ctl\.sh$' | head -1)" SCRIPT="$(git rev-parse --show-toplevel)/$SCRIPT" # (b) 从已 npm 全局安装的包 SCRIPT="$(npm root -g)/@tencentdb-agent-memory/memory-tencentdb/scripts/memory-tencentdb-ctl.sh" # (c) 从项目本地的 node_modules SCRIPT="$(npm root)/@tencentdb-agent-memory/memory-tencentdb/scripts/memory-tencentdb-ctl.sh" # (d) 完全手写绝对路径(如部署到非标准位置) SCRIPT="/opt/tdai/scripts/memory-tencentdb-ctl.sh" # 第二步:验证路径正确,然后软链 test -f "$SCRIPT" && echo "ok: $SCRIPT" || { echo "not found"; exit 1; } chmod +x "$SCRIPT" sudo ln -sf "$SCRIPT" /usr/local/bin/memory-tencentdb-ctl # 同样的办法把 install_hermes_memory_tencentdb.sh 链接成 install-memory-tencentdb(可选) INSTALL_SCRIPT="$(dirname "$SCRIPT")/install_hermes_memory_tencentdb.sh" test -f "$INSTALL_SCRIPT" && { chmod +x "$INSTALL_SCRIPT" sudo ln -sf "$INSTALL_SCRIPT" /usr/local/bin/install-memory-tencentdb } ``` 之后直接 `memory-tencentdb-ctl …` / `install-memory-tencentdb …`。 > **为什么不直接用 `npm bin` 注册?** 这两个脚本是运维工具而不是包的核心 API,主仓库希望用户**显式**完成 PATH 注册(避免无意中污染全局命令空间,并避免 npm 卸载时静默移除运维入口)。 ## 4. 生命周期管理(两种模式通用) ```bash memory-tencentdb-ctl start # 若 :8420 已占用会直接返回;否则后台 spawn,等待 /health 通过 memory-tencentdb-ctl stop # 先 SIGTERM,5s 内未退则 SIGKILL memory-tencentdb-ctl restart memory-tencentdb-ctl status # 打印模式、端口、data/log 路径、进程状态 memory-tencentdb-ctl health # GET /health,纯 python3 实现,不要求 curl memory-tencentdb-ctl logs # tail -f stdout + stderr memory-tencentdb-ctl logs err 500 # 只看 stderr 最近 500 行 ``` 启动命令解析顺序: 1. 环境变量 `MEMORY_TENCENTDB_GATEWAY_CMD`(`install_hermes_memory_tencentdb.sh` 写入 `/etc/profile.d/memory-tencentdb-env.sh` 的那条)。 2. 回退到 `sh -c 'cd $TDAI_INSTALL_DIR && exec npx tsx src/gateway/server.ts'`。 启动时会自动 `source` 的环境文件: - 两种模式:`/etc/profile.d/memory-tencentdb-env.sh` - 仅 hermes 模式:`/etc/profile.d/hermes-env.sh` 以及 `$HERMES_HOME/env.d/*.sh` ## 5. 配置 LLM / Embedding / VDB 三类凭据统一落到 `$TDAI_DATA_DIR/tdai-gateway.json`(`0600`,原子写)。**`config llm` 在 `--hermes` 模式下会额外写一份 env 文件**;Embedding / VDB 从不写 env。 ### 5.1 LLM ```bash # standalone 模式:只写 tdai-gateway.json memory-tencentdb-ctl config llm \ --api-key "sk-xxxxxxxxxxxx" \ --base-url "https://api.openai.com/v1" \ --model "gpt-4o" \ --restart # hermes 模式:tdai-gateway.json + $HERMES_HOME/env.d/memory-tencentdb-llm.sh memory-tencentdb-ctl --hermes config llm \ --api-key "sk-xxxxxxxxxxxx" \ --base-url "https://api.openai.com/v1" \ --model "gpt-4o" \ --restart ``` - JSON 写入点:`$.llm.{baseUrl, apiKey, model}`。 - env 文件写入(仅 `--hermes`):`TDAI_LLM_*` 及 `MEMORY_TENCENTDB_LLM_*` 别名(Python provider 的 `get_config_schema()` 会读后者)。 ### 5.2 Embedding 默认关闭(`provider=none`)。启用远端 OpenAI 兼容服务: ```bash memory-tencentdb-ctl config embedding \ --provider openai \ --api-key "sk-xxxx" \ --base-url "https://api.openai.com/v1" \ --model "text-embedding-3-small" \ --dimensions 1536 \ --restart # 关闭 embedding(退化为 BM25/关键词召回) memory-tencentdb-ctl config embedding --provider none --restart ``` - JSON 写入点:`$.memory.embedding.{provider, baseUrl, apiKey, model, dimensions, enabled, proxyUrl?}`。 - `qclaw` provider 额外要求 `--proxy-url`。 - 校验规则与 `src/config.ts` 的 `parseConfig()` 对齐:`dimensions` 为正整数,非 `none` 必须带 `apiKey/baseUrl/model/dimensions`;缺项直接报错不写半残 JSON。 ### 5.3 VectorDB(Tencent Cloud VDB / tcvdb) ```bash memory-tencentdb-ctl config vdb \ --url "http://xxx-vdb.tencentclb.com:8100" \ --username root \ --api-key "YOUR-VDB-API-KEY" \ --database "openclaw_memory" \ --alias "primary" \ --embedding-model "bge-large-zh" \ --ca-pem "/etc/ssl/vdb-ca.pem" \ --restart ``` - JSON 写入点:`$.memory.tcvdb.{url, username, apiKey, database, alias?, caPemPath?, embeddingModel?}`。 - 默认同时把 `$.memory.storeBackend` 切到 `"tcvdb"`;只想预埋配置先不切,加 `--no-set-backend`。 - `--ca-pem` 只写路径不复制文件;脚本会校验可读性。 ### 5.4 退回本地 SQLite(关闭 VDB 后端) ```bash # 默认:保留 memory.tcvdb 凭据(方便随时再切回去),仅把 storeBackend 改回 sqlite memory-tencentdb-ctl config vdb-off --restart # 同时把腾讯云 VDB 的 url / apiKey / database 等凭据从 JSON 中清掉 memory-tencentdb-ctl config vdb-off --purge-creds --restart ``` - JSON 写入点:把 `$.memory.storeBackend` 设为 `"sqlite"`;`--purge-creds` 时额外删除整段 `$.memory.tcvdb`。 - `$.llm` / `$.memory.embedding` 等其它顶级段**完全保留**,hermes 侧 `memory.provider` **不动**(仍是 `memory_tencentdb`,只是它内部存储退回 sqlite)。 - 配置文件不存在时给出 `warn` 并写入仅含 `{"memory":{"storeBackend":"sqlite"}}` 的最小配置。 - 与 `config vdb` 完全镜像:可与 `--dry-run` / `--restart` 组合使用。 ### 5.5 查看当前配置 ```bash memory-tencentdb-ctl config show ``` - 打印 `tdai-gateway.json`,`apiKey`/`password`/`token` 字段自动脱敏为 ``。 - hermes 模式下额外打印 `$HERMES_HOME/env.d/memory-tencentdb-*.sh`(API key 也会脱敏),可直接贴工单。 ## 6. 打通 hermes(仅 `--hermes` 模式) ```bash memory-tencentdb-ctl --hermes enable-hermes-memory ``` 幂等:把 `$HERMES_HOME/config.yaml` 的 `memory:` 段的 `provider:` 改成 `memory_tencentdb`(不存在则新增整段)。改完后重启 hermes: ```bash source "$HERMES_HOME/env.d/memory-tencentdb-llm.sh" pkill -f hermes-agent || true hermes ``` > **关于写入策略**:脚本采用"格式保真"双路径,**永不重写整个 YAML**: > > 1. **首选**:检测到 [`ruamel.yaml`](https://yaml.readthedocs.io/) 时走 round-trip,完整保留注释、键序、引号、缩进风格(推荐 `pip install --user ruamel.yaml` 享受最佳保真度,**非必装**); > 2. **降级**:未安装 ruamel 时走最小化原位行编辑——只重写 `provider:` 那一行,缩进直接从同段已有兄弟键的前缀**逐字符拷贝**(零猜测、零格式破坏); > 3. 若 `memory:` 段不存在,则在文件末尾追加最小段,缩进从文档其它顶级段的子键拓印。 > > 实测对真实 `~/.hermes/config.yaml` 做 byte-for-byte diff,除 `provider` 值本身外其余字节完全一致。 非 hermes 模式下调用该命令会直接报错退出。 > 想反过来"保留 hermes provider 不变,仅让 TDAI 内部存储退回 sqlite",用 §5.4 的 `config vdb-off` 即可(不需要也**不要**改 hermes 的 `memory.provider`)。 ## 7. 典型使用流程 ### 场景 A:Gateway 独立部署(不使用 hermes) ```bash # 1) 安装 # INSTALL_SCRIPT 的取值方式见上文 3.2 节(git rev-parse / npm root / 手填均可) # 例如从 git 仓库根:INSTALL_SCRIPT="$(git rev-parse --show-toplevel)/scripts/install_hermes_memory_tencentdb.sh" # 从 npm 全局: INSTALL_SCRIPT="$(npm root -g)/@tencentdb-agent-memory/memory-tencentdb/scripts/install_hermes_memory_tencentdb.sh" bash "$INSTALL_SCRIPT" # 2) 只配 Gateway 所需凭据 memory-tencentdb-ctl config llm --api-key "sk-..." --base-url "https://api.openai.com/v1" --model gpt-4o memory-tencentdb-ctl config embedding --provider openai --api-key "sk-..." --base-url "https://api.openai.com/v1" \ --model text-embedding-3-small --dimensions 1536 memory-tencentdb-ctl config vdb --url "http://xxx:8100" --api-key "..." --database openclaw_memory # 3) 启动 + 自检 memory-tencentdb-ctl start memory-tencentdb-ctl status memory-tencentdb-ctl health # 预期: {"status":"ok",...} ``` ### 场景 B:集成 hermes ```bash # 1) 安装(与场景 A 相同;INSTALL_SCRIPT 由上文 3.2 节算出) bash "$INSTALL_SCRIPT" # 2) 全程加 --hermes(或 export MEMORY_TENCENTDB_MODE=hermes 一次) memory-tencentdb-ctl --hermes config llm --api-key "sk-..." --base-url "https://api.openai.com/v1" --model gpt-4o memory-tencentdb-ctl --hermes config embedding --provider openai --api-key "sk-..." \ --base-url "https://api.openai.com/v1" \ --model text-embedding-3-small --dimensions 1536 memory-tencentdb-ctl --hermes config vdb --url "http://xxx:8100" --api-key "..." --database openclaw_memory # 3) 启动 Gateway(通常由 hermes supervisor 托管,这里是手动兜底) memory-tencentdb-ctl --hermes start memory-tencentdb-ctl --hermes status # 4) 在 hermes config 里启用 provider,并重启 hermes memory-tencentdb-ctl --hermes enable-hermes-memory source "$HERMES_HOME/env.d/memory-tencentdb-llm.sh" pkill -f hermes-agent ; hermes ``` 如果嫌 `--hermes` 每次都要带,可以: ```bash export MEMORY_TENCENTDB_MODE=hermes ``` 之后所有调用自动切到 hermes 模式,命令行无需再加 `--hermes`。 ### 场景 C:临时把 TDAI 的存储退回 sqlite(保留 hermes 集成) 适用于 VDB 不可达 / 排障 / 离线开发等场景:希望 hermes 端 `memory.provider` 仍是 `memory_tencentdb`,但让 Gateway 改用本地 SQLite 落盘。 ```bash # (A) 默认:保留 memory.tcvdb 凭据,仅把 storeBackend 切回 sqlite memory-tencentdb-ctl config vdb-off --restart # (B) 排障结束想切回 vdb:当前需要重新跑一次 config vdb(必填项需重新提供, # 即使 JSON 里凭据还在;脚本是基于必填校验的"重新声明"语义,不是 toggle) memory-tencentdb-ctl config vdb \ --url "http://xxx-vdb.tencentclb.com:8100" \ --api-key "<你的 KEY>" \ --database "openclaw_memory" \ --restart # (C) 彻底放弃 vdb:清掉凭据 memory-tencentdb-ctl config vdb-off --purge-creds --restart ``` > 之所以 (B) 不提供"零参数 vdb-on",是因为原 `config vdb` 子命令把 `--url/--api-key/--database` 设为强校验,避免用户拼装出半残配置;如果你希望把"已存的凭据重新激活"也做成单命令,告诉维护者补一个 `config vdb-on` 即可(实现方式与 `vdb-off` 完全镜像)。 > **不要**为了"切回 sqlite"去改 `~/.hermes/config.yaml` 的 `memory.provider`!hermes 看到的依然是 `memory_tencentdb` provider,存储后端切换是 Gateway 内部的事,对 hermes 完全透明。 ## 8. 全局选项 & 调试技巧 - 所有写操作支持 `--dry-run`(放在命令最前),会打印将要写入的内容但不落盘: ```bash memory-tencentdb-ctl --dry-run config llm --api-key k --base-url https://x --model m ``` - 敏感文件权限一律 `0600`;`env.d/memory-tencentdb-llm.sh` 含明文 API key,**不要** commit。 - 启动失败:`memory-tencentdb-ctl logs err 200` 查看 stderr;手动前台跑一遍更容易看到报错: ```bash cd "$TDAI_INSTALL_DIR" && npx tsx src/gateway/server.ts ``` - 端口冲突:`MEMORY_TENCENTDB_GATEWAY_PORT=18420 memory-tencentdb-ctl restart`。 - 验证 hermes 有没有吃到新 env(hermes 模式): ```bash tr '\0' '\n' < /proc/$(pgrep -n hermes-agent)/environ | grep -E 'TDAI_|MEMORY_TENCENTDB_' ``` ## 9. 退出码 | 码 | 含义 | |---|---| | 0 | 成功 | | 1 | 参数错误 / 业务校验失败(如 `--base-url` 非 http(s);在 standalone 下调 hermes 专属命令) | | 2 | 写盘失败(磁盘满、权限不足等) | | 127 | 依赖缺失(`python3` / `node` / `npx`) | --- 要封装成 systemd unit,基于 `memory-tencentdb-ctl start` / `memory-tencentdb-ctl stop` 写 `Type=forking` 的 service 即可(Gateway 是无状态 HTTP sidecar,不依赖 systemd readiness 协议)。