B·02 / ARTIFICIAL INTELLIGENCE
RAG: How to Make LLMs Accurate With Your Own Data
A practical guide to Retrieval-Augmented Generation — how to ground LLM answers in your own documents using embeddings, vector search, and smart chunking.
SEP 07, 2026 · 6 MIN READ

RAG · LLM · EMBEDDINGS · VECTOR-SEARCH · AI-ENGINEERING
Ask ChatGPT or Claude about your company's internal refund policy, and it will confidently tell you something — probably wrong, because it has never seen your documents. Large language models know a lot about the world in general, but nothing about your world specifically: your codebase, your product docs, your customer records, last week's meeting notes. Retrieval-Augmented Generation (RAG) is the standard pattern for fixing that gap, and it's one of the most practical techniques in production AI systems today.
The core problem: LLMs don't know your data
A language model's knowledge is frozen at training time and limited to whatever it saw during training. Two consequences follow directly:
- It doesn't know anything private — your internal wiki, your database, your PDFs sitting in a folder.
- It doesn't know anything recent — news, price changes, policy updates after its training cutoff.
You could try to fix this by stuffing your entire knowledge base into the prompt every time, but that runs into context window limits fast, costs a lot in tokens, and gets slower and less accurate as the context grows. RAG solves this more surgically: instead of giving the model everything, you give it only the small slice of information relevant to the current question.
How RAG actually works
RAG has two phases: indexing (done once, ahead of time) and retrieval + generation (done on every query).
Phase 1: Indexing
- Chunk your documents — split large documents into smaller pieces (paragraphs, sections) so each chunk is focused and retrievable independently.
- Embed each chunk — pass each chunk through an embedding model, which converts text into a vector (a list of numbers) that captures its semantic meaning.
- Store the vectors — save these embeddings in a vector database, alongside the original text.
JAVASCRIPT1import { OpenAI } from "openai";
2
3const openai = new OpenAI();
4
5async function embedText(text) {
6 const response = await openai.embeddings.create({
7 model: "text-embedding-3-small",
8 input: text,
9 });
10 return response.data[0].embedding;
11}
12
13async function indexDocument(chunks, vectorStore) {
14 for (const chunk of chunks) {
15 const vector = await embedText(chunk.text);
16 await vectorStore.insert({
17 id: chunk.id,
18 vector,
19 metadata: { text: chunk.text, source: chunk.source },
20 });
21 }
22}
Phase 2: Retrieval + Generation
When a user asks a question:
- Embed the query using the same embedding model.
- Search the vector store for chunks whose embeddings are closest to the query embedding (using cosine similarity or a similar metric) — this is "semantic search," because it matches meaning, not just keywords.
- Inject the retrieved chunks into the prompt, alongside the user's question.
- Let the LLM generate an answer grounded in that retrieved context.
JAVASCRIPT1async function answerWithRAG(question, vectorStore, llm) {
2 const queryVector = await embedText(question);
3
4 const results = await vectorStore.search(queryVector, { topK: 5 });
5 const context = results.map((r) => r.metadata.text).join("\n\n---\n\n");
6
7 const prompt = `Answer the question using only the context below.
8If the answer isn't in the context, say you don't know.
9
10Context:
11${context}
12
13Question: ${question}`;
14
15 return llm.generate(prompt);
16}
That's the whole pattern. No fine-tuning, no retraining — just search, then generate.
Why "semantic" search beats keyword search
A traditional keyword search for "how do I cancel my plan" might miss a document that says "terminating your subscription" because the words don't match literally. Embeddings solve this because they capture meaning: "cancel my plan" and "terminate subscription" end up as vectors that are close together in vector space, even with zero shared words.
PYTHON1# Conceptually, similarity is just the angle between two vectors
2import numpy as np
3
4def cosine_similarity(a, b):
5 return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
6
7query_vec = embed("how do I cancel my plan")
8doc_vec = embed("steps to terminate your subscription")
9
10similarity = cosine_similarity(query_vec, doc_vec)
11# High similarity even though almost no words overlap
Chunking strategy matters more than people expect
A common beginner mistake is chunking documents naively — e.g., every 500 characters, regardless of content. This can split a sentence or a table mid-way, destroying meaning. Better strategies:
- Chunk by structure — split on headings, paragraphs, or sections rather than a fixed character count.
- Add overlap — include the last 1-2 sentences of the previous chunk at the start of the next, so context isn't lost at boundaries.
- Keep chunks focused but not too small — a chunk that's just one sentence often lacks enough context to be useful on its own; a chunk that's an entire chapter dilutes the specific answer among noise.
Common failure modes and how to fix them
- Irrelevant retrieval — the search returns chunks that are topically similar but don't actually answer the question. Fix: tune
topK, improve chunking, or add a reranking step that re-scores retrieved chunks with a more precise (but slower) model before passing them to the LLM. - Missing information — the answer exists in your documents but wasn't retrieved. Fix: check your chunking granularity, or add hybrid search (combining keyword search with semantic search) to catch cases embeddings miss.
- The model ignores the context and hallucinates anyway — fix this with a stricter system prompt ("only answer using the provided context; say 'I don't know' if it's not there") and by testing that instruction explicitly.
- Stale data — if your source documents change, your vector store needs to be re-indexed. RAG pipelines need the same "keep it fresh" discipline as any other data pipeline.
When RAG is the right tool
RAG shines when:
- You need answers grounded in a specific, large, or frequently changing knowledge base (docs, tickets, internal wikis)
- You want to cite sources for generated answers
- Fine-tuning a model on your data would be too expensive, slow, or hard to keep updated
RAG is not the right tool when:
- You just need the model to adopt a certain tone or style — that's better solved with prompting or fine-tuning
- Your entire knowledge base easily fits in the context window and doesn't change — sometimes just pasting the whole thing in is simpler and good enough
- You need the model to learn a new skill or reasoning pattern, not just recall facts — that requires fine-tuning, not retrieval
Wrapping up
RAG turns a general-purpose language model into a system that can accurately answer questions about your specific world, without retraining the model itself. The mechanics are refreshingly simple — embed, store, search, inject, generate — but the details of chunking, retrieval quality, and prompt design are where a mediocre RAG system becomes a genuinely reliable one. If you're building anything that needs an LLM to reason over your own data, RAG is very likely the first architecture you should reach for.