nihaisha-deployment-analysis.md 26 KB

Nihaisha RAG — 合并部署与水平扩容分析

版本: v1.0 日期: 2026-07-28 目标: 将倪海厦 RAG 代码合并到药典助手项目 D:\project\AIyaodianzhushou,分析部署方案和扩容路径


目录

  1. 现有项目对比
  2. 合并策略
  3. 代码合并方案
  4. 部署拓扑
  5. 水平扩容路径
  6. 数据库规划
  7. API 设计

1. 现有项目对比

维度 药典助手 (AIyaodianzhushou) 倪海厦 RAG (nihaisha-nishi-tcm)
数据依赖 2025 年版中国药典 倪海厦中医课程资料(22 个 PDF)
框架 Spring Boot 3.3 + JPA/Hibernate 计划 Spring Boot 3.3 + MyBatis
Java 21 21
数据库 PostgreSQL 16 + pgvector PostgreSQL 16 + pgvector(新)
缓存 Redis 7 Redis 7(可共用)
LLM 百炼 DashScope (Qwen) Ollama 主 + 百炼降级
向量嵌入 DashScope text-embedding-v3 (1024维) BGE-M3 ONNX Runtime 本地 (1024维)
重排序 规则型 Reranker(关键词+权重) BGE-Reranker ONNX Runtime 本地
鉴权 Spring Security + JWT 需共用药典的鉴权体系
端口 9000 计划 8080
构建 单模块 Maven 计划单模块 Maven
部署 Standalone JAR + supervisord + nginx
ORM JPA/Hibernate MyBatis

关键冲突点

冲突 分析
ORM 不一致 药典用 JPA,倪海厦计划用 MyBatis。同一项目混用两套 ORM 维护成本高
Embedding 模型不同 药典用 DashScope API(远程),倪海厦用 ONNX(本地)。两个模型是独立的,不冲突
端口不同 如果不合并,需要两个端口、两个 JAR、两套部署

2. 合并策略

2.1 推荐方案:统一 ORM + 独立模块

AIyaodianzhushou/
├── backend-java/                          # 现有项目(保持 JPA)
│   ├── pom.xml
│   ├── src/main/java/com/pharmacopoeia/
│   │   ├── PharmacopoeiaApplication.java
│   │   ├── entity/          # 药典实体(JPA)
│   │   ├── repository/      # JPA Repository
│   │   ├── service/         # 药典服务
│   │   ├── controller/      # 药典 Controller
│   │   ├── config/          # 共用配置
│   │   ├── security/        # 共用鉴权
│   │   │
│   │   └── nihaisha/        # ★ 新增:倪海厦 RAG 子包
│   │       ├── entity/      # 倪海厦实体(JPA,统一风格)
│   │       ├── repository/  # JPA Repository
│   │       ├── service/     # 倪海厦检索服务
│   │       ├── controller/  # 倪海厦 Controller
│   │       ├── engine/      # ONNX Runtime 引擎
│   │       └── config/      # 倪海厦专属配置
│   │
│   └── src/main/resources/
│       ├── application.yml
│       └── db/migration/    # Flyway 迁移(新增倪海厦表)

2.2 为什么改 JPA 而不是 MyBatis

维度 用 JPA(推荐) 混用 MyBatis + JPA
维护成本 ✅ 一套规范,团队只学一个 ❌ 两套规范,排查问题分裂
代码一致性 ✅ entity/repository 模式统一 ❌ entity + mapper.xml 双模式
连接池 ✅ 共用 HikariCP ✅ 可共用
事务管理 ✅ 统一 @Transactional ⚠️ 需注意跨 ORM 事务
Spring Data 生态 ✅ 审计、分页、Specification 统一 ❌ 各用各的
动态查询 JPA Criteria / QueryDSL MyBatis XML 更灵活(但药典已有 JPA)

