Files
hms/docs/superpowers/specs/2026-05-05-ai-engine-v2-architecture-design.md
iven df1d85bfde docs: T40 UI 审计报告 + wiki 更新 + Docker 配置
- T40 UI 审计计划和结果文档(docs/qa/)
- wiki 更新:miniprogram 设计系统合规审计记录 + index 关键数字更新
- 审计 V2 完整报告(docs/audits/v2/)
- 讨论记录文档(docs/discussions/)
- 设计规格和实施计划(docs/superpowers/)
- 角色测试计划和结果(docs/qa/role-test-*)
- Docker 生产部署配置
2026-05-13 23:29:42 +08:00

27 KiB
Raw Blame History

AI 引擎 v2 架构设计规格

日期: 2026-05-05 | 状态: Draft | 范围: erp-ai 模块演进 实施周期: Q22-3 个月)| 方案: 混合 — 结构化核心 + RAG 接口预留

1. 背景与动机

1.1 当前状态

erp-ai 模块已完成 Phase 1 MVP6 实体、SSE 流式分析、Claude 单 Provider具备

  • AiProvider trait + ClaudeProvider 实现reqwest 直接调用 Anthropic API
  • SSE 三层流式架构Provider → AnalysisService → Handler
  • DB 级 SHA-256 缓存复用
  • LocalRulesEngine10 条规则,已实现但未集成到路由层)
  • 自动定时分析(每 24h 扫描高风险患者)
  • 双通道输出解析 + AI 建议生命周期管理
  • 透析 KDIGO 风险评分器14 条专科规则)

1.2 核心问题

  1. 单点依赖 — 所有分析绑定 Claude无降级能力Provider 故障 = 服务不可用
  2. 知识注入缺失 — Prompt 无结构化医学知识支撑,分析质量依赖模型通用能力
  3. 无配额管控 — 无成本感知,无租户预算,商业化前提缺失
  4. 管线断裂 — 事件驱动触发仅记录日志,无法自动响应体征异常等关键事件
  5. 缓存效率低 — 仅 DB 级缓存,高频重复分析仍需查询数据库

1.3 设计目标

  • 租户级 Provider 选择 — 客户选择本地Ollama或云端Claude/OpenAILLM
  • 按分析类型可覆盖 — 不同分析类型可使用不同 Provider/模型
  • 故障自动降级 — Provider 不可用时回退规则引擎,服务不中断
  • 结构化知识库 — KDIGO 规则、药物相互作用、科室指南以结构化数据注入 Prompt
  • RAG 接口预留KnowledgeSource trait 统一抽象pgvector 扩展预启用
  • 配额 & 成本感知 — 月度 token 预算、每患者日限、成本追踪、预算告警
  • 事件驱动管线 — 体征异常/新化验报告/透析完成自动触发分析
  • 两级缓存 — Redis TTL + DB 持久化,提升重复分析响应速度

2. 整体架构

2.1 核心数据流

                     ┌─────────────────────────┐
                     │      触发来源             │
                     │  SSE手动/定时/事件驱动    │
                     └───────────┬─────────────┘
                                 │
                     ┌───────────▼─────────────┐
                     │    1. 配额检查           │
                     │  QuotaService.check()   │
                     └───────────┬─────────────┘
                                 │ 通过
                     ┌───────────▼─────────────┐
                     │    2. 缓存检查           │
                     │  CacheService.get()     │
                     │  (Redis TTL + DB hash)  │
                     └───┬───────────────┬─────┘
                    命中 │               │ 未命中
                        ▼               ▼
                   直接返回     ┌───────────────────┐
                                │ 3. 路由决策        │
                                │ Router.resolve()  │
                                │ (租户配置→分析类型) │
                                └──┬──────────┬─────┘
                          规则引擎 │          │ LLM
                                  ▼          ▼
                        LocalRules    ProviderRegistry
                        (零成本)      .get_provider()
                                          │
                                 ┌────────▼────────┐
                                 │ 4. 知识上下文注入 │
                                 │ KnowledgeSource  │
                                 │ .get_context()   │
                                 └────────┬────────┘
                                          │
                                 ┌────────▼────────┐
                                 │ 5. 执行分析      │
                                 │ Provider调用     │
                                 │ + SSE流式返回    │
                                 └────────┬────────┘
                                          │
                                 ┌────────▼────────┐
                                 │ 6. 后处理        │
                                 │ 解析+建议+事件   │
                                 │ + 用量记录       │
                                 │ + 缓存写入       │
                                 └─────────────────┘

