8
n8n 한국어amn8n.com

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": "웹훅 - 여행 요청",
      "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": "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": "웹훅 응답",
      "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 채팅 모델",
      "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