feat: iOS 审核阉割版 - iOS 蓝牙功能屏蔽 + Info.plist 删蓝牙权限 + AI 提示词去医疗化(应对 App Store 1.4.1)

- iOS 蓝牙 UI 入口用 Platform.isIOS 屏蔽(抽屉/设置页/记数据卡片/AI 嵌入链接)
- Info.plist 删除 NSBluetoothAlwaysUsageDescription 和 NSBluetoothPeripheralUsageDescription
- AI 提示词重写:只描述客观事实和综合信息,不给医疗建议,不做关联性分析/推测
- 所有"专家/教练/预问诊/预解读"角色改为"记录/整理/结构化提取助手"
- 去掉基于疾病(冠心病/糖尿病等)的饮食运动建议
- 保留正常值参考范围用于客观描述"超出范围",不作诊断
This commit is contained in:
MingNian
2026-07-21 11:57:54 +08:00
parent 28f704c98e
commit 5cd3584ae9
8 changed files with 122 additions and 104 deletions

View File

@@ -10,10 +10,10 @@ public static class DietCommentaryPolicy
public const string LegacyPromptPrefix = "饮食记录页需要展示本餐建议。"; public const string LegacyPromptPrefix = "饮食记录页需要展示本餐建议。";
public const string SystemPrompt = """ public const string SystemPrompt = """
App 23 App 23
1222使 Markdown 1222使 Markdown
5
"""; """;
public static string BuildFoodDescription(IEnumerable<DietCommentaryFood> foods) => public static string BuildFoodDescription(IEnumerable<DietCommentaryFood> foods) =>

View File

@@ -19,13 +19,13 @@ public static class ReportAgentHandler
return new { success = false, message = "缺少报告图片" }; return new { success = false, message = "缺少报告图片" };
var prompt = """ var prompt = """
JSON格式返回 JSON格式返回
{ {
"reportType": "报告类型(血常规/生化全项/心电图/彩超/出院小结/其他)", "reportType": "报告类型(血常规/生化全项/心电图/彩超/出院小结/其他)",
"indicators": [ "indicators": [
{"name":"指标名","value":"数值","unit":"单位","range":"参考范围","status":"normal/high/low"} {"name":"指标名","value":"数值","unit":"单位","range":"参考范围","status":"normal/high/low"}
], ],
"summary": "面向患者的初步预解读,说明异常指标的可能方向,不作确定诊断,不给出处方、停药、换药或调药建议;必要时建议咨询医生", "summary": "列出异常指标及其数值和参考范围,客观说明超出范围,不解释可能原因,不结合用户档案做关联分析,建议咨询医生解读",
"needsDoctorReview": true/false "needsDoctorReview": true/false
} }
JSON JSON

View File

