2025 年 10 月 22 日,我给一个空的 Xcode 工程提交了第一次代码。一年出头,578 次提交之后,它成了 App Store 上的 100 Years Life。
这个 App 最初只有一个用户,就是我自己。我自己有一些基础病,平时又很喜欢运动,还是个数据党。体检报告、体脂秤、睡眠、运动,每一样我都留着数据。
问题是这些数据不在一处。
数据都有,只是散着
- 体检报告是体检机构发来的 PDF,一份二十多页。
- 体脂秤和健身房体测各有各的 App,或者干脆是一张长图。
- 睡眠在 Apple Watch 和 AutoSleep 里,两边的数字还会对不上。
- 运动记录在手表和几个健身 App 里。
每个 App 只看自己那一块。体检报告上某项指标偏了,没有哪个 App 会把它和我这三个月的睡眠、训练量放在一起看。我想要的就是这件事:把这些数据收进一个地方,按时间排好,再让 AI 帮我读。
一开始是给自己写的小工具,写着写着,就成了现在这个样子。
它现在在做什么
先说数据是怎么进来的:
| 数据 | 进来的方式 |
|---|---|
| 心率、HRV、睡眠、血氧等日常指标 | 授权后从 Apple 健康读取,目前支持 159 项指标 |
| 睡眠 | Apple Watch 或自己选定的睡眠 App |
| 运动 | Apple Watch 记录自动导入;其他健身 App 的记录靠截图识别;也可以手动记 |
| 体检报告、化验单 | PDF 或拍照导入,识别后逐项核对再保存 |
| 体脂秤、体测报告 | 截图或长图导入 |
| 病史、用药 | 手动录入,或从病历文档里识别后勾选导入 |
进来之后,每天打开看到的是「今天」页:今日身体状态、恢复情况、身体年龄和几项长期指标。每晚睡眠和每次运动各有一份复盘报告。数据页里每一项指标都能按 7 天、30 天、90 天看走势,体检指标和日常数据可以放在一起对照。手表上有配套的 App 和表盘组件,数字和手机上是同一份。

百岁预测只是其中一步
App 叫 100 Years,首页也有一个「活到 100 岁的可能性」。这个数字最容易被记住,但它只是其中一小步,也是被我推倒重写过的一块。
最早的版本(2025 年 11 月)做法很直接:把数据交给大模型,让模型给出预测数字。用了一段时间发现,同一份数据跑两次,数字会不一样。一个会漂的数字没法拿来看趋势。
今年 8 月我把这部分重写了。现在所有数值都由 App 里的确定性算法计算,同样的输入一定得到同样的输出;AI 只负责解释这些数字、给一般性的健康建议,改不了数。
10 月又对算法依据做了一轮审查,删掉了几条依据不足的规则。手表测的 HRV(SDNN)不再影响寿命和身体年龄的数值,手表估计的深睡时长也不再影响寿命评分。消费级设备这两项测量的精度,不够支持这么重的结论。HRV 还在,只用在「相对我自己的基线,今天恢复得怎么样」这种地方。
App 里也写明了:这是基于人群统计和个人数据的模型估计,没有经过外部人群队列的校准,不是医生给出的预测。
我自己更常看的是趋势:身体年龄这几个月往哪边走,HRV 相对自己的基线是高是低,训练量有没有超过恢复能力。

