Calvino-Y / mcd-mcvoice
git clone https://github.com/Calvino-Y/mcd-mcvoice.git
麦声 McVoice · 视障用户用语音完成麦当劳点餐全流程(选门店→点餐→算价→下单)· 麦当劳程序员节创意开发大赛参赛作品
—
stars, 24h
+26
stars, 7 days · #1,456
+26
stars, 30 days · #9,694
26
★ total · +26% this week
0
forks · 0 open issues
Star history starts 2026-10-20; the chart appears after the second daily snapshot.
LanguageHTML
LicenseMIT
Created2026-10-15 (5d ago)
Last push5d ago
Watchers0
OwnerUser
Links
README excerpt
# 麦声 · McVoice
> **用说的,点麦当劳。** 为视障用户设计的语音点餐:全程可以不看屏幕,三步点完一单。
打开麦当劳小程序,视障用户面对的是一连串读不出来的图片按钮和弹层。麦声把整条流程还原成
一场**对话**:我念当前状态,你说话就行——不用先选门店、不用一道道确认。
手机(390px,微信键盘通道)实拍:docs/preview-mobile.png
**在线体验(无需安装,也可以全程用键盘走完):https://calvino-y.github.io/mcd-mcvoice/**
**想用自己账号真实下单?** 把仓库拉下来跑 `python scripts/serve.py`,页面上填自己的 MCP Token,
就能拉取**你自己的**附近门店、实时菜单和真实报价,直到创建订单拿到支付链接——
详见 本地工具模式。
完整页面长图(含 14 项无障碍自检结果):docs/preview-full.png
## 它解决什么问题
主流点餐界面为视力正常的人设计:轮播图、宫格图标、悬浮弹层。对视障用户意味着:
- **找不到入口**——图标没有可读的替代文本,屏幕阅读器念出来是「按钮 按钮 图片」
- **跟不流程**——弹层一开,焦点乱跳,读到一半不知道自己进行到哪一步
- **不敢确认**——金额、门店、取餐方式藏在视觉角落里,听不到就敢不下单
麦声的答案不是「给页面加一点 aria」,而是**把流程整个翻转成语音优先**:
页面只负责把当前状态说出来、把该说的话列出来,点餐本身是一场对话。
## 快速开始
[code]
浏览器会自动打开 `http://127.0.0.1:8765`。在「设置」里填入你的 MCP Token(申请:
,免费)并点连接,然后按「开始使用」说话就行。
**没有麦克风、环境太吵、或者你就在用屏幕阅读器?** 页面下方的打字输入框走的是同一条
理解管道,能完成一模一样的流程——这不是附属功能,是降级路径,有专门的测试守着。
## 三步流程
第一版有八步(欢迎→门店→点餐→购物车→报价→确认→下单→完成),每一步先念一段引导。
真实用起来,一个已经知道自己要什么的用户平均要说四五轮才能走完一单——「过渡太长、
沟通太累」。现在只有三步:
| 步骤 | 我会说 | 你可以说 |
|---|---|---|
| 1 说单 | 门店已自动选好,直接说想吃什么 | 「来一份巨无霸和一杯可乐」「加一杯可乐」「不要薯条了」「换门店」 |
| 2 核对 | 一共 4 件,总计 ¥64.50,取餐方式堂食 | 「确认」「外带」「再加一份薯条」 |
| 3 完成 | 订单已创建 / 演示模式不会创建订单 | 「再点一单」 |
砍掉的不只是步骤,还有两道**默认替你答好的选择题**:门店自动选最近一家有菜单的(说
「换门店」随时改);取餐方式默认堂食(说「外带」当场改)。点单过程中每加一件都念小计,
你随时知道现在多少钱——不必专门喊一声「算价」。说「就这些」直接进入核对,跳过原来的
购物车与报价两个中间步骤。
任何时刻都可以说「再说一遍」「取消」「我能说什么」——控制指令在**任何步骤、
任何追问等待中**都优先放行,不会被歧义追问吞掉。
## 说话的三条通道(微信、手机自带浏览器也能用)
第一版依赖浏览器原生的 `SpeechRecognition`,实测只有桌面 Chrome / Edge 能用:
**微信内置浏览器和绝大多数手机自带浏览器根本没有这个 API**,国内的安卓 Chrome
也因为没有可用的识别服务而失败。「只能在 Google 浏览器用」不是代码写错了,
是这个 API 在中国用户的手机上等于不存在。现在按环境自动走三条通道:
| 通道 | 环境 | 体验 |
|---|---|---|
| **native** | 桌面 Chrome / Edge | 按一下连续说话(原来的方式) |
| **keyboard** | 微信、手机自带浏览器 | 点圆按钮 → 系统键盘弹出 → **按键盘上的 🎤 说话**,回车即发送。手机输入法的中文识别是厂商专门优化过的,通常比网页识别更准 |
| **cloud** | 任何浏览器(可选) | 在设置里填一个你自己的转写接口地址,圆按钮变成「按一下录音、说完再按一下」 |
键盘通道里,输入框不是备选项而是主入口:点圆按钮会把焦点送过去并唤起键盘(这一步
必须发生在点击事件的同步路径里,否则移动端不弹键盘);输入时**边打边显示「我理解成了
什么」**——「巨无霸」还是被听成了「巨无把」,在按下发送之前就能看见并改正,而不是
加购错了再退。
云端通道默认关闭、不预置任何地址:接口约定是最小形态的 `POST `,
body 为 `audio/webm` 录音,返回 `{"text": "识别结果"}`。用云函数包一层
腾讯云一句话识别或讯飞即可接入,页面零改动。
## 它听得懂什么
语音识别(Web Speech API,`zh-CN`)转出来的文字经常不是你说的那句话。麦声的理解层为此做了四层处理:
**① 口语别名(86 条)。** 没人会照着菜单念「龙焰芝士棒鸡腿堡三件套」,大家说的是
「巨无霸」「可乐」「薯条」「甜筒」「1+1」。别名表把口语词映射到真实商品编码,构建时逐条校验
指向的编码必须存在——写错一个就当场构建失败,绝不让「说了没反应」的别名上线。
**② 同音误听纠正(27 条)+ 拼音容错层(3766 字)。** 「巨无八」→ 巨无霸、「麦乐机」→ 麦乐鸡、
「数条」→ 薯条、「安格丝」→ 安格斯。手写表只收确定是误听的,不收可能另有含义的——它覆盖
不到的情形交给**拼音层**:识别错的字绝大多数是同音字(「可乐」听成「渴乐」),拼音层把整句话
转成音节序列再比,任何同音错法都能救回来。表在构建期用 pypinyin 算好(覆盖 GB2312 一级
汉字 3766 字,按音节分组压到 16 KB,运行时零依赖,只查表),平翘舌和前后鼻音再归并一次
模糊比对(「苏条」也能找回薯条)。表必须比菜单宽——**错字天生是菜单外的字**,只收菜单用字
的表在最该救的时刻必然失效。
**③ 整段读音嵌进名字 = 核心词命中。** 中文商品名是「修饰语 + 核心词」,核心词在后面。
用户把「蘸酱炸鸡」说成「咋鸡」时只有 2/4 个音节对上,光看比例会判成「没听懂」;但「你说的
整段读音原封不动就是它名字的结尾」是很硬的证据。反过来「薯条」是「薯条脆卜卜毛绒周边」的
**开头**——那是用户只说出了修饰语的一半,不能算认出了这件周边。拼音层对这两种情形分别
处理:嵌在名字中后段给分、嵌在开头不给。
**④ 一句话多样。** 「来一份巨无霸和一杯可乐」一句话进两件;「再来两份薯条」数量解析正确——
数量词正则曾经静默失效过(意图对、商品对、只有份数悄悄错掉,页面零报错),现在有专门的
断言组盯着这件事。
**⑤ 听不懂就问,说得清就别问(三层优先级)。**
完整商品名精确命中 → 直接采纳;并列候选里「不带后缀的单品」唯一 → 认定单品、顺口提一句还有
三件套;其余 → 念出候选让你选。泛