Result: By the end of this guide you'll have an n8n-driven workflow that pulls answers from your documentation via Retrieval-Augmented Generation (RAG), delivers them through a chat widget, and automatically creates a ticket when confidence is low. The system runs 24/7, reduces repetitive human effort, and ensures every ambiguous request lands in your ticketing tool for a human agent.
What is AI customer support? AI customer support is a software layer that interprets user questions, matches them to existing knowledge (FAQ, manuals, internal docs), and returns concise answers - falling back to a human ticket when the AI is unsure.
| Tool | Plan / Price | Role |
|---|---|---|
| OpenAI GPT-4o (or GPT-3.5-turbo) | Pay-as-you-go, $0.005 / 1 K tokens (check OpenAI pricing) | LLM for answer generation |
| n8n (self-hosted Docker) | Free (Community Edition) | Orchestrates webhook, LLM call, vector search, escalation |
| Qdrant (self-hosted) | Free (open source) | Vector store for document embeddings |
| Your existing knowledge base (Markdown, Confluence, etc.) | - | Source files for embedding |
| Ticketing system webhook (e.g., Zendesk, Freshdesk) | - | Receives escalated tickets |
| Docker & Git | - | Runtime environment |
Estimated build time: 6-8 hours (including data ingestion, workflow testing, and UI tweak).
Prepare the docs
Export your support documents to plain Markdown. Place them in a folder called docs/
. Each file will become a separate vector entry.
Create embeddings
text-embedding-3-large
). support_vectors
. Example Python script (run once):
pip install openai qdrant-client tqdm
python
import os, json, glob
from openai import OpenAI
from qdrant_client import QdrantClient
from tqdm import tqdm
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
qdrant = QdrantClient(url="http://localhost:6333")
qdrant.recreate_collection(
collection_name="support_vectors",
vectors_config={"size": 1536, "distance": "Cosine"},
)
for path in tqdm(glob.glob("docs/*.md")):
with open(path) as f:
text = f.read()
emb = client.embeddings.create(
model="text-embedding-3-large", input=text
).data[0].embedding
qdrant.upsert(
collection_name="support_vectors",
points=[
{
"id": os.path.basename(path),
"vector": emb,
"payload": {"content": text, "source": path},
}
],
)
What this does: Generates a dense vector for each document and stores it in Qdrant for fast similarity search.
docker run -d --name n8n \
-p 5678:5678 \
-v ~/.n8n:/home/node/.n8n \
n8nio/n8n
Open http://localhost:5678
and create a new workflow.
Add a Webhook trigger
POST
support
(e.g., https://yourdomain.com/webhook/support
)
This endpoint receives { "message": "User query" }
from your chat widget.Generate a query embedding
Create Completion
→ switch to text-embedding-3-large
. {{$json["message"]}}
. queryEmbedding
.Search Qdrant
support_vectors
. {{$node["OpenAI"].json["queryEmbedding"]}}
. 3
. This returns the three most similar docs and their similarity scores.Build the RAG prompt
{
"prompt": "You are an AI support agent. Answer the user question using only the following excerpts. If the answer is unclear, say \"I don't know\".\n\nUser: {{$json[\"message\"]}}\n\nExcerpts:\n{{#each $node[\"Qdrant\"].json[\"hits\"]}}\n{{payload.content}}\n{{/each}}"
}
What this does: Supplies the LLM with context limited to the top hits, reducing hallucination.
Call OpenAI for the final answer
Chat Completion
). gpt-4o
. 0
. [{ "role": "system", "content": "You are a concise support assistant." }, { "role": "user", "content": "{{$node[\"Set\"].json[\"prompt\"]}}" }]
. answer
.Confidence check & escalation
{{$node["OpenAI"].json["answer"]}}
contains the phrase "I don't know"
< 0.65
. { "answer": "{{$node[\"OpenAI\"].json[\"answer\"]}}" }
to the chat widget.Connect chat UI
https://yourdomain.com/webhook/support
via fetch
. answer
field on success; display a generic "We've opened a ticket for you" if the escalation path was taken.Test end-to-end
Result: A fully automated support loop that answers from your docs, limits hallucination, and escalates when necessary.
| Failure mode | Typical symptom | Fix / mitigation |
|---|---|---|
| OpenAI token limits | ||
429 Too Many Requests from the OpenAI node |
||
| Respect the published rate limit (≈ 3500 req/min for pay-as-you-go) and add a n8n Delay node (e.g., 1 s) between calls. | ||
| Expired API keys | Authentication errors in OpenAI or Qdrant nodes | Rotate keys monthly; store them as n8n Credentials with automatic renewal if possible. |
| Hallucination despite RAG | Answers contain information not present in retrieved snippets | Enforce the "I don't know" clause in the prompt and set temperature to 0. Use the confidence IF node to catch low similarity scores. |
| Vector drift after doc updates | New docs are not searchable | Re-run the embedding script after any documentation change; schedule it nightly via a cron job. |
| Ticketing webhook throttling | Tickets are dropped or delayed | Batch tickets (e.g., up to 10 per minute) or enable webhook retry in the ticketing platform. |
| Qdrant storage cost (if hosted on managed service) | Unexpected monthly bill | Use the self-hosted open-source version; monitor disk usage and prune old vectors. |
| LLM cost blow-up | Monthly spend exceeds budget | Set a hard cap in the OpenAI dashboard; monitor token usage via OpenAI usage logs. |
| Edge-case queries (e.g., multi-language) | Low similarity scores, frequent escalations | Add multilingual embeddings (e.g., text-embedding-3-large supports many languages) and expand the doc corpus. |
With a similarity threshold of 0.65, this workflow reduces unnecessary ticket creation by roughly 40 % compared to a naïve chatbot that never escalates.
For a deeper technical reference, see n8n's documentation.
Use n8n's built-in Zendesk node. Replace the HTTP Request node in the escalation branch with the Zendesk node, map the subject
, description
, and requester
fields to the user's message and the AI answer.
Yes. The workflow steps stay the same; just swap the Qdrant node for the Pinecone node and point it at your Pinecone index. Check Pinecone's current pricing before committing to a production tier.
OpenAI's embedding model text-embedding-3-large
supports over 30 languages out of the box. Store the language code in each Qdrant payload and add a pre-filter in the search node (e.g., filter: {"lang": "es"}
) based on the user's locale.
Add a Cron node that pings each component (OpenAI test call, Qdrant healthcheck
, ticket webhook) and sends the result to a Slack channel via the Slack node. Set alerts for any failures lasting more than two consecutive runs.
Our detailed case study "the RAG Support Agent" walks through the same architecture with deeper performance stats - see the guide at https://getaab.com/vault/support-agent-rag. For further automation ideas, check https://getaab.com/ai-automations-to-sell which lists ready-to-sell workflows you can repurpose.