Files
PlantAI-rag-new/docs/DEPLOY_SKILL.md
T
adminandClaude Fable 5 82b954b957 docs(deploy): BFF 手动重启 venv 绝对路径陷阱(2026-09-15 实测)
根因:venv 在部署根 install/venv,BFF 必须 cd sdk 起 app.main:app——
cd sdk 后相对路径 venv/bin/python 解析成不存在的
install/sdk/venv/bin/python(setsid failed to execute,端口静默 DOWN)。
修正 5f 示例命令(cd 前解析绝对路径)并补老进程 socket 失效场景。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-15 10:54:10 +08:00

13 KiB

PlantAI-rag-new 打包部署 Skill(175 构建机 → 176 生产)

本文件是 Claude Code skill 形态的部署方法(与 DEPLOY_FLOW.md 流程互补,侧重 agent 自动执行)。 生成于 2026-09-12;2026-09-14 增补部署变体与提示词示例、环境镜像全量同步原则(§7.4); 2026-09-15 增补 BFF 手动重启 venv 绝对路径陷阱(§4)。skill 源文件:本地 ~/.claude/skills/plantai-deploy/SKILL.md。

0. 前置事实(agent 必读,勿想当然)

  • 175 = 构建机/开发仓库(8.163.92.8,公网 ssh -p 26518 xzq@8.163.92.8):仓库 /home/xzq/5090/teamWorkspace/zh/PlantAI-rag-new,分支 feature/bioflow-conversation-orchestration,Gitea 在 http://127.0.0.1:3000。
  • 176 = 生产(8.138.231.183,公网 ssh -p 26518 xzq@8.138.231.183):部署根 /srv/plantai/(install/ + qa-db/ + data/ + docgen/ + bioflow/ + mineru-*)。
  • 端口终态:nginx 80(唯一客户入口)→ BFF 8090;QA 8091/methodology 8092/legacy 8093/vstore 8094/bioflow 8098 全回环;裸进程库 Neo4j 19888/Qdrant 27433;Ollama 11434。
  • 两机传输用 P2P 内网直连(175 上密钥 ~/.ssh/id_ed25519_p2p,勿走跳板机): rsync -a --numeric-ids -e 'ssh -i ~/.ssh/id_ed25519_p2p -p 26518 -o BatchMode=yes' <src>/ xzq@192.168.0.176:<dst>/
  • BFF 静态托管 ui/dist:前端改动部署 = 只同步 dist 资产 + 清旧哈希文件,不需整包重编。

1. 构建(175,全量约 30-40 分钟)

cd /home/xzq/5090/teamWorkspace/zh/PlantAI-rag-new
git status --short            # 先确认无未提交改动
bash tooling/build_all.sh     # 串行:五树 Cython 编译→双审计→冒烟 import→组装→SHA256SUMS→泄露门禁
  • 版本与 env 在 tooling/tooling.env(VERSION=2.0.0,SKIP_KB=1 数据库未定版)。
  • 产物断言:tooling/build/2.0.0/install/ 应含 sdk/qa/methodology/legacy-rag/vstore/ui/start_all.sh/vstore_restart.sh/ollama_restart.sh/SHA256SUMS。
  • SHA256SUMS 校验的已知良性失败:./SHA256SUMS: 失败 是清单自引用条目,永远不匹配;其余 1367 项应全 OK。
  • start_all.sh 由 build_all 模板生成(heredoc),改动模板=tooling/build_all.sh,不是产物文件。
  • UI dist 内置:build_all 内部会跑 npm run build;前端单测(r3debug 89 + 主套件 373)在构建前跑一遍: cd ui && npx vitest run --config tests/r3debug/vitest.config.js && npx vitest run

2. 部署前准备

# 2.1 176 备份(必做;恢复依据)
ssh -p 26518 xzq@8.138.231.183 "cp -a /srv/plantai/install /srv/plantai/install.bak-$(date +%s)"
# 2.2 从 175 发起 P2P 连通性预检
ssh -i ~/.ssh/id_ed25519_p2p -p 26518 -o BatchMode=yes -o ConnectTimeout=8 xzq@192.168.0.176 'hostname'

3. 传输(rsync --delete,必须 exclude 四个本地产物)

