New batches starting this week · Limited seats

LangChain Interview Questions & Answers for Freshers 2026 (155 Q&A)

155 LangChain interview questions with detailed, interview-ready answers for 2026 — covering foundations, LCEL, memory, RAG, agents & tools, LangGraph, LangSmith evaluation, production, and live-coding & system-design rounds. For GenAI, LLM and Agentic AI roles.

Cloud Soft Solutions — India's No.1 cloud placement institute in Hyderabad with 5,500+ placements (AWS, Azure, DevOps, GCP)
Last updated · 52 min read · 11,453 words

This handbook contains 155 LangChain interview questions with detailed, interview-ready answers, prepared by the training faculty at Cloud Soft Solutions for candidates targeting GenAI Engineer, AI Application Engineer, LLM Engineer and Agentic AI roles. The questions are ordered the way real interviews progress — foundations first, then composition, retrieval, agents, orchestration, evaluation, and finally production, coding and system-design rounds. Sections 1–5 are what a 0–2 year candidate is expected to own; Sections 6–9 separate mid-level from senior; Sections 10–12 are the live-coding, whiteboard and rapid-fire rounds.

Four rules for answering LangChain questions well

  • Name the trade-off. Almost every LangChain question has one — chunk size, agent versus chain, hosted versus self-hosted. Candidates who state the trade-off out-score candidates who state a fact.
  • Reach for the current API. Saying ConversationBufferMemory or LLMChain first signals tutorial-era knowledge. Say LCEL, with_structured_output, LangGraph checkpointers.
  • Talk about measurement. "I would add an eval dataset and measure recall@k" is the single most credible sentence you can say in a RAG interview.
  • Talk about failure. Every senior question is secretly asking what breaks in production and what you did about it.

Pair this with our RAG interview questions, AI & ML interview questions, and the LangGraph interview questions and the Agentic AI Engineer roadmap.

01 · LangChain Foundations

Framework fundamentals, architecture and the package ecosystem — the warm-up round every interviewer starts with.

1. What is LangChain and what problem does it actually solve?

LangChain is an open-source orchestration framework for building applications on top of large language models. A raw LLM API call gives you one thing: text in, text out. Real applications need much more — prompt templating, conversation state, access to private data, tool/function calling, multi-step reasoning, retries, streaming, caching and observability.

LangChain supplies standard, swappable abstractions for all of that: models, prompts, output parsers, retrievers, tools, agents and memory. The practical payoff is provider independence and composability — you can move from OpenAI to Anthropic to a self-hosted Llama model by changing one constructor, and you can wire components into pipelines without writing bespoke glue code each time.

2. Explain the LangChain package structure. Why was the monolith split up?

Early LangChain was a single package where a small core was buried under hundreds of third-party integrations, so one broken integration dependency could break everybody. The ecosystem is now modular:

  • langchain-core — the base abstractions and the LCEL Runnable interface. Lightweight, minimal dependencies, very stable API.
  • langchain — the cognitive architecture: chains, agents, retrieval strategies that are generic across providers.
  • langchain-community — community-maintained third-party integrations (vector stores, loaders, tools).
  • Partner packages — langchain-openai, langchain-anthropic, langchain-google-genai, langchain-aws etc., versioned and tested independently.
  • langgraph — stateful, graph-based orchestration for agents and multi-actor workflows.
  • langserve — deploy any chain as a REST API.
  • langsmith — tracing, evaluation and monitoring SDK (works with or without LangChain).

In interviews, mention that you pin partner packages in production because integration APIs move faster than core.

3. What is the difference between an LLM and a ChatModel in LangChain?

LLM is the legacy string-in/string-out interface (llm.invoke("text") returns a string). ChatModel takes a list of structured messages and returns an AIMessage. Every modern provider is chat-native, so ChatModel is the correct default; the completion-style interface is retained mostly for older or local models.

ChatModels are what support system prompts, multi-turn history, tool calling, multimodal content blocks and structured output — none of which map cleanly onto a bare string interface.

4. Describe the message types in LangChain.

  • SystemMessage — role, rules and persona; sets behaviour for the whole conversation.
  • HumanMessage — end-user input. Content may be a string or a list of content blocks (text + images) for multimodal models.
  • AIMessage — model output. Carries content, plus tool_calls, usage_metadata (token counts) and response_metadata.
  • ToolMessage — the result of executing a tool, linked back by tool_call_id. This is what closes the loop in an agent turn.
  • AIMessageChunk — a streamed partial AIMessage; chunks support + so they can be added together into a full message.

5. What is a PromptTemplate and why not just use f-strings?

A PromptTemplate is a declarative, reusable prompt with typed input variables. Over an f-string it gives you: validation of required variables, partial application, composition with other templates, serialisation to disk or a prompt registry, and — critically — it is a Runnable, so it can be piped into a model as part of an LCEL chain.

ChatPromptTemplate is the chat-model equivalent, built from role/message tuples, and MessagesPlaceholder reserves a slot where a list of prior messages (history, agent scratchpad) gets injected at runtime.

6. What is few-shot prompting in LangChain and how do you make it dynamic?

Few-shot prompting embeds worked examples in the prompt so the model infers the pattern. LangChain provides FewShotPromptTemplate and FewShotChatMessagePromptTemplate.

Making it dynamic is the interesting part: instead of hard-coding examples, plug in an ExampleSelector. SemanticSimilarityExampleSelector embeds all examples into a vector store and picks the k most similar to the current input; LengthBasedExampleSelector picks as many as fit the context budget; MaxMarginalRelevanceExampleSelector balances relevance with diversity. This keeps prompts short while making them maximally relevant per query.

7. What are output parsers, and when do you use structured output instead?

Output parsers convert raw model text into usable Python objects: StrOutputParser, JsonOutputParser, PydanticOutputParser, CommaSeparatedListOutputParser, XMLOutputParser, DatetimeOutputParser. They also expose get_format_instructions(), which you inject into the prompt so the model knows the required shape.

For models that support native tool/JSON-schema calling, prefer llm.with_structured_output(MySchema). It constrains generation at the provider level rather than parsing after the fact, so it is dramatically more reliable. Parsers remain useful for models without that capability, and OutputFixingParser / RetryOutputParser can send malformed output back to an LLM to be repaired.

8. How do you handle a model that returns invalid JSON in production?

Layered defence: (1) use with_structured_output() with a Pydantic schema so the provider enforces the schema; (2) lower temperature for extraction tasks; (3) wrap with OutputFixingParser so a second, cheap model repairs malformed output; (4) attach .with_retry() for transient failures; (5) validate with Pydantic and raise typed errors your application can act on; (6) log every failure to LangSmith so you can measure the parse-failure rate rather than guessing at it.

9. What are callbacks in LangChain?

Callbacks are the hook system for the lifecycle of a run — on_llm_start, on_llm_new_token, on_llm_end, on_chain_error, on_tool_start, on_retriever_end and so on. They power streaming to a UI, token counting and cost tracking, logging, and tracing to LangSmith.

Handlers can be passed constructor-scoped (attached to one component for its lifetime) or request-scoped (passed in config for a single invocation, and inherited by all children of that run).

10. What is a Document in LangChain?

A Document is the unit of text that moves through loaders, splitters, vector stores and retrievers. It has two fields: page_content (a string) and metadata (a dict). Metadata is not decoration — it carries source URI, page number, section heading, tenant ID, timestamps and access tags, and it is what makes citation, filtering and multi-tenant isolation possible downstream. Design your metadata schema before you index anything.

11. How does LangChain support multimodal input?

Pass a list of content blocks as the message content rather than a plain string — for example a text block plus an image block referencing a URL or base64 data. The provider integration translates that into the vendor-specific payload. The same chain code then works across providers that support vision, which is exactly the abstraction benefit LangChain exists to give you.

12. What is caching in LangChain and when is it appropriate?

LangChain can cache model responses so identical prompts do not incur repeat cost and latency — InMemoryCache for a single process, SQLiteCache for local persistence, Redis/Momento/GPTCache for distributed deployments. There is also a semantic cache which matches on embedding similarity rather than exact string equality, catching paraphrased repeats.

Appropriate for deterministic, high-repetition workloads (classification, extraction, FAQ). Inappropriate where freshness matters, where output should vary creatively, or where a cached response could leak one tenant's data to another — always key the cache by tenant.

13. What is the role of langchain-community versus a partner package?