花时间最多的,是把数据读对
这一年里,大部分时间花在了 AI 之前的那一步。举四个我自己撞上的例子。
睡了 6 小时还是 8 小时。 有一阵 App 显示我睡了 6 小时,AutoSleep 显示将近 8 小时。原因是合并两个来源时,范围更宽的那份数据被丢掉了。后来改成和 Apple 健康一样的做法:一个时段只认一个来源算总时长;如果这个来源没有睡眠分期,就从手表的数据里借,再按这个时段裁剪。
体脂率 17.7% 被读成 10。 导入一份健身房体测报告,体脂率 17.7% 成了 10,基础代谢 1642 千卡成了 25。查下来,要找的指标名词表里都有。出错的地方在文字识别之后:整页文字被压成了一长串,每个词在页面上的位置全丢了,程序在这一串字里找「体脂率」后面的第一个数字,找到的是旁边图表的坐标轴刻度。
修法是保留每个词的位置,按版面上的几何关系给指标名和数值配对。另外加了一道检查,比如去脂体重里的水分占比应该在 73% 左右,对不上的数值直接拒收。这次我给自己定的要求是:不能每换一个牌子的体脂秤就调一次规则,遇到新厂商只加测试样本。
24 页的体检报告。 旧逻辑取「指标名后面的第一个数字」。在真实报告里,这个数字可能是上次体检的结果,可能是参考范围的下限,也可能是历年对比表里的年份。有一项指标被读成了 1,那个 1 是解读段落的序号。改成先认表头、只读「本次结果」那一列之后,我这份报告的 76 项指标全部读对了。导入界面会显示每一项对应的原文,保存前要自己核对一遍。
年轻 2.2 岁,衰老速度却是 1.18 倍。 这两个数字曾经出现在同一个屏幕上,看起来互相矛盾。前一个是当前水平,后一个是近期变化,方向相反说得通。有问题的是 1.18 这个数:它只用了 32 天的数据,折算成一年要放大 11 倍左右,对应的实际变化只有 5.8 天,比屏幕上最小的显示精度 0.1 岁(36.5 天)还小。现在的规则是,变化量足够大、历史足够长才显示方向,否则只说「目前还判断不出来」。
来源冲突、版面、单位、噪声,这些事不解决,AI 读到的就是一份错的数据,解读写得再通顺也没用。

AI 在哪里跑,数据放在哪里
健康数据很私密,这部分我按自己能接受的标准来做:
- 健康数据存在手机本地。我没有运营任何服务器。
- 体检报告和截图先在手机上用系统自带的文字识别提取内容,图片本身不会发给文本模型。
- AI 解读有三种运行方式:全部本地(把 Gemma 或 Qwen 模型下载到手机上运行)、全部云端、两者平衡。云端用的是自己填的 API key,手机直接连服务商。
- 每条 AI 结果都带一张回执,写明这次用的是哪个模型、在本地还是云端跑的、用到了哪几类数据。回执里不保存 prompt 和原始健康数值。
- 不需要注册账户,建一个本地档案就能开始用。
代价也有。手机上跑的小模型,解读没有云端大模型细。选全部本地,就是拿一部分解读质量换数据不出手机。

最近加的:夜间身体信号
11 月 8 日加了一个新功能,起因是我听了一期 Eight Sleep 的访谈。
做法是拿每晚的静息心率、HRV、呼吸频率、手腕温度,和自己过去四周左右的夜间数据比。有两项以上同时偏出平时的范围,恢复卡片上就会提示。
依据来自几项可穿戴设备的研究。Stanford 的 NightSignal 研究(Nature Medicine,2022)只用夜间静息心率,就在约 80% 的新冠确诊病例出现症状的当天或之前给出了提醒,中位数提前约 3 天。同一项研究也写了,大部分提醒另有原因:旅行、大餐、压力、喝酒。
所以 App 里的措辞是「夜间信号偏离你的日常范围」,同时列出前一天的运动量、饮酒、睡眠不足这些可能的原因,不会说「要生病了」。推送默认打开,只在偏离比较明显时发,同一次变化只推一条,两条之间至少隔 7 天。

它做不到的事
- 它不是医疗器械,不做诊断,也不给治疗建议。有基础病该复查复查、该看医生看医生,它帮我做的是两次体检之间的记录和观察。
- 百岁数字和身体年龄都是模型估计,没有经过外部人群队列的验证。
- 数据缺了就显示缺,不会补一个看起来合理的数。刚装上、数据少的时候,不少卡片是空的。
- 只有 iPhone 和 Apple Watch 版本。我的本职是做 Android 性能优化,自己的 App 却没有 Android 版。
- 还没做完的事:夜间身体信号还没同步到手表;生理周期对体温的影响还没有处理。
怎么用,怎么收费
在 App Store 搜索「100 Years Life」,或者直接打开中国区链接 https://apps.apple.com/cn/app/100years/id6779898766(美区:https://apps.apple.com/us/app/100-years-life/id6779898766)。需要 iOS 17 及以上,手表端需要 watchOS 10 及以上。
免费版可以使用所有页面和全部算法结果,体检报告、体脂秤导入不限次数,JSON/CSV 导出免费;配置好模型或服务商后,AI 分析每天可以发起 1 次;预测历史保留最近 3 条。
一次性买断之后,AI 分析不限次,可以看完整的历史和趋势,可以导出 PDF。没有订阅。
我自己每天还在用它,功能也基本是跟着自己的需要一个一个加的。装上之后,哪个数据读错了,哪一页看不懂,欢迎到内测群里告诉我。二维码过期的话,加微信 553000664。
