iT邦幫忙

0

# Day 18 加入隨機盲盒抽籤、轉盤模式與 LINE Quick Reply 快速選單

n8n
  • 分享至 

  • xImage
  •  

在昨天的文章中,我們透過有限狀態機(FSM)實作了多輪對話機制,讓 Agent 能夠在關鍵資訊不足時主動追問,補齊使用者的地點或預算。

然而,美食推薦領域最常見的使用情境往往不是「條件太明確」,而是每到用餐時間最讓人頭痛的終極難題:

「今天中午到底要吃什麼?隨便啦,你幫我抽一個!」

面對選擇障礙(Decision Paralysis)的使用者,一口氣丟出 5 間餐廳只會讓對方更糾結。這時候,如果能提供一個「命運盲盒 / 美食大轉盤」模式,直接隨機盲抽一家,並搭配 LINE 貼心的 Quick Reply(快速回覆按鈕),使用者的互動體驗將會變得極其有趣又解痛!

今天 Day 18,我們將在微服務中實作隨機抽籤演算法,並在 LINE 回覆中加入 Quick Reply 按鈕選單,打造一鍵重抽與快速決策體驗!


一、什麼是 LINE Quick Reply(快速回覆)?

Quick Reply 是 LINE 提供的一種懸浮在聊天室底部的快速按鈕選單:

  • 不佔版面:平時不會堆疊在聊天記錄中,點擊後會自動收合。
  • 高點擊率:提供現成的預設選項(例如「再抽一次 🎲」、「看詳細資訊 📍」、「換吃別的 🍜」),使用者無需打字,只要單指輕點即可觸發下一步。
  • 支援多種動作:支援純文字輸入、開啟網址、回傳 Postback 參數,甚至一鍵開啟手機相機或位置定位。

二、在 FastAPI 中實作抽籤與盲盒端點 (app/routers/recommendation.py)

我們在推薦模組中新增一隻專屬的盲盒隨機抽選端點 POST /api/v1/recommend/random。

Step 1:定義抽籤請求與回應 Schema (app/schemas/food.py)

在 app/schemas/food.py 新增抽籤專屬結構:

from pydantic import BaseModel, Field
from typing import Optional
from app.schemas.food import RestaurantItem

class RandomDrawRequest(BaseModel):
    user_id: str
    location: str = Field(default="板橋區", description="搜尋範圍或商圈")
    budget_max: int = Field(default=300, description="預算上限")

class RandomDrawResponse(BaseModel):
    status: str
    drawn_restaurant: Optional[RestaurantItem] = None
    remaining_count: int = 0

Step 2:實作盲盒隨機演算法 (app/routers/recommendation.py)

利用 Python 的 random.choice 從符合條件的真實 Google Places 候選名單中,隨機抽出一位「天選店家」:

import random
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from app.database import get_db
from app.schemas.food import RandomDrawRequest, RandomDrawResponse
from app.services.places import search_places

router = APIRouter(
    prefix="/api/v1/recommend",
    tags=["Food Recommendation"]
)

@router.post("/random", response_model=RandomDrawResponse)
async def draw_random_food(req: RandomDrawRequest, db: Session = Depends(get_db)):
    """
    美食大轉盤:從指定區域符合預算的店家名單中,隨機盲抽一間餐廳
    """
    # 呼叫 Places API 搜尋候選餐廳(分類設定為全部,以擴大候選池)
    candidates = await search_places(
        query_location=req.location,
        query_cuisine="全部",
        budget_max=req.budget_max
    )

    if not candidates:
        return RandomDrawResponse(status="empty", drawn_restaurant=None, remaining_count=0)

    # 隨機抽取一間
    chosen_one = random.choice(candidates)

    return RandomDrawResponse(
        status="success",
        drawn_restaurant=chosen_one,
        remaining_count=len(candidates) - 1
    )

三、升級 Dify 抽取意圖:辨識「隨機 / 抽籤」關鍵詞

回到 Dify 的系統提示詞(System Prompt),新增一個 mode 標籤,讓大腦自動辨識使用者是否正處於「選擇困難」模式:

你是一個專業的美食推薦助理前置解析器。請從對話中提取以下欄位並輸出 JSON:

- mode: 對話意圖(若使用者提及「隨便」、「幫我抽」、「選擇困難」、「不知道吃什麼」、「命運轉盤」,請填 "random";一般查詢填 "search")
- cuisine: 料理類型(未提及填 "全部")
- budget_max: 每人預算(未提及預設 300)
- location: 地區或捷運站(未提及填 "板橋區")

輸出範例:
{
  "mode": "random",
  "cuisine": "全部",
  "budget_max": 300,
  "location": "板橋"
}

四、在 n8n 中組裝 Quick Reply 快速選單

當後端回傳抽籤結果時,我們使用 LINE Quick Reply 按鈕,讓使用者能一鍵進行「再抽一次」或「滿意導航」。

Step 1:在 Code 節點組裝附帶 Quick Reply 的訊息結構

在 n8n 的訊息格式化節點中,編寫 JavaScript 邏輯:

const responseData = $input.first().json;
const replyToken = $('Parse AI JSON').first().json.reply_token;

// 1. 若為抽籤模式 (Random Mode)
if (responseData.drawn_restaurant) {
  const shop = responseData.drawn_restaurant;
  const mapUri = `[https://www.google.com/maps/search/?api=1&query=$](https://www.google.com/maps/search/?api=1&query=$){encodeURIComponent(shop.location + ' ' + shop.name)}`;

  const resultText = `🎲 命運的美食轉盤轉動中...\n\n` +
                     `今日午餐就是它了:【${shop.name}】!✨\n` +
                     `📍 地址:${shop.location}\n` +
                     `💰 均消:約 ${shop.price_avg} 元\n` +
                     `⭐️ 評分:${shop.rating}\n\n` +
                     `不滿意嗎?可以點下方按鈕重新抽籤哦!`;

  // 2. 構造底部 Quick Reply 互動按鈕
  const quickReply = {
    items: [
      {
        type: "action",
        action: {
          type: "message",
          label: "🎲 再抽一次",
          text: "幫我隨便抽一間"
        }
      },
      {
        type: "action",
        action: {
          type: "uri",
          label: "🗺️ 查看地圖",
          uri: mapUri
        }
      },
      {
        type: "action",
        action: {
          type: "message",
          label: "🍜 改吃日式",
          text: "推薦西門町日式拉麵"
        }
      }
    ]
  };

  return {
    reply_token: replyToken,
    messages: [
      {
        type: "text",
        text: resultText,
        quickReply: quickReply
      }
    ]
  };
}

// 若是一般推薦模式,則維持昨天的 Flex Carousel 回傳
return $input.first().json;

五、實機操作與互動驗收

確保 FastAPI 伺服器與 n8n 工作流均正常處於 Active 狀態,拿起手機打開 LINE 測試:
1.發送選擇困難口語:

使用者:「中午不知道吃什麼,隨便幫我抽一個」

2.機器人秒抽回覆:

美食小幫手:
🎲 命運的美食轉盤轉動中...
今日午餐就是它了:【港式燒臘大王】!✨
📍 地址:新北市板橋區...
💰 均消:約 120 元
⭐️ 評分:4.4

3.聊天室底部懸浮按鈕:
手機螢幕底部整齊彈出三顆膠囊狀的 Quick Reply 選項:

[ 🎲 再抽一次 ]

[ 🗺️ 查看地圖 ]

[ 🍜 改吃日式 ]

4.一鍵互動:
直接點擊 [ 🎲 再抽一次 ],無需動手打字,系統立刻觸發下一輪隨機盲盒抽籤!
今日結語與明日預告
今天我們為美食 Agent 注入了趣味感與極高黏著度的互動體驗:

設計了專屬的 隨機盲盒抽籤演算法,直接化解使用者的選擇障礙。

整合了 LINE Quick Reply 快速選單,消除了在手機上反覆打字的繁瑣。

透過 Dify 意圖標籤切換,實現普通推薦與轉盤模式的無縫流轉!

抽到喜歡的餐廳之後,很多使用者希望能把口袋名單保存起來,下次隨時調閱。
明天 Day 19,我們將要實作個人化收藏夾:擴充 SQLite 資料庫,實現「加入我的最愛」與「口袋名單歷史回顧」功能! 敬請期待!


*提醒邦友,使用第三方服務/API 時,請務必評估資安風險與隱私保護
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言