AutomationFlowsAI & RAG › AI RAG Document Q&A Chat

AI RAG Document Q&A Chat

Original n8n title: RAG Query - Document Q&a

RAG Query - Document Q&A. Uses chatTrigger, httpRequest, postgres. Chat trigger; 13 nodes.

Chat trigger trigger★★★★☆ complexityAI-powered13 nodesChat TriggerHTTP RequestPostgres
AI & RAG Trigger: Chat trigger Nodes: 13 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow follows the Chat Trigger → HTTP Request recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "RAG Query - Document Q&A",
  "nodes": [
    {
      "parameters": {
        "content": "## RAG Query Workflow\n\nThis workflow handles document Q&A using vector similarity search.\n\n**Flow:**\n1. Receive question from Chat or Webhook\n2. Generate query embedding\n3. Search similar chunks in pgvector\n4. Build context from retrieved chunks\n5. Send to Ollama LLM with context\n6. Return answer",
        "height": 300,
        "width": 320
      },
      "id": "sticky-note",
      "name": "Instructions",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -200,
        -100
      ]
    },
    {
      "parameters": {
        "options": {
          "responseMode": "responseNode"
        }
      },
      "id": "chat-trigger",
      "name": "Chat Trigger",
      "type": "@n8n/n8n-nodes-langchain.chatTrigger",
      "typeVersion": 1.1,
      "position": [
        0,
        200
      ]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "rag-query",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "webhook-trigger",
      "name": "Webhook Trigger",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        400
      ]
    },
    {
      "parameters": {
        "jsCode": "// Get question from either Chat Trigger or Webhook\nlet question = '';\nlet sessionId = '';\n\ntry {\n  // Try Chat Trigger first\n  const chatData = $('Chat Trigger').first()?.json;\n  if (chatData?.chatInput) {\n    question = chatData.chatInput;\n    sessionId = chatData.sessionId || 'default';\n  }\n} catch (e) {\n  // Fall back to Webhook\n  const webhookData = $('Webhook Trigger').first()?.json;\n  if (webhookData) {\n    question = webhookData.question || webhookData.query || webhookData.message || '';\n    sessionId = webhookData.sessionId || webhookData.session_id || 'webhook';\n  }\n}\n\nif (!question) {\n  throw new Error('No question provided. Send {\"question\": \"your question\"}');\n}\n\nreturn [{\n  json: {\n    question,\n    sessionId\n  }\n}];"
      },
      "id": "extract-question",
      "name": "Extract Question",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        220,
        300
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://192.168.50.49:11434/api/embeddings",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ model: 'mxbai-embed-large:latest', prompt: $json.question }) }}",
        "options": {
          "timeout": 60000
        }
      },
      "id": "query-embedding",
      "name": "Generate Query Embedding",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        440,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const embedding = $input.first().json.embedding;\nconst question = $('Extract Question').first().json.question;\nconst sessionId = $('Extract Question').first().json.sessionId;\n\nif (!embedding || !Array.isArray(embedding)) {\n  throw new Error('Invalid embedding response from Ollama');\n}\n\n// Format embedding as PostgreSQL vector string\nconst vectorString = '[' + embedding.join(',') + ']';\n\nreturn [{\n  json: {\n    question,\n    sessionId,\n    queryEmbedding: vectorString\n  }\n}];"
      },
      "id": "format-embedding",
      "name": "Format Query Embedding",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        660,
        300
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT \n  id,\n  document_name,\n  chunk_index,\n  content,\n  1 - (embedding <=> '{{ $json.queryEmbedding }}') as similarity\nFROM document_chunks\nWHERE embedding IS NOT NULL\nORDER BY embedding <=> '{{ $json.queryEmbedding }}'\nLIMIT 5;",
        "options": {}
      },
      "id": "vector-search",
      "name": "Vector Similarity Search",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        880,
        300
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const chunks = $input.all();\nconst question = $('Extract Question').first().json.question;\nconst sessionId = $('Extract Question').first().json.sessionId;\n\nif (chunks.length === 0) {\n  return [{\n    json: {\n      question,\n      sessionId,\n      context: 'No relevant documents found in the knowledge base.',\n      hasContext: false\n    }\n  }];\n}\n\n// Build context from retrieved chunks\nlet context = '';\nconst sources = [];\n\nfor (const chunk of chunks) {\n  const data = chunk.json;\n  const similarity = (parseFloat(data.similarity) * 100).toFixed(1);\n  context += `\\n\\n--- From: ${data.document_name} (Chunk ${data.chunk_index}, Relevance: ${similarity}%) ---\\n${data.content}`;\n  \n  if (!sources.includes(data.document_name)) {\n    sources.push(data.document_name);\n  }\n}\n\nreturn [{\n  json: {\n    question,\n    sessionId,\n    context: context.trim(),\n    sources,\n    chunkCount: chunks.length,\n    hasContext: true\n  }\n}];"
      },
      "id": "build-context",
      "name": "Build Context",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1100,
        300
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://192.168.50.49:11434/api/generate",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({\n  model: 'gpt-oss:120b',\n  prompt: `You are a helpful assistant that answers questions based on the provided document context. Respond in the same language as the user's question. If the context doesn't contain relevant information, say so.\n\n=== DOCUMENT CONTEXT ===\n${$json.context}\n=== END CONTEXT ===\n\nUser Question: ${$json.question}\n\nProvide a clear, accurate answer based on the context above:`,\n  stream: false\n}) }}",
        "options": {
          "timeout": 300000
        }
      },
      "id": "ask-ollama",
      "name": "Ask Ollama LLM",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1320,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const ollamaResponse = $input.first().json;\nconst contextData = $('Build Context').first().json;\n\nconst answer = ollamaResponse.response || 'Sorry, I could not generate an answer.';\n\n// Build response object\nconst response = {\n  answer,\n  question: contextData.question,\n  sources: contextData.sources || [],\n  chunksUsed: contextData.chunkCount || 0\n};\n\nreturn [{\n  json: response\n}];"
      },
      "id": "format-response",
      "name": "Format Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1540,
        300
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO chat_memory (session_id, role, content)\nVALUES \n  ('{{ $('Extract Question').first().json.sessionId }}', 'user', '{{ $('Extract Question').first().json.question.replace(/'/g, \"''\") }}'),\n  ('{{ $('Extract Question').first().json.sessionId }}', 'assistant', '{{ $json.answer.replace(/'/g, \"''\").substring(0, 10000) }}');",
        "options": {}
      },
      "id": "save-to-memory",
      "name": "Save to Chat Memory",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        1760,
        300
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json) }}"
      },
      "id": "respond-webhook",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        1980,
        400
      ]
    },
    {
      "parameters": {
        "respondWith": "text",
        "responseBody": "={{ $json.answer }}"
      },
      "id": "respond-chat",
      "name": "Respond to Chat",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        1980,
        200
      ]
    }
  ],
  "connections": {
    "Chat Trigger": {
      "main": [
        [
          {
            "node": "Extract Question",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook Trigger": {
      "main": [
        [
          {
            "node": "Extract Question",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Question": {
      "main": [
        [
          {
            "node": "Generate Query Embedding",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Query Embedding": {
      "main": [
        [
          {
            "node": "Format Query Embedding",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Query Embedding": {
      "main": [
        [
          {
            "node": "Vector Similarity Search",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Vector Similarity Search": {
      "main": [
        [
          {
            "node": "Build Context",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Context": {
      "main": [
        [
          {
            "node": "Ask Ollama LLM",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ask Ollama LLM": {
      "main": [
        [
          {
            "node": "Format Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Response": {
      "main": [
        [
          {
            "node": "Save to Chat Memory",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save to Chat Memory": {
      "main": [
        [
          {
            "node": "Respond to Chat",
            "type": "main",
            "index": 0
          },
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": null,
  "tags": [
    {
      "name": "RAG"
    },
    {
      "name": "Q&A"
    }
  ],
  "triggerCount": 2
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

RAG Query - Document Q&A. Uses chatTrigger, httpRequest, postgres. Chat trigger; 13 nodes.

Source: https://github.com/mohsp-99/n8n-ollama/blob/0ba7d1fd1a606f212cfdf346f85fb70f905f5696/n8n-workflows/rag-query.json — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

This workflow allows users to ask portfolio-related questions in a simple format (). It validates the input, fetches client data and holdings from Google Sheets, retrieves live market prices via API,

Chat Trigger, Google Sheets, HTTP Request +2
AI & RAG

This workflow is for: People who want to quickly launch simple landing pages without paying monthly fees to landing page creators. It’s ideal for rapid prototyping, generation of large amounts of land

Google Gemini, OpenAI, Chat Trigger +3
AI & RAG

This n8n template demonstrates how to automatically download an Instagram Reel, analyze its content using AI video understanding, and regenerate a similar video using AI video generation models. The w

Chat Trigger, HTTP Request
AI & RAG

Automate the creation of high-performing YouTube Shorts in minutes! Content Creators: Generate engaging short videos effortlessly. Marketing Agencies: Produce client-ready content quickly. Business Ow

Chat Trigger, HTTP Request, OpenAI
AI & RAG

I prepared a comprehensive guide demonstrating how to build a multi-level retrieval AI agent in n8n that smartly narrows down search results first by file descriptions, then retrieves detailed vector

Tool Code, Chat Trigger, OpenAI +2