📘 Part 9 of the Agentic Engineering Series

✍️By: David Estrada   |   📅Date Published: 2026/06/19


In Part 8, we went deep on Markdown as the invisible substrate holding the entire agentic stack together. Now we zoom out to tackle the question underneath the question — how do AI systems actually find and use your information? The answer is RAG, and it’s not what most people think it is.


RAG Explained: How AI Systems Search Your Documents — And Why “Smarter AI” Won’t Fix a Broken Pipeline

The retrieval layer is where most AI systems fail. Here’s how it actually works.

"You can't fix a retrieval problem with a better LLM. The model only sees what you hand it. If you hand it garbage, it produces garbage — confidently."

If you’ve used an AI chatbot that “knows about your documents,” you’ve seen RAG in action. Maybe it worked brilliantly. Maybe it gave you a confidently wrong answer about something clearly in the document you uploaded.

That second outcome isn’t a model failure. It’s a pipeline failure. And you can’t fix it by upgrading the model.

RAG — Retrieval-Augmented Generation — is the architecture that lets AI systems search a knowledge base and use what they find to answer questions. It’s behind every “chat with your docs” product, every enterprise knowledge assistant, every AI that claims to know about your internal systems.

Understanding it deeply is not optional for agentic engineers. It’s table stakes.

Part 1: The Core Problem RAG Solves 🧩

Large language models are trained on massive datasets — but that training has a cutoff. They don’t know about your internal docs. They don’t know about the meeting notes from last Tuesday. They don’t know about the spec that changed three months ago.

The naive fix is to paste everything into the prompt. But there’s a hard limit: context windows cap out. You can’t stuff a 50,000-document knowledge base into a prompt. Even if you could, the model’s attention degrades when the context is too long.

RAG solves this by flipping the problem. Instead of giving the model everything upfront, you:

  1. Index your documents into a searchable vector database
  2. Retrieve only the most relevant chunks at query time
  3. Inject those chunks into the prompt

The model never sees the full knowledge base. It sees a curated, relevant slice — and answers from that.

Simple in theory. Surprisingly easy to break in practice.

① Indexing Phase ② Query Time Documents Chunk & Embed Vector Database User Question Embed Query Search Top-k Inject into Prompt LLM Answer

Part 2: The Indexing Phase — Building the Searchable Foundation 🏗️

Before you can retrieve anything, you have to build the index. This is the phase most people rush through. It’s where most RAG systems quietly break.

Step 1: Documents

Raw input. Text files, PDFs, Obsidian notes, web pages, Confluence pages — anything with content you want to make searchable. At this stage it’s unstructured text. Qdrant (or any vector database) doesn’t understand it yet.

Step 2: Chunking

You can’t embed an entire document as one unit effectively.

A 10,000-word document embedded as a single vector loses all specificity. The embedding becomes an average of everything, which means it matches nothing well. Ask about the billing section of a long spec — a whole-document embedding returns the whole spec, ranked below documents that are more narrowly about billing.

Chunking solves this by splitting documents into smaller pieces — typically 200–500 words per chunk, with overlap between adjacent chunks.

The overlap is not optional. Here’s why:

If chunk 3 ends mid-sentence and chunk 4 starts mid-sentence, retrieval might return chunk 3 without its conclusion, or chunk 4 without its setup. Either way, the context the LLM receives is broken. With overlap — say 50 words shared between adjacent chunks — each chunk carries enough surrounding context to stand alone as a coherent piece of text.

Get chunking wrong and everything downstream suffers. This is the most common root cause of RAG failures.
				
					Document (10,000 words)
    → Chunk 1: words 1–400
    → Chunk 2: words 350–750   ← 50-word overlap with chunk 1
    → Chunk 3: words 700–1100  ← 50-word overlap with chunk 2
    → ...

				
			

Get chunking wrong and everything downstream suffers. This is the most common root cause of RAG failures.

Step 3: Embedding

