把蓝耘接入鸿蒙健康教练:一句话打卡,模型估热量,网关记账
免责声明:本文的「瘦得漂亮」是个人开发的演示项目。文中涉及的热量估算、饮食与运动建议均由 AI 模型生成,仅供日常参考,不构成任何医疗、营养或诊疗意见。
今年 7 月 15 日入伏,入伏第一天早上站上体重秤,67.5kg,比年初重了快 4 斤。老话说伏天减肥事半功倍,先不管有没有科学依据,正好手上有现成的两样东西:一个装着 DevEco Studio 的 M1 Mac,一个前阵子刚测过一轮、印象不错的蓝耘元生代 MaaS 账号。当时给自己的开发工具挑第三方大模型 API,把蓝耘的 DeepSeek-V3.2 从连通性一路测到缓存命中,TTFT 五次全压在 174 到 194 毫秒之间,重复大上下文的缓存命中率 97.9%(自测数据,curl 直接跑出来的)。测完就想找个真项目把它用上,减肥这件事撞上来,两边一拍即合。
于是有了这个 App:瘦得漂亮。对着输入框说一句"今天 67.5kg,早餐燕麦粥和水煮蛋",它把体重和早餐分头入账,热量逐项估好,顶部的额度进度条跟着往前推。端侧是 ArkUI 写的三页签页面,模型走蓝耘的 DeepSeek-V3.2,中间隔一层本机 Python 网关。
Key 放哪儿,先定死
结构上先立一条规矩:蓝耘的 API Key 只放网关的环境变量,ArkTS 代码里一个字都不出现。端侧只跟 127.0.0.1:18090 的网关说话,网关再拿 Key 去调蓝耘的接口。这样端侧代码可以随便截图、随便发出来,不用每张图都提心吊胆检查有没有把 Key 带进去。
export LY_API_KEY="你的蓝耘 Key"
export LY_BASE_URL="https://maas-api.lanyun.net/v1"
export LY_MODEL="/maas/deepseek-ai/DeepSeek-V3.2"
python3 slim_health_gateway.py

启动输出里 LLM configured: True 亮了,模型这半边就算就位。

接蓝耘有两个地方容易踩,都是我自己踩过的:一是模型名带 /maas/ 前缀,/maas/deepseek-ai/DeepSeek-V3.2 才是完整调用名,写成 deepseek-chat 直接 404;二是 Base URL 已经带了 /v1,代码里再拼 /v1/chat/completions 就成了 /v1/v1/...,报 405。这两个坑在控制台模型详情页的 API 示例里其实都写清楚了,照着抄不会错。

模型广场里 DeepSeek、Qwen、GLM、Kimi 都挂着,一个 Key 全能调。这次选 DeepSeek-V3.2 没什么纠结,之前测过它在这条链路上的表现,延迟窗口窄得像一条直线,做打卡这种一问一答的活绰绰有余。还没有账号的话,从这个入口注册就能进控制台拿 Key:蓝耘元生代注册链接。

模型负责估,网关负责记
分工是这个项目里我最坚持的一条:大模型干模糊推理的活,代码干精确计算的活。
用户说"早餐吃了一碗燕麦粥、一个水煮蛋和一杯无糖豆浆",每样多少热量没人报数,这种估算只有模型干得了。网关发给 DeepSeek 的提示词里定义了一个动作结构,要求它只输出 JSON:
{
"actions": [
{"action": "log_weight", "weightKg": 67.5},
{"action": "log_meal", "meal": "早餐",
"items": [{"name": "燕麦粥", "kcal": 150, "note": "高碳水"}]}
],
"reply": "记下了,先给一句确认"
}
模型的输出到这儿为止。落盘写进 slim_journal.json、聚合当天摄入、对比 1650 kcal 的目标额度、算体重趋势,全是网关里的普通 Python 代码在干,数字不经过模型二次加工。日报里那句"今日已摄入 750 kcal",是从日记文件里逐条加出来的,模型想编也插不上手。
提示词里还写了一条规矩:用户提出极端节食目标时,不生成任何记录动作,只许劝。这条后面真的用上了。
App 里跑起来
DevEco 的 Previewer 打开,App 首屏顶部一个状态胶囊,启动时自动探测网关,绿点亮着写"模型在线"。下面三个统计位:已摄入、运动消耗、最近体重,再加一条热量额度进度条。

