在眼镜上背单词:一个 AIUI 对话式智能体的诞生
1. 开篇
背单词让人痛苦的点从来不是"找不到内容",而是"开始太难"。打开手机、解锁、找 App、点进词库——这几个动作足以让大多数人在犹豫中放弃。
楼主想做一款不需要手机的背词工具。 Rokid Glasses 给了我一个机会:通过 AIUI 智能体直接进入闯关卡片,用镜腿按键完成答题、提示、斩词。用户整个闯关过程只靠镜腿按键完成,不需要语音持续介入。
这就是单词地下城的起点。
2. 输入层:镜腿按键的单入口
在眼镜上,我选择了物理按键作为唯一的交互入口:
-
镜腿按键:上下键移动焦点,确定键确认选择。整个闯关过程只靠按键完成,不需要语音持续介入,也不打扰周围环境。
用户在智能体打开那一刻:单词卡片一旦打开,后续所有操作都由镜腿按键完成。
这种交互让用户几乎不需要改变日常姿态:站在地铁里、走在路上、甚至在厨房做饭时,都能顺便完成一轮背词。

3. 技术架构
整个应用的技术实现出乎意料地轻量。楼主没有采用复杂的多页面架构,而是用两张卡片承载所有功能:
-
闯关卡片:负责所有的游戏逻辑——出题、答题、提示、斩词、连击统计、回合总结。
-
战绩面板:负责数据展示——正确率、最佳连击、已斩单词、错词本。
在 AIUI 智能体中,卡片的生命周期与对话绑定,用户用完即走,不需要持久化的页面栈。两张卡片足以覆盖"学习 → 复习 → 统计"的完整闭环。
项目结构也因此保持了极致的简洁:
agent1/
├── AGENTS.md # 智能体清单
├── app.json # 应用配置
├── app.js # 应用入口
├── utils/
│ ├── wordbook.js # 词库工具
│ ├── words.js # 本地词库
│ └── store.js # 本地存储
└── pages/
├── index/index.ink # 闯关卡片
└── stats/index.ink # 战绩面板
4. 核心循环:闯关逻辑
如果把背单词看作一场小型 RPG,那么每一轮闯关就是一个"关卡"。我在闯关卡片里实现了一个有限状态机,用 phase 字段控制当前显示的 UI 和可接收的输入:
loading → quiz → answered → quiz → ... → summary → (revenge / restart) → quiz
阶段解析
-
loading:旋转的"斩"字动画,给本地词库 2 秒的冷启动时间。
-
quiz:展示单词、音标、4 个释义选项,以及"给点提示"和"斩 ⚔"两个辅助操作。
-
answered:用户选择后立即反馈对错,展示完整释义、例句,支持发音和译文切换。
-
summary:本轮战报——正确率、最佳连击、斩词数、错词列表。如果有错词,提供"错词复仇"入口。
-
empty:词库已空或错词本已清空时的特殊状态,给予正向反馈。
整个状态流转由一个 _session 对象驱动,它保存了当前轮次的全部上下文:
this._session = {
mode: 'quiz', // quiz | revenge
queue: [], // 单词队列
index: 0, // 当前索引
combo: 0, // 当前连击
bestCombo: 0, // 本轮最佳
correct: 0,
wrong: 0,
zhan: 0,
wrongList: [], // 答错的单词
hintUsed: false
};
这种设计让代码的"当前状态"一目了然,调试时只需要 inspect _session 就能知道用户卡在哪一步。

5. 数据层:离线设计
楼主刻意没有做任何网络请求。词库内嵌在项目中,学习进度存储在设备本地。
原因很简单:背单词这件事不需要实时联网。用户可能在通勤路上、在会议间隙、在飞机上,网络并不总是可靠。离线设计让"开始闯关"这个动作的成本降到最低——不需要等待加载,不需要担心流量,打开就用。
存储层面,使用了 wx.getStorageSync / wx.setStorageSync 按天分组组织数据,但核心数据结构非常直白:
// 战绩
{
answered: 42, // 总答题数
correct: 35, // 正确数
bestCombo: 8, // 最佳连击
zhanWords: ['apple', 'banana'], // 已斩单词
wrongWords: [ // 错词本
{ word: 'cherry', meaning: '...', wrongCount: 2 }
]
}
"斩词"是一个关键设计:它不仅将单词加入已掌握列表,还会从错词本中移除。这意味着用户对某个词的操作(斩)会永久改变它的复习优先级,系统不会再浪费时间在已经掌握的内容上。
6. 交互细节
眼镜上没有触摸屏,但我采用的解决方案是模拟键盘焦点:用上下键移动高亮边框,确定键触发操作。
这个系统的核心是两个方法的配合:
moveFocus(delta) {
const count = this.getFocusCount();
const next = (this.data.focusedIndex + delta + count) % count;
this.setData({ focusedIndex: next });
},
getFocusCount() {
switch (this.data.phase) {
case 'quiz':
return this.data.options.length + 3; // 4 选项 + 提示 + 发音 + 斩词
case 'answered':
return 3; // 发音、译文、下一词
case 'summary':
return this.data.summary?.wrongCount > 0 ? 2 : 1;
default:
return 1;
}
}
不同阶段,可聚焦的元素数量不同。在 quiz 阶段,用户可以在 4 个选项、提示、发音、斩词之间循环切换;进入 answered 阶段后,焦点数量收缩为 3(发音、译文、下一词)。这种动态调整避免了无效焦点的存在,让操作路径始终保持在最短。
UI 上,当前焦点的元素会获得绿色高亮边框,未聚焦的选项则保持低调。用户不需要精确点击,只需要"接近"目标即可,这大大降低了操作门槛。