@@ -40,12 +40,21 @@ public sealed class PromptManager
private const string MedicalBoundaryRules = """ private const string MedicalBoundaryRules = """
- AI -
- // -
- "血压 150/95收缩压超出正常范围""最近7天平均血压比上周高 5 mmHg""白细胞计数偏高"
- - //"您的档案中记录了高血压""病历中提到以下用药XXX"
- 使 - //
- AI × "根据您的病历您可能患有XX疾病"
× "您有高血压病史,本次血压偏高,考虑高血压"
× "冠心病患者忌高脂,本餐红烧肉不建议食用"
- "可能方向""初步分析""疑似""可能原因"
- "这代表什么""是不是XX病""怎么办"
-
-
- 尿//
-
- AI
- 使 ******~~线~~ Markdown - 使 ******~~线~~ Markdown
@@ -59,32 +68,31 @@ public sealed class PromptManager
"""; """;
private const string DefaultPrompt = """ private const string DefaultPrompt = """
AI健康管家 AI
1. 1.
2. 2.
3. 3.
4.
- -
- "血压 150/95收缩压超出正常范围"
- -
- / - 怀
- 怀
"""; """;
private const string ConsultationPrompt = """ private const string ConsultationPrompt = """
AI
1. 1.
2. 2-3 2. 2-3
3. 3. -> -> -> ->
4. 4.
5. >160/100>120<50 5.
6. 6. "您有心脏手术史+胸痛,可能是心脏问题"
7. 7.
"""; """;
private const string HealthDataPrompt = """ private const string HealthDataPrompt = """
@@ -92,15 +100,16 @@ public sealed class PromptManager
1. //// 1. ////
2. + record_health_data 2. +-> record_health_data
3. "血压120/80血糖6.2血氧97" record_health_data 3. "血压120/80血糖6.2血氧97"-> record_health_data
4. "早上血压120下午血压130"recorded_at 4. "早上血压120下午血压130"->recorded_at
5. "120""收缩压还是血糖?" 5. "120"->"收缩压还是血糖?"
6. 6. ->
7. 7. ->"收缩压 150 超出正常范围 90-139 mmHg"
8. record_health_data 8.
9. record_health_data "录入成功"
- 90-139 mmHg 60-89 mmHg - 90-139 mmHg 60-89 mmHg
- 60-100 / - 60-100 /
- 3.9-6.1 mmol/L - 3.9-6.1 mmol/L
@@ -108,18 +117,17 @@ public sealed class PromptManager
"""; """;
private const string DietPrompt = """ private const string DietPrompt = """
1. VLM食物识别结果后 check_archive 1. VLM食物识别结果后 check_archive
2. "能不能吃" 2. -> "含过敏原XX"
- 3. //-> "本餐盐分约X克超出您设置的低盐限制"
- // 4. 1-5
- 尿 5. ///"建议少吃"
3. 1-5 6. 尿
4. PCI术后/ 7. /"个性化建议"
5. + + 8. ///
6. ///
"""; """;
private const string MedicationPrompt = """ private const string MedicationPrompt = """
@@ -131,62 +139,64 @@ public sealed class PromptManager
- scope="current_plans" scope="upcoming_plans" scope="ended_plans" scope="inactive_plans" scope="all_plans" - scope="current_plans" scope="upcoming_plans" scope="ended_plans" scope="inactive_plans" scope="all_plans"
- scope - scope
2. /// 2. ///
3. "早饭后" 3. "早饭后"->
4. manage_medication(action="create") 4. manage_medication(action="create")
5. 5. ->->->
6. 6.
"""; """;
private const string ReportPrompt = """ private const string ReportPrompt = """
1. 1.
2. // 2. //"XX指标 150超出参考范围 90-120"
3. 3.
4. "AI预解读待医生确认" 4. /
5. /CT"需医生人工审阅" 5. "AI 整理,请咨询医生解读"
6. /CT"需医生人工审阅"
"""; """;
private const string ExercisePrompt = """ private const string ExercisePrompt = """
1. // 1. //
2. 2.
3. 3.
4. 4.
5. 5.
6. manage_exercise(action="query") 6.
7. manage_exercise(action="query")
- scope="today" scope="scheduled_date" date - scope="today" scope="scheduled_date" date
- scope="current_plans" scope="upcoming_plans" scope="ended_plans" scope="all_plans" - scope="current_plans" scope="upcoming_plans" scope="ended_plans" scope="all_plans"
- scope - scope
"""; """;
private const string UnifiedPrompt = """ private const string UnifiedPrompt = """
AI AI "小脉"
"已录入""已保存""请点击确认"
1. //// record_health_data 1. ////-> record_health_data
2. 使 query_diet_records 2. -> 使 -> query_diet_records
3. manage_medication 3. -> manage_medication
4. manage_exercise 4. -> manage_exercise
5. check_archive 5. -> check_archive
6. query_health_records 6. -> query_health_records
7. 访 query_followups 7. 访 -> query_followups
8. query_reports 8. -> query_reports
9. query_notifications 9. -> query_notifications
+ "时间意图 + 业务对象"
- - "某一天的任务""计划所处的生命周期"
- 使 scope="today"使 scope="scheduled_date" date - 使 scope="today"使 scope="scheduled_date" date
- 使 scope="current_plans"使 scope="upcoming_plans"使 scope="ended_plans"使 scope="all_plans" - 使 scope="current_plans"使 scope="upcoming_plans"使 scope="ended_plans"使 scope="all_plans"
- 使 scope="inactive_plans" - 使 scope="inactive_plans"
- 使 scope="next" scope="upcoming" scope="completed" scope="date" scope="all" - 使 scope="next" scope="upcoming" scope="completed" scope="date" scope="all"
- query create - "有没有、会不会有、新的、接下来" query create
- - "今天是否完成"
- scope - scope
- results_truncated=true items_truncated=true - results_truncated=true items_truncated=true
@@ -206,26 +216,25 @@ public sealed class PromptManager
- -
- - "我的、今天、当前、下一次、是否完成"
- -
- -
- - "记录存在""计划当前有效""指定日期有安排""指定任务已完成"
- -
- -
- /使 scope730 recent_days=7 - "今天/昨天"使 scope730 recent_days=7
- 100 - 100
- -
- record_health_data98% - record_health_data"血氧98%是不是太高了"
- record_health_data116/89 - "记录、录入、保存、记一下" record_health_data"帮我记录血压116/89"
- -
- -
- record_health_data - record_health_data
- -
- pendingConfirmation=true - pendingConfirmation=true "已录入""保存成功"
- - "点击确认按钮"
- /// -
- 2-5 - "您有心脏手术史+胸痛,可能是心脏问题"
-
- -
"""; """;
} }

View File