cd /home/xzq/5090/teamWorkspace/zh/PlantAI-rag-new/tooling/build/2.0.0
rsync -a --numeric-ids --delete \
  --exclude 'venv' \
  --exclude 'sdk/data' \
  --exclude '.env' \
  -e 'ssh -i ~/.ssh/id_ed25519_p2p -p 26518 -o BatchMode=yes' \
  install/ xzq@192.168.0.176:/srv/plantai/install/

exclude 原理(2026-09-12 实测,漏掉会删生产数据):源构建树不含 176 的 install/venv(1.8G Python 环境)、install/sdk/data(plantai.db/docgen 数据)、install/.env、install/sdk/.env;--delete 会把这些删掉。

传输后必做——恢复三份 prod.env:rsync 会删 176 的 {qa,methodology,legacy-rag}/env/prod.env(源构建树只有 .example)。从刚做的备份恢复:

ssh -p 26518 xzq@8.138.231.183 '
for t in qa methodology legacy-rag; do
  cp /srv/plantai/install.bak-*/$t/env/prod.env /srv/plantai/install/$t/env/prod.env
done
# 幸存校验:venv/sdk/data/.env 应仍在
ls -d /srv/plantai/install/venv /srv/plantai/install/sdk/data /srv/plantai/install/sdk/.env
'

4. 重启(注意 BFF 不随 start_all 换进程)

# 4.1 手动杀旧 BFF(lsof 只取 LISTEN;start_all 不替换在跑进程——E-6 陷阱)
ssh -p 26518 xzq@8.138.231.183 'kill $(lsof -ti tcp:8090 -sTCP:LISTEN) 2>/dev/null; sleep 2'
# 4.2 杀 QA/methodology/legacy(让它们吃到新代码)
ssh -p 26518 xzq@8.138.231.183 '
for p in 8091 8092 8093; do kill $(lsof -ti tcp:$p -sTCP:LISTEN) 2>/dev/null; done; sleep 2'
# 4.3 新版 start_all.sh 一键拉起(五服务 + 生信编排,自带 disown 不卡)
ssh -p 26518 xzq@8.138.231.183 'cd /srv/plantai/install && timeout 180 bash start_all.sh'

lsof 陷阱:lsof -ti tcp:PORT 会返回该端口所有 socket 的进程(含已建立连接);必须加 -sTCP:LISTEN 只取监听者,且 kill 不加引号。vstore/bioflow 的 restart.sh 已内置此过滤,直接调即可:

bash /srv/plantai/install/vstore_restart.sh              # vstore 幂等重启(自带 lsof LISTEN + disown)
bash /srv/plantai/bioflow/bioflow_service/restart.sh     # 生信(独立树,不在 install 内)

BFF 手动重启的 venv 路径陷阱(2026-09-15 实测):venv 在部署根 /srv/plantai/install/venv,而 BFF 必须 cd sdk 才能找到 app.main:app——必须在 cd sdk 之前把 python 解析成绝对路径(或 ../venv/bin/python),否则 cd sdk && venv/bin/python 会解析成不存在的 install/sdk/venv/bin/python(setsid: failed to execute venv/bin/python: No such file or directory,且该错误在 /tmp/plantai-sdk.log 里,端口静默 DOWN):

cd /srv/plantai/install && set -a && . sdk/.env && set +a && \
( cd sdk && setsid /srv/plantai/install/venv/bin/python -m uvicorn app.main:app \
  --host 127.0.0.1 --port 8090 > /tmp/plantai-sdk.log 2>&1 < /dev/null & ) && disown -a

另:老 BFF 可能「进程活着但 socket 已失效」(ss 无 8090 监听、curl 空响应),SIGTERM 杀不掉时用 kill -9;重起后验证 ss -tln | grep ':8090 ' 与 /api/health 两者,不能只看进程。

start_all 编排行为(2026-09-12 起):ollama → QA/methodology/legacy(已跑则跳过)→ vstore → 探测 install 同级 bioflow/ 树,存在则启动/重启生信 8098(不存在静默跳过)→ BFF。BFF 段有 disown -a,脚本正常退出不会卡。

5. 部署后质检(逐条核对)

# 5.1 六服务端口全景(8090/8091/8092/8093/8094/8098 应全 LISTEN 且是新进程 lstart)
ssh -p 26518 xzq@8.138.231.183 'for p in 8090 8091 8092 8093 8094 8098; do
  ss -tlnp | grep ":$p " | grep -o "pid=[0-9]*" | head -1; done'
