项目实训(二)|中医智能诊疗系统数据库模块设计与开发落地

前言

本阶段是中医智能诊疗系统的数据底座建设阶段,核心目标是搭建稳定、可扩展、可协作的数据库模块。本篇从需求理解、架构选择、设计思路、工程实践四个角度,记录我在本阶段的技术理解与真实开发进度。

后期根据项目整体业务定位,数据库结构已完成迭代升级,新增用户、患者档案、体质记录、养生食疗、药箱管理等表,完善了用户 - 会话 - 诊疗全流程关联,适配后续 RAG、舌诊、食疗安全检查等功能。

一、阶段目标与整体规划

在进入具体编码前,我先明确了本阶段必须完成的核心任务:

  1. 完成 PostgreSQL + pgvector 环境部署,为后续向量检索预留能力
  2. 围绕诊疗对话业务,设计会话、消息、症状记录相关表结构
  3. 基于 SQLAlchemy 完成 ORM 模型封装,实现面向对象的数据操作
  4. 封装标准化 CRUD,为上层接口提供稳定依赖
  5. 完成数据库连接管理、脚本规范化,并按团队 Git 规范提交

整个过程遵循先设计、再编码、后验证、再提交的工程化思路,确保每一步都可追溯、可复用、可协作。

二、数据库选型背后的业务思考

在选型时,我没有直接选用轻量级数据库,而是根据项目长期演进路线做出判断:

  • 项目未来需要接入中医知识库,需要向量存储
  • 团队多人协作,需要支持多用户、高并发
  • 诊疗数据具有强业务关系,需要事务与外键约束
  • 生产环境要求稳定、可备份、可扩展

PostgreSQL 配合 pgvector 扩展,恰好满足关系型数据与向量数据混合存储的需求。这一决策让项目从一开始就具备长期迭代能力,而不只是满足当前最小可用功能。

三、整体业务架构

整个数据库设计围绕 “用户 → 档案 → 会话 → 消息 → 智能体输出 → 体质记录” 这条业务主线展开。

users (用户)
  │
  ├── user_profiles (用户档案)   ← 一对多:一个用户可管理多个档案
  │     │
  │     ├── sessions (会话)      ← 会话是对话的上下文载体
  │     │     ├── messages (消息)  ← 记录完整对话历史
  │     │     └── agent_outputs (智能体输出) ← 记录诊断推理过程
  │     │
  │     └── constitution_records (体质记录) ← 为个性化推荐提供数据基础
  │
  └── knowledge_* (知识库表)     ← 食材、穴位、方剂、体质、疾病、养生

设计核心思想:

  • 用户与档案分离:一个用户可管理多个健康档案(自己、家人),支持多场景使用
  • 会话与消息分离:会话管理对话状态,消息记录对话内容,职责清晰
  • 智能体输出独立存储:记录七个智能体的推理过程,支撑可解释性
  • 体质记录独立成表:支持体质变化趋势跟踪,与个性化推荐联动

四、表结构设计:从业务流程到数据模型

在设计表结构时,我先梳理了诊疗对话全流程

用户注册 → 完善档案 → 体质识别 → 开启会话 → 多轮交互 → 症状提取 → 诊疗/养生方案 → 记录存档 → 历史对比。

基于项目最新业务定位,我设计了完整的结构化数据表体系,覆盖用户、诊疗、舌诊、养生、RAG知识库、食疗安全等全场景。

4.1 users 用户表

存储用户的基础信息,包括用户名、密码、姓名、角色等。

class User(Base):
    __tablename__ = "users"
    
    id = Column(String(36), primary_key=True)
    username = Column(String(50), unique=True, nullable=False)
    password = Column(String(100), nullable=False)  # bcrypt加密
    name = Column(String(50), nullable=False)
    role = Column(String(20), default="user")
    created_at = Column(DateTime(timezone=True), server_default=func.now())
    updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
    
    profiles = relationship("UserProfile", back_populates="user", cascade="all, delete-orphan")

业务驱动设计:

  • 用户表是整个系统的入口,密码使用 bcrypt 加密存储(而非明文),保证安全性
  • 用户名设置唯一约束,防止重复注册
  • 用户与档案采用一对多关系,一个用户可以创建多个健康档案,支持管理家庭成员或不同时期的健康数据

