Wróć do szablonów
SZABLON WORKFLOW
Multi Source Scraping | Operasyon için n8n Otomasyon İş Akışı Şablonu (HTTP, Google Sheets)
n8n için hazır otomasyon şablonu: Multi Source Scraping. 15 düğüm. Entegrasyonlar: HTTP, Google Sheets. JSON'u kopyalayıp n8n'e içe aktarın.
15 węzłyMulti_Source_Scraping-workflow.json
{"id": "SjiPE2lVXmmyh7Ca","meta": {"instanceId": "7578372eeadee5ee5414cdf5a06141eebec63e21f295454563ddce7b1217363f","templateCredsSetupCompleted": true},"name": "multi-source scraping","tags": [],"nodes": [{"id": "bf0013da-2066-45de-8833-fa2927728f00","name": "When chat message received","type": "@n8n/n8n-nodes-langchain.chatTrigger","position": [-288,0],"webhookId": "18e5e55a-53be-4afb-adc5-bdc4dabfa067","parameters": {"options": {}},"typeVersion": 1.3},{"id": "9fe3e0e0-f5ac-4018-90b7-f2e4fc7b207d","name": "Simple Memory","type": "@n8n/n8n-nodes-langchain.memoryBufferWindow","position": [224,144],"parameters": {"contextWindowLength": 30},"typeVersion": 1.3},{"id": "c9e95768-ea7c-43f0-b6fd-a824fc64d60d","name": "Google Gemini Chat Model","type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini","position": [80,144],"parameters": {"options": {}},"credentials": {"googlePalmApi": {"id": "89mVPG21GxPFECbN","name": "Google Gemini(PaLM) Api account"}},"typeVersion": 1},{"id": "cd7be0c8-f708-42f8-8411-a3622c25a0bf","name": "Firecrawl list","type": "n8n-nodes-mcp.mcpClientTool","position": [672,352],"parameters": {},"credentials": {"mcpClientApi": {"id": "aFCnpjjzHv0KPnei","name": "MCP: Firecrawl"}},"typeVersion": 1},{"id": "4cdda777-345c-4129-9ada-1a9c01b9a104","name": "Firecrawl execute","type": "n8n-nodes-mcp.mcpClientTool","position": [672,464],"parameters": {"toolName": "={{ \n$fromAI(\"tool\", \"the selected tool to use\")\n}}","operation": "executeTool","toolParameters": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Tool_Parameters', ``, 'json') }}"},"credentials": {"mcpClientApi": {"id": "aFCnpjjzHv0KPnei","name": "MCP: Firecrawl"}},"typeVersion": 1},{"id": "35a550c1-8fb7-40c3-af16-bb059c6d1947","name": "Brave list","type": "n8n-nodes-mcp.mcpClientTool","position": [768,352],"parameters": {},"credentials": {"mcpClientApi": {"id": "wzdpQy5uTbYZQiko","name": "MCP: Brave"}},"typeVersion": 1},{"id": "ecd7944c-0de2-4447-b640-7a7caaa30705","name": "Brave execute","type": "n8n-nodes-mcp.mcpClientTool","position": [768,464],"parameters": {"toolName": "={{\n$fromAI(\"tool\", \"the selected tool to use\")\n}}","operation": "executeTool","toolParameters": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Tool_Parameters', ``, 'json') }}"},"credentials": {"mcpClientApi": {"id": "wzdpQy5uTbYZQiko","name": "MCP: Brave"}},"typeVersion": 1},{"id": "3041cd71-2131-4cf5-b21b-7b7decc0a4bb","name": "Apify list","type": "n8n-nodes-mcp.mcpClientTool","position": [864,464],"parameters": {},"credentials": {"mcpClientApi": {"id": "0HMXUEhEUFLuWqlS","name": "MCP : Apify"}},"typeVersion": 1},{"id": "1ba3c633-998b-4ab2-83c1-c6dd0e7c6679","name": "Apify execute","type": "n8n-nodes-mcp.mcpClientTool","position": [864,352],"parameters": {"toolName": "={{ $fromAI(\"tool\", \"the selected tool to use\") }}","operation": "executeTool","toolParameters": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Tool_Parameters', ``, 'json') }}"},"credentials": {"mcpClientApi": {"id": "aFCnpjjzHv0KPnei","name": "MCP: Firecrawl"}},"typeVersion": 1},{"id": "73031a7a-6338-4683-a6e3-efee3819f89e","name": "Data Enrichment Request","type": "n8n-nodes-base.httpRequest","position": [1296,0],"parameters": {"url": "=https://sheets.googleapis.com/v4/spreadsheets/{{ $json.spreadsheetId }}/values:batchUpdate","method": "POST","options": {},"jsonBody": "={\n \"valueInputOption\": \"USER_ENTERED\",\n \"data\": {{ JSON.stringify($json.valueRanges) }}\n}","sendBody": true,"specifyBody": "json"},"typeVersion": 4.2},{"id": "9a7f6cbe-a132-4fef-9af8-b9851227faf6","name": "Gemini Research Orchestrator","type": "@n8n/n8n-nodes-langchain.agent","position": [208,0],"parameters": {"options": {"systemMessage": "You are a helpful assistant\n\nUse Brave to search into the web\nUse Firecrawl to scrape websites\nUse Apify to scrape websites\n\nthe process is to use the tool node first then the execute node.\n\nAlways refer to the list of tools before attempting to access or use the execute tools"}},"typeVersion": 2.2},{"id": "acf4a537-627a-4d20-8b02-215f8075900b","name": "Create Research Report","type": "n8n-nodes-base.googleSheets","position": [704,0],"parameters": {"title": "scrapped ","options": {},"resource": "spreadsheet"},"credentials": {"googleSheetsOAuth2Api": {"id": "FglkISb2md6llLsJ","name": "Google Sheets account"}},"typeVersion": 4.7},{"id": "13e00e06-9b7e-4fb2-bbbd-9e7211de9e96","name": "Format Research Data","type": "n8n-nodes-base.code","position": [880,0],"parameters": {"jsCode": "// Version sécurisée qui gère les feuilles existantes\nconst agentOutput = $('Gemini Research Orchestrator').item.json.output;\nconst spreadsheetId = $('Create Research Report').item.json.spreadsheetId;\n\n// Fonction pour parser le markdown (identique)\nfunction parseAgentOutput(output) {\n const lines = output.split('\\n').filter(line => line.trim() !== '');\n const data = {\n title: '',\n categories: [],\n jobs: []\n };\n \n let currentSection = '';\n let inTable = false;\n let tableHeaders = [];\n \n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n \n if (line.startsWith('Voici les données extraites')) {\n data.title = line.replace(/\\*\\*/g, '').replace('Voici les données extraites du site ', '').replace(/[`:`]/g, '');\n }\n \n if (line.startsWith('**') && line.endsWith(':**')) {\n currentSection = line.replace(/\\*\\*/g, '').replace(':', '');\n inTable = false;\n }\n \n if (currentSection === 'Catégories d\\'emploi' && line.startsWith('* ')) {\n data.categories.push(line.replace('* ', ''));\n }\n \n if (line.startsWith('|') && !inTable) {\n const headers = line.split('|').map(h => h.trim()).filter(h => h !== '');\n if (headers.length > 0) {\n tableHeaders = headers;\n inTable = true;\n i++;\n continue;\n }\n }\n \n if (inTable && line.startsWith('|')) {\n const cells = line.split('|').map(c => c.trim()).filter(c => c !== '');\n if (cells.length === tableHeaders.length) {\n const job = {};\n tableHeaders.forEach((header, index) => {\n job[header] = cells[index] || '';\n });\n data.jobs.push(job);\n }\n }\n }\n \n return data;\n}\n\n// Fonction pour créer ou mettre à jour une feuille\nfunction createOrUpdateSheet(sheetName, sheetId, properties = {}) {\n if (sheetId === 0) {\n // Pour la feuille par défaut (ID 0), on la met à jour\n return {\n updateSheetProperties: {\n properties: {\n sheetId: sheetId,\n title: sheetName,\n gridProperties: {\n rowCount: 1000,\n columnCount: 26\n },\n ...properties\n },\n fields: 'title,tabColor,gridProperties'\n }\n };\n } else {\n // Pour les nouvelles feuilles, on les crée\n return {\n addSheet: {\n properties: {\n sheetId: sheetId,\n title: sheetName,\n gridProperties: {\n rowCount: 1000,\n columnCount: 26\n },\n ...properties\n }\n }\n };\n }\n}\n\n// Fonction pour ajouter des données dans une feuille\nfunction addSheetData(sheetName, range, values, majorDimension = 'ROWS') {\n return {\n range: `'${sheetName}'!${range}`,\n majorDimension: majorDimension,\n values: values\n };\n}\n\n// Fonction pour formater les cellules\nfunction formatCells(sheetId, range, format) {\n return {\n repeatCell: {\n range: {\n sheetId: sheetId,\n startRowIndex: range.startRow,\n endRowIndex: range.endRow,\n startColumnIndex: range.startCol,\n endColumnIndex: range.endCol\n },\n cell: {\n userEnteredFormat: format\n },\n fields: 'userEnteredFormat'\n }\n };\n}\n\n// Fonction pour obtenir les styles de formatage\nfunction getTextFormat(style) {\n const formats = {\n 'TITLE': {\n textFormat: {\n bold: true,\n fontSize: 16,\n foregroundColor: { red: 0.1, green: 0.1, blue: 0.1 }\n },\n horizontalAlignment: 'CENTER',\n backgroundColor: { red: 0.85, green: 0.85, blue: 0.95 }\n },\n 'SECTION_HEADER': {\n textFormat: {\n bold: true,\n fontSize: 14,\n foregroundColor: { red: 0.2, green: 0.2, blue: 0.2 }\n },\n backgroundColor: { red: 0.9, green: 0.9, blue: 0.9 }\n },\n 'TABLE_HEADER': {\n textFormat: {\n bold: true,\n fontSize: 11,\n foregroundColor: { red: 1, green: 1, blue: 1 }\n },\n backgroundColor: { red: 0.2, green: 0.4, blue: 0.8 },\n horizontalAlignment: 'CENTER',\n borders: {\n top: { style: 'SOLID', width: 2 },\n bottom: { style: 'SOLID', width: 2 },\n left: { style: 'SOLID', width: 1 },\n right: { style: 'SOLID', width: 1 }\n }\n },\n 'TABLE_CELL': {\n textFormat: {\n fontSize: 10,\n foregroundColor: { red: 0.1, green: 0.1, blue: 0.1 }\n },\n borders: {\n top: { style: 'SOLID', width: 1 },\n bottom: { style: 'SOLID', width: 1 },\n left: { style: 'SOLID', width: 1 },\n right: { style: 'SOLID', width: 1 }\n },\n verticalAlignment: 'TOP'\n },\n 'CATEGORY': {\n textFormat: {\n fontSize: 11,\n foregroundColor: { red: 0.3, green: 0.3, blue: 0.3 }\n },\n backgroundColor: { red: 0.95, green: 0.98, blue: 0.95 }\n }\n };\n return formats[style] || formats['TABLE_CELL'];\n}\n\n// Parsing des données\nlet parsedData;\ntry {\n parsedData = parseAgentOutput(agentOutput);\n} catch (error) {\n throw new Error(`Erreur lors du parsing des données: ${error.message}`);\n}\n\n// Construction des requêtes et données\nconst requests = [];\nconst valueRanges = [];\n\ntry {\n // D'abord, effacer le contenu de la feuille par défaut\n requests.push({\n updateCells: {\n range: {\n sheetId: 0\n },\n fields: '*'\n }\n });\n\n // 1. Mise à jour de la feuille par défaut (ID 0) -> \"Résumé\"\n requests.push(createOrUpdateSheet('Resume', 0, {\n tabColor: { red: 0.2, green: 0.5, blue: 0.8 }\n }));\n\n // Données de la feuille Résumé\n const summaryData = [\n [parsedData.title || 'Données extraites'],\n [''],\n ['Résumé des données:'],\n [`Nombre de catégories: ${parsedData.categories.length}`],\n [`Nombre d'emplois: ${parsedData.jobs.length}`],\n [''],\n ['Catégories d\\'emploi:']\n ];\n\n parsedData.categories.forEach(category => {\n summaryData.push([`• ${category}`]);\n });\n\n valueRanges.push(addSheetData('Resume', 'A1', summaryData));\n\n // Formatage de la feuille Résumé\n requests.push(formatCells(0, { startRow: 0, endRow: 1, startCol: 0, endCol: 3 }, getTextFormat('TITLE')));\n requests.push(formatCells(0, { startRow: 2, endRow: 3, startCol: 0, endCol: 3 }, getTextFormat('SECTION_HEADER')));\n requests.push(formatCells(0, { startRow: 6, endRow: 7, startCol: 0, endCol: 3 }, getTextFormat('SECTION_HEADER')));\n \n const categoryStartRow = 7;\n const categoryEndRow = categoryStartRow + parsedData.categories.length;\n requests.push(formatCells(0, { \n startRow: categoryStartRow, \n endRow: categoryEndRow, \n startCol: 0, \n endCol: 2 \n }, getTextFormat('CATEGORY')));\n\n // 2. Création de la feuille \"Catégories\"\n requests.push(createOrUpdateSheet('Categories', 1, {\n tabColor: { red: 0.8, green: 0.9, blue: 0.7 }\n }));\n\n const categoriesData = [\n ['Catégories d\\'emploi'],\n [''],\n ['Catégorie', 'Numéro']\n ];\n\n parsedData.categories.forEach((category, index) => {\n categoriesData.push([category, index + 1]);\n });\n\n valueRanges.push(addSheetData('Categories', 'A1', categoriesData));\n\n requests.push(formatCells(1, { startRow: 0, endRow: 1, startCol: 0, endCol: 2 }, getTextFormat('TITLE')));\n requests.push(formatCells(1, { startRow: 2, endRow: 3, startCol: 0, endCol: 2 }, getTextFormat('TABLE_HEADER')));\n \n if (parsedData.categories.length > 0) {\n requests.push(formatCells(1, { \n startRow: 3, \n endRow: 3 + parsedData.categories.length, \n startCol: 0, \n endCol: 2 \n }, getTextFormat('TABLE_CELL')));\n }\n\n // 3. Création de la feuille \"Emplois\"\n requests.push(createOrUpdateSheet('Emplois', 2, {\n tabColor: { red: 0.9, green: 0.7, blue: 0.8 }\n }));\n\n if (parsedData.jobs.length > 0) {\n const jobHeaders = Object.keys(parsedData.jobs[0]);\n const jobsData = [\n ['Liste des emplois disponibles'],\n [''],\n jobHeaders\n ];\n\n parsedData.jobs.forEach(job => {\n const jobRow = jobHeaders.map(header => job[header] || '');\n jobsData.push(jobRow);\n });\n\n valueRanges.push(addSheetData('Emplois', 'A1', jobsData));\n\n requests.push(formatCells(2, { startRow: 0, endRow: 1, startCol: 0, endCol: jobHeaders.length }, getTextFormat('TITLE')));\n requests.push(formatCells(2, { startRow: 2, endRow: 3, startCol: 0, endCol: jobHeaders.length }, getTextFormat('TABLE_HEADER')));\n \n if (parsedData.jobs.length > 0) {\n requests.push(formatCells(2, { \n startRow: 3, \n endRow: 3 + parsedData.jobs.length, \n startCol: 0, \n endCol: jobHeaders.length \n }, getTextFormat('TABLE_CELL')));\n }\n }\n\n // 4. Création de la feuille \"Statistiques\"\n requests.push(createOrUpdateSheet('Statistiques', 3, {\n tabColor: { red: 0.95, green: 0.8, blue: 0.6 }\n }));\n\n const companyCount = [...new Set(parsedData.jobs.map(job => job.Entreprise || job.Company || ''))].length;\n const roleTypes = {};\n \n parsedData.jobs.forEach(job => {\n const role = job.Rôle || job.Role || '';\n const roleType = role.split(' ')[0];\n roleTypes[roleType] = (roleTypes[roleType] || 0) + 1;\n });\n\n const statsData = [\n ['Statistiques des données'],\n [''],\n ['Métrique', 'Valeur'],\n ['Nombre total d\\'emplois', parsedData.jobs.length],\n ['Nombre d\\'entreprises uniques', companyCount],\n ['Nombre de catégories', parsedData.categories.length],\n [''],\n ['Types de rôles les plus fréquents:'],\n ['Type de rôle', 'Occurrences']\n ];\n\n const sortedRoleTypes = Object.entries(roleTypes)\n .sort(([,a], [,b]) => b - a)\n .slice(0, 10);\n\n sortedRoleTypes.forEach(([role, count]) => {\n statsData.push([role, count]);\n });\n\n valueRanges.push(addSheetData('Statistiques', 'A1', statsData));\n\n requests.push(formatCells(3, { startRow: 0, endRow: 1, startCol: 0, endCol: 2 }, getTextFormat('TITLE')));\n requests.push(formatCells(3, { startRow: 2, endRow: 3, startCol: 0, endCol: 2 }, getTextFormat('TABLE_HEADER')));\n requests.push(formatCells(3, { startRow: 7, endRow: 8, startCol: 0, endCol: 2 }, getTextFormat('SECTION_HEADER')));\n requests.push(formatCells(3, { startRow: 8, endRow: 9, startCol: 0, endCol: 2 }, getTextFormat('TABLE_HEADER')));\n\n // Auto-redimensionnement des colonnes\n for (let i = 0; i <= 3; i++) {\n requests.push({\n autoResizeDimensions: {\n dimensions: {\n sheetId: i,\n dimension: 'COLUMNS',\n startIndex: 0,\n endIndex: 10\n }\n }\n });\n }\n\n} catch (error) {\n console.error('Erreur lors de la génération du contenu:', error);\n throw new Error(`Erreur lors de la génération du contenu: ${error.message}`);\n}\n\n// Validation finale des données\nif (!requests || requests.length === 0) {\n throw new Error('Aucune requête générée - vérifiez les données d\\'entrée');\n}\n\nif (!valueRanges || valueRanges.length === 0) {\n throw new Error('Aucune donnée générée - vérifiez les données d\\'entrée');\n}\n\n// Retour des données pour l'API Google Sheets\nreturn {\n json: {\n spreadsheetId: spreadsheetId,\n requests: requests,\n valueRanges: valueRanges,\n totalRequests: requests.length,\n totalValueRanges: valueRanges.length,\n sheetsCreated: 4,\n dataProcessed: {\n title: parsedData.title,\n categoriesCount: parsedData.categories.length,\n jobsCount: parsedData.jobs.length,\n companiesCount: [...new Set(parsedData.jobs.map(job => job.Entreprise || job.Company || ''))].length\n }\n }\n};"},"typeVersion": 2},{"id": "eccede4d-c790-4b60-a3cd-5e9f29ac8f0e","name": "Populate Research Report","type": "n8n-nodes-base.httpRequest","position": [1072,0],"parameters": {"url": "=https://sheets.googleapis.com/v4/spreadsheets/{{ $json.spreadsheetId }}:batchUpdate","method": "POST","options": {},"jsonBody": "={\n \"requests\": {{ JSON.stringify($json.requests) }}\n}","sendBody": true,"specifyBody": "json","authentication": "predefinedCredentialType","nodeCredentialType": "googleSheetsOAuth2Api"},"credentials": {"googleDocsOAuth2Api": {"id": "osBJI4KIPRu07ngs","name": "Google Docs account"},"googleSheetsOAuth2Api": {"id": "bkpmM7RgsOJtIBT5","name": "Google Sheets account 2"}},"typeVersion": 4.2},{"id": "c9603254-752b-4f86-8d77-e6231699094b","name": "Finalize Output Data","type": "n8n-nodes-base.set","position": [1488,0],"parameters": {"mode": "raw","options": {},"jsonOutput": "{\n \"error_message\": \"{{ $json.error.message }}\",\n \"error_code\": \"{{ $json.error.code }}\",\n \"spreadsheet_id\": \"{{ $('Code').item.json.spreadsheetId }}\",\n \"timestamp\": \"{{ new Date().toISOString() }}\"\n}"},"typeVersion": 3.4},{"id": "0c07aba4-88c4-4ddd-93e8-26057f6104f8","name": "Sticky Note","type": "n8n-nodes-base.stickyNote","position": [-544,-96],"parameters": {"color": 4,"width": 400,"height": 256,"content": "## Entry point: \n### - The user sends a request in natural language."},"typeVersion": 1},{"id": "4a113616-2e51-4bb7-8995-1c657f12fa9b","name": "Sticky Note1","type": "n8n-nodes-base.stickyNote","position": [-80,-320],"parameters": {"color": 4,"width": 256,"height": 128,"content": "## ⚙️ PREREQUISITES\n- **Google Gemini API Key**\n- **MCP API keys (Firecrawl, Brave, Apify)**\n- **Google Sheets credentials**\n- **Self-hosted n8n instance**"},"typeVersion": 1},{"id": "fadd1418-2831-4856-9913-de61ec11d418","name": "Sticky Note2","type": "n8n-nodes-base.stickyNote","position": [240,-320],"parameters": {"color": 4,"height": 128,"content": "## 📊 DATA FLOW\n1. **Request → AI analysis**\n2. **Tool selection → Collection**\n3. **Aggregation → Structuring** \n4. **Enrichment → Delivery**"},"typeVersion": 1},{"id": "6afc44f5-db99-46a8-96ca-5e788e48bfff","name": "Sticky Note3","type": "n8n-nodes-base.stickyNote","position": [528,-320],"parameters": {"color": 4,"width": 272,"height": 112,"content": "## ⚠️ ERROR HANDLING\n- **MCP timeout: Automatic retry**\n- **API limit: Fallback modes**\n- **Missing data: Validation**"},"typeVersion": 1},{"id": "104c3b3a-ae84-4ef5-99fb-b84cf599975a","name": "Sticky Note4","type": "n8n-nodes-base.stickyNote","position": [-80,-112],"parameters": {"color": 5,"width": 544,"height": 384,"content": "## Orchestration\n### - When chat message received\n### - Gemini Research Orchestrator\n### - Simple Memory"},"typeVersion": 1},{"id": "4bdd58e3-eddd-4fa3-82f4-bc84ec5498fc","name": "Sticky Note5","type": "n8n-nodes-base.stickyNote","position": [240,288],"parameters": {"width": 752,"height": 304,"content": "## Data Collection Tools \n### *The agent automatically selects the* \n### *appropriate tools based on the request.*\n### - Firecrawl (list + execute)\n### - Brave (list + execute)\n### - Apify (list + execute)\n\n\n"},"typeVersion": 1},{"id": "509c0f30-e634-46d5-83d2-b4aafd8090b8","name": "Sticky Note6","type": "n8n-nodes-base.stickyNote","position": [656,-160],"parameters": {"color": 3,"width": 544,"height": 304,"content": "## Report Generation\n### *The collected data is automatically organized and formatted.*\n### - Create Research Report\n### - Format Research Data\n### - Populate Research Report"},"typeVersion": 1},{"id": "7dd445d7-2785-4f0a-a938-e9e9e962e929","name": "Sticky Note7","type": "n8n-nodes-base.stickyNote","position": [1264,-160],"parameters": {"color": 7,"width": 432,"height": 304,"content": "## Post-Processing\n### *Data enrichment and finalization for delivery*\n### - Data Enrichment Request\n### - Finalize Output Data"},"typeVersion": 1}],"active": false,"pinData": {},"settings": {"executionOrder": "v1"},"versionId": "94fda3e6-3671-42e8-9f8a-d9d7688722eb","connections": {"Apify list": {"ai_tool": [[{"node": "Gemini Research Orchestrator","type": "ai_tool","index": 0}]]},"Brave list": {"ai_tool": [[{"node": "Gemini Research Orchestrator","type": "ai_tool","index": 0}]]},"Apify execute": {"ai_tool": [[{"node": "Gemini Research Orchestrator","type": "ai_tool","index": 0}]]},"Brave execute": {"ai_tool": [[{"node": "Gemini Research Orchestrator","type": "ai_tool","index": 0}]]},"Simple Memory": {"ai_memory": [[{"node": "Gemini Research Orchestrator","type": "ai_memory","index": 0}]]},"Firecrawl list": {"ai_tool": [[{"node": "Gemini Research Orchestrator","type": "ai_tool","index": 0}]]},"Firecrawl execute": {"ai_tool": [[{"node": "Gemini Research Orchestrator","type": "ai_tool","index": 0}]]},"Finalize Output Data": {"main": [[]]},"Format Research Data": {"main": [[{"node": "Populate Research Report","type": "main","index": 0}]]},"Create Research Report": {"main": [[{"node": "Format Research Data","type": "main","index": 0}]]},"Data Enrichment Request": {"main": [[{"node": "Finalize Output Data","type": "main","index": 0}]]},"Google Gemini Chat Model": {"ai_languageModel": [[{"node": "Gemini Research Orchestrator","type": "ai_languageModel","index": 0}]]},"Populate Research Report": {"main": [[{"node": "Data Enrichment Request","type": "main","index": 0}]]},"When chat message received": {"main": [[{"node": "Gemini Research Orchestrator","type": "main","index": 0}]]},"Gemini Research Orchestrator": {"main": [[{"node": "Create Research Report","type": "main","index": 0}]]}}}
W edytorze n8n: wklej za pomocą Ctrl+V→Workflow zostanie utworzony