From 4b57b56b78b63bc6b713bc629aae0879de7aae6a Mon Sep 17 00:00:00 2001 From: cnphpbb Date: Mon, 8 Jun 2026 00:03:53 +0800 Subject: [PATCH] =?UTF-8?q?feat(hindsight):=20=E6=96=B0=E5=A2=9E=20Hindsig?= =?UTF-8?q?ht=20=E6=96=B9=E6=A1=88=E4=B8=89=E9=83=A8=E7=BD=B2=20stack?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 采用 DB+Hindsight 分离部署方案: - pgvector/pgvector:pg18 向量数据库 - ghcr.nju.edu.cn/vectorize-io/hindsight:latest 应用 - 0.0.0.0:8888/9999 端口绑定 - ~/hindsight/pgdata bind mount (避免 9P fsync 性能) - HF_CACHE_DIR 参数化,适配非 WSL 环境 - 每日 pg_dump 备份,7 天保留 参考 Obsidian 知识库 DevOps/04-AI工具/Hindsight部署指南.md --- hindsight/backup.job | 25 ++++++ hindsight/env.cfg.example | 39 +++++++++ hindsight/readme.md | 168 ++++++++++++++++++++++++++++++++++++++ hindsight/stack.yml | 71 ++++++++++++++++ 4 files changed, 303 insertions(+) create mode 100644 hindsight/backup.job create mode 100644 hindsight/env.cfg.example create mode 100644 hindsight/readme.md create mode 100644 hindsight/stack.yml diff --git a/hindsight/backup.job b/hindsight/backup.job new file mode 100644 index 0000000..40f63d9 --- /dev/null +++ b/hindsight/backup.job @@ -0,0 +1,25 @@ +#!/bin/bash +# ============================================================ +# Hindsight 备份任务(每日 02:30 跑,cron 部署参考 crontab/) +# - 热备份:docker exec pg_dump 落盘到 bind 挂载的 backups/ +# - 保留 7 天的 .sql.gz +# ============================================================ +set -euo pipefail + +BACKUP_DIR="/home/geng/hindsight/backups" +KEEP_DAYS=7 +TS=$(date +%Y%m%d_%H%M%S) +FNAME="hindsight_db_${TS}.sql.gz" + +mkdir -p "${BACKUP_DIR}" + +docker exec hindsight-db pg_dump \ + -U hindsight_user \ + -d hindsight_db \ + --no-owner --no-privileges \ + | gzip > "${BACKUP_DIR}/${FNAME}" + +# 清理超过 KEEP_DAYS 天的旧备份 +find "${BACKUP_DIR}" -name "hindsight_db_*.sql.gz" -mtime +${KEEP_DAYS} -delete + +echo "[backup] ok: ${FNAME} ($(du -h "${BACKUP_DIR}/${FNAME}" | cut -f1))" diff --git a/hindsight/env.cfg.example b/hindsight/env.cfg.example new file mode 100644 index 0000000..98edfe7 --- /dev/null +++ b/hindsight/env.cfg.example @@ -0,0 +1,39 @@ +# ============================================================ +# Hindsight 部署 — 公共环境变量(不含敏感信息) +# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略 +# ============================================================ + +# 镜像版本 +HINDSIGHT_VERSION=latest +HINDSIGHT_DB_VERSION=18 + +# 镜像源 +HINDSIGHT_DB_IMAGE=pgvector/pgvector +HINDSIGHT_APP_IMAGE=ghcr.nju.edu.cn/vectorize-io/hindsight + +# 数据库账号/库名 +HINDSIGHT_DB_USER=hindsight_user +HINDSIGHT_DB_NAME=hindsight_db + +# 宿主机数据卷路径(**必须 WSL/Linux ext4 原生 fs**,不能放 /mnt/9P) +# bind 挂载 PostgreSQL 数据;放在 ~/hindsight/pgdata 而非 /mnt/d/mydata/, +# 因为 9P drvfs 的 fsync 不可靠,会导致 PG 数据损坏。 +Volumes_Path=/home/geng/hindsight + +# 服务端口(宿主机:容器) +HINDSIGHT_API_PORT=8888 +HINDSIGHT_ADMIN_PORT=9999 + +# LLM(MiniMax OpenAI-compatible 协议) +HINDSIGHT_API_LLM_PROVIDER=MiniMax-CN +HINDSIGHT_API_LLM_MODEL=MiniMax-M3 +HINDSIGHT_API_LLM_BASE_URL=https://api.minimaxi.com/v1 + +# 日志 +HINDSIGHT_API_LOG_LEVEL=info + +# ============================================================ +# 敏感值(必填,env.cfg 不提交)— 在 .gitignore 已忽略 +# ============================================================ +# HINDSIGHT_DB_PASSWORD=<强密码> +# HINDSIGHT_API_LLM_API_KEY=<你的 MiniMax key> diff --git a/hindsight/readme.md b/hindsight/readme.md new file mode 100644 index 0000000..8dea044 --- /dev/null +++ b/hindsight/readme.md @@ -0,0 +1,168 @@ +# Hindsight 部署栈 + +[vectorize-io/hindsight](https://github.com/vectorize-io/hindsight) 是一个面向 LLM Agent 的长期记忆后端,采用 PostgreSQL/pgvector 存储。Hermes Agent 用它做跨会话长期记忆。 + +本目录是 **方案三:DB + Hindsight 分离部署**(`/home/geng/hindsight/` 下的 `docker-compose.yaml` + `.env` + `pgdata/`)迁移到 `deploy.stack` 仓库的版本。 + +## 目录结构 + +| 文件 | 说明 | +|------|------| +| `stack.yml` | Docker Compose 主文件(2 服务:db + hindsight) | +| `env.cfg.example` | 公共环境变量模板(不含敏感信息,可提交) | +| `env.cfg` | **敏感配置(gitignore,不提交)** — 实际部署时从 example 复制后填密码/API Key | +| `backup.job` | 每日 `pg_dump` 热备份脚本,保留 7 天 | +| `readme.md` | 本文档 | + +## 架构 + +``` +┌──────────────────────┐ 5432 ┌──────────────────────┐ +│ hindsight-app 容器 │ ────────────► │ hindsight-db 容器 │ +│ (ghcr.nju.edu.cn/ │ │ (pgvector/pgvector │ +│ vectorize-io/ │ :8888 API │ :pg18) │ +│ hindsight) │ :9999 Admin │ │ +└──────────────────────┘ └──────────────────────┘ + │ │ + ▼ bind mount ▼ bind mount + ~/.cache/huggingface /home/geng/hindsight/pgdata + (BGE + ms-marco 复用) (PG 数据,WSL 原生 fs) +``` + +## 部署步骤 + +### 首次部署 + +```bash +# 1. 准备数据目录(WSL/Linux 原生 fs,不能放 /mnt/9P) +sudo mkdir -pv /home/geng/hindsight/pgdata /home/geng/hindsight/backups +sudo chown -R 999:999 /home/geng/hindsight/pgdata + +# 2. 复制 env 模板并填入真实值 +cp env.cfg.example env.cfg +$EDITOR env.cfg +# 必填:HINDSIGHT_DB_PASSWORD, HINDSIGHT_API_LLM_API_KEY + +# 3. 拉镜像 +docker compose --env-file ./hindsight/env.cfg -f ./hindsight/stack.yml pull + +# 4. 启动 +docker compose -p hindsight --env-file ./hindsight/env.cfg -f ./hindsight/stack.yml up -d +``` + +### 验证 + +```bash +# 容器状态 +docker ps -f name=hindsight + +# DB 启动正常(首次启动会建表) +docker exec -it hindsight-db psql -U hindsight_user -d hindsight_db -c '\dt' + +# 端口监听 +ss -tlnp | grep -E '8888|9999' + +# API 健康检查 +curl -s http://localhost:8888/health +``` + +### 停止/重启 + +```bash +# 停止(保留数据) +docker compose -p hindsight --env-file ./hindsight/env.cfg -f ./hindsight/stack.yml stop + +# 完全销毁(**数据不删**,bind 挂载保留在宿主机) +docker compose -p hindsight --env-file ./hindsight/env.cfg -f ./hindsight/stack.yml down + +# 重启 +docker compose -p hindsight --env-file ./hindsight/env.cfg -f ./hindsight/stack.yml restart +``` + +## 关键设计决策 + +| 决策点 | 决定 | 原因 | +|--------|------|------| +| 部署位置 | `/home/geng/hindsight/`(WSL 原生 fs) | `/mnt/d/mydata/` 是 9P drvfs,PG 在 9P 上 fsync 不可靠会损坏数据;Hindsight 跟随 Hermes 配置放 `~/` 下 | +| 数据卷方案 | bind 挂载到 `~/hindsight/pgdata` | 直观、可直接 `rsync`/`pg_dump`、跨机迁移用 `tar` 整个目录即可(用户决策,覆盖默认命名卷) | +| 端口绑定 | 0.0.0.0:8888 / 0.0.0.0:9999 | PVE LAN 上其他 VM 也可能访问(用户决策) | +| 镜像源 | `ghcr.nju.edu.cn/vectorize-io/hindsight` | 南京大学 ghcr 镜像,国内拉得快(用户偏好) | +| LLM | MiniMax-M3 via api.minimaxi.com | 用 MiniMax 的 OpenAI-compatible 协议 | +| 嵌入模型 | 容器内自带 BGE-small-en-v1.5 | 不需要额外配;HF 缓存 bind 复用免重下 | +| 旧数据迁移 | 见 `docs-hermes-Hindsight-记忆系统部署指南.md` 决策表 | 走 A(zip 导入) 或 B(重置新 DB) 路径 | + +## 备份与恢复 + +### 自动备份 + +`backup.job` 是 `pg_dump` 热备份脚本(不停服),落盘到 bind 挂载的 `backups/`。接入 cron: + +```bash +# /etc/cron.d/hindsight-backup 或 crontab -e +30 2 * * * /mnt/d/mydata/cnphpbb/deploy.stack/hindsight/backup.job >> /var/log/hindsight-backup.log 2>&1 +``` + +或参考 `crontab/` 目录的统一任务管理方式(`shell/up.bash` 会处理 `chmod +x`)。 + +### 手动备份 + +```bash +# 热备份(推荐) +docker exec hindsight-db pg_dump -U hindsight_user -d hindsight_db | gzip > ~/hindsight/backups/manual_$(date +%Y%m%d).sql.gz + +# 冷备份(停服时,更彻底) +docker compose -p hindsight -f ./hindsight/stack.yml stop +sudo rsync -a /home/geng/hindsight/pgdata/ /home/geng/hindsight/backups/pgdata-cold/ +docker compose -p hindsight -f ./hindsight/stack.yml start +``` + +### 恢复 + +```bash +# 从 pg_dump 恢复 +gunzip -c ~/hindsight/backups/hindsight_db_20260607_023000.sql.gz \ + | docker exec -i hindsight-db psql -U hindsight_user -d hindsight_db + +# 从冷备份恢复(停服 + 替换 bind 目录) +docker compose -p hindsight -f ./hindsight/stack.yml down +sudo rm -rf /home/geng/hindsight/pgdata/* +sudo rsync -a /home/geng/hindsight/backups/pgdata-cold/ /home/geng/hindsight/pgdata/ +sudo chown -R 999:999 /home/geng/hindsight/pgdata +docker compose -p hindsight --env-file ./hindsight/env.cfg -f ./hindsight/stack.yml up -d +``` + +## 故障排查 + +| 症状 | 排查命令 | +|------|----------| +| 容器起不来 | `docker logs -f hindsight-app` / `docker logs -f hindsight-db` | +| DB 连不上 | `docker exec -it hindsight-db psql -U hindsight_user -d hindsight_db` | +| 慢查询 | 在 psql 里 `SELECT * FROM pg_stat_activity;` | +| 端口冲突 | `ss -tlnp \| grep -E '8888\|9999'` | +| 端口未对外 | `ss -tlnp` 看是不是只监听 `127.0.0.1`,确认 `ports:` 没加 IP 前缀 | +| BGE 模型重下 | 容器内是否有 `HF_HUB_OFFLINE=1` 和 bind 挂的 HF 缓存 | +| glibc 错误 | 0.7.2 内嵌 pg0 需 glibc 2.38,方案三已用独立容器避开 | + +## 与 Hermes 集成 + +Hermes plugin 通过 HTTP 调用本服务的 API(`localhost:8888`),不直连 DB。配置在 `~/.hermes/config.yaml`: + +```yaml +memory: + provider: hindsight + hindsight: + api_url: http://localhost:8888 + bank_id: hermes + memory_mode: hybrid + auto_recall: true + auto_retain: true + retain_async: true +``` + +切换命令:`hermes config set memory.provider hindsight` + +## 相关文档 + +- `~/Obsibian/MyNotes/DevOps/04-AI工具/docs-hermes-Hindsight-记忆系统部署指南.md` — 完整部署指南(700+ 行,含方案对比、旧数据迁移决策表) +- 仓库根 `AGENTS.md` — `deploy.stack` 项目规范 +- `crontab/` — 定时任务集成参考 diff --git a/hindsight/stack.yml b/hindsight/stack.yml new file mode 100644 index 0000000..56cc579 --- /dev/null +++ b/hindsight/stack.yml @@ -0,0 +1,71 @@ +# Hindsight 部署栈 +# ============================================================ +# 部署前准备(仅首次): +# mkdir -pv /home/geng/hindsight/pgdata /home/geng/hindsight/backups +# sudo chown -R 999:999 /home/geng/hindsight/pgdata +# cp env.cfg.example env.cfg && $EDITOR env.cfg # 填入密码/API Key +# +# pull:: docker compose --env-file ./hindsight/env.cfg -f ./hindsight/stack.yml pull +# RUN:: docker compose -p hindsight --env-file ./hindsight/env.cfg -f ./hindsight/stack.yml up -d +# disc:: +# - DB 数据 bind 挂到 /home/geng/hindsight/pgdata(WSL 原生 fs,避 9P fsync 风险) +# - 复用宿主 HF 缓存(bge + ms-marco 不重下) +# - 镜像走南京大学 ghcr 镜像,国内拉得快 +# - 端口 8888=API, 9999=Admin UI,绑定 0.0.0.0 供 LAN VM 访问 +# ============================================================ + +services: + db: + image: ${HINDSIGHT_DB_IMAGE}:pg${HINDSIGHT_DB_VERSION:-18} + container_name: hindsight-db + restart: unless-stopped + environment: + - TZ=Asia/Shanghai + - POSTGRES_USER=${HINDSIGHT_DB_USER:-hindsight_user} + - POSTGRES_PASSWORD=${HINDSIGHT_DB_PASSWORD:?set HINDSIGHT_DB_PASSWORD} + - POSTGRES_DB=${HINDSIGHT_DB_NAME:-hindsight_db} + - POSTGRES_INITDB_ARGS=--encoding=UTF8 --locale=C + volumes: + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + - ${Volumes_Path}/pgdata:/var/lib/postgresql/${HINDSIGHT_DB_VERSION:-18}/docker + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${HINDSIGHT_DB_USER:-hindsight_user} -d ${HINDSIGHT_DB_NAME:-hindsight_db}"] + interval: 10s + timeout: 5s + retries: 10 + networks: + - hindsight-net + + hindsight: + image: ${HINDSIGHT_APP_IMAGE}:${HINDSIGHT_VERSION:-latest} + container_name: hindsight-app + restart: unless-stopped + depends_on: + db: + condition: service_healthy + ports: + - "${HINDSIGHT_API_PORT:-8888}:8888" + - "${HINDSIGHT_ADMIN_PORT:-9999}:9999" + environment: + - TZ=Asia/Shanghai + - HINDSIGHT_API_LLM_PROVIDER=${HINDSIGHT_API_LLM_PROVIDER} + - HINDSIGHT_API_LLM_API_KEY=${HINDSIGHT_API_LLM_API_KEY:?set HINDSIGHT_API_LLM_API_KEY} + - HINDSIGHT_API_LLM_MODEL=${HINDSIGHT_API_LLM_MODEL} + - HINDSIGHT_API_LLM_BASE_URL=${HINDSIGHT_API_LLM_BASE_URL} + - HINDSIGHT_API_DATABASE_URL=postgresql://${HINDSIGHT_DB_USER}:${HINDSIGHT_DB_PASSWORD}@db:5432/${HINDSIGHT_DB_NAME} + - HINDSIGHT_API_LOG_LEVEL=${HINDSIGHT_API_LOG_LEVEL:-info} + - HF_HUB_OFFLINE=1 + - TRANSFORMERS_OFFLINE=1 + volumes: + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + # 复用宿主 HF 缓存,bge + ms-marco 不重下 + - /home/geng/.cache/huggingface:/home/hindsight/.cache/huggingface + - ${Volumes_Path}/backups:/home/hindsight/backups + networks: + - hindsight-net + +networks: + hindsight-net: + driver: bridge