AI-HEALTH 智能健康分析系统:

本文基于 AI-HEALTH 项目真实架构撰写,聚焦大模型 API 集成、提示词工程、识图与文字处理分离、异常数据过滤、定时任务自动生成健康报告五大核心技术领域。面向有 LLM 应用开发经验的技术读者,在工程实现和设计思路上展开论述。


一、整体架构概览

AI-HEALTH 是一个基于 Spring Boot + Vue 3 的个人健康管理系统,核心智能化能力由大语言模型驱动。系统通过调用 Sophnet API 接入两款主流大模型,构建了一条完整的"数据采集 → 多模态识别 → 结构化解析 → 智能分析 → 定时报告"流水线。

输出层

定时任务层

LLM 分析层

多模态识别层

Spring Boot 控制器层

用户交互层

食物拍照

Base64 编码上传

穿戴设备截图

Base64 编码上传

手动录入

结构化表单提交

FoodRecordController

WorkoutController

SleepRecordController

AiHealthAnalysisController

FoodImageRecognitionService
食物实物识别

ScreenshotAnalysisService
截图数据提取

NutritionEstimationService
营养估算

DeepSeek-V4-Pro
健康报告 / AI 助手对话

Kimi-K2.6
视觉识别 / 截图提取

日报 9:50 AM

周报 周一 10:00 AM

月报 每月1日 10:00 AM

结构化健康记录

Markdown 健康报告

HTML 可视化报告

站内通知推送

核心技术决策

决策项选择理由
视觉模型Kimi-K2.6多模态能力强,对中文 OCR 与结构化提取效果好
文本模型DeepSeek-V4-Pro长文本分析能力强,支持复杂推理,性价比高
API 调用方式RestTemplate 同步调用业务场景对延迟要求适中,同步调用简化状态管理
响应解析手动 DOM 解析 + 正则避免 LLM 输出格式漂移导致 Jackson 反序列化失败
异常过滤Prompt 内嵌规则无需额外代码层,利用 LLM 推理能力灵活处理

二、提示词工程:从单次问答到多模板体系

提示词工程是本项目 AI 能力的核心。我们经过多轮迭代,沉淀出一套"角色定义 + 数据注入 + 约束规格 + 格式锚定"的四段式 Prompt 构建方法论。

2.1 四段式 Prompt 构建框架

🎭 角色定义
System Prompt

📊 数据注入
结构化数据嵌入

📏 约束规格
评价维度 / 阈值 / 规则

⚓ 格式锚定
输出模板 / JSON Schema

这个框架贯穿了食物识别、截图提取、健康分析、AI 助手对话四个场景。以一个具体的食物识别 Prompt 为例:

角色定义(System Prompt)"你是一个食物营养分析器。只返回 JSON,不要任何解释。"——角色限定为"食物营养分析器",输出格式锁定为 JSON,禁止解释性文本。这个 System Prompt 只有两句话,却完成了三个关键约束:专业领域限定、输出格式锁定、冗余内容抑制。

数据注入(User Prompt):将 Base64 编码的食物图片以内联方式嵌入请求,使用 OpenAI Vision 兼容的 data:image/jpeg;base64,{...} 格式。同时注入明确的结构化要求——对每种食物需要识别名称、分量、卡路里、碳水、蛋白质、脂肪,整餐需要汇总营养数据。

约束规格:要求"估算"而非精确计算、"中文名称"等,这些软约束既保证了输出的一致性,也为模型的合理推断留出了空间。

格式锚定:在 Prompt 末尾给出精确的 JSON 模板 {"foodItems": [{...}], "totalCalories": 数字, ...},并用指令"只返回 JSON,不要解释"锁死输出格式。这大大降低了解析层的复杂度。

2.2 五大场景的 Prompt 差异化设计

Prompt 模板体系

食物识别

System: 营养分析器角色

约束: 中文命名、分量估算

输出: foodItems 数组 JSON

