linghuchong6668 / mcd-optimizer
git clone https://github.com/linghuchong6668/mcd-optimizer.git
—
—
stars, 24h
+111
stars, 7 days · #456
+111
stars, 30 days · #2,082
111
★ total · +111% this week
8
forks · 0 open issues
Star history starts 2026-10-20; the chart appears after the second daily snapshot.
LanguagePython
LicenseMIT
Created2026-10-15 (5d ago)
Last push3d ago
Watchers0
OwnerUser
Links
README excerpt
# 麦麦最优解 · Mcd Optimizer > 在「预算 × 热量 × 人数 × 忌口」多重约束下,求解麦当劳最优下单方案的 Skill。 > 数据全部来自麦当劳 MCP Server 的真实接口——菜单、价格、营养、优惠券一律实时拉取,不做任何硬编码。 > 它还会**先把券领了,再带着券求解**。 --- ## 30 秒看懂效果 同一家门店、同一组约束(2 人 / 预算 ¥70 / 人均热量 ≤ 800 kcal), 差别只在于「有没有把券算进去」: | | 方案 | 实付 | 原价 | 热量 | 性价比 | | --- | --- | --- | --- | --- | --- | | 不用券 | 那么大鸡排 ×3 | ¥42.00 | ¥42.00 | 1155 kcal | 27.5 kcal/元 | | **用券** | **中薯条买一送一(券)×1 + 那么大鸡排 ×2** | **¥41.50** | **¥55.00** | **1348 kcal** | **32.5 kcal/元** | **少花 ¥0.5,多吃 193 kcal,每元买到的热量高 18%,同时省下 ¥13.5。** > 数据于 2026-10-15 由麦当劳 MCP 实时接口拉取,含真实领取的优惠券。 --- ## 目录 - 它解决什么问题 - 快速开始 - 为什么必须先领券 - 输出示例 - 常用参数 - 更多场景 - 求解原理 - 项目结构 - FAQ --- ## 它解决什么问题 减肥期想吃麦当劳、两个人只有 70 块、团队下午茶要凑够 8 份还不能太咸—— 这些本质上是同一个问题:**带约束的组合优化**。 菜单有上百个餐品、套餐还有隐藏的默认组成、卡包里躺着几张不知道能不能用的券, 靠人脑根本算不清。这个项目把它解出来,一次给出三套方案: | 目标 | 含义 | | --- | --- | | 最省钱 | 满足热量下限前提下的最低实付,附人均花费 | | 最扛饿 | 每元买到的千卡最高(性价比) | | 最均衡 | 蛋白供能比接近 20%、钠更低、总热量贴近目标 | --- ## 快速开始 纯 Python 标准库,**零依赖**,不用 pip 装任何东西。 [code] 把 MCP 接入任意支持 Streamable HTTP 的客户端,示例见 `mcp-config.example.json` (仅含环境变量占位符,不含任何真实凭据)。 --- ## 为什么必须先领券 这是本项目和"随便挑几样"最大的区别。券链路用到三个 Tool: [code] 之所以必须单独处理,是因为实测发现两个反直觉的坑——**这两个坑不修,券就等于白领**: **坑 1:券对应的商品不在菜单里。** 「中薯条买一送一」是一个券专属商品(编码 `9900012387`),`query-meals` 里根本查不到。 不单独建模,它永远进不了候选池——而它恰恰是最划算的选项(原价 ¥27,凭券 ¥13.5)。 **坑 2:不带券核价会静默丢件。** 把券商品塞进订单但不带券,接口会**悄悄把它丢掉**,只返回其余商品的合计: [code] 照 ¥28 去比较,会得出「不用券更便宜」的荒谬结论——而这单根本下不了。 代码做了三层防护:`price <= 0` 判无效、比对 `productList` 条数发现丢件、 含券商品的方案只认带券那次核价。 --- ## 输出示例 [code] [code] ### 生成可分享的 HTML 卡片 [code] 会输出一张带券信息、性价比、营养指标的方案卡片,浏览器直接打开 `docs/sample.html` 即可看到效果。 --- ## 常用参数 | 参数 | 说明 | 默认 | | --- | --- | --- | | `--people` | 用餐人数 | 1 | | `--budget` | 总预算上限(元),0 表示不限 | 0 | | `--kcal` | 每人热量上限(kcal),0 表示不限 | 0 | | `--min-kcal` | 每人热量下限,默认取上限的 60% | 自动 | | `--avoid` | 忌口关键词,逗号分隔 | 空 | | `--extra` | 除主食外允许的共享附加件数 | 1 | | `--main` | 主食件数下限 | 等于人数 | | `--top` | 每种目标输出的方案数 | 1 | | `--no-claim` | 跳过一键领券,只用现有卡包 | 关闭 | | `--html` | 输出可视化 HTML 卡片到指定路径 | 无 | | `--json` | 额外输出结构化结果,便于接自己的前端 | 关闭 | | `--city` / `--keyword` | 定位门店 | 上海 / 人民广场 | | `--store-code` | 直接指定门店编码,跳过搜索 | 无 | | `--be-type` | 1 到店自取,2 麦乐送,5 得来速,6 团餐 | 1 | --- ## 更多场景 [code] --- ## 求解原理 MCP 负责提供真实数据与权威核价,**重计算放本地**,两者通过约束求解衔接—— 而不是把大模型当计算器反复打接口。 1. **真实数据**:门店、菜单、现价/原价、营养成分全部实时拉取,菜单换了项目不用改。 2. **套餐营养反推**:营养表只有单品维度,菜单里大量是组合餐。命中失败时用 `query-meal-detail` 拆出套餐默认组成(主食+小食+饮料)逐项累加, 营养覆盖率从 32% 提到接近全覆盖。 3. **券商品单独建模**:菜单里买不到的券专属商品,带券核价拿到真实到手价后强制入池; 「买一送一」类券还会把营养翻倍,避免把两份薯条的热量算成一份。 4. **约束建模**:主食件数、预算上限、热量上下限、忌口关键词同时生效, 避免「最省钱 = 两个冰淇淋」这种废解。 5. **多样性保留**:三个目标各自用自己的代理指标先排一遍、各贡献 1/3 名额再统一核价。 否则先按价格砍到 N 个,最扛饿和最均衡就只能矮子里拔将军。 6. **按实付复核预算**:菜单价是「起价」,套餐真实核价往往更高 (实测有组合菜单合计 ¥128、实付 ¥155),所以核价后必须再复核一次预算。 调用量控制在单次 80 次以内,远低于 600 次/分钟的限额;客户端内置 429 指数退避, 营养表落盘缓存 12 小时,领券后查卡包会强制回源避免读到旧缓存。 --- ## 项目结构 [code] --- ## FAQ **会帮我自动下单吗?** 不会。项目定位是决策辅助,只用到了查询与核价类 Tool,不用下单类 Tool, 避免自动化下单带来的资金风险。方案出来后你自己确认再下单。 **一键领券是真实操作吗?** 是。`auto-bind-coupons` 会把券真实领进你的卡包。默认开启,不想要加 `--no-claim`。 **为什么算出来的价格跟 App 里看到的不一样?** 所有金额都以 `calculate-price` 的实时返回为准