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

784 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 缓存复用
- `LocalRulesEngine`10 条规则,已实现但未集成到路由层)
- 自动定时分析(每 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 核心抽象
```rust
// 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. 租户默认 Provider`ai_tenant_configs.default_provider`
3. ProviderRegistry 健康检查 → 不可用时走降级链:
- 配置的 `fallback_provider`
- 其他可用 Provider按配置顺序
- `LocalRulesEngine`(零成本降级)
### 3.2 租户级 Provider 配置
新增数据库表:
```sql
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` 示例:
```json
{
"lab_report": "claude",
"trends": "ollama",
"report_summary": "openai"
}
```
### 3.3 ProviderRegistry 核心方法
```rust
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
```rust
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
```rust
pub struct OllamaProvider {
client: reqwest::Client,
base_url: String, // 默认 http://localhost:11434
}
```
- Ollama API`/api/chat``stream: 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>`(单实例硬绑定)。重构为:
```rust
// 重构前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` 构造变更:
```rust
// 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 知识查询接口
```rust
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 实现)
```rust
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 知识数据库表
```sql
-- 知识规则表
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 扩展:
```sql
CREATE EXTENSION IF NOT EXISTS vector;
```
预留 `VectorKnowledgeSource` stubPhase 2 实现嵌入管道:
```sql
-- 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
```rust
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-ai``Cargo.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 分析队列
```rust
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>;
}
```
新增数据库表:
```sql
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
```rust
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 成本估算
```rust
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 预算告警事件
```rust
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/manage``ai.prompt.list/manage``ai.usage.list``ai.suggestion.list/manage``ai.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 |
所有表遵循项目规范:`id`UUIDv7`tenant_id``created_at``updated_at``version``deleted_at`(软删除)。
---
## 11. 风险与缓解
| 风险 | 概率 | 影响 | 缓解 |
|------|------|------|------|
| Ollama 本地模型质量不足 | 中 | 分析质量下降 | 保留 Claude 降级路径,本地模型仅用于基础分析 |
| Redis 不可用导致缓存失效 | 低 | 性能回退到 DB 查询 | CacheService 降级到仅 DB 缓存 |
| 知识库数据录入工作量 | 高 | Phase 3 延期 | 提供批量导入 API + 预置核心规则种子数据 |
| 多 Provider token 计量不一致 | 中 | 成本追踪偏差 | 统一从 Provider 响应的 usage 字段提取,不估算 |
| pgvector 扩展运维复杂度 | 低 | 数据库升级需求 | Docker 镜像预包含 pgvector无需额外编译 |