```
feat(AI): 集成MNN推理引擎替换MLX作为主AI运行时 - 引入MNN(alibaba) + Arm SME2 + CPU作为主AI运行时,支持A19/iPhone17的 SME2和A17的NEON加速 - 添加MLX Swift作为兜底GPU推理方案,实现双后端切换机制 - 使用单一Qwen3.5-2B多模态模型(1.2GB),替代原有的LLM+VL分离架构 - 实现InferenceEngine.current引擎选择逻辑,真机默认MNN,模拟器回退MLX - 更新AIAgent架构,通过MNNLLMBridge(ObjC++) → MNNBackend进行推理 - 修改队列机制防止并发推理导致OOM,使用信号量闸门控制显存占用 - 更新文档中的技术栈说明、模块边界和周次交付计划 ```
This commit is contained in:
@@ -450,6 +450,8 @@ struct HealthExportService {
|
||||
var reports: [Report]
|
||||
var diaries: [DiaryEntry]
|
||||
var profile: UserProfile
|
||||
/// 药品库(用户「我有哪些药」清单)→ AI 背景 current_meds。空 → 不写该字段。
|
||||
var medications: [Medication] = []
|
||||
/// 相关指标的趋势行(确定性计算,不进 LLM)。空 → 不渲染「## 指标趋势」段。
|
||||
var trends: [ExportTrend] = []
|
||||
}
|
||||
@@ -530,6 +532,9 @@ struct HealthExportService {
|
||||
// —— Profile(单例) ——
|
||||
let profile = UserProfileStore.loadOrCreate(in: ctx)
|
||||
|
||||
// —— 药品库(全量,作为 AI 背景 current_meds) ——
|
||||
let medications = (try? ctx.fetch(FetchDescriptor<Medication>())) ?? []
|
||||
|
||||
// —— 趋势(确定性,不进 LLM) ——
|
||||
// 用全量 in-window 还原完整序列;裁剪后的 indicators 决定哪些 series 相关。
|
||||
let trends = ExportTrendBuilder.build(
|
||||
@@ -546,6 +551,7 @@ struct HealthExportService {
|
||||
reports: reports,
|
||||
diaries: diaries,
|
||||
profile: profile,
|
||||
medications: medications,
|
||||
trends: trends
|
||||
)
|
||||
}
|
||||
@@ -561,6 +567,7 @@ struct HealthExportService {
|
||||
let indicators = (try? ctx.fetch(indicatorDesc)) ?? []
|
||||
let diaries = (try? ctx.fetch(diaryDesc)) ?? []
|
||||
let profile = UserProfileStore.loadOrCreate(in: ctx)
|
||||
let medications = (try? ctx.fetch(FetchDescriptor<Medication>())) ?? []
|
||||
|
||||
let dates = indicators.map(\.capturedAt) + diaries.map(\.createdAt)
|
||||
let fromDate = dates.min() ?? Date()
|
||||
@@ -581,6 +588,7 @@ struct HealthExportService {
|
||||
reports: [],
|
||||
diaries: diaries,
|
||||
profile: profile,
|
||||
medications: medications,
|
||||
trends: trends
|
||||
)
|
||||
}
|
||||
@@ -611,7 +619,11 @@ struct HealthExportService {
|
||||
if !profile.allergies.isEmpty { profDict["allergies"] = profile.allergies }
|
||||
if !profile.chronicConditions.isEmpty { profDict["chronic"] = profile.chronicConditions }
|
||||
if !profile.familyHistory.isEmpty { profDict["family_history"] = profile.familyHistory }
|
||||
if !profile.currentMedications.isEmpty { profDict["current_meds"] = profile.currentMedications }
|
||||
// current_meds 改读药品库(Medication);旧 profile.currentMedications 已停用。
|
||||
let medNames = snapshot.medications.map { m in
|
||||
m.detailLine.isEmpty ? m.name : "\(m.name) \(m.detailLine)"
|
||||
}
|
||||
if !medNames.isEmpty { profDict["current_meds"] = medNames }
|
||||
root["profile"] = profDict
|
||||
|
||||
// symptoms
|
||||
@@ -681,7 +693,8 @@ struct HealthExportService {
|
||||
/// 检索结果是否「实质为空」:无症状/指标/报告/日记,且 profile 也没有任何可写字段。
|
||||
/// 为真时跳过 LLM,改用确定性「无记录」摘要,避免小模型凭先验编造病例。
|
||||
static func isEffectivelyEmpty(_ s: Snapshot) -> Bool {
|
||||
guard s.symptoms.isEmpty, s.indicators.isEmpty, s.reports.isEmpty, s.diaries.isEmpty else {
|
||||
guard s.symptoms.isEmpty, s.indicators.isEmpty, s.reports.isEmpty,
|
||||
s.diaries.isEmpty, s.medications.isEmpty else {
|
||||
return false
|
||||
}
|
||||
let p = s.profile
|
||||
@@ -693,7 +706,6 @@ struct HealthExportService {
|
||||
&& p.allergies.isEmpty
|
||||
&& p.chronicConditions.isEmpty
|
||||
&& p.familyHistory.isEmpty
|
||||
&& p.currentMedications.isEmpty
|
||||
}
|
||||
|
||||
/// 无真实记录时的确定性摘要:6 段全「无记录」,主诉仅照搬本人原话,不做任何推断。
|
||||
|
||||
Reference in New Issue
Block a user