做医疗健康类小程序的朋友应该都有体会:用户一进来就问症状、找建议、要评估,但你一个个人开发者或者小团队,手里既没有医生资源,也没有成熟的知识库,很难凭人力撑起有质量的问答。我自己在做一个“微信小程序的AI健康问诊系统”时,踩了不少坑,也积累了一套从架构到落地的完整打法。这套系统本质上是一个个人健康评估工具,用户在微信里打开小程序,通过对话式问诊逐步提交症状、既往病史、生活习惯等信息,AI在后台完成分诊判断和健康风险分析,最后合成一份个人健康评估报告。它能解决的核心问题是:在没有人工客服和全职医生团队的情况下,用AI把“用户健康自测”这件事流程化、标准化、可追踪,适合想快速上线健康咨询类MVP的个人开发者、医疗行业的产品运营,以及需要给用户提供前置评估的创业团队参考。
下面我把整个项目的设计思路、小程序端实现、AI问诊流程、健康评估模型、数据安全合规、上线踩坑这些环节一次讲透,尽量给到可以直接抄作业的配置、代码和参数。
1. 项目定位与整体设计思路
1.1 这套系统到底解决什么问题
健康问诊类小程序最常见的尴尬是:用户想要的是一份“靠谱的回答”,而你的第一版产品只能给一个“固定的表单”。让用户填一堆选择题,体验很差;完全开放对话,AI又容易胡说。我的目标是做一个折中方案:先用结构化对话收集信息,再交给大模型做语义理解和推理,最后用规则引擎兜底,保证输出既有人情味,又有可解释性。
“健康评估”这个词听起来玄,实际上拆开就是三步:收集、分析、反馈。收集环节对应问诊对话,分析环节对应疾病风险推断和生活方式评分,反馈环节对应健康报告和建议列表。只要这三个环节的数据链路打通,系统就已经能跑起来了。我的建议是,不要把AI健康问诊当成“AI医生”,而是当成“AI分诊台+健康助理”,它的任务是帮用户理清问题严重程度、提示就医时机、记录健康趋势,而不是给出确诊结论。
1.2 技术选型:为什么是微信小程序加云端AI大模型
选微信小程序做前端,理由很实际:用户不用下载安装,搜一搜或扫码就能用,医疗健康类场景里用户往往是在临时不舒服、急需建议的时刻打开小程序,轻量化入口是硬需求。小程序本身的组件生态也覆盖了表单、单选、多选、文本输入、富文本展示这些问诊全流程需要的基础元素。
后端的AI部分,我选择了云端大模型API加自建业务服务层的组合,而不是把所有逻辑都塞进小程序本地。原因有三点:
- 大模型推理需要GPU资源,个人开发者不可能本地部署;
- 问诊会话和健康报告需要持久化存储,本地存储顶多存个草稿;
- 健康数据必须集中管控,加密、脱敏、审计这些操作放在服务端才可控。
整体架构上,小程序端只负责UI交互和本地缓存,业务服务部署在云服务器上,对外暴露HTTPS接口,AI推理通过统一网关调用大模型接口。为了省钱,初期可以直接用带公网API的服务,不必要自己买GPU服务器。
1.3 系统核心模块划分
我把系统拆成了五个模块:
| 模块 | 职责 | 关键点 |
|---|---|---|
| 问诊对话模块 | 引导用户描述症状、病史、生活习惯 | 多轮上下文管理 |
| 分诊评估模块 | 判断风险等级、给出就医建议 | 规则引擎兜底 |
| 报告生成模块 | 汇总对话数据生成个人健康评估报告 | 结构化内容模板 |
| 用户与数据模块 | 微信登录、健康档案、历史记录 | 隐私隔离 |
| 运维监控模块 | 异常日志、AI调用计费、反馈收集 | 防止失控 |
这五个模块缺一不可。很多初版项目只做前三个,结果用户换个手机登录,历史报告全不见了,或者AI接口超时直接把页面卡死,都是因为后两个模块没有提前布局。我的经验是:用户与数据模块在第一个版本就要上,哪怕只有一个手机号绑定和一张历史记录表,也要做。
2. 微信小程序端开发要点
2.1 页面结构与交互设计
问诊类小程序的核心页面就那么几个:首页、对话问诊页、评估报告页、个人中心页。首页不要堆功能,只放一个“开始健康自测”按钮,以及最近一次评估报告的入口。对话问诊页是整个产品的灵魂,交互上要模仿聊天软件,左侧是AI消息,右侧是用户消息,但不要用传统聊天输入框作为唯一入口,因为用户不知道该怎么描述症状。
我的做法是“按钮选择为主、文本输入为辅”。AI先问“你主要哪里不舒服?”,下面给出几个常见分类按钮(头疼、发热、腹痛、咳嗽等),用户点一下就发送;AI继续追问“持续多久了?”、“疼痛是什么性质的?”、“有没有伴随症状?”,每一步都给出可选答案,同时保留一个自由输入框,允许用户用自己的话补充。用单选框和复选框组件做多选症状的时候,记得给每个选项加一个sort字段,保证选中后按预设顺序展示,不要以用户点击顺序为准。
顶部导航栏的适配是个容易忽略的坑。不同机型的胶囊按钮位置和状态栏高度都不一样。我是用一个工具函数,通过wx.getWindowInfo()拿到状态栏高度和菜单按钮位置,把自定义导航栏的高度动态算出来。如果你用默认导航栏,至少要把页面标题设置清楚,别让用户在问诊中途迷失方向。
2.2 请求封装与AI对话流式返回处理
小程序网络请求必须走HTTPS,并且要在微信公众平台配置request合法域名。我封装了一个统一的request方法,所有业务接口都走同一套逻辑:统一处理token、超时、错误提示、加载状态。
// utils/request.js const BASE_URL = 'https://your-api-domain.com/api'; function request(path, method, data, options = {}) { return new Promise((resolve, reject) => { wx.request({ url: `${BASE_URL}${path}`, method: method || 'GET', data: data || {}, header: { 'Content-Type': 'application/json', 'Authorization': wx.getStorageSync('token') ? `Bearer ${wx.getStorageSync('token')}` : '' }, timeout: options.timeout || 15000, success(res) { if (res.statusCode === 401) { wx.removeStorageSync('token'); wx.navigateTo({ url: '/pages/login/login' }); reject(res); return; } if (res.statusCode >= 200 && res.statusCode < 300) { resolve(res.data); } else { wx.showToast({ title: res.data.message || '请求失败', icon: 'none' }); reject(res); } }, fail(err) { wx.showToast({ title: '网络异常,请检查网络', icon: 'none' }); reject(err); } }); }); } module.exports = { request };这里有个很重要的细节:AI对话别等完整响应再展示,用户等不了那几秒钟。新版小程序基础库的wx.request支持enableChunkedTransfer开启分块接收,配合onChunkReceived可以模拟流式输出。实际开发中我发现onChunkReceived返回的数据是ArrayBuffer,需要手动按\n拆包,而且有时候一次chunk里有多条数据。我在服务端统一按“data: {json}\n\n”格式推送,前端做缓冲解析。
千万不要在每次收到chunk时就setData整个消息数组,那会卡到没法用。我采用的方案是:维护一个用于渲染的消息列表,只在用户发言和AI完整回复结束时更新列表;流式过程中的内容更新通过selectComponent拿到子组件实例,只更新最后一条消息的文本节点,这样渲染压力小很多。
2.3 登录态与会话管理
登录这块我直接用了微信的wx.login(),拿到code之后发给后端,由后端调用微信的接口换取openid,同时签发自己的token。注意不要在响应里把openid下发到前端,openid属于敏感信息,泄露出去会被刷接口。
// utils/auth.js function wxLogin() { return new Promise((resolve, reject) => { wx.login({ success(res) { if (res.code) { // 将 code 发送到自己的后端 request('/auth/login', 'POST', { code: res.code }) .then(data => { wx.setStorageSync('token', data.token); resolve(data); }) .catch(reject); } else { reject(new Error('wx.login failed')); } }, fail: reject }); }); }问诊会话的超时机制要单独处理。很多用户聊到一半切出去回微信消息,再回来可能已经过了十几分钟。我的策略是:把问诊状态存到本地storage和远端双份,进入页面时如果检测到上一条AI消息停留超过30分钟且流程未结束,弹窗提醒用户“继续上次问诊”还是“重新开始”。这能用上小程序的生命周期监听,在onHide里记录离开时间,在onShow里做恢复判断。这里要注意,onHide在用户切后台、跳转其他页面、弹出授权框时都会触发,记录时间戳要精确到秒,不然容易误判。
3. AI问诊与健康评估的核心实现
3.1 结构化问诊流程设计
问诊流程不能设计成纯自由的聊天,否则AI很容易聊到失控。我的做法是“用户主诉引导 + 系统追问闭环”。流程分四步:
- 主诉收集:用户选择或输入主要不适,如发热、头痛、腹痛;
- 特征追问:持续时间、发作频率、严重程度、加重/缓解因素;
- 伴随症状选择:从预设列表多选,如恶心、呕吐、腹泻、乏力;
- 风险信号筛查:是否高龄、是否孕妇、是否有基础病、是否出现呼吸困难、意识模糊等危急信号。
每一步AI只问一个核心问题,优先问风险最高的信息。比如用户说“胸痛”,不要先问“疼了多久”,而要先问“有没有出冷汗、呼吸困难、濒死感?”,这是为了尽早筛出需要急诊的病例。规则配置里我维护了一个“风险信号词库”,如果用户描述中命中这些词,评估等级直接跳转最高风险,不再继续常规追问。
整个对话上下文会拼成一个结构化的prompt,传给大模型。我保留了一份完整对话记录,而非只传当前一轮,因为用户可能在第五轮才提到“我以前有过胃炎”,这个既往史对风险评估非常重要。在大模型拿到完整历史的同时,我也把前端收集的结构化字段一并传给服务端做规则判断,形成“AI生成追问+规则判定风险”的双通道机制。
3.2 个人健康评估模型搭建
健康评估这部分,我没有完全依赖大模型的自由发挥,而是做了一套可解释的评分体系。分两大块:疾病风险等级和生活方式得分。
疾病风险等级用“红黄绿”三色标签,对应紧急、关注、平稳。风险判断由两层完成:第一层是规则引擎,比如“发热≥39℃且持续超过3天”直接给黄色,“胸痛伴大汗”直接给红色;第二层是大模型根据完整对话,输出一个建议风险等级和依据说明。最终等级取两者更高的一级,宁可保守,不能漏。
生活方式得分则是从问诊中采集的睡眠时长、运动频率、饮水习惯、压力自评等数据,按百分制加权计算。我给每个维度设了权重:睡眠30%,运动25%,饮食25%,心理20%。每项按用户作答内容映射为0到100分,再乘权重求和。比如睡眠6小时以下算50分,7到8小时算90分,8小时以上但入睡困难也算70分。这个映射逻辑要在一开始就定清楚,不能指望大模型稳定地输出精确分数,否则报告前后标准不一致。
计算过程的代码放到服务端执行,我贴一个简化版伪代码,你可以按需改成Python或Node.js:
# score_calculator.py def calc_sleep_score(hours): if hours >= 7 and hours <= 8: return 90 elif hours >= 6 and hours < 7: return 60 elif hours < 6: return 40 elif hours > 9: return 70 else: return 80 def calc_health_score(profile): sleep = calc_sleep_score(profile.get("sleep_hours", 7)) exercise = profile.get("exercise_weekly", 2) * 10 diet = profile.get("diet_score", 70) mental = profile.get("stress_score", 70) return round(sleep * 0.3 + exercise * 0.25 + diet * 0.25 + mental * 0.2, 1)这里要特别注意,运动频率如果每周超过7次,得分不应该线性上涨,我做了封顶处理,超过7次按7次算,提醒用户“运动过量”也是风险。很多健康评估系统在这块拍脑袋加分,用户一周练十天也给高分,一看就不专业。
3.3 健康报告生成与建议推送
报告生成不是让AI自由写作文,而是“模板加变量填充加AI润色”。我设计了三个固定区块:基本信息区、风险评估区、生活建议区。基本信息区直接展示结构化数据;风险评估区把风险等级翻译成通俗语言,比如“目前症状倾向于轻度上呼吸道感染,建议居家观察三天,如出现持续高热请及时就医”;生活建议区根据评分结果生成可执行的建议,比如“睡眠得分偏低,建议固定23点前入睡,睡前1小时减少手机使用”。
模板文案我维护在服务端配置里,AI只负责在模板框架内微调措辞,避免出现“你可多吃水果”这种放之四海而皆准的废话。为了让报告看起来可信,我还加了一条核心逻辑:报告顶部永远显示“本报告由AI辅助生成,不构成医疗诊断,如有不适请线下就医”。这句话不只是合规要求,也是降低用户预期、防止纠纷的保命条款。
报告生成之后,推送给用户的方式有两种:在小程序内打开报告页面,同时通过订阅消息提醒用户“您的健康评估报告已生成”。订阅消息需要用户主动点击允许订阅,所以我的触发时机是问诊完成的最后一步,“点击查看报告”按钮同时拉起订阅授权,而不是在对话中途插入授权弹窗。
4. 数据安全与合规处理
4.1 健康数据的敏感等级划分
健康数据在《个人信息保护法》语境下属于敏感个人信息,这是我在项目初期就反复确认过的。只要涉及症状、病史、用药记录,就不能按普通用户数据来存。我在数据库设计上做了三层隔离:用户基本信息表、健康档案表、AI对话日志表。三张表之间用加密后的用户ID关联,不在任何一张表里同时出现openid和完整健康记录。
这意味着,即使数据库被人拖走了,攻击者也很难把“用户A的电话号码”和“用户A的腹痛症状”串联起来。实际开发里,我用了一个简单的办法:把微信openid做HMAC摘要作为关联键,原始openid单独存到用户表,健康档案表里只出现这个摘要值。
4.2 加密传输与脱敏存储
传输层全部走HTTPS,小程序端严格要求域名ICP备案且配置证书,这是微信官方强制项,不用自己纠结。存储层的敏感字段,比如姓名、手机号,在后端统一加字段级加密。我用的对称加密,密钥存在环境变量中,不写进代码仓库。密钥轮换周期我设的是90天,每次轮换要做数据迁移,所以接口里顺手做了一个密钥版本号字段,解密时按版本找对应密钥。
脱敏也是必须做的。AI对话日志如果要拿来做模型效果分析,我会先把姓名、手机号、具体地址替换成占位符,再导出。症状描述里偶尔会有用户不自觉输入电话和身份证号,我在入库前用正则做了扫描,命中号码规则就自动打码。
4.3 合规红线:AI辅助定位
这是我这套系统最重要的一个设计立场:AI不诊断、不开方、不替代医生。所有给用户的建议文案,都必须经过合规词过滤。我在服务端加了一个敏感词和违规律师函级别的规则列表,比如任何药品名称出现在AI回答中,除非是用户自己输入,否则直接拦截并要求AI改写成“请遵医嘱使用相关药物”。
此外,在小程序隐私保护指引中,我明确填写了“收集你的症状、病史信息用于健康评估”,并在用户首次进入问诊时弹出独立的健康数据授权弹窗,不做默认勾选。这块的体验损失是值得的,因为一旦被投诉,小程序下架是分分钟的事。我见过几个同类项目因为隐私弹窗不完整被拒审,大家一定不要抱侥幸心理。
5. 实操过程与踩坑记录
5.1 从原型到MVP的落地步骤
第一个版本不要想着一步到位,我的落地节奏是这样:
- 先用微信开发者工具搭建三个静态页面,把问诊的UI交互完整走一遍,这个阶段不接AI,用写死的模拟数据;
- 接一个最简单的AI接口,只做“单轮问答”,验证AI返回内容的质量和速度;
- 补齐问诊流程的状态机,把“进行中、已完成、已过期”三种状态跑通;
- 接入微信登录和基本用户表,保证报告可以持久化;
- 用体验版发给周围人收集反馈,同时开启真机调试。
最后一步很重要。小程序开发工具里模拟器跑得再顺,真机上也可能出现键盘顶起页面、输入框被遮挡、流式输出卡顿、安卓机大文本渲染掉帧等问题。我给体验用户发了丝滑版任务清单:让他们试着在问诊中途退出再进入、切到后台几分钟再接、快速点击多个症状选项,把这类边界场景的Bug都逼出来。
5.2 实测中的典型问题
我在实测阶段被下面两个问题折磨了很久。
第一个是iOS和安卓的流式文本渲染差异。同一段AI回复,iOS上rich-text组件渲染没问题,安卓上偶发显示不全。调试发现是流式更新时,部分内容还在缓冲里,就强制刷新了UI。我的解决方式是:前端增加一个“文本缓冲区”,只有缓冲区里检测到完整的“\n\n”分隔符,才允许刷新一次UI,避免边收边画导致的内容截断。
第二个是问诊上下文过长导致AI响应变慢。多轮问答之后,整个对话历史可能有几千字,每次请求都全量带上,响应时间从1秒涨到8秒。我是这么优化的:服务端维护一个“摘要压缩”机制,当对话超过十轮,就把前几轮的信息压缩成一段结构化摘要,只保留用户主诉、关键症状和时间线,再和最近三轮对话拼在一起发AI。这个优化把p95的响应时间降到了3秒以内。
5.3 常见问题速查表
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 真机上请求失败 | 域名未配置合法域名、证书不完整 | 公众平台配置request域名,用HTTPS检测工具检查证书链 |
| wx.request返回10002错误 | 网络异常或域名解析失败 | 检查域名备案、服务器防火墙、本地网络代理设置 |
| 用户登录后信息不同步 | token过期 | 启动时静默调用wx.login刷新token,利用refresh_token机制 |
| AI流式输出不显示 | onChunkReceived解析错位 | 按数据分隔符缓冲解析,避免多条数据合并 |
| 小程序审核被拒 | 医疗健康类目的资质不全 | 避免使用“诊断、治疗”等绝对化用语,增加免责声明,申请相关类目或走咨询工具定位 |
| 键盘遮挡输入框 | 页面没有做键盘适配 | 使用adjust-position="true",并在onKeyboardHeightChange中动态调整 |
| 报告分享到微信打不开 | 页面路径带参数过长 | 用短链或通过storage暂存报告ID,再跳转 |
另外单个很实用的建议:收集反馈的时候不要只收文字,让体验用户直接录屏。很多UI问题光看文字描述根本复现不出来,录屏配合日志能省一半排查时间。
6. 性能优化与体验细节
6.1 首屏加载优化
健康问诊小程序的目标是让用户“最短时间内开始问诊”,所以首屏加载链路要极简。首页只请求一个接口:获取用户最近一次报告摘要。这个接口必须控制在200毫秒内返回,做法是报告摘要字段单独存一份JSON,而不是查询原始大表。用户在报告页要看详情时再按ID拉取完整内容。
小程序这边优先考虑分包。问诊页和报告页通常依赖一堆第三方组件,全部塞在主包会让首包体积膨胀到几兆,审核和加载都很慢。我把“AI对话页”和“报告详情页”拆到单独分包,主包只保留首页、个人中心、登录相关页面。这样主包能控制在1.5MB以内,加载速度明显变快。
6.2 流式输出的并发控制
AI流式请求有个容易出问题的点:用户连续发送消息时,上一条请求还没结束,新请求就进来了。如果不做控制,AI会同时输出两段互相矛盾的内容,显存和服务账单全在燃烧。我在前端做了“正在生成中禁止发送”的锁,在服务端对同一个用户ID的对话请求做了串行化。具体实现是给用户加一把分布式锁,锁的过期时间设为30秒,如果当前已有未完成请求,新请求直接返回“请稍候”的状态码,前端提示用户等待。
体验细节上,AI输入中的“正在输入”动画一定要有,不然用户以为系统没反应就反复点发送。动画不要做那种复杂的打字机效果,简单三个点的透明度循环就够了,省电又流畅。
6.3 列表加载更多与历史报告分页
用户做过多次评估后,历史报告列表会越来越长。这里用小程序原生的onReachBottom触发加载下一页,配合selectComponent里的列表容器计算坑位,避免用户滑到底部时出现“加载中”和“到底了”两个状态同时出现。还要注意,每次从报告详情返回列表时,保持滚动位置不变,不要整页刷新,否则用户发现自己又滚回顶部,耐心直接清零。
6.4 调试工具的使用经验
开发过程中,网络请求排查我用的是微信开发者工具自带的Network面板,配合sources调试断点。遇到线上接口参数诡异的问题,我习惯在后端加一个echo调试接口,把请求头和请求体原样返回,前端在控制台直接看结构化输出,比任何外部工具都直观。对于线上偶发的接口故障,我在后端接了错误日志平台,把HTTP状态码、用户链路追踪ID、AI调用耗时统一上报,这样用户在群里反馈“刚才白屏了”的时候,我能直接按traceId查日志,而不是瞎猜。
结尾:关于这套系统,我最想说的一点
回头再看这个项目,我最大的感受是:AI健康问诊系统的难点,并不在AI,而在“边界”。你要在“让AI足够聪明”和“让AI不越界”之间找到平衡点。健康领域容错率极低,一句不严谨的话就可能误导用户。所以我最后再分享三个观点:
第一,所有AI生成的健康相关内容,都必须经过结构化模板和规则引擎的双重校验。我的系统里,AI的话术不是最终版本,规则引擎才是最终兜底。第二,用户体验的优先级大于AI炫技,把一个简单的问诊流程做到不迷路、不卡顿、不丢数据,比接入最新的AI模型更能提升用户留存。第三,上线之后持续用真实对话数据优化提示词,我在第一个月整理了近千条真实用户对话,把AI频繁追问同一个问题、漏问关键风险点的情况逐一修掉,健康评估的“靠谱感”才会有本质提升。
如果你也在做同类小程序,建议从小而美的问诊场景切入,先把“感冒发热初步评估”这一条链路做透,再慢慢扩展其他科室。先跑通一条路,也比铺一张大而全的网实际得多。