# AI 引擎 v2 架构设计规格 > **日期:** 2026-05-05 | **状态:** Draft | **范围:** erp-ai 模块演进 > **实施周期:** Q2(2-3 个月)| **方案:** 混合 — 结构化核心 + RAG 接口预留 ## 1. 背景与动机 ### 1.1 当前状态 erp-ai 模块已完成 Phase 1 MVP(6 实体、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/OpenAI)LLM - **按分析类型可覆盖** — 不同分析类型可使用不同 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, health_checker: tokio::task::JoinHandle<()>, } pub struct ProviderEntry { provider: Box, config: ProviderConfig, health: Arc>, } pub enum ProviderHealth { Healthy { last_check: DateTime }, Degraded { last_check: DateTime, error: String }, Unavailable { since: DateTime, error: String }, } // config.rs pub struct AiConfig { pub default_provider: String, pub providers: HashMap, 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, pub base_url: Option, 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; 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; /// 注册新 Provider(初始化阶段调用,一次性构建) pub fn register(&self, name: String, provider: Box); /// 获取指定 Provider(用于 Admin 测试) pub fn get_provider(&self, name: &str) -> Option<&dyn AiProvider>; /// 全量健康检查(后台 60s 间隔) pub async fn health_check_all(&self) -> HashMap; } ``` ### 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`(单实例硬绑定)。重构为: ```rust // 重构前(service/analysis.rs) pub struct AnalysisService { provider: Box, // 硬编码 Claude // ... } // 重构后 pub struct AnalysisService { registry: Arc, // 替换为 Registry quota: Arc, cache: Arc, knowledge: Arc, // ... } ``` `AiState` 构造变更: ```rust // state.rs 重构 pub struct AiState { db: DatabaseConnection, event_bus: Arc, analysis: AnalysisService, prompt: PromptService, usage: UsageService, suggestion: SuggestionService, health_provider: Arc, // 新增字段由 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, 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, 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>; } ``` ### 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` stub,Phase 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>; 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 分析队列 ```rust pub struct AnalysisQueue { db: DatabaseConnection, max_concurrent: usize, // 默认 3 } impl AnalysisQueue { pub async fn enqueue(&self, job: AnalysisJob) -> AiResult; pub async fn run_worker(&self, registry: &ProviderRegistry, cache: &CacheService); pub async fn queue_status(&self, tenant_id: Uuid) -> AiResult; } ``` 新增数据库表: ```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, pub remaining_budget: Option, pub remaining_daily: Option, } 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 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,无需额外编译 |