结论:药典项目已经用 JPA 跑了生产,强行引入 MyBatis 会增加认知负担。倪海厦的 11 张表查询用 JPA + Native Query(向量搜索、全文搜索本来就是写 SQL 的)完全够用。

2.3 共用 vs 独立

共用 独立
Spring Security + JWT 鉴权 ONNX Runtime 引擎(BGE-M3 + Reranker)
Redis 缓存 数据层(独立的表,不跨库 join)
HikariCP 连接池 Controller(独立路径 /api/v1/nihaisha/
Flyway 迁移 100 MB 日志文件(追加到 logback)
日志框架 (Logback)
同一个 PostgreSQL 实例
同一个 Nginx 反向代理
同一个 supervisord 进程

3. 代码合并方案

3.1 目录结构

backend-java/
├── pom.xml                                 # 加入 ONNX Runtime 依赖
├── src/main/java/com/pharmacopoeia/
│   ├── PharmacopoeiaApplication.java
│   │
│   ├── config/                             # 现有共用配置
│   │   ├── SecurityConfig.java
│   │   ├── WebConfig.java
│   │   └── RedisConfig.java
│   │
│   ├── entity/                             # 药典实体 (不变)
│   ├── repository/                         # 药典 Repository (不变)
│   ├── service/                            # 药典服务 (不变)
│   ├── controller/                         # 药典 Controller (不变)
│   │
│   ├── nihaisha/                           # ★ 倪海厦 RAG
│   │   ├── entity/
│   │   │   ├── NihMeta.java
│   │   │   ├── NihDocument.java
│   │   │   ├── NihParagraph.java
│   │   │   ├── NihRetrievalUnit.java
│   │   │   ├── NihVectorEmbedding.java
│   │   │   ├── NihKnowledgeUnit.java
│   │   │   ├── NihGuideNode.java
│   │   │   ├── NihEvidenceRecord.java
│   │   │   ├── NihGraphEntity.java
│   │   │   └── NihRelation.java
│   │   │
│   │   ├── repository/
│   │   │   ├── NihMetaRepository.java
│   │   │   ├── NihDocumentRepository.java
│   │   │   ├── NihParagraphRepository.java
│   │   │   ├── NihRetrievalUnitRepository.java
│   │   │   ├── NihVectorEmbeddingRepository.java
│   │   │   ├── NihKnowledgeUnitRepository.java
│   │   │   ├── NihGuideNodeRepository.java
│   │   │   ├── NihEvidenceRecordRepository.java
│   │   │   ├── NihGraphEntityRepository.java
│   │   │   └── NihRelationRepository.java
│   │   │
│   │   ├── service/
│   │   │   ├── NihSearchService.java           # 单通道检索
│   │   │   ├── NihHybridSearchService.java     # 混合检索 + RRF
│   │   │   ├── NihKnowledgeService.java        # 知识三元组
│   │   │   ├── NihGraphService.java             # 知识图谱
│   │   │   ├── NihEvidenceService.java          # 证据链
│   │   │   ├── NihGuideNodeService.java         # 导航节点
│   │   │   ├── NihQueryRewriterService.java     # 查询改写
│   │   │   ├── NihEmbeddingService.java         # ONNX BGE-M3
│   │   │   └── NihRerankerService.java          # ONNX BGE-Reranker
│   │   │
│   │   ├── controller/
│   │   │   ├── NihSearchController.java     # /api/v1/nihaisha/search
│   │   │   ├── NihKnowledgeController.java  # /api/v1/nihaisha/knowledge
│   │   │   ├── NihGuideController.java      # /api/v1/nihaisha/guide
│   │   │   ├── NihGraphController.java      # /api/v1/nihaisha/graph
│   │   │   └── NihEvidenceController.java   # /api/v1/nihaisha/evidence
│   │   │
│   │   ├── engine/
│   │   │   ├── OnnxEmbeddingEngine.java      # BGE-M3 ONNX 会话
│   │   │   ├── OnnxRerankerEngine.java       # BGE-Reranker ONNX 会话
│   │   │   └── BertTokenizer.java            # HuggingFace Tokenizer
│   │   │
│   │   ├── dto/
│   │   │   ├── request/
│   │   │   └── response/
│   │   │
│   │   ├── enums/
│   │   │   ├── UnitType.java
│   │   │   ├── KnowledgeUnitType.java
│   │   │   └── ...
│   │   │
│   │   └── config/
│   │       └── OnnxRuntimeConfig.java
│   │
│   └── security/                            # 共用鉴权(需补充倪海厦路径)
│
├── src/main/resources/
│   ├── application.yml
│   ├── application-dev.yml
│   ├── application-prod.yml
│   ├── logback-spring.xml
│   └── db/migration/
│       ├── V1__init_core.sql                # 现有药典表
│       ├── V2__init_exam.sql
│       ├── V3__init_nihaisha_extensions.sql  # ★ pgvector + pg_trgm
│       ├── V4__init_nihaisha_core.sql        # ★ meta, documents, paragraphs
│       ├── V5__init_nihaisha_retrieval.sql   # ★ retrieval_units, vector_embeddings
│       ├── V6__init_nihaisha_knowledge.sql   # ★ knowledge_units, guide_nodes
│       ├── V7__init_nihaisha_graph.sql       # ★ evidence_records, entities, relations
│       └── V8__init_nihaisha_fulltext.sql    # ★ tsvector + trigram 索引
│
├── models/                                   # ONNX 模型文件
│   ├── bge-m3-fp16/
│   │   ├── model.onnx
│   │   └── tokenizer.json
│   └── bge-reranker-fp16/
│       ├── model.onnx
│       └── tokenizer.json
│
└── Dockerfile                                # ★ 新增:应用容器化

3.2 ORM 选择:JPA + Native Query

向量搜索和全文搜索需要写原生 SQL,用 @Query(nativeQuery=true)JdbcTemplate。药典项目已经在 RetrieverService 中这样做了(pgvector <=> 通过 JdbcTemplate 执行)。

// NihVectorEmbeddingRepository.java
public interface NihVectorEmbeddingRepository extends JpaRepository<NihVectorEmbedding, String> {

    // pgvector 近似搜索 — 原生 SQL
    @Query(value = """
        SELECT ru.*, p.title, p.text, p.source_path, p.page_start, p.page_end,
               ve.embedding <=> CAST(:queryVector AS vector) AS distance
        FROM nih_vector_embeddings ve
        JOIN nih_retrieval_units ru ON ru.unit_id = ve.unit_id
        JOIN nih_paragraphs p ON p.paragraph_id = ru.paragraph_id
        ORDER BY ve.embedding <=> CAST(:queryVector AS vector)
        LIMIT :limit
        """, nativeQuery = true)
    List<Object[]> searchByVector(String queryVector, int limit);
}

3.3 表名前缀

为避免与药典现有表冲突,倪海厦表统一加 nih_ 前缀:

原始表名 实际表名
meta nih_meta
documents nih_documents
paragraphs nih_paragraphs
retrieval_units nih_retrieval_units
vector_embeddings nih_vector_embeddings
knowledge_units nih_knowledge_units
guide_nodes nih_guide_nodes
evidence_records nih_evidence_records
entities nih_entities
relations nih_relations
eval_cases nih_eval_cases

药典已有 entities 相关概念?检查后没有冲突,但仍加前缀隔离,便于未来分库。


4. 部署拓扑

4.1 单机部署(当前)

┌─────────────────────────────────────────────────────────┐
│                     线上服务器                            │
│                                                         │
│  ┌──────────────────────────────────────────────────┐  │
│  │                   Nginx (:80/:443)                 │  │
│  │  /api/v1/chat/*     → localhost:9000 (药典对话)    │  │
│  │  /api/v1/drug/*     → localhost:9000 (药典药品)    │  │
│  │  /api/v1/nihaisha/* → localhost:9000 (倪海厦 RAG)  │  │
│  │  /api/v1/exam/*     → localhost:9000 (药典考试)    │  │
│  └──────────────────────┬───────────────────────────┘  │
│                         │                               │
│  ┌──────────────────────┴───────────────────────────┐  │
│  │         pharmacopoeia-ai.jar (:9000)              │  │
│  │                                                   │  │
│  │  ┌─────────────┐  ┌────────────────────────────┐ │  │
│  │  │ 药典服务      │  │ 倪海厦 RAG (nihaisha/)      │ │  │
│  │  │ ├ Chat       │  │ ├ Search (四通道)          │ │  │
│  │  │ ├ Drug       │  │ ├ Knowledge               │ │  │
│  │  │ ├ Exam       │  │ ├ Graph                   │ │  │
│  │  │ └ Admin      │  │ ├ Evidence                │ │  │
│  │  └──────┬───────┘  │ └───────────┬──────────────┘ │  │
│  │         │          │             │                 │  │
│  │  ┌──────┴──────────┴─────────────┴──────────────┐ │  │
│  │  │              共用基础设施                      │ │  │
│  │  │  ├ Spring Security (JWT 鉴权)                 │ │  │
│  │  │  ├ Redis (QA 缓存 + Embedding 缓存)           │ │  │
│  │  │  ├ Flyway (数据库迁移)                        │ │  │
│  │  │  ├ ONNX Runtime (BGE-M3 + Reranker)          │ │  │
│  │  │  └ HikariCP (连接池)                          │ │  │
│  │  └────────────────────┬──────────────────────────┘ │  │
│  └───────────────────────┼────────────────────────────┘  │
│                          │                               │
│  ┌───────────────────────┴──────────────────────────┐   │
│  │          PostgreSQL 16 + pgvector                 │   │
│  │  ├ pharmacopoeia 库(药典: drugs, drug_chunks...)│   │
│  │  └ nih_* 表(倪海厦: 11 张表)                    │   │
│  └──────────────────────────────────────────────────┘   │
│                                                         │
│  ┌──────────┐  ┌──────────┐                             │
│  │ Redis 7  │  │ 模型文件  │                             │
│  │ (缓存)    │  │ bge-m3/  │                             │
│  │ :6379    │  │ bge-reranker/                           │
│  └──────────┘  └──────────┘                             │
└─────────────────────────────────────────────────────────┘
         │
         │ HTTP (查询改写)
         ▼
┌─────────────────────────┐
│  本地 GPU 服务器          │
│  Ollama                 │
│  ├ qwen2.5:14b (主)     │
│  └ :11434               │
└─────────────────────────┘
         │ 降级
         ▼
┌─────────────────────────┐
│  百炼 API (阿里云)       │
│  qwen-plus (兜底)       │
└─────────────────────────┘

4.2 Nginx 路由配置

# 倪海厦 RAG API
location /api/v1/nihaisha/ {
    proxy_pass http://127.0.0.1:9000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_read_timeout 60s;          # 混合搜索可能较慢
}

# 药典对话(现有,不变)
location /api/v1/chat/ {
    proxy_pass http://127.0.0.1:9000;
    proxy_buffering off;             # SSE 流式
    proxy_read_timeout 300s;
}

4.3 资源配置

组件 内存 说明
JVM 堆 3 GB(原 2GB) 新增 ONNX 模型 + 倪海厦检索
ONNX 模型 (FP16) ~1.7 GB BGE-M3 + Reranker 常驻内存
PostgreSQL ~2 GB shared_buffers
Redis ~0.5 GB 缓存
OS ~1 GB
合计 ~8 GB 线上服务器需 8-16 GB 内存

5. 水平扩容路径

5.1 阶段 1:单体(当前 → 上线初期)

                        Nginx
                          │
              ┌───────────┴───────────┐
              │        :9000          │
              │  pharmacopoeia-ai.jar │
              │  (药典 + 倪海厦)       │
              └───────────┬───────────┘
                          │
              ┌───────────┴───────────┐
              │   PostgreSQL (单机)    │
              └───────────────────────┘
  • 适用:QPS < 100,数据量 < 100 万条
  • 操作:无需任何改动,合并代码部署即可

5.2 阶段 2:读写分离(流量增长)

                        Nginx
                          │
              ┌───────────┴───────────┐
              │    应用服务器 × 2      │
              │  (药典 + 倪海厦)       │
              │  :9000  :9000         │
              └───────────┬───────────┘
                          │
          ┌───────────────┼───────────────┐
          │               │               │
  ┌───────┴───────┐ ┌─────┴─────┐ ┌───────┴───────┐
  │ PG 主库 (写)   │ │ PG 从库 1 │ │ PG 从库 2     │
  │               │ │ (只读)    │ │ (只读)        │
  └───────────────┘ └───────────┘ └───────────────┘

Java 侧改动:Spring 读写分离数据源配置

spring:
  datasource:
    master:
      url: jdbc:postgresql://pg-master:5432/pharmacopoeia
    slaves:
      - url: jdbc:postgresql://pg-slave-1:5432/pharmacopoeia
      - url: jdbc:postgresql://pg-slave-2:5432/pharmacopoeia
  • 搜索请求走从库(倪海厦 RAG 是读密集型)
  • 对话记录写入走主库
  • 适用:QPS 100-500,读多写少

5.3 阶段 3:服务拆分 + 独立扩容

                        Nginx / API Gateway
                          │
          ┌───────────────┼───────────────┐
          │               │               │
  ┌───────┴───────┐ ┌─────┴──────┐ ┌──────┴──────┐
  │  药典对话服务  │ │ 倪海厦 RAG │ │  考试服务   │
  │  × 2 实例     │ │  × 3 实例  │ │  × 1 实例   │
  │  :9001        │ │  :9002     │ │  :9003      │
  └───────┬───────┘ └─────┬──────┘ └──────┬──────┘
          │               │               │
          └───────────────┼───────────────┘
                          │
              ┌───────────┴───────────┐
              │   PostgreSQL 集群      │
              │   + Redis 集群         │
              └───────────────────────┘

拆分方式(与当前单模块不冲突,渐进式演进):

backend-java/                          # 当前单体
├── pharmacopoeia-chat/                # 未来:拆为独立模块
├── pharmacopoeia-exam/                # 未来:拆为独立模块
├── nihaisha-rag/                      # 未来:拆为独立模块
└── pharmacopoeia-common/              # 共用:entity, security, config

拆分触发条件

指标 阈值 说明
倪海厦 RAG QPS > 200 单独扩容倪海厦实例
药典对话 QPS > 100 单独扩容药典实例
ONNX 模型内存压力 JVM 堆 > 4GB 倪海厦独立部署,独占 ONNX 模型
数据库连接数 > 80 加从库或分库

5.4 分库路径

倪海厦和药典的数据天然隔离(不跨库 join),随时可分库:

# 未来分库配置
spring:
  datasource:
    pharmacopoeia:
      url: jdbc:postgresql://pg-pharma:5432/pharmacopoeia
    nihaisha:
      url: jdbc:postgresql://pg-nihaisha:5432/nihaisha_rag

当前阶段放在同一个 PostgreSQL 实例、同一个 database 内(nih_ 前缀隔离),减少运维复杂度。


6. 数据库规划

6.1 当前数据库

PostgreSQL 16 (pgvector)
└── pharmacopoeia 库
    ├── 药典表(现有 9 张)
    │   ├── conversations
    │   ├── messages
    │   ├── drugs
    │   ├── drug_chunks
    │   ├── users
    │   ├── knowledge_points
    │   ├── questions
    │   ├── answer_records
    │   └── user_progress
    │
    └── 倪海厦表(新增 11 张,nih_ 前缀)
        ├── nih_meta
        ├── nih_documents
        ├── nih_paragraphs
        ├── nih_retrieval_units
        ├── nih_vector_embeddings      # pgvector 向量
        ├── nih_knowledge_units
        ├── nih_guide_nodes
        ├── nih_evidence_records
        ├── nih_entities
        ├── nih_relations
        └── nih_eval_cases

6.2 存储增量

数据 磁盘占用
药典现有数据 ~2 GB
倪海厦段落 + 检索单元 ~3 GB
倪海厦向量 (1024维 × 50万) ~2 GB
倪海厦索引 (HNSW + GIN + trigram) ~2 GB
总计(新增) ~7 GB
数据库总大小 ~9 GB

7. API 设计

7.1 路径规划

领域 路径前缀 鉴权
药典对话 /api/v1/chat/** ✅ JWT
药典药品 /api/v1/drug/** ✅ JWT
药典考试 /api/v1/exam/** ✅ JWT
药典管理 /api/v1/admin/** ✅ JWT + admin-token
药典快问 /api/v1/yaodian/** ❌ 公开
倪海厦搜索 /api/v1/nihaisha/search/** ✅ JWT
倪海厦知识 /api/v1/nihaisha/knowledge/** ✅ JWT
倪海厦导航 /api/v1/nihaisha/guide/** ✅ JWT
倪海厦图谱 /api/v1/nihaisha/graph/** ✅ JWT
倪海厦证据 /api/v1/nihaisha/evidence/** ✅ JWT
健康检查 /health ❌ 公开

7.2 Security 配置补充

在现有 SecurityConfig 中补充倪海厦路径:

// 现有公开路径
"/health", "/api/v1/auth/**", "/api/v1/yaodian/**", "/static/**"

// 倪海厦搜索暂不单独公开,走 JWT 鉴权(与药典对话一致)
// 所有 /api/v1/nihaisha/** 需要 Bearer Token

7.3 前端问答框模型选择

前端需要一个问答框,支持选择模型。在现有药典对话系统基础上扩展:

POST /api/v1/nihaisha/search/hybrid
Authorization: Bearer <jwt>

Request:
{
  "query": "桂枝汤的组成和用法",
  "model": "nihaisha",           // ★ 新增字段
  "channels": ["vector", "text", "knowledge", "graph"],
  "limit": 20
}

Response:
{
  "results": [...],
  "tookMs": 380,
  "model": "nihaisha"            // 回显模型选择
}

前端对话页可以加一个下拉选择:

  • "药典助手" → 走 /api/v1/chat/stream(现有)
  • "倪海厦中医" → 走 /api/v1/nihaisha/search/hybrid(新增)

附录 A:pom.xml 新增依赖

在现有 backend-java/pom.xml 中追加:

<!-- ONNX Runtime — BGE-M3 Embedding + Reranker 本地推理 -->
<dependency>
    <groupId>com.microsoft.onnxruntime</groupId>
    <artifactId>onnxruntime</artifactId>
    <version>1.18.0</version>
</dependency>

<!-- HuggingFace Tokenizers — 加载 tokenizer.json(Rust JNI) -->
<dependency>
    <groupId>com.huggingface</groupId>
    <artifactId>tokenizers</artifactId>
    <version>0.21.0</version>
</dependency>

不需要加 MyBatis 依赖,统一用 JPA;不需要加 Flyway 依赖,药典项目已有。


附录 B:扩容决策树

QPS < 100 且 内存 < 8GB?
  ├── 是 → 阶段 1:单体,不做任何拆分
  └── 否 → 倪海厦 RAG QPS > 200?
           ├── 是 → 阶段 3:拆分倪海厦为独立服务
           └── 否 → 数据库 CPU > 70%?
                    ├── 是 → 阶段 2:加 PG 从库,读写分离
                    └── 否 → 阶段 2:加应用实例 + 负载均衡

文档版本: v1.0 | 下一步: 确认方案后开始写代码