- T40 UI 审计计划和结果文档(docs/qa/) - wiki 更新:miniprogram 设计系统合规审计记录 + index 关键数字更新 - 审计 V2 完整报告(docs/audits/v2/) - 讨论记录文档(docs/discussions/) - 设计规格和实施计划(docs/superpowers/) - 角色测试计划和结果(docs/qa/role-test-*) - Docker 生产部署配置
27 KiB
AI 引擎 v2 架构设计规格
日期: 2026-05-05 | 状态: Draft | 范围: erp-ai 模块演进 实施周期: Q2(2-3 个月)| 方案: 混合 — 结构化核心 + RAG 接口预留
1. 背景与动机
1.1 当前状态
erp-ai 模块已完成 Phase 1 MVP(6 实体、SSE 流式分析、Claude 单 Provider),具备:
AiProvidertrait +ClaudeProvider实现(reqwest 直接调用 Anthropic API)- SSE 三层流式架构(Provider → AnalysisService → Handler)
- DB 级 SHA-256 缓存复用
LocalRulesEngine(10 条规则,已实现但未集成到路由层)- 自动定时分析(每 24h 扫描高风险患者)
- 双通道输出解析 + AI 建议生命周期管理
- 透析 KDIGO 风险评分器(14 条专科规则)
1.2 核心问题
- 单点依赖 — 所有分析绑定 Claude,无降级能力,Provider 故障 = 服务不可用
- 知识注入缺失 — Prompt 无结构化医学知识支撑,分析质量依赖模型通用能力
- 无配额管控 — 无成本感知,无租户预算,商业化前提缺失
- 管线断裂 — 事件驱动触发仅记录日志,无法自动响应体征异常等关键事件
- 缓存效率低 — 仅 DB 级缓存,高频重复分析仍需查询数据库
1.3 设计目标
- 租户级 Provider 选择 — 客户选择本地(Ollama)或云端(Claude/OpenAI)LLM
- 按分析类型可覆盖 — 不同分析类型可使用不同 Provider/模型
- 故障自动降级 — Provider 不可用时回退规则引擎,服务不中断
- 结构化知识库 — KDIGO 规则、药物相互作用、科室指南以结构化数据注入 Prompt
- RAG 接口预留 —
KnowledgeSourcetrait 统一抽象,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:
- 分析类型覆盖(
ai_tenant_configs.analysis_type_overridesJSONB) - 租户默认 Provider(
ai_tenant_configs.default_provider) - 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/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>(单实例硬绑定)。重构为:
// 重构前(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 stub,Phase 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-ai的Cargo.toml新增redis+deadpool-redis依赖- Redis 不可用时自动降级为仅 DB 缓存:
CacheService::get()先查 Redis,Redis 报错则静默降级查 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 1(Week 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 2(Week 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 3(Week 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,无需额外编译 |