根据提供的code differences信息,由于没有具体的代码变更内容,我将生成一个通用的commit message模板:
``` docs(readme): 更新文档说明 - 添加了项目使用说明 - 完善了配置项描述 - 修正了示例代码格式 ``` 注意:由于您提供的code differences部分为空,无法获取具体的代码变更信息来生成准确的commit message。请提供实际的代码差异信息以便生成更精确的提交消息。
This commit is contained in:
@@ -22,6 +22,7 @@ enum ConsultationPrompts {
|
|||||||
|
|
||||||
硬性规则:
|
硬性规则:
|
||||||
- 这只是**转写整理**:只复述录音里【确实说过】的内容,【绝对不许】自己新增诊断、用药、剂量、频次或「建议就医」。
|
- 这只是**转写整理**:只复述录音里【确实说过】的内容,【绝对不许】自己新增诊断、用药、剂量、频次或「建议就医」。
|
||||||
|
- 输出语言必须与【录音转写原话】一致:原话是中文就用中文,是英文就用英文,对应语言不翻译(小节名可保留中文标签)。
|
||||||
- 数值、单位、药名、剂量、时间【一字不改】——原话说 140/90 就写 140/90,说一天两次就写一天两次。
|
- 数值、单位、药名、剂量、时间【一字不改】——原话说 140/90 就写 140/90,说一天两次就写一天两次。
|
||||||
- 录音里没提到的小节就【整节省略】,不要写「无」「未提及」之类占位,也不要硬凑。
|
- 录音里没提到的小节就【整节省略】,不要写「无」「未提及」之类占位,也不要硬凑。
|
||||||
- 区分说话人:本人的主诉用第一人称「我」;医生说的话写成「医生:…」。
|
- 区分说话人:本人的主诉用第一人称「我」;医生说的话写成「医生:…」。
|
||||||
|
|||||||
@@ -11,11 +11,31 @@ enum DiaryAssistPrompts {
|
|||||||
|
|
||||||
/// 固定问诊维度清单。模型每条问题必须正好归属其中一个;UI 累积已覆盖维度回传下一轮。
|
/// 固定问诊维度清单。模型每条问题必须正好归属其中一个;UI 累积已覆盖维度回传下一轮。
|
||||||
/// 顺序即展示/示例顺序,改动需同步 few-shot。
|
/// 顺序即展示/示例顺序,改动需同步 few-shot。
|
||||||
|
/// 注意:这是**受控词表 + 去重键**,模型固定输出中文维度名(prompt 已约束),
|
||||||
|
/// 内部匹配/去重都按中文原名进行,只在**展示**时经 `localizedDimension` 翻成界面语言。
|
||||||
static let dimensions: [String] = [
|
static let dimensions: [String] = [
|
||||||
"起病诱因", "症状性质", "伴随症状", "加重缓解",
|
"起病诱因", "症状性质", "伴随症状", "加重缓解",
|
||||||
"持续频率", "既往家族史", "用药过敏", "生活方式",
|
"持续频率", "既往家族史", "用药过敏", "生活方式",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
/// 把中文维度名翻成界面语言,仅用于 UI 展示(如追问输入框的占位提示)。
|
||||||
|
/// 走 `String(appLoc:)` 查表:这些维度名已作为 key 收进 Localizable.xcstrings。
|
||||||
|
/// 词表外的自定义 dim(模型偶发)原样返回,不至于丢字。
|
||||||
|
nonisolated static func localizedDimension(_ dim: String) -> String {
|
||||||
|
let d = dim.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
switch d {
|
||||||
|
case "起病诱因": return String(appLoc: "起病诱因")
|
||||||
|
case "症状性质": return String(appLoc: "症状性质")
|
||||||
|
case "伴随症状": return String(appLoc: "伴随症状")
|
||||||
|
case "加重缓解": return String(appLoc: "加重缓解")
|
||||||
|
case "持续频率": return String(appLoc: "持续频率")
|
||||||
|
case "既往家族史": return String(appLoc: "既往家族史")
|
||||||
|
case "用药过敏": return String(appLoc: "用药过敏")
|
||||||
|
case "生活方式": return String(appLoc: "生活方式")
|
||||||
|
default: return d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// - content: 用户当前全文。
|
/// - content: 用户当前全文。
|
||||||
/// - coveredDimensions: 之前各轮已经问过(或记录里已写明)的维度名,本轮必须避开。
|
/// - coveredDimensions: 之前各轮已经问过(或记录里已写明)的维度名,本轮必须避开。
|
||||||
/// 第一轮传空数组。
|
/// 第一轮传空数组。
|
||||||
@@ -72,7 +92,8 @@ enum DiaryAssistPrompts {
|
|||||||
本轮可选维度:\(allowedLine)
|
本轮可选维度:\(allowedLine)
|
||||||
【最新记录】:
|
【最新记录】:
|
||||||
\(content)
|
\(content)
|
||||||
|
\(aiOutputLanguageDirective())
|
||||||
|
(注意:dim 维度名仍固定用上面清单里的中文原名;只有 q 和 fill 面向用户的文字用上述语言。)
|
||||||
Output: /no_think
|
Output: /no_think
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
@@ -94,6 +115,7 @@ enum DiaryAssistPrompts {
|
|||||||
|
|
||||||
硬性规则:
|
硬性规则:
|
||||||
- 【绝对不许】增加、删除或改动任何数值、单位、药名、时间——原话说 140/90 就必须写 140/90。
|
- 【绝对不许】增加、删除或改动任何数值、单位、药名、时间——原话说 140/90 就必须写 140/90。
|
||||||
|
- 输出语言必须与【口述原话】完全一致:原话是中文就用中文,是英文就用英文,是日文/韩文就用对应语言,不要翻译。
|
||||||
- 只重组语言:去掉口头语和重复;用第一人称;不加入原话没有的事实。
|
- 只重组语言:去掉口头语和重复;用第一人称;不加入原话没有的事实。
|
||||||
- 内容只涉及一两个方面 → 整理成一段通顺的话(2-4 句)。
|
- 内容只涉及一两个方面 → 整理成一段通顺的话(2-4 句)。
|
||||||
- 内容涉及多个方面(症状/用药/饮食/睡眠/运动等) → 按「方面:内容」分行。
|
- 内容涉及多个方面(症状/用药/饮食/睡眠/运动等) → 按「方面:内容」分行。
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ enum HealthExportPrompts {
|
|||||||
|
|
||||||
输出格式:
|
输出格式:
|
||||||
- 严格 Markdown,标题用 # / ##,不要 markdown 围栏,不要输出 JSON,不写「数据」二字。
|
- 严格 Markdown,标题用 # / ##,不要 markdown 围栏,不要输出 JSON,不写「数据」二字。
|
||||||
- 不给诊断意见、用药建议或「建议就医」。全文中文,简洁,医生 30 秒能扫完。
|
- 不给诊断意见、用药建议或「建议就医」。简洁,医生 30 秒能扫完(输出语言见文末说明)。
|
||||||
- 严格按以下 6 段(顺序与标题固定):
|
- 严格按以下 6 段(顺序与标题固定):
|
||||||
\(labelLine)
|
\(labelLine)
|
||||||
## 主诉
|
## 主诉
|
||||||
@@ -111,7 +111,7 @@ enum HealthExportPrompts {
|
|||||||
【本人原话】:\(userPrompt)
|
【本人原话】:\(userPrompt)
|
||||||
|
|
||||||
再次强调:只整理上面【真实数据】里真实出现过的内容,禁止编造任何数字/日期/症状/药物。
|
再次强调:只整理上面【真实数据】里真实出现过的内容,禁止编造任何数字/日期/症状/药物。
|
||||||
直接输出 Markdown,不要思考过程,不要 <think> 标签:
|
直接输出 Markdown,不要思考过程,不要 <think> 标签:\(aiOutputLanguageDirective())
|
||||||
/no_think
|
/no_think
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
@@ -142,7 +142,7 @@ enum HealthExportPrompts {
|
|||||||
【用户最新问题】:
|
【用户最新问题】:
|
||||||
\(latestQuestion)
|
\(latestQuestion)
|
||||||
|
|
||||||
直接输出中文回答,不要 Markdown 标题,不要 <think>:
|
直接输出回答,不要 Markdown 标题,不要 <think>:\(aiOutputLanguageDirective())
|
||||||
/no_think
|
/no_think
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
@@ -164,7 +164,7 @@ enum HealthExportPrompts {
|
|||||||
|
|
||||||
输出要求:
|
输出要求:
|
||||||
- 严格 Markdown,不要 markdown 围栏,不要输出 JSON。
|
- 严格 Markdown,不要 markdown 围栏,不要输出 JSON。
|
||||||
- 中文,简洁,医生 30 秒能扫完。
|
- 简洁,医生 30 秒能扫完(输出语言见文末说明)。
|
||||||
- 「相关健康日记」每条单独一行,格式为「2026-05-01:正文摘要」,日期照抄 JSON 的 date 字段,精确到日。
|
- 「相关健康日记」每条单独一行,格式为「2026-05-01:正文摘要」,日期照抄 JSON 的 date 字段,精确到日。
|
||||||
- diaries 里 kind 为「问诊」的是既往问诊记录,优先列入「相关健康日记」并标注「(问诊)」。
|
- diaries 里 kind 为「问诊」的是既往问诊记录,优先列入「相关健康日记」并标注「(问诊)」。
|
||||||
- 严格按以下段落:
|
- 严格按以下段落:
|
||||||
@@ -182,7 +182,7 @@ enum HealthExportPrompts {
|
|||||||
【多轮对话】:
|
【多轮对话】:
|
||||||
\(transcript.isEmpty ? "无" : transcript)
|
\(transcript.isEmpty ? "无" : transcript)
|
||||||
|
|
||||||
直接输出 Markdown,不要思考过程,不要 <think>:
|
直接输出 Markdown,不要思考过程,不要 <think>:\(aiOutputLanguageDirective())
|
||||||
/no_think
|
/no_think
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ nonisolated enum InsightPrompts {
|
|||||||
现在的报告:\(title)(\(typeLabel))
|
现在的报告:\(title)(\(typeLabel))
|
||||||
指标:
|
指标:
|
||||||
\(indicatorLines)
|
\(indicatorLines)
|
||||||
只输出 2~3 句正文。/no_think
|
只输出 2~3 句正文。\(aiOutputLanguageDirective())/no_think
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ nonisolated enum InsightPrompts {
|
|||||||
|
|
||||||
现在的记录:
|
现在的记录:
|
||||||
\(dataLines)
|
\(dataLines)
|
||||||
只输出 1~2 句正文。/no_think
|
只输出 1~2 句正文。\(aiOutputLanguageDirective())/no_think
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,6 +118,48 @@ extension String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - AI 输出语言(供 Service/Prompt 层在非 MainActor 上下文读取当前界面语言)
|
||||||
|
|
||||||
|
/// 当前界面语言解析出的「输出语言」,给本地 LLM 的 prompt 用:
|
||||||
|
/// 让 AI 生成的追问 / 解读 / 摘要跟随用户所选界面语言,而不是永远中文。
|
||||||
|
/// 判定只看 `appLocLocale`(LanguageManager 切换时刷新的 nonisolated 快照);
|
||||||
|
/// `system` 跟随系统时,按系统首选语言归一到最接近的受支持语言,兜底中文。
|
||||||
|
nonisolated enum AIOutputLanguage: String {
|
||||||
|
case zhHans, en, ja, ko
|
||||||
|
|
||||||
|
/// 从当前 appLocLocale 解析。system(autoupdatingCurrent)时读系统语言码。
|
||||||
|
static var current: AIOutputLanguage {
|
||||||
|
let code = appLocLocale.language.languageCode?.identifier ?? "zh"
|
||||||
|
switch code {
|
||||||
|
case "en": return .en
|
||||||
|
case "ja": return .ja
|
||||||
|
case "ko": return .ko
|
||||||
|
default: return .zhHans // zh 及其它未支持语言统一回中文(few-shot 也是中文)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// prompt 里对模型自称的目标语言名(用该语言自己的说法,减少模型误判)。
|
||||||
|
var promptName: String {
|
||||||
|
switch self {
|
||||||
|
case .zhHans: return "简体中文"
|
||||||
|
case .en: return "English"
|
||||||
|
case .ja: return "日本語"
|
||||||
|
case .ko: return "한국어"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 追加到 prompt 末尾的「用某语言输出」硬指令。中文时返回空串(few-shot 已是中文,不必额外声明)。
|
||||||
|
/// 用于「生成给用户看的内容」(追问 / 解读 / 摘要),让输出语言=界面语言。
|
||||||
|
var outputDirective: String {
|
||||||
|
self == .zhHans ? "" : "\n\n重要:请全程用\(promptName)输出所有面向用户的文字(JSON 的字段值也用\(promptName)),不要用中文。"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 便捷:当前界面语言的「用某语言输出」指令(见 `AIOutputLanguage.outputDirective`)。
|
||||||
|
nonisolated func aiOutputLanguageDirective() -> String {
|
||||||
|
AIOutputLanguage.current.outputDirective
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Bundle 运行时重定向(供 Text / NSLocalizedString 双保险)
|
// MARK: - Bundle 运行时重定向(供 Text / NSLocalizedString 双保险)
|
||||||
|
|
||||||
/// 关联对象 key(需稳定地址,文件级全局即可)。
|
/// 关联对象 key(需稳定地址,文件级全局即可)。
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ import SwiftUI
|
|||||||
/// 任何展示 AI 生成的归纳/解读/建议文本的地方,都应在结果末尾附上 `AIDisclaimerFooter`;
|
/// 任何展示 AI 生成的归纳/解读/建议文本的地方,都应在结果末尾附上 `AIDisclaimerFooter`;
|
||||||
/// 离开 App 的文本(复制/分享)用 `AIDisclaimer.appended(to:)` 把声明带上。
|
/// 离开 App 的文本(复制/分享)用 `AIDisclaimer.appended(to:)` 把声明带上。
|
||||||
enum AIDisclaimer {
|
enum AIDisclaimer {
|
||||||
/// 面向用户展示的完整声明文本。
|
/// 面向用户展示的完整声明文本(随「我的 · 语言」本地化)。
|
||||||
static let text =
|
/// 注意:必须是**单个** `String(appLoc:)` key(勿再用字符串拼接),否则无法整句进 String Catalog 翻译;
|
||||||
"本内容由本机本地 AI 依据你录入的健康记录自动归纳整理,仅供个人健康管理与就医沟通参考," +
|
/// 且展示处用 `Text(AIDisclaimer.text)`(传 String 变量),SwiftUI 不会再做本地化 —— 这里先本地化好。
|
||||||
"不构成医学诊断、治疗建议或专业医疗意见;具体健康问题请咨询执业医师。"
|
static var text: String {
|
||||||
|
String(appLoc: "本内容由本机本地 AI 依据你录入的健康记录自动归纳整理,仅供个人健康管理与就医沟通参考,不构成医学诊断、治疗建议或专业医疗意见;具体健康问题请咨询执业医师。")
|
||||||
|
}
|
||||||
|
|
||||||
/// 复制/分享时把声明追加到正文末尾(分隔线 + 声明),让文本离开 App 也带着免责。
|
/// 复制/分享时把声明追加到正文末尾(分隔线 + 声明),让文本离开 App 也带着免责。
|
||||||
static func appended(to body: String) -> String {
|
static func appended(to body: String) -> String {
|
||||||
|
|||||||
@@ -513,7 +513,7 @@ struct HealthExportSheet: View {
|
|||||||
// 已有部分回答就保留;否则给一句友好兜底,绝不把技术异常当成 AI 的回答展示。
|
// 已有部分回答就保留;否则给一句友好兜底,绝不把技术异常当成 AI 的回答展示。
|
||||||
if let idx = turns.firstIndex(where: { $0.id == assistantTurn.id }),
|
if let idx = turns.firstIndex(where: { $0.id == assistantTurn.id }),
|
||||||
turns[idx].text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
turns[idx].text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||||
turns[idx].text = "这次没能回答上来,请换个说法再试一次。"
|
turns[idx].text = String(appLoc: "这次没能回答上来,请换个说法再试一次。")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -632,7 +632,7 @@ struct HealthExportSheet: View {
|
|||||||
/// 把推理过程中抛出的技术异常翻译成用户能看懂的一句话(取消不算错误)。
|
/// 把推理过程中抛出的技术异常翻译成用户能看懂的一句话(取消不算错误)。
|
||||||
private func friendlyMessage(for error: Error) -> String {
|
private func friendlyMessage(for error: Error) -> String {
|
||||||
if error is CancellationError { return "" }
|
if error is CancellationError { return "" }
|
||||||
return "这次没能生成成功,请稍后重试。"
|
return String(appLoc: "这次没能生成成功,请稍后重试。")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -497,9 +497,10 @@ struct DiaryQuickSheet: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func answerPlaceholder(for q: DiaryAssistService.Question) -> String {
|
private func answerPlaceholder(for q: DiaryAssistService.Question) -> String {
|
||||||
|
// 维度名走 localizedDimension 翻成界面语言(模型固定输出中文维度名,展示时本地化)。
|
||||||
q.dim.isEmpty
|
q.dim.isEmpty
|
||||||
? String(appLoc: "在这儿写下你的回答…")
|
? String(appLoc: "在这儿写下你的回答…")
|
||||||
: String(appLoc: "说说「\(q.dim)」的情况…")
|
: String(appLoc: "说说「\(DiaryAssistPrompts.localizedDimension(q.dim))」的情况…")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 「问答式」追问卡片(正文回落卡里):康康问一句 → 用户自由打字回答 →
|
/// 「问答式」追问卡片(正文回落卡里):康康问一句 → 用户自由打字回答 →
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user