Implementing RAG Applications

1. Understanding RAG Architecture

User question
   ↓
Embed query  ──→ Pinecone query (top-K)
                       ↓
              Retrieved context chunks
                       ↓
          LLM prompt = [system] + context + question
                       ↓
                LLM response
      
StageComponent
IngestChunk → embed → upsert
RetrieveEmbed query → Pinecone
AugmentInject chunks into prompt
GenerateLLM produces grounded answer

2. Chunking Documents

Example: Token-Based Chunker

import tiktoken
enc = tiktoken.get_encoding("cl100k_base")

def chunk(text, size=400, overlap=50):
    tokens = enc.encode(text)
    out = []
    for i in range(0, len(tokens), size - overlap):
        out.append(enc.decode(tokens[i:i+size]))
    return out
StrategyUse Case
Fixed-size tokensGeneral text
Sentence/paragraphPreserve semantic units
Semantic chunkingLLM-based boundary detection
RecursiveLangChain RecursiveCharacterTextSplitter

3. Generating Embeddings

Example: Embed Chunks

chunks = chunk(document_text)
embs = embed_batch(chunks)  # OpenAI / Pinecone Inference

4. Upserting Document Chunks

Example: Chunks → Pinecone

vectors = [{
    "id": f"{doc_id}_chunk_{i}",
    "values": emb,
    "metadata": {"doc_id": doc_id, "chunk": i, "text": text},
} for i, (text, emb) in enumerate(zip(chunks, embs))]
index.upsert(vectors=vectors, namespace="kb")
ParamRecommendation
top_k5–10 for typical RAG
include_metadataTrue (need chunk text)
FilterTenant ID, access controls

6. Retrieving Relevant Context

Example: Retrieve

res = index.query(vector=embed(query), top_k=8,
    include_metadata=True, namespace="kb")
context = "\n\n".join(m["metadata"]["text"] for m in res["matches"])

7. Constructing LLM Prompts

Example: Prompt Template

prompt = f"""Answer using only the context below. If unknown, say so.

Context:
{context}

Question: {query}
Answer:"""
ans = llm.complete(prompt)

8. Implementing Reranking

Example: Pinecone Rerank

candidates = [m["metadata"]["text"] for m in res["matches"]]
r = pc.inference.rerank(
    model="pinecone-rerank-v0",
    query=query,
    documents=candidates,
    top_n=5,
)
top = [candidates[d["index"]] for d in r.data]
RerankerProvider
pinecone-rerank-v0Pinecone Inference
bge-reranker-v2-m3HuggingFace
Cohere rerank-v3Cohere API

9. Using Metadata Filtering

Example: Filter for ACL

res = index.query(vector=qv, top_k=8,
    filter={"$and": [
        {"tenant_id": user.tenant_id},
        {"visibility": {"$in": ["public", user.role]}},
    ]})

10. Handling Multi-Query Retrieval

Example: Query Expansion

variants = llm.expand(query, n=3)  # ask LLM for 3 paraphrases
all_matches = []
for v in [query] + variants:
    all_matches += index.query(vector=embed(v), top_k=5)["matches"]
# dedupe by id, sort by best score
unique = {m["id"]: m for m in all_matches}.values()

11. Implementing Hybrid RAG

StepDetail
Index metricdotproduct
Per chunkDense + sparse vectors
QueryWeighted dense+sparse
WinBetter on jargon/IDs vs pure dense

12. Optimizing Retrieval Quality

LeverEffect
Smaller chunks (200-400)More precise retrieval
Overlap (10-20%)Don't miss context at boundary
Reranker+5–15% recall@5
Hybrid searchBetter on exact terms
Query rewritingLLM cleans ambiguous queries