langchain-community holds the long tail of integrations maintained by contributors; quality and test coverage vary. Partner packages (langchain-openai, langchain-anthropic, langchain-mongodb …) are co-maintained with the vendor, released on their own cadence and treated as first-class. For anything on a critical production path, prefer the partner package and pin the version.

14. How do you count tokens and estimate cost?

Modern chat models return usage_metadata on the AIMessage with input, output and total token counts, including cache-read tokens where the provider supports prompt caching. For pre-flight estimation use the model's tokenizer via get_num_tokens_from_messages(). For aggregate reporting, attach a callback handler or read cost directly from LangSmith traces, which roll up per run, per chain and per project.

15. What is LangServe?

LangServe deploys any Runnable as a FastAPI application with automatically generated /invoke, /batch, /stream and /stream_log endpoints, plus input/output schemas inferred from the chain and a built-in playground UI. It is the fastest path from a working chain to a production HTTP service. For agent workloads with long-running state, LangGraph Platform / a custom FastAPI server is the more common choice today.

02 · LCEL, Runnables and Chains

Composition is the heart of modern LangChain. Expect at least three questions from this section in any serious interview.

16. What is LCEL and why was it introduced?

LCEL (LangChain Expression Language) is a declarative composition syntax where components are combined with the pipe operator: prompt | model | parser. Every component implements the Runnable protocol, so every composed chain automatically inherits:

  • sync and async APIs (invoke/ainvoke)
  • batching with parallel execution (batch)
  • token-level streaming (stream, astream, astream_events)
  • automatic parallelism for independent branches
  • retries, fallbacks, and configurable fields
  • full tracing in LangSmith with no extra code

The legacy Chain classes (LLMChain, SequentialChain) had to implement each of those capabilities separately and inconsistently. LCEL made them properties of the interface instead.

17. List the core Runnable primitives and what each is for.

  • RunnableSequence — created by |; runs steps in order, output of one feeding the next.
  • RunnableParallel — a dict of runnables executed concurrently, returning a dict of results.
  • RunnablePassthrough — forwards input unchanged; .assign() adds computed keys while preserving the original ones.
  • RunnableLambda — lifts any Python function into a Runnable (plain functions are auto-coerced inside a pipe).
  • RunnableBranch — conditional routing on (condition, runnable) pairs with a default.
  • RunnableWithFallbacks — via .with_fallbacks([...]), switches to an alternate chain on error.
  • RunnableConfigurableFields / Alternatives — expose parameters (temperature, model choice) that can be set per request.

18. Write a minimal RAG chain in LCEL.

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser

prompt = ChatPromptTemplate.from_template(
    "Answer only from the context.\n\nContext:\n{context}\n\nQuestion: {question}"
)

def format_docs(docs):
    return "\n\n".join(d.page_content for d in docs)

chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

chain.invoke("What is the refund window?")

The leading dict is an implicit RunnableParallel: retrieval and passthrough run concurrently, then both keys land in the prompt.

19. How does streaming work in LCEL, and what breaks it?

Calling .stream() on a chain streams if every component in the path supports incremental output. Models yield AIMessageChunks; StrOutputParser and JsonOutputParser are streaming-aware (the JSON parser emits progressively completed partial objects).

Streaming breaks at any component that must see the whole input before producing output — a custom RunnableLambda that consumes the full string, a non-streaming output parser, or a retriever placed after the model. Everything upstream still streams internally, but the user sees output only after that blocking step completes.

For fine-grained UI events (which tool started, which documents were retrieved, tokens per node) use astream_events(), which emits a typed event stream for every nested component.

20. Difference between invoke, batch and stream, and their async variants?

invoke — one input, one output, blocking. batch — a list of inputs run concurrently with a configurable max_concurrency, returning results in input order; far cheaper in wall-clock time than a Python loop. stream — yields output chunks as they are produced.

Each has an a-prefixed coroutine version (ainvoke, abatch, astream) for use inside an event loop. In a web server, always use the async variants — the sync ones will block the event loop or force thread-pool offloading. Also note batch_as_completed() when you want results as soon as each finishes rather than in order.

21. How do you add retries and fallbacks to a chain?

robust = (
    primary_llm
      .with_retry(stop_after_attempt=3, wait_exponential_jitter=True)
      .with_fallbacks([backup_llm, cheap_local_llm])
)

with_retry handles transient faults (rate limits, 5xx, timeouts) on the same component with exponential backoff. with_fallbacks handles persistent faults by switching to a different component — a second provider, a smaller model, or a canned response. You can also specify exceptions_to_handle so you only fall back on the errors you intend to.

22. What is RunnablePassthrough.assign() used for?

It adds new keys to a dict flowing through the chain while keeping existing keys intact — essential when a later step needs both the original input and something derived from it. Typical use in RAG with citations:

chain = (
    RunnablePassthrough.assign(docs=lambda x: retriever.invoke(x["question"]))
    | RunnablePassthrough.assign(
        answer=(prompt | llm | StrOutputParser())
      )
)
# result has question, docs AND answer -> you can render sources

23. How do you make part of a chain configurable at runtime?

from langchain_core.runnables import ConfigurableField

llm = ChatOpenAI(temperature=0).configurable_fields(
    temperature=ConfigurableField(id="temperature", name="Sampling temp")
)
chain.invoke(x, config={"configurable": {"temperature": 0.9}})

configurable_alternatives() goes further and swaps whole components — letting one deployed chain serve a premium model for paid users and a cheaper model for free tier, chosen per request.

24. How do you pass metadata, tags and run names for observability?

Through the config argument: chain.invoke(x, config={"run_name": "support_rag", "tags": ["prod", "tier-1"], "metadata": {"user_id": uid, "tenant": t}}). These propagate to every child run and become filterable dimensions in LangSmith — which is how you answer "why is latency bad for tenant X" without adding print statements.

25. What is .bind() and give a real use case.

.bind() pre-sets keyword arguments on a Runnable so callers do not have to supply them. Classic uses: attaching tools to a model (llm.bind_tools([...])), forcing a stop sequence (llm.bind(stop=["\nObservation"])), or pinning response_format. It returns a new Runnable — the original is untouched, so it composes safely.

26. How do you write a custom Runnable?

Two routes. For simple transformations, wrap a function: RunnableLambda(my_func) — and note that the function receives exactly one argument, so use a dict for multiple inputs. For components needing custom streaming, config handling or async behaviour, subclass Runnable and implement invoke, plus optionally ainvoke, stream and batch. Use @chain as a decorator shorthand to turn a function into a traced Runnable.

27. Legacy chains vs LCEL: how would you migrate an existing codebase?

Map the old class to its LCEL equivalent: LLMChain → prompt | llm | parser; SequentialChain → a piped sequence with RunnablePassthrough.assign() to thread intermediate values; RetrievalQA → the explicit RAG chain shown earlier; ConversationChain → RunnableWithMessageHistory or a LangGraph node with a checkpointer; agents → create_tool_calling_agent or LangGraph.

Migrate incrementally: LCEL chains and legacy chains interoperate because legacy chains are themselves Runnables. Prioritise anything where you want streaming or tracing, since those are the biggest wins.

03 · Memory and Conversation State

How chat applications remember — and how the answer to this question changed with LangGraph.

28. How does LangChain handle conversational memory today?

LLM APIs are stateless, so "memory" means re-sending relevant prior turns on each call. The classic ConversationBufferMemory family is deprecated. The two current approaches are:

  • RunnableWithMessageHistory — wraps any chain, loads history by session_id from a store (Redis, Postgres, DynamoDB, file), injects it into a MessagesPlaceholder, and appends the new turn afterwards.
  • LangGraph checkpointers — state (including the message list) is persisted per thread_id after every node. This is the recommended path because it also gives you resumability, time-travel and human-in-the-loop.

29. Compare buffer, window, summary and summary-buffer memory strategies.

  • Buffer — keep everything. Perfect recall, unbounded token growth; fine for short sessions.
  • Window (last k turns) — bounded and cheap, but forgets earlier context abruptly.
  • Summary — an LLM condenses old turns into a running summary. Constant-ish size, but lossy and adds a model call per turn.
  • Summary + buffer — recent turns verbatim, older turns summarised. The best default for long-running assistants.
  • Vector-store memory — embed every turn and retrieve only the semantically relevant ones. Scales to very long histories and is effectively RAG over the conversation itself.

30. How do you trim message history correctly?

Use trim_messages() from langchain_core.messages, which trims by token count using the model's own tokenizer rather than by naive character length. Key options: strategy="last" to keep the most recent turns, include_system=True so the system prompt is never dropped, and start_on="human" so you never begin the window with an orphaned AI or Tool message — an invalid sequence that many providers reject.