2.2 新增文件结构

crates/erp-ai/src/
├── provider/
│   ├── mod.rs          # AiProvider trait已有不变
│   ├── claude.rs       # ClaudeProvider已有不变
│   ├── openai.rs       # OpenAIProvider新增
│   ├── ollama.rs       # OllamaProvider新增
│   └── registry.rs     # ProviderRegistry新增
├── config.rs           # AiConfig + ProviderConfig新增
├── knowledge/
│   ├── mod.rs          # KnowledgeSource trait新增
│   ├── structured.rs   # StructuredKnowledgeSource新增
│   └── vector.rs       # VectorKnowledgeSource预留 stub
├── service/
│   ├── analysis.rs     # 现有(扩展:集成 ProviderRegistry + 知识库上下文)
│   ├── auto_analysis.rs # 现有(改为入队逻辑)
│   ├── local_rules.rs  # 现有(扩展:更多规则 + 与知识库联动)
│   ├── quota.rs        # 配额服务(新增)
│   ├── cache.rs        # 缓存服务(新增)
│   ├── analysis_queue.rs # 分析队列(新增)
│   └── ...             # 其余不变
└── handler/
    ├── mod.rs          # 现有(扩展:配额检查 + 降级逻辑)
    ├── provider_admin_handler.rs  # Provider 管理 API新增
    └── quota_handler.rs           # 配额管理 API新增

2.3 核心抽象

// provider/registry.rs
pub struct ProviderRegistry {
    providers: DashMap<String, ProviderEntry>,
    health_checker: tokio::task::JoinHandle<()>,
}

pub struct ProviderEntry {
    provider: Box<dyn AiProvider>,
    config: ProviderConfig,
    health: Arc<RwLock<ProviderHealth>>,
}

pub enum ProviderHealth {
    Healthy { last_check: DateTime<Utc> },
    Degraded { last_check: DateTime<Utc>, error: String },
    Unavailable { since: DateTime<Utc>, error: String },
}

// config.rs
pub struct AiConfig {
    pub default_provider: String,
    pub providers: HashMap<String, ProviderConfig>,
    pub cache_ttl_seconds: u64,
    pub quota_check_enabled: bool,
}

pub struct ProviderConfig {
    pub provider_type: ProviderType,  // Claude / OpenAI / Ollama / Rules
    pub api_key_env: Option<String>,
    pub base_url: Option<String>,
    pub default_model: String,
    pub max_tokens: u32,
    pub temperature: f32,
    pub is_enabled: bool,
}

// knowledge/mod.rs
#[async_trait]
pub trait KnowledgeSource: Send + Sync {
    async fn get_context(&self, query: &KnowledgeQuery) -> AiResult<KnowledgeContext>;
    fn source_type(&self) -> &str;
    async fn health_check(&self) -> bool;
    async fn entry_count(&self) -> u64;
}

3. 多 Provider 路由引擎

3.1 路由决策流程

