RAG support assistant with designed failure paths

71% hit rate at 5, 4.80/5 faithfulness, across 7 evaluation experiments

Support bots fail worst when they answer confidently from weak retrieval. I built a pipeline where every low-confidence path has a designed response instead of a guess. Role: Sole builder.

Classify every query into five categories before retrieval

Because a cancellation request must never get a how-to answer, and nonsense should not waste retrieval compute.

Measured byeach category routing to its own purpose-built response path

Gate answers behind two reranker confidence thresholds

Because generating from weak context erodes trust faster than asking a clarifying question.

Measured byfaithfulness and relevancy scores across 7 experiments (4.80/5 and 4.76/5)

Run a self-critique step after generation

Because the cheapest place to catch a partial answer is before the user sees it.

Measured bythe critique verdict deciding whether human escalation is offered

Code: github.com/alfredpersson/rag-support-assistant.

Video walkthrough

Overview

A customer support RAG pipeline built over the Wix Help Center dataset. Every stage (chunking, retrieval, reranking, classification, generation, and evaluation) is implemented from scratch so the design decisions are visible and auditable.

The focus is on what happens when the pipeline can’t confidently answer: when retrieval returns nothing useful, when the model hedges, or when the user is upset and asking to cancel their account. Each of these cases has a specific, designed response path.

In a support context, a bot that handles a cancellation request the same way it handles a how-to question will lose that customer. A bot that confidently presents a wrong answer erodes trust faster than having no bot at all. So the pipeline is built to deflect tickets when the bot can help, preserve trust when it can’t, and route to a human when the situation calls for it.

Screenshots

The support widget lives in the corner of a mock SaaS dashboard:

Mock SaaS dashboard with closed chat widget

Dashboard with chat widget open

Response types

The widget renders each response type differently based on the pipeline’s routing decision:

Fully answered: a grounded answer with step-by-step instructions and source article links:

Fully answered query with source links

Follow-up question: when retrieval finds nothing relevant, the bot asks a clarifying question instead of guessing:

Follow-up clarifying question

High-stakes escalation: sensitive queries (cancellations, billing disputes) get an empathetic response with a path to a human agent:

High-stakes query with empathetic response and escalation

Low confidence: the answer is shown with a disclaimer and no source links:

Low confidence response with disclaimer

How it works

Every question is classified into one of five categories (answerable, nonsense, irrelevant, out-of-scope, or high-stakes) and routed to a purpose-built handler. Only answerable and high-stakes queries go through retrieval; the rest get static responses immediately without wasting compute.

For queries that go through retrieval, the pipeline applies two confidence gates before presenting an answer:

  1. Relevance gate: if the cross-encoder reranker’s top score is below 2.0, the retrieved results aren’t good enough. Instead of generating from weak context, the bot asks a clarifying follow-up question.
  2. Confidence gate: if the top score is below 5.0, the answer is shown but the bot signals that it isn’t fully sure and asks the user to be more specific.

After generation, a self-critique step assesses whether the answer fully, partially, or cannot address the question. This determines whether to offer human escalation and how much confidence to convey to the user.

High-stakes queries (cancellations, billing disputes, complaints) bypass normal generation entirely. They get an empathetic acknowledgment with a structured retention offer and escalation path. A standard RAG answer to “I want to cancel my account” is the wrong response.

Pipeline

Flowchart of the support pipeline: a user question enters FastAPI and a classifier routes it to a static response, a query rewriter, or an escalation offer; retrieved and reranked results pass a relevance gate to either a clarifying question or the generator, which passes a confidence gate before a self-critique step decides between escalation, a soft-escalation answer, or a fully sourced answer

Failure states

SituationResponseReasoning
Retrieval finds nothing relevantClarifying follow-up questionAvoids hallucinating from weak context; keeps the conversation going
Low retrieval confidence (score 2.0–5.0)Disclaimer above the answer, no source linksContext is good enough to attempt an answer but not good enough to cite confidently
Self-critique: CANNOT_ANSWER“I couldn’t find an answer” + connect-to-agent buttonImmediate escalation path instead of a vague hedge
Self-critique: PARTIALLY_ANSWEREDAnswer + 1 source link + soft inline escalation linkPartial answers are still useful: a soft text link gives the user an easy path if the answer isn’t enough
Self-critique: FULLY_ANSWEREDAnswer + up to 2 source linksConfident answer with “read more” links to the full articles
High-stakes queryEmpathetic acknowledgment + retention offer + escalationSensitive queries need tone and structure control, not a help article
Out-of-scope queryEscalation offerDoesn’t pretend to help with things it can’t handle
Nonsense or irrelevant queryPolite deflection + topic suggestionsSaves retrieval and generation compute

Evaluation

The pipeline is evaluated against the WixQA benchmark with retrieval and generation scored separately, so failures can be diagnosed to the right stage. 7 experiments were run across retrieval strategy, query expansion, classifier tuning, and chunk enrichment. The best configuration is title-prepended embeddings + cross-encoder reranking.

MetricScoreWhat it means
Expert Hit Rate @ 50.71 (n=200)The correct help article appeared in the top 5 results 71% of the time
Expert MRR0.51 (n=200)On average, the correct article appeared around position 2
Faithfulness4.80 / 5 (n=50)Answers are almost entirely grounded in retrieved content, not hallucinated
Relevancy4.76 / 5 (n=50)Answers consistently address what was actually asked

The 71% hit rate held steady across all seven experiments. The remaining 29% are KB coverage gaps and vocabulary mismatches that retrieval tuning alone won’t close. That’s exactly the scenario the confidence gates and failure paths above are built for: rather than trying to squeeze more accuracy out of retrieval, the system recognizes low-confidence results and routes them to clarifying questions, disclaimers, or human escalation.

What changes for production

AreaDemoProduction
Vector storeChromaDB (local file)pgvector or managed service (Pinecone, Qdrant)
RetrievalDense onlyHybrid: dense + BM25 with RRF
ClassificationLLM call (gpt-4o-mini)Fine-tuned small classifier, <50 ms
EvaluationOffline script, LLM-as-judge on 50 questionsCI-gated regression suite on prompt/retrieval changes
Rate limitingJSON fileRedis INCR + EXPIRE, per-user
Conversation contextStateless (single turn)Sliding window stored in Redis, session ID on API
StreamingNoneServer-Sent Events, token-by-token
FrontendVanilla JS widgetReact + TypeScript

Tech stack

LayerTechWhat it does
ChunkingParagraph split + merge/split heuristics + 50-token overlapBreaks help articles into retrievable pieces while preserving natural structure
Embeddingsentence-transformers/all-MiniLM-L6-v2 (local)Converts text to vectors for similarity search, runs locally with no API cost
Vector storeChromaDB (persistent, local)Stores and searches article chunks by similarity
Rerankercross-encoder/ms-marco-MiniLM-L6-v2Two-pass search: fast vector scan, then precise re-scoring of top candidates
LLMOpenAI gpt-4o-mini via pydantic-aiGenerates answers, classifies queries, and self-critiques responses
APIFastAPI + CORS + static filesServes the pipeline and frontend
FrontendVanilla HTML/CSS/JS chat widgetRenders each response type differently: source links, escalation buttons, suggestion chips
ObservabilityLangfuse (traces + prompt versioning)Shows exactly what happened on every request