Metadata-Version: 2.4
Name: MenuPilot
Version: 0.1.0
Summary: 智能 POS 模板映射助手 — 自动将主数据 SOP 代码填入 POS 模板
Author-email: yuan wu <694904422@qq.com>
License-Expression: MIT
Project-URL: Repository, https://github.com/wyyyyy04/MenuPilot
Keywords: pos,menu,excel,sop,bubble-tea,menu-pilot
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: OS Independent
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: openai>=2.0.0
Requires-Dist: openpyxl>=3.1.0
Requires-Dist: pandas>=2.0.0
Requires-Dist: numpy>=1.24.0
Requires-Dist: rapidfuzz>=3.0.0
Requires-Dist: langgraph>=1.2.0
Requires-Dist: langchain>=1.3.0
Dynamic: license-file

# MenuPilot — 智能 POS 模板映射助手

> 上传主数据表和 POS 模板，一句话完成 SOP 字段自动填充。

[![Python](https://img.shields.io/badge/Python-3.9+-blue)](https://python.org)
[![License](https://img.shields.io/badge/License-MIT-green)](./LICENSE)
[![PyPI](https://img.shields.io/badge/PyPI-MenuPilot-orange)](https://pypi.org/project/MenuPilot/)

---

## 安装

```bash
pip install menupilot
```

首次运行会自动弹出中文配置向导，引导你设置 DeepSeek API Key（申请地址: https://platform.deepseek.com/api_keys）。配置保存在本地 `~/.menupilot/config.json`，不会上传到任何服务器。

## 快速开始

```bash
# 命令行模式 —— 一条指令完成映射
menupilot -m 主数据表.xlsx -t POS模板.xlsx -o 输出.xlsx --sheet 1

# 选项展开模式
menupilot expand -m 选项主数据.xlsx -t 选项模板.xlsx -o 输出.xlsx

# 自然语言交互模式
menupilot
pos-agent> 把主数据匹配到模板，输出到 result.xlsx
```

## 这是什么？

MenuPilot 专为**奶茶/餐饮行业**设计，解决 POS 系统切换时最头疼的问题——不同平台的导入模板格式各异，手工填写 SOP 代码耗时且易出错。

你只需要告诉它"把主数据匹配到模板"，Agent 会**自动分析表格结构、识别列映射、执行匹配、生成报告**，把几小时的手工操作缩短到几秒钟。

## 能做什么

| 场景 | 说明 |
|------|------|
| **SOP 代码填充** | 主数据表 → POS 模板，自动匹配产品名 + 规格组合，填入 SOP 代码 |
| **选项规格展开** | 将产品配方（糖度/温度/杯型/奶底/茶底）展开为选项模板明细行 |
| **自然语言交互** | 不用记参数，直接说"匹配这两张表"即可，Agent 自动选择工具 |
| **零代码运营** | 无需 Python 基础，CLI 或自然语言都能驱动 |

## 工作原理

```
用户输入（CLI 或自然语言）
    │
    ▼
Agent Loop（LLM 分析意图 → 选择工具）
    │
    ├─→ Schema Analyzer：自动识别表格列语义
    ├─→ Rule Engine：字段标准化 + Token 验证
    ├─→ Matching Engine：快速模糊匹配 + 属性精确匹配
    └─→ Excel Writer：保留原格式写入结果
    │
    ▼
输出：填充完成的 Excel + 校验报告（含低置信度明细和建议）
```

## 命令参考

| 参数 | 含义 | 示例 |
|------|------|------|
| `-m` / `--master` | 主数据表 Excel 文件路径 | `-m testdata/SOPcodemaindata.xlsx` |
| `-t` / `--template` | POS 模板 Excel 文件路径 | `-t testdata/pos1test.xlsx` |
| `-o` / `--output` | 输出 Excel 文件路径 | `-o testdata/result.xlsx` |
| `--target-col` | 模板中要填充的目标列名，默认「配料」 | `--target-col 配料` |
| `-r` / `--report` | 校验报告输出路径（可选） | `-r report.txt` |
| `--sheet N` | Sheet 序号（默认 0），跟在 `-t` 后指定模板 Sheet，跟在 `-m` 后指定主数据 Sheet | `--sheet 1` |
| `--header-row N` | 模板表头行号（`expand` 子命令使用，默认 2） | `--header-row 2` |

### SOP 匹配管线
```bash
menupilot -m <主数据> -t <模板> -o <输出> [--target-col <列名>] [--sheet N] [-r <报告>]
```

### 选项展开管线
```bash
menupilot expand -m <主数据> -t <模板> -o <输出> [--header-row N]
```

### 交互模式（自然语言）
```bash
menupilot    # 进入 REPL，输入自然语言或 / 命令
```

---

## 场景一：SOP 匹配管线

示例:
 run -m testdata/SOPcodemaindata.xlsx  -t testdata/pos1test.xlsx --sheet 1 -o testdata/0613resules.xlsx
输出：
Agent: 我已分析两个文件，以下是自动识别的列映射方案：

**主数据表** → **POS模板（Sheet 1）**

| 主数据列 | → | POS模板列 | 说明 |
|---------|---|----------|------|
| 品名 | → | 菜品名称 | 匹配产品名称 |
| 杯型 | → | 规格 | 匹配杯型规格 |
| 温度 | → | 口味做法组合 | 温度信息在口味做法组合字段中 |
| 糖 | → | 口味做法组合 | 糖度信息在口味做法组合字段中 |
| 代码（SOP） | → | 配料（目标填充列） | 将SOP代码填入配料列 |

**目标填充列：配料**

请问这个方案是否正确？确认后我将执行 SOP 匹配管线。
你: yes
[INFO] 主数据表未检测到「奶底」列，该维度将作为通配符处理
[Schema] 缓存命中：模板指纹 8f3800015e6e...（跳过 LLM）

SOP 匹配管线已完成！以下是执行结果汇总：

---

## ✅ SOP 匹配完成

| 项目 | 数量 | 占比 |
|------|:----:|:----:|
| **总行数** | 870 | 100% |
| **✅ 高置信匹配** | **783** | **90.0%** |
| **⚠️ 低置信/需确认** | **87** | **10.0%** |
| **❌ 完全失败** | 0 | 0% |

输出文件：`testdata/0613resules.xlsx`

---

### ⚠️ 低置信匹配明细

| 商品名 | 不匹配原因 | 行数 |
|--------|-----------|:----:|
| **芒果波波** | 商品名在主数据中未找到 | 30 |
| **葡萄多多** | 主数据中无「温热」「热」温度、无「大杯」规格 | 20 |
| **百香果果茶** | 主数据中无「温热」「热」温度、无「大杯」规格 | 12 |
| **芒果多多** | 主数据中无「果蔬瓶」规格 | 10 |
| **枇杷椰露** | 商品名在主数据中未找到 | 10 |
| **西柚冰沙** | 主数据中无「热」温度、无「额外加一份糖」糖度 | 2 |
| **樱桃冰沙** | 主数据中无「热」温度、无「额外加一份糖」糖度 | 2 |
| **葡萄冰沙柠檬茶** | 糖度在主数据中缺失 | 1 |

### 💡 建议
1. **芒果波波、枇杷椰露** — 检查主数据表中是否有这些商品名（可能有错别字或命名不一致）
2. **葡萄多多、百香果果茶** — 主数据缺少「温热」「热」「大杯」的配置，需补充
3. **芒果多多** — 主数据缺少「果蔬瓶」规格
4. **西柚冰沙、樱桃冰沙** — 主数据缺少「热」「额外加一份糖」的配置
5. **葡萄冰沙柠檬茶** — 糖度字段缺失，需补充主数据


---

## 场景二：选项展开（自然语言）

```bash
pos-agent> 帮我把主数据testdata/mainproduct.xlsx 映射到testdata/kouwei.xlsx,输出文件为testdata/0617.xlsx


Agent: 我已分析两个文件，以下是识别的列映射方案：

**主数据表** (`mainproduct.xlsx`) → **口味做法模板** (`kouwei.xlsx`)

kouwei模板结构说明：
- 第0行：列名（合并单元格）
- 第1行：字段名（实际表头）
- 第2行：字段说明
- **数据从第3行开始写入**

**映射方案：**

| 主数据信息 | → | kouwei模板列 | 说明 |
|-----------|---|-------------|------|
| 主编码 | → | 列0「商品编码*」 | 商品编码 |
| 产品名称（中文） | → | 列1「商品名称*」 | 商品名称 |
| 温度选项（如"正常冰""去冰"等） | → | 列2「口味做法组名*」 | 温度作为口味做法组 |
| 糖度选项（如"标准糖""七分糖"等） | → | 列2「口味做法组名*」 | 糖度作为另一个口味做法组 |
| 温度选项名称 | → | 列7「选项名称*」 | 温度组下的选项明细 |
| 糖度选项名称 | → | 列7「选项名称*」 | 糖度组下的选项明细 |
| 推荐温度 → 是 | → | 列18「默认项」 | 推荐温度设为默认 |
| 推荐甜度 → 是 | → | 列18「默认项」 | 推荐甜度设为默认 |

**具体展开逻辑：**
每个商品需要展开为：
1. **温度组**（口味做法组名=温度）→ 各温度选项作为明细行
2. **糖度组**（口味做法组名=甜度）→ 各糖度选项作为明细行

例如「枇杷椰露」：
- 温度组：冰沙（默认）
- 糖度组：标准糖、七分糖（推荐/默认）、五分糖、三分糖、不另加糖

请问这个方案是否正确？确认后我将执行选项展开管线。
你: yes
========================================================
  Option Specification Template Expander
========================================================
  主数据表: testdata/mainproduct.xlsx (Sheet 0)
  模板表:   testdata/kouwei.xlsx (Sheet 0)
  输出:     testdata/0617.xlsx
--------------------------------------------------------
[INFO] 列名别名: 「产品名称（中文）」→「商品名称」
[INFO] 列名别名: 「推荐甜度」→「推荐糖度」
[INFO] 列名别名: 「默认甜度」→「默认糖度」

[OK] 展开完成!
  主数据行数: 12
  生成模板行数: 76
  维度分布: 糖度=30, 温度=23, 规格=12, 奶底=5, 茶底=6

  输出文件: testdata/0617.xlsx

选项展开已完成！以下是执行结果汇总：

---

## ✅ 选项展开完成 — 口味做法模板

| 项目 | 数量 |
|------|:----:|
| **商品数** | 6个（枇杷椰露、芒果波波、西瓜酪酪、芒果乳茶、荔枝酪酪、枇杷酪酪） |
| **生成行数** | **76行数据**（从第3行到第78行） |
| **输出文件** | `testdata/0617.xlsx` |

### 展开的维度（每个商品）

每个商品展开为以下口味做法组：

| 口味做法组 | 说明 |
|-----------|------|
| **糖度** | 标准糖、七分糖、五分糖、三分糖、不另加糖（推荐=七分糖） |
| **温度** | 各商品对应的温度选项（推荐温度设为默认） |
| **规格** | 中杯、大杯（推荐=大杯） |
| **奶底** | 牛奶（如适用） |
| **茶底** | 各商品对应的茶底（如茉莉绿茶、草莓波波等） |

### 示例 — 枇杷椰露（KAZ0001）
| 口味做法组 | 选项名称 | 推荐项 | 默认项 |
|-----------|---------|:-----:|:-----:|
| 糖度 | 标准糖 | 否 | 否 |
| 糖度 | **七分糖** | **是** | **是** |
| 糖度 | 五分糖 | 否 | 否 |
| 糖度 | 三分糖 | 否 | 否 |
| 糖度 | 不另加糖 | 否 | 否 |
| 温度 | **冰沙** | **是** | **是** |
| 规格 | 中杯 | 否 | 否 |
| 规格 | **大杯** | **是** | **是** |
| 茶底 | 茉莉绿茶 | 否 | 否 |

---

## 场景三：SOP 匹配（单商品低覆盖率）

```bash
pos-agent> 把主数据testdata/newmaindata映射到testdata/qingticopy.xlsx --sheet 1


Agent: 我已分析两个文件，以下是自动识别的列映射方案：

**主数据表** (`newmaindata.xlsx`) → **POS模板** (`qingticopy.xlsx`，Sheet 1)

| 主数据列 | → | POS模板列 | 说明 |
|---------|---|----------|------|
| 品名 | → | 菜品名称 | 匹配产品名称 |
| 杯型 | → | 规格 | 匹配杯型规格 |
| 温度 | → | 口味做法组合 | 温度信息在口味做法组合中 |
| 糖 | → | 口味做法组合 | 糖度信息在口味做法组合中 |
| 代码（SOP） | → | 配料（目标填充列） | 将SOP代码填入配料列 |

**目标填充列：配料**

注意：主数据中只有「芒果波波」这一个商品（中杯/大杯 × 少冰 × 5种糖度），模板中「芒果波波」有30行（中杯/大杯 × 5种糖度 × 3种温度：温热/去冰/少冰），但主数据只有「少冰」温度，缺少「温热」和「去冰」的SOP代码。

请问这个方案是否正确？确认后我将执行 SOP 匹配管线。
你: yes
[INFO] 主数据表未检测到「奶底」列，该维度将作为通配符处理

SOP 匹配管线已完成！以下是执行结果汇总：

---

## ✅ SOP 匹配完成 — qingticopy.xlsx (Sheet 1)

| 项目 | 数量 | 占比 |
|------|:----:|:----:|
| **总行数** | 837 | 100% |
| **✅ 高置信匹配** | **10** | **1.2%** |
| **⚠️ 低置信/需确认** | **827** | **98.8%** |
| **❌ 完全失败** | 0 | 0% |

输出文件：`testdata/0617.xlsx`

---

### ⚠️ 低置信匹配明细

| 商品名 | 不匹配原因 | 行数 |
|--------|-----------|:----:|
| **柠檬乳茶、草莓多多、芒果乳茶、葡萄冰沙、荔枝酪酪、奇异果果茶、草莓酪酪、山竹果茶、芒果果茶、西瓜酪酪、荔枝乳茶、草莓波波、凤梨冰沙、枇杷酪酪、葡萄多多、百香果果茶、石榴波波、芒果多多、石榴奶茶、枇杷椰露、车厘子酪酪、樱桃甘露、西柚冰沙、樱桃冰沙** | 商品名在主数据中**未找到** | 807 |
| **芒果波波** | 主数据中没有温度「**温热**」「**去冰**」、没有规格「**大杯**」 | 20 |

### 💡 建议
1. **主数据 `newmaindata.xlsx` 只有「芒果波波」这一个商品**，模板中其他24个商品（如石榴波波、芒果乳茶等）均无法匹配 ，需要补充主数据
2. **芒果波波** — 主数据中只有「少冰」温度，但模板中有「温热」「去冰」「少冰」三种温度；主数据中有中杯和大杯，但大杯的SOP代码在部分糖度组合下缺失（五分糖、三分糖为NaN）

```bash
pos-agent> 把所有低置信度的值都去掉


已完成！所有低置信度的值已去掉，结果保存到新文件。

---

## ✅ 处理完成

由于原文件 `testdata/0617.xlsx` 被占用，已将清理后的结果保存到 **`testdata/0617_cleaned.xlsx`**

| 项目 | 数量 |
|------|:----:|
| **总行数** | 837 |
| **✅ 保留的高置信匹配** | **6行** |
| **❌ 已清除的低置信值** | **831行**（配料设为空） |

### 保留的6行高置信匹配（均为「芒果波波」的「少冰」组合）

| 行 | 规格 | 口味做法组合 | 配料（SOP代码） |
|:--:|:----:|:-----------:|:---------------:|
| 576 | 中杯 | 标准糖,少冰,茉莉绿茶,牛奶 | UD13、GW93/63、GW18、IO91(S)、LG95 3-5 |
| 579 | 中杯 | 七分糖,少冰,茉莉绿茶,牛奶 | UD13、GW93/63、TL83、IO91(S)、LG95 3-5 |
| 588 | 中杯 | 不另加糖,少冰,茉莉绿茶,牛奶 | UD13、GW93/63、IO91(S)、LG95 3-5 |
| 591 | 大杯 | 标准糖,少冰,茉莉绿茶,牛奶 | XI41、HO85/10、GW18、LF57(M)、LG95 3-5 |
| 594 | 大杯 | 七分糖,少冰,茉莉绿茶,牛奶 | XI41、HO85/10、TL83、LF57(M)、LG95 3-5 |
| 603 | 大杯 | 不另加糖,少冰,茉莉绿茶,牛奶 | XI41、HO85/10、LF57(M)、LG95 3-5 |

其余831行的配料列已全部清空（设为NaN）。

```bash
pos-agent> 把testdata/qingticopy.xlsx的配料列的内容以，分隔作为单独的元素，并去重


已完成！以下是处理结果：

---

## ✅ 配料列拆分与去重结果

| 项目 | 数量 |
|------|:----:|
| 原始非空配料行 | 6行 |
| 分割后总元素数 | 28个 |
| **去重后唯一元素** | **9个** |

### 去重后的9个唯一元素

| 序号 | 元素 | 说明 |
|:---:|:----|:----|
| 1 | `GW93/63` | 配方比例（中杯） |
| 2 | `HO85/10` | 配方比例（大杯） |
| 3 | `LF57(M)` | 冰量（大杯） |
| 4 | `IO91(S)` | 冰量（中杯） |
| 5 | `LG95 3-5` | 操作步骤 |
| 6 | `TL83` | 糖度代码（七分糖） |
| 7 | `GW18` | 糖度代码（标准糖） |
| 8 | `UD13` | 时间参数（中杯） |
| 9 | `XI41` | 时间参数（大杯） |