7. 视觉系统
AIUI 官方对 AR 眼镜显示有明确规范:单绿色单色显示。纯黑背景上只渲染一种绿色通道,保证在透明镜片上清晰可读、不晕眩。
这不是限制,而是一种设计约束。楼主围绕这个约束建立了一套色彩体系:
-
主背景
#020402:极深的绿黑,接近纯黑但保留一丝绿色调 -
主绿色
#40ff5e:高亮、进度条、正确反馈、按钮激活态 -
正文
#dfffe6:柔和的白绿,长时间阅读不刺眼 -
标题
#f3fff6:更高的亮度,建立信息层级 -
连击/复仇
#ffd76a:琥珀色,用于强调游戏化元素 -
错误/错词
#ff8a8a:柔和的红色,用于负面反馈
所有颜色都围绕单一绿色展开,通过亮度和饱和度区分信息优先级。这种克制反而让界面更加聚焦——用户一眼就能看到单词、选项和当前进度,不会被多余的颜色分散注意力。

8. 性能与体验
Loading 动画的品牌意义
每轮闯关开始前,用户会看到一个旋转的"斩"字。"斩"是这款应用最核心的动作动词,用户每一次看到它,都是在强化"我要去斩词了"的心理暗示。
动画使用 CSS 3D transform 实现 Y 轴旋转,性能开销极低,但在眼镜透明镜片上呈现出足够的立体感。
自动发音的时机
单词渲染后,系统会立即通过 TTS 播报发音。这个时机很关键:太早,页面还没准备好;太晚,用户已经盯着屏幕等着了。我把播报放在 renderCurrent 方法的最后,确保单词文字已经写入页面,TTS 可以立即读取。
斩词的边界条件
"斩词"看似简单,但有几个边界需要处理:
-
同一轮不能重复斩同一个词(通过
zhanWords列表判断) -
斩词后立即从当前队列移除,不计入答对/答错统计
-
已斩单词在下一轮会被
pickQuizWords自动排除,避免重复出现
这些逻辑分散在 recordZhan 和 startQuiz 中,但组合起来形成了一个完整的"掌握"判定闭环。
9. 开发手记:从 Craft 到真机
开发环境使用 Rokid 官方提供的 AIUI Studio,支持直接导入本地项目并实时预览。这个工具在早期就能验证交互逻辑,而不需要每次都打包上传到眼镜设备。
不过,模拟器和真机之间依然有差异。最典型的是按键事件的延迟:在 AIUI Studio 中,键盘上下键的响应是瞬时的;但在真机上,镜腿按键会有几十毫秒的机械延迟,加上蓝牙传输,焦点移动会显得"肉"一些。我在 moveFocus 中加入了防抖处理,避免一次按键触发多次跳转。
另一个坑是 TTS 的异步特性。wx.speech.playTTS 在某些情况下会返回空字符串而不报错,加了一层防御:
function speakText(text) {
if (!text) return;
try {
const result = wx.speech.playTTS(text);
if (result === '') {
console.warn('[TTS] playTTS returned empty string');
}
} catch (error) {
console.error('[TTS] playTTS failed:', error.message);
}
}
10. 复盘:什么做对了,什么还可以更好
做对的
-
离线优先:零网络依赖让学习场景不再受限,用户反馈"打开就能用"是最高的评价。
-
镜腿按键导航:虽然实现起来比触摸复杂,但用户学习成本极低。上下键 + 确定键是物理世界的直觉映射。
可以更好的
-
焦点系统的可访问性:目前只有上下键和确定键,缺少"返回"或"取消"的语义。用户在
summary阶段如果想中途退出,没有快捷方式。 -
词库的可扩展性:当前词库是静态数组,扩充需要手动编辑。虽然可以通过脚本从 LLM 生成,但还没有集成到 App 内。
-
战绩的持久化策略:
wx.getStorageSync在数据量大时会有性能瓶颈。未来如果词库扩展到 2000+ 词,战绩数据也需要考虑分片或索引。
11. 结语
单词地下城不是一个追求"大而全"的产品。它只做一件事:让背单词变得足够简单,简单到你愿意在碎片时间里随时开始一场闯关。
如果你也在 Rokid Glasses 上试过它,或者对 AIUI 对话式开发有想法,欢迎交流。
项目代码开源,欢迎 Star:https://gitee.com/wangpangzhia/word-dungeon
12. 附录:快速上手
# 克隆项目
git clone https://gitee.com/wangpangzhia/word-dungeon.git
核心文件速览:
| 文件 | 职责 |
|
| 声明 |
|
| 闯关卡片:状态机、按键导航、TTS 播报 |
|
| 战绩面板:数据展示、错词本管理 |
|
| 词库工具:抽题、生成干扰项 |
|
| 本地存储:战绩读写、斩词、错词聚合 |
更多推荐

所有评论(0)