每次分析请求按以下优先级链解析 Provider

  1. 分析类型覆盖(ai_tenant_configs.analysis_type_overrides JSONB
  2. 租户默认 Providerai_tenant_configs.default_provider
  3. ProviderRegistry 健康检查 → 不可用时走降级链:
    • 配置的 fallback_provider
    • 其他可用 Provider按配置顺序
    • LocalRulesEngine(零成本降级)

3.2 租户级 Provider 配置

新增数据库表:

CREATE TABLE ai_tenant_configs (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
    tenant_id UUID NOT NULL,  -- 不设外键tenants 表在 erp-auth跨模块不直接引用
    default_provider VARCHAR(50) NOT NULL DEFAULT 'claude',
    fallback_provider VARCHAR(50),
    monthly_token_budget BIGINT,
    analysis_type_overrides JSONB,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by UUID NOT NULL,
    updated_by UUID NOT NULL,
    deleted_at TIMESTAMPTZ,
    version INT NOT NULL DEFAULT 1,
    UNIQUE(tenant_id)
);

analysis_type_overrides 示例:

{
    "lab_report": "claude",
    "trends": "ollama",
    "report_summary": "openai"
}

3.3 ProviderRegistry 核心方法

impl ProviderRegistry {
    /// 解析最终使用的 Provider含降级链
    pub async fn resolve(
        &self,
        tenant_config: &AiTenantConfig,
        analysis_type: &AnalysisType,
    ) -> AiResult<ResolvedProvider>;

    /// 注册新 Provider初始化阶段调用一次性构建
    pub fn register(&self, name: String, provider: Box<dyn AiProvider>);

    /// 获取指定 Provider用于 Admin 测试)
    pub fn get_provider(&self, name: &str) -> Option<&dyn AiProvider>;

    /// 全量健康检查(后台 60s 间隔)
    pub async fn health_check_all(&self) -> HashMap<String, ProviderHealth>;
}

3.4 后台健康检查

每 60 秒对所有注册 Provider 执行轻量级 health_check

  • Claude: GET /v1/models
  • OpenAI: GET /v1/models
  • Ollama: GET /api/tags
  • 连续 3 次失败标记为 Unavailable
  • 恢复后自动标记 Healthy

3.5 新增 Provider 实现

OpenAIProvider

pub struct OpenAiProvider {
    client: reqwest::Client,
    api_key: String,
    base_url: String,  // 默认 https://api.openai.com
}
  • OpenAI Chat Completions API/v1/chat/completions
  • SSE 流式解析 data: [DONE] 边界
  • token 用量从 usage 字段提取

OllamaProvider

pub struct OllamaProvider {
    client: reqwest::Client,
    base_url: String,  // 默认 http://localhost:11434
}
  • Ollama API/api/chatstream: true
  • 无 API Key无 token 计费(成本为零)
  • 适合私有化部署客户

3.6 Provider 管理 API

GET  /api/v1/ai/providers          — 列出所有 Provider 及健康状态
GET  /api/v1/ai/providers/:name    — 单个 Provider 详情
POST /api/v1/ai/providers/:name/test — 连通性测试
PUT  /api/v1/ai/tenant-config      — 更新租户 Provider 配置
GET  /api/v1/ai/tenant-config      — 获取租户配置

权限码:ai.provider.manage(全局 Provider 操作)、ai.analysis.manage(租户配置)

3.7 AnalysisService 重构

当前 AnalysisService 持有 provider: Box<dyn AiProvider>(单实例硬绑定)。重构为:

// 重构前service/analysis.rs
pub struct AnalysisService {
    provider: Box<dyn AiProvider>,  // 硬编码 Claude
    // ...
}

// 重构后
pub struct AnalysisService {
    registry: Arc<ProviderRegistry>,  // 替换为 Registry
    quota: Arc<QuotaService>,
    cache: Arc<CacheService>,
    knowledge: Arc<dyn KnowledgeSource>,
    // ...
}

AiState 构造变更:

// state.rs 重构
pub struct AiState {
    db: DatabaseConnection,
    event_bus: Arc<EventBus>,
    analysis: AnalysisService,
    prompt: PromptService,
    usage: UsageService,
    suggestion: SuggestionService,
    health_provider: Arc<dyn HealthDataProvider>,
    // 新增字段由 AnalysisService 内部持有
}

迁移策略Phase 1 先保留 AnalysisService 接口不变,内部将 provider 替换为 registry,对外透明。


4. 知识库 & RAG 架构

4.1 三层知识模型

层级 范围 内容 存储
L1 核心规则层 系统级 KDIGO 分期规则、危急值阈值、药物相互作用规则 ai_knowledge_rules
L2 常识层 平台级 ICD-10 映射、药物数据库、检验参考范围、通用健康建议 ai_knowledge_references
L3 本地化层 租户级 机构自定义指南、科室特色方案、本地化患者教育内容 ai_knowledge_guides

4.2 知识查询接口

pub struct KnowledgeQuery {
    pub analysis_type: AnalysisType,
    pub patient_context: PatientSummary,  // 脱敏后的患者概况
    pub query_text: Option<String>,
    pub tenant_id: Uuid,
}

// PatientSummary 获取方式:通过 raw SQL 查询(避免跨 crate 依赖 erp-health
// 与现有 handler/mod.rs 中 HealthDataProvider 的 raw SQL 模式一致
// 查询 patients 表基础信息(年龄/性别/标签),不查询 PII 字段

pub struct KnowledgeContext {
    pub source: String,
    pub context_text: String,
    pub references: Vec<Reference>,
    pub confidence: f32,
}

4.3 结构化知识源Phase 1 实现)

