健康管理平台最小闭环:FastAPI+SQLite实现基线评分与预警
“We Got Better at Keeping You Alive. Not at Keeping You Healthy.” 这句话常被用来讨论医疗体系的局限:急症救治、手术干预和重症监护的能力越来越强,但对普通人长期健康状态的追踪和管理却远远不够。放在医疗信息化领域,它对应着一个更具体的技术问题——大量系统擅长记录“就诊事件”,却不擅长管理“健康状态”。健康管理的目标不是等到指标恶化到疾病阈值才处理,而是在趋势尚未形成灾难前就发现偏移。要实现这一点,单靠电子病历和医嘱系统不够,还需要一套围绕连续健康数据、个体基线、风险评分和预警反馈设计的闭环平台。
这篇博客围绕“健康管理平台的最小闭环”展开,使用 FastAPI、SQLite、Pandas 搭建一个可运行的 Demo。你会看到五类常见健康指标如何被收集和建模,个体基线如何参与异常判断,健康评分如何计算,预警规则如何触发,以及同一个人的趋势数据如何比单次测量更有价值。内容面向后端工程师、医疗信息化从业者,以及对健康数据产品感兴趣的开发者。完成后,你可以把这套逻辑迁移到慢病随访、企业健康管理、可穿戴设备数据后台等场景。
说明:文中的所有指标区间、评分公式和预警阈值都是用于演示技术思路的模拟配置,不构成医疗建议,也不能作为临床诊断依据。
1. 先理解“救活一个人”和“管好一个人”在技术上的差异
1.1 医疗信息系统更擅长处理“事件”,而不是“状态”
传统医疗信息系统的核心对象是“事件”。病人挂号是事件,开具检查单是事件,手术是事件,医嘱执行是事件。事件有明确的开始时间、结束时间、操作者、结果,因此非常适合用关系型表结构来记录:就诊表、检查表、手术表、医嘱表。HIS、EMR、LIS、RIS 等系统都是围绕这些离散事件构建的。
但“健康”这个词描述的不是某一个时间点上的发生情况,而是一段时间内的连续状态。比如一个人的血压,早晨和晚上不同,活动前后不同,焦虑和放松时也不同。单看某一次测量,很难判断这个人是否正在变好或变差。健康管理系统的数据对象是在时间轴上连续采样的指标序列,而不是一张张孤立的诊疗单据。
这种区别直接影响数据库设计、接口设计、算法逻辑和产品交互。事件系统关注“有没有发生”,状态系统关注“趋势是否变化”。如果用事件系统的思路去管理健康,容易出现“只做检查记录、不做趋势判断”的局面——数据越来越多,但没有转化为干预行为。
1.2 健康管理需要连续数据、基线评估和趋势判断
要让系统从“记叙文”变成“管理工具”,需要具备四类基础能力:
- 连续采集:以固定频率获取用户健康指标,比如每天一次血压、心率、睡眠时长,每周一次体重和运动次数。
- 基线评估:为用户建立个人“正常范围”。每个人的身体条件不同,不能只用通用参考区间做判断。
- 异常判定:既要判断当前值是否超标,也要判断当前值是否明显偏离个体基线。
- 趋势预测:基于最近一段时间的数据预测未来走向,比如评分连续下滑时触发预警。
其中“基线”是很多健康管理项目最容易忽略的部分。通用参考区间解决的是“这个人是否超出医学正常值”,但无法告诉你“这个人相对自己的通常状态偏移了多少”。举个例子:用户甲的收缩压基线是 105 mmHg,今天测到 130 mmHg;用户乙的基线是 125 mmHg,今天测到 135 mmHg。前者虽然数值没有超过通用参考上限 140,但相对自身基线升高了 25 mmHg,显然更值得关注。这种判断依赖于历史数据,而不是单次测量结果。
趋势判断则要在基线上再进一步。它关心的是“最近 7 天评分均值”与“前 7 天评分均值”相比是否下降,或者连续多天的指标是否指向同一个方向。实现趋势判断时,可以用简单滑动平均、指数移动平均,也可以用时间序列模型。对最小系统来说,先选择可解释、易实现的移动平均即可。
1.3 从“事后救治”到“事前干预”的架构变化
传统系统的业务链条以“治疗”为终点:病人出现症状,前往医院,医生诊断,开药或手术,记录病历。健康管理平台的业务链条必须是闭环:用户主动或被动产生健康数据,系统汇总数据,评估风险,生成预警,触发干预,干预后再次测量,验证干预是否有效。
从架构上看,这个闭环需要增加几个传统 HIS 不常具备的模块:
- 数据采集网关:接收来自手机 App、可穿戴设备、体检系统、问卷表单的数据。
- 数据清洗模块:处理重复值、极端值、单位不一致等质量问题。
- 评分引擎:将多个指标映射到统一分制。
- 规则引擎:根据评分和阈值生成预警。
- 干预任务模块:生成随访工单、健康提醒或人工随访任务。
- 复测反馈模块:记录用户是否执行干预,以及后续指标变化。
这些模块并不复杂,但它们改变了系统的定位。以前系统是“记录发生了什么”,现在系统要回答“接下来应该做什么”。这个转变是健康管理平台与传统医疗信息化平台最大的区别,也是标题“我们更擅长让人活着,却不擅长让人健康”在技术层面的落点。
2. 最小平台的核心模型:先管好五类指标
2.1 指标集合不要贪多,先选可解释、易采集的指标
健康指标非常多,并不需要一开始就全部接入。对于最小闭环系统,优先选择五类具有明确健康含义、采集成本又较低的指标:
| 指标 | 常见来源 | 在健康管理中的作用 |
|---|---|---|
| 收缩压/舒张压 | 家用血压计、体检报告 | 反映心血管风险 |
| 静息心率 | 可穿戴设备、手动测量 | 反映心脏负荷 |
| 睡眠时长 | 手环、手机记录 | 反映恢复能力 |
| 身高体重计算出的 BMI | 体重秤、体检报告 | 反映代谢风险 |
| 每周中等强度运动次数 | 问卷、运动 App | 反映生活方式 |
选择这五类指标的原因有三个。第一,它们都能用数值表达,便于后续评分和趋势比较。第二,它们与生活方式和慢病风险高度相关,适合长期跟踪。第三,用户对这些指标有基本认知,系统给出的解释不会像“炎症因子组合评分”那样难懂。
演示项目中还会加入一个可选的空腹血糖字段。它不属于最小必要集,但可以展示“指标缺失时评分如何处理”。字段设置为可空,缺失时不会影响整体流程。
2.2 个体基线:判断异常要拿历史数据和自己比
基线不是一个固定数值,而是一个随历史数据变化的动态参考。最简单的方式是对每个用户的每个指标维护一个指数移动平均(EMA)。EMA 比简单算术平均更重视近期数据,能够更快反映身体的近期变化。
EMA 的计算公式是:
alpha 通常在 0.2 到 0.4 之间。alpha 越大,基线对你近期的变化越敏感,但也越容易受单次异常值干扰。对血压、心率这类容易波动的指标,alpha 可以设小一点,比如 0.2;对体重、运动次数这类变化缓慢的指标,alpha 可以设大一点。
有了基线后,指标相对偏移量可以定义成:
偏移量是百分比,用来判断“偏离了个体正常水平多少”。这个偏移量会作为预警规则的辅助条件。比如收缩压偏移量超过 10% 时,即使数值仍在参考区间内,系统也可以提示动态风险。
2.3 风险因子组合要可解释,不要黑盒
健康评分不能是“一个不知怎么算出来的数字”。用户和医生都需要知道分数背后是什么。更合理的设计是把每个指标映射成 0-100 的子得分,再按权重合成总分。
每个子得分都对应一段明确的数值区间。比如收缩压在 90-120 mmHg 区间得 100 分,130-139 mmHg 得 60 分,140 以上得 30 分。具体区间可以后期根据医学知识和真实数据调整,但“每个区间可以解释”这一结构不能去掉。
权重分配也应该是可调整的配置,而不是硬编码在算法里。最小系统中可以使用以下初始权重:
- 血压子分:30%
- 静息心率子分:15%
- BMI 子分:20%
- 睡眠时长子分:20%
- 运动次数子分:15%
这里的权重代表业务侧对风险重要性的判断。血压权重最高,因为心血管事件往往与血压波动直接相关;运动与睡眠权重次之,因为它们反映长期生活方式的积累。生产环境中可以通过回溯历史数据来校准权重,但即便使用机器学习模型,也要保留对风险等级的解释接口。
3. 环境准备与项目结构:用 FastAPI + SQLite 搭建闭环
3.1 依赖清单
Demo 使用 Python 3.10+,核心依赖如下:
FastAPI 负责提供 REST API,SQLAlchemy 负责 ORM 和建表,Pandas 和 NumPy 用于批量计算和趋势分析。SQLite 适合本地演示,不需要额外安装数据库服务。如果是正式项目,可以把数据库连接串替换成 PostgreSQL,其他逻辑基本不用改。
安装命令:
如果是在全新环境中运行,建议先创建虚拟环境:
3.2 项目目录结构
为了不让代码挤在同一个文件里,项目按职责拆成模块:
这个结构足够应对最小系统。如果继续扩展,可以把评分引擎和规则引擎拆成独立服务,但初期不需要。保持简单是为了让业务闭环更容易被看清楚。
3.3 数据表与字段设计
模型共三张表:用户表、健康记录表、预警表。健康记录表是核心,预警表由评分引擎自动写入。
models.py 中的核心表结构如下:
record_date 使用 Date 类型而不是 DateTime,因为健康管理通常按天汇总。对同一用户同一天的多条测量,可以选择取平均后写入,也可以在应用层做合并。UniqueConstraint 防止重复插入同一天的数据,这是避免预警重复生成的第一道保障。
4. 实现健康评分与预警规则
4.1 评分计算:从分项到总分
每个指标先映射成 0-100 的子分。以下是演示用的映射规则:
| 指标 | 优秀(100分) | 良好(80分) | 关注(60分) | 高风险(40分以下) |
|---|---|---|---|---|
| 收缩压/舒张压 | <120 / <80 | 120-129 / 80-84 | 130-139 / 85-89 | ≥140 / ≥90 |
| 静息心率 | 60-80 | 50-59 或 81-90 | 91-100 | >100 或 <50 |
| BMI | 18.5-23.9 | 24-27.9 或 18.5 以下 | 28-31.9 | ≥32 |
| 睡眠时长 | 7-9 小时 | 6-7 或 9-10 小时 | 5-6 或 10-11 小时 | <5 或 >11 |
| 运动次数 | ≥5 次/周 | 3-4 次/周 | 1-2 次/周 | 0 次/周 |
总分采用加权平均。如果某个字段缺失,不直接返回 0,而是把剩余字段的权重重新归一化。比如血压缺失时,剩余四项按比例分配 100% 权重。如果缺失项超过两项,则认为当天数据不足,不计算总分,返回“数据不足”状态。
4.2 关键代码实现
scoring.py 负责计算评分,核心代码如下:
这段代码有两个关键点。第一,权重重归一化:当某个字段缺失时,剩余字段权重的比例不变,总分仍可计算。第二,风险等级不是单纯使用“低于阈值就报警”的坏值判断,而是结合“数据不足”状态,避免在信息缺失时误下结论。
4.3 预警触发条件
保存一条健康记录后,系统会立即判断是否需要生成预警。最小系统使用三条可叠加的触发规则:
- 规则一:当天风险等级为
high或very_high。 - 规则二:当前评分比最近 7 天平均分下降超过 10 分。
- 规则三:连续两天睡眠时长低于 6 小时。
三条规则中有任意一条命中,就生成一条 Alert。预警内容需要包含具体的风险原因,不能只写“用户健康状况变差”。实现示例:
预警表需要限制同一用户同一天不能重复生成同类预警。生产环境还会增加预警指纹,比如“同一规则在同一个滚动窗口内最多触发一次”,防止高频数据导致短信轰炸。
5. 运行验证:造数据、调接口、看趋势
5.1 生成模拟健康数据
为了验证流程,需要一批模拟数据。演示脚本会创建三个用户,并为每个用户生成 60 天的记录。其中一个用户会故意设计成“前 40 天状态良好,后 20 天血压和睡眠逐步变差”,这样便于观察趋势下降和预警触发。
脚本核心循环如下:
运行脚本:
生成数据后,可以通过 SQL 或接口检查记录数量:
5.2 启动服务并验证接口
启动 API:
提交一条新的健康记录:
返回结果中会包含保存后的评分和风险等级:
查询用户最近 30 天的趋势:
返回的是按日期排序的评分数组,可以直接用于前端折线图。
5.3 结果分析:趋势分比单次分更重要
如果查看第三个用户的趋势数据,你会看到这样一段模式:
单看 2 月 25 日的 84 分,风险等级是低,但后面 5 天一路下滑到 59.7。如果系统只输出当天分数,用户可能直到风险等级进入 high 才收到预警。加入“较 7 日平均分下降超过 10 分”的规则后,下降过快本身就会触发预警,不需要等到分数跌破危险线。这就是健康管理与传统医疗事件记录的核心差别:连续值与变化量比孤立值更有管理价值。
6. 从可运行 Demo 到生产环境:还要补哪些能力
6.1 数据接入与设备兼容
生产环境的数据来源通常比 Demo 复杂得多。可穿戴设备、手机系统健康能力、体检系统、医院信息系统会输出不同格式的数据。设备之间还会存在测量误差和单位不一致。
需要在接入层完成几件事:
- 制定标准化指标字典,把不同厂商的字段名映射到统一指标码。
- 做异常点过滤,比如血压为 0、心率为负数、睡眠时间超过 24 小时这类脏数据要直接拒绝入库。
- 实现设备来源和用户授权关系表,避免无授权设备的数据流入。
- 保留原始数据快照,方便后续核对指标转换逻辑。
6.2 隐私合规、权限和审计
健康数据属于敏感个人信息,生产系统必须从设计阶段就纳入合规考虑。最小化措施包括:
- 数据最小化原则:只采集当前业务必需的指标。
- 传输和存储加密:数据库连接使用 SSL,敏感字段加密或使用密码学哈希。
- 访问审计:记录谁在什么时间读取了哪个用户的健康数据。
- 匿名化导出:用于数据分析或算法训练时,去掉可识别身份的信息。
这些工作虽然不直接影响评分算法,但决定了平台能否通过监管评估。Demo 中可以直接跳过,生产环境不能省。
6.3 模型校准、可解释性和人工复核
本文的评分映射表是演示用的,不能直接上线。上线前需要做后台数据回测:用过去一年或几年的脱敏数据,测试不同区间和权重下的风险分布是否合理,预警命中率是否被医生认可。
评分模型还要做周期性校准。人群健康水平会变化,指标阈值可能需要调整。每次调整都应有版本记录,比如 score_model_version 字段,否则之后的趋势分析无法对齐历史数据。
同时,系统不应该自动下诊断结论。预警信息可以推送给用户或随访护士,但最终是否要建议就医、是否需要调整用药,必须由有资质的人员人工复核。Demo 中可以直接生成预警,生产环境需要增加“复核人”“复核结果”“处理状态”等字段。
6.4 预警触达和干预闭环
预警发出去只是开始。真正让用户变健康的是后续干预,而干预必须形成反馈。建议在数据模型中增加两个概念:
- 触达记录:通过 App 推送、短信、微信模板消息等渠道触达用户后的状态(已发送、已读、未读)。
- 随访任务:由系统或个案管理师发起,包括任务内容、执行人、执行时间、用户反馈和复测结果。
只有把“测量—评估—预警—触达—随访—复测”全部串起来,评分才有意义。这也是健康管理平台和 BI 报表最大的区别:前者产生行动,后者只产生图表。
生产落地前可以对照这份检查清单逐项确认:
| 检查项 | 说明 | Demo 是否包含 | 生产是否需要 |
|---|---|---|---|
| 数据采集授权 | 用户知情同意 | 否 | 是 |
| 数据质量规则 | 非法值过滤 | 部分 | 是 |
| 评分版本管理 | 记录模型版本 | 否 | 是 |
| 预警去重 | 避免重复打扰 | 部分 | 是 |
| 人工复核流程 | 医生确认结论 | 否 | 是 |
| 触达追踪 | 记录推送状态 | 否 | 是 |
| 审计日志 | 记录数据访问 | 否 | 是 |
| 监控告警 | 监测数据链路异常 | 否 | 是 |
7. 常见问题与排查路径
7.1 评分结果波动过大
现象:同一个用户相邻两天的评分从 90 分掉到 55 分,但原始指标看起来没有剧烈变化。
可能原因:某一天某个字段缺失,导致权重重归一化后其他字段的影响被放大。比如血压字段缺失,原本只占 30% 的血压权重被分摊到剩余四项,如果心率或睡眠当天刚好偏差,总分波动就会明显变大。
检查方式:打印 score_result["details"],对比两天的子分和可用字段数量。
处理建议:固定缺失值处理策略。可以规定每天至少要有哪几个核心字段,比如血压、睡眠、BMI 必须存在,否则不显示完整总分,只显示“数据不足”。不要用“动态归一化 + 高风险判断”的组合,否则会产生大量误报。
7.2 某类指标缺失导致评分无法计算
现象:接口返回 risk_level: insufficient_data,但用户实际上传了大部分指标。
可能原因:最小必要指标数量判断为“少于 3 个字段则数据不足”,而用户当天只上传了血压和心率两个字段,因此被判定为不可计算。
检查方式:查看评分逻辑中 available 列表的长度。
处理建议:将“数据不足”阈值改成“必须包含的核心字段集合”,而不是简单的字段数量。比如血压、睡眠时间必须存在,其他字段可以缺失。否则会出现用户上传了两项重要指标,但系统仍然拒绝评分的情况。
7.3 预警不触发或重复触发
现象一:风险等级已经进入 high,但没有收到预警。
可能原因:预警写入失败,或者数据库中的 alert_date 使用了严格唯一约束,而当天已经有一条相同规则生成的预警。
检查方式:查看服务日志和 health_alerts 表的数据,使用 select * from health_alerts where user_id=? order by alert_date desc; 检查当天是否已有记录。
处理建议:在预警函数中处理“同一天同规则是否已存在”,存在则跳过。同时给 user_id + alert_date + rule_code 加唯一索引。
现象二:连续多天重复触发同一条规则,用户被频繁打扰。 可能原因:没有设置静默窗口。评分一旦进入高风险,每天都会触发,推送方没有收到任何去重信号。
处理建议:增加静默期字段,比如同一个规则 7 天内只推送一次;或者把预警状态改为 feedback_required,只有用户反馈或人工处理后才能重新触发。
7.4 排查优先级表
遇到问题时,按照下列顺序排查:
| 优先级 | 检查点 | 验证方法 |
|---|---|---|
| 1 | 输入数据是否有误 | 打印请求体和数据库原始记录 |
| 2 | 字段名是否匹配 | 确认 JSON 中字段名与 Pydantic 模型一致 |
| 3 | 日期是否有重复 | 查询 record_date 和唯一索引 |
| 4 | 评分逻辑是否入了脏数据 | 单独调用 compute_score() 调试 |
| 5 | 权重归一化是否异常 | 检查 available 和 total_weight |
| 6 | 预警规则条件是否覆盖 | 把所有规则条件逐一人工验证 |
| 7 | 推送渠道是否故障 | 查看推送服务日志和渠道配置 |
这套排查顺序适用于大多数健康数据系统。先排除数据问题,再排查算法问题,最后才看外部依赖问题。
健康管理平台的实现重点从来不是“做出一个健康分”,而是把连续数据、个体基线、趋势判断、风险预警和干预复测串成闭环。文中的最小系统虽然只覆盖了一部分,但已经能回答一个关键问题:当我们说“管好健康”而不是“治好疾病”时,技术系统到底需要哪些能力。下一步可以尝试接入真实设备数据,或者用时间序列模型对评分趋势做预测,再进一步对接随访任务引擎,把一个单点评分扩展成完整的慢病管理工具。对新手来说,最有价值的练习不是增加更多指标,而是用极端数据去测试评分和预警规则,观察系统会不会误判、会不会漏报。只有先处理好这些边角情况,健康管理平台才真正具备实用价值。