温度: 0.1(精确一致)

截图提取

System: 数据提取器角色

分类: workout / sleep 场景路由

约束: 字段值提取 + extraNotes 兜底

输出: 场景特定字段 JSON

温度: 0.1(精确一致)

健康分析

System: 健康分析师角色

维度: 饮食/运动/睡眠/情绪/体重

约束: 异常过滤阈值、五个评价等级

输出: Markdown 格式化报告

温度: 0.5(平衡分析)

AI 助手

System: 人格化健康顾问

约束: 沟通原则、性格特征注入

输出: 自然语言对话

温度: 0.7(创造性与温度感)

营养估算

System: 营养计算器角色

约束: 数学计算准确性

输出: 营养素数值 JSON

温度: 0.1(精确一致)

场景路由设计——截图分析服务通过 sceneType 参数动态切换 Prompt 模板。传入 "workout" 时,Prompt 要求提取运动时长、距离、心率、配速、步频等字段;传入 "sleep" 时,切换为深睡时长、REM 时长、睡眠评分、醒来次数等字段。无法匹配预定义字段的信息统一归入 extraNotes 字段,既保证了结构化数据的整洁性,又防止信息丢失。

2.3 温度参数的工程含义

温度(Temperature)是 LLM 输出随机性的控制参数(0~1)。在本项目中,不同场景的温度选择经过了针对性的调优:

场景温度值设计意图
食物识别0.1需要稳定、可预测的 JSON 输出;食物名称和营养估算不能"即兴发挥"
截图提取0.1从固定格式截图中提数,输出的一致性远比"创造性"重要
健康分析0.5需要在专业分析和个性化建议之间找到平衡;过冷则报告千篇一律,过热则可能偏离医学常识
AI 助手0.7对话场景需要自然流畅的交流体验;用户期望感受到"有温度"的陪伴

三、识图与文字处理分离:两条流水线的架构设计

3.1 为什么分离

最初的设计是所有 AI 调用使用同一个模型和同一个服务类。但很快我们发现两个问题:

  1. 模型能力不对称:视觉模型(Kimi-K2.6)擅长图像理解但文本分析能力相对较弱;文本模型(DeepSeek-V4-Pro)反之。用同一个模型同时处理图像和长文本分析,要么精度不够,要么成本过高。
  2. Prompt 冲突:图像识别的 Prompt 要求输出严格 JSON;健康分析的 Prompt 要求输出结构化 Markdown。两种输出格式共享同一个 System Prompt 角色设定会导致模型"人格分裂"。

事实上,这两个问题都不是最关键的,是AI在这瞎扯淡,我们决策的唯一原因是Kimi-K2.6太贵了,我们想给戴老师省点钱,再加上做这种分离的API设计好像不困难,于是就做了。
同AI探讨API分离策略

3.2 架构分离策略

文本分析流水线 · DeepSeek-V4-Pro

视觉识别流水线 · Kimi-K2.6

结构化数据作为
健康分析输入

食物照片

FoodImageRecognitionService

运动截图

ScreenshotAnalysisService
sceneType=workout

睡眠截图

JSON 解析
→ FoodRecord

JSON 解析
→ Workout/SleepRecord

结构化存储

用户交互

AiAssistantService

周报生成请求

AiHealthAnalysisService

对话历史管理
→ 自然语言回复

五维数据聚合
→ Markdown 报告

前端渲染

HTML 生成
→ 站内通知

分离带来的直接收益:

  • Prompt 独立性:每个服务维护自己的 Prompt 模板,彼此不受影响。食物识别的 "只返回 JSON,不要解释" 不会污染健康分析的 Markdown 输出要求。
  • 模型匹配:视觉任务走 Kimi-K2.6,文本分析走 DeepSeek-V4-Pro,各展所长。
  • 独立迭代:截图提取新增一种穿戴设备格式时,只需修改 ScreenshotAnalysisService 的 Prompt,不影响其他服务。
  • 容错隔离:某个识别服务的解析异常不会导致整个健康分析管道崩溃。