# 5.2 BFF health 五上游全 ok
curl -s http://127.0.0.1:8090/api/health
# 5.3 生信 TAB 依赖的端点(掉进程时 TAB 静默消失;无生信部署此项应为 UPSTREAM_DOWN 且属预期)
curl -s http://127.0.0.1:8090/api/bioflow/health
# 5.4 nginx 80 全链(唯一客户入口)
curl -s http://127.0.0.1:80/api/health
# 5.5 前端新资产已服务 + 旧哈希已清
curl -s http://127.0.0.1:80/ | grep -o 'index-[A-Za-z0-9_-]*\.\(js\|css\)'
# 5.6 SHA256SUMS(排除 SHA256SUMS 自引用条目,其余应全 OK)
cd /srv/plantai/install && sha256sum -c SHA256SUMS 2>&1 | grep -cv 'OK$'

6. 纯前端改动快速部署(不重编)

# 175 构建后:
rsync -a --numeric-ids -e 'ssh -i ~/.ssh/id_ed25519_p2p -p 26518 -o BatchMode=yes' \
  ui/dist/ xzq@192.168.0.176:/srv/plantai/install/ui/dist/
# 176 清旧哈希资产(先看 index.html 引用哪个,删其余):
ssh -p 26518 xzq@8.138.231.183 'cd /srv/plantai/install/ui/dist && grep -o "index-[A-Za-z0-9_-]*\.\(js\|css\)" index.html'
# BFF 是静态托管,无需重启;浏览器强刷即生效

7. 部署变体(换机/裁剪/自定义库路径)

本 skill 主体以「175 → 176 全量」为模板;以下变体全部实测可行,机制均内建:

7.1 部署到其他机器

业务层流程(§1-6)原样复用,只需替换三处:

  • P2P 密钥:把 175 的 ~/.ssh/id_ed25519_p2p.pub 装进新机器 authorized_keys(或改用新机器自己的 SSH 端口/凭据),rsync 命令的目标地址同步替换。
  • 部署根路径:rsync 目标与后续命令里的 /srv/plantai 换成新机器路径。
  • 基础设施一次准备(新机器首次,业务层外):按 docs/DEPLOY_FLOW.md §5.1-5c 做 py3.12 venv+依赖清单、各树 env(路径类键改目标机布局)、nginx 80 反代、MinerU(可选)、Ollama(可选);数据库用 deploy/qa-db/ 的 systemd 脚本裸装(数据目录见 §7.3)。
  • 数据库数据从现网同步:整目录 rsync(82G 约 8 分钟),或 replace_db.sh 整库替换(自动探活+回滚)。

7.2 不含生信部分(零配置,自动行为)

start_all 的生信编排(9d7ed1b)探测 install 同级 bioflow/bioflow_service/restart.sh,树不存在即静默跳过。彻底不含生信的做法:

  1. 不部署 bioflow 树(不传 /srv/plantai/bioflow/),也不搬运 8 个镜像(40G);
  2. sdk/.env 不加 BIOFLOW_ENABLED(或设为 false)——前端生信 TAB 按 /api/bioflow/health 轮询显隐,自然不显示。

注意:start_all 只看「树是否存在」,不看 env——若树在而 env 关,8098 仍会拉起。要彻底不含,树和 env 都去掉。 质检时 §5.3 的 bioflow health 返回 UPSTREAM_DOWN 属预期,跳过该项。

7.3 三个向量库用自定义路径(纯配置,零代码改动)

库 数据路径决定 连接键(QA 服务 env)
Neo4j QA_DB_ROOT env → $QA_DB_ROOT/neo4j/data(install_neo4j.sh 的 server.directories.data) NEO4J_URI
Qdrant 同脚本 storage_path: $STORAGE(源于 QA_DB_ROOT) QDRANT_URL / V15_QDRANT_COLLECTION / NORMALIZED_RELEASE_ID
vstore VSTORE_ROOT env(vstore_restart.sh source 自己的 .env) VSTORE_BASE_URL(BFF 侧)
  • 「向量库放 xxxx 路径」= 数据库 systemd 安装阶段设 QA_DB_ROOT=xxxx + qa env 连接 URI 指向对应端口——无代码改动。
  • 换库(如水产库)是同一机制:换路径 + 换 NORMALIZED_RELEASE_ID/V15_QDRANT_COLLECTION 两个键,整库替换走 replace_db.sh(自动探活+回滚)。
  • db 层不在 start_all 编排:systemd 的 neo4j/qdrant 在安装期定路径,之后 start_all 不动它们。