31. What is the difference between session_id and thread_id?

Conceptually the same idea in different APIs. session_id is the key RunnableWithMessageHistory uses to look up a BaseChatMessageHistory. thread_id is the key a LangGraph checkpointer uses to load and save the whole graph state, of which messages are one field. LangGraph additionally supports checkpoint_id to rewind to a specific point in a thread.

32. How would you implement long-term memory across sessions?

Separate short-term (within-thread messages, handled by a checkpointer) from long-term (facts about the user that should survive every session). For long-term memory: after each conversation, run an extraction chain that pulls durable facts and preferences into a structured schema, write them to a store (LangGraph's Store API, a vector index or a plain database keyed by user ID), and retrieve the relevant subset into the system prompt at the start of each new thread. Add de-duplication and an update/expire policy or the memory store will slowly fill with stale contradictions.

33. How do you persist chat history in a multi-user production app?

Never in process memory — it dies with the pod and cannot be shared across replicas. Use a backed implementation such as RedisChatMessageHistory, PostgresChatMessageHistory or DynamoDBChatMessageHistory. Key by (tenant_id, user_id, session_id), enforce authorisation at the data layer rather than trusting the session ID from the client, set TTLs to satisfy retention policy, and encrypt at rest since chat logs frequently contain PII.

34. What are the failure modes of naive memory, and how do you mitigate them?

Context window overflow and cost blow-up (mitigate with trimming/summarisation); "lost in the middle" degradation where the model ignores content buried mid-prompt (mitigate by placing critical instructions and recent turns at the edges); summary drift where repeated summarisation compounds errors (mitigate by summarising from the original transcript rather than the previous summary); and prompt-injection persistence, where malicious instructions written into history keep re-executing (mitigate by sanitising stored content and keeping system instructions outside the mutable history).

35. How do you support editing or regenerating an earlier turn?

With LangGraph checkpointers this is native: fetch the checkpoint history for the thread, choose the checkpoint before the message being edited, and invoke with that checkpoint_id — the graph forks from that state, leaving the original branch intact. Implementing this over a flat message list requires you to hand-roll versioning, which is a good argument to make for LangGraph in an interview.

04 · Data Ingestion, Splitting and Vector Stores

The unglamorous half of RAG, where most real-world quality problems actually originate.

36. Walk through the document ingestion pipeline.

Load → Split → Embed → Store → Index. Loaders read from a source (PDF, HTML, Confluence, S3, SQL, Notion) into Document objects with metadata. Splitters break them into retrievable chunks. An embedding model maps chunks to vectors. A vector store persists vectors plus metadata and builds an ANN index. Add a document-ID scheme and hash-based change detection at this stage or re-ingestion will duplicate everything.

37. Why do we chunk documents at all?

Four reasons: (1) context windows are finite and expensive; (2) embeddings of very long texts wash out into an average that matches nothing specifically, hurting retrieval precision; (3) smaller units let you cite precisely; (4) irrelevant surrounding text degrades answer quality and invites distraction. Chunking is a precision/context trade-off — too small loses meaning, too large loses specificity.

38. Compare the main text splitters.

  • CharacterTextSplitter — splits on a single separator. Crude, rarely the right choice.
  • RecursiveCharacterTextSplitter — tries a hierarchy of separators (paragraph → line → sentence → word) so it breaks at the most natural boundary that fits. The sensible default.
  • Language-aware splitters — from_language() supplies syntax-aware separators for Python, JS, Markdown, HTML, LaTeX; keeps functions and classes intact.
  • MarkdownHeaderTextSplitter / HTMLHeaderTextSplitter — split on document structure and promote headers into metadata, which is excellent for filtered retrieval.
  • TokenTextSplitter — splits on model tokens, giving exact budget control.
  • SemanticChunker — embeds sentences and cuts where semantic distance spikes, producing topically coherent chunks at higher ingestion cost.

39. How do you choose chunk size and overlap?

There is no universal answer — it depends on document type and query style, and you should tune it empirically against an evaluation set. Reasonable starting points: 800–1,200 characters (or ~200–400 tokens) with 10–20% overlap for prose; larger chunks for narrative documents where context matters; smaller for dense reference material and tables. Overlap exists so a fact straddling a boundary is not lost. The professional answer is: build a golden question set, sweep the parameters, and measure recall@k — not guess.

40. What are embeddings and what should you watch out for?

Embeddings map text to dense vectors where geometric proximity approximates semantic similarity. Key considerations: dimensionality versus storage and speed; the model's own token limit; consistency — queries and documents must be embedded by the same model, and changing the model means re-indexing everything; domain fit, since general-purpose embeddings underperform on specialised jargon; multilingual capability; and cost/latency at ingestion scale. Note the distinction between embed_documents() (batch, for indexing) and embed_query() (single, for search) — some models use different prefixes or instructions for each.

41. Compare FAISS, Chroma, pgvector and a managed store like Pinecone.

  • FAISS — an in-process library, extremely fast, no server. Great for prototypes, embedded use and read-heavy workloads that fit in RAM; you manage persistence and there is no built-in multi-tenancy or horizontal scaling.
  • Chroma — developer-friendly, runs embedded or client/server, good metadata filtering; ideal for small-to-mid workloads.
  • pgvector — vectors inside PostgreSQL. Unbeatable when you already run Postgres: transactional consistency, joins against relational data, one backup story, real row-level security.
  • Pinecone / Weaviate / Milvus / Qdrant — purpose-built vector databases with sharding, replication, hybrid search, namespaces and managed operations. Choose when scale, uptime or hybrid search demands exceed what a library can do.

Because LangChain exposes a common VectorStore interface, swapping between them is a small code change — so start simple and migrate on evidence.

42. Explain similarity search vs MMR.

Plain similarity search returns the top-k nearest vectors, which on a redundant corpus often means five near-duplicate chunks. Maximal Marginal Relevance re-ranks candidates to balance relevance against diversity, controlled by lambda_mult (1.0 = pure relevance, 0.0 = pure diversity), typically fetching a larger candidate pool via fetch_k first. MMR is the better default for summarisation-style questions and any corpus with heavy duplication.

43. What distance metrics are used, and does the choice matter?

Cosine similarity (angle only), dot product (angle and magnitude) and Euclidean/L2 distance. For normalised vectors, cosine and dot product rank identically. What matters is matching the metric the embedding model was trained with — using L2 on a model trained for cosine silently degrades retrieval quality, and it is a subtle bug because nothing errors out.

44. How do you keep a vector index in sync with changing source data?

Use LangChain's indexing API (index() with a RecordManager). It hashes each document, tracks what has been written, and supports cleanup modes: incremental deletes old versions of changed source documents, full deletes anything absent from the current batch. This gives you idempotent re-ingestion — re-running the pipeline does not duplicate content, and deletions at source propagate to the index. Without it, teams end up with indexes full of stale, contradictory chunks that quietly poison answers.

45. How do you handle tables, images and scanned PDFs in ingestion?

Naive PDF text extraction destroys tables and ignores images. Options: layout-aware parsers (Unstructured, LlamaParse, Azure Document Intelligence) that emit tables as HTML/Markdown; OCR for scanned pages; and the multi-vector pattern — generate an LLM summary of each table or image, embed the summary for retrieval, but return the original element to the model at answer time. For charts and diagrams, a vision model can produce the searchable description.

46. What metadata should you attach to chunks, and why?

Source URI and title (citation), page/section/heading (precise references and structural filtering), created/updated timestamps (recency filtering and freshness scoring), document type and language, tenant/organisation ID and access-control tags (security filtering), and a stable chunk ID plus parent document ID (for parent-document retrieval and deletion). Metadata design is what turns a demo into a governable system.

47. How do you enforce access control in a RAG system?

Filter at retrieval time, never after generation — once a restricted chunk is in the prompt it can leak. Store ACL tags in chunk metadata and pass a metadata filter derived from the authenticated user's entitlements into every retriever call. For hard isolation, use separate namespaces, collections or databases per tenant. Additionally, resolve permissions server-side from the session, never from a client-supplied parameter, and re-sync ACLs when they change at source.

48. What is the multi-tenancy strategy trade-off?

Shared index with metadata filters is cheapest and simplest, but a single filter bug becomes a data breach. Namespace-per-tenant gives logical isolation with shared infrastructure — the usual middle ground. Database-per-tenant gives the strongest isolation and per-tenant tuning/backup, at the highest operational cost. Regulated customers usually force you toward the latter two.

49. How do you handle very large ingestion jobs efficiently?

Batch embedding calls (hundreds of chunks per request), parallelise with bounded concurrency to respect rate limits, use async loaders and abatch, checkpoint progress so a failure does not restart from zero, deduplicate before embedding with content hashes, and run ingestion as an offline job (queue + workers) rather than inside a request. Track cost per document so the finance conversation is data-driven.

50. What is a self-query retriever?

SelfQueryRetriever uses an LLM to translate a natural-language question into both a semantic query and a structured metadata filter, given a description of your metadata schema. Ask "papers on transformers published after 2023 by Google" and it emits a vector query for "transformers" plus a filter on year and organisation. It converts vague natural language into precise database predicates — powerful, but it needs an accurate schema description and validation, since a hallucinated filter returns nothing.

05 · Retrieval and RAG Architecture

The single most-asked area in LangChain interviews. Depth here separates candidates who have shipped from candidates who have followed a tutorial.

51. Explain the RAG pipeline end to end.

Offline: load → split → embed → index. Online: receive query → optionally rewrite/expand it → retrieve candidates (dense, sparse, or hybrid) → re-rank and compress → assemble a grounded prompt with citations → generate → post-process and validate → log the trace.

The two independent quality levers are retrieval quality (did we fetch the right evidence?) and generation faithfulness (did the model use only that evidence?). Diagnose them separately; teams waste weeks prompt-tuning a generation problem that is actually a retrieval problem.

52. Why use RAG instead of fine-tuning?

RAG injects knowledge at inference time: content updates are an index write rather than a training run, sources can be cited, access control is enforceable, and hallucination is reduced by grounding. Fine-tuning changes behaviour, format, tone and task-specific skill — it is poor at instilling facts that change, and it cannot cite. The mature answer is that they are complementary: RAG for knowledge, fine-tuning (or few-shot prompting) for style and task adherence.

53. What is a Retriever and how does it differ from a VectorStore?

A VectorStore is storage plus similarity search. A Retriever is a narrower interface — given a query string, return relevant documents — and it is a Runnable, so it composes in LCEL. Crucially, retrievers need not be vector-based: BM25, Elasticsearch, a SQL query, a web search API, or an ensemble of all of them are all valid retrievers. vectorstore.as_retriever(search_type=..., search_kwargs=...) is just the most common construction.

Hybrid combines sparse lexical retrieval (BM25 — exact terms, product codes, error strings, rare names) with dense vector retrieval (paraphrase and concept matching). Each covers the other's blind spot: vectors miss exact identifiers like ORA-01555; BM25 misses "how do I get my money back" against a document titled "Refund Policy".

In LangChain, use EnsembleRetriever with weights, which fuses result lists using Reciprocal Rank Fusion. Many vector databases also offer native hybrid scoring with an alpha parameter.

55. What is re-ranking and where does it fit?

Retrieve a wide candidate set (say 50) cheaply, then score each candidate against the query with a cross-encoder that reads query and document together — far more accurate than the bi-encoder similarity used at index time, but too slow to run over a whole corpus. Keep the top 3–5. In LangChain this is a ContextualCompressionRetriever wrapping a base retriever with a CrossEncoderReranker or a hosted reranker (Cohere, Voyage, Jina). This two-stage design is usually the highest-ROI single improvement to a mediocre RAG system.

56. What is contextual compression?

Post-retrieval reduction of what actually reaches the model. LLMChainExtractor pulls only the query-relevant sentences from each document; LLMChainFilter drops whole irrelevant documents; EmbeddingsFilter does the same cheaply with a similarity threshold; DocumentCompressorPipeline chains splitters, redundancy filters and rerankers together. The payoff is lower token cost and less distraction — a smaller, denser context often produces better answers than a larger one.

57. Explain the parent document retriever pattern.

A tension: small chunks retrieve precisely, large chunks answer well. ParentDocumentRetriever resolves it by indexing small child chunks for search while storing larger parent documents in a docstore. You match on the precise child, then hand the model the full parent for context. Variants return an expanded window around the hit rather than the whole parent. This is one of the cleanest fixes for "retrieval finds the right paragraph but the answer lacks context".

58. What is the multi-query retriever and when does it help?

MultiQueryRetriever asks an LLM to generate several paraphrases of the user's question from different angles, retrieves for each, and unions the deduplicated results. It compensates for vocabulary mismatch between how users ask and how documents are written. Cost is one extra LLM call plus n retrievals, so reserve it for high-value queries or pair it with caching. RAG-Fusion is the closely related variant that fuses the ranked lists with RRF.

59. What is HyDE?

Hypothetical Document Embeddings: instead of embedding the short question, ask the LLM to write a plausible answer, then embed that and search with it. A hypothetical answer looks structurally like the documents you are searching, so it lands closer to them in embedding space than a terse question does. Effective for sparse or highly technical corpora; risky when the model's hypothetical answer drifts to an unrelated topic, so it pairs well with hybrid retrieval as a safety net.

60. How do you produce reliable citations?

Carry a stable ID in each chunk's metadata, render the context with explicit markers (e.g. [1] …), instruct the model to cite those markers inline, and use structured output so the answer returns as {answer, citations: [id, ...]}. Then verify — post-check that each cited ID was actually in the supplied context, and optionally that the claim is entailed by it. Map IDs back to source URIs and page numbers for the UI. Never let the model invent a URL; it only ever selects from IDs you provided.

61. How do you reduce hallucination in a RAG system?

Ground strictly: instruct the model to answer only from context and to say it does not know otherwise — and make "I don't know" an explicitly rewarded behaviour. Improve retrieval first (hybrid + rerank), since most hallucination is missing evidence. Keep context tight with compression. Use low temperature. Require citations and validate them. Add a faithfulness check — an LLM-as-judge or NLI model verifying each claim against the context — and route low-confidence answers to a human or to a clarifying question. Finally, measure it: track groundedness on an eval set so changes are provable.

62. What is query rewriting and why does it matter in chat?

Follow-up questions are context-dependent: "what about the second one?" is meaningless to a retriever. A history-aware retriever first rewrites the question into a standalone form using the conversation, then retrieves. create_history_aware_retriever implements exactly this. Other rewriting techniques include step-back prompting (ask a more general question first to retrieve background) and query decomposition for multi-hop questions.

63. How do you evaluate a RAG system?

Evaluate the components separately. Retrieval: recall@k, precision@k, MRR, NDCG against a golden set of question→relevant-document pairs. Generation: faithfulness/groundedness (is every claim supported by context?), answer relevance, and correctness against reference answers. Frameworks like RAGAS provide these metrics; LangSmith hosts the datasets, runs the experiments and tracks regressions across versions.

Also track operational metrics: p95 latency, cost per query, retrieval hit rate, and the rate of "I don't know" responses — a sudden jump there usually signals an ingestion breakage, not a model problem.

64. A user says the RAG answers are wrong. How do you debug it systematically?

Work backwards through the pipeline with the LangSmith trace open. (1) Was the right document even ingested and chunked sensibly? Inspect the chunks. (2) Did retrieval return it? Run the retriever alone and check recall — if the evidence is absent, no prompt will save you. (3) Was it ranked highly enough to survive top-k? Add reranking or raise fetch_k. (4) Did it survive compression? (5) Was the context assembled correctly — right order, not truncated? (6) Only then look at the prompt and model. Fix in that order; teams that start at step 6 waste the most time.

65. What are the main RAG architectures beyond naive RAG?

  • Naive RAG — retrieve once, generate once.
  • Advanced RAG — query rewriting, hybrid retrieval, reranking, compression.
  • Self-RAG / CRAG — the system critiques its own retrieval, and re-retrieves or falls back to web search when the evidence is judged insufficient.
  • Agentic RAG — an agent decides which knowledge source to query, how many hops to take, and when to stop; implemented as a LangGraph loop.
  • GraphRAG — build a knowledge graph over the corpus and traverse relationships, which handles multi-hop and global "summarise the whole corpus" questions that chunk retrieval cannot.

06 · Agents and Tools

Where LLMs stop answering and start acting — and where most production incidents come from.

66. What is an agent, and how is it different from a chain?

A chain has a control flow you wrote: the sequence of steps is fixed. An agent lets the model decide the control flow — which tool to call, with what arguments, and whether to loop again or stop. Chains are predictable, cheap and easy to test. Agents are flexible and handle open-ended tasks, at the cost of non-determinism, higher latency and more failure modes.

The engineering judgement an interviewer is testing: use the least agentic architecture that solves the problem. Many "agent" requirements are really a router plus two chains.

67. How does tool calling actually work under the hood?

Tool schemas (name, description, JSON-schema parameters) are sent with the request via llm.bind_tools([...]). The model does not execute anything — it returns an AIMessage whose tool_calls field names a tool and supplies arguments. Your code executes the tool, wraps the result in a ToolMessage carrying the matching tool_call_id, and sends the whole message list back. The model then either answers or requests more tools. That loop is the agent.

68. How do you define a good tool?

from langchain_core.tools import tool
from pydantic import BaseModel, Field

class OrderInput(BaseModel):
    order_id: str = Field(description="Order ID, format ORD-12345")

@tool(args_schema=OrderInput)
def get_order_status(order_id: str) -> str:
    """Look up the current shipping status of a customer order.
    Use only when the user supplies an explicit order ID."""
    return db.fetch_status(order_id)

The description is the prompt — it is the only thing the model uses to choose. State what the tool does, when to use it, when not to use it, and the exact argument formats. Keep tools narrow and non-overlapping; two similar tools cause thrashing. Return concise, structured strings, and never dump 10,000 tokens of raw API response into the context.

69. How do you handle tool errors inside an agent?

Catch exceptions and return them to the model as a ToolMessage describing the failure — models are good at correcting a bad argument when told what was wrong. Use handle_tool_error on the tool, or handle_validation_error for schema mismatches. Guard against infinite retry loops with an attempt counter, and distinguish recoverable errors (bad input, retry) from terminal ones (auth failure, escalate). Never surface raw stack traces containing internal hostnames or keys to the model.

70. Explain the ReAct pattern.

ReAct interleaves Reasoning and Acting: Thought → Action → Observation → Thought → … → Final Answer. The visible reasoning trace helps the model plan and helps you debug. Historically it was implemented by parsing text against a strict format, which was brittle. With native tool-calling models you get the same loop structurally and far more reliably, which is why create_tool_calling_agent or LangGraph's create_react_agent is preferred over the old string-parsing ReAct agent.

71. What does AgentExecutor do?

It is the runtime loop: call the model, parse the tool calls, execute the tools, append observations, repeat until a final answer or a stop condition. It provides max_iterations, max_execution_time, early_stopping_method, return_intermediate_steps and handle_parsing_errors. It is legacy relative to LangGraph, which offers the same loop with persistence, streaming of intermediate state, branching and human-in-the-loop — but you should still know what AgentExecutor does, because plenty of production code still uses it.

72. What guardrails do you put around an agent with real-world side effects?

  • Least privilege — scoped credentials per tool; read-only by default.
  • Human approval — interrupt before irreversible actions (payments, deletes, emails).
  • Idempotency keys so a retried action does not double-charge.
  • Hard limits — max iterations, wall-clock timeout, token and spend budget per run.
  • Validation — never interpolate model output into SQL, shell or URLs without parameterisation and allow-lists.
  • Sandboxing — code execution in an isolated container with no network and no secrets.
  • Audit logging — every tool call, argument and result, traced and retained.

73. How do you defend against prompt injection in a tool-using agent?

Assume any retrieved document, web page or API response may contain hostile instructions. Defences: keep untrusted content clearly delimited and labelled as data, not instructions; do not grant the agent tools whose misuse you cannot tolerate; require human confirmation for high-impact actions; enforce authorisation in the tool implementation rather than relying on the model to behave; filter outbound content for exfiltration patterns; and apply an input/output guardrail model. The durable principle is that no prompt wording is a security control — the permission boundary must live in code.

74. What is the difference between a tool and a toolkit?

A tool is one callable with a schema. A toolkit is a curated bundle of related tools for a domain — SQL database, file system, GitHub, Gmail, browser — designed to be used together, often with a matching prompt. Toolkits speed you up, but audit their permissions before production; several ship with write capability enabled by default.

75. How would you build a text-to-SQL agent safely?

Supply only the schema of permitted tables (not the whole database) via SQLDatabase(include_tables=[...]). Connect with a read-only user. Add a query-checker step that reviews generated SQL before execution. Enforce a LIMIT and a statement timeout. Reject DDL/DML by pattern and by database permission. Use few-shot examples of correct queries for your schema. Return results to the model as compact rows and let it phrase the answer. Log every executed query for audit.

76. How do you decide between a single agent and a multi-agent system?

Start with one agent. Split when the tool count grows large enough that selection accuracy drops, when sub-tasks need genuinely different prompts, models or permissions, or when you want independent teams to own separate components. Common topologies: supervisor (a router delegates to specialists and aggregates), hierarchical (supervisors of supervisors), and network (peers hand off freely — powerful but hard to reason about). Every extra hop adds latency, cost and failure surface, so the burden of proof is on splitting.

77. What are the common agent failure modes in production?

Looping between two tools without progress; choosing the wrong tool because descriptions overlap; hallucinating arguments for missing information instead of asking; context overflow as the scratchpad grows; silently degrading when an upstream API changes its response shape; and cost runaway on a single pathological request. Mitigations: strict iteration and budget caps, tool descriptions that specify when not to use them, forcing a clarifying question when required parameters are absent, trimming the scratchpad, contract tests on tools, and alerting on per-run token spend.

07 · LangGraph

Stateful orchestration. Increasingly the differentiator between a junior and a senior answer.

78. What is LangGraph and why use it over LCEL chains?

LangGraph models an application as a state machine: nodes are functions that read and update a shared typed state, edges define transitions, and conditional edges let the flow branch or loop. LCEL is excellent for directed acyclic pipelines; LangGraph exists for what LCEL cannot express naturally — cycles, retries with different strategies, branching on runtime conditions, persistence between steps, human interruption, and multiple actors sharing state.

Rule of thumb: linear pipeline → LCEL; anything that loops, pauses, or needs durable state → LangGraph.

79. Explain nodes, edges and state.

State is a typed schema (a TypedDict or Pydantic model) representing everything the graph knows. Nodes are functions taking the state and returning a partial update. Edges wire nodes together; conditional edges call a router function that returns the next node's name (or END). Updates are merged into state according to each field's reducer — for example Annotated[list, add_messages] appends messages rather than replacing the list, which is why message accumulation works automatically.

80. What is a checkpointer and what does it enable?

A checkpointer persists the full graph state after every super-step, keyed by thread_id. Implementations include MemorySaver (dev), SqliteSaver, and Postgres/Redis savers for production. It enables: conversational memory for free; crash recovery mid-run; time travel — rewinding to an earlier checkpoint and forking a new branch; and human-in-the-loop, because the graph can stop, persist and resume days later.

81. How do you implement human-in-the-loop approval?

Use interrupt() inside a node (or configure interrupt_before on a node) to pause execution and surface the pending action. The state is checkpointed, so your API can return it to a UI, wait for a human decision, and later resume with Command(resume=decision). Because state is durable, the pause can span a page refresh, a shift change, or an approval workflow — which is exactly why this is hard to build on stateless chains.

82. How do you build a supervisor multi-agent system in LangGraph?

Define a supervisor node whose LLM has structured output constrained to the set of worker names plus FINISH. Each worker is a node (often itself a compiled subgraph with its own tools). A conditional edge routes from the supervisor to the chosen worker; each worker routes back to the supervisor. Shared state carries the message history and any accumulated results. Add a step counter with a hard cap so a confused supervisor cannot loop forever.

83. How does streaming work in LangGraph?

Several modes: values streams the full state after each step; updates streams only what each node changed; messages streams LLM tokens as they are generated inside nodes; custom streams arbitrary progress events you emit yourself; and debug streams everything. Real UIs typically combine messages for the answer text with updates to show which agent or tool is currently working.

84. What are subgraphs and when are they useful?

A compiled graph can be used as a node inside a parent graph. This gives encapsulation (a team owns a subgraph with its own state schema), reuse across applications, and independent testing. State is shared by matching keys, or you can map between schemas at the boundary. Subgraphs are how large agent systems stay maintainable.

85. How do you handle errors and retries in a graph?

Node-level retry policies for transient faults; try/except inside a node returning an error field to state, with a conditional edge routing to a recovery or fallback node; a global step limit via recursion_limit; and — because state is checkpointed — the ability to resume from the last good checkpoint rather than replaying the entire run, which matters when earlier steps were expensive.

86. What is the Store API versus the checkpointer?

The checkpointer holds thread-scoped state — this conversation. The Store holds cross-thread data — durable facts about a user or organisation, namespaced and searchable (including semantic search). In short: checkpointer for short-term memory, store for long-term memory. Interviewers like this distinction because it shows you have thought past a single chat session.

87. Give a concrete architecture where LangGraph is clearly the right choice.

An insurance claims assistant: ingest a claim document, extract structured fields, validate against policy data, loop back for clarification if fields are missing, call a fraud-scoring service, pause for human approval above a threshold, then submit and notify. It has cycles (clarification), branching (threshold), external side effects (submission), a mandatory human pause, and must survive process restarts because approval takes hours. Every one of those requirements maps to a LangGraph feature and none maps cleanly to a linear chain.

08 · LangSmith, Testing and Evaluation

How you prove the system works — and keep proving it after every prompt change.

88. What is LangSmith and what do you use it for?

An observability and evaluation platform for LLM applications. Tracing captures every nested run — prompts, completions, tool calls, retrieved documents, latency, tokens and cost — as a tree you can inspect. On top of that it provides datasets, automated evaluations, experiment comparison, prompt versioning via a hub, human annotation queues, and production monitoring with online evaluators. It works with plain SDK code too; LangChain is not required.

89. How do you enable tracing?

Set LANGSMITH_TRACING=true and LANGSMITH_API_KEY (plus LANGSMITH_PROJECT to separate environments) and every LangChain/LangGraph run is traced automatically. For non-LangChain code, use the @traceable decorator or the wrapped provider clients. Add metadata and tags in the run config so you can slice traces by tenant, version or user segment.

90. How would you build an evaluation suite for an LLM feature?

(1) Curate a dataset of representative inputs with reference outputs — seed it from real production traces, including the failures. (2) Define evaluators: exact/fuzzy match for deterministic tasks, embedding similarity for semantic closeness, LLM-as-judge with an explicit rubric for open-ended quality, and custom code for hard constraints such as valid JSON, no PII, latency budget. (3) Run experiments on every change and compare against the baseline. (4) Gate deployment in CI on regression thresholds. (5) Continue sampling production traffic for online evaluation and feed newly discovered failures back into the dataset.

91. What are the pitfalls of LLM-as-a-judge?

Position bias (favouring the first option), verbosity bias (longer looks better), self-preference (a model rating its own family higher), and poor consistency on vague rubrics. Mitigate with: a specific rubric and a discrete scale, requiring a written justification before the score, randomising option order, using a different and stronger model as judge, and — most importantly — calibrating the judge against human labels on a sample before you trust it to gate releases.

92. How do you unit-test LangChain code?

Test the deterministic parts properly: prompt rendering, output parsing, tool functions, retrieval filters and post-processing are all ordinary Python. Use FakeListLLM / FakeListChatModel to make chains deterministic, and mock retrievers with fixed document sets. Reserve real model calls for a smaller, slower integration suite that runs on a schedule, not on every commit. Then layer evaluation runs on top — unit tests catch breakage, evals catch quality regressions, and you need both.

93. What metrics do you monitor in production?

Quality: groundedness, user thumbs, escalation rate, "no answer" rate. Performance: p50/p95/p99 latency, time-to-first-token, throughput. Cost: tokens and spend per request, per tenant, per feature; cache hit rate. Reliability: provider error rate, timeout rate, fallback activation rate, tool failure rate. Retrieval: hit rate and average top-1 score. Alert on drift in any of these — a silent embedding-model change or an index breakage shows up here long before users complain.

94. How do you version prompts safely?

Treat prompts as code: store them in version control or the LangSmith prompt hub with commit hashes, tag the version used in every trace, run the eval suite against a candidate before promoting it, roll out behind a flag with A/B comparison on live traffic, and keep instant rollback. Never edit a production prompt in place without an eval run — small wording changes routinely cause double-digit swings in task success.

95. How do you handle sensitive data in traces?

Use LangSmith's data-masking/anonymisation hooks or a custom serialiser to redact PII before it leaves your process; opt out of payload capture for specific runs where content must never be stored; self-host if the compliance regime requires it; set short retention; and restrict project access by role. Also confirm the provider's own data-retention terms — tracing is only one of several places conversation content lands.

09 · Production, Performance, Cost and Security

Senior-level questions. These are what determine the offer band.

96. How do you reduce latency in a LangChain application?

Stream so time-to-first-token, not total time, is what the user perceives. Parallelise independent work with RunnableParallel and abatch. Use a smaller or faster model for easy sub-tasks and route only hard ones to the large model. Cut prompt size — shorter context is faster as well as cheaper. Cache aggressively, including semantic caching and provider-side prompt caching for long static system prompts. Pre-compute embeddings offline. Use async end to end. Finally, profile with a trace before optimising: the bottleneck is often a serial retrieval or a slow tool, not the LLM.

97. How do you control cost?

Model routing by task difficulty; prompt compression and context trimming; caching (exact and semantic); batching where latency permits; capping max_tokens; reranking so you send 4 excellent chunks rather than 20 mediocre ones; using batch/off-peak APIs for offline workloads; and per-tenant budgets with hard cut-offs. Measure cost per successful outcome, not cost per call — a cheap model that fails and triggers a retry plus a human handoff is not cheap.

98. How do you scale a LangChain service?

Stateless application pods behind a load balancer, with all state in Redis/Postgres, so you can scale horizontally. Async workers (FastAPI + uvicorn) since the workload is I/O-bound. A queue for long-running jobs, with results delivered over websockets or SSE rather than a held HTTP connection. Connection pooling to the vector store. Client-side rate limiting and backoff per provider, with multiple provider keys or regions for headroom. Separate ingestion workers from serving so a large re-index does not degrade query latency.

99. How do you manage secrets and API keys?

Environment variables injected from a secret manager (AWS Secrets Manager, Vault, Kubernetes secrets) — never in code, notebooks or prompt text. Scope keys per environment and per service, rotate them, and set provider-side spend limits. In agent systems, tools should receive credentials from the runtime, never from model-generated arguments; and secrets must never appear in traces, logs or error messages returned to the model.

100. What are the main security risks specific to LLM applications?

Prompt injection (direct and indirect via retrieved content); sensitive information disclosure through over-broad retrieval or leaked system prompts; insecure output handling, where model text is executed as SQL, shell or HTML (a classic XSS/SSRF vector); excessive agency, where the agent holds permissions beyond its task; supply-chain risk from unvetted community integrations; unbounded consumption leading to cost-based denial of service; and training-data leakage if you fine-tune on customer data without isolation. Controls: strict authorisation in code, output encoding, sandboxing, rate limits, allow-lists and human approval for consequential actions.

101. How do you handle PII?

Detect and redact before the prompt leaves your boundary (Presidio, cloud DLP, or a regex+NER pipeline), keeping a reversible token map if you need to restore values in the response. Minimise what you send. Use providers with zero-retention or enterprise terms, or self-hosted models for regulated data. Redact traces and logs. Encrypt chat history at rest, enforce retention limits, and support deletion requests — which means being able to purge a user's data from the vector index as well as the database.

102. How do you deploy a LangChain application?

Containerise a FastAPI/LangServe app; pin every LangChain package version. Externalise state to managed Redis/Postgres/vector DB. Run on ECS/EKS/Cloud Run with autoscaling on concurrency rather than CPU, since the workload is I/O-bound. Health checks that verify provider and vector-store connectivity. CI runs unit tests plus the eval suite; deploys are canaried with automatic rollback on error-rate or quality regression. Ingestion runs as a separate scheduled job. Observability via LangSmith plus your standard APM and log stack.

103. How do you handle provider rate limits and outages?

Exponential backoff with jitter via with_retry; a client-side rate limiter to stay under quota rather than discovering it at 429; fallbacks to a second provider or a smaller model via with_fallbacks; a circuit breaker so a hard-down provider does not consume your whole thread pool; a request queue with graceful degradation ("answering from cache"); and multi-region or multi-key distribution. Test the fallback path deliberately — an untested fallback is a wish, not a control.

104. How do you decide between an API model and a self-hosted open model?

API models: best quality, no ops, fast iteration, but per-token cost, data leaves your perimeter, and you inherit their deprecations. Self-hosted: data residency and compliance control, predictable cost at high sustained volume, full fine-tuning freedom, but you own GPUs, serving (vLLM/TGI), scaling and evaluation. The usual pragmatic answer: API models for reasoning-heavy paths, self-hosted small models for high-volume narrow tasks such as classification, embedding and reranking. LangChain makes the mix cheap because the interface is the same.

105. What does a good LangChain project repository look like?

Clear layers: chains/, agents/, tools/, retrievers/, prompts/ (versioned files, not inline strings), ingestion/, evals/ with datasets, api/, plus config via Pydantic settings and typed interfaces throughout. Dependency-inject the model and vector store so tests can substitute fakes. Pin dependencies. Document the data contract of every tool. Keep prompts, evals and code in the same repo so a prompt change is reviewable in a pull request.

106. When should you NOT use LangChain?

When the application is a single templated call to one provider — the abstraction cost is not repaid. When you need absolute control over payloads and latency at extreme scale. When your team's operational maturity cannot absorb a fast-moving dependency. A credible senior answer names the trade-off honestly: LangChain's value is composability, integration breadth and observability; if you need none of those, the SDK plus a hundred lines of your own code is a legitimate choice.

107. How would you migrate an existing LangChain 0.1 codebase to the current version?

Upgrade in order: split imports to langchain-core and partner packages; replace deprecated memory classes with RunnableWithMessageHistory or LangGraph; replace legacy chains with LCEL equivalents; replace initialize_agent with create_tool_calling_agent or LangGraph; switch parsers to with_structured_output where available. Do it behind an eval suite so you can prove behaviour is unchanged, and migrate module by module rather than in one big-bang branch.

10 · Coding Questions

Expect a shared editor and thirty minutes. Write these until they are muscle memory.

108. Build a chain that summarises text and returns strict JSON.

from pydantic import BaseModel, Field
from langchain_core.prompts import ChatPromptTemplate

class Summary(BaseModel):
    headline: str = Field(description="Under 10 words")
    bullets: list[str] = Field(description="3 key points")
    sentiment: str = Field(description="positive | neutral | negative")

prompt = ChatPromptTemplate.from_messages([
    ("system", "You summarise support tickets precisely."),
    ("human", "{text}"),
])

chain = prompt | llm.with_structured_output(Summary)
result = chain.invoke({"text": ticket})
print(result.headline, result.sentiment)

109. Implement a conversational RAG chain with history.

from langchain.chains import (create_history_aware_retriever,
                              create_retrieval_chain)
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

rewrite = ChatPromptTemplate.from_messages([
    ("system", "Rewrite the follow-up as a standalone question."),
    MessagesPlaceholder("chat_history"),
    ("human", "{input}"),
])
hist_retriever = create_history_aware_retriever(llm, retriever, rewrite)

answer_prompt = ChatPromptTemplate.from_messages([
    ("system", "Answer using only this context:\n\n{context}"),
    MessagesPlaceholder("chat_history"),
    ("human", "{input}"),
])
doc_chain = create_stuff_documents_chain(llm, answer_prompt)
rag = create_retrieval_chain(hist_retriever, doc_chain)

110. Write a routing chain that sends a question to the right specialist.

from langchain_core.runnables import RunnableBranch
from langchain_core.output_parsers import StrOutputParser

classifier = (
    ChatPromptTemplate.from_template(
        "Classify as billing, technical or other. One word.\n{question}")
    | llm | StrOutputParser()
)

route = RunnableBranch(
    (lambda x: "billing" in x["topic"].lower(), billing_chain),
    (lambda x: "technical" in x["topic"].lower(), tech_chain),
    general_chain,
)

full = {"topic": classifier, "question": lambda x: x["question"]} | route

111. Create a hybrid retriever with reranking.

from langchain.retrievers import EnsembleRetriever, ContextualCompressionRetriever
from langchain_community.retrievers import BM25Retriever
from langchain.retrievers.document_compressors import CrossEncoderReranker

bm25 = BM25Retriever.from_documents(docs); bm25.k = 20
dense = vectorstore.as_retriever(search_kwargs={"k": 20})

hybrid = EnsembleRetriever(retrievers=[bm25, dense], weights=[0.4, 0.6])

retriever = ContextualCompressionRetriever(
    base_retriever=hybrid,
    base_compressor=CrossEncoderReranker(model=cross_encoder, top_n=4),
)

112. Build a tool-calling agent with two tools.

from langchain.agents import create_tool_calling_agent, AgentExecutor

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a support agent. Use tools when needed."),
    ("human", "{input}"),
    MessagesPlaceholder("agent_scratchpad"),
])

