# 线上部署与数据导入完整操作方案 > **适用环境**: 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/ ```