根因: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>
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,树不存在即静默跳过。彻底不含生信的做法:
- 不部署 bioflow 树(不传
/srv/plantai/bioflow/),也不搬运 8 个镜像(40G); 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. 陷阱速查(全部实测)
- rsync --delete 删本地产物:必须 exclude venv/sdk/data/.env;prod.env 传输后从备份恢复。
- start_all 不杀在跑的 BFF:新 uvicorn 端口被占静默退出,自检看到的是旧进程——先
kill $(lsof -ti tcp:8090 -sTCP:LISTEN)。 - lsof 不带 LISTEN 过滤:kill 多行 PID(含已建立连接进程)失败,旧进程存活、新进程绑定失败静默退出。
- start_all 缺 disown:bash 尾部隐式 wait BFF 永不返回(已修,勿回退)。
- 生信 TAB 消失 = 8098 掉进程:bioflow_service 独立树,start_all 现会自拉;排查先 curl
/api/bioflow/health。 - figures 双键 + registry(r4):
PLANTAI_FIGURES_MINERU_ROOT/REGISTRY缺一即静默降级纯 PMC;registry 文件需单独传输。 - cython .so 验证必须 runtime import:
strings探不到 env 键,会误判旧代码。 - vstore 数据完整性:health 计数异常先查进程 environ(VSTORE_* 键),再怀疑数据。
- MinerU 缺 accelerate:启动期
ValueError: requires accelerate,目标机需pip install accelerate。 - 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 整库替换;
植物库保留原位可回滚;部署后跑金标问答质检并汇报。