agent = create_tool_calling_agent(llm, [get_order_status, search_kb], prompt)
executor = AgentExecutor(
    agent=agent, tools=[get_order_status, search_kb],
    max_iterations=6, return_intermediate_steps=True,
    handle_parsing_errors=True,
)
executor.invoke({"input": "Where is ORD-88213?"})

113. Write a minimal LangGraph agent loop.

from typing import Annotated, TypedDict
from langgraph.graph import StateGraph, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode
from langgraph.checkpoint.memory import MemorySaver

class State(TypedDict):
    messages: Annotated[list, add_messages]

model = llm.bind_tools(tools)

def call_model(state: State):
    return {"messages": [model.invoke(state["messages"])]}

def should_continue(state: State):
    return "tools" if state["messages"][-1].tool_calls else END

g = StateGraph(State)
g.add_node("agent", call_model)
g.add_node("tools", ToolNode(tools))
g.set_entry_point("agent")
g.add_conditional_edges("agent", should_continue)
g.add_edge("tools", "agent")
app = g.compile(checkpointer=MemorySaver())

app.invoke({"messages": [("user", "hi")]},
           config={"configurable": {"thread_id": "u-1"}})

114. Stream a chain’s tokens to a client.

async def token_stream(question: str):
    async for chunk in chain.astream({"question": question}):
        yield chunk           # StrOutputParser yields plain strings

