Rokid Glasses AIUI 实战:从零开发“今天吃什么”语音技能
2026/9/23 6:05:33 网站建设 项目流程

1. 为什么要在 Rokid Glasses 上折腾一个“今天吃什么”的 AIUI

拿到 Rokid Glasses 的开发者套件之后,我第一反应不是去做导航、翻译这类“正经”功能,而是想验证一个更接地气的问题:AIUI 这种交互范式,到底能不能扛住一个高频、低决策成本、但极其日常的场景。“今天吃什么”就是这么一个场景——它足够简单,简单到任何人一听就懂;又足够真实,真实到每个人每天都要面对。用它来当 AIUI 开发的第一个练手项目,比做一个“Hello World”式的语音播报有意义得多。

先把概念说清楚。AIUI 不是单纯的语音助手,也不是单纯的图形界面,它是把语音、视觉、传感器输入和意图理解揉在一起的一套交互框架。在 Rokid Glasses 这种带显示、带麦克风阵列、带摄像头的眼镜形态设备上,AIUI 的核心价值在于:用户不需要掏出手机、不需要点按屏幕,只需要一句话或者一个眼神方向,系统就能给出一个“够用”的回应。而“今天吃什么”这个需求,恰好完美匹配这种交互——用户要的不是一个精确答案,而是一个能打破选择困难的随机建议,外加一点点决策依据。

这个项目适合谁?如果你已经拿到了 Rokid Glasses 的开发权限,会一点基础的脚本或应用开发,但对 AIUI 的意图定义、语音链路、显示层渲染还没有完整跑通过一遍,那这个项目就是为你准备的。它不涉及复杂的模型训练,也不需要你搭建后端服务,核心工作量集中在意图设计、语音交互闭环、以及眼镜端的信息呈现这三块。做完之后你会对 AIUI 的整个数据流有一个肌肉记忆级别的理解,后面再做什么天气播报、日程提醒、物品识别,都是同一套骨架换皮。

我踩过的第一个坑就是低估了“随机”这件事在语音交互里的复杂度。你以为用户说“今天吃什么”,你随机返回一个菜名就完事了?实际测试下来,用户会追问“还有别的吗”“太辣了”“有没有清淡点的”“附近能买到吗”。这些追问在图形界面里是几个按钮的事,在 AIUI 里就是意图的二次甚至三次分发。所以这个项目真正的技术含量,不在于随机算法,而在于如何用有限的意图槽位覆盖用户无限的发散追问

2. 开发前的环境确认与 Rokid Glasses AIUI 能力边界

2.1 开发者账号与设备连接状态检查

在写第一行代码之前,必须先把设备侧的准备工作做扎实。Rokid Glasses 的开发模式需要先在开发者平台注册应用,拿到对应的 App Key 和 App Secret,然后通过调试工具把应用推送到眼镜端。这一步看起来是流程性的,但实际卡人的地方在于设备固件版本和 SDK 版本的匹配。我遇到过 SDK 文档里写的某个语音接口在设备上直接返回 unsupported,排查了半天才发现是固件版本低了两个小版本。

具体操作上,你需要确认三件事:眼镜端已经开启开发者模式并且和电脑处于同一调试网络下;开发者后台创建的应用类型选择的是带 AIUI 能力的应用模板;本地开发环境里安装的 CLI 工具版本与 SDK 版本对应。这三件事任何一件不对,后面都会出现“代码没问题但设备没反应”的诡异现象。

提示:设备连接后先用官方提供的设备信息查询命令确认固件版本,再去对照 SDK 的兼容性列表。不要跳过这一步,否则后面调试语音链路时你会怀疑人生。

2.2 AIUI 在眼镜端的输入输出通道

理解 AIUI 的能力边界,本质上是理解眼镜端有哪些输入通道和输出通道。输入侧主要有三路:麦克风阵列采集的语音流、摄像头采集的图像流、以及触摸板/按键的物理交互。输出侧主要是两路:骨传导或微型扬声器的音频输出、以及光波导显示屏的视觉输出。AIUI 框架做的事情,就是把这些通道编排成一个统一的交互会话。

