OPERATIONS_GUIDE.md 10 KB

线上部署与数据导入完整操作方案

适用环境: Linux 生产服务器
项目路径: /opt/pharmacopoeia-ai
药典数据源: /opt/2025(DOCX 文件)
操作前提: 已拉取最新代码,JDK 21 可用


一、操作总览

Step 0: 确认环境(1 分钟)
Step 1: 启动基础设施 PostgreSQL + Redis(2 分钟)
Step 2: 初始化/迁移数据库(1 分钟)
Step 3: 启动 Java 后端验证连通性(2 分钟)
Step 4: 导入 Wiki 临床数据 via import_all.py(~2 分钟)
Step 5: 导入药典 DOCX 原文 via docx_ingest.py(~数小时)
Step 6: 验证搜索功能(2 分钟)
Step 7: 正式启动服务(2 分钟)

二、详细步骤

Step 0: 确认环境

# 0.1 确认 Java 版本
java -version
# 期望: java version "21.x.x"

# 如果 JAVA_HOME 未设置:
export JAVA_HOME=/usr/lib/jvm/jdk-21
export PATH=$JAVA_HOME/bin:$PATH

# 0.2 确认项目目录
cd /opt/pharmacopoeia-ai
git status
ls data-pipeline/ backend-java/ deploy/ database/

