Listen up. If you're building modern applications, understanding Context & RAG in AI & Artificial Intelligence is non-negotiable. This is where simple logic turns into intelligent behavior.
1Why LLMs Need RAG
A model's knowledge is frozen at the point its training data was collected, and it has no built-in way to read a specific company's internal documents. Retrieval-Augmented Generation closes that gap by fetching relevant private data at query time and handing it to the model as part of the prompt, rather than trying to retrain the model on it.
This makes RAG the standard way to build a support bot that knows your product docs, or a search assistant that answers from your own PDFs, without ever fine-tuning the underlying LLM.
// Example
console.log("Running AI concept...");AI logic processed successfully.
2Turning Text into Embeddings
An embedding model converts a chunk of text into a fixed-length list of numbers (a vector) positioned so that semantically similar text ends up mathematically close in that vector space β 'vacation policy' and 'time off rules' land near each other even though they share no exact words.
Every document you want the model to know about has to go through this same embedding step once, ahead of time, before any user ever asks a question.
const response = await openai.embeddings.create({
model: "text-embedding-3-small",
input: "Our company policy on remote work...",
});
const vector = response.data[0].embedding;AI logic processed successfully.
3Storing Vectors in a Vector Database
A vector database like Pinecone or Weaviate is purpose-built to store millions of these embeddings alongside their original text and metadata, and to answer 'which stored vectors are closest to this query vector' in milliseconds using approximate nearest-neighbor search.
This is fundamentally different from a keyword index: a vector DB search for 'remote work' can surface a document that only ever says 'working from home', because it matches on meaning rather than shared words.
// Storing in Pinecone/Weaviate
await index.upsert([
{ id: 'doc1', values: vector, metadata: { text: 'Remote policy...' } }
]);AI logic processed successfully.
4Upserting Documents into the Index
Loading a document into the vector store is an 'upsert': you give it a unique id, the embedding vector, and a metadata payload (the original text, the source file, a department tag), so a later search can return not just a similarity score but the actual passage to show the model.
Getting this metadata right up front matters β it's what lets you later filter a search to just one department or document type without re-embedding anything.
// Example
console.log("Running AI concept...");AI logic processed successfully.
5Augmenting the Prompt with Retrieved Context
At query time, the user's question is embedded with the same model used for indexing, the vector DB returns the most similar stored chunks, and those chunks are pasted directly into the prompt above the question β this is the 'augmented' part of Retrieval-Augmented Generation.
The model then answers using text that is physically present in its own context window, rather than trying to recall a fact from training, which is the whole reason RAG reduces made-up answers.
Context: Grounded
AI logic processed successfully.
6Grounding: Why This Reduces Hallucinations
'Grounding' means forcing the model to base its answer on specific evidence you supplied, instead of on statistical patterns from training data it can no longer verify. A well-grounded prompt typically instructs the model to say it doesn't know rather than guess when the retrieved context doesn't contain the answer.
RAG doesn't make hallucinations impossible β a model can still misread the retrieved text β but it collapses the failure mode from 'invented from nothing' to 'misread a specific passage', which is far easier to debug and fix.
// Example
console.log("Running AI concept...");AI logic processed successfully.
7Chunking: Why Document Size Matters
An embedding compresses an entire chunk of text into one fixed-size vector, so if you embed a 50-page document as a single chunk, the resulting vector is an average of everything in it and matches almost nothing precisely. Splitting documents into smaller chunks (a few hundred characters, often with slight overlap between chunks) keeps each vector focused on one specific idea.
Chunk too small, though, and you lose surrounding context the model would need to answer correctly β chunk size is a real tuning knob, not a fixed constant.
const results = await index.query({
vector: queryVector,
filter: { department: 'HR' }
});AI logic processed successfully.
8Narrowing Search with Metadata Filtering
Pure vector similarity search can surface a technically-relevant chunk from the wrong department or an outdated policy version. Metadata filtering fixes this by combining the similarity search with a hard constraint β search only vectors tagged department: 'HR', for instance β before ranking by closeness.
This is the mechanism that lets one shared vector index serve multiple teams or tenants safely, since each query is scoped to only the data it's allowed to see.
// Example
console.log("Running AI concept...");AI logic processed successfully.
9What a Complete RAG Pipeline Looks Like
End to end: documents are chunked, embedded, and upserted into a vector store ahead of time; a user question is embedded the same way at query time; the top matching chunks are retrieved (optionally filtered by metadata); and the augmented prompt β retrieved context plus the question β is sent to the LLM for a grounded answer.
Every production RAG system is a variation on these same five steps, whether it's a simple FAQ bot or a multi-tenant enterprise search assistant.
Streaming Next
AI logic processed successfully.
10Next: Streaming the Answer
A RAG query typically has to retrieve context, then wait for a full LLM completion, which adds up to noticeable latency for the user staring at a blank screen. The next lesson covers streaming responses token-by-token as they're generated, so users start reading the answer immediately instead of waiting for the whole thing.
// Example
console.log("Running AI concept...");AI logic processed successfully.
11Step-by-Step Breakdown
LLMs are limited by their training data. To give them access to YOUR private files, we use Retrieval-Augmented Generation (RAG).
First, we convert text into 'Embeddings'βnumerical vectors that represent the meaning of the words in a mathematical space.
These vectors are stored in a 'Vector Database'. This allows us to find documents that are 'semantically similar' to a user's question.
Checkpoint: What is the primary purpose of an 'Embedding' in a RAG system?
- βTo compress text for cheaper storage
- βTo represent the semantic meaning as a numerical vector
When a user asks a question, we search the Vector DB for relevant chunks, then 'augment' the prompt with that retrieved context.
This process ensures the AI answers using your specific data instead of hallucinating based on its general knowledge.
Checkpoint: Why do we use a Vector Database instead of a regular SQL database for RAG?
- βSQL is too slow for text
- βVector DBs can find 'meaning' rather than just exact keyword matches
Data needs to be 'Chunked' before embedding. If a document is too long, the embedding loses detail. Smaller chunks provide more precision.
We often use 'Metadata Filtering' to narrow down searches. For example, only search documents from the 'Human Resources' department.
Checkpoint: What is 'Chunking' in the context of RAG?
- βMerging multiple files into one
- βSplitting long documents into smaller, manageable pieces for embedding
RAG mastered! Your AI can now 'read' your documents and provide accurate, data-driven answers.
Next, we'll learn how to deliver these answers in real-time using 'Streaming'.
Build a Real Grounded Prompt. Finish building a RAG-style prompt that forces the model to answer only from retrieved context.
Level Up π
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Announce Retrieval Latency to Screen Reader Users
A RAG query involves an extra network round-trip to the vector database before the LLM even starts generating, so a 'Searching your documents...' status announced via aria-live gives non-visual users the same sense of progress a sighted user gets from a loading spinner.
<div aria-live="polite" role="status">Searching your documentsβ¦</div>SEO Implications
- 1
Retrieved Context Is Ephemeral, Not a Page to Index
The documents a RAG system retrieves and pastes into a prompt exist only inside that one API request β they are never rendered as their own indexable URL, so there is no duplicate-content risk from the retrieval step itself; the only page that needs unique, crawlable prose is this documentation page explaining the architecture.
Best Practices
Cite the Source Chunk, Not Just the Answer
Return the metadata (document name, page, department) alongside each retrieved chunk and surface it in the UI as a citation. This lets users verify the answer against the source and makes it obvious when the retrieval step, not the model, is the cause of a wrong answer.
Re-Embed When You Change Embedding Models
Vectors from two different embedding models are not comparable in the same index. Switching from text-embedding-3-small to a newer model requires re-embedding every stored document, not just new ones going forward.
Frequent Bugs
Embedding an entire large document as one chunk instead of splitting it.
A single embedding vector for a 50-page file averages out all its content, so it rarely matches specific user questions well. Split documents into a few hundred characters per chunk (with modest overlap) before embedding, and embed each chunk separately.
Real-World Examples
An Internal HR Policy Bot
An HR chatbot embeds the company handbook once, then for every employee question retrieves the top 3 matching policy chunks filtered to metadata.department = 'HR' before answering, so it never leaks finance or engineering documents into an HR conversation.
const results = await index.query({
vector: queryVector,
filter: { department: 'HR' },
topK: 3
});