{
  "nodes": [
    {
      "parameters": {
        "jsCode": "// Analyze question and determine optimal top_k\nconst question = $json.body.chatInput || '';\nconst lowerQ = question.toLowerCase();\n\nlet top_k = 10;\nif (lowerQ.includes('szczeg\u00f3\u0142') || lowerQ.includes('jak') || lowerQ.includes('por\u00f3wn')) {\n  top_k = 15;\n} else if (lowerQ.includes('przyk\u0142ad') || lowerQ.includes('lista')) {\n  top_k = 12;\n}\n\nreturn {\n  json: {\n    query: question,\n    top_k: top_k\n  }\n};"
      },
      "id": "e9357206-6f9d-49b3-a157-a296fb990115",
      "name": "Code: Analyze Question",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        608,
        688
      ]
    },
    {
      "parameters": {
        "jsCode": "// Get embedding response from Fireworks\n  const responseInfo = $input.item.json;                                        \n  const params = $('Code: Analyze Question').item.json;                         \n\n  // --- 1. EXTRACT VECTOR FROM FIREWORKS RESPONSE ---\n  let vector;\n  if (responseInfo.data && responseInfo.data[0] &&\n  responseInfo.data[0].embedding) {\n    vector = responseInfo.data[0].embedding;\n  } else if (Array.isArray(responseInfo)) {\n    vector = responseInfo;\n  } else {\n    throw new Error('Brak wektora z API Fireworks. Response: ' +\n  JSON.stringify(responseInfo).substring(0, 200));\n  }\n\n  // --- 2. BUILD QDRANT QUERY (NAMED VECTORS) ---\n  const qdrantQuery = {\n    vector: {\n      name: \"dense\",\n      vector: vector\n    },\n    limit: params.top_k || 10,\n    with_payload: true,\n    score_threshold: 0.3\n  };\n\n  // --- 3. ADD SLUG FILTER IF articleSlug PROVIDED ---\n  const articleSlug = $('Webhook: RAG Question').item.json.body.articleSlug;\n  if (articleSlug) {\n    qdrantQuery.filter = {\n      must: [{ key: \"slug\", match: { value: articleSlug } }]\n    };\n  }\n\n  return {\n    json: {\n      qdrantQuery,\n      originalQuestion: params.query,\n      top_k: params.top_k,\n      articleSlug: articleSlug || null\n    }\n  };\n"
      },
      "id": "f6ddef9c-be78-4f5b-912e-fe4903595ba1",
      "name": "Code: Prepare Qdrant Query",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1184,
        688
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://79a7ee05-96b9-4ab0-8670-25d5b081a97d.europe-west3-0.gcp.cloud.qdrant.io/collections/wordpress_articles/points/search",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "httpBearerAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json.qdrantQuery }}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          },
          "timeout": 60000
        }
      },
      "id": "dfe0d749-ecdf-4fe6-9775-64e6a6514951",
      "name": "HTTP: Qdrant Search",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        1472,
        688
      ],
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Format Qdrant results for LLM context\nconst response = $input.item.json;\nconst originalQuestion = $('Code: Prepare Qdrant Query').item.json.originalQuestion;\n\n// Check for results\nif (!response || !response.result || response.result.length === 0) {\n  return {\n    json: {\n      context: 'Brak wynik\u00f3w. System RAG nie znalaz\u0142 odpowiednich artyku\u0142\u00f3w.',\n      hasResults: false,\n      resultCount: 0,\n      results: [],\n      originalQuestion\n    }\n  };\n}\n\nconst results = response.result;\n\n// Build formatted context\nlet context = `Znaleziono ${results.length} wynik\u00f3w z bazy wiedzy:\\n\\n`;\nconst formattedResults = [];\n\nresults.forEach((hit, index) => {\n  const payload = hit.payload || {};\n\n  context += `--- Wynik ${index + 1} (Score: ${(hit.score * 100).toFixed(1)}%) ---\\n`;\n  context += `Tytu\u0142: ${payload.title || 'Brak tytu\u0142u'}\\n`;\n  context += `URL: ${payload.url || ''}\\n`;\n  context += `Typ sekcji: ${payload.section_type || 'content'}\\n`;\n\n  if (payload.publication_date) {\n    context += `Data publikacji: ${payload.publication_date}\\n`;\n  }\n\n  if (payload.tags && payload.tags.length > 0) {\n    context += `Tagi: ${payload.tags.join(', ')}\\n`;\n  }\n\n  if (payload.categories && payload.categories.length > 0) {\n    context += `Kategorie: ${payload.categories.join(', ')}\\n`;\n  }\n\n  context += `\\nTre\u015b\u0107:\\n${payload.text || ''}\\n\\n`;\n\n  formattedResults.push({\n    title: payload.title || 'Brak tytu\u0142u',\n    url: payload.url || '',\n    score: hit.score,\n    text: payload.text || '',\n    section_type: payload.section_type || 'content'\n  });\n});\n\nreturn {\n  json: {\n    context,\n    hasResults: true,\n    resultCount: results.length,\n    results: formattedResults,\n    originalQuestion\n  }\n};"
      },
      "id": "ed996b1b-06db-4b83-bc0c-3abf77d14c3b",
      "name": "Code: Format Qdrant Results",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1760,
        688
      ]
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "={{ $json.chatInput }}",
        "options": {
          "systemMessage": "=Jeste\u015b ekspertem i asystentem na blogu o UX. Twoim zadaniem jest udzielenie wyczerpuj\u0105cej odpowiedzi na pytanie u\u017cytkownika.\n\nTwoja odpowied\u017a ma struktur\u0119 odwr\u00f3conej piramidy: najpierw og\u00f3lna, merytoryczna odpowied\u017a (korzystaj\u0105ca z Twojej ca\u0142ej wiedzy), a nast\u0119pnie wskazanie konkretnych \u017ar\u00f3de\u0142 dost\u0119pnych w dostarczonym kontek\u015bcie.\n\n=== HISTORIA ROZMOWY ===\n{{ $json.chat_history }}\n\n=== KONTEKST ARTYKU\u0141\u00d3W ===\n{{ $json.context }}\n\n=== ONE-SHOT LEARNING (WZ\u00d3R ODPOWIEDZI) ===\nPytanie: \"Co lepsze: Sketch czy Figma?\"\nKontekst: [Artyku\u0142: \"Migracja do Figmy\", Tre\u015b\u0107: \"Przeszli\u015bmy na Figm\u0119 ze wzgl\u0119du na tryb multiplayer.\"]\n\n(ODPOWIED\u0179):\nWyb\u00f3r zale\u017cy od \u015brodowiska pracy. Sketch to natywna aplikacja na macOS, \u015bwietna dla indywidualnych projektant\u00f3w ceni\u0105cych wydajno\u015b\u0107 lokaln\u0105. Figma dominuje jednak w zespo\u0142ach, poniewa\u017c dzia\u0142a w chmurze, co umo\u017cliwia prac\u0119 wielu os\u00f3b na jednym pliku jednocze\u015bnie i jest niezale\u017cna od systemu operacyjnego. Je\u015bli pracujesz w zespole, Figma zazwyczaj wygrywa \u0142atwo\u015bci\u0105 kolaboracji.\n\n### Warto przeczyta\u0107\n* Szczeg\u00f3\u0142owy opis procesu przej\u015bcia na narz\u0119dzia chmurowe i korzy\u015bci z trybu multiplayer znajdziesz w tek\u015bcie: [Migracja do Figmy](Link).\n\n=== TWOJE ZADANIA ===\nTw\u00f3rz odpowied\u017a zawsze w dw\u00f3ch krokach:\n\nKROK 1: Merytoryczna Odpowied\u017a (Bez nag\u0142\u00f3wka)\n1. Odpowiedz na pytanie u\u017cytkownika, wykorzystuj\u0105c swoj\u0105 szerok\u0105 wiedz\u0119 og\u00f3ln\u0105 ORAZ informacje z kontekstu.\n2. Nie ograniczaj si\u0119 tylko do artyku\u0142\u00f3w \u2013 je\u015bli u\u017cytkownik pyta o narz\u0119dzie, kt\u00f3rego nie ma w kontek\u015bcie (np. Cursor), opisz je rzetelnie bazuj\u0105c na swojej wiedzy.\n3. W tej sekcji nie u\u017cywaj link\u00f3w, skup si\u0119 na wyja\u015bnieniu problemu.\n\nKROK 2: Sekcja \u0179r\u00f3d\u0142owa (Nag\u0142\u00f3wek: \"### Warto przeczyta\u0107\")\n1. To miejsce WY\u0141\u0104CZNIE na tre\u015bci z sekcji `=== KONTEKST ARTYKU\u0141\u00d3W ===`.\n2. Wypunktuj konkretne artyku\u0142y, kt\u00f3re rozszerzaj\u0105 temat.\n3. Nie lej wody. Napisz konkretnie, co wnosi ten tekst, np.: \"Omawiamy tam techniki [X] i [Y]: [Tytu\u0142](Link)\".\n4. Je\u015bli w dostarczonym kontek\u015bcie nie ma nic pasuj\u0105cego do pytania \u2013 **ca\u0142kowicie pomi\u0144 t\u0119 sekcj\u0119** (nie pisz \"brak wynik\u00f3w\", po prostu zako\u0144cz wypowied\u017a na Kroku 1).\n\n=== ZAKAZY ===\n- Nie pisz wst\u0119p\u00f3w typu \"Jako model AI...\", \"Bazuj\u0105c na bazie wiedzy...\" ani \"W naszych artyku\u0142ach...\".\n- Nie u\u017cywaj sformu\u0142owa\u0144 sugeruj\u0105cych w\u0142asno\u015b\u0107 (\"nasze\", \"moje\"). Pisz bezosobowo lub bezpo\u015brednio.\n- Nie u\u017cywaj emoji.\n\nOdpowiedz teraz w j\u0119zyku polskim:"
        }
      },
      "id": "fb304eda-18f6-4d82-bddb-dcfb1f76db0d",
      "name": "AI Agent: Generate Answer",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 3.1,
      "position": [
        2336,
        688
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.fireworks.ai/inference/v1/embeddings",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "httpBearerAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ { \"model\": \"nomic-ai/nomic-embed-text-v1.5\", \"input\": \"search_query: \" + $json.query } }}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          },
          "timeout": 30000
        }
      },
      "id": "f8cdf396-ee07-4b06-8002-6d5677ea5842",
      "name": "HTTP: Fireworks Nomic Embed1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        896,
        688
      ],
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Format Chat History from webhook payload\nconst context = $json.context;\nconst chatInput = $('Webhook: RAG Question').item.json.body.chatInput;\nconst chatHistoryArray = $('Webhook: RAG Question').item.json.body.chatHistory || [];\n\n// Format history as text\nlet chatHistoryText = '';\nif (chatHistoryArray && chatHistoryArray.length > 0) {\n  chatHistoryArray.forEach(msg => {\n    const role = msg.role === 'user' ? 'U\u017cytkownik' : 'Asystent';\n    chatHistoryText += `${role}: ${msg.content}\\n\\n`;\n  });\n} else {\n  chatHistoryText = 'Brak poprzedniej rozmowy.';\n}\n\nreturn {\n  json: {\n    context,\n    chatInput,\n    chat_history: chatHistoryText\n  }\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2048,
        688
      ],
      "id": "5e92ca7c-6016-4e34-9fbf-9c82c18e3f8e",
      "name": "Code: Format Chat History1"
    },
    {
      "parameters": {
        "model": "anthropic/claude-haiku-4.5",
        "options": {
          "temperature": 0.5
        }
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "typeVersion": 1,
      "position": [
        2288,
        960
      ],
      "id": "b30e576a-3858-4f5e-af68-96627ab9a4c4",
      "name": "OpenRouter Chat Model2",
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "sessionIdType": "customKey",
        "sessionKey": "={{ $('Webhook: RAG Question').item.json.body.sessionId || 'default' }}"
      },
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "typeVersion": 1.3,
      "position": [
        2432,
        976
      ],
      "id": "3454a5e6-7e95-49c6-964a-0d5a0d0951eb",
      "name": "Simple Memory1"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "rag-agent",
        "responseMode": "lastNode",
        "options": {}
      },
      "id": "7e7472c5-fa8e-480d-81d0-5ffc61129b1f",
      "name": "Webhook: RAG Question",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        128,
        640
      ]
    }
  ],
  "connections": {
    "Code: Analyze Question": {
      "main": [
        [
          {
            "node": "HTTP: Fireworks Nomic Embed1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: Prepare Qdrant Query": {
      "main": [
        [
          {
            "node": "HTTP: Qdrant Search",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP: Qdrant Search": {
      "main": [
        [
          {
            "node": "Code: Format Qdrant Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: Format Qdrant Results": {
      "main": [
        [
          {
            "node": "Code: Format Chat History1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP: Fireworks Nomic Embed1": {
      "main": [
        [
          {
            "node": "Code: Prepare Qdrant Query",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: Format Chat History1": {
      "main": [
        [
          {
            "node": "AI Agent: Generate Answer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model2": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent: Generate Answer",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory1": {
      "ai_memory": [
        [
          {
            "node": "AI Agent: Generate Answer",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Webhook: RAG Question": {
      "main": [
        [
          {
            "node": "Code: Analyze Question",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}