# 0.3 确认数据源存在
ls /opt/2025/output*/品种正文/*.docx | head -5
# 应能看到 DOCX 文件

# 0.4 创建日志目录
mkdir -p /var/log/pharmacopoeia

# 0.5 确认 .env 配置
cat .env | grep QWEN_API_KEY
# 确保 QWEN_API_KEY 已填写真实的 DashScope API Key
# 如果 .env 不存在,复制模板:
# cp .env.example .env && vim .env

Step 1: 启动基础设施

cd /opt/pharmacopoeia-ai/deploy

# 启动 PostgreSQL + Redis
docker-compose up -d

# 等待健康检查通过(约 15 秒)
sleep 15
docker-compose ps
# pharmacopoeia-pg     Up (healthy)
# pharmacopoeia-redis  Up (healthy)

# 验证 PG 连接
docker exec pharmacopoeia-pg pg_isready -U postgres
# 输出: /var/run/postgresql:5432 - accepting connections

Step 2: 初始化/迁移数据库

cd /opt/pharmacopoeia-ai

# 2.1 导入表结构(首次部署)
docker exec -i pharmacopoeia-pg psql -U postgres -d pharmacopoeia < database/schema.sql

# 2.2 执行 json → jsonb 迁移(如果是旧库升级)
docker exec -i pharmacopoeia-pg psql -U postgres -d pharmacopoeia < database/migrate_json_to_jsonb.sql

# 2.3 确认表已创建
docker exec pharmacopoeia-pg psql -U postgres -d pharmacopoeia -c "\dt"
# 期望看到: drugs, drug_chunks, knowledge_points, questions, conversations, messages, users, user_progress, answer_records

Step 3: 启动 Java 后端(验证连通性)

cd /opt/pharmacopoeia-ai/backend-java

# 3.1 编译打包
export JAVA_HOME=/usr/lib/jvm/jdk-21
mvn clean package -DskipTests -q
# 输出: BUILD SUCCESS

# 3.2 临时启动验证(前台运行,Ctrl+C 可停止)
java -jar target/pharmacopoeia-ai-1.0.0.jar

# 或以后台方式启动:
# nohup java -Xms512m -Xmx2g -Dfile.encoding=UTF-8 \
#   -jar target/pharmacopoeia-ai-1.0.0.jar \
#   > /var/log/pharmacopoeia/backend.log 2>&1 &

# 3.3 验证健康检查
curl http://localhost:9000/api/v1/health
# 期望: {"status":"UP"}

# 3.4 验证当前数据量(此时应为零)
curl http://localhost:9000/api/v1/admin/stats
# 期望: {"total_drugs":0, "total_chunks":0, "total_queries":0, ...}

Step 4: 导入 Wiki 临床数据

数据来源: data-pipeline/data/wiki_merged.json(13 个药品的维基百科临床专著,含概述/适应症/药理/不良反应等)+ sample_drugs.json(4 个药典示例)+ drug_guides.json(4 个用药指导)

cd /opt/pharmacopoeia-ai/data-pipeline

# 4.1 安装 Python 依赖
pip install httpx sqlalchemy asyncpg psycopg2-binary python-docx

# 4.2 执行导入
python import_all.py

预期输出:

📂 扫描数据目录: .../data-pipeline/data
   ✅ sample_drugs.json — 兼容,将导入
   ✅ wiki_merged.json — 兼容,将导入
   ✅ drug_guides.json — 兼容,将导入
   ⏭️ drug_index.json — 跳过
   ⏭️ catalog_volume1.json — 跳过
   ...
🎉 全部导入完成!

耗时: 约 1-2 分钟(~200 个 chunk 向量化)

验证:

curl http://localhost:9000/api/v1/admin/stats
# total_drugs 应为 21 左右

# 验证甲硝唑、布洛芬等已入库
curl -s http://localhost:9000/api/v1/drug/search?keyword=甲硝唑 | python3 -m json.tool | head -20

Step 5: 导入药典 DOCX 原文

数据来源: /opt/2025/ 下 6317 个 DOCX 文件(2025 年版中国药典全文)

cd /opt/pharmacopoeia-ai/data-pipeline

# 5.1 确认环境变量
export DOCX_SOURCE_DIR=/opt/2025
export QWEN_API_KEY=$(grep QWEN_API_KEY ../.env | cut -d= -f2)

# 5.2 执行导入(建议在 screen/tmux 中运行,防止断连)
screen -S ingest
python docx_ingest.py
# Ctrl+A D 分离 screen,screen -r ingest 重新连接

预期输出:

📂 扫描 DOCX 文件...
   发现 6317 个 DOCX 文件
📦 解析完成: 6xxx 个有效条目 (跳过 xx 个)
   分类分布:
     化学药: 2930
     中药: 2276
     通则: 473
     药用辅料: 387
     ...
🚀 第 1/13x 批 (50 个药品) ...
  ✂️ 250 个 chunks,向量化中...
    向量化: 10/250
    向量化: 20/250
    ...
  ✅ 入库: 50 药品, 250 chunks

🚀 第 2/13x 批 ...

耗时估算:

阶段 数量 耗时
解析 DOCX 6317 个 ~5 分钟
向量化 ~35,000 个 chunk / 10 条每批 = 3500 次 API 调用 ~2-3 小时
写入 PG ~35,000 行 ~5 分钟
合计 约 2-4 小时

断点续传: 脚本使用 ON CONFLICT DO NOTHING,重复运行不会产生重复数据。如果中途中断,重新运行即可从断点继续(已入库的会被跳过)。

监控进度:

# 另一个终端,每隔 30 秒查看入库量
watch -n 30 "docker exec pharmacopoeia-pg psql -U postgres -d pharmacopoeia -c 'SELECT COUNT(*) AS total_chunks FROM drug_chunks;'"

Step 6: 验证搜索功能

# 6.1 确认数据量
curl -s http://localhost:9000/api/v1/admin/stats | python3 -m json.tool
# total_drugs: ~6300+, total_chunks: ~35000+

# 6.2 测试药品搜索
curl -s "http://localhost:9000/api/v1/drug/search?keyword=布洛芬" | python3 -m json.tool | head -30

# 6.3 测试 AI 问答(非流式)
curl -s -X POST http://localhost:9000/api/v1/chat/ask \
  -H "Content-Type: application/json" \
  -d '{"message":"布洛芬缓释胶囊的用法用量"}' | python3 -m json.tool | head -50

# 6.4 测试 AI 问答(流式 SSE)
curl -N -X POST http://localhost:9000/api/v1/chat/stream \
  -H "Content-Type: application/json" \
  -d '{"message":"甲硝唑的不良反应有哪些"}'

# 6.5 验证 Wiki 数据(临床专著格式)
curl -s "http://localhost:9000/api/v1/drug/search?keyword=二甲双胍" | python3 -m json.tool | head -20

# 6.6 测试分类树
curl -s http://localhost:9000/api/v1/drug/category/tree | python3 -m json.tool | head -20

Step 7: 正式启动服务

# 7.1 关闭临时 Java 进程
pkill -f pharmacopoeia-ai-1.0.0.jar

# 7.2 使用 supervisor 管理(推荐)
cp deploy/supervisord-java.conf /etc/supervisor/conf.d/pharmacopoeia.conf

# 编辑 supervisor 配置,填入真实环境变量
vim /etc/supervisor/conf.d/pharmacopoeia.conf
# 修改 environment 行:
#   POSTGRES_PASSWORD="实际密码",
#   QWEN_API_KEY="实际的Key"

supervisorctl reread
supervisorctl update
supervisorctl start pharmacopoeia-backend

# 7.3 或者用 systemd
cat > /etc/systemd/system/pharmacopoeia.service << 'EOF'
[Unit]
Description=Pharmacopoeia AI Backend
After=network.target docker.service

[Service]
Type=simple
User=root
WorkingDirectory=/opt/pharmacopoeia-ai/backend-java
Environment="JAVA_HOME=/usr/lib/jvm/jdk-21"
Environment="POSTGRES_PASSWORD=pharma2025"
Environment="QWEN_API_KEY=sk-your-key"
ExecStart=/usr/lib/jvm/jdk-21/bin/java -Xms512m -Xmx2g -Dfile.encoding=UTF-8 -jar target/pharmacopoeia-ai-1.0.0.jar
Restart=always
RestartSec=10
StandardOutput=append:/var/log/pharmacopoeia/backend.log
StandardError=append:/var/log/pharmacopoeia/backend-error.log

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable pharmacopoeia
systemctl start pharmacopoeia
systemctl status pharmacopoeia

# 7.4 最终验证
curl http://localhost:9000/api/v1/health

三、故障排查

PG 连接失败

docker logs pharmacopoeia-pg --tail 20
docker exec pharmacopoeia-pg pg_isready -U postgres

Embedding API 报错

# 检查 QWEN_API_KEY 是否正确
grep QWEN_API_KEY /opt/pharmacopoeia-ai/.env
# 手动测试 API
curl -X POST https://dashscope.aliyuncs.com/api/v1/services/embeddings/text-embedding/text-embedding \
  -H "Authorization: Bearer $QWEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"text-embedding-v3","input":{"texts":["测试"]},"parameters":{"text_type":"query"}}'

Java 后端启动失败

# 查看错误日志
tail -100 /var/log/pharmacopoeia/backend-error.log
# 常见原因: PG 未启动、端口被占用、API Key 无效

DOCX 导入中断恢复

# docx_ingest.py 使用 ON CONFLICT DO NOTHING,直接重新运行即可
cd /opt/pharmacopoeia-ai/data-pipeline
python docx_ingest.py

搜索无结果

# 确认数据已入库
docker exec pharmacopoeia-pg psql -U postgres -d pharmacopoeia -c "
SELECT COUNT(*) AS drugs FROM drugs WHERE is_active=TRUE;
SELECT COUNT(*) AS chunks FROM drug_chunks WHERE vec IS NOT NULL;
SELECT name FROM drugs WHERE name LIKE '%布洛芬%' LIMIT 5;
"
# 如果 drugs 有数据但搜索无结果,检查 vec 列是否为 NULL:
# SELECT drug_id, vec IS NULL FROM drug_chunks LIMIT 5;

四、数据导入决策表

场景 导入内容 命令 耗时
快速验证 Wiki (21 条) python import_all.py 2 分钟
完整部署 Wiki + 6317 DOCX python import_all.py && python docx_ingest.py 2-4 小时
增量更新 仅新增 DOCX python docx_ingest.py 首次长/后续短
清空重建 全部重来 TRUNCATE drug_chunks, drugs CASCADE; 然后重新导入 2-4 小时

五、关键路径汇总

部署路径:     /opt/pharmacopoeia-ai/
DOCX 源:      /opt/2025/output*/.../*.docx (6317 个)
PG 容器:      pharmacopoeia-pg (5432)
Redis 容器:   pharmacopoeia-redis (6379)
Java 后端:    http://localhost:9000
API 前缀:     /api/v1
Supervisor:   /etc/supervisor/conf.d/pharmacopoeia.conf
日志:         /var/log/pharmacopoeia/