对于“今天吃什么”这个项目,我们主要用到的是语音输入和视觉+语音输出。摄像头暂时不用,但你要知道它在那里,因为后续如果要做“识别冰箱里有什么食材再推荐菜谱”,摄像头就是核心输入。物理交互也要保留一个兜底——当语音识别置信度低的时候,用户可以通过触摸板确认或取消,这是 AIUI 设计里非常重要的一条原则:永远给用户留一个不用说话就能操作的退路

这里有个经验之谈:眼镜端的显示区域非常有限,大概只够显示两到三行短文本加一个简单的图标。所以你的输出内容必须极度精简。我一开始想把菜名、食材、做法、热量全塞进去,结果在眼镜上看起来就是一坨糊在一起的文字。后来改成“菜名 + 一句推荐理由 + 一个操作提示”的三段式,可读性立刻上来了。

2.3 语音唤醒与意图识别的链路拆解

AIUI 的语音链路可以粗略拆成四段:唤醒词检测、语音转文字、意图解析、技能分发。唤醒词检测是在设备本地跑的,功耗很低,负责监听特定的唤醒词。一旦唤醒,设备开始把语音流送到语音转文字模块,得到文本后再交给意图解析器,解析出用户到底想干什么,最后把意图和槽位信息分发给对应的技能处理函数。

“今天吃什么”这个技能,需要注册的意图其实不止一个。核心意图是“推荐食物”,但还要处理“换一个”“太辣了”“有没有面食”这类修正意图,以及“算了不吃了”这类取消意图。每个意图都要定义对应的语料模板和槽位。比如“有没有{口味}的”就是一个带槽位的意图模板,槽位是口味类型。这些模板写得越丰富,用户的实际表达被正确解析的概率就越高。

我实测下来,意图模板的数量和识别准确率不是线性关系。写到二十条左右的时候,常见表达基本都能覆盖了,再往上加边际收益很低。真正影响体验的是兜底策略——当所有意图都没匹配上时,系统应该怎么回应。我的做法是统一回一句“没听清,你可以说随便来一个,或者告诉我你想吃什么口味”,然后给一个默认推荐。这样即使用户说了很奇怪的话,交互也不会断掉。

3. 从零搭建“今天吃什么”技能的完整实操

3.1 项目初始化与技能注册

在开发者后台创建一个新的 AIUI 技能,技能名称填“今天吃什么”,调用名称填一个简短的英文标识比如what_to_eat。技能类型选择自定义技能,因为我们要自己处理意图逻辑。创建完成后会得到一个技能 ID,这个 ID 在后面本地代码里注册意图的时候要用到。

本地项目结构我建议这样组织:一个入口文件负责初始化 AIUI 客户端和注册意图,一个intent_handler文件放所有意图的处理函数,一个food_data文件放菜品数据和推荐逻辑,再加一个config文件放 App Key 之类的配置。这个结构不复杂,但胜在清晰,后面加新意图的时候不用在一堆代码里翻来翻去。

初始化的时候有一个细节容易忽略:AIUI 客户端的初始化是异步的,必须等初始化完成的回调触发之后才能注册意图。我一开始把注册意图的代码写在初始化调用后面,结果意图一直注册不上,因为初始化还没完成。后来改成在初始化成功的回调里做注册,问题就解决了。这个坑在官方文档里只是一笔带过,但实际开发中非常容易踩。

// 伪代码示意,实际 API 名称以官方 SDK 为准 const aiuiClient = new AIUICient({ appKey: 'your_app_key', appSecret: 'your_app_secret' }); aiuiClient.on('initialized', () => { aiuiClient.registerIntent('what_to_eat', { intentName: 'recommend_food', slots: ['flavor', 'meal_type'] }); aiuiClient.registerIntent('what_to_eat', { intentName: 'change_food' }); aiuiClient.registerIntent('what_to_eat', { intentName: 'cancel' }); });

3.2 意图语料的设计与槽位定义

语料设计是这个项目里最需要“人味”的部分。你不能只写“今天吃什么”这一句,因为真实用户会说出各种变体:“中午吃啥”“晚上吃什么好”“给我推荐个饭”“随便来一个”“不知道吃啥”。这些都要作为语料模板注册进去。我的做法是先自己对着眼镜说了二十遍不同说法,把实际说出来的句子记下来,再去重、归类,最后整理成语料模板。