# FastAPI
@app.post("/ask")
async def ask(body: Ask):
    return StreamingResponse(token_stream(body.q),
                             media_type="text/event-stream")

# Richer UI events (tool starts, retrieved docs, per-node tokens):
async for ev in chain.astream_events({"question": q}, version="v2"):
    if ev["event"] == "on_chat_model_stream":
        ...

115. Ingest a folder of PDFs into FAISS with idempotent re-runs.

from langchain_community.document_loaders import PyPDFDirectoryLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain.indexes import SQLRecordManager, index

docs = PyPDFDirectoryLoader("./policies").load()
chunks = RecursiveCharacterTextSplitter(
    chunk_size=1000, chunk_overlap=150).split_documents(docs)

store = FAISS.from_documents(chunks, embeddings)
store.save_local("faiss_policies")

# For an updatable store, use the indexing API:
rm = SQLRecordManager("faiss/policies", db_url="sqlite:///rm.db")
rm.create_schema()
index(chunks, rm, store, cleanup="incremental", source_id_key="source")

116. Add per-request configuration and observability metadata.

result = chain.invoke(
    {"question": q},
    config={
        "run_name": "policy_rag",
        "tags": ["prod", "v3"],
        "metadata": {"tenant": tenant_id, "user": user_id},
        "configurable": {"llm": "fast", "temperature": 0},
        "max_concurrency": 5,
    },
)

