From cfeb25247a67de1e3411483b925db76df3408674 Mon Sep 17 00:00:00 2001 From: link2026 Date: Wed, 10 Jun 2026 06:11:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E8=AF=AD=E9=9F=B3=E6=97=A5=E8=AE=B0):=20o?= =?UTF-8?q?rganize=20prompt(=E8=87=AA=E9=80=82=E5=BA=94=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=20+=20=E6=95=B0=E5=80=BC=E4=B8=8D=E5=8F=AF=E6=94=B9=E7=BA=A2?= =?UTF-8?q?=E7=BA=BF)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- 康康/AI/Prompts/DiaryAssistPrompts.swift | 40 ++++++++++++++++++++++++ 康康Tests/DiaryOrganizePromptTests.swift | 28 +++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 康康Tests/DiaryOrganizePromptTests.swift diff --git a/康康/AI/Prompts/DiaryAssistPrompts.swift b/康康/AI/Prompts/DiaryAssistPrompts.swift index 00274c1..0a0490d 100644 --- a/康康/AI/Prompts/DiaryAssistPrompts.swift +++ b/康康/AI/Prompts/DiaryAssistPrompts.swift @@ -76,4 +76,44 @@ enum DiaryAssistPrompts { Output: /no_think """ } + + // MARK: - 语音口述 → 日记整理 + + /// 口述转写稿截断上限(字符)。2B 模型 context 保护:超长口述只取前面部分。 + static let organizeTranscriptLimit = 1200 + + /// 把语音转写稿整理成健康日记草稿。自适应样式:内容少 → 一段通顺的话; + /// 多方面 → 按「方面:内容」分行。 + /// 红线(spec 2026-06-10-voice-diary §2):只重组语言,严禁增删改任何数值、单位、药名、时间—— + /// 2B 模型把 140/90 改成 130/90 即健康数据事故,所以规则放第一条并配 few-shot 强化。 + static func organize(transcript: String) -> String { + let trimmed = String(transcript.prefix(organizeTranscriptLimit)) + return """ + 你是健康记录助手。下面是用户口述身体状态的语音转写原话,可能口语化、有重复、缺标点。 + 请把它整理成一条清晰的健康日记。 + + 硬性规则: + - 【绝对不许】增加、删除或改动任何数值、单位、药名、时间——原话说 140/90 就必须写 140/90。 + - 只重组语言:去掉口头语和重复;用第一人称;不加入原话没有的事实。 + - 内容只涉及一两个方面 → 整理成一段通顺的话(2-4 句)。 + - 内容涉及多个方面(症状/用药/饮食/睡眠/运动等) → 按「方面:内容」分行。 + - 不诊断、不给用药建议、不写「建议就医」。 + - 只输出整理后的日记正文,不要解释、不要 markdown 围栏、不要 标签。 + + 示例 1(口述:那个今天早上起来有点头晕然后我量了下血压140 90比平时高一点没吃早饭就出门了): + 今天早上起来有点头晕,量了血压 140/90,比平时高一点。没吃早饭就出门了。 + + 示例 2(口述:今天头晕了一上午下午好点了血压早上量的140 90嗯缬沙坦吃了降脂药忘了吃早饭没吃中午吃的清淡晚上散步了半小时): + 症状:头晕了一上午,下午好转。 + 血压:早上 140/90。 + 用药:缬沙坦已服,降脂药忘服。 + 饮食:早饭未吃,午餐清淡。 + 运动:晚上散步半小时。 + + 【口述原话】: + \(trimmed) + + Output: /no_think + """ + } } diff --git a/康康Tests/DiaryOrganizePromptTests.swift b/康康Tests/DiaryOrganizePromptTests.swift new file mode 100644 index 0000000..31db324 --- /dev/null +++ b/康康Tests/DiaryOrganizePromptTests.swift @@ -0,0 +1,28 @@ +import Testing +@testable import 康康 + +struct DiaryOrganizePromptTests { + @Test func organizePromptContainsTranscriptAndHardRules() { + let prompt = DiaryAssistPrompts.organize(transcript: "今天早上头晕量了血压140 90") + + #expect(prompt.contains("今天早上头晕量了血压140 90")) + // 健康数据红线:数值/单位/药名/时间不许改,必须写进 prompt + #expect(prompt.contains("数值")) + #expect(prompt.contains("药名")) + // 自适应样式两条规则都在 + #expect(prompt.contains("一段通顺的话")) + #expect(prompt.contains("分行")) + // 项目 prompt 规范:禁思考标签 + #expect(prompt.contains("/no_think")) + } + + @Test func organizePromptTruncatesLongTranscript() { + let long = String(repeating: "头晕", count: 2000) // 4000 字符,超过上限 + let prompt = DiaryAssistPrompts.organize(transcript: long) + + // 整条 prompt 里口述部分被截断到 organizeTranscriptLimit + let expectedTail = String(long.prefix(DiaryAssistPrompts.organizeTranscriptLimit)) + #expect(prompt.contains(expectedTail)) + #expect(!prompt.contains(String(long.prefix(DiaryAssistPrompts.organizeTranscriptLimit + 2)))) + } +}