The silent page awaits the waking dawn.
mkdir -p /opt/apps/hindsight
chmod 750 /opt/apps/hindsight
# 应用与反向代理共用的网络,由 docker 单独管理
docker network create openresty_defaultservices:
postgres:
image: pgvector/pgvector:pg16
container_name: hindsight-db
restart: unless-stopped
environment:
POSTGRES_USER: hindsight
POSTGRES_PASSWORD: your_db_password
POSTGRES_DB: hindsight
POSTGRES_INITDB_ARGS: "--data-checksums"
volumes:
- hindsight-pgdata:/var/lib/postgresql/data
networks:
- openresty_default
shm_size: 256m
mem_limit: 768m
healthcheck:
test: ["CMD-SHELL", "pg_isready -U hindsight -d hindsight"]
interval: 10s
timeout: 5s
retries: 10
start_period: 30s
logging:
driver: json-file
options:
max-size: "10m"
max-file: "2"
volumes:
hindsight-pgdata:
name: hindsight-pgdata
networks:
openresty_default:
external: true# ── 服务本身 ──
HINDSIGHT_API_HOST=0.0.0.0
HINDSIGHT_API_PORT=9998
HINDSIGHT_CP_PORT=9999
TZ=Asia/Shanghai
# ── 数据库,主机名使用容器名 ──
HINDSIGHT_API_DATABASE_URL=postgresql://hindsight:your_db_password@hindsight-db:5432/hindsight
# ── 主 LLM ──
HINDSIGHT_API_LLM_PROVIDER=openai
HINDSIGHT_API_LLM_BASE_URL=https://api.your-gateway.com/compatible-mode/v1
HINDSIGHT_API_LLM_MODEL=qwen3.7-flash
HINDSIGHT_API_LLM_API_KEY=sk-xxxxxxxx
HINDSIGHT_API_LLM_TIMEOUT=300
HINDSIGHT_API_LLM_MAX_CONCURRENT=6
# ── 事实抽取 ──
HINDSIGHT_API_RETAIN_LLM_PROVIDER=openai
HINDSIGHT_API_RETAIN_LLM_BASE_URL=https://api.your-gateway.com/compatible-mode/v1
HINDSIGHT_API_RETAIN_LLM_MODEL=qwen3.7-flash
HINDSIGHT_API_RETAIN_LLM_API_KEY=sk-xxxxxxxx
HINDSIGHT_API_RETAIN_LLM_MAX_CONCURRENT=6
# ── 整合 ──
HINDSIGHT_API_CONSOLIDATION_LLM_PROVIDER=openai
HINDSIGHT_API_CONSOLIDATION_LLM_BASE_URL=https://api.your-gateway.com/compatible-mode/v1
HINDSIGHT_API_CONSOLIDATION_LLM_MODEL=qwen3.7-flash
HINDSIGHT_API_CONSOLIDATION_LLM_API_KEY=sk-xxxxxxxx
# ── 知识页刷新 ──
HINDSIGHT_API_MENTAL_MODEL_REFRESH_LLM_PROVIDER=openai
HINDSIGHT_API_MENTAL_MODEL_REFRESH_LLM_BASE_URL=https://api.your-gateway.com/compatible-mode/v1
HINDSIGHT_API_MENTAL_MODEL_REFRESH_LLM_MODEL=qwen3.7-flash
HINDSIGHT_API_MENTAL_MODEL_REFRESH_LLM_API_KEY=sk-xxxxxxxx
HINDSIGHT_API_MENTAL_MODEL_REFRESH_LLM_MAX_CONCURRENT=3
# ── 向量化(OpenAI 兼容) ──
HINDSIGHT_API_EMBEDDINGS_PROVIDER=openai
HINDSIGHT_API_EMBEDDINGS_OPENAI_BASE_URL=https://api.your-gateway.com/compatible-mode/v1
HINDSIGHT_API_EMBEDDINGS_OPENAI_MODEL=qwen3.7-text-embedding-flash
HINDSIGHT_API_EMBEDDINGS_OPENAI_API_KEY=sk-xxxxxxxx
HINDSIGHT_API_EMBEDDINGS_OPENAI_BATCH_SIZE=10
# ── 精排(Cohere 兼容) ──
HINDSIGHT_API_RERANKER_PROVIDER=cohere
HINDSIGHT_API_RERANKER_COHERE_BASE_URL=https://api.your-gateway.com/compatible-api/v1/reranks
HINDSIGHT_API_RERANKER_COHERE_MODEL=qwen3.7-text-rerank
HINDSIGHT_API_RERANKER_COHERE_API_KEY=sk-xxxxxxxx
HINDSIGHT_API_RERANKER_COHERE_TIMEOUT=60.0
# ── 反思任务超时 ──
HINDSIGHT_API_REFLECT_WALL_TIMEOUT=900hindsight:
image: ghcr.io/vectorize-io/hindsight:latest-slim
container_name: hindsight
restart: unless-stopped
env_file:
- /opt/apps/hindsight/.env
ports:
- "127.0.0.1:9998:9998"
- "127.0.0.1:9999:9999"
networks:
- openresty_default
shm_size: 256m
mem_limit: 2048m
healthcheck:
test: ["CMD", "python3", "-c", "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:9998/health/live', timeout=5).status==200 else 1)"]
interval: 30s
timeout: 10s
retries: 5
start_period: 180s
logging:
driver: json-file
options:
max-size: "20m"
max-file: "3"mkdir -p /opt/apps/openresty/nginx/conf.d
mkdir -p /opt/apps/openresty/nginx/html
mkdir -p /opt/apps/openresty/certs
mkdir -p /opt/apps/openresty/letsencryptservices:
openresty:
image: openresty/openresty:alpine
container_name: openresty
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- /opt/apps/openresty/nginx/conf.d:/etc/nginx/conf.d:ro
- /opt/apps/openresty/nginx/html:/usr/local/openresty/nginx/html
- /opt/apps/openresty/certs:/etc/nginx/certs:ro
networks:
- openresty_defaultdocker run --rm \
-v /opt/apps/openresty/nginx/html:/var/www/html \
-v /opt/apps/openresty/letsencrypt:/etc/letsencrypt \
certbot/certbot certonly --webroot \
-w /var/www/html \
-d hindsight.your-domain.com \
--email you@example.com --agree-tos --no-eff-email# ── 80:跳转 HTTPS 并放行 ACME 验证 ──
server {
listen 80;
server_name hindsight.your-domain.com;
location ^~ /.well-known/acme-challenge/ {
root /usr/local/openresty/nginx/html;
}
location / {
return 301 https://$host$request_uri;
}
}
# ── 443:Hindsight API ──
server {
listen 443 ssl;
http2 on;
server_name hindsight.your-domain.com;
ssl_certificate /etc/nginx/certs/hindsight.fullchain.pem;
ssl_certificate_key /etc/nginx/certs/hindsight.privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=15552000" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "no-referrer" always;
add_header X-Robots-Tag "noindex, nofollow" always;
client_max_body_size 32m;
client_body_timeout 300s;
# 交互式 API 文档不对公网开放
location ~ ^/(docs|redoc|openapi\.json) {
return 404;
}
location /v1/ {
proxy_pass http://hindsight:9998;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Connection "";
proxy_connect_timeout 10s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
proxy_buffering off;
proxy_request_buffering off;
}
location /health/ {
proxy_pass http://hindsight:9998;
proxy_set_header Host $host;
}
}cp -L /opt/apps/openresty/letsencrypt/live/hindsight.your-domain.com/fullchain.pem \
/opt/apps/openresty/certs/hindsight.fullchain.pem
cp -L /opt/apps/openresty/letsencrypt/live/hindsight.your-domain.com/privkey.pem \
/opt/apps/openresty/certs/hindsight.privkey.pem
docker exec openresty nginx -t && docker exec openresty nginx -s reload#!/bin/bash
docker run --rm \
-v /opt/apps/openresty/nginx/html:/var/www/html \
-v /opt/apps/openresty/letsencrypt:/etc/letsencrypt \
certbot/certbot:latest renew --quiet
cp -L /opt/apps/openresty/letsencrypt/live/hindsight.your-domain.com/fullchain.pem \
/opt/apps/openresty/certs/hindsight.fullchain.pem
cp -L /opt/apps/openresty/letsencrypt/live/hindsight.your-domain.com/privkey.pem \
/opt/apps/openresty/certs/hindsight.privkey.pem
docker exec openresty nginx -s reload# 1. 备份现有配置
cp .env .env.bak-$(date +%Y%m%d-%H%M%S)
# 2. 修改配置后强制重建
docker compose up -d --force-recreate hindsight
# 3. 立即确认健康状态
docker compose ps
整套服务由三个容器组成:应用本体、PostgreSQL(含 pgvector 扩展)、OpenResty 反向代理。对外只暴露 80/443,其余端口全部锁在回环地址上。
一·准备
要求
系统:Linux(Ubuntu / Debian)
推荐配置:CPU 2 核及以上,内存 4GB 及以上
其他:一个可解析的域名、可用的模型 API 网关(OpenAI 兼容协议即可)
准备
验证解析是否生效:
安装 Docker
本文使用发行版仓库安装,升级路径更干净,apt upgrade 会一并更新守护进程与 compose。
安装完成后验证:
目录与网络
所有自建服务统一放在 /opt/apps 下,一个服务一个目录。该目录属主为 root,日常操作需要 sudo,这同时保证了 .env 的权限可以稳定停在 600。
注意: 该网络不写进任何一个 compose 文件,两个服务都通过 external: true 引用它,这样任一方重启不会连带重启另一方。
二·部署数据库
Hindsight 需要存储向量并做语义检索,因此必须使用带 pgvector 的镜像,官方 postgres 镜像缺少 vector 扩展,启动时会直接报错。
创建 compose 文件
参数说明
三·配置应用
创建 .env 文件
配置通过 env_file 挂载进容器,建议权限设为 600:
配置说明
服务内部包含四条独立的 LLM 调用链,每条都能单独指定模型与网关,全部走 OpenAI 兼容协议:
用途
变量前缀
作用
主 LLM
`HINDSIGHT_API_LLM_*`
通用生成
事实抽取
`HINDSIGHT_API_RETAIN_LLM_*`
将文本切分为一条条事实
整合
`HINDSIGHT_API_CONSOLIDATION_LLM_*`
将事实归纳为观测
知识页刷新
`HINDSIGHT_API_MENTAL_MODEL_REFRESH_LLM_*`
定期将记忆合成为综述页
HINDSIGHT_API_RERANKER_PROVIDER=cohere 只是复用了 Cohere 的请求格式,BASE_URL 指向任何兼容该格式的网关均可,与该厂商本身无关。
向量维度由 embedding 模型决定并写入数据库,更换 embedding 模型等于更换维度,已有向量全部失效,需要重建索引并重新嵌入历史数据。
HINDSIGHT_API_REFLECT_WALL_TIMEOUT 单独设置为 900 秒,因为反思任务需要串联多次模型调用,使用默认值容易被中断。
创建应用 compose
将上述片段合并进 docker-compose.yml 的 services 下,与 postgres 同级。
参数说明
四·反向代理与证书
部署 OpenResty
申请证书
使用 certbot 容器配合 HTTP-01 的 webroot 模式,证书首次签发:
注意: 签发前需保证 80 端口可从公网访问,且下方站点配置中已放行 /.well-known/acme-challenge/ 路径,否则验证失败。
站点配置
在 /opt/apps/openresty/nginx/conf.d/ 下新建 hindsight.conf:
反向代理配置
将首次签发的证书复制到家目录并重载:
不要让 nginx 直接指向 letsencrypt/live 目录,该目录下的证书是符号链接,续期后容器内可能读取到旧文件,因此统一复制到 certs 目录再加载。
配置说明
证书续期
新增 /opt/apps/_scripts/renew-cert.sh:
加入定时任务,每天执行一次:
certbot renew 仅在证书距到期 30 天内才会真正续期,每日执行没有副作用。
五·启动与验证
启动服务
等待 hindsight 容器的健康状态变为 healthy,首次启动需要执行数据库迁移,通常一到三分钟。
验证顺序
验证应遵循由内到外的顺序,便于区分问题出在应用还是代理。
验证清单
检查项
期望结果
不通过时检查
`docker compose ps`
三个容器均为 `Up (healthy)`
`docker compose logs --tail 50 hindsight`
`127.0.0.1:9998/health/live`
返回 200
容器状态、端口绑定
域名 `/health/live`
返回 200
反向代理配置、网络、证书路径
`/v1/version` 带鉴权
返回 200 与版本号
`.env` 中的 API Key、反向代理路径
六·日常运维
常用命令
更新流程
注意: 容器重建期间 API 不可用,反向代理会返回 502,通常持续十秒左右。若客户端存在定时写入,应配置重试逻辑,不要将 502 视为业务错误。
七·几点注意
八·参考文献