117. Write a custom retriever.

from langchain_core.retrievers import BaseRetriever
from langchain_core.documents import Document

class TenantRetriever(BaseRetriever):
    store: object
    tenant_id: str
    k: int = 4

    def _get_relevant_documents(self, query, *, run_manager):
        return self.store.similarity_search(
            query, k=self.k,
            filter={"tenant_id": self.tenant_id},   # enforced server-side
        )

Because it subclasses BaseRetriever, it is a Runnable and traces automatically.

11 · Scenario and System-Design Questions

Open-ended rounds. Structure your answer: requirements → architecture → trade-offs → evaluation → operations.

118. Design an internal knowledge assistant over 500,000 company documents with role-based access.

Ingestion: connector-based crawlers per source (SharePoint, Confluence, Drive) running incrementally with the indexing API; layout-aware parsing; structure-aware chunking; metadata carrying source, section, timestamp, and the ACL groups copied from the source system.

Retrieval: hybrid BM25 + dense with a mandatory metadata filter built from the caller's verified group memberships, then cross-encoder reranking to 4–6 chunks, then compression.

Generation: grounded prompt with numbered sources, structured output with citations, citation verification, refusal when evidence is weak.

Scale: managed vector DB with namespaces per department; separate ingestion workers; embedding cache. Ops: ACL re-sync job, nightly eval run, LangSmith monitoring, per-department cost reporting. Key risk: permission drift between source and index — state that you would re-verify permissions at query time against the source of truth for sensitive collections.

