Atmosis这个名字是我从 atmosphere 和 osmosis 两个词里拼出来的,想法也很直白:让环境数据像渗透一样,自然融进每个人的日常健康决策。做了大半年,从最初一个只会读传感器数据的脚本,慢慢长成了一套能输出健康建议的AI环境智能系统。市面上查空气质量、查花粉指数的App很多,但大多数止步于"告诉你环境怎么样",而Atmosis想多走一步——告诉你"在这种情况下,你该怎么做"。这篇文章就把整个项目的设计思路、系统架构、核心算法和踩坑记录完整拆开,给同样想做环境智能或健康顾问方向的朋友一些可以直接参考的经验。
1. 为什么"看得见环境数据"和"知道该怎么做"之间隔着最后一公里
1.1 环境数据平台普遍只到"状态层",没有到"行动层"
我一开始也觉得奇怪,环境数据已经这么丰富了,怎么大家还是不满足?后来想明白一个关键问题:绝大多数产品提供的是"状态",不是"行动"。状态是"当前PM2.5为75微克每立方米,空气质量指数105",行动是"你有过敏性鼻炎,今天风小湿度高,出门建议佩戴N95口罩,并且避开早高峰路段"。
前者只是把传感器和气象站的数字搬到了手机屏幕上,后者才真正影响人的行为决策。而这个"翻译"过程,恰恰是最费劲、最需要结合多维度数据的。天气App不会知道你家里有哮喘的老人,不会知道你今天的通勤路线要路过一片正处在盛花期的行道树,也不会关心你昨晚没睡好、今天心肺负担本来就偏重。传统环境监测产品的信息架构就没有为"个人化行动建议"留出位置。Atmosis想补上的,正是这个从环境状态到个人行动的推理层。
1.2 同样的空气,对不同的人是完全不同的风险
同样一个空气质量指数为120的早晨,对一个心肺功能正常的年轻人来说可能只是"出门有点不舒服";但对一个尘螨和花粉双重过敏的患者来说,这可能意味着接下来一整天的鼻塞、结膜充血,甚至诱发哮喘发作。再叠加湿度和风速——湿度高会让花粉颗粒吸水膨胀、更容易破裂释放过敏原;风速低则让污染物和花粉在地面附近堆积。这些因素单看都不起眼,组合起来风险等级完全不一样。
所以环境健康顾问必须做两件事:第一,把多维环境信号压缩成"暴露风险等级";第二,把风险等级结合个体画像翻译成具体动作。只做第一件事,那就还是环境监测,不是健康顾问。这两步合在一起,才是Atmosis被称为"Intelligence & Health Advisory"而不是"Air Quality Monitor"的原因。
1.3 目标用户与适用场景界定
项目早期我犯过一个典型错误——想把所有人群、所有环境一网打尽。后来痛定思痛,把目标用户收敛成了四类:过敏体质人群(花粉、尘螨、霉菌过敏)、有呼吸系统慢病的人群(哮喘、慢阻肺)、需要精细化环境管理的家庭(有婴幼儿或高龄老人),以及注重运动表现的户外运动爱好者。
适用场景也做了裁剪:通勤出行规划、室内通风换气决策、户外运动时段选择、家庭环境异常预警。这四个场景有一个共同点——环境变量对人的影响在短时间内就能显现,用户能直观感受到建议是否有效,这为后续的产品迭代和信任建立带来很大便利。如果一开始就去做"环境与肿瘤长期发病率关联"这类超长周期的判断,既缺少数据,也缺少用户反馈闭环。
2. 系统骨架:四层单向流动的架构
2.1 感知层:室内传感器与外部数据源的分工
Atmosis的数据感知层有两类来源,分工非常明确。室内部分靠自建的物联网传感器节点,采集温度、相对湿度、PM2.5、PM10、二氧化碳浓度、TVOC(总挥发性有机物)。这些节点体积不大,每个房间放一个,通过Wi-Fi上报数据。室外部分则对接开放数据接口,拉取气象预报、空气质量指数、花粉浓度、紫外线强度。两类数据的语义完全不同:室内数据回答"你现在所处的微观环境怎么样",室外数据回答"你即将进入的宏观环境怎么样"。
为什么室内要自建传感器而不是直接买现成的?因为健康建议对数据完整性要求很高。现成智能家居设备通常只上报温湿度和PM2.5,缺少二氧化碳和TVOC。而二氧化碳浓度是判断室内通风质量的黄金指标——当浓度升到1000ppm以上时,人会出现嗜睡、注意力下降,这对居家办公和儿童学习的场景非常关键。没有CO2数据,通风建议就只能靠猜。这一层我的建议是:传感器可以不用买贵的,但一定要保证覆盖关键指标,宁可精度差一点,不能缺维度。
2.2 处理层:清洗、校准与时间对齐
感知层的数据是原始的、充满噪声的,直接喂给模型会出事。处理层做三件事:清洗、校准、对齐。
清洗主要过滤明显异常值——传感器信号跳变、离线补传导致的重复数据、户外接口偶尔返回的空值。经验是不要用全局均值填充缺失值,而要用前后时间窗的线性插值。比如温度传感器五分钟没上报,用前后两个有效值的线性插值补上,比用全天平均值填充要合理得多。
校准针对的是低成本传感器的长期漂移。便宜的电化学PM2.5传感器用半年之后,零点漂移可能让数据整体偏高20%以上。解决思路是周期性零点校准,同时与附近固定监测站的公开数据做交叉验证。我曾做了一版自动校准脚本,每周自动拉取距离最近的几个参考站点的数据,对自家传感器读数做线性回归修正。这么做之后,室内PM2.5数据和参考站的偏差从平均每立方米15微克降到了5微克以内。
时间对齐是处理层最琐碎但又最重要的一环。不同数据源的时间戳语义不一样,有些接口返回的"当前小时花粉指数"其实滞后了一小时。统一做法是:所有数据在进入特征层之前,一律先转换成标准UTC时间戳存储,并且记录每个数据源的"数据时间"和"到达时间"两个字段。任何算法只认数据时间,不认到达时间,这个问题在后面真正跑线上时让我吃过大亏,第5节会详细讲。
2.3 特征层与画像层:把原始值变成风险画像
处理完之后的数据还不能直接用。特征层的主要工作,是把时间序列转成有物理含义的统计特征。举例来说,原始数据是"过去24小时每小时一个PM2.5浓度值",特征层会计算:当前值、3小时滑动平均、24小时滑动平均、变化率(现在比3小时前高还是低)、以及"当前浓度超过同季度历史80分位"这类相对排名指标。
同样的处理也应用到温度、湿度、风速、花粉指数上,最终拼成一个几百维的特征向量。之所以用滑动窗口而不是只看瞬时值,是因为人体对环境刺激的反应有滞后性,哮喘发作往往不是被某个瞬时峰值触发,而是被"持续几小时的高浓度暴露"触发。只看瞬时值会漏掉这种累积效应。
画像层单独存放用户的静态和半静态信息:年龄、是否有过敏史(花粉/尘螨/霉菌)、是否患有哮喘或慢阻肺、日常通勤时段、常用出行方式、是否佩戴运动手环等。画像层不与传感器数据混在一起存储,而是独立成表,只有在生成建议时才会被特征层调用。这样设计的好处是:用户更换设备、更换传感器品牌、甚至搬家迁移部署环境时,健康画像不会丢。
2.4 推荐层:建议输出与展示策略
推荐层拿到风险等级和个人画像之后,输出的是结构化的建议对象,包含四个字段:动作建议、可选方案、置信度、时间窗口。动作建议是主推的行动,比如"关闭朝南窗户并开启新风系统";可选方案给用户选择空间,比如"如果必须外出,请佩戴N95口罩并缩短停留时间";置信度表示模型对自己判断的把握程度;时间窗口告诉用户这条建议什么时候到期。
展示策略上踩过不少坑。最初我让建议一生成就弹到用户手机屏幕上,结果一个月后通知关闭率达到七成。后来改成"静默待命+关键节点推送":轻度风险时只更新App内页面,中度以上风险才推送通知,极高风险直接用强提醒。置信度低于0.6的建议一律不主动推送,而是让用户手动查询时展示。这套策略上线之后,通知关闭率降到了两成以内。低置信度内容不打扰用户,是高可信度建议的保障。
2.5 为什么流程必须是单向的
整个架构我强制规定数据只能从感知层往推荐层流动,禁止反向调用。也就是说,推荐层不能反过来要求感知层"现在立刻把传感器精度调高一点"。原因很简单:健康决策系统最怕的就是反馈回路失控。如果推荐层能反向影响数据采集,理论上一旦建议规则写了一行Bug,可能导致采集层数据被篡改,而后续建议又基于被篡改的数据继续生成,最后整个系统都不可信。单向流动虽然牺牲了一些灵活性,但最大程度保证了每一步的输入输出都是可审计、可复现的。
3. 从监测到建议:两阶段推理模型的设计思路
3.1 为什么不用端到端深度学习做"空气到建议"
做AI环境健康顾问,最直觉的方案是收集大量"环境数据+用户行为+健康反馈"的配对样本,训练一个端到端的深度学习模型,输入环境序列,直接输出建议。我评估过这个方案,最终否掉了,原因有三点。
第一,健康标签太稀疏。用户不会每天记录"今天鼻炎发作了几次",更不会把每次发作都归因到环境因素。拿不到稠密、准确的标签,监督学习就无从谈起。第二,可解释性需求极强。健康建议不同于商品推荐,用户会追问"为什么让我今天别晨跑"。端到端模型给不出病历级解释,而规则引擎可以把推理链路一条条列出来。第三,冷启动问题。新用户没有历史反馈数据,端到端模型只能随机猜测,而规则引擎至少能覆盖"大众健康阈值"这类基线知识。
所以最终方案是两阶段推理:先用一个机器学习模型把多维环境特征压缩成暴露风险等级,再用一个规则引擎根据风险等级和用户画像选择具体建议。机器学习和规则各管一段,各发挥各的优势。
3.2 阶段一:环境暴露风险分级模型
第一阶段的核心任务是4分类——把当前环境判成低、中、高、极高四个暴露风险等级。模型输入是特征层生成的几百维特征向量,输出是四个等级的概率分布。我尝试过多种模型,实践下来梯度提升树家族的效果和工程性价比最高。一方面它对特征尺度不敏感,不需要做复杂的归一化;另一方面它对缺失值天然有容忍度,某个传感器故障时结果不会立刻崩溃。树模型另外的好处是能输出特征重要度,这对我做可解释性非常有帮助。
训练数据从哪里来?公开的环境监测历史数据可以当作"环境侧"的数据集,但"风险等级"标签怎么打?这里用了一个关键技巧:把医学指南和临床文献里的暴露阈值转化为标签规则。比如世界卫生组织的PM2.5日均指导值、各国过敏学会的花粉等级对应症状概率表,把它们组装成"如果PM2.5高于X且湿度高于Y,则判定为高风险"这类先验规则。然后用这些规则给历史环境数据自动打标签,再让树模型去拟合。这本质上是一种弱监督方案——规则不完美,但能提供足够的信号让模型学到跨维度的非线性组合。
线上推理时,模型返回四个等级的概率,但不会直接硬取最高等级。我加了一个保守偏置:如果"高"和"极高"的概率都超过0.2,就向上取一级。环境健康领域误报的危害小于漏报,宁可建议更谨慎一些,不能漏掉真正的高风险时刻。
3.3 阶段二:基于规则模板的建议匹配引擎
第二阶段接收风险等级和用户画像,输出具体建议。这一层用的是白盒规则引擎,逻辑如下:先根据画像过滤出适用于该用户的建议模板池,再根据风险等级和当前环境特征在模板池里做条件匹配。模板长这样:
- 场景:通勤出行
- 条件:风险等级为"高" 且 用户有花粉过敏史 且 当前风速低于3米每秒
- 动作:建议改乘公共交通或地下通勤路线;若骑行,佩戴密闭性更好的口罩,并把出发时间后延到风起之后
- 备选:若用户选择"必须按原计划出行",则建议缩短户外停留时间并回屋后立即清洗面部和更换衣物
规则模板的来源分两类:一类是循证医学指南的改写,比如花粉过敏人群在高浓度时段应减少户外活动、关闭门窗;另一类是环境工程常识的结构化,比如室内外温差大时长时间开窗会导致湿度倒灌、霉菌滋生。两类知识都用"条件-动作"的统一格式存放,方便维护和审计。
这里有一个设计细节:规则引擎永远不会直接对用户说"你可能要得哮喘了"这类诊断性语言。Atmosis的边界是环境顾问,不是医生,所有输出都是行动建议和预防性提示,不做疾病预测。边界一旦划清,规则设计就简单了,产品的法律和伦理风险也小了很多。
3.4 置信度与"安静模式"的权衡
模型概率和规则匹配两者都会输出一个0到1的分数,置信度取两者的几何平均。比如风险分级模型给出"高风险"的概率是0.85,规则匹配的满足度是0.8,最终置信度大概是0.82。置信度低于0.6的结论只记录不进推送;0.6到0.8之间展示但不打扰;高于0.8才触发通知提醒。
这套分级在实践中有个意想不到的好处——它变相解决了"狼来了"问题。如果每条建议都推,用户很快会麻木。安静的展示策略让用户形成"只有真正重要时才会弹出提醒"的预期,反而提高了紧急建议的响应率。我经常跟人讲,健康顾问产品的核心指标不是建议生成量,而是"每百次建议中被用户采纳的次数"。
4. 完整跑通一个真实案例:花粉季早晨通勤
4.1 场景与输入
拿一个实际跑通的案例来演示整个链路。某位花粉过敏的用户,我们叫A同学,工作日在某城市骑车通勤,出发时间是早上7点40分。四月底正处在柏树花粉的盛花期,前一天晚上他的手机收到了一条预告——建议明天关注花粉和风速变化。到了早晨7点,后台的定时任务开始拉取各项数据。
输入数据大致是:室外气温16℃,相对湿度72%,东北风2米每秒,花粉指数等级4(满分5),PM2.5浓度58微克每立方米。室内节点数据显示卧室夜间CO2峰值到了1100ppm,开窗通风后降到600ppm。画像库里查到的A同学信息是:有明确的柏树花粉过敏史,鼻炎症状每年春季规律发作,通勤方式是普通自行车,戴一次性外科口罩,没有其他基础疾病。
4.2 处理与特征工程
这些原始输入先进入处理层。外部接口数据统一转成标准时间戳,花粉指数保留等级和浓度两列,风速取过去一小时的均值而不是瞬时值。特征层随后计算:当前PM2.5的3小时趋势呈缓降,湿度处于过去五天的高位(比同期均值高12个百分点),花粉等级连续三天维持4级,风速低于3米每秒的静风条件,污染物扩散条件较差。
组件拼成的特征向量显示这是一个典型的"高花粉浓度+高湿度+弱风"三要素叠加的早晨。单拎出任何一个指标都不到极端水平:PM2.5的58只是轻度污染,花粉4级不是最高级,风速2米每秒只是有点静风。但这个组合对花粉过敏人群来说,属于"症状大概率会被明显诱发"的配置。
4.3 模型输出与建议生成
风险分级模型给出的结果是:高风险概率0.76,中等风险概率0.19,剩下0.05分散在低和极高。经过保守偏置后,等级定为"高"。这个结果和我自己翻文献的感受一致——高湿度会让花粉颗粒吸水膨胀、裂解释放出更多致敏蛋白,静风又让它们在近地面持续堆积,这比单纯的花粉高等级更需要警惕。
规则引擎接着开始匹配。A同学的画像命中了"花粉过敏+通勤出行"模板池,当前环境满足"风险等级高+湿度大于65%+风速小于3米每秒"三个条件,因此输出:
- 主建议:更换通勤方式,优先选择地铁或公交,把户外暴露时间压缩到10分钟以内
- 备选一:如果坚持骑车,改为佩戴N95级别的口罩,并选择绿化带离道路较远的路线
- 备选二:把出发时间推迟到9点以后,通常这个时段风会起来、露水也会蒸发,湿度下降后风险随之降低
- 附加提醒:到达公司后先清洗面部和鼻腔,并更换进门前穿的衣物
置信度最终为0.78,触发了通知推送。消息在7点10分送达,正好在A同学出门前半小时。
4.4 验证与复盘
这条建议到底准不准?我做了两层验证。第一层是事后的数据复盘:当天上午9点后风速果然升到4米每秒,湿度从72%降到58%,花粉浓度也略有回落,9点后出门的环境确实比早高峰时段温和。第二层是用户反馈:A同学当天按建议推迟了出门时间并选了地铁,傍晚反馈说"上午基本没有明显打喷嚏,比前两天好太多"。
单个案例当然不能说明模型处处准确,但它提供了一个重要的信号——规则推理出的行动建议与用户的主观体感吻合。为了积累更系统的证据,我建了一个小规模反馈表单,每推一条建议,都记录用户后来是否采纳、症状是否加重,再用累积数据反哺下一版规则阈值。项目跑到现在,积累了两百多条带反馈标签的有效样本,建议采纳率大概在65%上下,比第一版盲推不到30%的采纳率好了不少。
5. 上线半年踩过的坑:数据质量、校准与建议可信度
5.1 CO2传感器漂移导致的误触发
Atmosis上线的第三个月,家里那台CO2传感器开始发作。明明开着一整夜新风系统,早上系统却提示"室内CO2浓度偏高,建议开窗通风"。我以为是新风系统坏了,手动测试发现新风正常,开窗后传感器读数也从1100ppm跌到500ppm。问题出在传感器自身:CO2传感器每隔一段时间需要做零点校准,而我的节点固件里没有这个机制,读数慢慢偏移,最终整体偏高了三四百ppm。
修法分两层。硬件层,换成支持自动基线校准的红外传感器,并要求固件至少每24小时做一次零点校准。软件层,增加了一条交叉校验规则——当室内CO2读数持续两小时高于室外参考值500ppm以上且新风系统正常开启时,判定为传感器可疑,标记数据不参与建议生成。这两个改动之后,CO2误报基本消失。现在回头看,这个坑迟早会踩,早踩反而逼着我提前把数据质量监控机制做了。
5.2 外部数据源的时间戳对齐问题
这是最隐蔽的一个坑。接入室外数据时,我最初没有仔细读文档,想当然地认为"当前小时的花粉指数"就是本小时刚测出来的。后来对比发现每天凌晨0点左右总会莫名其妙地弹出一条"极高风险"预警,白天却没有。排查了大半天,才意识到那个接口返回的实际上是上一小时的累计数据,凌晨0点返回的是前一天半夜的数据,累积起来跨越了日期边界,导致"日级统计特征"被严重污染。
修复方案是在采集层加了一个强制约束:凡进入处理层的每条记录,都必须自带"数据时间"字段,该字段由数据源显式给出,不由服务器到达时间推断。同时对所有外部数据做滞后对齐,比如花粉指数统一视为滞后1小时数据。这一改之后,凌晨的误报消失了,特征层的日级统计也恢复了正常。现在项目里所有时间敏感的数据流都遵循一个原则:宁可慢,不可错。
5.3 建议措辞的尺度:不可制造恐慌
最初版本的模板里,我犯过"过度渲染"的错。为了强调PM2.5的严重性,我写过"相当于你今天被动吸了10根烟"之类的类比。用户在问卷里反馈:看到这句话之后特别焦虑,甚至不太敢看App了。这个反馈点醒了我——健康顾问建议的目标是降低用户风险,不是制造焦虑。如果建议让用户产生无力感和恐慌,用户就会关闭通知,反而在真正高风险时刻失去保护。
后来我把所有类比类表述全部删除,改用两类安全措辞:一类是相对变化,比如"比昨天同一时段升高40%,处于本月较高水平";另一类是具体行动,比如"关闭朝东窗户可减少约30%的室外颗粒物进入"。让用户感觉"我还能做点什么",而不是"完蛋了"。这个措辞尺度的转变,影响的不只是用户满意度,更是整个产品的可信度。
5.4 用户画像隐私的云端与本地之争
项目早期,为了攒数据集优化模型,我把用户画像和原始环境数据都传到了后端服务器。后来朋友提醒我:健康画像属于非常敏感的个人信息,一旦服务器被拖库,后果很严重。我评估之后决定回滚架构,改为画像只存本地设备,云端只接收匿名化的聚合特征,比如"35到45岁、花粉过敏人群的平均采纳率",不涉及具体个体。
这个改动牺牲了一部分模型迭代便利性——我自己拿到手里的数据不再包含个体的完整信息。但安全合规的红线不能碰。现在的做法是:个性化推理尽可能在端侧完成,云端只做群体层面的统计分析。这条路走起来慢一些,但每一步都踏实。涉及健康数据的项目,隐私保护不是加分项,是准入门槛。
6. 隐私边界、可解释性与下一步的扩展方向
6.1 端侧推理与完全离线模式
基于5.4那条回滚的经验,我把推理链路做了进一步的本土化。风险分级模型用工具转换成端侧可运行的格式,四个风险等级的接口在手机或边缘网关本地就能完成预测。建议匹配的规则引擎本来就是离线可跑的,所以整套Atmosis的推理不依赖云端网络。只有在拉取室外数据时,才需要联网访问公开数据接口。
由此我还做了一个"完全离线模式"——用户家里局域网断开、彻底不访问外部API时,系统退回室内传感器检测加通用健康基线规则。这个模式不适合室外场景,但至少在断网环境下还能给出室内通风建议。做这个模式不是技术难点,而是给用户一个选择:你可以完全不用任何云端服务,数据全部留在家里。
6.2 从"瞬时浓度"升级到"累计暴露剂量"
目前的Atmosis核心用的是瞬时或短时窗平均浓度,这是有局限的。环境健康学里更看重的是累计暴露剂量——不是你某分钟吸入了多少,而是你这一天、这一周累计吸入了多少。两个用户同样面对PM2.5浓度60的环境,一个在户外待了20分钟,一个待了6小时,健康影响完全不同。
下一步计划引入剂量模型:结合用户的位置轨迹和停留时长,估算每天累计暴露剂量,再与本周累计值联动。比如"本周累计细颗粒物暴露剂量已经超过你的个人周均水平,尽量把明天的户外跑步换成室内力量训练"。这会是个更有说服力的健康顾问行为。
6.3 融合可穿戴设备与自然语言交互
再往后走,我想把手环数据接进来。心率、血氧、睡眠时长这些生理指标,能大幅降低健康建议的不确定性。比如用户睡眠不足时,环境刺激对免疫系统的负担会被放大,同样浓度下风险比休息充足时更高。这些信号目前是缺失的,只能靠画像里的静态信息兜底。
交互层也在尝试自然语言问答。与其让用户面对一堆卡片式建议,不如支持"我今天适合跑步吗""家里能开窗吗"这类自由提问。后端先把问题解析成环境查询加上画像查询,跑一遍推理链路,拿到结构化建议后,再用语言模板组装成自然回复。整个过程仍然可解释——用户可以一键查看"这条建议的依据"。
6.4 对社区开放的一点点期待
整个Atmosis项目走到现在,我最深的体会是:环境健康智能这个方向,单靠某个人或者某个小团队的力量远远不够。环境数据的采集标准、个人健康画像的隐私保护模式、建议输出的评估规范,每一块都需要更大范围的协作。我现在把内部使用的建议模板格式和匿名化数据结构整理了出来,准备逐步开源,期待更多做传感器、做算法、做医学信息学的朋友一起来把这套框架打磨得更扎实。健康建议这件事,多一分严谨,就多一分安心。