@@ -82,7 +82,7 @@ public sealed class ReportAnalysisService(
throw new InvalidOperationException("报告图片文件不存在或无法读取"); throw new InvalidOperationException("报告图片文件不存在或无法读取");
var prompt = """ var prompt = """
@@ -128,7 +128,7 @@ public sealed class ReportAnalysisService(
} }
var prompt = $$""" var prompt = $$"""
你是一名医学检验报告分析专家。下面是用户上传 PDF 中提取出的文字。 你是报告结构化提取助手。下面是用户上传 PDF 中提取出的文字。
PDF 文本: PDF 文本:
{{text}} {{text}}
@@ -180,24 +180,24 @@ public sealed class ReportAnalysisService(
private async Task<string> GenerateSummaryAsync(string indicatorsJson, CancellationToken ct) private async Task<string> GenerateSummaryAsync(string indicatorsJson, CancellationToken ct)
{ {
var prompt = $""" var prompt = $"""
你是患者端医学报告预解读助手。请根据以下检验指标,用通俗易懂的语言写一份报告解读 你是报告整理助手。请根据以下检验指标,整理一份报告摘要
检测指标:{indicatorsJson} 检测指标:{indicatorsJson}
要求: 要求:
1. 总字数200-300字 1. 总字数200-300字
2. 先总结整体情况 2. 先总结整体情况(指标项数、异常项数)
3. 出异常指标及其可能原因,但不要给出确定诊断 3. 出异常指标及其数值和参考范围,客观说明"",不解释可能原因
4. 给出生活方式和复查/就医沟通建议,不要给出处方、停药、换药或调药建议 4. /
5. 如指标明显异常或存在急症风险,优先建议及时就医 5.
6. 末尾提醒"AI预解读" 6. "以上为AI整理不能替代医生诊断和治疗建议"
JSON格式 JSON格式
"""; """;
var messages = new List<ChatMessage> var messages = new List<ChatMessage>
{ {
new() { Role = "system", Content = "你是患者端医学报告预解读助手,不替代医生诊断、处方或治疗决策。" }, new() { Role = "system", Content = "你是报告整理助手,不替代医生诊断。所有指标解读请用户咨询医生。" },
new() { Role = "user", Content = prompt } new() { Role = "user", Content = prompt }
}; };

View File

@@ -26,10 +26,6 @@
<string>$(FLUTTER_BUILD_NUMBER)</string> <string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>需要使用蓝牙连接支持的血压计等健康设备,以同步用户主动测量的健康数据</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>需要使用蓝牙连接支持的血压计等健康设备</string>
<key>NSCameraUsageDescription</key> <key>NSCameraUsageDescription</key>
<string>需要使用相机拍摄饮食照片和体检报告,以便 AI 分析和记录</string> <string>需要使用相机拍摄饮食照片和体检报告,以便 AI 分析和记录</string>
<key>NSMicrophoneUsageDescription</key> <key>NSMicrophoneUsageDescription</key>

View File

@@ -1194,7 +1194,9 @@ class ChatMessagesView extends ConsumerWidget {
pushRoute(ref, 'reports'); pushRoute(ref, 'reports');
break; break;
case 'device': case 'device':
pushRoute(ref, 'devices'); if (!Platform.isIOS) {
pushRoute(ref, 'devices');
}
break; break;
default: default:
if (uri.host.isNotEmpty) pushRoute(ref, uri.host); if (uri.host.isNotEmpty) pushRoute(ref, uri.host);
@@ -2148,7 +2150,12 @@ final _agentActions = <ActiveAgent, List<_AgentAction>>{
}; };
extension _AgentActionsExt on ActiveAgent { extension _AgentActionsExt on ActiveAgent {
List<_AgentAction> get actions => List<_AgentAction> get actions {
_agentActions[this] ?? final all = _agentActions[this] ??
[const _AgentAction(label: '开始对话', icon: Icons.chat_outlined)]; [const _AgentAction(label: '开始对话', icon: Icons.chat_outlined)];
if (Platform.isIOS) {
return all.where((a) => a.route != 'devices').toList();
}
return all;
}
} }

View File

@@ -1,3 +1,4 @@
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shadcn_ui/shadcn_ui.dart'; import 'package:shadcn_ui/shadcn_ui.dart';
@@ -49,11 +50,12 @@ class SettingsPage extends ConsumerWidget {
title: '长辈模式', title: '长辈模式',
onTap: () => pushRoute(ref, 'elderMode'), onTap: () => pushRoute(ref, 'elderMode'),
), ),
_SettingsTile( if (!Platform.isIOS)
icon: LucideIcons.bluetooth, _SettingsTile(
title: '蓝牙设备', icon: LucideIcons.bluetooth,
onTap: () => pushRoute(ref, 'devices'), title: '蓝牙设备',
), onTap: () => pushRoute(ref, 'devices'),
),
_SettingsTile( _SettingsTile(
icon: LucideIcons.bell, icon: LucideIcons.bell,
title: '消息通知', title: '消息通知',

View File

@@ -1,3 +1,4 @@
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shadcn_ui/shadcn_ui.dart'; import 'package:shadcn_ui/shadcn_ui.dart';
@@ -371,7 +372,7 @@ class _NavigationSection extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final elderMode = ElderModeScope.enabledOf(context); final elderMode = ElderModeScope.enabledOf(context);
final items = [ final allItems = <_NavItem>[
_NavItem( _NavItem(
icon: LucideIcons.folderHeart, icon: LucideIcons.folderHeart,
title: '档案', title: '档案',
@@ -421,6 +422,9 @@ class _NavigationSection extends StatelessWidget {
colors: AppColors.deviceGradient.colors, colors: AppColors.deviceGradient.colors,
), ),
]; ];
final items = Platform.isIOS
? allItems.where((item) => item.route != 'devices').toList()
: allItems;
return _LightSection( return _LightSection(
title: '常用功能', title: '常用功能',