pub struct StructuredKnowledgeSource {
    db: DatabaseConnection,
    rules_engine: LocalRulesEngine,
}

impl StructuredKnowledgeSource {
    /// 查询流程:
    /// 1. 匹配 L1 规则conditions JSONB 匹配分析类型和患者状态)
    /// 2. 查询 L2 参考category + tags 匹配)
    /// 3. 查询 L3 指南tenant_id + department 匹配)
    /// 4. 合并为 context_text按优先级排序
    async fn query_structured(&self, query: &KnowledgeQuery) -> AiResult<Vec<KnowledgeEntry>>;
}

4.4 知识数据库表

-- 知识规则表
CREATE TABLE ai_knowledge_rules (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
    tenant_id UUID,
    category VARCHAR(100) NOT NULL,
    rule_name VARCHAR(200) NOT NULL,
    conditions JSONB NOT NULL,
    conclusion TEXT NOT NULL,
    priority INT NOT NULL DEFAULT 0,
    is_active BOOLEAN NOT NULL DEFAULT true,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by UUID NOT NULL,
    updated_by UUID NOT NULL,
    deleted_at TIMESTAMPTZ,
    version INT NOT NULL DEFAULT 1
);

-- 知识参考表
CREATE TABLE ai_knowledge_references (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
    tenant_id UUID,
    title VARCHAR(500) NOT NULL,
    category VARCHAR(100) NOT NULL,
    content TEXT NOT NULL,
    source VARCHAR(200),
    tags TEXT[],
    is_active BOOLEAN NOT NULL DEFAULT true,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by UUID NOT NULL,
    updated_by UUID NOT NULL,
    deleted_at TIMESTAMPTZ,
    version INT NOT NULL DEFAULT 1
);

-- 知识指南表(租户级)
CREATE TABLE ai_knowledge_guides (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
    tenant_id UUID NOT NULL,
    title VARCHAR(500) NOT NULL,
    department VARCHAR(100),
    content TEXT NOT NULL,
    applies_to TEXT[],
    is_active BOOLEAN NOT NULL DEFAULT true,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by UUID NOT NULL,
    updated_by UUID NOT NULL,
    deleted_at TIMESTAMPTZ,
    version INT NOT NULL DEFAULT 1
);

4.5 知识上下文注入

通过 Prompt 模板变量注入:

基于以下知识库信息:
{{knowledge_context}}

分析以下患者数据:
{{sanitized_data}}

KnowledgeSource.get_context() 的返回值填入 knowledge_context

4.6 向量知识源Phase 2 预留)

Phase 1 启用 pgvector 扩展:

CREATE EXTENSION IF NOT EXISTS vector;

预留 VectorKnowledgeSource stubPhase 2 实现嵌入管道:

