feat(ai+db): 趋势分析 prompt 升级为结构化统计摘要
- 新增迁移 000093:更新 health_trend_analysis prompt,使用统计字段 替代原始数据点遍历,引导 AI 专注 slope/R²/异常点分析 - erp-ai handler: stream_trends 改用 get_trend_analysis_data() 替代 get_vital_signs(),传递预计算趋势特征 - sanitizer: 新增 sanitize_trend_analysis() 方法
This commit is contained in:
@@ -104,11 +104,11 @@ where
|
||||
end: chrono::Utc::now(),
|
||||
};
|
||||
|
||||
let vital_dtos = state
|
||||
let trend_data = state
|
||||
.health_provider
|
||||
.get_vital_signs(ctx.tenant_id, patient_id, &metrics, &range)
|
||||
.get_trend_analysis_data(ctx.tenant_id, patient_id, &metrics, &range)
|
||||
.await?;
|
||||
let sanitized_data = state.analysis.sanitizer.sanitize_vital_signs(&vital_dtos)?;
|
||||
let sanitized_data = state.analysis.sanitizer.sanitize_trend_analysis(&trend_data)?;
|
||||
|
||||
let prompt = state
|
||||
.prompt
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use erp_core::health_provider::{
|
||||
HealthReportDto, LabReportDto, PatientSummaryDto, VitalSignDto,
|
||||
HealthReportDto, LabReportDto, PatientSummaryDto, TrendAnalysisDto, VitalSignDto,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
||||
@@ -43,6 +43,13 @@ impl SanitizationService {
|
||||
Ok(sanitized)
|
||||
}
|
||||
|
||||
pub fn sanitize_trend_analysis(&self, data: &TrendAnalysisDto) -> AiResult<Value> {
|
||||
let sanitized = serde_json::to_value(data)
|
||||
.map_err(|e| AiError::SanitizationError(format!("序列化失败: {e}")))?;
|
||||
self.verify_no_pii(&sanitized)?;
|
||||
Ok(sanitized)
|
||||
}
|
||||
|
||||
/// 二次验证: 确保没有意外泄漏的 PII
|
||||
fn verify_no_pii(&self, value: &Value) -> AiResult<()> {
|
||||
let pii_keys = [
|
||||
|
||||
@@ -92,6 +92,7 @@ mod m20260428_000089_blind_indexes;
|
||||
mod m20260428_000090_critical_alerts;
|
||||
mod m20260428_000091_dead_letter_events;
|
||||
mod m20260429_000092_device_readings_metric;
|
||||
mod m20260429_000093_trend_analysis_prompt_v2;
|
||||
|
||||
pub struct Migrator;
|
||||
|
||||
@@ -191,6 +192,7 @@ impl MigratorTrait for Migrator {
|
||||
Box::new(m20260428_000090_critical_alerts::Migration),
|
||||
Box::new(m20260428_000091_dead_letter_events::Migration),
|
||||
Box::new(m20260429_000092_device_readings_metric::Migration),
|
||||
Box::new(m20260429_000093_trend_analysis_prompt_v2::Migration),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
//! 更新 health_trend_analysis prompt 模板为结构化统计摘要版本
|
||||
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[derive(DeriveMigrationName)]
|
||||
pub struct Migration;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
let db = manager.get_connection();
|
||||
|
||||
// 更新系统提示词 — 强调使用统计摘要而非原始数据
|
||||
let system_prompt = r#"你是一名健康数据分析专家。你将收到经过预处理的结构化统计摘要数据,包括线性回归趋势、异常检测结果等。
|
||||
|
||||
要求:
|
||||
1. **趋势判断** — 基于回归斜率(slope)和R²判断指标趋势(上升/下降/稳定),注意R²较低时趋势不确定性大
|
||||
2. **异常预警** — 重点分析被检测为异常的数据点,说明偏离程度和可能的原因
|
||||
3. **综合分析** — 考虑各指标间的关联性(如血压和体重、血糖和心率)
|
||||
4. **临床建议** — 给出切实可行的健康管理建议,不替代医生诊断
|
||||
5. **风险评级** — 对整体健康风险给出低/中/高评估并说明理由
|
||||
6. **关注重点** — 用简洁的语言总结最需要关注的 2-3 个问题"#;
|
||||
|
||||
// 更新用户提示词模板 — 使用结构化统计字段
|
||||
let user_template = r#"请分析以下患者的健康趋势数据:
|
||||
|
||||
分析周期:{{period_start}} 至 {{period_end}}
|
||||
|
||||
## 各指标趋势分析
|
||||
|
||||
{{#each metrics}}
|
||||
### {{metric}}({{unit}})
|
||||
- 数据点数:{{data_point_count}}
|
||||
{{#if regression}}
|
||||
- **趋势方向**:{{regression.direction}}(斜率:{{regression.slope}},R²:{{regression.r_squared}})
|
||||
- **日变化量**:{{regression.daily_change}} {{unit}}/天
|
||||
- **周期变化**:{{regression.period_change}} {{unit}}({{regression.daily_change}} × 天数)
|
||||
{{else}}
|
||||
- 数据不足,无法计算回归趋势
|
||||
{{/if}}
|
||||
{{#if anomalies.length}}
|
||||
- **异常数据点**:
|
||||
{{#each anomalies}}
|
||||
- {{date}}:值 {{value}} {{../unit}}(均值 {{mean}},偏离 {{deviation}} 个标准差)
|
||||
{{/each}}
|
||||
{{else}}
|
||||
- 未检测到显著异常
|
||||
{{/if}}
|
||||
|
||||
{{/each}}
|
||||
|
||||
请基于以上统计摘要,给出详细的趋势分析报告。"#;
|
||||
|
||||
let esc_sys = esc(system_prompt);
|
||||
let esc_tpl = esc(user_template);
|
||||
|
||||
// 更新已有的 health_trend_analysis prompt(version 升级到 2)
|
||||
db.execute(sea_orm::Statement::from_string(
|
||||
sea_orm::DatabaseBackend::Postgres,
|
||||
format!(
|
||||
"UPDATE ai_prompt SET system_prompt = '{esc_sys}', user_prompt_template = '{esc_tpl}', version = version + 1, updated_at = NOW() WHERE name = 'health_trend_analysis' AND is_active = true"
|
||||
),
|
||||
))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
// 回滚到原始 prompt(保持与 000082 seed 一致)
|
||||
let db = manager.get_connection();
|
||||
|
||||
let system_prompt = "你是一名健康数据分析专家。请根据提供的生命体征趋势数据,分析患者的健康变化趋势。\n\n要求:\n1. 识别数据中的关键趋势\n2. 对异常趋势提出预警\n3. 结合各指标间的关联性进行综合分析\n4. 给出健康管理建议";
|
||||
|
||||
let user_template = "以下是患者的生命体征趋势数据:\n\n患者年龄段:{{age_group}},性别:{{sex}}\n\n监测指标:\n{{#each metrics}}\n### {{name}}({{unit}})\n数据点:{{#each values}}{{this.[0]}}: {{this.[1]}}{{#unless @last}}, {{/unless}}{{/each}}\n{{/each}}\n\n请分析以上健康趋势数据。";
|
||||
|
||||
let esc_sys = esc(system_prompt);
|
||||
let esc_tpl = esc(user_template);
|
||||
|
||||
db.execute(sea_orm::Statement::from_string(
|
||||
sea_orm::DatabaseBackend::Postgres,
|
||||
format!(
|
||||
"UPDATE ai_prompt SET system_prompt = '{esc_sys}', user_prompt_template = '{esc_tpl}', version = version + 1, updated_at = NOW() WHERE name = 'health_trend_analysis' AND is_active = true"
|
||||
),
|
||||
))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn esc(s: &str) -> String {
|
||||
s.replace('\'', "''")
|
||||
}
|
||||
Reference in New Issue
Block a user