fix(ai): AI 分析预校验 + prompt 非对话化
- 四个 SSE 端点增加数据完整性校验:items/sections 为空时返回 400 - 迁移 000123 更新全部 prompt system_prompt:明确非对话、输出结构化结果 - 前端用户看到的是分析结论,不再收到"请补充数据"的对话式回复
This commit is contained in:
@@ -42,6 +42,13 @@ where
|
||||
.health_provider
|
||||
.get_lab_report(ctx.tenant_id, report_id)
|
||||
.await?;
|
||||
|
||||
if lab_dto.items.is_empty() {
|
||||
return Err(erp_core::error::AppError::Validation(
|
||||
"化验报告缺少检查项目数据,无法进行 AI 分析。请先录入完整的化验指标。".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let sanitized_data = state.analysis.sanitizer.sanitize_lab_report(&lab_dto)?;
|
||||
|
||||
let prompt = state
|
||||
@@ -112,6 +119,13 @@ where
|
||||
.health_provider
|
||||
.get_trend_analysis_data(ctx.tenant_id, patient_id, &metrics, &range)
|
||||
.await?;
|
||||
|
||||
if trend_data.metrics.is_empty() {
|
||||
return Err(erp_core::error::AppError::Validation(
|
||||
"患者在选定时间段内无体征监测数据,无法进行趋势分析。".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let sanitized_data = state.analysis.sanitizer.sanitize_trend_analysis(&trend_data)?;
|
||||
|
||||
let prompt = state
|
||||
@@ -223,6 +237,13 @@ where
|
||||
.health_provider
|
||||
.get_full_report(ctx.tenant_id, report_id)
|
||||
.await?;
|
||||
|
||||
if report_dto.sections.is_empty() {
|
||||
return Err(erp_core::error::AppError::Validation(
|
||||
"健康报告缺少内容数据,无法生成摘要。请先完善报告内容。".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let sanitized_data = state
|
||||
.analysis
|
||||
.sanitizer
|
||||
|
||||
@@ -122,6 +122,7 @@ mod m20260505_000119_enable_pgvector;
|
||||
mod m20260505_000120_create_ai_knowledge_rules;
|
||||
mod m20260505_000121_create_ai_knowledge_references;
|
||||
mod m20260505_000122_create_ai_knowledge_guides;
|
||||
mod m20260505_000123_update_ai_prompts_system_instruction;
|
||||
|
||||
pub struct Migrator;
|
||||
|
||||
@@ -251,6 +252,7 @@ impl MigratorTrait for Migrator {
|
||||
Box::new(m20260505_000120_create_ai_knowledge_rules::Migration),
|
||||
Box::new(m20260505_000121_create_ai_knowledge_references::Migration),
|
||||
Box::new(m20260505_000122_create_ai_knowledge_guides::Migration),
|
||||
Box::new(m20260505_000123_update_ai_prompts_system_instruction::Migration),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
//! 更新所有 AI 分析 prompt 的 system_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 sys_lab = esc(r#"你是一名专业的医学检验解读助手。这是由健康管理系统自动触发的分析任务,不是对话。
|
||||
|
||||
请直接输出结构化的分析结果,格式如下:
|
||||
|
||||
## 指标分析
|
||||
逐项列出每个指标的数值、是否在正常范围、简要说明。
|
||||
|
||||
## 异常指标
|
||||
列出所有异常指标,说明可能的原因(不作为诊断)。
|
||||
|
||||
## 健康建议
|
||||
给出 2-3 条切实可行的后续行动建议。
|
||||
|
||||
## 总体评估
|
||||
用一句话总结健康状况。
|
||||
|
||||
要求:
|
||||
1. 直接输出结果,不要寒暄或询问
|
||||
2. 使用通俗易懂的语言
|
||||
3. 异常指标要重点标注"#);
|
||||
|
||||
// 趋势分析 — 保持已有的 v2 格式,只加非对话指令前缀
|
||||
let sys_trend = esc(r#"你是一名健康数据分析专家。你将收到经过预处理的结构化统计摘要数据,包括线性回归趋势、异常检测结果等。
|
||||
这是由健康管理系统自动触发的分析任务,不是对话。请直接输出结构化的分析结果。
|
||||
|
||||
要求:
|
||||
1. **趋势判断** — 基于回归斜率(slope)和R²判断指标趋势(上升/下降/稳定),注意R²较低时趋势不确定性大
|
||||
2. **异常预警** — 重点分析被检测为异常的数据点,说明偏离程度和可能的原因
|
||||
3. **综合分析** — 考虑各指标间的关联性(如血压和体重、血糖和心率)
|
||||
4. **临床建议** — 给出切实可行的健康管理建议,不替代医生诊断
|
||||
5. **风险评级** — 对整体健康风险给出低/中/高评估并说明理由
|
||||
6. **关注重点** — 用简洁的语言总结最需要关注的 2-3 个问题"#);
|
||||
|
||||
// 体检方案 — 加非对话指令
|
||||
let sys_checkup = esc(r#"你是一名健康管理顾问。这是由健康管理系统自动触发的分析任务,不是对话。
|
||||
请直接输出个性化的体检方案,格式如下:
|
||||
|
||||
## 推荐检查项目
|
||||
按优先级列出,每项说明目的、建议频率。
|
||||
|
||||
## 重点关注的健康风险
|
||||
基于患者情况列出 2-3 个需要特别关注的健康风险。
|
||||
|
||||
## 生活方式建议
|
||||
给出 3-5 条切实可行的日常健康管理建议。
|
||||
|
||||
要求:
|
||||
1. 直接输出结果,不要寒暄或询问
|
||||
2. 基于患者年龄、性别、既往病史推荐
|
||||
3. 按优先级排序"#);
|
||||
|
||||
// 报告摘要 — 加非对话指令
|
||||
let sys_summary = esc(r#"你是一名医疗报告摘要撰写专家。这是由健康管理系统自动触发的分析任务,不是对话。
|
||||
请直接输出结构化的报告摘要,格式如下:
|
||||
|
||||
## 关键发现
|
||||
列出报告中的主要发现。
|
||||
|
||||
## 异常项目
|
||||
列出所有异常项目及其严重程度。
|
||||
|
||||
## 结论
|
||||
简明的总体结论。
|
||||
|
||||
## 行动建议
|
||||
具体的后续步骤建议。
|
||||
|
||||
要求:
|
||||
1. 直接输出结果,不要寒暄或询问
|
||||
2. 控制在 500 字以内
|
||||
3. 语言简洁专业"#);
|
||||
|
||||
for (name, sys) in [
|
||||
("lab_report_interpretation", sys_lab),
|
||||
("health_trend_analysis", sys_trend),
|
||||
("personalized_checkup_plan", sys_checkup),
|
||||
("report_summary_generation", sys_summary),
|
||||
] {
|
||||
db.execute(sea_orm::Statement::from_string(
|
||||
sea_orm::DatabaseBackend::Postgres,
|
||||
format!(
|
||||
"UPDATE ai_prompt SET system_prompt = '{sys}', version = version + 1, updated_at = NOW() WHERE name = '{name}' AND is_active = true"
|
||||
),
|
||||
))
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
// prompt 更新不做回滚,保持最新版本
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn esc(s: &str) -> String {
|
||||
s.replace('\'', "''")
|
||||
}
|
||||
Reference in New Issue
Block a user