-- Phase 2 使用
-- CREATE TABLE ai_knowledge_embeddings (
--     id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
--     source_id UUID NOT NULL,
--     source_type VARCHAR(50) NOT NULL,
--     chunk_text TEXT NOT NULL,
--     embedding vector(1536),
--     metadata JSONB
-- );

4.7 知识库管理 API

GET    /api/v1/ai/knowledge/rules          — 规则列表
POST   /api/v1/ai/knowledge/rules          — 创建规则
PUT    /api/v1/ai/knowledge/rules/:id      — 更新规则
DELETE /api/v1/ai/knowledge/rules/:id      — 软删除

GET    /api/v1/ai/knowledge/references     — 参考列表
POST   /api/v1/ai/knowledge/references     — 新增参考
PUT    /api/v1/ai/knowledge/references/:id — 更新
DELETE /api/v1/ai/knowledge/references/:id — 软删除

GET    /api/v1/ai/knowledge/guides         — 指南列表
POST   /api/v1/ai/knowledge/guides         — 新增指南
PUT    /api/v1/ai/knowledge/guides/:id     — 更新
DELETE /api/v1/ai/knowledge/guides/:id     — 软删除

权限码:ai.knowledge.list / ai.knowledge.manage


5. 缓存 & 事件驱动管线

5.1 两级缓存

请求 → L1 Redis 缓存 (TTL=1h)
         │ 命中 → 直接返回
         │ 未命中 ↓
         → L2 DB 缓存 (SHA-256 hash 复用,已有)
         │ 命中 → 回填 Redis + 返回
         │ 未命中 ↓
         → 执行完整分析 → 写入 Redis + DB

5.2 CacheService

pub struct CacheService {
    redis: RedisConnection,
    db: DatabaseConnection,
    default_ttl: Duration,
}

// 缓存键格式: ai:cache:{tenant_id}:{analysis_type}:{input_hash}:{prompt_version}
impl CacheService {
    pub async fn get(&self, key: &CacheKey) -> AiResult<Option<CachedAnalysis>>;
    pub async fn set(&self, key: &CacheKey, value: &CachedAnalysis) -> AiResult<()>;
    pub async fn invalidate_tenant(&self, tenant_id: Uuid) -> AiResult<()>;
}

缓存失效触发条件:

  • Provider 切换 → invalidate_tenant
  • Prompt 更新 → invalidate_tenant(新 prompt_version 自动失效旧缓存)
  • 知识库更新 → invalidate_tenant(知识变化影响分析结果)

Redis 接入方式:

  • Redis 连接池从 AppState 共享获取(与 erp-core 其他模块共用同一 Redis 实例)
  • erp-aiCargo.toml 新增 redis + deadpool-redis 依赖
  • Redis 不可用时自动降级为仅 DB 缓存:CacheService::get() 先查 RedisRedis 报错则静默降级查 DB不阻塞分析流程

5.3 事件驱动触发

扩展 module.rs on_startup 订阅:

事件 触发条件 分析类型 优先级
health_data.critical_alert 体征超出参考范围erp-health 已发布) 趋势分析
lab_report.uploaded 新化验报告上传erp-health 已发布) 化验单解读
appointment.confirmed 预约确认/完成erp-health 已发布) 报告摘要
dialysis.record.created 透析记录创建erp-health 已发布) KDIGO 风险评估
ai.reanalysis.requested 建议执行后 7/14/30 天erp-ai 已发布) 再分析

5.4 分析队列

pub struct AnalysisQueue {
    db: DatabaseConnection,
    max_concurrent: usize,  // 默认 3
}

impl AnalysisQueue {
    pub async fn enqueue(&self, job: AnalysisJob) -> AiResult<Uuid>;
    pub async fn run_worker(&self, registry: &ProviderRegistry, cache: &CacheService);
    pub async fn queue_status(&self, tenant_id: Uuid) -> AiResult<QueueStatus>;
}

新增数据库表:

CREATE TABLE ai_analysis_queue (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
    tenant_id UUID NOT NULL,
    patient_id UUID NOT NULL,
    analysis_type VARCHAR(50) NOT NULL,
    priority INT NOT NULL DEFAULT 0,
    -- 状态: pending → running → completed/failed/cancelled
    -- 重试: failed 且 retry_count < max_retries → pending
    status VARCHAR(20) NOT NULL DEFAULT 'pending',
    source_event VARCHAR(100),
    scheduled_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    started_at TIMESTAMPTZ,
    completed_at TIMESTAMPTZ,
    result_analysis_id UUID REFERENCES ai_analyses(id),
    error_message TEXT,
    retry_count INT NOT NULL DEFAULT 0,
    max_retries INT NOT NULL DEFAULT 2,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by UUID NOT NULL,
    updated_by UUID NOT NULL,
    deleted_at TIMESTAMPTZ,
    version INT NOT NULL DEFAULT 1
);

5.5 与现有 auto_analysis 合并

当前 auto_analysis.rs(每 24h 扫描高风险患者)并入 AnalysisQueue

  • 定时扫描变为队列的定时入队逻辑
  • 队列负责实际执行调度和并发控制
  • 保留"24h 扫描高风险患者"逻辑,但通过队列执行

6. 配额 & 成本管理

6.1 配额模型

租户配额 (ai_tenant_configs)
    ├── 月度 token 预算 (monthly_token_budget)
    │      └── 80% → 告警事件
    │      └── 100% → 拒绝请求 / 降级到规则引擎
    ├── 每患者每日分析次数 (default: 10)
    └── 分析类型限制 (JSONB)
           e.g. {"lab_report": 5, "trends": 20, "report_summary": 3}

6.2 QuotaService

pub struct QuotaService {
    db: DatabaseConnection,
    redis: RedisConnection,
}

pub struct QuotaCheckResult {
    pub allowed: bool,
    pub reason: Option<QuotaDenyReason>,
    pub remaining_budget: Option<u64>,
    pub remaining_daily: Option<u32>,
}

pub enum QuotaDenyReason {
    MonthlyBudgetExhausted,
    DailyLimitReached { limit: u32, current: u32 },
    AnalysisTypeLimitReached { analysis_type: String, limit: u32 },
}

配额检查位于数据流第 1 步(路由之前):

  • allowed=true → 继续
  • MonthlyBudgetExhausted → 降级规则引擎
  • 其他拒绝原因 → 返回 429 Too Many Requests

配额消耗统计复用已有 ai_usage 表:

  • 月度 token 预算消耗通过 SUM(input_tokens + output_tokens) WHERE tenant_id = ? AND created_at BETWEEN ? AND ? 聚合查询
  • 不新建独立的配额追踪表,避免数据冗余
  • Redis 缓存当日/当月用量计数器TTL=24h/31d减少 DB 查询

6.3 成本估算

struct ModelPricing {
    input_per_million: u32,
    output_per_million: u32,
}

// 默认定价(可配置覆盖)
// Claude Sonnet:   $3/M input, $15/M output
// Claude Haiku:    $0.25/M input, $1.25/M output
// OpenAI GPT-4o:   $2.5/M input, $10/M output
// Ollama (本地):   $0
// LocalRules:      $0

6.4 预算告警事件

pub struct BudgetAlertEvent {
    tenant_id: Uuid,
    alert_level: BudgetAlertLevel,  // Warning(80%) / Critical(95%) / Exhausted(100%)
    current_usage_tokens: u64,
    budget_tokens: u64,
    percentage: f32,
}

