# 工作流程

### 前置步骤：安装/升级 FlyAI CLI

在执行任何搜索之前，**必须先执行安装命令**（无论是否已安装，确保为最新版本）：

```bash
npm install -g @fly-ai/flyai-cli@latest --registry=https://registry.npmjs.org
```

> 💡 此命令会自动处理首次安装和版本升级，无需手动判断是否已安装。**安装后验证：**
```bash
flyai --help
```

**安装失败处理：**
| 情况 | 处理方式 |
|-----|---------|
| npm 未安装 | 提示用户先安装 Node.js (https://nodejs.org/) |
| 权限不足 | 建议使用 `sudo npm install -g @fly-ai/flyai-cli@latest --registry=https://registry.npmjs.org` 或使用 nvm 管理 Node |
| 网络问题 | 建议用户检查网络或使用国内镜像 `npm config set registry https://registry.npmmirror.com` |

**注意：** 此步骤只在首次使用时执行，后续调用会直接跳过已安装的情况。

### 第1步：读取 Memory 并收集盲盒条件

**1.1 读取用户记忆**

首先调用 `search_memory` 查询用户是否有历史偏好：

```python
search_memory(
  query="用户旅行偏好、常驻城市、去过的城市",
  keywords="旅行,盲盒,偏好,去过,城市",
  category="user_hobby",
  depth="shallow"
)
```

**1.2 收集盲盒底线条件**

使用 `ask_user_question` 收集用户的底线条件。

**必须收集的条件**：
1. **人均预算上限** - 如"不超过3000"
2. **最长飞行时间** - 如"3小时内"
3. **出行时间** - 如"下周五出发，3天2晚"

**可选收集的条件**：
4. **出发城市** - 如果 Memory 中没有记录
5. **硬性排除** - 如"不去海边"、"去过的城市不要"

**提问示例**：

```json
{
  "questions": [
    {
      "question": "🎁 准备开盲盒了！先设个底线——人均预算上限是多少？",
      "header": "预算上限",
      "options": [
        { "label": "¥1500以内", "description": "经济实惠型盲盒" },
        { "label": "¥2000以内", "description": "舒适体验型盲盒" },
        { "label": "¥3000以内", "description": "品质优选型盲盒" },
        { "label": "¥5000以内", "description": "豪华惊喜型盲盒" }
      ]
    },
    {
      "question": "飞多久你能接受？（影响能抽到的目的地范围）",
      "header": "飞行时间",
      "options": [
        { "label": "2小时内 ✈️", "description": "周边城市，轻松出行" },
        { "label": "3小时内 ✈️✈️", "description": "国内主流目的地" },
        { "label": "4小时内 ✈️✈️✈️", "description": "覆盖全国+东南亚" },
        { "label": "不限 🌏", "description": "全球盲盒，惊喜更大" }
      ]
    },
    {
      "question": "什么时候出发？玩几天？",
      "header": "出行时间",
      "options": [
        { "label": "这周末·2天1晚", "description": "说走就走" },
        { "label": "下周末·3天2晚", "description": "短途度假" },
        { "label": "小长假·4-5天", "description": "深度体验" },
        { "label": "我来指定日期", "description": "自定义时间" }
      ]
    },
    {
      "question": "有什么地方是绝对不要的吗？（多选）",
      "header": "排除条件",
      "multiSelect": true,
      "options": [
        { "label": "去过的城市不要 🚫", "description": "追求新鲜感" },
        { "label": "不要海边 🚫🏖️", "description": "不喜欢沙滩" },
        { "label": "不要爬山 🚫🏔️", "description": "腿不想累" },
        { "label": "都可以，全凭缘分 🎲", "description": "真正的盲盒精神" }
      ]
    }
  ]
}
```

### 第2步：搜索候选目的地池

根据用户设定的底线条件，调用 FlyAI 搜索所有可能的目的地。

**2.1 广泛搜索候选目的地**

```bash
NODE_TLS_REJECT_UNAUTHORIZED=0 flyai keyword-search \
  --query "[出发城市]出发 飞行[X小时]内 [天数]天 人均[预算]以内"
```

**2.2 对候选目的地搜索机票价格**

对每个候选目的地，搜索往返机票：

```bash
NODE_TLS_REJECT_UNAUTHORIZED=0 flyai search-flight \
  --origin "[出发城市]" --destination "[候选目的地]" \
  --dep-date [出发日期] --back-date [返回日期] \
  --sort-type 3 --journey-type 1
```

**2.3 筛选符合条件的目的地**

计算每个目的地的预估总花费（机票+酒店+基本开销），筛选出：
- 总花费 ≤ 用户预算上限
- 飞行时间 ≤ 用户设定的上限
- 不在用户排除列表中

**2.4 排除用户去过的城市**（如果用户设定了此条件）

从 Memory 中读取用户去过的城市，从候选池中排除。

### 第3步：盲盒交互 — 准备揭晓

当候选池确定后，输出盲盒准备界面：

```markdown
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🎁 你的旅行盲盒 · 准备好了吗？

  📦 盲盒条件确认：
  ✅ 人均 ≤ ¥[预算上限]
  ✅ 飞行 ≤ [X]小时
  ✅ [出发日期]出发，[天数]天[晚数]晚
  ✅ 排除：[排除条件列表]

  🎲 候选池中有 [N] 个目的地符合条件
  已随机抽取 1 个！

  👇 准备好了就说"拆开"

  [🎁 拆盲盒！]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```

**注意**：在用户说"拆开"之前，不要透露抽中的目的地。

### 第4步：拆盲盒 — 揭晓目的地

用户确认后，从候选池中**随机抽取**一个目的地，然后：

**4.1 搜索抽中目的地的详细信息**

```bash
# 搜索酒店
NODE_TLS_REJECT_UNAUTHORIZED=0 flyai search-hotel \
  --dest-name "[抽中城市]" \
  --check-in-date [入住日期] --check-out-date [退房日期] \
  --max-price [预算/晚数/60%] --sort rate_desc

# 搜索景点
NODE_TLS_REJECT_UNAUTHORIZED=0 flyai search-poi --city-name "[抽中城市]"
```

**4.2 输出盲盒揭晓 + 完整方案**

```markdown
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

🎉🎉🎉 恭喜！你的盲盒目的地是——

    ╔═══════════════════════╗
    ║                       ║
    ║    🏙  [城市名]  🏙     ║
    ║                       ║
    ║  "[城市标语/特色]"     ║
    ║  [城市一句话描述]       ║
    ║                       ║
    ╚═══════════════════════╝

💡 为什么是[城市名]？
  · [亮点1]
  · [亮点2]
  · [亮点3]
  · 而且——[预算优势]！

━━━━━━━━━━━━━━━━━━

📋 你的[城市名][天数]天[晚数]晚方案：

✈️ 机票：
  去 [航班号] [日期] [时间] ¥[价格]/人
  回 [航班号] [日期] [时间] ¥[价格]/人
  往返 ¥[总价]/人

🏨 酒店：
  [酒店名称] ¥[单价]/晚 ⭐[评分]
  [晚数]晚 = ¥[总价]（[酒店亮点]）

📍 精华景点：
  · [景点1] — [一句话描述]（[门票价格]）
  · [景点2] — [一句话描述]（[门票价格]）
  · [景点3] — [一句话描述]（[门票价格]）
  · [景点4] — [一句话描述]（[门票价格]）
  
🍜 必吃美食：
  [当地特色美食列表]
  （人均¥[金额]就能吃到撑）

💰 总费用预估：
  机票 ¥[金额] + 酒店 ¥[金额] + 餐饮 ¥[金额] + 交通 ¥[金额]
  = 人均 ¥[总价] 🎉
  [与预算对比，省了多少钱]

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

🎲 不满意？你有3次重抽机会：
  [🔄 换一个盲盒（还剩2次）]
  [✅ 就它了！开始预订]
  [📋 保存方案，容我想想]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```

### 第5步：重抽机制

如果用户不满意，提供最多 **3次重抽机会**：

- 每次重抽从**剩余候选池**中随机抽取
- 重抽时输出："🔄 盲盒重摇中...从剩余 [N-1] 个候选中抽取..."
- 3次都不满意 → 展示完整候选列表让用户自选

```markdown
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📋 盲盒模式结束 — 以下是所有符合条件的目的地：

  1️⃣ [城市A] — 往返¥[价格] | [特色标签]
  2️⃣ [城市B] — 往返¥[价格] | [特色标签]
  3️⃣ [城市C] — 往返¥[价格] | [特色标签]
  ...

  👉 选一个数字，我帮你生成完整方案
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```

---

---