119. A customer complains the chatbot invented a refund policy. Walk me through your response.

Immediate: pull the LangSmith trace for that conversation, confirm what was retrieved and what the prompt contained, and if the failure is systemic, ship a temporary guardrail — tighten the refusal instruction or gate that intent to a scripted answer.

Diagnosis: was the correct document indexed? retrieved? ranked? compressed away? Or was it present and the model still deviated? Fix at the right layer — usually retrieval.

Prevention: add that exact case to the eval dataset; add a groundedness evaluator to CI; require citations with post-hoc verification; add an online evaluator that samples production traffic for unsupported claims; and add a feedback button so users report the next one faster. Close with the process point: a single hallucination is a bug, an unmeasured hallucination rate is a management failure.

120. Design a multi-agent research assistant that produces a cited report.

LangGraph supervisor topology. Nodes: planner (decomposes the brief into sub-questions), searcher (web + internal retrieval per sub-question), analyst (synthesises findings with source IDs), critic (checks coverage and flags unsupported claims, looping back if inadequate), writer (assembles the report). Shared state holds the plan, findings keyed by sub-question, and the draft.

Controls: max research iterations, per-run token budget, deduplication of sources, and a human checkpoint before publication. Streaming updates shows the user which stage is running. Evaluation: rubric-based judging on coverage, factuality and citation validity against a held-out set of briefs.

