liuchengsen 1 месяц назад
Родитель
Сommit
1c74d5e4d4
1 измененных файлов с 374 добавлено и 0 удалено
  1. 374 0
      docs/OPERATIONS_GUIDE.md

+ 374 - 0
docs/OPERATIONS_GUIDE.md

@@ -0,0 +1,374 @@
+# 线上部署与数据导入完整操作方案
+
+> **适用环境**: 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: 确认环境
+
+```bash
+# 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: 启动基础设施
+
+```bash
+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: 初始化/迁移数据库
+
+```bash
+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 后端(验证连通性)
+
+```bash
+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 个用药指导)
+
+```bash
+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 向量化)
+
+**验证**:
+```bash
+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 年版中国药典全文)
+
+```bash
+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`,重复运行不会产生重复数据。如果中途中断,重新运行即可从断点继续(已入库的会被跳过)。
+
+**监控进度**:
+```bash
+# 另一个终端,每隔 30 秒查看入库量
+watch -n 30 "docker exec pharmacopoeia-pg psql -U postgres -d pharmacopoeia -c 'SELECT COUNT(*) AS total_chunks FROM drug_chunks;'"
+```
+
+---
+
+### Step 6: 验证搜索功能
+
+```bash
+# 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: 正式启动服务
+
+```bash
+# 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 连接失败
+```bash
+docker logs pharmacopoeia-pg --tail 20
+docker exec pharmacopoeia-pg pg_isready -U postgres
+```
+
+### Embedding API 报错
+```bash
+# 检查 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 后端启动失败
+```bash
+# 查看错误日志
+tail -100 /var/log/pharmacopoeia/backend-error.log
+# 常见原因: PG 未启动、端口被占用、API Key 无效
+```
+
+### DOCX 导入中断恢复
+```bash
+# docx_ingest.py 使用 ON CONFLICT DO NOTHING,直接重新运行即可
+cd /opt/pharmacopoeia-ai/data-pipeline
+python docx_ingest.py
+```
+
+### 搜索无结果
+```bash
+# 确认数据已入库
+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/
+```