An embedding model — for example, all-MiniLM-L6-v2 — converts each text chunk into a vector: a list of floating point numbers.

				
					"The server is down" → [0.12, -0.83, 0.44, 0.07, -0.31, ...]
                         ← 384 dimensions in MiniLM's case →
				
			

What do those numbers mean? Semantic meaning, encoded as a position in high-dimensional space.

Chunks that mean similar things end up geometrically close to each other. “The server is down” and “the machine stopped responding” land near each other. “The server is down” and “I like pasta” do not.

This is the translation step — from human language to math that Qdrant can reason about. The quality of your embedding model sets the ceiling on how well semantic search can work.

Step 4: Store in Qdrant

Each chunk gets stored as a point in Qdrant with three parts:

FieldWhat it is
IDUnique identifier for this chunk
VectorThe embedding — the 384 (or 768, or 1536, depending on model) floating point numbers
PayloadMetadata: source file, original text, section heading, date, whatever you attach
Each Qdrant Point 📄 Documents ✂️ Chunking (200-500 words, overlap) 🧮 Embedding (Text → 384-dim vector) 🗄️ Store in Qdrant ⚡ HNSW Index (ms-level search) ID · Vector · Payload

Qdrant builds an HNSW graph (Hierarchical Navigable Small World) over all stored vectors. This index structure lets Qdrant find nearest neighbors in milliseconds — without comparing your query against every single point in the database.

At scale, that index is the difference between a 20ms query and a 20-second one.

Part 3: The Retrieval + Generation Phase — From Question to Answer 🔍

With the index built, you’re ready for the live pipeline. This is what runs every time a user asks a question.

Step 1: Query

The user’s question. Plain text. No special syntax required — this is the whole point.

Step 2: Embed the Query

The query goes through the exact same embedding model you used during indexing. This produces a vector in the same dimensional space as your stored chunks.

This constraint is non-negotiable. If you indexed with all-MiniLM-L6-v2 (384 dimensions) and query with text-embedding-3-large (1536 dimensions), the vectors live in completely different spaces. The geometry doesn’t align. You’ll get garbage results with no error message to tell you why.

				
					"Why is the server slow?" → [0.09, -0.71, 0.51, ...] (384 dimensions)
				
			

Step 3: Search Qdrant

Qdrant computes the cosine similarity (or dot product, depending on your config) between your query vector and every stored vector, then returns the top-k closest matches.

“Closest” = most semantically similar to your question.

This is not keyword search. It doesn’t look for matching words. It finds chunks whose meaning is geometrically near your query’s meaning. That’s the power:

  • “server slow” matches “high CPU utilization causing degraded response times” — even with zero word overlap
  • “billing issue” matches “payment processing error in the invoicing module” — same idea

It’s also the failure mode: near in meaning isn’t always relevant to the question. Semantic similarity is a proxy for relevance, not a guarantee of it.

Step 4: Inject Top-k Chunks into the LLM Prompt

You take the retrieved chunks and inject them into your prompt as context:

				
					Here is relevant context:
[chunk 1 text]
[chunk 2 text]
[chunk 3 text]

Answer this question using the context above: {user question}
				
			

The LLM never searched Qdrant. It never saw your documents. It only sees what you paste into the prompt.

The retrieval step is entirely your responsibility. You chose which chunks to retrieve. You chose how many (top-k). You chose how to format them. The LLM is just reading what you hand it.

This is the critical point most people miss: the LLM is not doing RAG — you are. The model is pattern-matching over whatever context it receives. If you hand it the wrong chunks, it either hallucinates or says “I don’t know.” Upgrading the model won’t fix a retrieval problem.

Step 5: Generate the Answer

The LLM reads the injected context and answers based on it. A good model grounds its response in the chunks. If the chunks are relevant but the model is weak, it may still drift. If the chunks are irrelevant, even the best model can’t save you.

❓ 'Why is the server slow?' 🧮 Embed Query (Same model as indexing) 🔍 Search Qdrant (Cosine similarity) 📋 Top-k Chunks 📝 Inject into Prompt 📄 Context chunks injected above prompt 🧠 LLM ✅ Answer ⚠️ The LLM never searched Qdrant. It only sees what you paste.

