内容
# 麦麦专属点单管家
> 一个由**个性化规则驱动**的麦当劳点单管家 —— 记住每个用户的饮食禁忌与口味偏好,分餐次智能推荐,按健康目标给出热量分析与优惠计算,确认后一键下单。
基于[麦当劳中国官方 MCP](https://github.com/M-China/mcd-mcp-server) 构建,是[麦当劳程序员创意开发大赛](https://github.com/M-China/mcd-developer-innovation-challenge)参赛作品。
**核心不是写死某个人的习惯,而是一套可配置、可扩展、面向大众的个性化规则系统。** 不同用户各自设定规则,管家按规则自动决策,并把"因为哪条规则做了什么决策"讲清楚。
---
## 为什么不是"又一个推荐脚本"
| 常见做法 | 本项目的做法 |
|---|---|
| 把某个人的忌口写进代码 | 规则是**数据**,谁都能改,改完立即生效 |
| 规则一多就打架,结果随机 | **七层优先级**仲裁,高层的否决压掉低层的偏好,且冲突被显式记录 |
| AI 拍脑袋推荐,说不清为什么 | 每条推荐/排除都点名规则与层级,冲突写明"谁的优先级更高" |
| 加一种新规则要改引擎 | 规则类型是**数据描述符**,加一条即可,引擎一行不改 |
| 让用户先填一堆表单才能用 | **零配置可用**,规则从对话里自动抽取,表单只是可选的进阶工具 |
| 只给一个"最优解" | 给 **3 个**符合条件的选项(主食互不重复),用户自己挑 |
## 七层优先级
| 层级 | rank | 覆盖什么 | 裁决方式 |
|---:|---:|---|---|
| 安全 · 禁忌 | 100 | 过敏、宗教戒律(清真/素食)、辣度、硬忌口 | 一票否决 |
| 健康目标 | 80 | 热量预算、控盐、控糖、控脂、增肌高蛋白 | 强约束,超了换搭配 |
| 人群差异 | 60 | 儿童 / 老人 / 成人 / 孕期 | 人群禁忌 + 营养加权 + 必备项 |
| 餐次菜单 | 45 | 早 / 午 / 晚 / 夜宵的品类与份量 | 不在该餐次菜单即排除 |
| 口味偏好 | 30 | 喜欢 / 不喜欢 / 收藏 / 换换口味 | 只影响排序 |
| 定制指令 | 15 | 去酱、去冰、不要小食、取餐方式 | 改配置项 |
| 优惠 · 性价比 | 5 | 预算上限、优先用券、性价比 | 最后加权 |
**高层覆盖低层。** 冲突在输出里点名:
> 巨无霸 —— 含禁用项「牛肉」 ⚔ 这条否决压掉了你的偏好:**口味偏好 · 喜欢**(分层优先级 100 > 30)
## 规则类型目录(26 种)
**安全**:禁用食材/品类 · 过敏原 · 辣度上限 · 宗教戒律 · 单品钠上限
**健康**:热量预算 · 增肌高蛋白 · 控盐 · 控糖 · 控脂
**人群**:人群禁忌 · 营养加权 · 必备项
**餐次**:可售品类 · 份量区间 · 跳过品类
**偏好**:喜欢 · 不喜欢 · 收藏套餐 · 换换口味
**定制**:单品定制 · 订单级偏好 · 不要某品类
**优惠**:单次预算 · 优先用券 · 性价比
新增一种 = 往 `RULE_TYPES` 加一条数据描述符,引擎不动:
```js
{ typeId: 'ext.no-fried', label: '自定义 · 拒绝油炸', tier: 'health',
kind: 'item', effect: 'veto',
when: { field: 'searchText', op: 'containsAny', valueKey: 'keywords' },
reason: '自定义规则拒绝「{hit}」',
paramsSchema: [{ key: 'keywords', label: '关键词', type: 'tags' }] }
```
## 快速开始
**Skill 本体是纯对话的** —— 不需要打开任何页面,也不需要预填规则。下面第 1 种是用它的正常方式;2、3 是可选工具,不是使用前提。
### 0. 30 秒体验(**不需要 MCP Token**)
```bash
node demo.js
```
零参数、零依赖、不联网、不调任何麦当劳接口。用内置示例菜单跑 4 个场景:新用户点单、减肥控卡、带娃、规则冲突。看完就知道它在做什么。
### 1. 直接用:说人话就行(推荐,零配置)
把仓库根目录的 `SKILL.md` 放到 WorkBuddy 的 skills 目录,并按 `mcp-config.example.json` 接好 `mcd-mcp`。然后**直接说话**:
> "我在减肥,今天中午可以吃 400k,你帮我下一份麦当劳"
管家会:
1. 从这句话里抽出餐次(午)、长期规则(控卡)、本次约束(400k)
2. 调 MCP 取实时菜单与营养数据
3. 给 **3 个**符合条件的选项,各自带热量/蛋白/钠/糖/价格与命中理由
4. 你选一个 → **自动找券** → 算实付 → 再确认一次 → 下单
全程**不需要先填任何表单**。听到"我花生过敏""我不吃香菜"这类约束会自动记成长期规则,下次直接生效。
> "帮我点个午餐,我在控卡,别给我含糖饮料"
> "带娃去麦麦,娃那份要均衡点"
> "我对花生过敏" → 记住了:**花生过敏**(长期,以后都避开)
### 2. 命令行
```bash
# 用示例画像跑一次,给 3 个选项
node engine/cli.js --profile config/profile.example.json --menu samples/menu.json --slot lunch --group adult --top 3
# 带优惠券、输出 JSON 供脚本消费
node engine/cli.js --slot lunch --coupons 5:30 --json
```
输出示例:
```
符合你规则的 3 个选项:
【1】推荐:板烧鸡腿堡(去酱) + 玉米杯 + 矿泉水 合计:520kcal 蛋白 31g 钠 780mg 糖 12g ¥30
· 板烧鸡腿堡 定制:去酱 —— 口味偏好 · 喜欢(偏好加分:命中「鸡肉」)
· 玉米杯 —— 口味偏好 · 喜欢(偏好加分:命中「玉米」)
【2】推荐:麦麦脆汁鸡 + 玉米杯 + 牛奶(中) 合计:530kcal …
整餐约束核对:
· [通过] 健康目标 · 热量预算 —— 实际 520kcal ≤ 上限 600kcal
被排除了(前 8 条):
· 巨无霸 —— 含禁用项「洋葱」
· 麦辣鸡腿堡 —— 辣度 2 超过上限 1 ⚔ 压制了「口味偏好 · 喜欢」
```
### 3. 离线沙盘(可选,**不属于 Skill 流程**)
`app/index.html` 是一个独立的离线工具,用来把规则摊开看、批量增删改。单文件、零外部依赖、双击即用。
- 左侧按层级管理规则:新增 / 编辑 / 停用 / 删除,随时导出导入 JSON
- 右侧实时跑推荐:给出多个候选,选中项带命中理由,被排除项带原因与被压制的偏好
- 切换餐次与人群,立刻看到不同结果
```bash
open app/index.html # macOS
```
**它不参与 Skill 的日常使用。** 装上 Skill 之后你全程在对话里完成点单,不需要打开它;规则平时从对话里自动积累。它更适合调试规则、或给别人演示这套引擎怎么跑。
Skill 只有在你**明确要求可视化产物**时才会生成 HTML(例如"给我出一个我这一周的看板"),其余情况一律纯对话。
## 装到你自己的环境
**为什么仓库里没有"接好的 MCP"?** 因为接不了——MCP Token 绑定的是**个人麦当劳账号**,你的券、积分、收货地址都在里面,不可能共用,也不该共用。活动规则同样要求"配置文件仅使用环境变量占位符"。所以仓库只给占位符,Token 每人自己申请。
三步:
1. **申请你自己的 Token**:[open.mcd.cn/mcp](https://open.mcd.cn/mcp) → 手机号登录 → 控制台 → 激活 → 复制
2. **配 MCP**:把 `mcp-config.example.json` 里的 `${MCD_MCP_TOKEN}` 换成你的 Token,粘贴到客户端的 MCP 配置里(WorkBuddy 在「连接器 → 自定义连接器 → 配置 MCP」,保存后记得启用)
3. **装 Skill**:把仓库根目录的 `SKILL.md` 复制到你的 skills 目录,例如 `~/.workbuddy/skills/mcd-order-butler/SKILL.md`
然后在对话里说人话就行。
### 没有 Token 也能玩
规则引擎**完全离线可跑**,不依赖 MCP:
```bash
node engine/cli.js --slot lunch --group adult --top 3 # 用示例菜单跑决策
open app/index.html # 离线沙盘,可视化调规则
node tests/verify.js # 27 项引擎断言
```
差别只有一处:**没有 Token 就不能真实下单**(查不了实时菜单、券、积分,也下不了单)。规则引擎、分层仲裁、冲突解释这些核心能力,离线一样完整体验。
## 目录结构
```
mcd-order-butler/
├── demo.js 零 Token 一键演示(node demo.js,离线跑 4 个场景)
├── app/index.html 离线沙盘(零依赖,独立工具,不参与 Skill 对话流程)
├── app/template.html UI 模板(改动后跑 node tools/inline.js 重新内联)
├── engine/order-butler.js 规则引擎(UMD,浏览器 / Node 通用)
├── engine/cli.js 命令行入口
├── config/profile.example.json 示例规则画像
├── samples/menu.json 示例菜单(仅用于演示与测试)
├── SKILL.md WorkBuddy Skill 定义(根目录,可直接放进 skills 目录)
├── tools/inline.js 把引擎与数据内联进单文件应用
├── tests/verify.js 引擎自测(23 项断言)
├── tests/smoke-ui.js 单文件应用 DOM 冒烟(9 项)
├── mcp-config.example.json 脱敏 MCP 配置
├── MCP_INTEGRATION.md MCP 接入与调用流程
├── CONTEST_DECLARATION.md 参赛声明
└── workbuddy.md 使用 WorkBuddy 开发的上下文记录
```
## 自测
```bash
node tests/verify.js # 引擎:硬约束 / 作用域 / 冲突可解释 / 可扩展性
npm i jsdom && node tests/smoke-ui.js # 单文件应用 DOM 冒烟
```
`verify.js` 里有一条断言专门守着"可扩展性":运行时注册一种全新的规则类型,引擎源码未改动,规则立即生效。
## 目标用户
- **有明确饮食约束的人**:过敏、宗教戒律、控卡/控糖/控盐、增肌,需要每次点单都不踩线
- **带娃家庭 / 照顾老人**:一份订单里不同人群不同规则,儿童那份自动保证营养均衡
- **减脂或健身人群**:热量与蛋白质有硬指标,想吃得明白
- **麦当劳高频用户**:收藏套餐一键复购,自动用券,不必每次重新决策
## 边界
- 营养数据来自麦当劳官方口径,**仅供参考,不构成医疗或营养建议**;有疾病或强过敏史请遵医嘱。
- 价格、供应状态、优惠券以 MCP 实时返回为准;仓库内示例数据仅用于演示与测试。
- 本项目为参赛作品,由参赛者独立开发,非麦当劳官方产品。
## 许可
MIT。麦当劳及相关商标归其权利人所有,本项目不构成任何商标授权。