121. Your RAG system costs $18,000/month. Halve it without hurting quality.

Measure first — break cost down by endpoint, tenant and stage. Then, in rough order of return: (1) rerank so you send 4 chunks instead of 20, which often cuts input tokens by 60–70%; (2) route easy queries to a cheaper model and keep the flagship for hard ones, using a small classifier; (3) enable exact and semantic caching — FAQ traffic is highly repetitive; (4) exploit provider prompt caching for the static system prompt; (5) trim conversation history with trim_messages; (6) cap max_tokens and stop verbose formatting; (7) move ingestion embeddings to a cheaper or self-hosted model. Validate every change against the eval suite so "no quality loss" is a measurement, not a claim.

122. Design an agent that acts on production systems (restart services, scale clusters).

Architecture: LangGraph with a strict tool allow-list, each tool backed by a scoped service account. Every mutating action is preceded by an interrupt() human approval node showing the exact command and blast radius. Idempotency keys prevent double execution on resume. A policy node validates the proposed action against rules (no production changes during freeze windows, no more than N nodes at once). All actions are logged to an audit store with the requesting user, the reasoning trace and the approval record.

Explicitly say what you would not automate: irreversible operations such as data deletion stay human-initiated. Interviewers weight this answer heavily on risk judgement, not on cleverness.

123. Users say answers are good but too slow (12s). Fix it.

Instrument first: a trace will usually show retrieval, reranking or a serial tool call dominating, not generation. Then: stream so time-to-first-token drops to under a second; parallelise retrieval and any independent enrichment; move reranking to a smaller/hosted model or reduce the candidate pool; cache embeddings and frequent queries; use a faster model for the first draft or for simple intents; and pre-warm connections. If a slow external tool is unavoidable, return an interim answer and update it — perceived latency is what the user is actually complaining about.

124. How would you add a new language (Telugu/Hindi) to an existing English RAG system?

Use a multilingual embedding model and re-index — cross-lingual retrieval only works if queries and documents share an embedding space. Decide between translating documents at ingestion (one index, consistent quality, translation cost and drift) or indexing natively with multilingual embeddings (cheaper, retrieval quality depends on the model's coverage of the language). Ensure the generation model is strong in the target language and instruct it to answer in the user's language while citing the original source. Extend the eval set with native-language questions — do not assume English metrics transfer. Watch tokenisation cost, which can be 2–3× higher for non-Latin scripts.

125. How do you decide between LangChain, LlamaIndex, and a custom build?

LangChain: broadest integrations, strongest agent/orchestration story with LangGraph, best observability with LangSmith. LlamaIndex: deeper out-of-the-box indexing and retrieval abstractions, particularly for document-heavy RAG. Custom: maximum control, minimum abstraction risk, highest engineering cost. They are not mutually exclusive — using LlamaIndex retrievers inside a LangChain chain is common. Judge on: how much of your value is orchestration versus retrieval, your team's tolerance for dependency churn, and whether you need production observability you would otherwise have to build.

12 · Rapid-Fire Round

One-line answers. Interviewers use these to check breadth quickly, often in the first ten minutes.

126. What does StrOutputParser do?

Extracts the string content from an AIMessage, and streams chunk by chunk.

127. What does MessagesPlaceholder do?

Reserves a slot in a chat prompt for a runtime-supplied list of messages, such as history or the agent scratchpad.

128. Default text splitter you reach for?

RecursiveCharacterTextSplitter.

129. What is fetch_k?

The size of the candidate pool retrieved before MMR re-ranks down to k.

130. What does lambda_mult control?

The relevance/diversity balance in MMR — 1.0 is pure relevance, 0.0 is pure diversity.

131. Difference between embed_query and embed_documents?

Single query embedding versus batch document embedding; some models apply different instruction prefixes to each.

132. What is tool_call_id for?

It links a ToolMessage result back to the specific tool call the model requested.

133. What is bind_tools?

Attaches tool schemas to a chat model so it can emit tool calls.

134. What is with_structured_output?

Constrains the model to return an object matching a Pydantic/JSON schema, using native tool or JSON mode.

135. What is the @tool decorator?

Turns a Python function into a LangChain tool, deriving name, description and schema from the signature and docstring.

136. What is astream_events?

An async stream of typed lifecycle events from every nested component of a run.

137. What is RunnableParallel used for?

Running several runnables concurrently and collecting their outputs into a dict.

138. How do you cap batch concurrency?

Set max_concurrency in the run config.

139. What is recursion_limit in LangGraph?

The maximum number of super-steps before the graph aborts, protecting against infinite loops.

140. What is add_messages?

A LangGraph reducer that appends (and de-duplicates by ID) messages instead of overwriting the list.

141. What is MemorySaver?

An in-memory LangGraph checkpointer for development; use a Postgres or Redis saver in production.

142. What is interrupt()?

A LangGraph call that pauses the graph, persists state, and waits for human input before resuming.

143. What is thread_id?

The key identifying a persisted LangGraph conversation state.

144. What is an EnsembleRetriever?

A retriever that fuses results from multiple retrievers using Reciprocal Rank Fusion with weights.

145. What is ContextualCompressionRetriever?

A wrapper that post-processes retrieved documents — reranking, filtering or extracting the relevant parts.

146. What is a cross-encoder?

A model that scores a query and a document jointly; more accurate than embedding similarity but too slow for full-corpus search.

147. What does the indexing API prevent?

Duplicate and stale content on re-ingestion, by hashing documents and tracking writes with a record manager.

148. What is SelfQueryRetriever?

A retriever that uses an LLM to produce both a semantic query and a metadata filter from natural language.

149. What is a semantic cache?

A cache that returns a stored response when a new query is semantically similar, not just identical.

150. What environment variable turns on tracing?

LANGSMITH_TRACING=true, with LANGSMITH_API_KEY set.

151. What is FakeListChatModel for?

Deterministic unit testing of chains without calling a real provider.

152. What does with_fallbacks do?

Switches to an alternative runnable when the primary raises a handled exception.

153. What is trim_messages for?

Token-aware trimming of chat history that preserves the system message and valid role ordering.

154. What is prompt caching?

A provider feature that caches a long static prefix so repeat requests are cheaper and faster.

155. Which retriever fixes "precise match, missing context"?

ParentDocumentRetriever — search small children, return the larger parent.

Keep going — from interview-ready to job-ready

This handbook is part of the GenAI & Agentic AI curriculum at Cloud Soft Solutions, Ameerpet. Our APEX program covers AI / GenAI, Cloud and Cyber Security; NEXUS covers Cloud, DevOps, SRE and AIOps. Both are delivered with live projects, real cloud accounts, mock interviews and placement support — with 5,500+ alumni placed to date.

Become a GenAI & Agentic AI Engineer

APEX — AI, ML, Cloud & Cyber Security Engineering Program

Hands-on LangChain, RAG, LangGraph and Agentic AI projects with interview prep and a 100% placement guarantee.

Explore the APEX Program →

📞 For course details or a free demo, call or WhatsApp +91 96660 19191 / +91 99496 16388, or email info@cloudsoftsol.com. Cloud Soft Solutions, 513, 5th Floor, Aditya Enclave, Nilagiri Block, beside Ameerpet Metro Station, Ameerpet, Hyderabad – 500016. Browse the 2026 fresher jobs hub.

Share𝕏inf
EnrollWhatsAppCall us