Part 4: The Core Insight — Every Layer Caps the Next ⚠️

The entire RAG system is a pipeline where each step’s output quality sets a ceiling on the next step:

				
					Bad chunks → bad embeddings → bad retrieval → bad context → bad answer
				
			

You can’t fix a retrieval problem by swapping in a better LLM. You can’t fix a chunking problem by upgrading your embedding model. Each layer has to be right independently.

Most RAG failures are chunking or retrieval failures misdiagnosed as LLM failures.

The debugging order matters:

  1. Are the chunks coherent and appropriately sized?
  2. Is the embedding model a good fit for your content domain?
  3. Is the top-k actually returning relevant chunks for your test queries?
  4. Is the retrieved context being injected cleanly into the prompt?
  5. Only then: Is the LLM response quality the problem?

 

Teams that skip straight to step 5 spend weeks swapping models and see no improvement. Because the model was never the problem.

Debugging Order Bad Chunks (Wrong size, no overlap) Bad Embeddings (Context lost) Bad Retrieval (Wrong chunks) Bad Context (Prompt is garbage) Bad Answer (Confident hallucination) ① Chunking ② Embedding ③ Retrieval ④ Injection ⑤ Only then: LLM

Part 5: Common Failure Modes 🚨

FailureSymptomRoot Cause
Chunks too largeRetrieval returns vaguely relevant but unfocused contextEmbedding averages too much meaning
Chunks too smallRetrieved chunks lack enough context to answer the questionSentences without surrounding setup/conclusion
No chunk overlapMid-sentence boundaries break contextAdjacent chunks don’t share enough overlap
Model mismatchGarbage retrieval results, no errorIndexing and querying used different embedding models
Top-k too lowCorrect answer exists but wasn’t retrievedNot enough candidates surfaced
Top-k too highLLM context overloaded with irrelevant chunksToo many candidates dilute signal
Weak embedding modelDomain-specific terminology not capturedGeneral model on specialized content

Part 6: RAG in the Agentic Engineering Stack 🤖

RAG doesn’t exist in isolation in an agentic system. It’s a retrieval tool that agents invoke — and the quality of what the agent can reason about is bounded by how well that tool is built.

In the Agent, for example, the FAQ knowledge base is indexed nightly. When a visitor asks a question, the agent doesn’t search the raw Confluence pages — it queries the Qdrant index, retrieves the top-k FAQ chunks, and generates a response grounded in that content.

The agent is only as good as the index. Stale chunks = stale answers. Bad chunking = broken context. No overlap = incomplete answers. The model — even a strong one — can’t compensate.

This is why RAG setup is an engineering task, not a configuration task. Chunk size, overlap, embedding model selection, index refresh cadence — these are decisions with real quality consequences. They deserve the same rigor as any other infrastructure choice.

RAG Quick Reference

DecisionRecommendation
Chunk size200–500 words to start; tune based on your content
Chunk overlap10–15% of chunk size (e.g., 50 words on a 400-word chunk)
Embedding modelMatch domain — general content: MiniLM; code/technical: look at domain-specific models
Top-kStart at 3–5; increase if answers are incomplete, decrease if too noisy
Index refreshMatch your content update cadence — nightly for most KB use cases
Debugging orderChunks → embeddings → retrieval → injection → generation
Model mismatch checkAlways verify indexing and query embedding models are identical
Metadata in payloadAlways store source + original text; add section/date if available
RAG Tool Maintenance 🌐 Website Visitor 🤖 Agent 💬 Grounded Answer Visitor Question 🔍 Query Qdrant Retrieve Top-k Ground in Retrieved Content Nightly Re-index 🗄️ Qdrant FAQ Index

"The teams that get RAG right aren't using better models than everyone else. They're building better pipelines."

DSCSA checklist form

Get Your DSCSA Readiness Checklist

Enter your name and email below — we’ll send the checklist PDF directly to your inbox.