槽位方面,我定义了两个:flavor表示口味偏好,取值包括辣、清淡、甜、酸等;meal_type表示餐次,取值包括早餐、午餐、晚餐、夜宵。这两个槽位不是必须的,用户不说就为空,推荐逻辑里根据槽位是否为空做不同的过滤。比如用户说“有没有清淡点的”,flavor槽位就是“清淡”,推荐时只从清淡菜品里选。

意图名称触发语料示例槽位处理逻辑
recommend_food今天吃什么 / 推荐个饭flavor, meal_type根据槽位过滤菜品后随机推荐
change_food换一个 / 还有别的吗排除上一次推荐结果后重新随机
cancel算了 / 不吃了结束会话并给出友好回应

语料模板写完之后,一定要在开发者后台的测试工具里逐条验证。我遇到过一条语料“吃啥都行”被解析成了两个意图的冲突情况,后来把这条语料删掉,换成了“随便”才解决。语料之间的语义重叠是意图冲突的主要来源,写的时候要刻意保持每条语料只指向一个明确的意图。

3.3 推荐逻辑的实现与随机策略

推荐逻辑看起来简单,但要做好体验有几个讲究。第一,随机不能是真随机。如果用户连续三次都听到同一个菜名,他会觉得这个功能坏了。所以我在推荐逻辑里加了一个最近推荐队列,每次推荐时排除掉最近五次出现过的菜品。第二,推荐要带理由。光说一个菜名太干巴巴了,加一句“今天天气热,来碗凉面挺合适”或者“你上次说想吃辣的,水煮肉片可以考虑”,体验会好很多。理由可以基于时间、天气、历史偏好来生成,哪怕只是简单的模板拼接,也比干说菜名强。

菜品数据我用一个 JSON 数组存,每个菜品包含名称、口味标签、适合餐次、一句推荐语。数据量不用大,三十到五十个菜就够覆盖日常选择了。关键是要分类均衡,不能全是川菜,也不能全是主食。我按口味和餐次做了交叉分类,保证任何过滤条件下都至少有三到五个候选。

