Applied LLMs & GenAI ๐Ÿค–

โ€” system design side of AI interviews โ€”
#placements-2026 #RAG #fine-tuning #high-priority

โ‘ Prompt Engineering Techniques

how much "scaffolding" do we give the model? Zero-shot "Translate: Hi" Few-shot 2 examples + query CoT "let's think step by step" ReAct reasoning + tool calls increasing capability to handle complex, multi-step, tool-using tasks โ†’ ReAct loop, zoomed in: Thought Action Observation Ans? loop back if not confident yet
fig 1 โ€” from zero-shot to agentic ReAct loops
interview line: "CoT improves reasoning; ReAct grounds reasoning in real tool outputs โ€” that's what turns an LLM into an agent"

โ‘กEmbeddings & Vector Databases

An embedding = a piece of text squeezed into a dense numeric vector where "semantic closeness" โ‰ˆ "geometric closeness." Vector DBs store millions of these and answer: "give me the k nearest vectors to this query."

Distance / similarity metrics โ€”

Cosine Similarity ฮธ measures angle only โ€” ignores length Dot Product angle AND magnitude both matter Euclidean Distance straight-line dist raw "as the crow flies" gap
fig 2 โ€” cosine ignores scale, dot product rewards larger/confident vectors, euclidean = literal gap

Vector Indexing (how to search millions of vectors fast) โ€”

Flat (brute-force) compares query to EVERY vector โ€” exact, but O(n), slow at scale IVF (Inverted File) cluster first (k-means); only search nearby clusters ("probe") HNSW (graph) layered "skip-list"-like graph โ€” hop greedily toward the query, top-down
fig 3 โ€” exact search โ†’ clustered search โ†’ graph-navigated search (speed โ†‘, exactness trade-off โ†‘)
IndexIdeaTrade-off
FlatLinear scan, exact k-NN100% accurate, but slow / doesn't scale past ~1M vectors
IVFk-means cluster centroids; only probe nearest few clustersMuch faster; accuracy depends on nprobe (clusters searched)
HNSWMulti-layer proximity graph; greedy hop from sparse top layer down to dense bottom layerVery fast + high recall; more memory, slower to build/insert

โ‘ขRAG Pipeline Architecture

RAG = Retrieval-Augmented Generation: fetch relevant external knowledge at query time and feed it to the LLM, instead of relying only on frozen training-time knowledge.

Docs chunked Embed โ†’ Vector DB Retrieve top-k (~20) Re-rank โ†’ top 3-5 Synthesize context + prompt โ†’ LLM offline (index build) once, then online (query time) every request Final Answer
fig 4 โ€” retrieve wide (recall), re-rank narrow (precision), then synthesize

Chunking strategies โ€” how you split docs before embedding decides retrieval quality:

Why re-ranking? Vector search is fast but approximate โ€” it optimizes recall over a huge corpus using cheap similarity. A re-ranker (usually a cross-encoder that jointly reads query+passage) is slower but far more accurate, applied only to the top-k shortlist to pick the best few for the final prompt.

๐Ÿ“ RAGAS evaluation framework
  • Faithfulness โ€” is the generated answer actually grounded in the retrieved context (no hallucinated facts)?
  • Answer Relevance โ€” does the answer actually address the user's question (not just faithful, but on-topic)?
  • (also commonly paired with) Context Precision / Context Recall โ€” did retrieval fetch the right, and only the right, passages?
gotcha: faithfulness โ‰  relevance โ€” an answer can be 100% grounded in context yet not actually answer the question asked!

โ‘ฃCustomization Decision Framework

Interview favorite: "Would you prompt-engineer, RAG, fine-tune, or pre-train?" โ€” answer by asking what's actually missing.

what's missing? just phrasing/format? Prompt Engineering cheapest, try first, always missing knowledge / fresh facts? RAG external / dynamic / cited knowledge missing behavior/style/domain skill? Fine-tuning (PEFT) tone, format, task specialization new language / fundamentally new capability? Pre-train rare, $$$, from scratch โ†‘ cost / data / infra needed increases left โ†’ right most real products stack these: prompt-engineered + RAG + a small LoRA adapter
fig 5 โ€” pick the cheapest lever that actually fixes the gap

PEFT / LoRA / QLoRA intuition โ€” full fine-tuning updates all model weights (huge, expensive, needs lots of GPU memory). PEFT (Parameter-Efficient Fine-Tuning) freezes the base model and trains a tiny number of extra parameters instead.

one-liner: "LoRA = train a small side-patch instead of the whole model; QLoRA = do that on a compressed (4-bit) base model to fit on less GPU"

โ‘คModel Compression โ€” Quantization

Quantization = storing weights with fewer bits โ†’ smaller model, faster inference, small accuracy loss.

same weight value, shown at different precisions: FP32 32 bits โ€” full precision FP16 / BF16 half size, minimal loss INT8 1/4 size, small accuracy dip INT4 1/8 size, noticeable dip size โ†“ + speed โ†‘ + accuracy risk โ†‘ โ†’
fig 6 โ€” the precision/size/speed trade-off ladder

โ‘ฅTooling Landscape

LangChainLlamaIndex
Core focus General orchestration โ€” chains, agents, tool-calling, memory, many integrations Data-centric โ€” ingesting, indexing, and retrieving your documents efficiently
Mental model "Lego blocks" to wire an LLM to tools, APIs, and multi-step logic/agents "Smart librarian" โ€” specializes in turning messy docs into query-ready indexes
When to reach for it Complex agent workflows, multiple tools, conversational chains RAG-heavy apps where retrieval quality over your own data is the bottleneck

In practice teams often combine both โ€” LlamaIndex for the retrieval/indexing layer, LangChain (or a custom agent loop) for orchestration on top.

โ‘ฆEnd-to-End RAG Pipeline (with re-ranker) โ€” pseudo-code

Two phases: offline indexing (once) and online query (every request).

# ---- OFFLINE: build the index ---- for doc in corpus: chunks = chunk_document(doc, size=512, overlap=50) for chunk in chunks: vector = embed_model.encode(chunk.text) vector_db.upsert(id=chunk.id, vector=vector, metadata={text: chunk.text, source: doc.id}) # ---- ONLINE: answer a user query ---- def rag_answer(user_query): q_vector = embed_model.encode(user_query) # 1. Retrieve โ€” cast a wide, cheap net candidates = vector_db.search(q_vector, top_k=20) # 2. Re-rank โ€” slow but precise cross-encoder scoring scored = [(c, reranker.score(user_query, c.text)) for c in candidates] top_chunks = sort_desc(scored)[:5] # 3. Synthesize context + prompt context = join([c.text for c in top_chunks], sep="\n---\n") prompt = build_prompt(system=RAG_SYSTEM_PROMPT, context=context, question=user_query) # 4. Generate answer = llm.generate(prompt) # 5. (optional) Evaluate before returning / logging metrics = ragas_eval(question=user_query, answer=answer, context=top_chunks) # faithfulness, relevance return answer, metrics
๐Ÿงฉ why each step earns its place
  • Retrieve wide (k=20) โ€” vector search alone is imprecise; over-fetch to protect recall.
  • Re-rank narrow (โ†’5) โ€” cross-encoder is too slow to run on the whole corpus, but cheap on 20 candidates; boosts precision right before it matters most.
  • Synthesize โ€” dedupe/order chunks, respect the LLM's context window, cite sources if needed.
  • Evaluate โ€” RAGAS-style scoring in the loop (or offline on a test set) catches hallucination/off-topic drift before it ships.