4.2 user_profiles 用户档案表

记录用户详细信息:性别、年龄、身高、体重、过敏史、病史、电话等,为个性化诊疗提供依据。

class UserProfile(Base):
    __tablename__ = "user_profiles"
    
    id = Column(String(36), primary_key=True)
    user_id = Column(String(36), ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
    gender = Column(String(10))
    age = Column(Integer)
    height = Column(Integer)
    weight = Column(Integer)
    allergies = Column(Text)
    medical_history = Column(Text)
    phone = Column(String(20))
    current_constitution = Column(String(20))
    secondary_constitutions = Column(JSONB, server_default="'[]'")
    current_constitution_updated_at = Column(DateTime(timezone=True))
    current_tongue_analysis = Column(JSONB, nullable=True)
    created_at = Column(DateTime(timezone=True), server_default=func.now())
    updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
    
    user = relationship("User", back_populates="profiles")
    constitution_records = relationship("ConstitutionRecord", back_populates="profile", cascade="all, delete-orphan")
    sessions = relationship("Session", back_populates="profile", cascade="all, delete-orphan")

业务驱动设计:

  • current_constitution 字段记录用户当前体质,直接对接知识库查询模块的个性化推荐功能
  • secondary_constitutions 用 JSONB 存储兼夹体质(如“气虚兼血瘀”),支持多种体质并存
  • 档案表还关联了体质记录、舌诊、会话、药箱等多张表,是整个诊疗系统的数据枢纽
  • constitution_records 表形成一对多关系,支持体质变化的长期跟踪

4.3 constitution_records 体质记录表

存储用户体质问卷答案、体质得分、AI判定结果、解释与建议,支持中医体质辨识。

class ConstitutionRecord(Base):
    __tablename__ = "constitution_records"
    
    id = Column(String(36), primary_key=True)
    profile_id = Column(String(36), ForeignKey("user_profiles.id", ondelete="CASCADE"), nullable=False)
    answers = Column(JSONB, nullable=False)          # 问卷答案
    scores = Column(JSONB, nullable=False)           # 各体质得分
    constitution = Column(String(20), nullable=False) # 最终体质
    secondary_constitutions = Column(JSONB, server_default="'[]'")
    five_elements = Column(JSONB)                    # 五行分析
    bagang = Column(JSONB)                           # 八纲分析
    confidence = Column(Float)
    ai_interpretation = Column(Text)
    suggestions = Column(JSONB)                      # 调理建议
    created_at = Column(DateTime(timezone=True), server_default=func.now())
    
    profile = relationship("UserProfile", back_populates="constitution_records")

业务驱动设计:

  • 每次体质评估产生一条新记录,支持体质变化的长期跟踪
  • answersscores 用 JSONB 存储,灵活适配不同版本的问卷,无需改表结构
  • constitution 字段与 user_profiles.current_constitution 联动,确保档案表始终同步最新体质
  • suggestions 存储调理建议,可直接对接个性化推荐功能
  • five_elementsbagang 支持五行八纲分析,扩展中医分析维度

与知识库模块的联动:

用户完成体质问卷 → ConstitutionRecord 记录评估结果
                    ↓
         user_profiles.current_constitution 同步更新
                    ↓
         个性化推荐功能读取 current_constitution
                    ↓
         从食材表/方剂表匹配对应的调理方案

4.4 sessions 会话表

作为诊疗/舌诊的上下文载体,关联用户与档案,支持两种会话类型,存储已提取症状。

class Session(Base):
    __tablename__ = "sessions"
    
    id = Column(String(36), primary_key=True)
    user_id = Column(String(36), ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
    profile_id = Column(String(36), ForeignKey("user_profiles.id", ondelete="CASCADE"), nullable=False)
    name = Column(String(100), nullable=False)
    type = Column(String(20), nullable=False)  # diagnosis / tongue
    extracted_symptoms = Column(ARRAY(Text), default=[])
    can_start_diagnosis = Column(Boolean, default=False)
    created_at = Column(DateTime(timezone=True), server_default=func.now())
    updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
    
    messages = relationship("Message", back_populates="session", cascade="all, delete-orphan")
    wellness_records = relationship("WellnessRecord", back_populates="session", cascade="all, delete-orphan")

业务驱动设计:

  • type 字段区分“诊室”和“舌诊”两种业务场景,支持不同类型会话的独立管理
  • extracted_symptoms 用 ARRAY 存储已提取的症状列表,支持症状的累积更新,无需额外建表
  • can_start_diagnosis 标志控制辨证流程的启动条件,保证业务流程的可控性
  • 删除会话时级联清理关联的消息和诊疗记录,保证数据完整性

4.5 messages 消息表

记录用户与AI助手的多轮对话内容,保证对话历史可追溯、可复现。

class Message(Base):
    __tablename__ = "messages"
    
    id = Column(String(36), primary_key=True)
    session_id = Column(String(36), ForeignKey("sessions.id"), nullable=False)
    role = Column(String(20), nullable=False)  # user / assistant / system
    content = Column(Text, nullable=False)
    timestamp = Column(DateTime(timezone=True), server_default=func.now())
    
    session = relationship("Session", back_populates="messages")

业务驱动设计:

  • role 字段支持 user/assistant/system 三种角色,为系统提示词等场景预留扩展
  • timestamp 保证消息按时间顺序排列,还原真实对话流程
  • 消息独立成表,支持大模型多轮对话的上下文构建
  • 与会话表形成一对多关系,一个会话可有多条消息

4.6 wellness_records 养生记录表

核心用于食疗养生,包含症状、状态分析、食疗方案、穴位推荐、生活建议、安全警告等。

class WellnessRecord(Base):
    __tablename__ = "wellness_records"
    
    id = Column(String(36), primary_key=True)
    session_id = Column(String(36), ForeignKey("sessions.id"), nullable=False)
    symptoms = Column(ARRAY(String), default=[])
    state_analysis = Column(Text)
    confidence = Column(Float)
    food_therapy = Column(Text)
    herbal_reference = Column(Text)
    acupoints = Column(ARRAY(String))
    lifestyle_advice = Column(Text)
    safety_warnings = Column(ARRAY(String))
    reasoning_path = Column(JSONB)
    created_at = Column(DateTime(timezone=True), server_default=func.now())

业务驱动设计:

  • reasoning_path 用 JSONB 存储推理过程,支撑诊断结果的可解释性
  • safety_warnings 存储安全警告,为食疗安全检查提供数据基础
  • sessions 表关联,一次会话对应一条诊疗记录

4.7 agent_outputs 智能体输出表

记录大模型与RAG检索的原始输出,用于幻觉控制、过程回溯与效果验证。

class AgentOutput(Base):
    __tablename__ = "agent_outputs"
    
    id = Column(String(36), primary_key=True)
    wellness_record_id = Column(String(36), ForeignKey("wellness_records.id", ondelete="CASCADE"), nullable=False)
    agent_name = Column(String(50), nullable=False)
    output = Column(JSONB, nullable=False)
    created_at = Column(DateTime(timezone=True), server_default=func.now())
    
    wellness_record = relationship("WellnessRecord", back_populates="agent_outputs")

业务驱动设计:

  • 系统每次诊疗触发七个智能体(辨证、古籍、文献、处方、审方、康复、报告)协同工作
  • 每个智能体输出结构不同,output 用 JSONB 存储灵活适配
  • agent_name 标识不同智能体,支撑按名称查询特定智能体的输出
  • 支撑系统的可解释性——用户可以回溯每个智能体的推理过程,验证诊断结果的合理性
  • 为后续的幻觉检测效果评估提供数据基础

五、设计原则总结

在设计细节上,我坚持几个原则:

原则 说明
UUID 主键 所有表统一使用 UUID 主键,带时区时间戳,保证分布式环境下数据一致性
外键约束与级联删除 保证关联数据不冗余、不异常,删除会话时自动清理关联数据
JSONB / ARRAY 高级类型 适配大模型输出与复杂结构存储,无需频繁改表结构
SQL 脚本统一管理 支持团队快速初始化、重复执行,保证环境一致性
密码加密存储 使用 bcrypt 加密,限制密码长度为 72 字节,安全性高

这套设计不仅覆盖当前业务,更提前为 RAG检索、舌诊模块、食疗安全检查、大模型幻觉控制、历史对比 等功能预留了完整的数据结构支撑。

六、代码架构设计:三层分层与 CRUD 封装

在编码阶段,我采用清晰的三层数据架构

  1. 连接层:负责数据库连接、会话管理、连接池配置
  2. 模型层:将表结构映射为 ORM 类,实现类型安全与对象化操作
  3. CRUD 层:封装通用数据操作,对外提供稳定接口,避免重复 SQL

6.1 CRUD 目录结构

backend/db/crud/
├── session_crud.py      # 用户、档案、会话、诊疗记录、智能体输出的统一 CRUD
├── message_crud.py      # 消息表的独立 CRUD
└── agent_output.py      # 智能体输出的独立 CRUD

6.2 各模块职责

文件 包含的 CRUD 操作 说明
session_crud.py 用户增删查、档案增删改查、会话增删改查、诊疗记录增删查、智能体输出增查 核心业务的统一管理类 AsyncDatabaseManager
message_crud.py 消息增、查(按会话) 消息独立管理,支持会话级联删除
agent_output.py 智能体输出增、查(按记录/按名称) 智能体输出独立管理

6.3 统一管理类实现

class AsyncDatabaseManager:
    """封装所有核心表的 CRUD 操作"""
    
    # 用户管理
    async def create_user(...) -> User
    async def get_user_by_id(...) -> Optional[User]
    async def get_user_by_username(...) -> Optional[User]
    async def delete_user(...) -> bool
    
    # 档案管理
    async def create_user_profile(...) -> UserProfile
    async def get_user_profile(...) -> Optional[UserProfile]
    async def update_user_profile(...) -> Optional[UserProfile]
    
    # 会话管理
    async def create_session(...) -> Session
    async def get_session(...) -> Optional[Session]
    async def get_sessions_by_user(...) -> List[Session]
    async def delete_session(...) -> bool   # 级联清理
    async def update_session_symptoms(...) -> bool
    async def update_can_start_diagnosis(...) -> bool
    
    # 消息管理
    async def add_message(...) -> Message
    async def get_messages_by_session(...) -> List[Message]
    
    # 诊疗记录
    async def create_wellness_record(...) -> WellnessRecord
    async def get_wellness_records_by_session(...) -> List[WellnessRecord]
    
    # 智能体输出
    async def add_agent_output(...) -> AgentOutput
    async def get_agent_outputs_by_wellness_record(...) -> List[AgentOutput]

6.4 设计亮点

  1. 集中管理:所有数据库操作集中在一个类中,便于维护和查找
  2. 异步统一:所有方法都是异步的(async/await),与 FastAPI 的异步特性保持一致
  3. 事务处理:每个方法内部独立处理 commitrollback,保证数据一致性
  4. 级联删除delete_session 中按依赖顺序清理数据(agent_outputswellness_recordsmessagessession),保证外键约束不被违反
  5. 动态更新update_user_profile 使用 **kwargs + 字段白名单,防止意外更新敏感字段

这种架构的优势非常明显:

  • 逻辑解耦,便于团队分工维护
  • 业务变化时只需修改对应层,不影响整体
  • 代码可读性高,符合现代后端工程规范

在实现过程中,我深刻体会到:好的架构不是写最复杂的代码,而是写最容易被别人读懂、维护、扩展的代码

七、功能验证与进度成果

本阶段最终完成并验证通过的内容包括:

  • 数据库环境正常,pgvector 扩展启用成功
  • 全套业务表创建完成,关系正确,约束生效,支持用户、档案、会话、诊疗、养生、药箱全流程
  • 所有 CRUD 方法均通过真实数据读写测试验证
  • 数据库连接稳定,支持并发访问
  • 会话创建、消息存储、症状记录等功能全部可用
  • 初始化脚本可重复执行,适配团队开发流程
  • 代码按规范提交至远程 dev 分支

所有功能模块可直接接入下一阶段的接口开发

八、技术理解与收获

通过本阶段开发,我对数据库工程化实践有了更真实的理解:

  • 数据库设计本质是业务逻辑的持久化映射,必须先懂业务再建表
  • 好的数据结构能大幅降低上层代码复杂度
  • 连接管理、权限控制、脚本规范是团队协作的基础
  • 代码规范、结构清晰、注释合理比单纯实现功能更重要

本阶段完成的不仅是功能,更是整个项目的数据底座与开发规范

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