7.4 环境镜像全量同步(2026-09-14 起为部署原则)

原则:175 上所有环境类镜像(8 个 bioflow- + 16 个 plantai-env- + plantai-allenv + plantai-protected = 26 个)必须全部部署到 176。** 扩展分析的 runtime_image 可选范围 = 176 本地镜像全集,缺一个环境就少一类分析能力。

  • 首次全量同步:逐个 docker save <ref> | ssh p2p docker load(流式,175→176 内网直连;18 个 plantai 系约 69GB,全部完成后 176 镜像计数应为 26)。
  • 新增环境镜像后:175 打新镜像(playbook docs/bioflow_image_build_playbook.md)后,同法同步到 176 并更新 playbook 台账;176 上 tag 按 dev/构建日期后缀(20260906)保持与 175 一致。
  • 镜像修复后:修复版覆盖同名 tag 前,旧镜像 tag 回退保存(如 bioflow-pan:dev-before-panfamily),新镜像 load 到 176 后旧 tag 可随时回滚。
  • activate.d 陷阱:镜像构建后必须排查 conda env 的 activate.d/*.sh 是否注入了宿主绝对路径(PYTHONPATH 断链,panfamily 案例)——容器内无法访问宿主路径,需把模块 COPY 进镜像。

8. 陷阱速查(全部实测)

  1. rsync --delete 删本地产物:必须 exclude venv/sdk/data/.env;prod.env 传输后从备份恢复。
  2. start_all 不杀在跑的 BFF:新 uvicorn 端口被占静默退出,自检看到的是旧进程——先 kill $(lsof -ti tcp:8090 -sTCP:LISTEN)。
  3. lsof 不带 LISTEN 过滤:kill 多行 PID(含已建立连接进程)失败,旧进程存活、新进程绑定失败静默退出。
  4. start_all 缺 disown:bash 尾部隐式 wait BFF 永不返回(已修,勿回退)。
  5. 生信 TAB 消失 = 8098 掉进程:bioflow_service 独立树,start_all 现会自拉;排查先 curl /api/bioflow/health。
  6. figures 双键 + registry(r4):PLANTAI_FIGURES_MINERU_ROOT/REGISTRY 缺一即静默降级纯 PMC;registry 文件需单独传输。
  7. cython .so 验证必须 runtime import:strings 探不到 env 键,会误判旧代码。
  8. vstore 数据完整性:health 计数异常先查进程 environ(VSTORE_* 键),再怀疑数据。
  9. MinerU 缺 accelerate:启动期 ValueError: requires accelerate,目标机需 pip install accelerate。
  10. QA LLM 截断重试:LLM output was truncated by the token limit 会重试 4 次(每 ~9.5s),最终仍出答案,非部署故障。

9. 提示词示例(直接复制给 agent)

全量部署到 176(默认形态):

按 plantai-deploy skill 部署:175 仓库最新代码打包部署到 176,含生信,部署后跑完整质检并汇报。

部署到新机器 + 不含生信 + 自定义库路径:

按 plantai-deploy skill 部署到一台新机器(192.168.0.177,ssh 端口 26518,部署根 /srv/plantai)。
要求:不含生信部分(不部署 bioflow 树、不搬运镜像、sdk/.env 不开 BIOFLOW_ENABLED);
Neo4j 与 Qdrant 的数据目录放在 /data/qa-db(即 QA_DB_ROOT=/data/qa-db);
基础设施按 DEPLOY_FLOW §5.1 从零准备,数据库数据从 176 的 /srv/plantai/qa-db 同步;
完成后跑质检(生信项按无生信预期)并汇报。

纯前端快速部署:

按 plantai-deploy skill 把 175 最新前端 dist 部署到 176(纯前端,不重编),并验证 nginx 80 已服务新资产。

换库部署(以水产库为例):

按 plantai-deploy skill 换库部署:用水产库(kb-libraries/aqua)替换 176 当前植物库。
换库是纯配置:NORMALIZED_RELEASE_ID 与 V15_QDRANT_COLLECTION 换成 aqua 值,
Neo4j/Qdrant 数据目录指向 aqua 库位置,replace_db.sh 整库替换;
植物库保留原位可回滚;部署后跑金标问答质检并汇报。