第一句话就是开头那句,一句话里塞了两件事。发出去,回复下面弹出两张卡:体重打卡卡,早餐入账卡。早餐卡里燕麦粥、水煮蛋、无糖豆浆各自的估算热量和营养标注逐行列着,末行是本餐合计。顶部最近体重同时变成 67.5 kg,进度条动了。卡片底部一行小字"由大模型解析",这行字后面还有用处。

中午一份鸡胸肉沙拉加一杯拿铁,午餐卡末行的"今日已摄入"把早餐自动加了进来。这个累加发生在网关侧的日记文件上,跟模型的对话记忆没关系,就算把 App 杀掉重开,账还在。

傍晚快走 40 分钟,运动卡估了 160 kcal 的消耗,顶部"还可摄入"跟着回升了一截。

日报页不动,查出一个 @Builder 的坑
写到日报页时出了个怪事:顶部统计位明明是 750 kcal 摄入、160 kcal 消耗,切到日报页签,卡片里还是三个 0。同一份数据,同一个页面,一半是活的一半是死的。
排查下来是 ArkUI @Builder 的传参语义。我把卡片渲染抽成了一个带参数的 @Builder,日报页里写的是 this.CardView(this.reportCard)。参数是按值传进去的,首次构建时拷了一份快照,之后 @State reportCard 更新,Builder 内部拿的还是那份旧拷贝。顶部三个数字直接读 this.intakeKcal,走的是正常的状态绑定,所以它们是活的。
修法是日报页不走带参 Builder,直接在页签里读状态渲染:
Text(this.reportCard.title)
ForEach(this.reportCard.lines, (line: string) => {
Text(line)
}, (line: string, index: number) => String(index) + '-' + line)
改完再切页签,日报卡和顶部数字对上了。聊天流里的历史卡片倒是故意留着按值传参,那些是每条消息当时的快照,本来就不该跟着后面的状态变。同一个语义,一处是坑,一处恰好是想要的行为。

趋势页把每条体重记录画成一根绿色条形,补了一条 67.2kg 的记录之后,底部给出"较最早记录变化 -0.3 kg"。伏天刚过去一周,这个数字目前还看不出什么,先记着。

两道边界,一道在提示词里,一道在代码里
试探了一下它的底线,输入"我想一天只吃一顿饭,一周瘦 5 斤,帮我安排"。回复是劝阻加替代方案:一天一顿容易血糖不稳、报复性暴食,一周 2.5kg 减掉的多是水分和肌肉,建议温和一点的节奏。重点在卡片区:一张入账卡都没有,顶部数字纹丝不动。提示词里"极端节食不生成记录动作"这条真的兜住了。

另一道边界在代码里。把网关停掉,unset LY_API_KEY 再启动,LLM configured: False,App 顶部胶囊转成橙点"兜底模式"。这时输入"现在 67.0kg",体重照样入账,因为网关里留了一层正则兜底,体重这种格式固定的话不需要模型也接得住。

再输入"晚饭吃了两串炸串和一杯奶茶",回复是"大模型暂时不可用,这句话我记不进去"。热量估算这活正则干不了,兜底层干不了的事就明说,不假装记录成功。之前卡片底部那行"由大模型解析"的小字,在这儿变成了"规则兜底解析",用户能看出这条回复的成色。

测完把 Key 重新 export 回去,绿点复亮。
出伏那天见
这个 App 现在每天的工作就是早上收一条体重,三餐收几句话,晚上在日报页给我一张聚合出来的账。成本这边也顺便算过:DeepSeek-V3.2 在蓝耘的标价是输入 2 元每百万 token(平台定价页),打卡一句话的请求体不过几百 token,一天几十次调用花不到一分钱。之前测出的那条稳定的延迟曲线,落到 App 里的体感就是点了发送、卡片很快弹出来,没有出现过转圈转到怀疑人生的时刻。
没做完的部分也记一笔:饭菜的热量估算目前完全信任模型的常识,没有对接食物数据库做校准,同一碗燕麦粥两次估算可能差几十 kcal,对打卡够用,对严格控卡的人不够。出伏是 8 月 23 日,到时候趋势页那排绿色条形会给出四十天的答案,这算是我和这个 App、还有背后那个蓝耘账号三方的共同 KPI。
更多推荐

所有评论(0)