docs(health-profile): 添加防编造加固修订记录到导出健康档案设计文档 补充了关于导出摘要出现虚构病例问题的详细分析和修复方案, 包括检索策略优化、空数据兜底处理和prompt重写等三层防护措施。 ```
118 lines
5.4 KiB
Swift
118 lines
5.4 KiB
Swift
import Foundation
|
|
|
|
/// 「导出身体档案」用到的两个 LLM prompt:
|
|
/// 1. `intentExtraction` —— 抽取时间窗 + 指标/症状关键词,只输出 JSON
|
|
/// 2. `reportGeneration` —— 拼真实数据后生成给医生看的 Markdown
|
|
///
|
|
/// 解析逻辑见 `HealthExportService`(§3.2 失败回退红线:
|
|
/// 抽不出 JSON → 用 30 天 + 空关键词兜底,流程不中断)。
|
|
enum HealthExportPrompts {
|
|
|
|
// MARK: - 意图抽取
|
|
|
|
/// `intentExtraction(userPrompt:)` 把用户原话拼到模板末尾。
|
|
/// 期望输出形如:
|
|
/// ```json
|
|
/// {"time_range_days":30,
|
|
/// "keywords":["体温","血压"],
|
|
/// "symptom_keywords":["感冒","咳嗽"],
|
|
/// "intent":"cold_consult",
|
|
/// "intent_label_cn":"感冒就诊"}
|
|
/// ```
|
|
static func intentExtraction(userPrompt: String) -> String {
|
|
"""
|
|
你是健康数据助手。读用户的请求,只输出严格 JSON,不要解释、不要 markdown 围栏、不要任何前后缀文字。
|
|
|
|
字段说明(全部必填):
|
|
{
|
|
"time_range_days": int, // 回溯天数,默认 30,最大 365
|
|
"keywords": [string], // 指标关键词(中文,如「血压」「血糖」「体温」「肝功」),无则 []
|
|
"symptom_keywords": [string], // 症状关键词,无则 []
|
|
"intent": string, // 英文 snake_case 标签,如 "cold_consult"
|
|
"intent_label_cn": string // 中文短语,会作为报告标题副题,如 "感冒就诊"
|
|
}
|
|
|
|
规则:
|
|
- 时间未指定 → 30
|
|
- 「最近一个月」→ 30,「最近三个月」→ 90,「最近半年」→ 180
|
|
- 关键词要中文,常见健康指标 / 症状词
|
|
- intent 简短,4-25 字符,小写下划线
|
|
|
|
示例 1:
|
|
User: 我感冒3天了,要把最近一个月的健康情况给医生看
|
|
Output: {"time_range_days":30,"keywords":["体温","血压","脉搏"],"symptom_keywords":["感冒","咳嗽","咽喉痛","发烧"],"intent":"cold_consult","intent_label_cn":"感冒就诊"}
|
|
|
|
示例 2:
|
|
User: 我最近血糖好像不稳,把上次体检前后的化验单整理一下
|
|
Output: {"time_range_days":90,"keywords":["血糖","糖化血红蛋白","胰岛素"],"symptom_keywords":[],"intent":"glucose_review","intent_label_cn":"血糖复查"}
|
|
|
|
现在请输出 JSON:
|
|
User: \(userPrompt)
|
|
Output: /no_think
|
|
"""
|
|
}
|
|
|
|
// MARK: - 报告生成
|
|
|
|
/// `reportGeneration(userPrompt:intentLabelCN:dataJSON:)` 拼好后流式生成 Markdown。
|
|
static func reportGeneration(userPrompt: String,
|
|
intentLabelCN: String,
|
|
dataJSON: String) -> String {
|
|
let labelLine = intentLabelCN.isEmpty
|
|
? "# 就诊摘要"
|
|
: "# 就诊摘要 — \(intentLabelCN)"
|
|
return """
|
|
你是健康数据整理员。任务是把下面【真实数据】(JSON)里**已经存在**的内容,
|
|
原样整理成一份给社区医生看的就诊摘要。这是**抽取 / 搬运**任务,不是创作。
|
|
|
|
【最重要的铁律 —— 违反即失败】
|
|
- 只能使用【真实数据】JSON 里**真实出现过**的内容。
|
|
- 严禁编造或推测任何数字、日期、症状、药物、检查结果、诊断,哪怕看起来很合理。
|
|
- JSON 里没有的信息,对应小节一律写「无记录」,不要补全、不要举例、不要套用常见病例模板。
|
|
- 数值必须原样照搬(含单位与参考范围);status 为 high/low/abnormal 的指标前加 ⚠️。
|
|
- 「主诉」「患者疑问」可参考【患者原话】,但不得加入原话与数据里都没有的症状。
|
|
|
|
输出格式:
|
|
- 严格 Markdown,标题用 # / ##,不要 markdown 围栏,不要输出 JSON,不写「数据」二字。
|
|
- 不给诊断意见、用药建议或「建议就医」。全文中文,简洁,医生 30 秒能扫完。
|
|
- 严格按以下 6 段(顺序与标题固定):
|
|
\(labelLine)
|
|
## 主诉
|
|
## 患者背景
|
|
## 近期症状(按时间倒序)
|
|
## 关键指标(异常项优先)
|
|
## 在服药与过敏
|
|
## 患者疑问
|
|
|
|
—— 格式示例(只示范「无记录」与数值写法,内容请勿照抄)——
|
|
真实数据:{"profile":{},"symptoms":[],"indicators":[{"name":"体温","value":"38.5","unit":"℃","range":"36-37.2","status":"high","date":"2026-05-01"}],"reports":[],"diaries":[],"time_window":{"from":"2026-04-02","to":"2026-05-02"}}
|
|
输出:
|
|
# 就诊摘要 — 近期健康摘要
|
|
## 主诉
|
|
无记录
|
|
## 患者背景
|
|
无记录
|
|
## 近期症状(按时间倒序)
|
|
无记录
|
|
## 关键指标(异常项优先)
|
|
⚠️ 体温 38.5 ℃(参考 36-37.2,2026-05-01)
|
|
## 在服药与过敏
|
|
无记录
|
|
## 患者疑问
|
|
无记录
|
|
—— 示例结束(以上咳嗽/体温等仅示范格式,切勿出现在你的输出里)——
|
|
|
|
现在,严格根据下面这份【真实数据】生成;数据里没有的就写「无记录」,**禁止编造**:
|
|
|
|
【真实数据】:
|
|
\(dataJSON)
|
|
|
|
【患者原话】:\(userPrompt)
|
|
|
|
再次强调:只整理上面【真实数据】里真实出现过的内容,禁止编造任何数字/日期/症状/药物。
|
|
直接输出 Markdown,不要思考过程,不要 <think> 标签:
|
|
/no_think
|
|
"""
|
|
}
|
|
}
|