AI-Powered Stock Document Q&A with OpenAI and Qdrant
Build a RAG (Retrieval-Augmented Generation) system to answer questions about financial documents. Ingest PDFs from Google Drive, create embeddings with OpenAI, store in Qdrant, and query via a webhook API.
Overview
Automates the creation of an AI-powered Q&A system that ingests stock reports from Google Drive and answers user queries about them through a simple API endpoint using OpenAI and Qdrant.
🧠 Description & Use Case
This workflow builds a complete Retrieval-Augmented Generation (RAG) system for querying documents. It consists of two main parts:
- Data Ingestion: Processing a source document (a PDF from Google Drive), converting it into searchable vector embeddings, and storing it in a Qdrant vector database.
- Question Answering: Exposing a webhook API that allows users to ask natural language questions and receive AI-generated answers based on the ingested document's content.
🔄 How It Works:
Part 1: Data Ingestion (Manual Trigger)
This part of the workflow is executed manually to load a document into the knowledge base.
- Trigger: The workflow begins when you manually click 'Execute Workflow'.
- Fetch Document: The
Google Drivenode downloads a specified PDF file (e.g., a company's financial report). - Process & Chunk: The binary data from the PDF is passed to the
Binary to Documentloader. This loader utilizes theRecursive Character Text Splitterto break the document's text into smaller, overlapping chunks, which is optimal for AI processing. - Embed & Store: The
Qdrant Vector Storenode orchestrates the final step. It takes the text chunks and uses theEmbeddings OpenAInode to convert each chunk into a numerical vector (an embedding). These embeddings are then stored (upserted) into a specified collection in your Qdrant database, making the document's knowledge semantically searchable.
Part 2: Question & Answering (Webhook API)
This part is always active, listening for user questions.
- API Endpoint: The
Webhooknode acts as an API endpoint, waiting for a POST request. The request should contain the user's question (input) and the name of the company/collection to query (company). - Retrieve Relevant Context: When a question is received, the
Retrieval QA Chainis triggered. It uses aVector Store Retrieverto search the specified Qdrant collection for the text chunks that are most semantically similar to the user's question. - Generate Answer: The retrieved chunks (the context) and the original question are sent to the
OpenAI Chat Model(e.g., GPT-4). The large language model analyzes the context and formulates a coherent, human-like answer to the question. - Send Response: The
Respond to Webhooknode sends the final, AI-generated answer back to the user who made the API request.
✅ Real-World Use Cases:
- Financial Analysis: Quickly ask questions about specific figures, statements, or risk factors mentioned in lengthy 10-K filings or earnings reports.
- Internal Knowledge Base: Create a Q&A bot for your company's internal documentation, HR policies, or technical manuals.
- Customer Support Automation: Build a chatbot that can answer customer questions by referencing product user guides or FAQs.
- Research Assistant: Ingest complex research papers or legal documents and query them for specific information, summaries, or cross-references without reading the entire text.
Instructions
Basic steps
- Download workflow JSON
- Import into n8n
- Configure node parameters as needed
- Test and enable the workflow
💡 Tips
After importing, verify all connectors and credentials match your environment.
Preview
Workflow Visualization
Nodes
Embeddings OpenAI1
@n8n/langchain.embeddingsOpenAi
On new manual Chat Message
@n8n/langchain.manualChatTrigger
Sticky Note1
stickyNote
Retrieval QA Chain
@n8n/langchain.chainRetrievalQa
Respond to Webhook
respondToWebhook
Vector Store Retriever
@n8n/langchain.retrieverVectorStore
Webhook1
webhook
When clicking "Execute Workflow"
manualTrigger
Google Drive
googleDrive
Binary to Document
@n8n/langchain.documentBinaryInputLoader
Recursive Character Text Splitter
@n8n/langchain.textSplitterRecursiveCharacterTextSplitter
Embeddings OpenAI
@n8n/langchain.embeddingsOpenAi
Sticky Note
stickyNote
Qdrant Vector Store
@n8n/langchain.vectorStoreQdrant
Qdrant Vector Store1
@n8n/langchain.vectorStoreQdrant
OpenAI Chat Model
@n8n/langchain.lmChatOpenAi
Sticky Note2
stickyNote
Statistics
View full JSON structure
{
"nodes": [
{
"id": "ec3b86be-4113-4fd5-8365-02adb67693e9",
"name": "Embeddings OpenAI1",
"type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
"position": [
1960,
720
],
"parameters": {
"options": {}
},
"credentials": {
"openAiApi": {
"id": "fOF5kro9BJ6KMQ7n",
"name": "OpenAi account"
}
},
"typeVersion": 1
},
{
"id": "42fd8020-3861-4d0f-a7a2-70e2c35f0bed",
"name": "On new manual Chat Message",
"type": "@n8n/n8n-nodes-langchain.manualChatTrigger",
"disabled": true,
"position": [
1620,
240
],
"parameters": {},
"typeVersion": 1
},
{
"id": "a9b48d04-691e-4537-90f8-d7a4aa6153af",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
1560,
120
],
"parameters": {
"color": 6,
"width": 903.0896125323785,
"height": 733.5099670584011,
"content": "## Step 2: Setup the Q&A \n### The incoming message from the webhook is queried from the Supabase Vector Store. The response is provided in the response webhook. "
},
"typeVersion": 1
},
{
"id": "472b4800-745a-4337-9545-163247f7e9ae",
"name": "Retrieval QA Chain",
"type": "@n8n/n8n-nodes-langchain.chainRetrievalQa",
"position": [
1880,
240
],
"parameters": {
"query": "={{ $json.body.input }}"
},
"typeVersion": 1
},
{
"id": "e58bd82d-abc6-44ed-8e93-ec5436126d66",
"name": "Respond to Webhook",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
2280,
240
],
"parameters": {
"options": {},
"respondWith": "text",
"responseBody": "={{ $json.response.text }}"
},
"typeVersion": 1
},
{
"id": "04bbf01e-8269-47c7-897d-4ea94a1bd1c0",
"name": "Vector Store Retriever",
"type": "@n8n/n8n-nodes-langchain.retrieverVectorStore",
"position": [
2020,
440
],
"parameters": {
"topK": 5
},
"typeVersion": 1
},
{
"id": "feee6d68-2e0d-4d40-897e-c1d833a13bf2",
"name": "Webhook1",
"type": "n8n-nodes-base.webhook",
"position": [
1620,
420
],
"webhookId": "679f4afb-189e-4f04-9dc0-439eec2ec5f1",
"parameters": {
"path": "19f5499a-3083-4783-93a0-e8ed76a9f742",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 1.1
},
{
"id": "1b8d251f-7069-4d7d-b6d6-4bfa683d4ad1",
"name": "When clicking \"Execute Workflow\"",
"type": "n8n-nodes-base.manualTrigger",
"position": [
280,
260
],
"parameters": {},
"typeVersion": 1
},
{
"id": "b746a7a4-ed94-4332-bf7b-65aadcf54130",
"name": "Google Drive",
"type": "n8n-nodes-base.googleDrive",
"position": [
580,
260
],
"parameters": {
"fileId": {
"__rl": true,
"mode": "list",
"value": "1LZezppYrWpMStr4qJXtoIX-Dwzvgehll",
"cachedResultUrl": "https://drive.google.com/file/d/1LZezppYrWpMStr4qJXtoIX-Dwzvgehll/view?usp=drivesdk",
"cachedResultName": "crowdstrike.pdf"
},
"options": {},
"operation": "download"
},
"credentials": {
"googleDriveOAuth2Api": {
"id": "1tsDIpjUaKbXy0be",
"name": "Google Drive account"
}
},
"typeVersion": 3
},
{
"id": "83a7d470-f934-436d-ba3f-1ae7c776f5a5",
"name": "Binary to Document",
"type": "@n8n/n8n-nodes-langchain.documentBinaryInputLoader",
"position": [
860,
480
],
"parameters": {
"loader": "pdfLoader",
"options": {}
},
"typeVersion": 1
},
{
"id": "b52b4a90-99a1-49cc-a6f0-7551d6754496",
"name": "Recursive Character Text Splitter",
"type": "@n8n/n8n-nodes-langchain.textSplitterRecursiveCharacterTextSplitter",
"position": [
860,
640
],
"parameters": {
"options": {},
"chunkSize": 3000,
"chunkOverlap": 200
},
"typeVersion": 1
},
{
"id": "b525e130-2029-4f55-a603-1fdc05a19c17",
"name": "Embeddings OpenAI",
"type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
"position": [
1160,
480
],
"parameters": {
"options": {}
},
"credentials": {
"openAiApi": {
"id": "fOF5kro9BJ6KMQ7n",
"name": "OpenAi account"
}
},
"typeVersion": 1
},
{
"id": "5358c53f-55f9-431d-8956-c6bae7ad25bc",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
540,
120
],
"parameters": {
"color": 6,
"width": 772.0680602743597,
"height": 732.3675002130781,
"content": "## Step 1: Upserting the PDF\n### Fetch file from Google Drive, split it into chunks and insert into Supabase index\n\n"
},
"typeVersion": 1
},
{
"id": "fb91e2da-0eeb-47a5-aa49-65bf56986857",
"name": "Qdrant Vector Store",
"type": "@n8n/n8n-nodes-langchain.vectorStoreQdrant",
"position": [
940,
260
],
"parameters": {
"mode": "insert",
"options": {},
"qdrantCollection": {
"__rl": true,
"mode": "id",
"value": "=crowd"
}
},
"credentials": {
"qdrantApi": {
"id": "U5CpjAgFeXziP3I1",
"name": "QdrantApi account"
}
},
"typeVersion": 1
},
{
"id": "89e14837-d1fc-4b1e-9ebc-7cf3e7fd9a70",
"name": "Qdrant Vector Store1",
"type": "@n8n/n8n-nodes-langchain.vectorStoreQdrant",
"position": [
1980,
600
],
"parameters": {
"qdrantCollection": {
"__rl": true,
"mode": "id",
"value": "={{ $json.body.company }}"
}
},
"credentials": {
"qdrantApi": {
"id": "U5CpjAgFeXziP3I1",
"name": "QdrantApi account"
}
},
"typeVersion": 1
},
{
"id": "c619245b-5ea0-4354-974d-21ec6b8efa93",
"name": "OpenAI Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
1880,
460
],
"parameters": {
"options": {}
},
"credentials": {
"openAiApi": {
"id": "fOF5kro9BJ6KMQ7n",
"name": "OpenAi account"
}
},
"typeVersion": 1
},
{
"id": "e4aa780d-8069-4308-a61f-82ed876af71a",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-560,
120
],
"parameters": {
"color": 6,
"width": 710.9124489067698,
"height": 726.4452519516944,
"content": "## Start here: Step-by Step Youtube Tutorial :star:\n\n[](https://www.youtube.com/watch?v=pMvizUx5n1g)\n"
},
"typeVersion": 1
}
],
"connections": {
"Webhook1": {
"main": [
[
{
"node": "Retrieval QA Chain",
"type": "main",
"index": 0
}
]
]
},
"Google Drive": {
"main": [
[
{
"node": "Qdrant Vector Store",
"type": "main",
"index": 0
}
]
]
},
"Embeddings OpenAI": {
"ai_embedding": [
[
{
"node": "Qdrant Vector Store",
"type": "ai_embedding",
"index": 0
}
]
]
},
"OpenAI Chat Model": {
"ai_languageModel": [
[
{
"node": "Retrieval QA Chain",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Binary to Document": {
"ai_document": [
[
{
"node": "Qdrant Vector Store",
"type": "ai_document",
"index": 0
}
]
]
},
"Embeddings OpenAI1": {
"ai_embedding": [
[
{
"node": "Qdrant Vector Store1",
"type": "ai_embedding",
"index": 0
}
]
]
},
"Retrieval QA Chain": {
"main": [
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
},
"Qdrant Vector Store1": {
"ai_vectorStore": [
[
{
"node": "Vector Store Retriever",
"type": "ai_vectorStore",
"index": 0
}
]
]
},
"Vector Store Retriever": {
"ai_retriever": [
[
{
"node": "Retrieval QA Chain",
"type": "ai_retriever",
"index": 0
}
]
]
},
"On new manual Chat Message": {
"main": [
[
{
"node": "Retrieval QA Chain",
"type": "main",
"index": 0
}
]
]
},
"When clicking \"Execute Workflow\"": {
"main": [
[
{
"node": "Google Drive",
"type": "main",
"index": 0
}
]
]
},
"Recursive Character Text Splitter": {
"ai_textSplitter": [
[
{
"node": "Binary to Document",
"type": "ai_textSplitter",
"index": 0
}
]
]
}
}
}Actions
Technical Specs
Compatibility
License
This workflow template follows MIT license, you can freely use, modify and distribute.
Please comply with relevant third-party service terms when using.