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 生产部署配置
This commit is contained in:
@@ -0,0 +1,783 @@
|
||||
# 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<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` 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<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 分析队列
|
||||
|
||||
```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 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,无需额外编译 |
|
||||
@@ -0,0 +1,75 @@
|
||||
# HMS 夯实基础设计规格
|
||||
|
||||
> 日期: 2026-05-05 | 版本: 1.0 | 状态: Approved
|
||||
|
||||
## 1. 概述
|
||||
|
||||
### 1.1 问题
|
||||
|
||||
HMS 已完成主体功能开发(18 crate / 328 路由 / 46 health 实体 / 772 后端测试),但存在三个结构性问题:
|
||||
|
||||
1. **安全漏洞未清零** — 多专家组审查发现 5 CRITICAL + 10 HIGH + 8 MEDIUM + 5 LOW
|
||||
2. **功能膨胀** — 20 个功能域中 7 个在当前定位下不是首发重点
|
||||
3. **UX 不统一** — 各端视觉风格、交互模式、错误处理不一致;小程序 60 页面功能过度
|
||||
|
||||
### 1.2 目标
|
||||
|
||||
暂停新功能开发,用 6-8 周夯实安全基础、统一设计系统、打磨核心流程、精简小程序,产出可上生产的基础版本。
|
||||
|
||||
### 1.3 范围
|
||||
|
||||
**保留并完善(12 个功能域):** 患者管理、健康数据/体征、告警系统、行动收件箱、AI 智能分析、随访管理、咨询管理、内容管理、积分商城、线下活动、统计仪表盘、设备与数据采集
|
||||
|
||||
**冻结推迟(7 个模块):** 护理计划、班次管理、家庭代理、药物记录、透析管理、医生排班、预约管理
|
||||
|
||||
## 2. Phase 1:安全清零(2-3 周)
|
||||
|
||||
### 2.1 CRITICAL(5 个)
|
||||
|
||||
| ID | 问题 | 修复方案 |
|
||||
|----|------|----------|
|
||||
| C1 | FHIR `allowed_patient_ids=None` 无限制访问 | None 视为空列表(拒绝所有) |
|
||||
| C2 | AI 队列 `claim_next` 绕过 RLS 租户隔离 | 添加 tenant_id,SET `app.current_tenant_id` |
|
||||
| C3 | FHIR `$everything` 子查询缺 tenant_id | 每个子查询加 TenantId 过滤 |
|
||||
| C4 | `.env.bak` 泄露 AES 密钥 | 删除文件、轮换密钥 |
|
||||
| C5 | Docker 硬编码默认密码 | 改用 .env 注入 |
|
||||
|
||||
### 2.2 HIGH(10 个)
|
||||
|
||||
| ID | 问题 | 修复方案 |
|
||||
|----|------|----------|
|
||||
| H1 | FHIR converter 输出加密密文 | 解密后再输出或脱敏 |
|
||||
| H2 | 审计日志泄漏加密密文 | 加密字段记录 REDACTED |
|
||||
| H3 | Refresh Token 验证缺 tenant_id | 添加 tenant_id 过滤 |
|
||||
| H4 | Token revoke 无租户校验 | 增加 tenant_id 参数 |
|
||||
| H5 | readiness_check 泄露内部信息 | 替换为 generic message |
|
||||
| H6 | OAuth handler expect() panic | 改为返回 Internal Error |
|
||||
| H7 | AI 提示词模板 Prompt Injection | 安全检查 + 限制权限 |
|
||||
| H8 | Web JWT 存 localStorage | 评估迁移到 httpOnly cookie |
|
||||
| H9 | 小程序加密密钥嵌入客户端 | 配合服务端 session 失效机制 |
|
||||
| H10 | Debug 构建绕过 KEK 要求 | CI 检查 + 编译守卫 |
|
||||
|
||||
## 3. Phase 2:冻结推迟模块(2-3 天)
|
||||
|
||||
冻结 7 个模块(护理计划/班次/家庭代理/药物/透析/排班/预约),策略:
|
||||
- 不删代码,保留后端路由和数据库迁移
|
||||
- 菜单迁移新增 disabled 标记
|
||||
- 前端路由守卫检查 enabled 标记
|
||||
- 小程序同步隐藏入口
|
||||
|
||||
## 4. Phase 3:设计系统统一(1 周)
|
||||
|
||||
统一范围:色板、StatusTag、错误提示、日期格式、表格布局模式。
|
||||
适老化标准:正文 ≥ 16px、按钮 ≥ 48px、对比度 ≥ 4.5:1。
|
||||
|
||||
## 5. Phase 4:核心流程打磨(1-2 周)
|
||||
|
||||
12 个保留功能域的前后端闭环验证和 UX 打磨。
|
||||
|
||||
## 6. Phase 5:小程序精简与分层(2 周)
|
||||
|
||||
60 页 → ~20 页,砍掉 BLE 同步/知情同意/医生端重表单,适老化改造,多角色分层。
|
||||
|
||||
## 7. 验证标准
|
||||
|
||||
每个 Phase:cargo check + test 通过、浏览器操作无 500、小程序可导航、冻结模块已隐藏、pnpm build 通过。
|
||||
@@ -0,0 +1,301 @@
|
||||
# HMS 质量验证策略 — 分层端到端验证
|
||||
|
||||
> 日期: 2026-05-05 | 状态: Draft
|
||||
|
||||
## 1. 背景
|
||||
|
||||
HMS 健康管理平台已完成 Phase 0-1 的功能开发,Phase 2 Web UI 补全正在进行中。系统当前拥有:
|
||||
|
||||
- 18 个 Rust crate,87k 行后端代码,328 个 API 路由
|
||||
- 46 个健康业务实体,80+ 个 health 模块端点
|
||||
- Web 管理后台 36+ 条健康路由,微信小程序 40+ 页面
|
||||
- 772 个后端单元/集成测试(97.5% 通过率)
|
||||
|
||||
**核心矛盾:** 功能完整度很高(前后端几乎全部贯通),但从未从业务角色视角进行过端到端的系统性验证。无法确认所有业务链路闭环、各角色的功能是否可用、是否存在过度开发。
|
||||
|
||||
**触发因素:** 已签约的血透中心客户要求 1-2 周内看到可试用的版本。
|
||||
|
||||
## 2. 策略概述
|
||||
|
||||
采用**分层验证**策略(Layer C):
|
||||
|
||||
- **第一周(Day 0-5):** 角色场景冒烟测试 — 定义 6 条端到端业务链路,手动走通并记录问题
|
||||
- **第二周(Day 6-10):** 修复 + 固化 — 修复 P0 问题,将通过的链路固化为 Playwright 自动化测试
|
||||
|
||||
## 3. 前置准备(Day 0)
|
||||
|
||||
开始冒烟测试前,必须确认以下基础环境:
|
||||
|
||||
| 检查项 | 验证方式 | 通过标准 |
|
||||
|--------|---------|---------|
|
||||
| 后端服务启动 | `cd crates/erp-server && cargo run` | 无报错,监听 3000 端口 |
|
||||
| 前端开发服务 | `cd apps/web && pnpm dev` | 无报错,访问 localhost:5174 正常 |
|
||||
| 数据库迁移 | 查看启动日志 | 所有迁移成功执行 |
|
||||
| 初始种子数据 | 检查数据库 | 有默认管理员账号 + 基础科室/角色数据 |
|
||||
| 小程序开发工具 | 微信开发者工具加载项目 | 编译无错误,模拟器正常运行 |
|
||||
| API 文档 | 访问 `/api/docs/openapi.json` | OpenAPI spec 正常返回 |
|
||||
|
||||
**种子数据最低要求:**
|
||||
- 1 个管理员账号(admin/Admin@2026)
|
||||
- 2 个科室(血透室、体检科)
|
||||
- 4 个用户(2 医生 + 2 护士),已分配对应角色
|
||||
- 5 个患者档案(其中 2 个需绑定微信测试号,供 S4 使用)
|
||||
- 下周的排班数据
|
||||
- 基础告警规则(血压/心率阈值)
|
||||
|
||||
**已知审计问题确认(Day 0 必须检查):**
|
||||
|
||||
| 审计 ID | 问题描述 | 状态 | 冒烟测试影响 |
|
||||
|---------|---------|------|-------------|
|
||||
| CRITICAL-1 | 小程序晚间血压丢失(`blood_pressure_evening` 类型缺失) | wiki 显示已修复 | S4 步骤 3 涉及体征录入,需确认晚间血压可正常保存 |
|
||||
| CRITICAL-2 | 告警权限码拼写错误(`health.alert.manage` vs `health.alerts.manage`) | 待确认 | S2 步骤 6-7 涉及告警查看/处理,如权限码错误将导致 403 |
|
||||
| HIGH-1 | 透析管理小程序端缺失 | 未修复 | S4 仅验证 Web 端透析功能,小程序端降级为"查看透析记录" |
|
||||
| HIGH-2 | 知情同意小程序端缺失 | 未修复 | S4 跳过知情同意功能验证 |
|
||||
| HIGH-3 | 前端日志严重不足 | 未修复 | 不阻塞冒烟测试,但影响问题定位效率 |
|
||||
|
||||
**降级策略:** HIGH-1/HIGH-2 未修复的小程序功能,在 S4 中标记为 SKIP,不阻塞场景判定。
|
||||
|
||||
## 4. 冒烟测试场景
|
||||
|
||||
### 4.0 场景数据依赖
|
||||
|
||||
```
|
||||
S1 系统初始化(数据基础)
|
||||
├── S2 透析日流程(依赖 S1 创建的科室、护士、排班)
|
||||
├── S3 患者管理(依赖 S1 创建的医生、患者数据)
|
||||
├── S4 小程序核心(依赖 S1 创建的患者 + 微信测试号绑定)
|
||||
├── S5 运营配置(依赖 S1 创建的告警规则基础数据)
|
||||
└── S6 关怀闭环(依赖 S1 创建的患者/医生数据)
|
||||
```
|
||||
|
||||
**硬前置:** S1 必须先通过,否则 S2-S6 无法执行。如果 S1 FAIL,优先修复 S1 再继续。
|
||||
|
||||
### 4.1 场景定义
|
||||
|
||||
#### S1: 系统初始化(管理员)
|
||||
|
||||
| # | 步骤 | 操作路径 | 端 | 期望结果 |
|
||||
|---|------|---------|----|---------|
|
||||
| 1 | 管理员登录 | `/` 登录页 | Web | 进入工作台首页,仪表盘渲染正常 |
|
||||
| 2 | 创建科室 | 侧边栏 > 组织管理 > `/organizations` > 新增科室 | Web | 科室列表显示血透室、体检科 |
|
||||
| 3 | 添加医生 ×2 + 护士 ×2 | 侧边栏 > 用户管理 > `/users` > 新增用户 | Web | 用户列表显示新创建的用户 |
|
||||
| 4 | 分配角色和权限 | 侧边栏 > 角色管理 > `/roles` > 编辑角色 > 绑定用户 | Web | 角色绑定生效,权限控制正确 |
|
||||
| 5 | 创建下周排班 | 侧边栏 > 健康管理 > 排班管理 > `/health/schedules` > 新增排班 | Web | 排班日历视图显示排班数据 |
|
||||
| 6 | 查看统计看板 | 侧边栏 > 健康管理 > 统计报表 > `/health/statistics` | Web | 图表渲染正常,数据不为空 |
|
||||
|
||||
**验证重点:** 系统初始化后所有基础数据就绪,后续场景可使用这些数据。
|
||||
|
||||
---
|
||||
|
||||
#### S2: 透析日流程(护士)
|
||||
|
||||
| # | 步骤 | 操作路径 | 端 | 期望结果 |
|
||||
|---|------|---------|----|---------|
|
||||
| 1 | 护士登录 | `/` 登录页 | Web | 进入工作台,看到今日待办 |
|
||||
| 2 | 查看今日排班 | 侧边栏 > 健康管理 > 排班管理 > `/health/schedules` | Web | 显示今日透析排班列表 |
|
||||
| 3 | 患者签到 | 侧边栏 > 健康管理 > 预约管理 > `/health/appointments` > 签到按钮 | Web | 患者状态变为"已签到" |
|
||||
| 4 | 采集体征 | 患者详情页 > `/health/patients/:id` > 体征 Tab > 录入按钮 | Web | 体征数据保存成功(血压/心率/体温) |
|
||||
| 5 | 记录透析会话 | 侧边栏 > 健康管理 > 透析管理 > `/health/dialysis` > 新建透析记录 > 状态按钮切换(待开始 → 进行中 → 已完成) | Web | 透析记录完整,状态流转正确 |
|
||||
| 6 | 触发异常 | 患者详情页体征录入 > 输入超标血压值(如 200/120) | Web | 告警自动生成,告警列表可见 |
|
||||
| 7 | 确认告警 | 侧边栏 > 健康管理 > 告警列表 > `/health/alerts` > 处理按钮 | Web | 告警状态变为"已处理" |
|
||||
| 8 | 填写交接班记录 | 侧边栏 > 健康管理 > 班次管理 > `/health/shifts/:id` > 交接记录 Tab > 新增 | Web | 交接班记录保存成功 |
|
||||
|
||||
**验证重点:** 血透中心最核心的日常工作流,一条龙走通。
|
||||
|
||||
---
|
||||
|
||||
#### S3: 患者管理与决策(医生)
|
||||
|
||||
| # | 步骤 | 操作路径 | 端 | 期望结果 |
|
||||
|---|------|---------|----|---------|
|
||||
| 1 | 医生登录 | `/` 登录页 | Web | 进入医生工作台/仪表盘 |
|
||||
| 2 | 查看患者列表 | 侧边栏 > 健康管理 > 患者管理 > `/health/patients` | Web | 列表显示患者,可搜索/筛选 |
|
||||
| 3 | 查看患者详情 | 患者列表 > 点击某患者 > `/health/patients/:id` | Web | 详情页显示基本信息、体征、诊断、用药 |
|
||||
| 4 | 查看体征趋势图 | 患者详情页 > 趋势 Tab | Web | 趋势图表渲染正确,数据连续 |
|
||||
| 5 | 审阅透析处方 | 小程序医生端 > 透析 > 处方列表 > `pages/doctor/prescription/detail` | MP | 处方详情显示正常 |
|
||||
| 6 | 创建随访计划 | 侧边栏 > 健康管理 > 随访任务 > `/health/follow-up-tasks` > 新建 | Web | 随访任务生成成功 |
|
||||
| 7 | 查看 AI 分析报告 | 侧边栏 > 健康管理 > AI 分析 > `/health/ai-analysis` | Web | AI 分析结果正常展示 |
|
||||
| 8 | 处理行动收件箱 | 侧边栏 > 健康管理 > 行动收件箱 > `/health/action-inbox` > 完成按钮 | Web | 任务可标记完成 |
|
||||
|
||||
**验证重点:** 医生日常查看数据 + 做决策的完整流程。
|
||||
|
||||
---
|
||||
|
||||
#### S4: 小程序核心体验(患者)
|
||||
|
||||
| # | 步骤 | 操作路径 | 端 | 期望结果 |
|
||||
|---|------|---------|----|---------|
|
||||
| 1 | 微信登录 → 手机绑定 | 首页 `pages/index/index` > 登录按钮 > `pages/login/index` | MP | 登录成功,进入健康首页(`ERP__WECHAT__DEV_MODE=true` 跳过 jscode2session) |
|
||||
| 2 | 查看健康首页 | 底部 Tab > 健康 > `pages/health/index` | MP | 显示今日体征/待办/通知 |
|
||||
| 3 | 体征数据录入 | 健康首页 > 体征录入 > `pages/pkg-health/input/index` | MP | 数据提交成功(含晚间血压验证) |
|
||||
| 4 | 查看体征趋势 | 健康首页 > 趋势查看 > `pages/pkg-health/trend/index` | MP | 趋势图表渲染正常 |
|
||||
| 5 | 查看预约列表 | 底部 Tab > 预约 > `pages/appointment/index` | MP | 显示预约记录 |
|
||||
| 6 | 查看告警通知 | 健康首页 > 告警 > `pages/pkg-health/alerts/index` | MP | 告警列表正常显示 |
|
||||
| 7 | 查看用药记录 | 个人中心 > 用药记录 > `pages/pkg-profile/medication/index` | MP | 用药列表显示正常 |
|
||||
| 8 | 查看诊断记录 | 个人中心 > 诊断记录 > `pages/pkg-profile/diagnoses/index` | MP | 诊断记录显示正常 |
|
||||
|
||||
> **注意:** 透析管理(HIGH-1)和知情同意(HIGH-2)的小程序端尚未实现,本场景跳过这两项。
|
||||
|
||||
**验证重点:** 患者端小程序的基础可用性。
|
||||
|
||||
---
|
||||
|
||||
#### S5: 运营配置(管理员)
|
||||
|
||||
| # | 步骤 | 操作路径 | 端 | 期望结果 |
|
||||
|---|------|---------|----|---------|
|
||||
| 1 | 配置告警规则 | 侧边栏 > 健康管理 > 告警规则 > `/health/alert-rules` > 新建规则 | Web | 规则保存成功,列表显示 |
|
||||
| 2 | 配置危急值阈值 | 侧边栏 > 健康管理 > 危急值阈值 > `/health/critical-value-thresholds` > 新建 | Web | 阈值保存成功 |
|
||||
| 3 | 注册 BLE 网关 | 侧边栏 > 健康管理 > BLE 网关 > `/health/ble-gateways` > 新建网关 | Web | 网关显示为在线/离线状态 |
|
||||
| 4 | 创建知情同意模板 | 侧边栏 > 健康管理 > 知情同意 > `/health/consents` > 新建 | Web | 模板保存成功 |
|
||||
| 5 | 创建随访模板 | 侧边栏 > 健康管理 > 随访模板 > `/health/follow-up-templates` > 新建 | Web | 模板保存成功 |
|
||||
| 6 | 检查侧边栏菜单完整性 | 以管理员登录,逐一点击侧边栏所有健康管理子菜单 | Web | 所有健康模块功能在菜单中可见(参见第 7 节缺口清单) |
|
||||
|
||||
**验证重点:** 管理后台的配置能力是否完整,菜单可见性是否正确。
|
||||
|
||||
---
|
||||
|
||||
#### S6: 关怀闭环(医生)
|
||||
|
||||
| # | 步骤 | 操作路径 | 端 | 期望结果 |
|
||||
|---|------|---------|----|---------|
|
||||
| 1 | 创建护理计划 | 侧边栏 > 健康管理 > 护理计划 > `/health/care-plans` > 新建 > 添加条目 | Web | 计划保存成功,条目可见 |
|
||||
| 2 | 查看行动收件箱 | 侧边栏 > 健康管理 > 行动收件箱 > `/health/action-inbox` | Web | 显示待处理行动(与 S3 步骤 8 共享页面,重点关注护理计划相关行动) |
|
||||
| 3 | 回复咨询消息 | 侧边栏 > 健康管理 > 咨询管理 > `/health/consultations/:id` > 发送消息 | Web | 消息发送成功 |
|
||||
| 4 | 审批 AI 建议 | 行动收件箱 > AI 建议 Tab > 审批按钮 | Web | 建议状态变更 |
|
||||
| 5 | 记录结果测量 | 护理计划详情 > `/health/care-plans/:id` > 结果测量 Tab > 新增 | Web | 测量数据保存成功 |
|
||||
| 6 | 查看内容管理文章 | 侧边栏 > 健康管理 > 文章管理 > `/health/articles` | Web | 文章列表和详情正常显示 |
|
||||
|
||||
> **S3 与 S6 的边界:** S3 侧重"数据查看与决策"(查看趋势、开处方、AI 报告),S6 侧重"计划执行与闭环"(护理计划、咨询回复、结果测量)。行动收件箱在两个场景中都会用到但关注点不同。
|
||||
|
||||
**验证重点:** 护理计划 → 执行 → 测量结果的关怀闭环。
|
||||
|
||||
### 4.2 场景优先级
|
||||
|
||||
| 优先级 | 场景 | 原因 |
|
||||
|--------|------|------|
|
||||
| P0 | S1 系统初始化 | 所有后续场景的数据基础 |
|
||||
| P0 | S2 透析日流程 | 血透中心最核心的业务流程 |
|
||||
| P0 | S3 患者管理 | 医生日常工作的核心路径 |
|
||||
| P0 | S4 小程序核心 | 患者端唯一入口,必须可用 |
|
||||
| P1 | S5 运营配置 | 管理能力,首次演示可以后补 |
|
||||
| P1 | S6 关怀闭环 | 旗舰功能但复杂度高,可降级 |
|
||||
|
||||
## 5. 判定标准
|
||||
|
||||
### 5.1 步骤级判定
|
||||
|
||||
| 状态 | 含义 | 处理方式 |
|
||||
|------|------|---------|
|
||||
| PASS | 步骤完全通过 | 记录,无需修复 |
|
||||
| PARTIAL | 步骤可用但有瑕疵 | 记录问题,不阻塞后续 |
|
||||
| FAIL | 步骤无法完成 | 记录并立即标记为 BUG |
|
||||
|
||||
### 5.2 场景级判定
|
||||
|
||||
| 状态 | 含义 |
|
||||
|------|------|
|
||||
| PASS | 全部步骤 PASS |
|
||||
| PASS_WITH_ISSUES | 全部关键步骤 PASS,有 PARTIAL 项 |
|
||||
| FAIL | 任一关键步骤 FAIL |
|
||||
|
||||
### 5.3 BUG 优先级
|
||||
|
||||
| 级别 | 条件 | 修复时限 |
|
||||
|------|------|---------|
|
||||
| BLOCKER | P0 场景的关键步骤 FAIL | 当天修复 |
|
||||
| CRITICAL | P0 场景非关键步骤 FAIL,或数据不一致 | 48h 内修复 |
|
||||
| HIGH | P1 场景 FAIL | 第二周修复 |
|
||||
| MEDIUM | PARTIAL 问题(UI 错位、文案错误等) | 记录,按优先级排期 |
|
||||
| LOW | 建议性改进 | 积压 |
|
||||
|
||||
## 6. 执行计划
|
||||
|
||||
### 6.1 第一周:冒烟测试
|
||||
|
||||
| 天 | 日期 | 任务 | 交付物 |
|
||||
|----|------|------|--------|
|
||||
| Day 0 | W1-Mon | 前置环境检查 + 种子数据准备 | 环境就绪确认 |
|
||||
| Day 1 | W1-Tue | S1 系统初始化 + 菜单可见性排查 | S1 验证报告 + 菜单缺口清单 |
|
||||
| Day 2 | W1-Wed | S2 透析日流程 | S2 验证报告 |
|
||||
| Day 3 | W1-Thu | S3 患者管理与决策 | S3 验证报告 |
|
||||
| Day 4 | W1-Fri | S4 小程序核心体验 | S4 验证报告 |
|
||||
| Day 5 | W1-Sat/Sun | S5 运营配置 + S6 关怀闭环 + 汇总 | 全场景验证报告 + BUG 清单 |
|
||||
|
||||
### 6.2 第二周:修复 + 固化
|
||||
|
||||
| 天 | 任务 | 交付物 |
|
||||
|----|------|--------|
|
||||
| Day 6-7 | BLOCKER + CRITICAL BUG 修复 | 修复提交 |
|
||||
| Day 8 | P0 场景回归验证(重跑修复步骤 + 前后各一个相邻步骤) | 回归报告 |
|
||||
| Day 9-10 | S2 透析日流程 Playwright 自动化 + P1 场景验证 + 质量报告 | 测试脚本 + 完整质量报告 |
|
||||
|
||||
## 7. 菜单可见性排查
|
||||
|
||||
根据代码分析,以下功能的路由已注册但可能不在侧边栏菜单中显示(需要通过数据库迁移或手动配置添加菜单项):
|
||||
|
||||
- 透析管理
|
||||
- 护理计划
|
||||
- 班次管理
|
||||
- 用药记录
|
||||
- BLE 网关
|
||||
- 危急值阈值
|
||||
- 诊断记录
|
||||
- 家庭健康代理
|
||||
- 知情同意
|
||||
- 随访模板
|
||||
- 行动收件箱
|
||||
- 内容管理(文章/分类/标签)
|
||||
- 实时监控
|
||||
- OAuth 合作方
|
||||
|
||||
**排查方式:** 以管理员登录后查看侧边栏,逐一确认以上功能是否有菜单入口。缺失的需创建菜单迁移文件。
|
||||
|
||||
## 8. 第二周 Playwright 自动化范围
|
||||
|
||||
优先固化最核心的 S2 透析日流程为自动化测试。每个场景预计 4-8 小时(含调试),因此两周内只覆盖 S2:
|
||||
|
||||
1. **S2 透析日流程(Day 9-10)** — 登录 → 排班查看 → 体征录入 → 透析记录 → 告警处理
|
||||
|
||||
S1 和 S3 的自动化留到后续迭代。现有 Playwright 基础设施(`apps/web/e2e/`)已有 page object 和 fixture 模式可复用。
|
||||
|
||||
**自动化质量标准:**
|
||||
- 每个关键步骤至少一个断言
|
||||
- flaky 测试最大重试 2 次
|
||||
- 测试数据通过 API setup 生成,不依赖手动准备
|
||||
|
||||
小程序端(S4)暂不纳入自动化(微信开发者工具的自动化测试生态不成熟),持续手动验证。
|
||||
|
||||
## 9. 交付物清单
|
||||
|
||||
| 交付物 | 产出时间 | 说明 |
|
||||
|--------|---------|------|
|
||||
| 环境就绪确认 | Day 0 | 所有前置检查通过 |
|
||||
| 可重复执行的种子数据脚本 | Day 0 | SQL 或 seed 脚本,可一键初始化测试数据 |
|
||||
| 6 份场景验证报告 | Day 1-5 | 每条链路的步骤级结果 |
|
||||
| 菜单缺口清单 | Day 1 | 需要补充的侧边栏菜单项 |
|
||||
| BUG 清单 | Day 5 | 按优先级排列的完整问题列表 |
|
||||
| 修复提交记录 | Day 6-8 | 所有 BLOCKER/CRITICAL 的修复 |
|
||||
| Playwright 测试脚本(S2) | Day 9-10 | 透析日流程自动化测试 |
|
||||
| 质量报告 | Day 10 | 两周验证总结 + 发布建议 |
|
||||
|
||||
### 质量报告模板
|
||||
|
||||
质量报告应包含以下内容:
|
||||
|
||||
1. **场景判定汇总** — 6 个场景的最终判定(PASS / PASS_WITH_ISSUES / FAIL)
|
||||
2. **BUG 清单及修复状态** — 所有发现的问题、当前状态(已修复/待修复/降级)
|
||||
3. **发布风险评估** — GO / CONDITIONAL GO / NO-GO 判定及理由
|
||||
4. **遗留问题清单** — 未修复问题的清单、影响范围和后续计划
|
||||
5. **下一步建议** — 第二阶段验证或正式发布的前置条件
|
||||
|
||||
## 10. 风险与应对
|
||||
|
||||
| 风险 | 概率 | 影响 | 应对 |
|
||||
|------|------|------|------|
|
||||
| 种子数据不完整,S1 无法执行 | 中 | 高 | Day 0 优先准备可重复执行的种子数据脚本 |
|
||||
| BLOCKER 数量过多,修复超过 2 天 | 中 | 高 | 降级 P1 场景,集中修复 P0 |
|
||||
| 小程序登录流程不通 | 中 | 高 | 提前准备测试号和 mock 环境(`ERP__WECHAT__DEV_MODE=true`) |
|
||||
| 微信开发者工具版本兼容性导致登录失败 | 中 | 中 | 使用稳定版开发者工具,避免最新 beta 版 |
|
||||
| 菜单缺失导致功能"找不到" | 高 | 低 | Day 1 集中排查并补充菜单迁移 |
|
||||
| 两周时间不够完成所有修复 | 中 | 中 | 只交付 P0 通过的版本给客户 |
|
||||
926
docs/superpowers/specs/2026-05-11-copilot-gene-design.md
Normal file
926
docs/superpowers/specs/2026-05-11-copilot-gene-design.md
Normal file
@@ -0,0 +1,926 @@
|
||||
# HMS Copilot 基因化设计规格
|
||||
|
||||
> 日期: 2026-05-11 | 状态: Draft | 分支: feat/media-library-banner
|
||||
> 讨论来源: 2026-05-11 发散式互动探讨
|
||||
|
||||
---
|
||||
|
||||
## §1 愿景与定位
|
||||
|
||||
### 1.1 问题陈述
|
||||
|
||||
当前 erp-ai 模块是一个独立的 AI 分析工具,覆盖 3 个场景(化验单解读、趋势分析、报告摘要),用户需要主动点击"AI 分析"按钮才能触发。AI 与系统的关系是"附加工具"——不用它,系统照常运转。
|
||||
|
||||
这带来三个问题:
|
||||
|
||||
1. **医护端:被动发现** — 医护需要主动查数据、看报告才能发现异常。高风险患者的风险信号淹没在数据海洋中,依赖医护的经验和注意力。
|
||||
2. **患者端:沟通空白** — 血透机构无互联网医院资质,医生不能在线与患者对话产生诊断行为。患者离院后的疑问、不适、焦虑没有合规渠道可以解答。
|
||||
3. **系统层面:AI 价值未被释放** — AI 只在用户主动触发时才工作,99% 的运行时间处于闲置状态,但它本可以持续观察数据、发现模式、生成洞察。
|
||||
|
||||
### 1.2 Copilot 定义
|
||||
|
||||
Copilot 将 AI 从"工具"转变为"基因"——一个始终在场、主动观察、适时建议的智能层。
|
||||
|
||||
它不是系统的一个器官,而是弥漫在每个交互点的基础能力。就像免疫系统不是一个独立的器官,而是无处不在的防御能力。
|
||||
|
||||
**Copilot 不是什么:**
|
||||
- 不是自动决策系统——它不替医护做决定
|
||||
- 不是诊断工具——它不做医疗诊断
|
||||
- 不是聊天机器人——它有深度上下文感知能力
|
||||
- 不是 erp-ai 的替代品——它是 erp-ai 的进化形态
|
||||
|
||||
**Copilot 是什么:**
|
||||
- 一个"永远醒着的观察者"——持续监控数据变化
|
||||
- 一个"适时开口的顾问"——在关键时刻主动提供建议
|
||||
- 一个"合规的沟通桥梁"——在法律允许范围内连接医护和患者
|
||||
- 一个"越用越了解你的助手"——基于患者数据提供个性化洞察
|
||||
|
||||
### 1.3 核心价值主张
|
||||
|
||||
**医护端:从"查数据发现问题"到"被推送需要关注的风险"**
|
||||
|
||||
| 现在 | Copilot 之后 |
|
||||
|------|-------------|
|
||||
| 医护每天花 30 分钟逐个查看患者数据 | Copilot 自动筛选高风险患者,推送到仪表盘 |
|
||||
| 异常数据依赖医护经验发现 | 规则引擎 + LLM 自动检测异常并分级告警 |
|
||||
| 随访计划从空白模板开始写 | Copilot 基于风险画像推荐个性化随访方案 |
|
||||
| 咨询时需要手动翻看患者历史 | Copilot 侧边栏实时展示背景和追问建议 |
|
||||
|
||||
**患者端:合规替代医患在线沟通**
|
||||
|
||||
血透机构的核心痛点:想服务好患者、想提高随访率、想增加到院量,但没有互联网医院资质,医生不能在线"看病"。
|
||||
|
||||
Copilot 以"AI 健康管家"身份填补这一空白:
|
||||
- 解答患者疑问(在合规边界内)
|
||||
- 提供健康科普和个性化数据解读
|
||||
- 引导需要关注的症状到院就医
|
||||
- 驱动患者日常互动(每日问候、健康打卡、积分激励)
|
||||
|
||||
### 1.4 业务驱动力
|
||||
|
||||
**合规痛点是 Copilot 患者端存在的根本理由:**
|
||||
|
||||
血透机构没有互联网医院资质,意味着:
|
||||
- 医生不能在线与患者进行问诊对话
|
||||
- 不能在线给出诊断或治疗建议
|
||||
- 不能在线开具处方
|
||||
|
||||
但患者离院后确有大量沟通需求:用药疑问、不适咨询、复查提醒、心理支持。这些需求目前没有合规渠道可以满足。
|
||||
|
||||
Copilot 作为 AI 客服/管家:
|
||||
- 不做诊断、不开处方、不给治疗建议
|
||||
- 在合规边界内解答患者疑问
|
||||
- 智能识别需要就医的情况并引导到院
|
||||
- 所有输出经过合规审查引擎自动检查
|
||||
|
||||
这不仅是功能创新,是解决了一个真实的合规痛点。
|
||||
|
||||
### 1.5 设计原则
|
||||
|
||||
1. **Copilot 不替人做决定** — 只建议,医护审批。患者端不诊断,只引导。
|
||||
2. **规则保底,LLM 拓展** — 规则引擎保证确定性和可解释性,LLM 提供超越规则的洞察。规则是下限,LLM 是上限。
|
||||
3. **合规第一** — 患者端所有 AI 输出必须经过合规审查引擎。宁可过度保守,不可越界。
|
||||
4. **渐进式渗透** — 从一个触点(风险画像)开始,逐步扩展到全系统。不追求一步到位。
|
||||
|
||||
---
|
||||
|
||||
## §2 架构总览
|
||||
|
||||
### 2.1 分层架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ 前端呈现层 │
|
||||
│ ├─ 医护端:<CopilotCard /> <CopilotBadge /> <CopilotAlert /> │
|
||||
│ └─ 患者端:对话式 UI(嵌入小程序消息体系) │
|
||||
│ │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Copilot API 层 │
|
||||
│ ├─ GET /copilot/insights — 获取预计算洞察 │
|
||||
│ ├─ POST /copilot/chat — 患者端对话(经合规审查) │
|
||||
│ └─ GET /copilot/patients/{id}/risk — 获取风险评分 │
|
||||
│ │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Copilot 引擎层 │
|
||||
│ ├─ 混合评分引擎(规则引擎 + LLM 补充) │
|
||||
│ ├─ 意图识别引擎(患者端对话分类) │
|
||||
│ ├─ 合规审查引擎(关键词 + 语义双层) │
|
||||
│ └─ 洞察调度器(决定何时/如何生成洞察) │
|
||||
│ │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 数据层 │
|
||||
│ ├─ copilot_insights — 预计算洞察存储(带过期时间) │
|
||||
│ ├─ copilot_risk_snapshots — 患者风险评分快照 │
|
||||
│ ├─ copilot_chat_logs — 患者端对话审查日志(合规审计) │
|
||||
│ └─ copilot_rules — 规则引擎配置(可动态调整) │
|
||||
│ │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 触发层 │
|
||||
│ ├─ 异步:事件总线订阅 → 后台预计算洞察 │
|
||||
│ └─ 同步:API 请求时 → 合并预计算结果 + 实时补充 │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 混合触发模型
|
||||
|
||||
洞察通过两种机制产生,互相补充:
|
||||
|
||||
**异步预计算(后台):**
|
||||
|
||||
事件总线上的健康数据变更事件触发 Copilot 后台引擎,异步生成洞察并写入存储。医护在打开页面前,相关洞察已经准备就绪。
|
||||
|
||||
| 触发源 | 时机 | 延迟 | 示例 |
|
||||
|--------|------|------|------|
|
||||
| 事件总线订阅 | 数据写入后秒级 | < 5s | 血压录入 → 后台更新风险评分 |
|
||||
| 定时任务 | 每日凌晨 | 批量 | 重新计算所有在管患者风险评分 |
|
||||
| 化验报告确认 | 报告确认后 | < 10s | 肌酐异常 → 生成告警洞察 |
|
||||
|
||||
**同步实时补充(前端请求时):**
|
||||
|
||||
医护或患者打开页面时,前端调用 Copilot API。API 先返回预计算的洞察,再根据当前上下文(如正在查看的页面、当前对话内容)实时补充额外建议。
|
||||
|
||||
| 触发源 | 时机 | 延迟 | 示例 |
|
||||
|--------|------|------|------|
|
||||
| 页面加载 | 用户请求时 | < 500ms | 打开患者档案 → 返回风险画像 |
|
||||
| 对话消息 | 患者发消息时 | < 2s | 患者提问 → 意图识别 + 合规审查 + 回复 |
|
||||
| 随访创建 | 医护操作时 | < 1s | 创建随访 → 返回推荐方案 |
|
||||
|
||||
**合并策略:** API 响应中同时包含 `precomputed`(预计算结果,即时返回)和 `realtime`(实时补充,可能稍慢)。前端先渲染预计算部分,实时部分流式追加。
|
||||
|
||||
### 2.3 与现有 erp-ai 的关系
|
||||
|
||||
Copilot 不替换 erp-ai,而是在其上构建。现有 erp-ai 的 AI Provider 抽象(OpenAI / Claude / Qwen / Ollama)、Prompt 模板管理、SSE 流式输出能力完全保留。
|
||||
|
||||
```
|
||||
erp-ai(保持不变)
|
||||
├─ AI Provider 抽象(4 个 Provider)
|
||||
├─ Prompt 模板管理(Handlebars)
|
||||
└─ SSE 流式输出
|
||||
|
||||
Copilot 引擎层(新增,调用 erp-ai)
|
||||
├─ 规则引擎 — 纯 Rust 实现,不依赖 AI Provider
|
||||
├─ LLM 补充分析 — 调用 erp-ai 的 Provider 能力
|
||||
├─ 合规审查 — 调用 erp-ai 的 Provider 能力
|
||||
└─ 意图识别 — 调用 erp-ai 的 Provider 能力
|
||||
```
|
||||
|
||||
**分层依赖关系:**
|
||||
- 规则引擎:完全离线可用,不依赖任何 AI Provider
|
||||
- LLM 补充 / 合规审查 / 意图识别:依赖 erp-ai 的 Provider 抽象
|
||||
- AI Provider 不可用时,Copilot 降级为纯规则模式
|
||||
|
||||
### 2.4 降级策略
|
||||
|
||||
当 AI 服务不可用时(Provider 宕机、配额用尽、网络故障),Copilot 分层降级:
|
||||
|
||||
| 降级级别 | 触发条件 | 影响范围 | 表现 |
|
||||
|----------|---------|---------|------|
|
||||
| 正常 | 所有 Provider 可用 | 全部功能 | 规则 + LLM 混合模式 |
|
||||
| 一级降级 | 主 Provider 不可用,备用可用 | LLM 延迟增加 | 自动切换到备用 Provider |
|
||||
| 二级降级 | 所有 LLM Provider 不可用 | 医护端部分降级 | 规则评分正常,无 LLM 补充;患者端仅回答服务类问题,健康类问题使用安全兜底模板 |
|
||||
| 三级降级 | Copilot 引擎整体不可用 | 全部 Copilot 功能 | 静默降级,系统回到无 Copilot 状态,不阻塞任何业务流程 |
|
||||
|
||||
**关键设计约束:Copilot 的任何故障都不能阻塞 HMS 核心业务流程。** 患者管理、预约、随访、咨询等功能在 Copilot 完全不可用时必须正常运转。
|
||||
|
||||
---
|
||||
|
||||
## §3 医护端 Copilot
|
||||
|
||||
医护端 Copilot 不改变现有工作流,而是在每个关键节点"旁边"插入智能建议。医护照常操作,Copilot 在适当的时候主动开口。
|
||||
|
||||
4 个触点形成闭环:风险画像(基础)→ 异常检测(感知)→ 随访推荐(决策)→ 咨询辅助(执行)→ 回到异常检测。
|
||||
|
||||
### 3.1 触点①:患者风险画像(基础层)
|
||||
|
||||
**触发时机:** 医护打开任意患者相关页面时(同步 API 调用 + 预计算数据)
|
||||
|
||||
**呈现方式:** 患者姓名旁的风险等级徽章 + 可展开的 Copilot 洞察卡片
|
||||
|
||||
**评分机制(混合):**
|
||||
|
||||
Layer 1 — 规则引擎(确定性):
|
||||
- 医疗专家定义规则,存储在 `copilot_rules` 表中
|
||||
- 每条规则包含:条件表达式、风险分值(+1~+5)、严重度、建议文案
|
||||
- 规则以 JSON 表达式存储,支持动态加载和机构自定义
|
||||
- 基础风险分 = 所有匹配规则的分值之和
|
||||
|
||||
Layer 2 — LLM 补充分析(拓展性):
|
||||
- 输入:患者近期数据 + 规则评分结果
|
||||
- 输出:自然语言的补充风险描述、建议、相似病例参考
|
||||
- 非阻塞:LLM 失败时仅展示规则结果
|
||||
|
||||
内置规则覆盖 5 大类:
|
||||
|
||||
| 类别 | 示例规则 | 分值范围 |
|
||||
|------|---------|---------|
|
||||
| 体征异常 | 收缩压连续>140、体重周增幅>2kg | +1~3 |
|
||||
| 化验异常 | eGFR<60、肌酐环比>20%、血钾>5.5 | +2~5 |
|
||||
| 依从性 | 随访失约>2次、药物依从性<80% | +1~2 |
|
||||
| 透析质量 | Kt/V<1.2、透析间期体重增长>5% | +2~4 |
|
||||
| 综合风险 | 多指标同时异常叠加 | 叠加计算 |
|
||||
|
||||
风险等级映射:0-2 低 | 3-5 中 | 6-8 高 | 9-10 危急
|
||||
|
||||
**规则条件表达式 Schema(JSONLogic 子集):**
|
||||
|
||||
采用 JSONLogic 风格的表达式格式,支持嵌套逻辑组合,存储在 `copilot_rules.condition_expr` 字段中。
|
||||
|
||||
```json
|
||||
// 示例:收缩压连续 >140
|
||||
{
|
||||
"and": [
|
||||
{ ">=": [{ "var": "vital_signs.systolic.latest" }, 140] },
|
||||
{ ">=": [{ "var": "vital_signs.systolic.prev1" }, 140] },
|
||||
{ ">=": [{ "var": "vital_signs.systolic.prev2" }, 140] }
|
||||
]
|
||||
}
|
||||
|
||||
// 示例:eGFR < 60
|
||||
{ "<": [{ "var": "lab_reports.egfr.latest" }, 60] }
|
||||
|
||||
// 示例:肌酐环比 > 20%
|
||||
{
|
||||
">": [
|
||||
{ "var": "lab_reports.creatinine.change_pct" },
|
||||
20
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
支持的数据引用路径:
|
||||
- `vital_signs.{指标}.latest` / `.prev1` / `.prev2` — 最近 3 次体征值
|
||||
- `lab_reports.{指标}.latest` / `.change_pct` — 最新化验值 / 环比变化百分比
|
||||
- `follow_up.missed_count` — 随访失约次数
|
||||
- `dialysis.ktv.latest` — 最新透析充分性指标
|
||||
- `medication.adherence_rate` — 药物依从性百分比
|
||||
|
||||
支持的操作符:`>`, `>=`, `<`, `<=`, `==`, `!=`, `and`, `or`, `!`, `in`, `var`
|
||||
|
||||
规则引擎在 Rust 中实现为递归下降的 JSONLogic 解释器,不依赖外部 DSL。
|
||||
|
||||
### 3.2 触点②:健康数据异常检测(感知层)
|
||||
|
||||
**触发时机:** 新体征/化验数据入库时(异步事件驱动)
|
||||
|
||||
**呈现方式:**
|
||||
- 实时:医护首页仪表盘的 Copilot 告警卡片
|
||||
- 推送:浏览器通知(严重异常时)
|
||||
|
||||
**告警分级:**
|
||||
|
||||
| 级别 | 条件 | 示例 | 呈现 |
|
||||
|------|------|------|------|
|
||||
| 🔴 危急 | 危及生命或需立即干预 | 血钾>6.0、严重低血压 | 首页置顶 + 浏览器通知 |
|
||||
| 🟡 警告 | 需要关注,非紧急 | 收缩压>160、肌酐环比>30% | 首页告警列表 |
|
||||
| 🟢 提示 | 轻度异常或趋势变化 | 体重周增幅>1.5kg、血压趋势上升 | 患者档案内洞察卡片 |
|
||||
|
||||
**与现有告警系统的关系:**
|
||||
|
||||
当前 HMS 已有 `health.alerts` 模块。Copilot 异常检测不替代,而是增强:
|
||||
- 现有告警:基于固定阈值的简单规则(血压>140)
|
||||
- Copilot 告警:基于趋势 + 上下文的智能判断(血压从130快速升到150比稳定在145更值得关注)
|
||||
|
||||
两者共存。固定阈值走现有系统,趋势/上下文异常走 Copilot。
|
||||
|
||||
### 3.3 触点③:随访计划智能推荐(决策层)
|
||||
|
||||
**触发时机:** 医护为患者创建或编辑随访计划时
|
||||
|
||||
**呈现方式:** 随访表单右侧的 Copilot 推荐面板
|
||||
|
||||
**生成逻辑:**
|
||||
1. 规则引擎匹配患者疾病类型 → 基础随访模板(如 CKD 4 期标准随访方案)
|
||||
2. 规则引擎叠加风险因素 → 调整频率和关注指标
|
||||
3. LLM 补充 → 基于近期数据生成个性化问诊要点
|
||||
|
||||
**输出内容:**
|
||||
- 推荐随访频率(如"每 2 周 1 次")及理由
|
||||
- 关注指标列表(如"肾功能、电解质、甲状旁腺激素")
|
||||
- 建议问诊要点(如"近期是否有恶心、食欲下降、尿量变化")
|
||||
|
||||
医护可选择"采纳全部"、"选择性采纳"或"不采纳"。
|
||||
|
||||
### 3.4 触点④:在线咨询实时辅助(执行层)
|
||||
|
||||
**触发时机:** 医护进入咨询对话时
|
||||
|
||||
**呈现方式:** 对话界面侧边栏的 Copilot 面板,不侵入对话区域
|
||||
|
||||
**生成逻辑:**
|
||||
1. 加载患者风险画像(触点①的预计算结果)
|
||||
2. 实时分析患者消息内容
|
||||
3. LLM 生成:建议追问方向 + 注意事项提醒 + 过敏/禁忌提示
|
||||
|
||||
**输出内容:**
|
||||
- 患者背景摘要(诊断、透析方案、上次关键指标)
|
||||
- 建议追问方向(如"浮肿是双侧还是单侧?")
|
||||
- 注意事项(如"该患者对 XX 药物过敏")
|
||||
|
||||
医护可选择"一键插入"追问问题到回复框。
|
||||
|
||||
### 3.5 数据流闭环
|
||||
|
||||
```
|
||||
① 风险画像(基础层)
|
||||
│ 输出:风险评分 + 规则匹配结果 + LLM 补充
|
||||
▼
|
||||
② 异常检测(感知层)← 新数据入库事件触发
|
||||
│ 输出:分级告警 + 异常指标
|
||||
▼
|
||||
③ 随访推荐(决策层)← 风险评分 + 异常指标输入
|
||||
│ 输出:随访方案建议 + 关注指标 + 问诊要点
|
||||
▼
|
||||
④ 咨询辅助(执行层)← 患者档案 + 对话内容输入
|
||||
│ 输出:追问建议 + 注意事项 + 过敏提醒
|
||||
│
|
||||
└─→ 产生新的健康数据/咨询记录 → 回到 ②
|
||||
```
|
||||
|
||||
每个触点的输出是下一个触点的输入。数据在闭环中越转越丰富,Copilot 的建议也越来越精准。
|
||||
|
||||
---
|
||||
|
||||
## §4 患者端 Copilot
|
||||
|
||||
### 4.1 角色定位与行为边界
|
||||
|
||||
患者端 Copilot 以"小H 健康管家"的身份存在,是血透机构无互联网医院资质下的合规医患沟通桥梁。
|
||||
|
||||
**角色:** AI 客服 + 健康管家,嵌入小程序消息体系
|
||||
|
||||
**行为边界:**
|
||||
|
||||
| 可以做 | 不可以做 |
|
||||
|--------|---------|
|
||||
| 解释化验单指标含义(科普) | 诊断疾病("你得了XX") |
|
||||
| 生活方式建议(饮食、运动) | 开处方("吃XX药") |
|
||||
| 预约引导、流程咨询 | 预测疗效("吃了会好") |
|
||||
| 健康数据通俗解读 | 替代医生评估 |
|
||||
| 紧急情况引导就医 | 推荐特定治疗方案 |
|
||||
| 基于数据的关怀提醒 | 承诺治疗结果 |
|
||||
|
||||
### 4.2 产品形态:对话式嵌入消息体系
|
||||
|
||||
"小H"作为小程序内的一个"联系人"出现在消息列表中,患者可以像微信聊天一样互动。
|
||||
|
||||
**与 erp-message 模块的关系:**
|
||||
|
||||
小H 对话**不复用** erp-message 的消息系统。erp-message 管理的是医护之间的通知消息,而小H 对话是 AI 驱动的实时交互,两者的数据模型、推送机制、存储需求完全不同。小H 对话使用独立的 `copilot_chat_logs` 表存储。
|
||||
|
||||
**消息列表集成方式:**
|
||||
|
||||
- 小程序 TabBar 中的"咨询"标签(现有)中新增"小H 健康管家"入口卡片
|
||||
- 点击进入独立的 Copilot 对话页面(新页面,不属于现有消息列表)
|
||||
- 不修改现有消息 TabBar 的结构和功能
|
||||
|
||||
**微信服务号模板消息推送:**
|
||||
|
||||
透析日提醒、复查提醒等需要通过微信服务号模板消息推送。这需要:
|
||||
- 机构在微信公众平台注册并认证服务号
|
||||
- 申请模板消息权限并创建所需模板
|
||||
- 后端集成微信模板消息 API
|
||||
|
||||
此功能作为 Phase 4 后期可选增强,不影响核心对话功能。Phase 4 MVP 仅实现小程序内对话。
|
||||
|
||||
交互入口:
|
||||
- "咨询"Tab 中的"小H 健康管家"卡片 → 对话窗口
|
||||
- 首页 AI 问候卡片 → 点击进入对话
|
||||
- 各健康数据页面的"问小H"按钮 → 带上下文进入对话
|
||||
|
||||
### 4.3 意图识别引擎
|
||||
|
||||
患者消息先过 LLM 意图分类,再路由到不同处理逻辑。5 种意图类型,按优先级排序:
|
||||
|
||||
| 优先级 | 意图类型 | 示例 | 处理方式 | 合规要求 |
|
||||
|--------|---------|------|---------|---------|
|
||||
| 1 | 紧急情况 | "我胸痛"、"喘不上气"、"出血不止" | 优先响应 + 强制引导就医 + 通知医护 | 必须包含"请立即就医或拨打120" |
|
||||
| 2 | 健康咨询 | "头晕是不是血压高了"、"这个指标什么意思" | 科普式回答 + 基于患者数据的个性化解读 | 禁止诊断性语言,必须附"建议到院评估" |
|
||||
| 3 | 服务咨询 | "怎么预约"、"透析时间"、"收费多少" | 规则库直接匹配回答 | 无特殊合规要求 |
|
||||
| 4 | 情感关怀 | "我不想透析了"、"好累啊"、"谢谢小H" | 共情回应 + 自然过渡到健康话题 | 不做健康承诺 |
|
||||
| 5 | 闲聊 | "今天天气怎么样"、"你好" | 友好回应 + 巧妙关联健康 | 保持角色一致性 |
|
||||
|
||||
**分类策略:** 单次 LLM 调用完成分类(低 token 消耗的快速分类 prompt),延迟 < 500ms。分类结果缓存到对话上下文中,连续同类消息可跳过重复分类。
|
||||
|
||||
### 4.4 对话上下文管理
|
||||
|
||||
每次对话自动注入患者上下文,使"小H"真正"认识"患者:
|
||||
|
||||
```
|
||||
上下文结构(后端自动组装,前端不可篡改):
|
||||
{
|
||||
"patient": {
|
||||
"name": "张三",
|
||||
"age": 62,
|
||||
"diagnosis": "CKD 4期",
|
||||
"dialysis_schedule": "每周二、四、六 下午",
|
||||
"allergies": ["青霉素"],
|
||||
"medications": ["硝苯地平", "碳酸氢钠"]
|
||||
},
|
||||
"recent_data": {
|
||||
"last_bp": "135/85",
|
||||
"last_weight": "68.5kg",
|
||||
"last_dialysis": "2026-05-09",
|
||||
"next_dialysis": "2026-05-13",
|
||||
"next_checkup": "2026-05-15"
|
||||
},
|
||||
"risk_summary": {
|
||||
"score": 7,
|
||||
"level": "中高",
|
||||
"top_risks": ["eGFR快速下降", "血压趋势上升"]
|
||||
},
|
||||
"conversation_summary": "最近5轮对话摘要..."
|
||||
}
|
||||
```
|
||||
|
||||
**设计约束:**
|
||||
- 上下文由后端自动组装,前端不可篡改
|
||||
- 对话历史保留最近 5 轮摘要(控制 token 消耗)
|
||||
- 敏感字段(详细诊断、具体用药剂量)不注入患者端上下文,只在医护端可见
|
||||
- 上下文随每次请求刷新,确保数据时效性
|
||||
|
||||
### 4.5 引导到院策略
|
||||
|
||||
"引导到院"不是生硬的"请去医院",而是基于上下文的自然引导。通过规则 + LLM 配合实现:
|
||||
|
||||
**规则驱动引导(确定性):**
|
||||
- 患者提到任何身体不适 → 触发引导
|
||||
- 患者问药物相关问题 → 触发引导
|
||||
- 患者数据持续异常(后台检测)→ 主动推送提醒
|
||||
- 患者表达消极情绪("不想来了")→ 共情 + 正面引导
|
||||
|
||||
**引导话术模板(可配置):**
|
||||
- 症状类 → "XX可能有多种原因,建议让医生当面评估。要不要帮您预约?"
|
||||
- 用药类 → "用药调整需要医生评估。我帮您看看最近有没有门诊?"
|
||||
- 消极类 → "理解您可能有些疲惫。规律透析很重要,要不看看有没有更方便的时间段?"
|
||||
|
||||
---
|
||||
|
||||
## §5 合规审查引擎
|
||||
|
||||
### 5.1 双层审查架构
|
||||
|
||||
患者端 Copilot 的每一条 AI 输出都必须经过合规审查。审查不通过则自动修正,不阻断对话流程。
|
||||
|
||||
**Layer 1 — 关键词过滤(规则层,< 5ms):**
|
||||
|
||||
使用 Aho-Corasick 多模式字符串匹配(精确子串匹配),扫描预定义的违规词表。不使用正则表达式——Aho-Corasick 只做子串匹配,不支持模式,因此违规词表需列举具体词组而非模式。
|
||||
|
||||
| 扫描维度 | 违规关键词(示例,非穷举) | 严重度 |
|
||||
|---------|---------|--------|
|
||||
| 诊断类 | "确诊为"、"诊断为"、"你得了"、"诊断结果是" | CRITICAL |
|
||||
| 处方类 | "建议你吃"、"开点"、"处方"、"调整药量" | CRITICAL |
|
||||
| 疗效类 | "吃了会好"、"可以治愈"、"保证能好" | HIGH |
|
||||
| 评估类 | "我判断"、"我认定" | HIGH |
|
||||
| 承诺类 | "肯定没问题"、"绝对不会出问题" | MEDIUM |
|
||||
| 误导安慰类 | "完全不用担心"、"绝对没事" | MEDIUM |
|
||||
|
||||
命中违规 → 标记违规片段 → 进入修正流程。
|
||||
未命中 → 进入 Layer 2。
|
||||
|
||||
**Layer 2 — 语义审查(LLM 层,< 200ms):**
|
||||
|
||||
通过低 token 消耗的快速分类 prompt 进行语义级审查,捕捉绕过关键词的隐性违规:
|
||||
|
||||
```
|
||||
Prompt: "以下AI回复是否存在医疗合规问题?
|
||||
A.无问题 B.含诊断 C.含处方建议 D.含疗效承诺 E.其他违规
|
||||
只输出字母。"
|
||||
|
||||
实现:调用 erp-ai Provider(优先本地 Ollama 降成本)
|
||||
```
|
||||
|
||||
返回 A → 放行。返回 B/C/D/E → 标记违规类型 → 进入修正流程。
|
||||
|
||||
### 5.2 审查规则配置
|
||||
|
||||
合规过滤规则与风险评分规则(§3.1 的 `copilot_rules` 表)是不同的数据结构,使用独立的内存加载方式:
|
||||
|
||||
- **风险评分规则** → 存储 `copilot_rules` 表(含 condition_expr、score),通过事件驱动执行
|
||||
- **合规过滤规则** → 使用 Rust 代码内嵌的静态词表 + 可选的 `copilot_compliance_rules` 表(机构自定义扩展词)
|
||||
|
||||
机构自定义合规词表在 Phase 4 MVP 中不实现。MVP 使用代码内嵌的固定词表,覆盖 §5.1 中列出的标准违规关键词。后续版本可通过管理后台动态管理词表。
|
||||
|
||||
```json
|
||||
{
|
||||
"rule_id": "no_diagnosis",
|
||||
"category": "diagnosis",
|
||||
"severity": "critical",
|
||||
"keywords": ["确诊为", "诊断为", "你得了", "诊断结果是", "可以确诊"],
|
||||
"replacement_template": "这个情况建议让医生当面评估一下",
|
||||
"auto_fix": true
|
||||
}
|
||||
```
|
||||
|
||||
内置规则分类:CRITICAL(诊断/处方)→ 自动修正,不可跳过;HIGH(疗效/评估)→ 自动修正;MEDIUM(绝对化/误导)→ LLM 重写。
|
||||
|
||||
### 5.3 修正策略
|
||||
|
||||
三级修正,逐级升格:
|
||||
|
||||
**策略 1 — 模板替换(关键词违规,确定性高):**
|
||||
- 直接用预设安全模板替换违规片段
|
||||
- 例:"可能是高血压引起的头晕" → "头晕可能有多种原因,建议到院让医生评估"
|
||||
|
||||
**策略 2 — LLM 重写(语义违规,需理解上下文):**
|
||||
- Prompt:"将以下回复改写为合规版本,移除诊断/处方语言,改为引导到院,保持关怀语气"
|
||||
- 重写后再次过 Layer 1 审查
|
||||
|
||||
**策略 3 — 兜底降级(两次修正仍不通过):**
|
||||
- 使用预设安全模板:
|
||||
- "感谢您的提问,这个问题建议您下次来的时候直接跟医生聊聊。要不要我帮您预约?"
|
||||
|
||||
### 5.4 降级策略(AI 不可用时)
|
||||
|
||||
当 LLM 服务不可用时,合规审查降级为纯规则模式(Layer 1 only):
|
||||
|
||||
- 仅回答服务咨询类问题(预约、流程、地址等)
|
||||
- 健康类问题统一使用安全兜底模板
|
||||
- 不尝试生成个性化健康解读
|
||||
- 对话 UI 显示"小H 暂时只能回答预约和流程类问题,健康问题建议直接咨询医生"
|
||||
|
||||
### 5.5 审计追踪
|
||||
|
||||
每条患者端对话的审查记录持久化存储到 `copilot_chat_logs` 表:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| user_message | 患者原文 |
|
||||
| intent_classification | 意图分类结果 |
|
||||
| ai_raw_response | AI 原始输出(修正前) |
|
||||
| layer1_result | 关键词审查结果 |
|
||||
| layer2_result | 语义审查结果 |
|
||||
| violations_found | 违规项列表 |
|
||||
| fix_strategy | 修正策略类型 |
|
||||
| final_response | 最终发给患者的文本 |
|
||||
|
||||
**数据保留策略:**
|
||||
- 审查日志保留 3 年(医疗数据合规要求)
|
||||
- 原始输出与最终输出的对比可用于持续评估审查准确性
|
||||
- 机构可定期审查 AI 对话是否有违规漏过
|
||||
|
||||
---
|
||||
|
||||
## §6 技术设计
|
||||
|
||||
### 6.1 Crate 架构
|
||||
|
||||
扩展现有 erp-ai crate,在其内部新增 `copilot/` 子模块。不新建独立 crate。
|
||||
|
||||
理由:Copilot 的 AI 调用复用 erp-ai 的 Provider 抽象层,无需重复实现。规则引擎虽不依赖 AI,但作为子模块放在 erp-ai 内部更内聚。如未来 Copilot 发展到需要独立部署,再拆分为微服务。
|
||||
|
||||
```
|
||||
crates/erp-ai/src/
|
||||
├── copilot/ (新增)
|
||||
│ ├── mod.rs — 模块入口
|
||||
│ ├── engine.rs — 洞察调度器
|
||||
│ ├── rules.rs — 规则引擎(条件解析 + 评分)
|
||||
│ ├── scoring.rs — 混合评分(规则分 + LLM 补充)
|
||||
│ ├── intent.rs — 患者端意图识别
|
||||
│ ├── compliance.rs — 合规审查引擎
|
||||
│ └── context.rs — 对话上下文组装
|
||||
├── handler/ (新增)
|
||||
│ ├── insight_handler.rs — 洞察查询 API
|
||||
│ ├── chat_handler.rs — 患者对话 API
|
||||
│ └── risk_handler.rs — 风险评分 API
|
||||
├── entity/ (新增)
|
||||
│ ├── copilot_insights.rs
|
||||
│ ├── copilot_risk_snapshots.rs
|
||||
│ ├── copilot_chat_logs.rs
|
||||
│ └── copilot_rules.rs
|
||||
├── service/ (新增)
|
||||
│ ├── insight_service.rs
|
||||
│ ├── risk_service.rs
|
||||
│ ├── chat_service.rs
|
||||
│ └── compliance_service.rs
|
||||
└── event/ (新增)
|
||||
└── copilot_consumer.rs — 订阅 health 模块事件
|
||||
```
|
||||
|
||||
### 6.2 数据库设计
|
||||
|
||||
4 张新表,均包含标准字段(id, tenant_id, created_at, updated_at, created_by, updated_by, deleted_at, version)。
|
||||
|
||||
**跨 crate 外键策略:** copilot_insights 和 copilot_risk_snapshots 中的 `patient_id` 使用逻辑关联(无外键约束),不直接引用 erp-health 的 `patients` 表。理由:根据架构铁律"模块间只通过事件总线和 trait 通信",erp-ai 不应直接依赖 erp-health 的表结构。数据一致性通过事件驱动保证——patient.created 事件触发初始化,patient 数据变更通过事件通知。
|
||||
|
||||
**copilot_rules — 规则配置**
|
||||
|
||||
```sql
|
||||
CREATE TABLE copilot_rules (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
name VARCHAR(200) NOT NULL,
|
||||
category VARCHAR(50) NOT NULL, -- vital_signs/lab/adherence/dialysis/composite
|
||||
condition_expr JSONB NOT NULL, -- 规则条件表达式
|
||||
score SMALLINT NOT NULL, -- +1 ~ +5
|
||||
severity VARCHAR(20) NOT NULL, -- info/warning/critical
|
||||
suggestion TEXT,
|
||||
enabled BOOLEAN DEFAULT true,
|
||||
sort_order INT DEFAULT 0,
|
||||
created_at TIMESTAMPTZ DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ DEFAULT now(),
|
||||
created_by UUID, updated_by UUID,
|
||||
deleted_at TIMESTAMPTZ, version INT DEFAULT 1
|
||||
);
|
||||
```
|
||||
|
||||
**copilot_insights — 洞察存储**
|
||||
|
||||
```sql
|
||||
CREATE TABLE copilot_insights (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
patient_id UUID NOT NULL, -- 逻辑关联 patients 表,无外键约束(跨 crate)
|
||||
insight_type VARCHAR(50) NOT NULL, -- risk_score/anomaly/follow_up_hint/consult_hint
|
||||
source VARCHAR(20) NOT NULL, -- rule/llm/hybrid
|
||||
severity VARCHAR(20),
|
||||
title VARCHAR(500) NOT NULL,
|
||||
content JSONB NOT NULL,
|
||||
rule_matches JSONB,
|
||||
llm_supplement TEXT,
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
is_read BOOLEAN DEFAULT false,
|
||||
is_dismissed BOOLEAN DEFAULT false,
|
||||
created_at TIMESTAMPTZ DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ DEFAULT now(),
|
||||
created_by UUID, updated_by UUID,
|
||||
deleted_at TIMESTAMPTZ, version INT DEFAULT 1
|
||||
);
|
||||
```
|
||||
|
||||
**copilot_risk_snapshots — 风险评分快照**
|
||||
|
||||
```sql
|
||||
CREATE TABLE copilot_risk_snapshots (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
patient_id UUID NOT NULL, -- 逻辑关联 patients 表,无外键约束(跨 crate)
|
||||
risk_score SMALLINT NOT NULL, -- 0-10
|
||||
risk_level VARCHAR(20) NOT NULL, -- low/medium/high/critical
|
||||
rule_details JSONB NOT NULL,
|
||||
llm_summary TEXT,
|
||||
computed_at TIMESTAMPTZ NOT NULL,
|
||||
data_freshness JSONB,
|
||||
created_at TIMESTAMPTZ DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ DEFAULT now(),
|
||||
created_by UUID, updated_by UUID,
|
||||
deleted_at TIMESTAMPTZ, version INT DEFAULT 1
|
||||
);
|
||||
```
|
||||
|
||||
**copilot_chat_logs — 对话审查日志**
|
||||
|
||||
```sql
|
||||
CREATE TABLE copilot_chat_logs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
patient_id UUID NOT NULL,
|
||||
session_id UUID NOT NULL,
|
||||
user_message TEXT NOT NULL,
|
||||
intent_classification VARCHAR(30),
|
||||
ai_raw_response TEXT,
|
||||
layer1_result JSONB,
|
||||
layer2_result JSONB,
|
||||
violations_found JSONB,
|
||||
fix_strategy VARCHAR(30),
|
||||
final_response TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ DEFAULT now(),
|
||||
created_by UUID, updated_by UUID,
|
||||
deleted_at TIMESTAMPTZ, version INT DEFAULT 1
|
||||
);
|
||||
```
|
||||
|
||||
### 6.3 API 设计
|
||||
|
||||
**医护端 API(需 JWT 认证 + 权限码):**
|
||||
|
||||
| 方法 | 路径 | 权限码 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| GET | `/api/v1/copilot/insights` | copilot.insights.list | 查询洞察列表(支持按患者/类型/严重度过滤) |
|
||||
| GET | `/api/v1/copilot/insights/{id}` | copilot.insights.list | 获取单条洞察详情 |
|
||||
| POST | `/api/v1/copilot/insights/{id}/dismiss` | copilot.insights.manage | 标记洞察已处理/忽略 |
|
||||
| GET | `/api/v1/copilot/patients/{id}/risk` | copilot.risk.view | 获取患者风险画像 |
|
||||
| GET | `/api/v1/copilot/patients/{id}/followup-hint` | copilot.risk.view | 获取随访推荐建议 |
|
||||
| GET | `/api/v1/copilot/patients/{id}/consult-hint` | copilot.risk.view | 获取咨询辅助建议 |
|
||||
| GET | `/api/v1/copilot/rules` | copilot.rules.list | 列出规则 |
|
||||
| POST | `/api/v1/copilot/rules` | copilot.rules.manage | 创建规则 |
|
||||
| PUT | `/api/v1/copilot/rules/{id}` | copilot.rules.manage | 更新规则 |
|
||||
|
||||
**患者端 API(需患者 JWT):**
|
||||
|
||||
患者端与医护端共享同一套 JWT 认证体系(erp-auth)。小程序通过微信登录获取 token(见 `apps/miniprogram/src/services/` 的现有 auth 流程),该 token 包含 `user_id` 和 `tenant_id`。Copilot API 通过 `user_id` 关联 `patients` 表确定患者身份。
|
||||
|
||||
`copilot.chat.patient` 权限码在角色初始化时自动赋予"患者"角色(非管理后台角色),无需手动分配。
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| POST | `/api/v1/copilot/chat` | 发送消息,返回合规审查后的回复 |
|
||||
| GET | `/api/v1/copilot/chat/history` | 获取对话历史(分页) |
|
||||
| GET | `/api/v1/copilot/chat/daily-greeting` | 获取今日个性化问候 |
|
||||
|
||||
**权限码汇总:**
|
||||
|
||||
```
|
||||
copilot.insights.list — 查看洞察列表
|
||||
copilot.insights.manage — 处理/忽略洞察
|
||||
copilot.risk.view — 查看风险画像
|
||||
copilot.rules.list — 查看规则
|
||||
copilot.rules.manage — 管理规则
|
||||
copilot.chat.patient — 患者端对话(患者角色自带)
|
||||
```
|
||||
|
||||
### 6.4 事件订阅
|
||||
|
||||
Copilot 引擎订阅以下 erp-health 模块事件。事件名称已对齐 `crates/erp-health/src/event/mod.rs` 中的实际常量:
|
||||
|
||||
**已有事件(可直接订阅):**
|
||||
|
||||
| 事件常量 | 触发动作 |
|
||||
|---------|---------|
|
||||
| `daily_monitoring.created` | 体征数据录入 → 异常检测 + 风险评分刷新 |
|
||||
| `lab_report.reviewed` | 化验报告审核 → 化验异常检测 + 风险评分刷新 |
|
||||
| `follow_up.completed` | 随访完成 → 更新随访依从性 |
|
||||
| `follow_up.overdue` | 随访失约 → 风险评分 +1 + 生成告警 |
|
||||
| `patient.created` | 患者建档 → 初始化风险基线 |
|
||||
|
||||
**需新增事件(在 erp-health 中添加发布点):**
|
||||
|
||||
| 事件常量 | 发布时机 | 触发动作 |
|
||||
|---------|---------|---------|
|
||||
| `appointment.completed` | 预约状态变为已完成 | 更新依从性数据 + 风险评分刷新 |
|
||||
|
||||
事件消费流程:EventBus 收到事件 → copilot_consumer 匹配事件类型 → 调用规则引擎评估 → 生成/更新洞察 → 写入 copilot_insights → 如有异常告警则通知医护端。
|
||||
|
||||
### 6.5 前端组件设计
|
||||
|
||||
**医护端 React 组件(apps/web/src/components/Copilot/):**
|
||||
|
||||
```
|
||||
├── CopilotBadge.tsx — 风险等级徽章(嵌入患者列表/详情页)
|
||||
├── CopilotCard.tsx — 洞察卡片(风险画像/异常告警)
|
||||
├── CopilotAlert.tsx — 告警通知(仪表盘/首页)
|
||||
├── CopilotPanel.tsx — 侧边栏面板(随访推荐/咨询辅助)
|
||||
├── CopilotInsightList.tsx — 洞察列表页
|
||||
└── hooks/
|
||||
├── useCopilotInsights.ts — 洞察数据 hook
|
||||
└── useCopilotRisk.ts — 风险评分 hook
|
||||
```
|
||||
|
||||
**患者端小程序组件(apps/miniprogram/src/pages/copilot/):**
|
||||
|
||||
```
|
||||
├── index.tsx — 对话主页
|
||||
└── components/
|
||||
├── ChatBubble.tsx — 消息气泡
|
||||
├── QuickActions.tsx — 快捷入口
|
||||
└── InputBar.tsx — 输入框
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## §7 实施阶段
|
||||
|
||||
总体策略:自下而上,每个阶段交付可验证的价值。
|
||||
|
||||
### Phase 0:基础设施(地基)
|
||||
|
||||
**目标:** 搭建 Copilot 引擎骨架,让它能"跑起来"
|
||||
|
||||
| 任务 | 产出 | 依赖 |
|
||||
|------|------|------|
|
||||
| 数据库迁移 | 4 个迁移文件(copilot_rules, copilot_insights, copilot_risk_snapshots, copilot_chat_logs) | 无 |
|
||||
| 规则引擎核心 | `copilot/rules.rs` — 条件解析 + 评分计算 | 无 |
|
||||
| 洞察存储 CRUD | `insight_service.rs` — 写入/查询/过期清理 | 迁移文件 |
|
||||
| 风险评分基础框架 | `scoring.rs` — 规则评分逻辑(纯规则,无 LLM) | 规则引擎 |
|
||||
| Copilot API 骨架 | 3 个 handler + 路由注册 | 洞察存储 |
|
||||
| 预置规则种子数据 | 10-15 条内置规则(体征/化验/依从性) | 迁移文件 |
|
||||
|
||||
**验收标准:**
|
||||
- `cargo check` 通过
|
||||
- 内置规则对患者数据跑通评分逻辑
|
||||
- API 可查询风险评分和洞察列表
|
||||
|
||||
### Phase 1:医护端风险画像(第一个可见价值)
|
||||
|
||||
**目标:** 医护打开患者档案时,能看到 Copilot 风险徽章和洞察卡片
|
||||
|
||||
| 任务 | 产出 | 依赖 |
|
||||
|------|------|------|
|
||||
| 事件消费者 | `copilot_consumer.rs` — 订阅体征/化验事件 | Phase 0 |
|
||||
| 风险评分刷新(异步) | 事件触发 → 规则评估 → 写入风险快照 | 事件消费者 |
|
||||
| LLM 补充分析集成 | 规则评分结果 → 调用 erp-ai → LLM 补充洞察 | erp-ai Provider |
|
||||
| 前端 CopilotBadge | 患者列表/详情页风险徽章 | API |
|
||||
| 前端 CopilotCard | 可展开的洞察卡片 | API |
|
||||
| 每日风险快照批量刷新 | 定时任务:凌晨重算所有在管患者 | 评分逻辑 |
|
||||
|
||||
**验收标准:**
|
||||
- 录入新体征数据后,风险评分自动更新
|
||||
- 医护端患者详情页显示风险徽章和洞察卡片
|
||||
- LLM 补充分析正常返回(非阻塞,失败降级为纯规则)
|
||||
|
||||
### Phase 2:异常检测 + 告警推送
|
||||
|
||||
**目标:** 健康数据入库时自动检测异常,推送告警给医护
|
||||
|
||||
| 任务 | 产出 | 依赖 |
|
||||
|------|------|------|
|
||||
| 异常检测规则扩展 | 新增趋势类/复合类规则 | Phase 1 |
|
||||
| 告警洞察生成 | 异常 → 生成洞察 → 推送通知 | 事件消费者 |
|
||||
| 前端 CopilotAlert | 仪表盘告警卡片 + 浏览器通知 | API |
|
||||
| 告警处理工作流 | 标记已处理 / 忽略 / 升级 | API |
|
||||
|
||||
**验收标准:**
|
||||
- 危急值(如血钾 >6.0)入库后秒级生成告警
|
||||
- 医护仪表盘显示分级告警列表
|
||||
- 告警可标记处理状态
|
||||
|
||||
### Phase 3:随访推荐 + 咨询辅助
|
||||
|
||||
**目标:** Copilot 在医护创建随访/咨询时提供智能建议
|
||||
|
||||
| 任务 | 产出 | 依赖 |
|
||||
|------|------|------|
|
||||
| 随访推荐逻辑 | 基于风险画像 + 疾病模板 → 推荐随访方案 | Phase 1 风险数据 |
|
||||
| 咨询辅助逻辑 | 患者档案 + 对话内容 → 追问建议 | Phase 1 + 对话上下文 |
|
||||
| 前端 CopilotPanel | 随访/咨询页面侧边栏 | API |
|
||||
| 一键采纳/插入 | 医护可将建议直接填入表单 | 前端组件 |
|
||||
|
||||
**验收标准:**
|
||||
- 创建随访计划时 Copilot 面板显示个性化建议
|
||||
- 咨询对话时侧边栏显示患者背景和追问建议
|
||||
- 建议可一键插入到表单/回复框
|
||||
|
||||
### Phase 4:患者端 Copilot(AI 客服/管家)
|
||||
|
||||
**目标:** 患者小程序内可与小H对话,合规解答、引导到院
|
||||
|
||||
| 任务 | 产出 | 依赖 |
|
||||
|------|------|------|
|
||||
| 意图识别引擎 | `intent.rs` — 5 类意图分类 | erp-ai Provider |
|
||||
| 合规审查引擎 | `compliance.rs` — 双层审查 + 自动修正 | 意图识别 |
|
||||
| 对话上下文组装 | `context.rs` — 患者数据 + 对话历史自动注入 | 风险画像数据 |
|
||||
| 对话 API | `chat_handler.rs` — 消息收发 + 审查 | 合规引擎 |
|
||||
| 小程序对话页面 | 聊天 UI + 快捷入口 | API |
|
||||
| 每日问候生成 | 基于患者数据的个性化推送 | 风险画像 + 定时任务 |
|
||||
| 审计日志 | 对话审查记录持久化 | 合规引擎 |
|
||||
|
||||
**验收标准:**
|
||||
- 患者可发送消息,获得合规审查后的回复
|
||||
- 诊断性/处方性提问被自动修正为引导到院
|
||||
- 对话记录完整可审计
|
||||
- 每日个性化问候正常推送
|
||||
|
||||
### Phase 5:日活引擎(小程序游戏化)
|
||||
|
||||
**目标:** 积分体系 + AI 问候驱动患者日常互动
|
||||
|
||||
| 任务 | 产出 | 依赖 |
|
||||
|------|------|------|
|
||||
| 每日任务系统 | 打卡/录入/阅读任务定义 + 积分规则 | Phase 4 |
|
||||
| 积分经济扩展 | 分层兑换:服务特权 + 实物商品 | 现有积分模块 |
|
||||
| 连续打卡 + 勋章 | streak 追踪 + 成就系统 | 任务系统 |
|
||||
| AI 问候与任务联动 | 问候消息嵌入任务入口 | Phase 4 + 任务系统 |
|
||||
| 小程序首页改版 | 任务入口 + 积分展示 + AI 问候卡片 | 前端 |
|
||||
|
||||
**验收标准:**
|
||||
- 患者每日可完成健康任务获得积分
|
||||
- 积分可兑换服务特权/实物商品
|
||||
- 连续打卡有加成奖励
|
||||
- AI 问候与当日任务关联
|
||||
|
||||
### 阶段依赖总览
|
||||
|
||||
```
|
||||
Phase 0(基础设施)
|
||||
│
|
||||
▼
|
||||
Phase 1(风险画像)← 第一个可见价值
|
||||
│
|
||||
├─────────────────┐
|
||||
▼ ▼
|
||||
Phase 2(异常检测) Phase 3(随访/咨询辅助)
|
||||
│ │
|
||||
└────────┬────────┘
|
||||
▼
|
||||
Phase 4(患者端 Copilot)← 合规 AI 客服
|
||||
│
|
||||
▼
|
||||
Phase 5(日活引擎)← 游戏化闭环
|
||||
```
|
||||
|
||||
### 预估工作量
|
||||
|
||||
| 阶段 | 后端 | 前端 | 数据库 | 核心挑战 |
|
||||
|------|------|------|--------|---------|
|
||||
| Phase 0 | 5-7 天 | — | 2 天 | 规则引擎的条件表达式设计 |
|
||||
| Phase 1 | 5-7 天 | 3-5 天 | — | LLM 集成的稳定性与降级 |
|
||||
| Phase 2 | 3-5 天 | 2-3 天 | — | 告警分级与推送策略 |
|
||||
| Phase 3 | 5-7 天 | 3-5 天 | — | 随访模板与疾病类型的映射 |
|
||||
| Phase 4 | 7-10 天 | 5-7 天 | 1 天 | 合规审查的准确性与延迟 |
|
||||
| Phase 5 | 5-7 天 | 5-7 天 | 1 天 | 积分经济平衡 |
|
||||
Reference in New Issue
Block a user