Qwen3-VL-8B-Thinking旅行計画ツール

上級

これはPersonal Productivity, Multimodal AI分野の自動化ワークフローで、18個のノードを含みます。主にSet, Code, Gmail, Slack, Webhookなどのノードを使用。 Skyscanner、Booking.com、Gmailを活用したAI最適化旅行行程生成システム

前提条件
  • Googleアカウント + Gmail API認証情報
  • Slack Bot Token または Webhook URL
  • HTTP Webhookエンドポイント(n8nが自動生成)
  • ターゲットAPIの認証情報が必要な場合あり
ワークフロープレビュー
ノード接続関係を可視化、ズームとパンをサポート
ワークフローをエクスポート
以下のJSON設定をn8nにインポートして、このワークフローを使用できます
{
  "id": "Yi6ZHj09hQ6aoCt2",
  "meta": {
    "instanceId": "b91e510ebae4127f953fd2f5f8d40d58ca1e71c746d4500c12ae86aad04c1502",
    "templateCredsSetupCompleted": true
  },
  "name": "Qwen3-VL-8B-Thinking Travel Planner: Search-->Compare-->Email Best Itineraries",
  "tags": [],
  "nodes": [
    {
      "id": "f3a23f51-4039-4a08-bad5-d9e1f475e8af",
      "name": "Webhook - Travel Request",
      "type": "n8n-nodes-base.webhook",
      "position": [
        2832,
        -256
      ],
      "webhookId": "1fa59b2c-c301-41bf-8f01-6feb10be6aa8",
      "parameters": {
        "path": "travel-search",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "65e7017e-5242-4875-bf24-661fc68eb771",
      "name": "リクエストデータ抽出",
      "type": "n8n-nodes-base.set",
      "position": [
        2992,
        -256
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "a1",
              "name": "destination",
              "type": "string",
              "value": "={{ $json.body.destination || 'Shanghai' }}"
            },
            {
              "id": "a2",
              "name": "departureCity",
              "type": "string",
              "value": "={{ $json.body.departureCity }}"
            },
            {
              "id": "a3",
              "name": "checkInDate",
              "type": "string",
              "value": "={{ $json.body.checkInDate }}"
            },
            {
              "id": "a4",
              "name": "checkOutDate",
              "type": "string",
              "value": "={{ $json.body.checkOutDate }}"
            },
            {
              "id": "a5",
              "name": "travelers",
              "type": "number",
              "value": "={{ $json.body.travelers || 1 }}"
            },
            {
              "id": "a6",
              "name": "email",
              "type": "string",
              "value": "={{ $json.body.email }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "51891077-df8a-45db-b575-0585f52aa1db",
      "name": "フライト検索 - Skyscanner",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3216,
        -640
      ],
      "parameters": {
        "url": "https://skyscanner-api.p.rapidapi.com/v3/flights/live/search/create",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "authentication": "predefinedCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "query",
              "value": "={{ {\"market\":\"US\",\"locale\":\"en-US\",\"currency\":\"USD\",\"queryLegs\":[{\"originPlace\":{\"queryPlace\":{\"iata\":$json.departureCity}},\"destinationPlace\":{\"queryPlace\":{\"iata\":$json.destination}},\"date\":{\"year\":$json.checkInDate.split('-')[0],\"month\":$json.checkInDate.split('-')[1],\"day\":$json.checkInDate.split('-')[2]}}],\"adults\":$json.travelers,\"cabinClass\":\"CABIN_CLASS_ECONOMY\"} }}"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "X-RapidAPI-Key",
              "value": "={{ $credentials.rapidApiKey }}"
            },
            {
              "name": "X-RapidAPI-Host",
              "value": "skyscanner-api.p.rapidapi.com"
            }
          ]
        },
        "nodeCredentialType": "httpHeaderAuth"
      },
      "typeVersion": 4.2
    },
    {
      "id": "953e3d0b-528d-41ba-b7ba-4fc7363c703f",
      "name": "ホテル検索 - Booking.com",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3216,
        -448
      ],
      "parameters": {
        "url": "https://booking-com.p.rapidapi.com/v1/hotels/search",
        "options": {},
        "sendQuery": true,
        "sendHeaders": true,
        "authentication": "predefinedCredentialType",
        "queryParameters": {
          "parameters": [
            {
              "name": "dest_type",
              "value": "city"
            },
            {
              "name": "dest_id",
              "value": "={{ $('Extract Request Data').item.json.destination }}"
            },
            {
              "name": "checkin_date",
              "value": "={{ $('Extract Request Data').item.json.checkInDate }}"
            },
            {
              "name": "checkout_date",
              "value": "={{ $('Extract Request Data').item.json.checkOutDate }}"
            },
            {
              "name": "adults_number",
              "value": "={{ $('Extract Request Data').item.json.travelers }}"
            },
            {
              "name": "order_by",
              "value": "price"
            },
            {
              "name": "units",
              "value": "metric"
            },
            {
              "name": "room_number",
              "value": "1"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "X-RapidAPI-Key",
              "value": "={{ $credentials.rapidApiKey }}"
            },
            {
              "name": "X-RapidAPI-Host",
              "value": "booking-com.p.rapidapi.com"
            }
          ]
        },
        "nodeCredentialType": "httpHeaderAuth"
      },
      "typeVersion": 4.2
    },
    {
      "id": "133388c8-6fe6-42cb-94b7-f76bf0cb4b2e",
      "name": "メール送信 via Gmail",
      "type": "n8n-nodes-base.gmail",
      "position": [
        4480,
        64
      ],
      "webhookId": "10b4febd-7ca2-41c6-866c-979cc9592dbb",
      "parameters": {
        "sendTo": "={{ $('Webhook - Travel Request').item.json.body.email }}",
        "message": "={{ $json.html }}",
        "options": {},
        "subject": "🤖 AI-Optimized Travel Itinerary: {{ $('Extract Request Data').item.json.destination }} Trip"
      },
      "typeVersion": 2.1
    },
    {
      "id": "5fcf6271-c0fd-430b-858e-4a462e98b648",
      "name": "Webhook への返信",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        4784,
        64
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ { \"success\": true, \"message\": \"AI-optimized travel itinerary sent successfully!\", \"itinerariesCount\": $('AI Score & Recommendations').all().length, \"email\": $('Extract Request Data').item.json.email, \"bestDeal\": { \"price\": $('AI Score & Recommendations').first().json.totalPrice, \"aiScore\": $('AI Score & Recommendations').first().json.aiScore } } }}"
      },
      "typeVersion": 1
    },
    {
      "id": "2aedf5b8-536b-44bc-9b6d-3b80daa2c6c0",
      "name": "AIエージェント - 行程オプティマイザー",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        3808,
        -496
      ],
      "parameters": {
        "options": {
          "systemMessage": "You are a travel expert. Analyze the provided flight and hotel combinations and provide a detailed recommendation score (0-100) for each itinerary based on:\n1. Total price value\n2. Flight convenience (stops, duration, departure times)\n3. Hotel quality (rating, reviews, location)\n4. Overall trip experience\n\nFor each itinerary, return:\n- score (0-100)\n- reasoning (2-3 sentences)\n- highlights (bullet points)\n- warnings (if any)\n\nFormat as JSON array."
        }
      },
      "typeVersion": 1
    },
    {
      "id": "759f481f-598a-4bff-a3d1-3cb3310ecb88",
      "name": "代替フライト検索 - Kiwi",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3216,
        -256
      ],
      "parameters": {
        "url": "https://api.tequila.kiwi.com/v2/search",
        "options": {},
        "authentication": "predefinedCredentialType"
      },
      "typeVersion": 1
    },
    {
      "id": "63c46681-a628-4bd8-aab0-a226ab57e703",
      "name": "天気予報取得",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3216,
        -64
      ],
      "parameters": {
        "url": "https://api.openweathermap.org/data/2.5/forecast",
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "15404c5f-d821-4455-abf6-a08db26baee3",
      "name": "現地アクティビティ検索 - Viator",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3216,
        128
      ],
      "parameters": {
        "url": "https://viator-api.p.rapidapi.com/search",
        "options": {},
        "authentication": "predefinedCredentialType"
      },
      "typeVersion": 1
    },
    {
      "id": "27b5a21e-02a6-43e2-b1b9-a7bd5074dd29",
      "name": "全データソース統合",
      "type": "n8n-nodes-base.code",
      "position": [
        3440,
        -256
      ],
      "parameters": {
        "jsCode": "const skyscannerFlights = $input.item(0).json;\nconst kiwiFlights = $input.item(1).json;\nconst bookingHotels = $input.item(2).json;\nconst weather = $input.item(3).json;\nconst activities = $input.item(4).json;\nconst requestData = $('Extract Request Data').first().json;\n\nconst allFlightData = {\n  skyscanner: skyscannerFlights,\n  kiwi: kiwiFlights,\n  weather: weather,\n  activities: activities\n};\n\nconst hotelData = bookingHotels;\n\nreturn [{\n  json: {\n    flights: allFlightData,\n    hotels: hotelData,\n    request: requestData\n  }\n}];"
      },
      "typeVersion": 1
    },
    {
      "id": "d460fa9d-9991-4f29-befc-caff18a9cf38",
      "name": "拡張行程ビルダー",
      "type": "n8n-nodes-base.code",
      "position": [
        3616,
        -256
      ],
      "parameters": {
        "jsCode": "const mergedData = $input.first().json;\nconst requestData = mergedData.request;\n\n// Parse Skyscanner flights\nconst skyscannerFlights = [];\nif (mergedData.flights.skyscanner.itineraries?.results) {\n  mergedData.flights.skyscanner.itineraries.results.slice(0, 8).forEach((flight, i) => {\n    skyscannerFlights.push({\n      id: `sky_${i}`,\n      source: 'Skyscanner',\n      airline: flight.legs[0]?.carriers?.marketing[0]?.name || 'Unknown',\n      departureTime: flight.legs[0]?.departure || '',\n      arrivalTime: flight.legs[0]?.arrival || '',\n      duration: flight.legs[0]?.duration || 0,\n      stops: flight.legs[0]?.stopCount || 0,\n      price: flight.pricing?.price?.amount || 0,\n      currency: flight.pricing?.price?.currency || 'USD',\n      bookingLink: flight.deeplink || '#'\n    });\n  });\n}\n\n// Parse Kiwi flights\nconst kiwiFlights = [];\nif (mergedData.flights.kiwi.data) {\n  mergedData.flights.kiwi.data.slice(0, 8).forEach((flight, i) => {\n    kiwiFlights.push({\n      id: `kiwi_${i}`,\n      source: 'Kiwi.com',\n      airline: flight.airlines?.[0] || 'Unknown',\n      departureTime: new Date(flight.local_departure).toISOString(),\n      arrivalTime: new Date(flight.local_arrival).toISOString(),\n      duration: flight.duration?.total || 0,\n      stops: (flight.route?.length || 1) - 1,\n      price: flight.price || 0,\n      currency: flight.currency || 'USD',\n      bookingLink: flight.deep_link || '#'\n    });\n  });\n}\n\nconst allFlights = [...skyscannerFlights, ...kiwiFlights].sort((a,b) => a.price - b.price);\n\n// Parse hotels\nconst hotels = [];\nif (mergedData.hotels.result) {\n  mergedData.hotels.result.slice(0, 10).forEach((hotel, i) => {\n    hotels.push({\n      id: `hotel_${i}`,\n      name: hotel.hotel_name || 'Unknown',\n      address: hotel.address || '',\n      rating: hotel.review_score || 0,\n      reviewCount: hotel.review_nr || 0,\n      price: hotel.min_total_price || 0,\n      pricePerNight: hotel.price_breakdown?.gross_price || 0,\n      currency: hotel.currency_code || 'USD',\n      amenities: hotel.unit_configuration_label || '',\n      distanceToCenter: hotel.distance || '',\n      bookingLink: hotel.url || '#'\n    });\n  });\n}\n\n// Weather summary\nlet weatherSummary = 'No weather data';\nif (mergedData.flights.weather?.list) {\n  const temps = mergedData.flights.weather.list.slice(0, 8).map(w => w.main.temp);\n  const avgTemp = (temps.reduce((a,b)=>a+b,0)/temps.length).toFixed(1);\n  const conditions = mergedData.flights.weather.list[0]?.weather[0]?.description || 'N/A';\n  weatherSummary = `${avgTemp}°C, ${conditions}`;\n}\n\n// Activities\nconst activities = [];\nif (mergedData.flights.activities?.data) {\n  mergedData.flights.activities.data.slice(0, 10).forEach((act, i) => {\n    activities.push({\n      id: `act_${i}`,\n      name: act.title || 'Activity',\n      description: act.description || '',\n      rating: act.rating || 0,\n      reviewCount: act.reviewCount || 0,\n      price: act.price?.amount || 0,\n      currency: act.price?.currency || 'USD',\n      duration: act.duration || '',\n      bookingLink: act.productUrl || '#'\n    });\n  });\n}\n\n// Create enhanced itineraries (top 8)\nconst itineraries = [];\nfor (let f = 0; f < Math.min(4, allFlights.length); f++) {\n  for (let h = 0; h < Math.min(2, hotels.length); h++) {\n    if (itineraries.length >= 8) break;\n    \n    const flight = allFlights[f];\n    const hotel = hotels[h];\n    const selectedActivities = activities.slice(0, 3);\n    \n    const totalPrice = flight.price + hotel.price + selectedActivities.reduce((sum, act) => sum + act.price, 0);\n    \n    itineraries.push({\n      id: `itinerary_${itineraries.length + 1}`,\n      flight: flight,\n      hotel: hotel,\n      activities: selectedActivities,\n      weather: weatherSummary,\n      totalPrice: totalPrice,\n      currency: flight.currency,\n      destination: requestData.destination,\n      departureCity: requestData.departureCity,\n      checkInDate: requestData.checkInDate,\n      checkOutDate: requestData.checkOutDate,\n      travelers: requestData.travelers\n    });\n  }\n  if (itineraries.length >= 8) break;\n}\n\nitineraries.sort((a, b) => a.totalPrice - b.totalPrice);\n\nreturn itineraries.slice(0, 8).map(it => ({ json: it }));"
      },
      "typeVersion": 1
    },
    {
      "id": "33047f44-9726-4f2f-a029-c7e71c8aceb8",
      "name": "AIスコア&レコメンデーション",
      "type": "n8n-nodes-base.code",
      "position": [
        4128,
        64
      ],
      "parameters": {
        "jsCode": "const itineraries = $input.all().map(item => item.json);\nconst aiAnalysis = $('AI Agent - Itinerary Optimizer').first().json;\n\nlet aiScores = [];\ntry {\n  aiScores = typeof aiAnalysis.output === 'string' ? JSON.parse(aiAnalysis.output) : aiAnalysis.output;\n} catch (e) {\n  aiScores = itineraries.map((_, i) => ({\n    score: 85 - i * 5,\n    reasoning: 'AI analysis pending',\n    highlights: ['Competitive pricing', 'Good availability'],\n    warnings: []\n  }));\n}\n\nconst enrichedItineraries = itineraries.map((itinerary, index) => {\n  const aiData = aiScores[index] || { score: 75, reasoning: 'Standard option', highlights: [], warnings: [] };\n  return {\n    ...itinerary,\n    aiScore: aiData.score || 75,\n    aiReasoning: aiData.reasoning || 'Good option',\n    aiHighlights: aiData.highlights || [],\n    aiWarnings: aiData.warnings || []\n  };\n});\n\nenrichedItineraries.sort((a, b) => b.aiScore - a.aiScore);\n\nreturn enrichedItineraries.map(it => ({ json: it }));"
      },
      "typeVersion": 1
    },
    {
      "id": "015b10c8-ad0a-4f34-8855-e1366cde7658",
      "name": "プレミアムHTMLメール生成",
      "type": "n8n-nodes-base.code",
      "position": [
        4288,
        64
      ],
      "parameters": {
        "jsCode": "const itineraries = $input.all().map(item => item.json);\n\nif (itineraries.length === 0) {\n  return [{ json: { html: '<html><body><h2>No Results</h2></body></html>' } }];\n}\n\nconst first = itineraries[0];\nlet html = `\n<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"UTF-8\">\n<style>\n  body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; color: #1a1a1a; max-width: 1000px; margin: 0 auto; padding: 20px; background: #f8f9fa; }\n  .container { background: white; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 12px rgba(0,0,0,0.1); }\n  .header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 40px 30px; text-align: center; }\n  .header h1 { margin: 0; font-size: 32px; font-weight: 700; }\n  .summary { background: #f0f4ff; padding: 25px 30px; border-left: 5px solid #667eea; margin: 20px; }\n  .summary strong { color: #667eea; }\n  .itinerary { border: 2px solid #e0e0e0; margin: 20px; border-radius: 10px; overflow: hidden; transition: transform 0.2s; }\n  .itinerary:hover { transform: translateY(-3px); box-shadow: 0 6px 20px rgba(0,0,0,0.15); }\n  .best-deal { border-color: #ffd700; background: linear-gradient(to right, #fff9e6, #ffffff); }\n  .best-deal .itinerary-header { background: linear-gradient(135deg, #ffd700, #ffed4e); }\n  .itinerary-header { background: #667eea; color: white; padding: 20px; display: flex; justify-content: space-between; align-items: center; }\n  .ai-score { background: white; color: #667eea; padding: 8px 20px; border-radius: 20px; font-weight: bold; font-size: 18px; }\n  .section { padding: 25px; }\n  .section-title { color: #667eea; font-size: 20px; font-weight: 600; margin-bottom: 15px; border-bottom: 2px solid #e0e0e0; padding-bottom: 8px; }\n  .detail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin: 15px 0; }\n  .detail-item { padding: 12px; background: #f8f9fa; border-radius: 6px; }\n  .detail-label { color: #666; font-size: 13px; margin-bottom: 5px; }\n  .detail-value { color: #1a1a1a; font-weight: 600; font-size: 15px; }\n  .activities-list { list-style: none; padding: 0; }\n  .activities-list li { padding: 12px; margin: 8px 0; background: #f0f4ff; border-left: 4px solid #667eea; border-radius: 4px; }\n  .ai-insights { background: linear-gradient(135deg, #e0f7fa, #f0f4ff); padding: 20px; border-radius: 8px; margin: 15px 0; }\n  .highlight { color: #2e7d32; }\n  .warning { color: #d32f2f; }\n  .price-box { text-align: center; background: linear-gradient(135deg, #667eea, #764ba2); color: white; padding: 25px; border-radius: 8px; margin: 20px 0; }\n  .price { font-size: 42px; font-weight: 700; }\n  .button { display: inline-block; padding: 14px 28px; background: linear-gradient(135deg, #667eea, #764ba2); color: white; text-decoration: none; border-radius: 25px; margin: 8px; font-weight: 600; transition: all 0.3s; }\n  .button:hover { transform: scale(1.05); box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4); }\n  .comparison-table { width: 100%; border-collapse: collapse; margin: 20px 0; }\n  .comparison-table th { background: #667eea; color: white; padding: 15px; text-align: left; font-weight: 600; }\n  .comparison-table td { padding: 12px; border-bottom: 1px solid #e0e0e0; }\n  .comparison-table tr:hover { background: #f8f9fa; }\n  .badge { display: inline-block; padding: 5px 12px; background: #ffd700; color: #1a1a1a; border-radius: 15px; font-size: 12px; font-weight: 700; margin-left: 10px; }\n  .weather-box { background: linear-gradient(135deg, #4facfe, #00f2fe); color: white; padding: 15px; border-radius: 8px; text-align: center; margin: 15px 0; }\n</style>\n</head>\n<body>\n<div class=\"container\">\n  <div class=\"header\">\n    <h1>✈️ Your AI-Optimized Travel Itineraries</h1>\n    <p style=\"margin: 10px 0 0 0; font-size: 18px; opacity: 0.9;\">Powered by Advanced AI Analysis</p>\n  </div>\n\n  <div class=\"summary\">\n    <strong>📍 Route:</strong> ${first.departureCity} → ${first.destination}<br>\n    <strong>📅 Dates:</strong> ${first.checkInDate} to ${first.checkOutDate}<br>\n    <strong>👥 Travelers:</strong> ${first.travelers}<br>\n    <strong>🌤️ Weather:</strong> ${first.weather}\n  </div>\n\n  <div style=\"padding: 30px;\">\n    <h2 style=\"color: #667eea; font-size: 28px; margin-bottom: 20px;\">🏆 Top ${itineraries.length} AI-Ranked Itineraries</h2>\n`;\n\nitineraries.forEach((it, index) => {\n  const isBest = index === 0;\n  html += `\n    <div class=\"itinerary ${isBest ? 'best-deal' : ''}\">\n      <div class=\"itinerary-header\" ${isBest ? 'style=\"background: linear-gradient(135deg, #ffd700, #ffed4e); color: #1a1a1a;\"' : ''}>\n        <div>\n          <h3 style=\"margin: 0; font-size: 24px;\">Option ${index + 1} ${isBest ? '<span class=\"badge\">BEST VALUE</span>' : ''}</h3>\n          <p style=\"margin: 5px 0 0 0; opacity: 0.8;\">AI-Optimized Package</p>\n        </div>\n        <div class=\"ai-score\">🤖 ${it.aiScore}/100</div>\n      </div>\n\n      <div class=\"section\">\n        <div class=\"ai-insights\">\n          <strong style=\"color: #667eea; font-size: 16px;\">🧠 AI Analysis:</strong>\n          <p style=\"margin: 10px 0;\">${it.aiReasoning}</p>\n          ${it.aiHighlights.length > 0 ? `<div class=\"highlight\">✓ ${it.aiHighlights.join(' • ')}</div>` : ''}\n          ${it.aiWarnings.length > 0 ? `<div class=\"warning\">⚠ ${it.aiWarnings.join(' • ')}</div>` : ''}\n        </div>\n\n        <div class=\"section-title\">✈️ Flight Details</div>\n        <div class=\"detail-grid\">\n          <div class=\"detail-item\"><div class=\"detail-label\">Source</div><div class=\"detail-value\">${it.flight.source}</div></div>\n          <div class=\"detail-item\"><div class=\"detail-label\">Airline</div><div class=\"detail-value\">${it.flight.airline}</div></div>\n          <div class=\"detail-item\"><div class=\"detail-label\">Departure</div><div class=\"detail-value\">${it.flight.departureTime}</div></div>\n          <div class=\"detail-item\"><div class=\"detail-label\">Arrival</div><div class=\"detail-value\">${it.flight.arrivalTime}</div></div>\n          <div class=\"detail-item\"><div class=\"detail-label\">Stops</div><div class=\"detail-value\">${it.flight.stops === 0 ? 'Non-stop ⭐' : it.flight.stops + ' stop(s)'}</div></div>\n          <div class=\"detail-item\"><div class=\"detail-label\">Flight Price</div><div class=\"detail-value\">${it.currency} $${it.flight.price.toFixed(2)}</div></div>\n        </div>\n\n        <div class=\"section-title\">🏨 Hotel Details</div>\n        <div class=\"detail-grid\">\n          <div class=\"detail-item\"><div class=\"detail-label\">Hotel Name</div><div class=\"detail-value\">${it.hotel.name}</div></div>\n          <div class=\"detail-item\"><div class=\"detail-label\">Rating</div><div class=\"detail-value\">⭐ ${it.hotel.rating}/10 (${it.hotel.reviewCount} reviews)</div></div>\n          <div class=\"detail-item\"><div class=\"detail-label\">Location</div><div class=\"detail-value\">${it.hotel.distanceToCenter || 'City Center'}</div></div>\n          <div class=\"detail-item\"><div class=\"detail-label\">Hotel Price</div><div class=\"detail-value\">${it.currency} $${it.hotel.price.toFixed(2)}</div></div>\n        </div>\n\n        ${it.activities.length > 0 ? `\n        <div class=\"section-title\">🎯 Recommended Activities</div>\n        <ul class=\"activities-list\">\n          ${it.activities.map(act => `\n            <li>\n              <strong>${act.name}</strong> ${act.rating > 0 ? `(${act.rating}⭐)` : ''}<br>\n              <span style=\"color: #666; font-size: 13px;\">${act.description.substring(0, 100)}...</span><br>\n              <strong style=\"color: #667eea;\">${act.currency} $${act.price.toFixed(2)}</strong> • ${act.duration}\n            </li>\n          `).join('')}\n        </ul>\n        ` : ''}\n\n        <div class=\"weather-box\">\n          <strong>🌤️ Weather Forecast:</strong> ${it.weather}\n        </div>\n\n        <div class=\"price-box\">\n          <div style=\"font-size: 16px; margin-bottom: 5px;\">TOTAL PACKAGE PRICE</div>\n          <div class=\"price\">${it.currency} $${it.totalPrice.toFixed(2)}</div>\n          <div style=\"margin-top: 20px;\">\n            <a href=\"${it.flight.bookingLink}\" class=\"button\">Book Flight</a>\n            <a href=\"${it.hotel.bookingLink}\" class=\"button\">Book Hotel</a>\n          </div>\n        </div>\n      </div>\n    </div>\n  `;\n});\n\nhtml += `\n    <div style=\"margin-top: 40px;\">\n      <h2 style=\"color: #667eea; font-size: 24px;\">📊 Price Comparison Matrix</h2>\n      <table class=\"comparison-table\">\n        <tr>\n          <th>Option</th>\n          <th>AI Score</th>\n          <th>Source</th>\n          <th>Flight</th>\n          <th>Hotel</th>\n          <th>Activities</th>\n          <th>Total</th>\n        </tr>\n`;\n\nitineraries.forEach((it, i) => {\n  const actPrice = it.activities.reduce((sum, a) => sum + a.price, 0);\n  html += `\n    <tr ${i === 0 ? 'style=\"background: #fff9e6; font-weight: bold;\"' : ''}>\n      <td>Option ${i + 1} ${i === 0 ? '<span class=\"badge\">BEST</span>' : ''}</td>\n      <td>${it.aiScore}/100</td>\n      <td>${it.flight.source}</td>\n      <td>$${it.flight.price.toFixed(2)}</td>\n      <td>$${it.hotel.price.toFixed(2)}</td>\n      <td>$${actPrice.toFixed(2)}</td>\n      <td style=\"color: #667eea; font-size: 18px; font-weight: bold;\">$${it.totalPrice.toFixed(2)}</td>\n    </tr>\n  `;\n});\n\nhtml += `\n      </table>\n    </div>\n\n    <div style=\"margin-top: 40px; padding: 25px; background: linear-gradient(135deg, #f0f4ff, #e0f7fa); border-radius: 10px;\">\n      <h3 style=\"color: #667eea; margin-top: 0;\">💡 AI Travel Tips</h3>\n      <ul style=\"line-height: 1.8;\">\n        <li>Book flights and hotels separately for maximum flexibility</li>\n        <li>Weather data is forecast-based; pack accordingly</li>\n        <li>Activities can be booked on-site or in advance</li>\n        <li>Prices are dynamic and subject to change</li>\n        <li>Consider travel insurance for peace of mind</li>\n      </ul>\n    </div>\n  </div>\n</div>\n\n<div style=\"text-align: center; margin-top: 30px; padding: 20px; color: #666; font-size: 13px;\">\n  <p>🤖 This itinerary was generated by AI-powered n8n workflow automation<br>\n  Prices and availability are subject to change • Book early for best rates</p>\n</div>\n</body>\n</html>\n`;\n\nreturn [{ json: { html, itineraries } }];"
      },
      "typeVersion": 1
    },
    {
      "id": "0551345f-ca44-4adc-8f49-5ddf522f8a74",
      "name": "Slack 通知送信",
      "type": "n8n-nodes-base.slack",
      "position": [
        4656,
        256
      ],
      "parameters": {
        "text": "🎉 New travel itinerary sent!\\n✈️ Route: {{ $('Extract Request Data').item.json.departureCity }} → {{ $('Extract Request Data').item.json.destination }}\\n📧 Email: {{ $('Webhook - Travel Request').item.json.body.email }}\\n🏆 Best Deal: ${{ $('AI Score & Recommendations').first().json.totalPrice.toFixed(2) }}\\n🤖 AI Score: {{ $('AI Score & Recommendations').first().json.aiScore }}/100",
        "channel": "#travel-bookings",
        "attachments": [],
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "typeVersion": 1
    },
    {
      "id": "82bb48ce-ad62-43cc-bb7e-69cf5f55e4c0",
      "name": "OpenRouter Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        3792,
        -320
      ],
      "parameters": {
        "model": "qwen/qwen3-vl-8b-thinking",
        "options": {}
      },
      "credentials": {
        "openRouterApi": {
          "id": "fKnn6LL7cRFqNHDX",
          "name": "OpenRouter account2"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "e66d7eb9-383f-4ec3-9e76-c33dcfabffdf",
      "name": "付箋",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2288,
        -608
      ],
      "parameters": {
        "width": 512,
        "height": 768,
        "content": "## Introduction\nAutomates travel planning by aggregating flights, hotels, activities, and weather via APIs, then uses AI to generate professional itineraries delivered through Gmail and Slack.\n\n## How It Works\nWebhook receives requests, searches APIs (Skyscanner, Booking.com, Kiwi, Viator, weather), merges data, AI builds itineraries, scores options, generates HTML emails, delivers via Gmail/Slack.\n\n## Workflow Template\nWebhook → Extract → Parallel Searches (Flights/Hotels/Activities/Weather) → Merge → Build Itinerary → AI Processing → Score → Generate HTML → Gmail → Slack → Response\n\n## Workflow Steps\n1. Trigger & Extract: Receives destination, dates, preferences, extracts parameters.\n2. Data Gathering: Parallel APIs fetch flights, hotels, activities, weather, merges responses.\n3. AI Processing: Analyzes data, creates itinerary, ranks recommendations.\n4. Delivery: Generates HTML email, sends via Gmail/Slack, confirms completion.\n\n## Setup Instructions\n1. API Configuration: Add keys for Skyscanner, Booking.com, Kiwi, Viator, OpenWeatherMap, OpenRouter.\n2. Communication: Connect Gmail OAuth2, Slack webhook.\n3. Customization: Adjust endpoints, AI prompts, HTML template, scoring criteria.\n\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "70771bd2-ff0c-46a1-86ae-08d5698b5c56",
      "name": "付箋1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4144,
        -544
      ],
      "parameters": {
        "color": 5,
        "width": 352,
        "height": 576,
        "content": "## Prerequisites\n- API keys: Skyscanner, Booking.com, Kiwi, Viator, OpenWeatherMap, OpenRouter\n- Gmail account\n- Slack workspace\n- n8n instance\n## Use Cases\n- Corporate travel planning\n- Vacation itinerary generation\n- Group trip coordination\n\n## Customization\n- Add sources (Airbnb, TripAdvisor)\n- Filter by budget preferences\n- Add PDF generation\n- Customize Slack format\n\n## Benefits\n- Saves 3-5 hours per trip\n- Real-time pricing aggregation\n- AI-powered personalization\n- Automated multi-channel delivery"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "973f16a5-77bb-4015-a5c5-a32efacdddd7",
  "connections": {
    "Generate HTML Email": {
      "main": [
        [
          {
            "node": "133388c8-6fe6-42cb-94b7-f76bf0cb4b2e",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "65e7017e-5242-4875-bf24-661fc68eb771": {
      "main": [
        [
          {
            "node": "51891077-df8a-45db-b575-0585f52aa1db",
            "type": "main",
            "index": 0
          },
          {
            "node": "953e3d0b-528d-41ba-b7ba-4fc7363c703f",
            "type": "main",
            "index": 0
          },
          {
            "node": "759f481f-598a-4bff-a3d1-3cb3310ecb88",
            "type": "main",
            "index": 0
          },
          {
            "node": "63c46681-a628-4bd8-aab0-a226ab57e703",
            "type": "main",
            "index": 0
          },
          {
            "node": "15404c5f-d821-4455-abf6-a08db26baee3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "63c46681-a628-4bd8-aab0-a226ab57e703": {
      "main": [
        [
          {
            "node": "27b5a21e-02a6-43e2-b1b9-a7bd5074dd29",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "133388c8-6fe6-42cb-94b7-f76bf0cb4b2e": {
      "main": [
        [
          {
            "node": "5fcf6271-c0fd-430b-858e-4a462e98b648",
            "type": "main",
            "index": 0
          },
          {
            "node": "0551345f-ca44-4adc-8f49-5ddf522f8a74",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "82bb48ce-ad62-43cc-bb7e-69cf5f55e4c0": {
      "ai_languageModel": [
        [
          {
            "node": "2aedf5b8-536b-44bc-9b6d-3b80daa2c6c0",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "27b5a21e-02a6-43e2-b1b9-a7bd5074dd29": {
      "main": [
        [
          {
            "node": "d460fa9d-9991-4f29-befc-caff18a9cf38",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "0551345f-ca44-4adc-8f49-5ddf522f8a74": {
      "main": [
        [
          {
            "node": "5fcf6271-c0fd-430b-858e-4a462e98b648",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "f3a23f51-4039-4a08-bad5-d9e1f475e8af": {
      "main": [
        [
          {
            "node": "65e7017e-5242-4875-bf24-661fc68eb771",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "33047f44-9726-4f2f-a029-c7e71c8aceb8": {
      "main": [
        [
          {
            "node": "015b10c8-ad0a-4f34-8855-e1366cde7658",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine & Rank Itineraries": {
      "main": [
        [
          {
            "node": "Generate HTML Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "d460fa9d-9991-4f29-befc-caff18a9cf38": {
      "main": [
        [
          {
            "node": "2aedf5b8-536b-44bc-9b6d-3b80daa2c6c0",
            "type": "main",
            "index": 0
          },
          {
            "node": "33047f44-9726-4f2f-a029-c7e71c8aceb8",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "015b10c8-ad0a-4f34-8855-e1366cde7658": {
      "main": [
        [
          {
            "node": "133388c8-6fe6-42cb-94b7-f76bf0cb4b2e",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "51891077-df8a-45db-b575-0585f52aa1db": {
      "main": [
        [
          {
            "node": "Combine & Rank Itineraries",
            "type": "main",
            "index": 0
          },
          {
            "node": "27b5a21e-02a6-43e2-b1b9-a7bd5074dd29",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "953e3d0b-528d-41ba-b7ba-4fc7363c703f": {
      "main": [
        [
          {
            "node": "Combine & Rank Itineraries",
            "type": "main",
            "index": 0
          },
          {
            "node": "27b5a21e-02a6-43e2-b1b9-a7bd5074dd29",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "2aedf5b8-536b-44bc-9b6d-3b80daa2c6c0": {
      "main": [
        [
          {
            "node": "33047f44-9726-4f2f-a029-c7e71c8aceb8",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "15404c5f-d821-4455-abf6-a08db26baee3": {
      "main": [
        [
          {
            "node": "27b5a21e-02a6-43e2-b1b9-a7bd5074dd29",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "759f481f-598a-4bff-a3d1-3cb3310ecb88": {
      "main": [
        [
          {
            "node": "27b5a21e-02a6-43e2-b1b9-a7bd5074dd29",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
よくある質問

このワークフローの使い方は?

上記のJSON設定コードをコピーし、n8nインスタンスで新しいワークフローを作成して「JSONからインポート」を選択、設定を貼り付けて認証情報を必要に応じて変更してください。

このワークフローはどんな場面に適していますか?

上級 - 個人の生産性, マルチモーダルAI

有料ですか?

このワークフローは完全無料です。ただし、ワークフローで使用するサードパーティサービス(OpenAI APIなど)は別途料金が発生する場合があります。

ワークフロー情報
難易度
上級
ノード数18
カテゴリー2
ノードタイプ10
難易度説明

上級者向け、16ノード以上の複雑なワークフロー

作成者
Cheng Siong Chin

Cheng Siong Chin

@cschin

Prof. Cheng Siong CHIN serves as Chair Professor in Intelligent Systems Modelling and Simulation in Newcastle University, Singapore. His academic credentials include an M.Sc. in Advanced Control and Systems Engineering from The University of Manchester and a Ph.D. in Robotics from Nanyang Technological University.

外部リンク
n8n.ioで表示

このワークフローを共有

カテゴリー

カテゴリー: 34