This workflow corresponds to n8n.io template #10887 — we link there as the canonical source.
This workflow follows the Agent → Chat Trigger 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 →
{
"id": "dVHDSa7Hkd3WYyYb",
"meta": {
"templateId": "3577",
"templateCredsSetupCompleted": true
},
"name": "Semantic Cache with a Redis Vector Store",
"tags": [],
"nodes": [
{
"id": "44708aff-a859-4d15-923f-b33f38000db7",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1744,
-400
],
"parameters": {
"width": 580,
"height": 1216,
"content": "## Try it out!\n\n### This workflow implements an intelligent semantic caching system that reduces LLM costs and improves response times by caching similar queries.\n\n### How It Works\n\n**Chat Trigger \u2192 Vector Search \u2192 Cache Decision \u2192 Response**\n\n1. **Receive Chat Message**: User sends a question through the chat interface\n2. **Check for Similar Prompts**: Searches Redis vector store for semantically similar previous queries using OpenAI embeddings\n3. **Analyze Results**: Evaluates similarity scores to determine if there's a cache hit (similar enough query exists)\n4. **Decision Point**:\n - **Cache Hit** (similar query found): Returns the cached response immediately - fast and cost-effective!\n - **Cache Miss** (no similar query): Routes to LLM Agent for a fresh response\n\n### Cache Miss Flow (New Query)\n5. **LLM Agent**: Generates a new response using GPT-4.1-mini with conversation memory\n6. **Store in Cache**: Saves the query-response pair as an embedded vector in Redis for future use\n7. **Respond to User**: Delivers the LLM-generated response\n\n### Key Components\n- **Redis Vector Store**: Stores embedded query-response pairs for semantic similarity search\n- **Redis Chat Memory**: Maintains conversation context across interactions\n- **Score Threshold**: Configurable similarity threshold (default: 0.3) to balance precision vs. recall\n- **OpenAI Embeddings**: Converts text to vectors for semantic comparison\n\n### Requirements\n* Redis database with initialized index\n* OpenAI [API key](https://platform.openai.com/settings/organization/api-keys) for LLM generation \n_(alternatively) Replace the node with another provider_\n* Hugging Face [token](https://huggingface.co/settings/tokens) for generating embeddings \n_(alternatively) Replace the node with another provider_\n* Redis 8.x OSS or Redis Stack\n_(or any Redis deployment with the Redis Query Engine module installed)_"
},
"typeVersion": 1
},
{
"id": "7c408b00-c33a-4953-94c6-007cde96a416",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1024,
672
],
"parameters": {
"color": 5,
"width": 736,
"height": 136,
"content": "### Tuning the Cache\nAdjust the `distanceThreshold` in the `Analyze results from store` node to control cache sensitivity:\n- **Lower threshold** (e.g., 0.2): More strict matching, fewer false positives, more LLM calls\n- **Higher threshold** (e.g., 0.5): More lenient matching, more cache hits, potential for less relevant responses"
},
"typeVersion": 1
},
{
"id": "485fe302-f0d6-4889-812e-549244cf5ffe",
"name": "OpenAI Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
-112,
496
],
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1-mini"
},
"options": {}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.2
},
{
"id": "08cc2596-a105-45ce-9645-158607387623",
"name": "Redis Chat Memory",
"type": "@n8n/n8n-nodes-langchain.memoryRedisChat",
"position": [
64,
496
],
"parameters": {},
"credentials": {
"redis": {
"name": "<your credential>"
}
},
"typeVersion": 1.5
},
{
"id": "50407adf-e53c-4a6c-a32f-0bd2a62425b3",
"name": "When chat message received",
"type": "@n8n/n8n-nodes-langchain.chatTrigger",
"position": [
-1088,
288
],
"parameters": {
"options": {
"responseMode": "responseNodes"
}
},
"typeVersion": 1.4
},
{
"id": "acc2d01d-4694-47fc-88f5-f79c2ca47d36",
"name": "Analyze results from store",
"type": "n8n-nodes-base.code",
"position": [
-528,
288
],
"parameters": {
"jsCode": "// Modify this to tweak the ratio between false positives and false negatives\nconst distanceThreshold = 0.4; \n// Step 1. - find all documents that score below the threshold\nconst allMatches = $input.all().filter(item => item.json.score < distanceThreshold);\n// Step 2. - choose the one with the best score\nreturn allMatches.length > 1 ? allMatches.reduce((min, item) => item.json.score < min.json.score ? item : min) : allMatches;\n// AT THIS POINT ONLY ONE (OR ZERO) DOCUMENTS WOULD PASS\n// 1 DOCUMENT - document with highest score, above the score threshold (cache hit)\n// 0 DOCUMENTS - none of the documents are above the score threshold (cache miss)"
},
"executeOnce": true,
"typeVersion": 2,
"alwaysOutputData": true
},
{
"id": "9a49690b-fab4-4759-a633-9f7dfcef2be7",
"name": "Check for similar prompts",
"type": "@n8n/n8n-nodes-langchain.vectorStoreRedis",
"position": [
-864,
288
],
"parameters": {
"mode": "load",
"prompt": "={{ $json.chatInput }}",
"options": {},
"redisIndex": {
"__rl": true,
"mode": "list",
"value": "chat_cache"
}
},
"credentials": {
"redis": {
"name": "<your credential>"
}
},
"typeVersion": 1.3
},
{
"id": "70b60272-9e7f-4d06-9d3c-2da8d5c83ee5",
"name": "Respond to Chat (from semantic cache)",
"type": "@n8n/n8n-nodes-langchain.chat",
"position": [
-32,
-32
],
"parameters": {
"message": "={{ $json.document.metadata.reply }}",
"options": {},
"waitUserReply": false
},
"typeVersion": 1,
"alwaysOutputData": false
},
{
"id": "00e5c3d4-eada-4caf-bf5e-a3b49d73196e",
"name": "Respond to Chat (from LLM)",
"type": "@n8n/n8n-nodes-langchain.chat",
"position": [
768,
288
],
"parameters": {
"message": "={{ $json.metadata.reply }}",
"options": {},
"waitUserReply": false
},
"typeVersion": 1
},
{
"id": "f7919340-de57-41ca-a2b6-d41528ed6c34",
"name": "LLM Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
-80,
288
],
"parameters": {
"text": "={{ $('When chat message received').item.json.chatInput }}",
"options": {
"maxIterations": 10,
"systemMessage": "You are a helpful assistant helping out with generic questions. Reply to the user with your best answer and don't invent much."
},
"promptType": "define"
},
"typeVersion": 1.8
},
{
"id": "ad7dfa5e-4687-4cce-be6f-694779b7ff83",
"name": "Store entry in cache",
"type": "@n8n/n8n-nodes-langchain.vectorStoreRedis",
"position": [
304,
288
],
"parameters": {
"mode": "insert",
"options": {
"overwriteDocuments": true
},
"redisIndex": {
"__rl": true,
"mode": "list",
"value": "chat_cache",
"cachedResultName": "chat_cache"
}
},
"credentials": {
"redis": {
"name": "<your credential>"
}
},
"typeVersion": 1.3
},
{
"id": "e60f4189-4d99-4f95-98d5-2485a80509a0",
"name": "Add response as metadata",
"type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
"position": [
400,
496
],
"parameters": {
"options": {
"metadata": {
"metadataValues": [
{
"name": "reply",
"value": "={{ $json.output }}"
}
]
}
},
"jsonData": "={{ $('When chat message received').item.json.chatInput }}",
"jsonMode": "expressionData"
},
"typeVersion": 1
},
{
"id": "2546c015-da8f-4481-bb61-87113f82e206",
"name": "Recursive Character Text Splitter",
"type": "@n8n/n8n-nodes-langchain.textSplitterRecursiveCharacterTextSplitter",
"position": [
496,
672
],
"parameters": {
"options": {}
},
"typeVersion": 1
},
{
"id": "16887dcb-ccba-44a7-a64e-eb99db4256bc",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
288,
-32
],
"parameters": {
"color": 7,
"width": 576,
"height": 152,
"content": "### Embedding model\nObviously using your own model to calculate the embeddings would not only increase performance but may also drastically reduce the costs.\n\nEven with the existing popular models though calling an embedding model is still much more cheaper than calling the chat model."
},
"typeVersion": 1
},
{
"id": "d7cf0fd0-c042-45b5-8a24-29288c92cea4",
"name": "Is this a cache hit?",
"type": "n8n-nodes-base.if",
"position": [
-304,
288
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "c59204e1-85b1-4d40-89ca-04718c693b36",
"operator": {
"type": "object",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.document }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2,
"alwaysOutputData": false
},
{
"id": "96820700-4963-492f-a91f-b039d6efba03",
"name": "Embeddings HuggingFace Inference",
"type": "@n8n/n8n-nodes-langchain.embeddingsHuggingFaceInference",
"position": [
-864,
480
],
"parameters": {
"options": {}
},
"credentials": {
"huggingFaceApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "e5b313d3-ce98-4188-a593-890f5ae09b6b",
"name": "Embeddings HuggingFace Inference1",
"type": "@n8n/n8n-nodes-langchain.embeddingsHuggingFaceInference",
"position": [
256,
496
],
"parameters": {
"options": {}
},
"credentials": {
"huggingFaceApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "56925ea7-b773-486b-91af-c26837844e94",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1104,
-400
],
"parameters": {
"color": 3,
"width": 884,
"height": 640,
"content": "\n## Initialize the index\n_(manually creating the index can be done using the [langchain library](https://docs.langchain.com/oss/javascript/integrations/vectorstores/redis) )_\n\nIn case your Redis deployment does not\nhave a suitable index created you could run\nthe following manual flow to create it"
},
"typeVersion": 1
},
{
"id": "b47e75f4-9b9e-43ca-b100-95a88728dc83",
"name": "When clicking \u2018Execute workflow\u2019",
"type": "n8n-nodes-base.manualTrigger",
"position": [
-864,
-128
],
"parameters": {},
"typeVersion": 1
},
{
"id": "e4595eb8-ad5e-47be-b718-3897fcd8d120",
"name": "Initialize Redis store",
"type": "@n8n/n8n-nodes-langchain.vectorStoreRedis",
"position": [
-512,
-272
],
"parameters": {
"mode": "insert",
"options": {
"overwriteDocuments": false
},
"redisIndex": {
"__rl": true,
"mode": "list",
"value": "chat_cache",
"cachedResultName": "chat_cache"
}
},
"credentials": {
"redis": {
"name": "<your credential>"
}
},
"typeVersion": 1.3
},
{
"id": "7988808f-8d22-40c5-afe4-2e26d0d676f5",
"name": "Process sample data",
"type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
"position": [
-528,
-64
],
"parameters": {
"options": {
"metadata": {
"metadataValues": [
{
"name": "reply",
"value": "={{ $('When clicking \u2018Execute workflow\u2019').item.json.answer }}"
}
]
}
},
"jsonData": "={{ $('When clicking \u2018Execute workflow\u2019').item.json.question }}",
"jsonMode": "expressionData"
},
"typeVersion": 1
},
{
"id": "66828a6a-00ec-4377-b5bc-c25808050b34",
"name": "Use Huggingface for embeddings",
"type": "@n8n/n8n-nodes-langchain.embeddingsHuggingFaceInference",
"position": [
-720,
80
],
"parameters": {
"options": {}
},
"credentials": {
"huggingFaceApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "5e69ae73-70e5-463d-8e90-df327211d47f",
"name": "Recursive Character Text Splitter1",
"type": "@n8n/n8n-nodes-langchain.textSplitterRecursiveCharacterTextSplitter",
"position": [
-528,
80
],
"parameters": {
"options": {}
},
"typeVersion": 1
}
],
"active": false,
"settings": {},
"versionId": "0f65253d-9c23-418f-8bb4-4c811092a429",
"connections": {
"LLM Agent": {
"main": [
[
{
"node": "Store entry in cache",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Chat Model": {
"ai_languageModel": [
[
{
"node": "LLM Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Redis Chat Memory": {
"ai_memory": [
[
{
"node": "LLM Agent",
"type": "ai_memory",
"index": 0
}
]
]
},
"Process sample data": {
"ai_document": [
[
{
"node": "Initialize Redis store",
"type": "ai_document",
"index": 0
}
]
]
},
"Is this a cache hit?": {
"main": [
[
{
"node": "Respond to Chat (from semantic cache)",
"type": "main",
"index": 0
}
],
[
{
"node": "LLM Agent",
"type": "main",
"index": 0
}
]
]
},
"Store entry in cache": {
"main": [
[
{
"node": "Respond to Chat (from LLM)",
"type": "main",
"index": 0
}
]
]
},
"Add response as metadata": {
"ai_document": [
[
{
"node": "Store entry in cache",
"type": "ai_document",
"index": 0
}
]
]
},
"Check for similar prompts": {
"main": [
[
{
"node": "Analyze results from store",
"type": "main",
"index": 0
}
]
]
},
"Analyze results from store": {
"main": [
[
{
"node": "Is this a cache hit?",
"type": "main",
"index": 0
}
]
]
},
"When chat message received": {
"main": [
[
{
"node": "Check for similar prompts",
"type": "main",
"index": 0
}
]
]
},
"Use Huggingface for embeddings": {
"ai_embedding": [
[
{
"node": "Initialize Redis store",
"type": "ai_embedding",
"index": 0
}
]
]
},
"Embeddings HuggingFace Inference": {
"ai_embedding": [
[
{
"node": "Check for similar prompts",
"type": "ai_embedding",
"index": 0
}
]
]
},
"Embeddings HuggingFace Inference1": {
"ai_embedding": [
[
{
"node": "Store entry in cache",
"type": "ai_embedding",
"index": 0
}
]
]
},
"Recursive Character Text Splitter": {
"ai_textSplitter": [
[
{
"node": "Add response as metadata",
"type": "ai_textSplitter",
"index": 0
}
]
]
},
"Recursive Character Text Splitter1": {
"ai_textSplitter": [
[
{
"node": "Process sample data",
"type": "ai_textSplitter",
"index": 0
}
]
]
},
"When clicking \u2018Execute workflow\u2019": {
"main": [
[
{
"node": "Initialize Redis store",
"type": "main",
"index": 0
}
]
]
}
}
}
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.
huggingFaceApiopenAiApiredis
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Your LLM is answering the same questions over and over. "What's the weather?" "How's the weather today?" "Tell me about the weather." Same answer, three API calls, triple the cost. This workflow fixes that.
Source: https://n8n.io/workflows/10887/ — 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.
• Create a Google Drive folder to watch. • Connect your Google Drive account in n8n and authorize access. • Point the Google Drive Trigger node to this folder (new/modified files trigger the flow).
Advanced Ai Demo Presented At Ai Developers 14 Meetup. Uses slack, stickyNote, textSplitterRecursiveCharacterTextSplitter, embeddingsOpenAi. Chat trigger; 39 nodes.
Advanced Ai Demo (Presented At Ai Developers #14 Meetup). Uses slack, stickyNote, textSplitterRecursiveCharacterTextSplitter, embeddingsOpenAi. Chat trigger; 39 nodes.
Workflow 2358. Uses slack, textSplitterRecursiveCharacterTextSplitter, embeddingsOpenAi, documentDefaultDataLoader. Chat trigger; 39 nodes.
2358. Uses slack, textSplitterRecursiveCharacterTextSplitter, embeddingsOpenAi, documentDefaultDataLoader. Chat trigger; 39 nodes.