根据提供的code differences信息,由于没有具体的代码变更内容,我将生成一个通用的commit message模板:

```
docs(readme): 更新文档说明

- 添加了项目使用说明
- 完善了配置项描述
- 修正了示例代码格式
```

注意:由于您提供的code differences部分为空,无法获取具体的代码变更信息来生成准确的commit message。请提供实际的代码差异信息以便生成更精确的提交消息。
This commit is contained in:
link2026
2026-07-01 10:02:16 +08:00
parent e179a369f6
commit 404abbf10b
9 changed files with 7179 additions and 330 deletions

View File

@@ -22,6 +22,7 @@ enum ConsultationPrompts {
硬性规则: 硬性规则:
- 这只是**转写整理**:只复述录音里【确实说过】的内容,【绝对不许】自己新增诊断、用药、剂量、频次或「建议就医」。 - 这只是**转写整理**:只复述录音里【确实说过】的内容,【绝对不许】自己新增诊断、用药、剂量、频次或「建议就医」。
- 输出语言必须与【录音转写原话】一致:原话是中文就用中文,是英文就用英文,对应语言不翻译(小节名可保留中文标签)。
- 数值、单位、药名、剂量、时间【一字不改】——原话说 140/90 就写 140/90,说一天两次就写一天两次。 - 数值、单位、药名、剂量、时间【一字不改】——原话说 140/90 就写 140/90,说一天两次就写一天两次。
- 录音里没提到的小节就【整节省略】,不要写「无」「未提及」之类占位,也不要硬凑。 - 录音里没提到的小节就【整节省略】,不要写「无」「未提及」之类占位,也不要硬凑。
- 区分说话人:本人的主诉用第一人称「我」;医生说的话写成「医生:…」。 - 区分说话人:本人的主诉用第一人称「我」;医生说的话写成「医生:…」。

View File

@@ -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 句)。
- 内容涉及多个方面(症状/用药/饮食/睡眠/运动等) → 按「方面:内容」分行。 - 内容涉及多个方面(症状/用药/饮食/睡眠/运动等) → 按「方面:内容」分行。

View File

@@ -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
""" """
} }

View File

@@ -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
""" """
} }
} }

View File

@@ -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(,)

View File

@@ -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 {

View File

@@ -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: "这次没能生成成功,请稍后重试。")
} }
} }

View File

@@ -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