事件名:ai.budget.alert(携带 alert_level 字段区分 Warning / Critical / Exhausted erp-message 模块订阅 → 通知租户管理员

6.5 配额管理 API

GET  /api/v1/ai/tenant-config           — 获取配额配置
PUT  /api/v1/ai/tenant-config           — 更新配额配置
GET  /api/v1/ai/usage/overview          — 当月用量概览
GET  /api/v1/ai/usage/by-type           — 按分析类型统计
GET  /api/v1/ai/usage/by-day            — 按日统计
GET  /api/v1/ai/usage/by-provider       — 按 Provider 统计
GET  /api/v1/ai/cost/estimate/:type     — 单次分析成本估算

7. 分阶段实施

7.1 Phase 1Week 1-3路由引擎 + 配额基础

任务 涉及文件 产出
AiConfig + ProviderConfig config.rs (新) TOML 配置解析
ProviderRegistry + 健康检查 provider/registry.rs (新) Provider 注册/解析/降级
OpenAIProvider provider/openai.rs (新) OpenAI 兼容 API
OllamaProvider provider/ollama.rs (新) 本地模型
ai_tenant_configs migration + entity (新) 租户配置
QuotaService 基础版 service/quota.rs (新) 配额检查 + 用量记录
Provider Admin API handler/provider_admin_handler.rs (新) 管理接口
集成到 AnalysisService service/analysis.rs (改) 路由决策替换硬编码

验收:租户可配置 Provider降级链工作超配额请求被拒绝或降级。

7.2 Phase 2Week 4-5缓存 + 事件驱动

任务 涉及文件 产出
CacheService Redis 缓存 service/cache.rs (新) 两级缓存
AnalysisQueue service/analysis_queue.rs (新) 异步分析调度
ai_analysis_queue migration + entity (新) 队列持久化
事件订阅扩展 module.rs (改) 4 个新事件触发
合并 auto_analysis service/auto_analysis.rs (改) 入队逻辑

验收:缓存命中率 > 30%(重复分析),体征异常自动入队分析。

7.3 Phase 3Week 6-8知识库 + 成本管理 + Admin UI

任务 涉及文件 产出
KnowledgeSource trait knowledge/mod.rs (新) 统一抽象
StructuredKnowledgeSource knowledge/structured.rs (新) 结构化查询
知识库表 (3 张) + pgvector migration + entity (新) 知识 CRUD + 向量预留
成本估算 + 预算告警 service/quota.rs (扩展) 成本追踪
用量统计 API handler/quota_handler.rs (新) 统计接口
Web Admin UI 前端 (新) Provider/配额/知识库管理页

验收:结构化知识注入 Prompt 工作,单次分析成本可查,管理页面可用。


8. 新增权限码

权限码 说明 角色 状态
ai.provider.manage Provider 级管理 超级管理员 已有module.rs 已声明)
ai.knowledge.list 知识库查看 医护/管理员 新增
ai.knowledge.manage 知识库管理 管理员 新增
ai.quota.manage 配额管理 管理员 新增

已有权限码(无需新增):ai.analysis.list/manageai.prompt.list/manageai.usage.listai.suggestion.list/manageai.provider.manage


9. 新增事件类型

事件名 发布方 消费方 说明
ai.budget.alert QuotaService erp-message 预算告警level 字段区分 Warning/Critical/Exhausted
ai.analysis.queued AnalysisQueue 日志 分析任务入队

10. 新增数据库表汇总

表名 用途 Phase
ai_tenant_configs 租户 AI 配置 Phase 1
ai_analysis_queue 分析任务队列 Phase 2
ai_knowledge_rules 知识规则 Phase 3
ai_knowledge_references 知识参考 Phase 3
ai_knowledge_guides 知识指南 Phase 3
ai_knowledge_embeddings 向量嵌入(预留) Phase 4

所有表遵循项目规范:idUUIDv7tenant_idcreated_atupdated_atversiondeleted_at(软删除)。


11. 风险与缓解

风险 概率 影响 缓解
Ollama 本地模型质量不足 分析质量下降 保留 Claude 降级路径,本地模型仅用于基础分析
Redis 不可用导致缓存失效 性能回退到 DB 查询 CacheService 降级到仅 DB 缓存
知识库数据录入工作量 Phase 3 延期 提供批量导入 API + 预置核心规则种子数据
多 Provider token 计量不一致 成本追踪偏差 统一从 Provider 响应的 usage 字段提取,不估算
pgvector 扩展运维复杂度 数据库升级需求 Docker 镜像预包含 pgvector无需额外编译