const foodList = [ { name: '番茄鸡蛋面', flavor: '清淡', meal: ['早餐','午餐','晚餐'], tip: '简单快手,什么时候吃都不违和' }, { name: '水煮肉片', flavor: '辣', meal: ['午餐','晚餐'], tip: '想吃辣的时候来一份,下饭' }, { name: '皮蛋瘦肉粥', flavor: '清淡', meal: ['早餐','夜宵'], tip: '胃不舒服的时候来一碗很舒服' }, // ... 更多菜品 ]; function recommendFood(flavor, mealType, recentList) { let candidates = foodList.filter(f => !recentList.includes(f.name)); if (flavor) candidates = candidates.filter(f => f.flavor === flavor); if (mealType) candidates = candidates.filter(f => f.meal.includes(mealType)); if (candidates.length === 0) candidates = foodList; // 兜底 const picked = candidates[Math.floor(Math.random() * candidates.length)]; return picked; }

3.4 语音播报与屏幕显示的协同

眼镜端的输出是语音加显示双通道,这两个通道的协同是有讲究的。语音负责传递核心信息,显示负责补充细节和提供操作提示。比如推荐“番茄鸡蛋面”的时候,语音说“给你推荐番茄鸡蛋面,简单快手,什么时候吃都不违和”,屏幕上同时显示菜名和一行小字“说‘换一个’看看别的”。这样用户即使没听清语音,扫一眼屏幕也能知道结果,并且知道下一步能做什么。

语音播报的文本要单独准备,不能直接把显示文本读出来。显示文本可以带标点和格式,语音文本要更口语化、更短。我一开始偷懒用同一份文本,结果语音读出来像在念说明书。后来把语音文本单独写了一套,控制在二十个字以内,听起来自然多了。

屏幕显示的刷新时机也要注意。AIUI 的语音播报是异步的,如果播报还没结束就刷新屏幕,用户会看到文字和语音不同步。我的做法是等语音播报完成的回调触发后再更新显示,虽然会慢那么零点几秒,但体验上更连贯。这个细节在快速连续交互的时候尤其明显,比如用户说“换一个”,如果显示刷新太快而语音还在读上一个,就会很混乱。

4. 实测中暴露的问题与逐项排查过程

4.1 唤醒后首句识别率低的排查

设备刚唤醒后的第一句话,识别率明显低于后续对话。我一开始以为是麦克风的问题,后来用日志工具抓了语音链路的原始数据才发现,唤醒词检测和语音转文字之间的切换有一个短暂的空窗期,如果用户唤醒后立刻说话,开头几个字会被吃掉。这不是 bug,是语音链路的设计特性,但需要在交互上做补偿。

我的解决方案是在唤醒成功后先播一个很短的提示音,大概两百毫秒,给链路切换留出时间,同时提示用户“可以说了”。加了提示音之后,首句识别率从大概六成提升到了九成以上。这个改动成本极低,但效果立竿见影。如果你也在做眼镜端的语音交互,强烈建议加上这个提示音。

注意:提示音的音量和时长要控制好,太长会显得拖沓,太短又起不到提示作用。我试下来两百到三百毫秒的中等音量提示音最合适。

4.2 意图冲突导致的错误分发

前面提到过语料语义重叠会导致意图冲突,实际表现是用户说“随便”的时候,有时候被解析成recommend_food,有时候被解析成change_food。排查方法是把每次意图解析的结果和原始文本都打到日志里,跑一百次看分布。我发现“随便”这个词在两条语料模板里都出现了,一条在recommend_food下,一条在change_food下。删掉其中一条之后,冲突就消失了。

这个问题的根因是意图解析器在多个模板匹配度相近时会随机选一个,而不是报错。所以语料设计的第一原则是:任何一条语料只能出现在一个意图的模板里。写完语料之后一定要做交叉检查,把重复的句子揪出来。我后来养成了一个习惯,把所有语料导出到一个表格里,用条件格式标出重复项,每次改完语料都跑一遍这个检查。

4.3 连续交互时的会话保持问题

“今天吃什么”这个场景天然是多轮交互:推荐、换一个、再换一个、就这个吧。但 AIUI 默认的会话超时时间比较短,用户如果犹豫了几秒没说话,会话就断了,再说“换一个”的时候系统会重新走唤醒流程。这个体验很割裂。

解决办法是在技能配置里把会话超时时间调长,同时在一轮交互结束后主动追问“还要换吗”,给用户一个继续对话的钩子。我把超时从默认的五秒调到了十五秒,并且在每次推荐后都加一句“说换一个可以继续看”。实测下来,用户连续交互的完成率提升了很多。但超时也不能太长,否则用户已经走开了会话还挂着,会浪费设备资源。十五秒是我试下来比较平衡的值。

4.4 显示内容在强光下的可读性

这个问题比较硬件向,但很影响实际使用。Rokid Glasses 的光波导显示在室内看起来很清楚,但到了户外强光下,浅色文字几乎看不见。我一开始用的白色文字,户外基本废了。后来改成高对比度的配色方案,文字用深色描边加浅色填充,可读性好了很多。

另外,显示区域的有效像素比想象中少,字号不能太小。我试过用最小字号显示三行文字,结果在眼镜上看起来像蚂蚁在爬。后来改成最多两行,字号调大,虽然信息量少了,但至少能看清。在眼镜端做显示,宁可少显示一点,也要保证看清,这是和手机屏幕完全不同的设计逻辑。

5. 让这个技能更好用的几个进阶思路

5.1 接入时间与天气做情境化推荐

基础的随机推荐跑通之后,最自然的扩展就是接入情境信息。早上八点推荐早餐类,中午十二点推荐午餐类,晚上六点之后推荐晚餐类,这是基于时间的过滤。如果再接入天气数据,下雨天推荐热汤面,大热天推荐凉菜,推荐的理由就更充分了。Rokid Glasses 本身有网络能力,拉取天气接口不难,难的是把天气数据映射到菜品标签上。我的做法是给每个菜品打上适合的天气标签,比如“热食”“冷食”“汤类”,然后根据实时天气做加权随机。

这个扩展的技术点在于情境数据的获取时机。不要在每次推荐的时候都去拉天气,那样延迟太高。我的做法是在技能启动的时候拉一次天气缓存起来,会话期间用缓存数据。如果会话持续超过半小时,再刷新一次。这样既保证了推荐的情境相关性,又不会让用户等太久。

5.2 用历史记录做个性化排序

如果用户经常用这个技能,可以记录他的选择历史,慢慢学习他的偏好。比如他连续三次都选了辣味菜品,那下次推荐的时候辣味菜品的权重就调高。这个逻辑不需要复杂的机器学习,简单的计数加权就够了。实现上就是在本地存一个偏好计数器,每次用户确认某个菜品就给它对应的口味标签加一分,推荐时按分数做加权随机。

但要注意不要过度拟合。如果用户偶尔想换口味,系统一直推辣的他也会烦。所以加权要有上限,不能让某个口味永远霸占推荐位。我设的上限是权重不超过基础权重的三倍,这样既体现偏好,又保留多样性。

5.3 多人场景下的快速切换

眼镜设备的一个特点是可能被多人轮流使用,比如一家人吃饭前每个人都问一遍。这时候历史记录和偏好就会混在一起。我的处理方式是在会话开始时做一个简单的声纹区分,如果检测到不同的说话人,就切换到独立的偏好档案。声纹区分在 AIUI 框架里有现成的接口,调用一下就行,不需要自己训练模型。

如果声纹区分不可用,退而求其次的方案是让用户手动切换档案,比如说“换个人问”就重置当前偏好。这个方案体验差一些,但至少不会把不同人的偏好混在一起。我在没有声纹数据的情况下用的就是这个兜底方案。

5.4 从“推荐”延伸到“决策辅助”

“今天吃什么”的本质是决策困难,推荐只是解决决策困难的一种方式。更进一步的做法是提供对比信息,帮助用户自己做决定。比如同时给出两个候选,语音说“A 是番茄鸡蛋面,清淡快手;B 是水煮肉片,辣味下饭,你选哪个”。这样用户不是在被动接受推荐,而是在做选择,参与感更强。

这个模式的实现难点在于语音交互的选项确认。用户说“第一个”或者“A”都要能正确解析,还要处理“都不想要”的情况。我在意图里加了select_optionreject_all两个意图来处理这些情况。实测下来,双选项模式的用户满意度比单推荐模式高,但交互轮次也更多,适合不赶时间的场景。

6. 我在这个项目里攒下的几条实在经验

第一条,AIUI 开发的核心工作量在意图设计,不在代码。我大概花了七成时间在写语料、测语料、改语料,写推荐逻辑的代码可能只占了两成。如果你打算做类似的技能,先把意图和语料想清楚再动手写代码,会省很多返工。

第二条,眼镜端的交互设计要遵循“一句话一个动作”的原则。用户说一句话,系统给一个明确回应,不要让用户在一句话里表达多个意图。我试过让用户一句话同时指定口味和餐次,比如“来个清淡的午餐”,识别率明显低于分开说。后来改成先问餐次再问口味,虽然多了一轮,但每轮的识别准确率都高了。

第三条,测试一定要在真实设备上做,模拟器靠不住。语音链路的延迟、麦克风的拾音效果、显示的可读性,这些东西在模拟器上完全体现不出来。我早期在模拟器上测得好好的,推到设备上发现唤醒后首句识别率惨不忍睹。真实设备测试是绕不过去的。

第四条,给每个意图都留一个兜底回应。用户的实际表达永远比你想象的丰富,总有意料之外的句子。兜底回应不需要多聪明,只要能让对话继续下去就行。我的兜底回应是“没太听明白,你可以说随便来一个,或者告诉我你想吃什么”,实测下来用户听到这句话基本都会换一种说法再说一遍,交互不会断。

第五条,会话超时时间要结合场景调。吃饭决策这个场景,用户犹豫的时间比较长,超时设短了体验很差。但如果是查天气这种场景,用户要的是快速答案,超时设短一点反而干脆。没有通用的最优值,要根据具体场景去试。

最后说一个我个人的体会:AIUI 这种交互范式,最怕的就是“为了语音而语音”。如果一个操作在屏幕上点一下就能完成,硬要用语音去做,反而更累。“今天吃什么”之所以适合 AIUI,是因为它天然就是语音场景——你不可能为了决定吃什么去掏手机点开一个 App。找到这种天然匹配的场景,AIUI 的价值才能体现出来。这个项目做完之后,我对什么场景该用 AIUI、什么场景不该用,有了比看十篇文档都清晰的认识。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询