This workflow follows the Agent → OpenAI Embeddings 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 →
{
"name": "RAG Search Agent",
"nodes": [
{
"parameters": {},
"id": "execute-trigger",
"name": "Execute Workflow Trigger",
"type": "n8n-nodes-base.executeWorkflowTrigger",
"typeVersion": 1,
"position": [
200,
300
]
},
{
"parameters": {
"mode": "runOnceForAllItems",
"jsCode": "// Input validation for RAG query\nconst input = $input.first().json;\n\n// Validate required fields\nif (!input.query || typeof input.query !== 'string' || input.query.trim().length === 0) {\n return {\n json: {\n success: false,\n error: 'Invalid input: query is required and must be a non-empty string',\n agentType: 'rag_search',\n errorCode: 'INVALID_INPUT'\n }\n };\n}\n\n// Sanitize and prepare query for semantic search\nconst sanitizedQuery = input.query.trim()\n .replace(/<[^>]*>/g, '') // Remove HTML\n .slice(0, 2000); // Limit length\n\n// Determine memory type hints from query\nlet memoryTypeHint = 'semantic'; // default\nif (/past|previous|last time|before|history/i.test(sanitizedQuery)) {\n memoryTypeHint = 'episodic';\n} else if (/how to|process|steps|procedure|workflow/i.test(sanitizedQuery)) {\n memoryTypeHint = 'procedural';\n}\n\nreturn {\n json: {\n query: sanitizedQuery,\n sessionId: input.sessionId || $execution.id,\n memoryTypeHint,\n startTime: Date.now(),\n metadata: {\n originalLength: input.query.length,\n memoryTypeHint,\n executionId: $execution.id\n }\n }\n};"
},
"id": "validate-input",
"name": "Validate Input",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
350,
300
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "check-valid",
"leftValue": "={{ !$json.error }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "check-input-valid",
"name": "Input Valid?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
500,
300
]
},
{
"parameters": {
"options": {
"systemMessage": "You are a RAG (Retrieval Augmented Generation) Search Agent.\n\nYour role is to search the internal knowledge base and retrieve relevant information.\n\nMEMORY TYPES:\n1. **Semantic Memory** - Facts, domain knowledge, documentation\n - Use for: \"What is...\", \"Explain...\", \"Define...\"\n \n2. **Episodic Memory** - Past sessions, conversations, decisions\n - Use for: \"What did we discuss...\", \"Previously...\", \"Last time...\"\n \n3. **Procedural Memory** - Patterns, workflows, best practices\n - Use for: \"How to...\", \"Process for...\", \"Steps to...\"\n\nSEARCH STRATEGY:\n1. Analyze query to determine memory type(s) needed\n2. Formulate semantic search query\n3. Search knowledge base\n4. Rank results by relevance\n5. Synthesize answer from retrieved context\n\nOUTPUT FORMAT:\n```\n## Answer\n[Direct answer using retrieved information]\n\n## Source Documents\n1. [Document title/ID] - Relevance: X%\n Key excerpt: \"...\"\n\n2. [Document title/ID] - Relevance: X%\n Key excerpt: \"...\"\n\n## Confidence\n[High/Medium/Low] - [Explanation]\n\n## Related Topics\n- [Suggested related queries]\n```\n\nGUIDELINES:\n- Always cite source documents\n- If no relevant results found, say so clearly\n- Note when information may be outdated\n- Suggest follow-up queries if partial match"
}
},
"id": "rag-agent",
"name": "RAG Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 1.7,
"position": [
650,
200
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"modelId": {
"__rl": true,
"value": "gpt-4o-mini",
"mode": "list"
},
"options": {
"temperature": 0.2,
"maxTokens": 2000,
"timeout": 60000
}
},
"id": "rag-llm",
"name": "RAG LLM",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1,
"position": [
650,
450
],
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"name": "search_knowledge_base",
"description": "Search the semantic knowledge base using vector similarity. Input is a natural language query. Returns relevant documents with similarity scores. Best for finding related information, past decisions, and domain knowledge.",
"topK": 5
},
"id": "vector-search-tool",
"name": "Vector Store Tool",
"type": "@n8n/n8n-nodes-langchain.toolVectorStore",
"typeVersion": 1,
"position": [
750,
450
]
},
{
"parameters": {
"qdrantCollection": "={{ $env.QDRANT_COLLECTION || 'agent_long_term_memory' }}",
"qdrantUrl": "={{ $env.QDRANT_URL || 'http://localhost:6333' }}",
"options": {}
},
"id": "qdrant-store",
"name": "Qdrant Vector Store",
"type": "@n8n/n8n-nodes-langchain.vectorStoreQdrant",
"typeVersion": 1,
"position": [
750,
600
],
"credentials": {
"qdrantApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"model": "text-embedding-3-small",
"options": {
"dimensions": 1536
}
},
"id": "embeddings",
"name": "OpenAI Embeddings",
"type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
"typeVersion": 1,
"position": [
900,
600
],
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"mode": "runOnceForAllItems",
"jsCode": "// Format RAG output with retrieved documents\nconst input = $('Validate Input').first().json;\nconst result = $input.first().json;\n\nconst executionTime = Date.now() - (input.startTime || Date.now());\n\n// Extract retrieved documents and scores\nlet retrievedDocuments = [];\nlet relevanceScores = [];\n\ntry {\n if (result.intermediateSteps) {\n for (const step of result.intermediateSteps) {\n if (step.observation) {\n // Parse vector search results\n const obs = typeof step.observation === 'string'\n ? step.observation\n : step.observation;\n \n // Try to extract document info\n if (typeof obs === 'object' && obs.documents) {\n for (const doc of obs.documents) {\n retrievedDocuments.push({\n id: doc.id || doc.metadata?.id,\n content: doc.pageContent || doc.content,\n metadata: doc.metadata || {},\n score: doc.score\n });\n if (doc.score) {\n relevanceScores.push(doc.score);\n }\n }\n } else if (Array.isArray(obs)) {\n // Array of results\n for (const item of obs) {\n if (item.pageContent || item.content) {\n retrievedDocuments.push({\n id: item.id || item.metadata?.id,\n content: item.pageContent || item.content,\n metadata: item.metadata || {},\n score: item.score\n });\n if (item.score) {\n relevanceScores.push(item.score);\n }\n }\n }\n }\n }\n }\n }\n} catch (e) {\n // Non-critical parsing error\n}\n\n// Calculate average relevance\nconst avgRelevance = relevanceScores.length > 0\n ? relevanceScores.reduce((a, b) => a + b, 0) / relevanceScores.length\n : null;\n\nreturn {\n json: {\n success: true,\n agentType: 'rag_search',\n output: result.output || 'Search completed',\n retrievedDocuments,\n relevanceScores,\n averageRelevance: avgRelevance,\n memoryType: input.memoryTypeHint,\n documentCount: retrievedDocuments.length,\n metadata: {\n executionTime,\n toolCalls: (result.intermediateSteps || []).length,\n queryLength: input.query.length,\n executionId: input.metadata?.executionId\n }\n }\n};"
},
"id": "format-output",
"name": "Format Output",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
850,
200
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"mode": "runOnceForAllItems",
"jsCode": "// Handle validation error\nconst input = $input.first().json;\n\nreturn {\n json: {\n success: false,\n agentType: 'rag_search',\n error: input.error || 'Validation failed',\n errorCode: input.errorCode || 'VALIDATION_ERROR',\n output: null,\n retrievedDocuments: [],\n metadata: {\n executionId: $execution.id\n }\n }\n};"
},
"id": "format-error",
"name": "Format Error",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
650,
400
]
},
{
"parameters": {
"mode": "runOnceForAllItems",
"jsCode": "return $input.all();"
},
"id": "merge-output",
"name": "Merge Output",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1000,
300
]
}
],
"connections": {
"Execute Workflow Trigger": {
"main": [
[
{
"node": "Validate Input",
"type": "main",
"index": 0
}
]
]
},
"Validate Input": {
"main": [
[
{
"node": "Input Valid?",
"type": "main",
"index": 0
}
]
]
},
"Input Valid?": {
"main": [
[
{
"node": "RAG Agent",
"type": "main",
"index": 0
}
],
[
{
"node": "Format Error",
"type": "main",
"index": 0
}
]
]
},
"RAG Agent": {
"main": [
[
{
"node": "Format Output",
"type": "main",
"index": 0
}
]
]
},
"RAG LLM": {
"ai_languageModel": [
[
{
"node": "RAG Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Vector Store Tool": {
"ai_tool": [
[
{
"node": "RAG Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Qdrant Vector Store": {
"ai_vectorStore": [
[
{
"node": "Vector Store Tool",
"type": "ai_vectorStore",
"index": 0
}
]
]
},
"OpenAI Embeddings": {
"ai_embedding": [
[
{
"node": "Qdrant Vector Store",
"type": "ai_embedding",
"index": 0
}
]
]
},
"Format Output": {
"main": [
[
{
"node": "Merge Output",
"type": "main",
"index": 0
}
]
]
},
"Format Error": {
"main": [
[
{
"node": "Merge Output",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"saveManualExecutions": true,
"callerPolicy": "workflowsFromSameOwner"
},
"staticData": null,
"tags": [
"agent",
"rag",
"vector",
"production"
]
}
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.
openAiApiqdrantApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
RAG Search Agent. Uses executeWorkflowTrigger, agent, lmChatOpenAi, toolVectorStore. Event-driven trigger; 11 nodes.
Source: https://github.com/NarsistPanda/Claude1-n8n-avatar/blob/232d49109540ee19b25950df82fafc5f2a646f63/n8n-workflows/agents/rag-agent.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Code Extractfromfile. Uses manualTrigger, sort, httpRequest, compression. Event-driven trigger; 50 nodes.
2464. Uses httpRequest, compression, editImage, documentDefaultDataLoader. Event-driven trigger; 50 nodes.
Workflow 2464. Uses httpRequest, compression, editImage, documentDefaultDataLoader. Event-driven trigger; 50 nodes.
Are you a popular tech startup accelerator (named after a particular higher order function) overwhelmed with 1000s of pitch decks on a daily basis? Wish you could filter through them quickly using AI
Alfred (funcional). Uses gmailTool, googleCalendarTool, gmail, embeddingsOpenAi. Event-driven trigger; 83 nodes.