3.3 视觉流水线的 JSON 解析策略

视觉模型的输出格式是 "JSON 包裹在 Markdown 代码块中",即模型可能返回:

{"foodItems": [...], "totalCalories": 850}

也可能直接返回裸 JSON,甚至夹杂解释性文字。为应对这种不确定性,我们选择手动 DOM 解析而非 Jackson 自动反序列化。

核心思路:先通过正则清洗 Markdown 标记(^```json / ^````/ ```$),再定位最外层 {…}边界,然后逐字段提取数值。对于嵌套数组(如foodItems`),使用括号匹配算法找到数组起止位置后逐元素解析。这种方案虽然代码量更大,但对 LLM 输出格式的微小偏移容忍度极高,在真实生产环境中远比 Jackson + try-catch 兜底更可靠。


四、异常数据过滤:让 AI 做"数据清洗师"

健康数据来源多样——手动录入、AI 识图估算、截图 OCR 提取——不可避免地存在异常值。传统的做法是在入库前做硬编码校验(if calories > 10000: reject),但这有两个缺陷:一是指定阈值难以覆盖所有场景(运动员的单餐热量可能确实很高);二是硬拒绝会丢失数据,用户可能不知道自己录入了异常值。

4.1 方案设计:Prompt 内嵌过滤

我们将异常检测逻辑内嵌到健康分析的 Prompt 中,让 LLM 在生成报告时自动识别并处理:

**异常数据过滤**:如果某条记录存在明显不合理的数据
(如单餐热量超过 5000 千卡、一顿饭超过 50 个食物、
运动时长超过 24 小时连续、单次运动消耗超过 10000 千卡、
睡眠超过 20 小时等),请在分析中标注并使用合理范围估算,
不要直接引用异常值

4.2 设计决策:过滤 vs 拒绝

本方案:Prompt 内嵌过滤

原始数据

全量入库
保留原始值

LLM 分析时标注异常
用合理值替代参与计算

用户可在报告中
看到标注 ✅

传统方案:入库前硬拒绝

通过

拒绝

原始数据

阈值校验
if value > threshold

入库

丢弃 ❌
用户无感知

选择"标注而非拒绝"的理由:

  1. 数据不丢失:异常值可能是用户真实情况的反映(如马拉松运动员的日消耗),全量保留给予人工复核空间。
  2. 用户可感知:报告中的异常标注本身就是一种用户教育——“您的某条记录看起来不太合理,建议核实”。
  3. 阈值可动态调整:修改 Prompt 文本即可调整异常判定规则,无需改动代码、重新部署。

4.3 分级阈值体系

类别阈值说明
单餐热量> 5000 kcal正常成人一餐约 500-1200 kcal
单餐食物数> 50 种自助餐极端情况也极少超过 30 种
连续运动时长> 24 小时排除超马等极端赛事的数据录入错误
单次运动消耗> 10000 kcal全程马拉松约消耗 2500-3500 kcal
单次睡眠> 20 小时排除因病卧床等特殊情况

五、大模型 API 调用封装:从混乱到有序

5.1 调用链路标准化

在项目早期,每个 Service 类各自封装 HTTP 调用逻辑,导致大量重复代码。重构后,虽然各 Service 仍保留独立的 RestTemplate 实例(同步调用的设计选择),但调用的链路上已形成统一模式:

构建 Headers(Bearer Token + Content-Type)
    → 构建 Messages(System Prompt + User Prompt)
    → 设置模型参数(model / temperature / max_tokens)
    → POST /chat/completions
    → 提取 choices[0].message.content
    → 清洗输出(去 Markdown 标记、定位 JSON 边界)
    → 解析为业务对象
    → 异常兜底(返回空结果 / 默认值)

5.2 模型参数调优经验

在实践过程中,我们总结了以下调参经验:

  • max_tokens 的分级设置:食物识别 800(短 JSON),截图提取 800,健康分析 4000(长 Markdown 报告),AI 助手 2000(中等长度对话)。
  • 内容截断保护:当 AI 返回内容被 max_tokens 截断导致 JSON 不完整时,解析层的括号匹配算法能检测到数组未闭合,触发异常兜底逻辑,而非返回残缺数据给前端。
  • 重试机制的阶段性取舍:当前版本未实现自动重试(temperature=0.1 的场景下输出高度一致,重试意义有限)。计划在后续引入指数退避重试,主要针对网络抖动导致的超时。

5.3 配置外部化

所有 API 相关配置统一管理在 application.properties 中,支持环境差异:

ai.api.key=${AI_API_KEY:default_key}
ai.api.url=${AI_API_URL:https://www.sophnet.com/api/open-apis/v1}
ai.analysis.model=DeepSeek-V4-Pro
ai.vision.model=Kimi-K2.6

通过 Spring 的 @Value 注解注入,支持通过环境变量覆盖(${AI_API_KEY:default_key}),实现开发/测试/生产环境的无缝切换。


六、定时任务自动生成报告

6.1 调度体系设计

系统内置三级定时任务,覆盖日、周、月三个时间维度:

09:5009:5109:5209:5309:5409:5509:5609:5709:5809:5910:00日报生成 周报生成 月报生成 日报(每日)周报(每周一)月报(每月1日)报告生成定时任务调度

三个 Cron 表达式驱动:

任务Cron语义
日报0 50 9 * * *每天上午 9:50,分析前一日数据
周报0 0 10 * * MON每周一上午 10:00,分析上周一至周日数据
月报0 0 10 1 * *每月 1 日上午 10:00,分析上月整月数据

时间窗口的选取经过了考量:日报选在 9:50 而非午夜,是因为用户可能在凌晨录入数据;周报选在周一上午,符合职场用户"周一查看上周总结"的习惯。

6.2 遍历用户生成 + 容错机制

定时任务的核心逻辑是遍历所有用户逐个生成报告。这意味着 N 个用户会触发 N 次 LLM 调用,整个周期可能持续数分钟。为此设计了以下容错策略:

  1. 单用户异常隔离:每个用户的报告生成包裹在 try-catch 中——用户 A 生成失败不会阻塞用户 B。
  2. 日志记录:异常信息通过 SLF4J 记录完整的用户名和堆栈信息,便于追溯。
  3. 通知推送:报告生成成功后,通过 NotificationService 创建站内通知,引导用户查看。
  4. 幂等性考虑:当前版本按时间窗口生成报告(指定 startDate/endDate),天然支持幂等——同一时段多次执行不会产生重复的差异化报告。

6.3 报告从 Markdown 到 HTML 的转换

LLM 输出的健康报告是 Markdown 格式,存储为 LONGTEXT。在用户查看时,ReportHtmlService 将其实时转换为 HTML 页面。转换逻辑不依赖第三方 Markdown 解析库,而是用正则直接处理:

  • **加粗**<strong>
  • # ## ### ####<h1> ~ <h4>
  • - 列表项<ul><li>
  • 连续的段落文本 → <p>

选择手写转换而非引入 Markdown 库的理由:LLM 输出的 Markdown 格式高度规整(因为 Prompt 中已指定了格式规范),不存在"脏 Markdown"的兼容性问题。手写转换器仅需覆盖已知的 6 种格式标记,代码量不足 100 行。


七、数据闭环:从识别到分析的完整链路

以一条完整的用户旅程为例,串联所有模块:

🤖 DeepSeek-V4-Pro⏰ 定时任务🗄️ MySQL🤖 Kimi-K2.6👁️ 视觉识别服务🔧 Spring 控制器🖥️ 前端🤖 DeepSeek-V4-Pro⏰ 定时任务🗄️ MySQL🤖 Kimi-K2.6👁️ 视觉识别服务🔧 Spring 控制器🖥️ 前端【阶段一:食物拍照识别】【阶段二:定时周报生成】loop[遍历每位用户]【阶段三:用户查看报告】👤 用户拍摄午餐照片POST /api/v1/food-recordsrecognizeFood(base64 image)Chat Completions API(System: 营养分析器User: 图片 + JSON 模板)JSON: foodItems / totalCalories / macros解析后的 FoodRecord 对象存储结构化饮食记录Cron: 周一 10:00 触发查询所有活跃用户聚合本周饮食/运动/睡眠/情绪/体重Chat Completions API(System: 健康分析师User: 五维数据 + 分析规范)Markdown 格式健康周报存储 HealthAnalysisRecord创建站内通知点击消息通知GET /api/v1/ai-analysis/{id}查询分析记录Markdown → HTML 转换渲染后的 HTML 报告可视化健康周报👤 用户

阶段一的关键点

食物识别的输入是一张照片,输出是带营养估算的结构化记录。这里有两个工程挑战:

  1. 图片传输格式:前端将图片转 Base64 后通过 FormData 上传。后端接收后直接拼接为 data:image/jpeg;base64,{...} 传入 LLM,避免服务器端文件存储再读取的 IO 开销。
  2. JSON 解析容错:上文已详述,手动 DOM 解析处理 LLM 输出的各种格式变体。

阶段二的关键点

定时任务的核心挑战是遍历大量用户的耗时控制。当前实现是顺序遍历,若用户量增长,可改进为并行流(parallelStream)或消息队列异步处理。

阶段三的关键点

报告的"生成"和"查看"是解耦的:定时任务预生成报告存入数据库,用户查看时只需从 DB 读取并转换 HTML。这种设计避免了用户查看时实时调用 LLM 的延迟(15-60 秒),将首屏加载控制在毫秒级。


八、演进方向的思考

8.1 当前阶段的不足

  • 视觉识别的局限性:仅有食物识别和两大运动/睡眠场景的截图提取,尚未覆盖心率变异性(HRV)、血氧、血压等更多健康维度。
  • 异常过滤的精度依赖:Prompt 内嵌阈值依赖 LLM 的推理能力,在极端边缘场景可能漏判或误判。
  • 定时任务的单点风险:遍历用户顺序执行,用户量增长后存在超时风险。

8.2 后续规划

方向说明
向量化检索增强生成(RAG)引入嵌入模型对历史健康报告建立向量索引,新报告生成时检索相似历史案例作为上下文,提升分析的个性化程度
式输出(SSE)将 AI 助手对话从同步请求-响应改为 Server-Sent Events 流式输出,改善用户体验
多轮对话记忆AI 助手引入滑动窗口对话历史管理,支持跨会话的健康建议连续性
用户反馈闭环收集用户对报告的满意度评分,微调 Prompt 策略
异步报告生成引入消息队列解耦定时任务和 LLM 调用,支持水平扩展

九、总结

AI-HEALTH 的大模型集成实践,核心围绕一个命题展开:如何让大模型在健康管理场景下既"看得准"又"说得对"

"看得准"依赖于视觉流水线和文本流水线的分离架构——Kimi-K2.6 专注图像理解,DeepSeek-V4-Pro 专注文本分析,各自的 Prompt 独立调优。

"说得对"依赖于精心设计的四段式 Prompt 框架——角色定位锁定输出域,数据注入提供事实基础,约束规格嵌入业务规则,格式锚定降低解析复杂度。同时将异常数据过滤逻辑从硬编码校验升级为 Prompt 内嵌的智能标注,兼顾了数据完整性和分析准确性。

这些设计不是一个"完美方案",而是一个在模型能力边界、业务需求、工程成本三者之间不断权衡和迭代的结果——这也是 LLM 应用开发中最真实的一面。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