Top 100+ LangGraph Interview Questions and Answers for 2026
LangGraph has become the default framework for building production-grade AI agents — the kind that remember state, loop until the job is done, pause for human approval, and recover from crashes. With 43,000+ GitHub stars and version 1.2 shipping in mid-2026, companies from Klarna to Uber to LinkedIn are running LangGraph in production. If you’re interviewing for an AI engineer, ML engineer, or agentic AI architect role in 2026, LangGraph questions will come up. (For broader context on why agent skills matter, see our guide to the agent skills explosion in 2026.)
- Core Concepts & Fundamentals (Q1–Q15)
- Q1. What is LangGraph and what problem does it solve?
- Q2. How does LangGraph differ from LangChain?
- Q3. What are the three core primitives of LangGraph?
- Q4. What is a StateGraph and how do you create one?
- Q5. What is the difference between a node and an edge in LangGraph?
- Q6. What does graph compilation do in LangGraph?
- Q7. Explain the difference between a DAG and a cyclic graph. Why does LangGraph support cycles?
- Q8. What is the difference between StateGraph and MessageGraph?
- Q9. What is MessagesState and when would you use it?
- Q10. What is the role of START and END in a LangGraph graph?
- Q11. How do you invoke a compiled LangGraph graph?
- Q12. What is the Graph API vs. the Functional API in LangGraph?
- Q13. When is LangGraph overkill? Name three scenarios where you wouldn’t use it.
- Q14. What is the create_react_agent function and is it still recommended?
- Q15. What languages and runtimes does LangGraph support?
- State Management & Reducers (Q16–Q28)
- Q16. What is state in LangGraph and how is it structured?
- Q17. What is a reducer in LangGraph? Why are they important?
- Q18. Explain the add_messages reducer and how it handles message IDs.
- Q19. What happens when two parallel branches write to the same state field?
- Q20. How do you define input and output schemas separately from internal state?
- Q21. What is the difference between state, runtime config, and context in LangGraph?
- Q22. How should you keep state small and typed in production?
- Q23. What is the Overwrite sentinel and when would you use it?
- Q24. How does LangGraph handle state schema evolution and migration?
- Q25. What is a super-step in LangGraph?
- Q26. Explain the difference between short-term and long-term memory in LangGraph.
- Q27. How does the Store work for cross-thread memory?
- Q28. How do you manage context window limits with message history?
- Graph Architecture & Control Flow (Q29–Q42)
- Q29. How do conditional edges work in LangGraph?
- Q30. What is the Command object and how does it combine routing with state updates?
- Q31. Explain the Send primitive and map-reduce patterns in LangGraph.
- Q32. How do you prevent infinite loops in cyclic graphs?
- Q33. What is the difference between a subgraph and a node?
- Q34. How do errors and retries work in LangGraph?
- Q35. What is the ToolNode and tools_condition pattern?
- Q36. How do you build a quality-score retry loop?
- Q37. What is node granularity and why does it matter?
- Q38. Explain the difference between synchronous and asynchronous execution in LangGraph.
- Q39. How do you visualize a LangGraph graph?
- Q40. What are breakpoints and how do they differ from interrupts?
- Q41. What is the recursion_limit and how has it changed in LangGraph 1.x?
- Q42. How does LangGraph handle pending writes on node failure within a super-step?
- Checkpointing & Persistence (Q43–Q55)
- Q43. What is a checkpointer in LangGraph and why is it essential?
- Q44. Compare the three checkpointer backends: InMemorySaver, SqliteSaver, and PostgresSaver.
- Q45. What is a thread_id and how does it scope checkpoints?
- Q46. How do you resume a graph after a crash?
- Q47. What is time-travel debugging in LangGraph?
- Q48. Explain durability modes: sync, async, and exit.
- Q49. How do you handle idempotent side effects under checkpoint replay?
- Q50. How do you manage checkpoint storage growth in production?
- Q51. What is get_state and how do you use it for inspection?
- Q52. How does update_state work and when would you use it?
- Q53. How do you implement conversation memory that persists across sessions?
- Q54. What is the difference between within-thread checkpointing and cross-thread Store memory?
- Q55. How do you test checkpoint-dependent behavior in unit tests?
- Human-in-the-Loop Patterns (Q56–Q67)
- Q56. What is human-in-the-loop (HITL) in LangGraph and why does it matter?
- Q57. How do you implement an interrupt in LangGraph?
- Q58. What are the prerequisites for interrupts to work?
- Q59. Explain the approve/edit/reject pattern for HITL.
- Q60. What happens to code execution when a graph resumes after an interrupt?
- Q61. How do static breakpoints differ from dynamic interrupts?
- Q62. How do you implement per-tool interrupts with create_agent?
- Q63. What is the "blast-radius rule" for deciding when to interrupt?
- Q64. How do you handle multiple sequential interrupts in a single graph run?
- Q65. How do you prevent stale data issues in long-paused interrupts?
- Q66. How do you build a HITL flow that works with a web UI?
- Q67. What are common HITL gotchas in LangGraph?
- Multi-Agent Systems (Q68–Q80)
- Q68. What is a multi-agent system in LangGraph?
- Q69. Explain the three main multi-agent topologies: supervisor, swarm, and hierarchical.
- Q70. When does a single agent with tools beat a multi-agent system?
- Q71. How do you implement a supervisor pattern in LangGraph?
- Q72. What is the difference between shared-state and message-passing multi-agent designs?
- Q73. How do subgraphs share state with parent graphs?
- Q74. How do you prevent ping-pong loops in multi-agent systems?
- Q75. How do you handle errors in multi-agent workflows?
- Q76. How do you implement agent handoffs in LangGraph?
- Q77. Explain streaming in multi-agent systems.
- Q78. How do you evaluate and debug multi-agent systems?
- Q79. What is the difference between a multi-agent system and a workflow with specialized nodes?
- Q80. How do you architect a team of agents with different models?
- Tools, MCP & Integrations (Q81–Q90)
- Q81. How does tool calling work in LangGraph?
- Q82. What are injected parameters in tools?
- Q83. How do you connect LangGraph to MCP servers?
- Q84. How do you manage large tool catalogs without blowing up context windows?
- Q85. How do you secure write tools in LangGraph?
- Q86. What is the difference between LangGraph's tool calling and function calling?
- Q87. How do you handle tool errors gracefully?
- Q88. How do you test tools in isolation from the graph?
- Q89. How does LangGraph integrate with LangSmith for observability?
- Q90. How do you implement prompt injection defenses in LangGraph tools?
- Production, Deployment & Scaling (Q91–Q100)
- Q91. What are the deployment options for LangGraph in production?
- Q92. What is the langgraph.json configuration file?
- Q93. Explain the different streaming modes in LangGraph.
- Q94. How do you reduce latency in LangGraph agents?
- Q95. How do you control cost per agent run?
- Q96. How do you horizontally scale LangGraph?
- Q97. How do you version graphs and do safe rollouts?
- Q98. How do you implement observability for production LangGraph agents?
- Q99. How do you handle rate limits from LLM providers in production?
- Q100. What metrics should you track for LangGraph agents in production?
- Scenario-Based & System Design (Q101–Q110)
- Q101. Design a customer support agent that processes refunds with human approval for amounts over $500.
- Q102. You're asked to design a research agent that handles 10,000 requests per day. Walk through your architecture.
- Q103. A medical documentation agent is producing duplicate clinical drafts after crashes. Diagnose and fix.
- Q104. Your supervisor agent keeps misrouting IT incidents between the network and security agents. How do you fix it?
- Q105. Your checkpoint table has grown to 500 GB and queries are slowing down. What's your remediation plan?
- Q106. A customer's chat UI shows a long delay before any response. The agent works correctly but feels slow. Optimize.
- Q107. You need to deploy a graph update, but there are 200 paused HITL threads waiting for approval. How do you handle it?
- Q108. Design a LangGraph agent that can handle both structured (form-filling) and unstructured (free-text conversation) interactions.
- Q109. Compare LangGraph, CrewAI, and the Claude Agent SDK for a production agent deployment. When would you choose each?
- Q110. Design an enterprise knowledge assistant that searches internal documents, respects access controls, and cites sources.
- Frequently Asked Questions
- Is LangGraph free to use?
- What version of LangGraph should I learn for interviews in 2026?
- How popular is LangGraph compared to alternatives in 2026?
- Do I need to know LangChain to use LangGraph?
- What companies are using LangGraph in production?
- Key Takeaways
- External References
This guide covers 110 interview questions with detailed answers, organized from fundamentals through system design scenarios. Every answer reflects the current LangGraph 1.x API (not the deprecated AgentExecutor patterns). Whether you’re preparing as a candidate or evaluating one, this is the reference.
Section: Core Concepts & Fundamentals | Questions: Q1–Q15 | Difficulty: Beginner
Section: State Management & Reducers | Questions: Q16–Q28 | Difficulty: Beginner–Intermediate
Section: Graph Architecture & Control Flow | Questions: Q29–Q42 | Difficulty: Intermediate
Section: Checkpointing & Persistence | Questions: Q43–Q55 | Difficulty: Intermediate
Section: Human-in-the-Loop Patterns | Questions: Q56–Q67 | Difficulty: Intermediate–Advanced
Section: Multi-Agent Systems | Questions: Q68–Q80 | Difficulty: Advanced
Section: Tools, MCP & Integrations | Questions: Q81–Q90 | Difficulty: Intermediate–Advanced
Section: Production, Deployment & Scaling | Questions: Q91–Q100 | Difficulty: Advanced
Section: Scenario-Based & System Design | Questions: Q101–Q110 | Difficulty: Expert
Core Concepts & Fundamentals (Q1–Q15)
Q1. What is LangGraph and what problem does it solve?
LangGraph is an open-source Python and TypeScript framework from LangChain Inc. for building stateful, multi-step AI agents as cyclic directed graphs. It solves the core problem earlier agent frameworks had: agents that lose their place, repeat work, or crash mid-workflow because state was discarded between steps. LangGraph gives you explicit control over state, branching, loops, and persistence — making agents that are deterministic enough for production.
Q2. How does LangGraph differ from LangChain?
LangChain is a component library — prompts, retrievers, parsers, tools — while LangGraph is the lower-level orchestration runtime that controls how those components execute. Since LangChain 1.0, the built-in create_agent function actually runs on LangGraph underneath. Use LangChain when you need component abstractions; use LangGraph when you need explicit control flow, cycles, checkpointing, or human-in-the-loop patterns. A simple RAG pipeline may only need LangChain; anything with loops, retries, or multi-agent coordination needs LangGraph.
Q3. What are the three core primitives of LangGraph?
State — a shared typed object (TypedDict, dataclass, or Pydantic model) that all nodes read and update. It serves as the agent’s working memory for a single run. Nodes — Python functions that take the current state, perform an operation (LLM call, tool execution, validation), and return a partial state update. Edges — routing rules that define transitions between nodes. Edges can be unconditional (fixed sequence) or conditional (branching based on state). Together, these three primitives let you express any agent workflow as a graph.
Q4. What is a StateGraph and how do you create one?
A StateGraph is the builder class you use to construct a LangGraph agent. You pass it a state schema, add nodes and edges, then compile it into a runnable graph. Example:
from langgraph.graph import StateGraph, START, END
from typing import TypedDict
class MyState(TypedDict):
messages: list
step_count: int
graph = StateGraph(MyState)
graph.add_node("analyze", analyze_fn)
graph.add_node("respond", respond_fn)
graph.add_edge(START, "analyze")
graph.add_conditional_edges("analyze", route_fn, {"done": END, "continue": "respond"})
app = graph.compile()
Q5. What is the difference between a node and an edge in LangGraph?
A node is where work happens — it’s a function that reads the current state, does something (calls an LLM, runs a tool, validates data), and returns a partial state update. An edge is a transition rule between nodes. Unconditional edges create fixed sequences (“after A, always go to B”). Conditional edges evaluate a routing function against the state to decide which node to visit next. The special constants START and END mark the entry point and terminal points of the graph.
Q6. What does graph compilation do in LangGraph?
Calling .compile() on a StateGraph runs structural validation checks (unreachable nodes, missing edges, schema mismatches), attaches any checkpointer or breakpoints you’ve configured, and produces a CompiledGraph that exposes invoke, stream, ainvoke, and astream methods. The compiled graph is the runnable object you actually call with input state. Compilation is a one-time operation — you compile once and invoke many times.
Q7. Explain the difference between a DAG and a cyclic graph. Why does LangGraph support cycles?
A DAG (Directed Acyclic Graph) flows in one direction — no node can be visited twice. Most workflow engines (Airflow, Step Functions) are DAG-based. LangGraph supports cyclic graphs, meaning execution can loop back to earlier nodes. This is essential for agents because the ReAct pattern — reason, act, observe, reason again — is inherently a loop. An agent might need to retry a tool call, refine its plan based on observations, or iterate until a quality threshold is met. Without cycles, you can’t express these patterns.
Q8. What is the difference between StateGraph and MessageGraph?
MessageGraph is a convenience wrapper where the entire state is a list of messages — equivalent to StateGraph with MessagesState as the schema. It’s simpler for basic chatbot-style agents. StateGraph is the general-purpose builder that accepts any typed schema. In practice, most production agents use StateGraph because they need additional state fields beyond just messages (step counts, intermediate results, user context, tool outputs).
Q9. What is MessagesState and when would you use it?
MessagesState is a pre-built state schema with a single messages field that uses the add_messages reducer. It’s ideal for conversational agents where message history is the primary state. You’d extend it with additional fields for production:
from langgraph.graph import MessagesState
class AgentState(MessagesState):
current_tool: str
attempt_count: int
user_id: str
Q10. What is the role of START and END in a LangGraph graph?
START is a virtual node that marks where execution begins — you connect it to your first real node with add_edge(START, "first_node"). END is a virtual terminal node — when execution reaches END, the graph run completes and returns the final state. A graph can have multiple paths to END (different conditional branches ending execution), but exactly one entry from START.
Q11. How do you invoke a compiled LangGraph graph?
Use invoke() for a single complete run, or stream() for incremental output:
# Full run
result = app.invoke({"messages": [HumanMessage("Analyze Q3 revenue")]})
# Streaming (node-level updates)
for event in app.stream({"messages": [HumanMessage("Analyze Q3 revenue")]},
stream_mode="updates"):
print(event)
# Async versions
result = await app.ainvoke(...)
async for event in app.astream(...):
...
Q12. What is the Graph API vs. the Functional API in LangGraph?
The Graph API (StateGraph) defines workflows declaratively — you add nodes and edges as objects. The Functional API (@entrypoint and @task decorators) lets you write procedural Python that LangGraph automatically converts into a graph. The Graph API is better for visualization, complex routing, and team collaboration. The Functional API is faster for prototyping and simpler flows. Both compile to the same runtime.
Q13. When is LangGraph overkill? Name three scenarios where you wouldn’t use it.
(1) A linear RAG pipeline — retrieve, generate, return — has no loops, no branching, no state persistence needs. Plain LangChain or a simple function chain suffices. (2) A single API call wrapper — formatting a prompt, calling an LLM, parsing the response — adds no value from graph orchestration. (3) A batch data pipeline — ETL jobs, data cleaning — is better served by Airflow or Prefect, which are optimized for scheduled DAG execution with data lineage tracking.
Q14. What is the create_react_agent function and is it still recommended?
The original create_react_agent from LangChain’s agent module is deprecated. It was replaced by create_agent in the langchain package, which runs on LangGraph underneath. The new function provides the same ReAct tool-calling loop but with LangGraph’s state management, checkpointing, and streaming capabilities. If you’re building a new project in 2026, use create_agent or build your own graph with StateGraph.
Q15. What languages and runtimes does LangGraph support?
LangGraph has two official implementations: Python (langgraph on PyPI) and JavaScript/TypeScript (@langchain/langgraph on npm). Both support the same core concepts (StateGraph, nodes, edges, checkpointing, streaming). The Python version is more mature and has broader ecosystem integration. For deployment, LangSmith Deployment (formerly LangGraph Platform) provides a hosted runtime, or you can self-host using standard Python/Node web servers.
State Management & Reducers (Q16–Q28)
Q16. What is state in LangGraph and how is it structured?
State is a shared typed object — defined as a TypedDict, dataclass, or Pydantic model — that every node in the graph can read and update. It’s the agent’s working memory for a single run. Each field in the state schema represents a piece of data the agent tracks: messages, intermediate results, counters, user context. State is checkpointed after each node execution, which enables persistence, resume, and time-travel debugging.
Q17. What is a reducer in LangGraph? Why are they important?
A reducer controls how partial state updates from nodes are merged into the current state. Without a reducer, a field is simply overwritten by the latest node’s return value. With a reducer (specified via Annotated type hints), you get controlled merge behavior. For example, add_messages appends to a message list instead of replacing it. Reducers are critical when multiple nodes or parallel branches write to the same field — without them, you get race conditions and data loss.
Q18. Explain the add_messages reducer and how it handles message IDs.
add_messages is the most common reducer. It appends new messages to the existing list, but with a twist: if a new message has the same id as an existing message, it replaces that message instead of duplicating it. This enables in-place correction patterns. RemoveMessage objects can be returned to delete specific messages by ID. This ID-based deduplication is what makes message management safe under retries and crash recovery.
Q19. What happens when two parallel branches write to the same state field?
If the field has a reducer, the reducer merges both updates deterministically — for example, operator.add concatenates two lists. If there’s no reducer, this is a conflict. LangGraph’s behavior depends on the execution model: in a single super-step with parallel branches, writes without reducers will raise an error or produce undefined results. The fix is always to define a reducer for any field that multiple nodes might write to concurrently.
Q20. How do you define input and output schemas separately from internal state?
LangGraph supports separate input, output, and internal schemas. The input schema defines what the graph accepts from callers. The output schema defines what it returns. Internal state fields (counters, intermediate results, scratch data) stay hidden from the API surface:
class InputSchema(TypedDict):
query: str
class OutputSchema(TypedDict):
answer: str
sources: list
class InternalState(InputSchema, OutputSchema):
intermediate_results: list
retry_count: int
graph = StateGraph(InternalState, input=InputSchema, output=OutputSchema)
Q21. What is the difference between state, runtime config, and context in LangGraph?
State is data that evolves as the graph runs — messages, counters, intermediate results. It’s checkpointed and mutable. Config is static per-run metadata passed as the second argument to invoke() — things like thread_id, model_name, user permissions. It doesn’t change during execution and shouldn’t contain sensitive data in the checkpointed state. Context refers to external dependencies (database connections, API clients) injected into nodes — typically through closures or dependency injection patterns rather than through state.
Q22. How should you keep state small and typed in production?
Production best practices: (1) Use typed schemas (TypedDict or Pydantic) — never dict. (2) Store large results (documents, images, embeddings) by reference (a URL or key) rather than inline in state. (3) Trim message history proactively — use RemoveMessage or summarization to keep the message list bounded. (4) Separate ephemeral scratch data from durable state fields. (5) Define clear input/output schemas to hide internal fields. Bloated state inflates checkpoint storage and serialization costs.
Q23. What is the Overwrite sentinel and when would you use it?
Overwrite is a special value that forces a field to be completely replaced even when a reducer is defined. For example, if a messages field uses add_messages but you want to hard-reset it (clear the conversation), you return Overwrite([]). It bypasses the reducer’s append behavior. Use it sparingly — typically for reset operations, error recovery, or state migration.
Q24. How does LangGraph handle state schema evolution and migration?
State schemas evolve as features are added. LangGraph checkpoints store the state as-is, so loading an old checkpoint with a new schema can fail if fields are missing or types changed. Best practices: (1) Make new fields optional with defaults. (2) Use Pydantic models with validators for backward compatibility. (3) Version your state schemas. (4) Test replay of old checkpoints against new graph versions before deploying. The LangSmith Deployment platform handles some migration automatically, but self-hosted deployments need manual migration strategies.
Q25. What is a super-step in LangGraph?
A super-step is one round of node execution in the graph’s bulk-synchronous execution model. Within a single super-step, all currently active nodes execute in parallel. After all nodes in the step complete, their state updates are merged (using reducers), a checkpoint is saved, and the next super-step begins based on edge routing. This is how LangGraph achieves deterministic parallel execution — nodes within the same step are isolated from each other’s writes.
Q26. Explain the difference between short-term and long-term memory in LangGraph.
Short-term memory is the state within a single thread — the checkpointed messages and data from the current conversation. It’s scoped to one thread_id and lives in the checkpointer. Long-term memory uses the cross-thread Store — a key-value store (with optional embedding-based search) that persists data across threads. Use short-term for conversation context; use long-term for user preferences, learned facts, and knowledge that should carry across sessions.
Q27. How does the Store work for cross-thread memory?
The Store provides namespaced key-value storage with put, get, and search operations. Each item lives under a namespace (e.g., ("users", user_id)) and has a key and value. PostgresStore is the production backend, supporting embedding-based semantic search with configurable indexes. TTLs let items expire. The store is accessed from nodes via injected dependencies, not through state — it’s a side channel for durable knowledge.
Q28. How do you manage context window limits with message history?
Three strategies: (1) Trimming — remove older messages beyond a count or token limit using RemoveMessage. (2) Summarization — periodically condense older messages into a summary message, replacing the originals. (3) Offloading — move older messages to the long-term Store and retrieve them via semantic search when relevant. In practice, most production agents use trimming with a rolling window (e.g., keep the last 20 messages) combined with a system message that carries essential context from earlier in the conversation.
Graph Architecture & Control Flow (Q29–Q42)
Q29. How do conditional edges work in LangGraph?
Conditional edges use a routing function that takes the current state and returns a string indicating which node to visit next. You map return values to node names:
def route(state):
if state["quality_score"] > 0.8:
return "publish"
elif state["retry_count"] < 3:
return "retry"
return "escalate"
graph.add_conditional_edges("evaluate", route, {
"publish": "publish_node",
"retry": "draft_node",
"escalate": END
})
Q30. What is the Command object and how does it combine routing with state updates?
Command lets a node return a state update and a routing decision in a single return value — replacing the need for a separate conditional edge. It has two key fields: update (partial state to merge) and goto (the next node name). Command(resume=...) is used to continue a graph after an interrupt. Command.PARENT lets a subgraph hand control back to its parent graph. Command simplifies patterns where the node that produces data also knows where execution should go next.
Q31. Explain the Send primitive and map-reduce patterns in LangGraph.
Send enables dynamic fan-out — spawning multiple parallel branches at runtime with different input states. This is LangGraph's map-reduce pattern:
def fan_out(state):
# Spawn one branch per document
return [Send("process_doc", {"doc": doc}) for doc in state["documents"]]
graph.add_conditional_edges("router", fan_out)
# Results gathered via reducer on the shared state field
Each Send creates a parallel execution branch. Results are collected through a reducer on the shared state field. This is essential for parallel document processing, multi-query research, and any task that needs to process a dynamic list of items concurrently.
Q32. How do you prevent infinite loops in cyclic graphs?
Three mechanisms: (1) recursion_limit — set in config, defaults to ~10,000 super-steps since LangGraph 1.0.6 (was 25 previously). Exceeding it raises GraphRecursionError. (2) RemainingSteps — a state channel nodes can read to check how many steps remain and exit gracefully. (3) Business logic caps — maintain a counter in state (e.g., retry_count) and route to END when it exceeds a threshold. Best practice: use both the system-level recursion limit and an application-level budget.
Q33. What is the difference between a subgraph and a node?
A node is a single function. A subgraph is a complete compiled graph used as a node within a parent graph — it has its own state schema, nodes, and edges. Subgraphs enable modular composition: a "research agent" subgraph, a "writing agent" subgraph, and a "review agent" subgraph composed by a parent supervisor. Subgraphs can share state with the parent (mapped schemas) or be fully isolated (message-passing). They have their own checkpointing and can be developed and tested independently.
Q34. How do errors and retries work in LangGraph?
LangGraph provides RetryPolicy for automatic retries on node failures — configurable backoff, max attempts, and exception filters. For business-logic failures (not exceptions), handle them in state: a node returns {"error": "rate limited", "retry_count": count + 1} and a conditional edge routes back to retry or to an error handler. After a crash, the checkpointer enables resume from the last successful super-step. Side effects in retried nodes must be idempotent.
Q35. What is the ToolNode and tools_condition pattern?
ToolNode is a pre-built node that executes tool calls from an LLM's response. tools_condition is a routing function that checks whether the LLM response contains tool calls — if yes, route to ToolNode; if no, route to END or the next step. Together they implement the standard ReAct tool-calling loop:
from langgraph.prebuilt import ToolNode, tools_condition
tool_node = ToolNode(tools=[search_tool, calc_tool])
graph.add_node("tools", tool_node)
graph.add_conditional_edges("agent", tools_condition)
Q36. How do you build a quality-score retry loop?
A common pattern: generate output → evaluate quality → retry if below threshold, up to a cap:
def evaluate(state):
score = quality_check(state["draft"])
return {"quality_score": score, "attempt": state["attempt"] + 1}
def should_retry(state):
if state["quality_score"] >= 0.8:
return "accept"
if state["attempt"] >= 3:
return "accept_best" # Take best attempt
return "retry"
graph.add_conditional_edges("evaluate", should_retry, {
"accept": "publish",
"accept_best": "publish",
"retry": "generate"
})
Q37. What is node granularity and why does it matter?
Node granularity is how much work each node does. Fine-grained nodes (one operation per node) give better checkpointing (resume at a finer point), retry isolation (only the failed operation retries), and interrupt placement (can pause between any two operations). Coarse-grained nodes (multiple operations) reduce graph complexity but make recovery less precise. The rule: one side effect per node. Each API call, database write, or LLM call should be its own node so it can be individually checkpointed and retried.
Q38. Explain the difference between synchronous and asynchronous execution in LangGraph.
LangGraph supports both. invoke() and stream() are synchronous — they block until completion. ainvoke() and astream() are async (asyncio) — they yield control while waiting on I/O. For production web servers (FastAPI, Starlette), use async methods to avoid blocking the event loop. Node functions can be either sync or async — LangGraph handles the dispatch. The checkpointer also has async variants (AsyncPostgresSaver).
Q39. How do you visualize a LangGraph graph?
Compiled graphs have visualization methods: app.get_graph().draw_mermaid() generates a Mermaid diagram string, and app.get_graph().draw_mermaid_png() renders a PNG image. You can also export to DOT format for Graphviz. Visualization is essential for debugging complex multi-agent graphs and for documentation. LangSmith also provides visual graph inspection in its UI.
Q40. What are breakpoints and how do they differ from interrupts?
Static breakpoints are set at compile time — they pause execution before or after a specific node for debugging purposes. They're development tools. Dynamic interrupts (interrupt()) are set in code and fire conditionally based on runtime state — they pause execution for human approval, review, or input. Interrupts are production features. Both require a checkpointer to save state at the pause point. Breakpoints use compile(interrupt_before=["node_name"]); interrupts use the interrupt() function inside node code.
Q41. What is the recursion_limit and how has it changed in LangGraph 1.x?
The recursion_limit caps the maximum number of super-steps a graph can execute before raising GraphRecursionError. In early LangGraph, the default was 25 — low enough to catch infinite loops quickly but too low for complex multi-step agents. Since LangGraph 1.0.6, the default is approximately 10,000 super-steps. You set it in config: app.invoke(inputs, config={"recursion_limit": 50}). For production, set it to the realistic maximum for your workflow plus a safety margin.
Q42. How does LangGraph handle pending writes on node failure within a super-step?
If a node fails mid-super-step (before all parallel nodes complete), its state updates become pending writes — they're buffered but not yet committed. LangGraph discards all pending writes from the failed super-step and restores state to the last committed checkpoint. This ensures atomicity: either all nodes in a super-step succeed and their writes commit, or none do. This is important for maintaining state consistency in parallel execution.
Checkpointing & Persistence (Q43–Q55)
Q43. What is a checkpointer in LangGraph and why is it essential?
A checkpointer saves the graph's state after each super-step execution. It enables: (1) Crash recovery — resume from the last checkpoint instead of restarting. (2) Human-in-the-loop — pause and resume execution across requests. (3) Time-travel debugging — inspect historical states and replay from any point. (4) Conversation memory — maintain context across multiple invocations within a thread. Without a checkpointer, the graph is stateless between invocations — every call starts fresh.
Q44. Compare the three checkpointer backends: InMemorySaver, SqliteSaver, and PostgresSaver.
Backend: InMemorySaver | Use Case: Development, testing | Concurrency: Single process | Persistence: Lost on restart
Backend: SqliteSaver | Use Case: Local development, single-user apps | Concurrency: Single writer | Persistence: File-based, durable
Backend: PostgresSaver / AsyncPostgresSaver | Use Case: Production | Concurrency: Safe multi-writer | Persistence: Durable, cross-session
Always use PostgresSaver (or AsyncPostgresSaver) in production. It handles concurrent access safely, supports cross-session retrieval, and integrates with standard database backup/migration tools.
Q45. What is a thread_id and how does it scope checkpoints?
thread_id is a string identifier passed in the runtime config that scopes all checkpoints to a single conversation or workflow instance. All invocations with the same thread_id share state — the agent picks up where it left off. Different thread_id values are completely isolated. It's analogous to a session ID. You generate a unique thread_id per user conversation and store the mapping in your application layer.
Q46. How do you resume a graph after a crash?
If the graph was compiled with a durable checkpointer, simply invoke it again with the same thread_id. The graph loads the last committed checkpoint and resumes from the node that follows the last completed super-step. If the crash happened mid-node, that node re-executes from the beginning — which means any side effects in that node must be idempotent (e.g., using idempotency keys for API calls, checking if a record already exists before creating it).
Q47. What is time-travel debugging in LangGraph?
Time travel lets you inspect the state at any historical checkpoint and optionally fork execution from that point. Using get_state_history(), you get a list of all checkpoints for a thread. You can then call update_state() to modify a past state and re-invoke the graph — it replays from that modified checkpoint, taking a different execution path. This is invaluable for debugging: you can find where an agent went wrong, fix the state, and see if the corrected path produces the right result.
Q48. Explain durability modes: sync, async, and exit.
These control when checkpoints are persisted: sync — the checkpoint is written and confirmed before the next super-step begins. Safest, but adds latency. async — the checkpoint write is dispatched asynchronously; execution continues without waiting for confirmation. Faster, but a crash between dispatch and write loses the checkpoint. exit — checkpoints are only written when the graph run completes (reaches END or is interrupted). Fastest, but no mid-run recovery. Choose based on your durability vs. latency trade-off.
Q49. How do you handle idempotent side effects under checkpoint replay?
When a node re-executes after crash recovery, any side effects (API calls, database writes, emails) will fire again. Solutions: (1) Use idempotency keys — include a unique key (e.g., checkpoint ID + node name) with every external API call. (2) Check-before-write — verify if the action was already completed before executing. (3) Use the @task decorator in the Functional API, which checkpoints at the task level and skips re-execution of completed tasks on replay. (4) Record outcomes in state — if the node already wrote "email_sent": True, skip the send on re-execution.
Q50. How do you manage checkpoint storage growth in production?
Checkpoints accumulate over time and can consume significant database storage. Strategies: (1) Set retention policies — delete checkpoints older than N days or keep only the last N per thread. (2) Delete completed threads — once a workflow is fully resolved, remove its checkpoints. (3) Store large state values by reference — instead of embedding a full document in state, store a URL or key and retrieve it when needed. (4) Choose appropriate durability modes — exit mode creates fewer checkpoints than sync. (5) Monitor checkpoint table size as a standard operational metric.
Q51. What is get_state and how do you use it for inspection?
get_state(config) returns the current state snapshot for a thread, including the state values, the next nodes to execute, and metadata. It's useful for building UIs that show agent status, debugging stuck agents, and implementing approval flows where a reviewer needs to see what the agent plans to do next. The return value is a StateSnapshot object with .values, .next, and .config fields.
Q52. How does update_state work and when would you use it?
update_state(config, values, as_node=...) manually injects a state update as if a specific node had produced it. Use cases: (1) Human review — a reviewer approves an agent's draft by updating a status field. (2) Correction — fix a wrong intermediate result without re-running the entire graph. (3) Testing — inject specific state conditions to test downstream behavior. (4) Time-travel forking — update a historical state and re-invoke to explore alternative paths. The as_node parameter determines which node's outgoing edges are followed after the update.
Q53. How do you implement conversation memory that persists across sessions?
Combine a checkpointer with consistent thread_id management. Each user gets a unique thread_id stored in your application database. When they return, you invoke the graph with the same thread_id — the checkpointer loads their previous conversation state. For long-lived conversations, implement message trimming to keep state manageable. For facts that should persist beyond a single conversation, use the cross-thread Store to save user preferences and learned context.
Q54. What is the difference between within-thread checkpointing and cross-thread Store memory?
Within-thread checkpointing saves state snapshots scoped to a single thread_id — it's the conversation's history and working memory. The cross-thread Store is a separate key-value store accessible from any thread. Use checkpointing for "what happened in this conversation." Use the Store for "what I know about this user across all conversations." They complement each other: checkpointing gives you session continuity, the Store gives you user-level persistence.
Q55. How do you test checkpoint-dependent behavior in unit tests?
Use InMemorySaver in tests — it's fast, requires no infrastructure, and supports all checkpointer operations. Test patterns: (1) Invoke, then re-invoke with the same thread_id to verify state continuity. (2) Use update_state to inject specific states and verify downstream behavior. (3) Use get_state_history to verify the expected sequence of checkpoints. (4) Simulate crashes by stopping mid-graph and re-invoking to verify resume correctness.
Human-in-the-Loop Patterns (Q56–Q67)
Q56. What is human-in-the-loop (HITL) in LangGraph and why does it matter?
HITL is the ability to pause agent execution, present the current state to a human for review, and resume based on their decision. It matters because production AI agents frequently need human oversight — approving financial transactions, reviewing generated content, confirming destructive actions, or providing information the agent can't access. LangGraph makes HITL a first-class primitive, not an afterthought.
Q57. How do you implement an interrupt in LangGraph?
Use the interrupt() function inside a node to pause execution:
from langgraph.types import interrupt, Command
def review_node(state):
decision = interrupt({
"question": "Approve this refund?",
"amount": state["refund_amount"],
"customer": state["customer_id"]
})
if decision == "approve":
return {"status": "approved"}
return Command(goto="escalate")
When interrupt() is reached, execution pauses. The value passed to interrupt() is sent to the calling application (your UI). When the human responds, you resume with Command(resume=decision_value).
Q58. What are the prerequisites for interrupts to work?
Two requirements: (1) A checkpointer must be configured — interrupts save state at the pause point, and the checkpointer persists it so the graph can be resumed later (minutes, hours, or days). (2) A thread_id must be set in config — the resume call needs to find the right paused execution. Without both, interrupt() will raise an error.
Q59. Explain the approve/edit/reject pattern for HITL.
A standard three-way decision pattern: Approve — the human accepts the agent's proposed action; resume execution normally. Edit — the human modifies the proposed action (changes amount, corrects a field); update state with the edits before resuming. Reject — the human denies the action; route to an alternative path (escalate, cancel, try a different approach). This is implemented with interrupt() returning the decision, and a conditional edge routing based on it.
Q60. What happens to code execution when a graph resumes after an interrupt?
The interrupted node re-executes from the beginning of its function. The interrupt() call returns the value provided via Command(resume=...) instead of pausing. This means any code before the interrupt() call runs again — so pre-interrupt side effects must be idempotent. This is a common gotcha: if the node makes an API call before the interrupt, that call will fire again on resume.
Q61. How do static breakpoints differ from dynamic interrupts?
Static breakpoints are set at compile time with compile(interrupt_before=["node_name"]) — they always pause before that node, regardless of state. They're for debugging and development. Dynamic interrupts use the interrupt() function in code and can be conditional — pause only if the refund is over $500, pause only on the first attempt, etc. Dynamic interrupts are production features; static breakpoints are development tools.
Q62. How do you implement per-tool interrupts with create_agent?
The create_agent function supports interrupt_on configuration per tool — specifying which tools require human approval before execution:
agent = create_agent(
model,
tools=[search_tool, delete_tool, send_email_tool],
interrupt_on={"delete_tool": True, "send_email_tool": True}
)
This triggers an interrupt whenever the agent wants to call delete_tool or send_email_tool, showing the human the proposed call and arguments before execution.
Q63. What is the "blast-radius rule" for deciding when to interrupt?
The blast-radius rule: interrupt when the cost of an incorrect action exceeds the cost of human delay. Read-only operations (searching, retrieving, analyzing) rarely need approval. Write operations should be assessed by impact: sending an email to one person (low blast radius) vs. sending to 10,000 customers (high blast radius). Financial transactions over a threshold, data deletions, and external communications are common interrupt points. The rule keeps approval fatigue low while catching high-impact mistakes.
Q64. How do you handle multiple sequential interrupts in a single graph run?
Each interrupt() call is indexed in order. When resuming, LangGraph matches resume values to interrupts by index. If a node has two interrupts — the first already resumed, the second now pausing — the framework skips the first (using the previously resumed value) and pauses at the second. This enables multi-stage review flows where different aspects of a task require separate approvals.
Q65. How do you prevent stale data issues in long-paused interrupts?
If an interrupt is paused for hours or days, the data the agent based its decision on may have changed. Solutions: (1) Re-fetch critical data after resume — don't rely on pre-interrupt state for time-sensitive fields. (2) Include a timestamp in the interrupt payload so the reviewer sees when the data was current. (3) Set expiration logic — if more than N hours have passed, re-run the analysis node before presenting for review. (4) Use optimistic locking — check that key fields haven't changed between pause and resume.
Q66. How do you build a HITL flow that works with a web UI?
The pattern: (1) Your web server invokes the graph, which pauses at an interrupt. (2) The interrupt payload (what needs review) is returned to the server. (3) The server stores the thread_id and presents a review form to the user. (4) When the user decides, the server calls app.invoke(Command(resume=decision), config={"configurable": {"thread_id": thread_id}}). (5) The graph resumes and continues execution. The key is persisting the thread_id between the pause and resume HTTP requests.
Q67. What are common HITL gotchas in LangGraph?
(1) Forgetting the checkpointer — interrupts silently fail. (2) Non-idempotent pre-interrupt code — side effects fire again on resume. (3) Approval fatigue — interrupting too often trains reviewers to click "approve" without reading. (4) Not handling timeout — a paused graph that's never resumed leaks resources. (5) Missing error handling on resume — the human might provide invalid input. (6) Testing only the happy path — test reject, edit, and timeout scenarios.
Multi-Agent Systems (Q68–Q80)
Q68. What is a multi-agent system in LangGraph?
A multi-agent system is a graph where multiple specialized agents collaborate to complete a task. (For a real-world look at how enterprise platforms approach multi-agent orchestration, see our Automation Anywhere Agentic Guide.) Each agent is typically a subgraph with its own tools, prompts, and behavior. A parent graph coordinates them — routing tasks to the right agent, aggregating results, and managing shared state. Examples: a research agent + writing agent + review agent for content creation, or a planner agent + coder agent + tester agent for software development.
Q69. Explain the three main multi-agent topologies: supervisor, swarm, and hierarchical.
Supervisor — a central "manager" node routes tasks to specialist agents and aggregates their responses. Simple but creates a bottleneck. Swarm — agents hand off directly to each other without a central coordinator. More flexible but harder to debug — can lead to ping-pong loops. Hierarchical — supervisors at multiple levels: a top-level planner delegates to mid-level coordinators, which delegate to specialists. Best for complex domains with clear decomposition boundaries.
Q70. When does a single agent with tools beat a multi-agent system?
Most of the time. A single agent with well-designed tools is simpler, cheaper, and easier to debug. Use multi-agent only when: (1) Different tasks require fundamentally different prompts or models — a coding agent needs a code model, a writing agent needs a creative model. (2) The task naturally decomposes into sequential handoffs — research → draft → review. (3) You need isolation — each agent has its own state and error boundary. If one agent with the right tools can handle the workflow, adding agents adds complexity without value.
Q71. How do you implement a supervisor pattern in LangGraph?
def supervisor(state):
response = llm.invoke([
SystemMessage("Route to the best agent for this task."),
*state["messages"]
])
return Command(goto=response.tool_calls[0]["name"],
update={"messages": [response]})
graph = StateGraph(AgentState)
graph.add_node("supervisor", supervisor)
graph.add_node("researcher", researcher_subgraph)
graph.add_node("writer", writer_subgraph)
graph.add_node("reviewer", reviewer_subgraph)
graph.add_edge(START, "supervisor")
# Each agent routes back to supervisor when done
for agent in ["researcher", "writer", "reviewer"]:
graph.add_edge(agent, "supervisor")
Q72. What is the difference between shared-state and message-passing multi-agent designs?
Shared-state — all agents read and write to a common state object. Simpler, but creates coupling — one agent's state changes affect all others. Good for tightly coordinated teams. Message-passing — agents communicate by sending messages, each maintaining its own private state. More modular and isolated — easier to develop and test independently. LangGraph supports both: shared state through subgraphs with mapped schemas, message-passing through tool calls or structured handoff objects.
Q73. How do subgraphs share state with parent graphs?
Through schema mapping. The parent and subgraph can have different state schemas, but overlapping field names are automatically shared. You can also explicitly map fields. The subgraph reads mapped fields from the parent at entry and writes results back at exit. Private fields in the subgraph's schema (not in the parent's) stay isolated. The checkpointer can be shared (subgraph checkpoints stored alongside parent) or separate.
Q74. How do you prevent ping-pong loops in multi-agent systems?
(1) Maintain a shared task state — each agent marks tasks as "in progress" or "completed" so another agent doesn't re-route back. (2) Set hop limits — a counter increments on each handoff, and the system forces completion after N hops. (3) Use narrow agent descriptions — if two agents' capabilities overlap too much, the supervisor can't decide who to route to and bounces between them. (4) Track handoff history — route to an agent only if it hasn't already handled this specific task.
Q75. How do you handle errors in multi-agent workflows?
Errors in one agent shouldn't crash the entire workflow. Strategies: (1) Wrap each subgraph invocation in error handling — catch exceptions and record failures in state. (2) Define fallback agents — if the primary agent fails, route to a simpler fallback. (3) Use structured error results — instead of raising exceptions, return {"error": "description", "partial_result": ...} so the supervisor can decide what to do. (4) Set per-agent timeouts — don't let one stuck agent block the workflow indefinitely.
Q76. How do you implement agent handoffs in LangGraph?
Handoffs transfer control from one agent to another, optionally passing context. In LangGraph, use Command(goto="target_agent", update={"handoff_context": ...}) to route and pass data. The helper library langgraph.prebuilt provides handoff utilities that handle message formatting and state transfer. For clean handoffs, define a handoff schema specifying what context is passed — don't dump the entire state.
Q77. Explain streaming in multi-agent systems.
When streaming a multi-agent graph, events come from all agents. LangGraph prefixes each event with a namespace indicating which subgraph produced it. This lets your UI show which agent is currently active and display their outputs separately. Use stream_mode="updates" to get node-level events with their source graph. For token-level streaming from the LLM within a specific agent, combine stream_mode="messages" with namespace filtering.
Q78. How do you evaluate and debug multi-agent systems?
Evaluation: Test at multiple levels — unit test individual agents, integration test agent pairs, and end-to-end test the full workflow. Use trajectory evaluation to check not just the final output but the sequence of agent invocations. Debugging: LangSmith tracing shows the full execution trace across agents. Use graph visualization to verify routing logic. Log handoff decisions with the supervisor's reasoning. Test with deterministic inputs to reproduce specific agent interaction patterns.
Q79. What is the difference between a multi-agent system and a workflow with specialized nodes?
The line is blurry, but generally: a multi-agent system uses LLMs for routing decisions (the supervisor asks a model "which agent should handle this?"), while a specialized-node workflow uses deterministic routing (conditional edges based on state values). Multi-agent adds flexibility (handles novel requests) at the cost of predictability and cost. If your routing logic can be expressed as simple conditionals, you don't need the LLM overhead of a supervisor agent.
Q80. How do you architect a team of agents with different models?
Assign models based on each agent's needs: a coding agent gets a code-specialized model, a summarization agent gets a fast/cheap model, a planning agent gets the most capable model. In LangGraph, each agent subgraph binds its own LLM instance. The supervisor uses a model optimized for classification/routing (often a smaller, faster model). Cost optimization: use the cheapest model that meets each agent's accuracy requirements, and reserve expensive models for the tasks that genuinely need them.
Tools, MCP & Integrations (Q81–Q90)
Q81. How does tool calling work in LangGraph?
Tools are Python functions decorated with @tool that the LLM can invoke. The agent node sends messages to the LLM, which returns tool call requests. ToolNode executes those calls and adds results to state. A conditional edge (tools_condition) checks if there are tool calls — if yes, route to ToolNode; if no, the agent is done. The loop repeats until the LLM stops requesting tools. LangGraph handles parallel tool execution automatically when the LLM requests multiple tools in one response.
Q82. What are injected parameters in tools?
Injected parameters are function arguments that come from the runtime context (not from the LLM). They're marked with InjectedToolArg and hidden from the LLM's tool schema. Common uses: injecting the current user's ID for authorization, passing a database connection, or providing the thread_id. The LLM never sees or controls these values — they're set by the framework.
Q83. How do you connect LangGraph to MCP servers?
MCP (Model Context Protocol) is the emerging standard for tool interoperability across agent frameworks — we covered it in depth in our Atlassian Rovo MCP guide. Use the langchain-mcp-adapters package to convert MCP tools into LangChain-compatible tools:
from langchain_mcp_adapters import MCPToolkit
toolkit = MCPToolkit(server_url="http://localhost:3000")
tools = toolkit.get_tools()
# Use these tools in your LangGraph agent
agent = create_agent(model, tools=tools)
MCP servers expose tools, resources, and prompts via a standardized protocol. LangGraph agents can discover and invoke them at runtime. Both stdio and HTTP transports are supported. This lets your agent connect to databases, APIs, file systems, and external services through a growing ecosystem of MCP servers.
Q84. How do you manage large tool catalogs without blowing up context windows?
Strategies: (1) Domain splitting — group tools by domain and only load the relevant group based on the current task. (2) Dynamic tool selection — use a fast classifier to select the top-k relevant tools before sending them to the agent. (3) Tool description optimization — write concise descriptions that help the model select correctly without long explanations. (4) Two-stage selection — a router agent picks the domain, then the specialist agent gets only that domain's tools.
Q85. How do you secure write tools in LangGraph?
(1) Use HITL interrupts for high-impact write operations. (2) Implement strict input validation in every tool function — don't trust LLM-generated parameters. (3) Use delegated permissions — tools check the current user's authorization via injected parameters. (4) Apply allow-lists — restrict which tools each agent can call. (5) Use idempotency keys for external API calls. (6) Log all tool executions for audit trails. (7) Implement rate limiting per tool to prevent runaway agents from flooding external services.
Q86. What is the difference between LangGraph's tool calling and function calling?
They're the same concept at different levels. Function calling is the LLM API feature — the model outputs structured tool call requests. Tool calling in LangGraph is the framework pattern — ToolNode receives those requests, executes the actual functions, and feeds results back. LangGraph adds orchestration on top of raw function calling: parallel execution, error handling, retry policies, state tracking, and the tool-calling loop.
Q87. How do you handle tool errors gracefully?
Don't raise exceptions from tools — return structured error messages that the LLM can interpret:
@tool
def query_database(sql: str) -> str:
try:
result = db.execute(sql)
return json.dumps(result)
except DatabaseError as e:
return f"Query failed: {e}. Please fix the SQL and try again."
The LLM receives the error message as a tool result and can adjust its approach — fix the SQL, try a different tool, or ask the user for clarification. Unhandled exceptions crash the node; structured errors keep the agent loop running.
Q88. How do you test tools in isolation from the graph?
Test tools as regular Python functions with known inputs and expected outputs. Mock external dependencies (databases, APIs). For tool schema testing, verify the function signature matches what the LLM expects using tool.args_schema. For integration testing, use update_state(as_node="agent") to inject specific tool call requests and verify ToolNode handles them correctly. Test edge cases: malformed inputs, timeout conditions, rate limits.
Q89. How does LangGraph integrate with LangSmith for observability?
LangSmith (formerly LangGraph Platform's observability layer) provides: Tracing — every node execution, LLM call, and tool invocation is captured as a span with inputs, outputs, and latency. Metadata tagging — add custom tags for filtering (user_id, workflow_type, environment). Dashboards — aggregate metrics on agent success rates, latency, and cost. PII redaction — mask sensitive data in traces. Evaluation — run evals on traced runs to measure quality. Set LANGCHAIN_TRACING_V2=true and your API key to enable.
Q90. How do you implement prompt injection defenses in LangGraph tools?
(1) Validate all tool inputs against strict schemas — reject unexpected patterns. (2) Use parameterized queries for database tools — never interpolate user input into SQL. (3) Sanitize tool outputs before feeding them back to the LLM — strip any instruction-like content from external data. (4) Implement a "tool output firewall" node that checks results for suspicious patterns before the agent processes them. (5) Use separate system prompts for different agents to limit the blast radius of injected instructions.
Production, Deployment & Scaling (Q91–Q100)
Q91. What are the deployment options for LangGraph in production?
Two main paths: (1) Self-managed — deploy as a standard Python web service (FastAPI, Flask) with a Postgres checkpointer. You manage scaling, monitoring, and infrastructure. (2) LangSmith Deployment (formerly LangGraph Platform) — a hosted runtime from LangChain that handles scaling, persistence, and the API layer. It provides a standard REST API, built-in auth, and managed checkpointing. Self-managed gives full control; LangSmith Deployment reduces operational overhead.
Q92. What is the langgraph.json configuration file?
langgraph.json is the deployment manifest for LangSmith Deployment. It specifies: the graph definitions (entry points), dependencies, environment variables, and the Python/Node version. It's similar to a Docker Compose file but specific to LangGraph's hosting platform. When deploying self-managed, you don't need it — you configure everything in your own application code.
Q93. Explain the different streaming modes in LangGraph.
Mode: values | What It Streams: Full state after each super-step | Use Case: Debugging, full state inspection
Mode: updates | What It Streams: Partial state updates from each node | Use Case: UI progress indicators, node-level tracking
Mode: messages | What It Streams: Individual LLM tokens | Use Case: Real-time chat UIs, typewriter effect
Mode: custom | What It Streams: Custom events emitted by nodes | Use Case: Progress bars, status updates
Mode: debug | What It Streams: Detailed execution trace | Use Case: Development debugging
You can combine modes: stream_mode=["updates", "messages"] gives both node-level and token-level events.
Q94. How do you reduce latency in LangGraph agents?
(1) Minimize sequential hops — parallelize independent work with Send. (2) Use smaller, faster models for routing and classification. (3) Enable streaming — show progress instead of making users wait for completion. (4) Cache repeated operations — if a node fetches the same data frequently, cache it. (5) Use prompt caching (supported by Anthropic and OpenAI) to reduce LLM latency on repeated prompt prefixes. (6) Optimize tool execution — async I/O, connection pools, batch API calls.
Q95. How do you control cost per agent run?
(1) Set recursion limits and per-run token budgets. (2) Trim message history aggressively — fewer tokens per LLM call. (3) Use model tiering — expensive models for complex reasoning, cheap models for simple tasks. (4) Implement cost gateways — track cumulative token usage in state and route to END when budget is exhausted. (5) Monitor per-run costs in LangSmith and set alerts on outliers. (6) Cache deterministic operations — don't call the LLM for decisions that could be rule-based.
Q96. How do you horizontally scale LangGraph?
LangGraph workers are stateless (state lives in the checkpointer). This means you can run multiple instances behind a load balancer. Requirements: (1) Use PostgresSaver (shared database) so any worker can resume any thread. (2) Implement per-thread locking to prevent concurrent writes to the same thread. (3) Use connection pools (pgBouncer) for database scalability. (4) For task queue patterns, use Celery or a message broker to distribute graph invocations across workers. (5) Monitor database connection saturation.
Q97. How do you version graphs and do safe rollouts?
(1) Version your graph definitions — each deployment gets a version tag. (2) Ensure backward-compatible state schemas — new fields are optional with defaults. (3) Test new graph versions against old checkpoints — verify that in-flight threads resume correctly. (4) Use canary deployments — route 5% of new threads to the new version, monitor, then ramp up. (5) Keep old graph versions available to complete in-flight threads that were started on that version.
Q98. How do you implement observability for production LangGraph agents?
Layer your observability: (1) Tracing — LangSmith, Langfuse, or OpenTelemetry for per-run execution traces. (2) Metrics — track success rate, latency (P50/P95/P99), cost per run, and error rate. Export to Prometheus/Grafana or Datadog. (3) Logging — structured logs from each node with request IDs for correlation. (4) Alerting — set alerts on error rate spikes, latency increases, and cost outliers. (5) PII redaction — mask sensitive data in traces before storage.
Q99. How do you handle rate limits from LLM providers in production?
(1) Implement exponential backoff with jitter in your LLM client. (2) Use RetryPolicy on agent nodes for automatic retries. (3) Queue requests — don't send all concurrent graph runs to the LLM simultaneously. (4) Use multiple API keys or model providers for failover. (5) Cache deterministic LLM calls — if the same prompt always produces the same result, cache it. (6) Monitor rate limit headers and throttle proactively before hitting limits.
Q100. What metrics should you track for LangGraph agents in production?
Reliability: Success rate, error rate by type, crash recovery rate. Performance: End-to-end latency, time-to-first-token, per-node latency. Cost: Tokens consumed per run, LLM API cost per run, checkpoint storage growth. Quality: Task completion rate, human override rate (for HITL agents), user satisfaction scores. Operational: Concurrent runs, checkpoint database size, queue depth, rate limit utilization. (For a broader look at agent observability in enterprise settings, see our ServiceNow AI Control Tower guide.)
Scenario-Based & System Design (Q101–Q110)
Q101. Design a customer support agent that processes refunds with human approval for amounts over $500.
Architecture: StateGraph with nodes: classify_intent → gather_info → calculate_refund → review_gate → process_refund. The review gate uses a conditional edge: if amount > 500, interrupt for human approval; otherwise, auto-approve. Checkpointer: AsyncPostgresSaver for persistence across the approval wait. State includes: customer_id, order_id, refund_amount, approval_status, messages. The process_refund node uses an idempotency key (order_id + refund_id) to prevent double refunds on crash recovery. SLA tracking: record interrupt timestamp in state; alert if not resolved within 4 hours.
Q102. You're asked to design a research agent that handles 10,000 requests per day. Walk through your architecture.
Architecture: Stateless LangGraph workers behind a load balancer, AsyncPostgresSaver with connection pooling (pgBouncer). Use a task queue (Celery + Redis) for request distribution. Graph: plan_research → fan-out with Send to parallel search nodes → synthesize → review → format_output. Model tiering: fast model for planning and routing, capable model for synthesis. Cost controls: 50-step recursion limit, 10,000-token budget per run, message trimming to last 10 messages. Observability: LangSmith tracing, custom metrics exported to Prometheus, alerts on error rate > 5%. Estimated cost: ~$0.15-0.30 per research run depending on depth.
Q103. A medical documentation agent is producing duplicate clinical drafts after crashes. Diagnose and fix.
Root cause: The draft generation node has a side effect (writing to the EHR system) that isn't idempotent. On crash recovery, the node re-executes and creates a second draft. Fix: (1) Use the @task decorator or implement manual idempotency — check if a draft already exists for this case ID before creating. (2) Switch from async to sync durability mode to ensure checkpoints are committed before proceeding to the write step. (3) Separate the draft generation (pure computation) from the draft submission (side effect) into two nodes — the checkpoint between them prevents re-generation. (4) Add a clinician review interrupt before submission as a safety net.
Q104. Your supervisor agent keeps misrouting IT incidents between the network and security agents. How do you fix it?
Diagnosis: Likely overlapping agent descriptions — the supervisor can't distinguish "network connectivity issue" from "security-related network block." Fixes: (1) Narrow agent descriptions — give each agent specific, non-overlapping keywords and examples. (2) Add structured pre-routing — before the LLM supervisor, a deterministic classifier checks for known patterns (specific error codes, keywords) and routes directly. (3) Include routing examples in the supervisor's prompt — few-shot examples of correct routing decisions. (4) Track routing accuracy as a metric and run regular routing evaluations. (5) Allow agents to "bounce back" with a reason — if the security agent determines it's a network issue, it can hand off with context.
Q105. Your checkpoint table has grown to 500 GB and queries are slowing down. What's your remediation plan?
Immediate: (1) Delete checkpoints for completed/expired threads — this is usually the biggest win. (2) Identify threads with unusually large state (embedded documents, images) and move to reference-based storage. Medium-term: (3) Implement retention policies — auto-delete checkpoints older than 30 days for completed threads. (4) Switch large state fields to store references (S3 URLs) instead of inline data. (5) Consider exit durability mode for workflows that don't need mid-run recovery. Monitoring: Track checkpoint table size, average state size, and checkpoints per thread as standard operational metrics.
Q106. A customer's chat UI shows a long delay before any response. The agent works correctly but feels slow. Optimize.
Fixes: (1) Enable streaming with stream_mode="messages" — tokens appear as the LLM generates them instead of waiting for the full response. (2) Check for proxy or load balancer buffering — some reverse proxies buffer SSE streams. (3) Add custom streaming events at the start of long operations — "Searching your order history..." gives immediate feedback. (4) Use a fast model for the first response (acknowledge the request) and a capable model for the detailed answer. (5) Ensure the v2 streaming chunk format is enabled for optimal incremental delivery. (6) Measure time-to-first-token as a key metric.
Q107. You need to deploy a graph update, but there are 200 paused HITL threads waiting for approval. How do you handle it?
Strategy: (1) Make the new graph version backward-compatible — paused threads must resume correctly. Test this explicitly by loading old checkpoints with the new graph and verifying resume works. (2) If the state schema changed, add new fields as optional with defaults so old checkpoints deserialize correctly. (3) If the interrupt signature changed (different payload format), maintain both old and new interrupt handlers during a transition period. (4) Deploy with canary — new threads go to the new version, paused threads complete on the old version. (5) Monitor resume success rate after deployment.
Q108. Design a LangGraph agent that can handle both structured (form-filling) and unstructured (free-text conversation) interactions.
Architecture: A mode-switching graph with two subgraphs. The classify_mode entry node determines if the user wants structured input (filling a form, providing specific data) or unstructured conversation (asking questions, exploring options). The structured subgraph follows a deterministic sequence: validate fields → prompt for missing fields → confirm → submit. The unstructured subgraph is a standard ReAct agent with tools. A mode-switch edge lets the agent transition between modes mid-conversation — if the conversational agent identifies that structured data collection is needed, it hands off to the form subgraph.
Q109. Compare LangGraph, CrewAI, and the Claude Agent SDK for a production agent deployment. When would you choose each?
LangGraph: Choose when you need fine-grained control over agent state, loops, and execution flow. Best for complex multi-step workflows with human-in-the-loop, custom routing, and production persistence requirements. Steepest learning curve but most flexible. CrewAI: Choose for role-based multi-agent systems where you want a higher-level abstraction — define agents by role, goal, and backstory. Faster to prototype, but less control over execution internals. Better for content creation and research tasks. Claude Agent SDK: Choose for single-agent tool-use loops where you want a clean, minimal framework tied to Anthropic's models. Simpler than LangGraph but less orchestration capability. Note: AutoGen is archived as of 2026 and no longer recommended. For how AWS approaches production agent infrastructure, see our Amazon Bedrock AgentCore guide.
Q110. Design an enterprise knowledge assistant that searches internal documents, respects access controls, and cites sources.
Architecture: (1) input_guard — validates the query and checks for prompt injection attempts. (2) classify — determines the query type (factual lookup, analysis, comparison) and required document domains. (3) retrieve — uses permissioned retrieval (user's access level injected via config, applied as a filter on the vector store). (4) grade — evaluates retrieved documents for relevance, discards irrelevant results. (5) generate — produces the answer with inline citations (document name, section, page number). (6) verify — checks that every claim in the response is supported by a retrieved source. State includes: user_id, access_level, retrieved_docs, citations. Use the Store for caching frequently accessed documents. MCP integration for connecting to SharePoint, Confluence, and other document stores. Cost: cap at 20 tool calls per query, trim context to last 5 messages.
Frequently Asked Questions
Is LangGraph free to use?
Yes. LangGraph is MIT-licensed and free for both commercial and non-commercial use. The Python (langgraph) and TypeScript (@langchain/langgraph) libraries are open source. LangSmith Deployment (the hosted platform) is a paid service from LangChain Inc., but self-hosting LangGraph with your own infrastructure is entirely free.
What version of LangGraph should I learn for interviews in 2026?
Focus on LangGraph 1.x (specifically 1.2, the latest stable as of October 2026). The 1.x API introduced significant changes from 0.x — including the Functional API, the Command primitive, the updated create_agent replacement for create_react_agent, and the 10,000-step default recursion limit. Don't study deprecated 0.x patterns like AgentExecutor.
How popular is LangGraph compared to alternatives in 2026?
LangGraph has approximately 43,000+ GitHub stars and is the most widely adopted production agent framework. According to LangChain's State of Agent Engineering report, 57% of surveyed organizations have agents in production, with LangGraph being the leading framework. CrewAI is the main alternative for role-based multi-agent systems. AutoGen (Microsoft) is archived and no longer maintained.
Do I need to know LangChain to use LangGraph?
No. LangGraph works independently — you can build graphs with plain Python functions, any LLM client, and any tools. LangChain components (prompts, retrievers, parsers) are optional and useful for common patterns. However, interviewers may expect familiarity with both, since LangChain 1.0 runs on LangGraph and they're from the same company.
What companies are using LangGraph in production?
Notable production deployments include Klarna (customer support for ~85 million users), Uber (automated unit test generation), LinkedIn (recruiting automation and SQL bot), AppFolio (property management copilot), Replit (multi-agent coding copilot), and Elastic (security threat detection). These are vendor-published case studies from LangChain.
Key Takeaways
- LangGraph is the production standard for stateful AI agents in 2026 — 43K+ GitHub stars, version 1.2, and enterprise adoption from Klarna to Uber.
- Master the three core primitives: State (typed, with reducers), Nodes (one side effect each), and Edges (conditional routing). Everything else builds on these.
- Checkpointing is non-negotiable for production: Use PostgresSaver, understand thread_id scoping, and design for crash recovery with idempotent side effects.
- Human-in-the-loop is a first-class feature: The interrupt/resume pattern is how you add human oversight without bolting it on. Know the blast-radius rule.
- Multi-agent is powerful but often overkill: Start with a single agent with good tools. Add agents only when tasks require different models, isolated state, or clear decomposition.
- Production readiness means: streaming, observability (LangSmith/OpenTelemetry), cost controls (recursion limits, token budgets), and horizontal scaling with stateless workers.
- Scenario questions test design thinking: Practice designing refund agents, research assistants, and knowledge bases with specific numbers (latency targets, cost budgets, scale requirements).
External References
- LangChain. "LangGraph Documentation." docs.langchain.com
- LangChain. "Building Graphs — LangGraph Guides." mintlify.com/langchain-ai
- LangChain. "LangGraph Concepts — Graphs." mintlify.com/langchain-ai
- Atlan. "What is LangGraph? State, Agents, and Production Use Cases (2026)." atlan.com
- FutureAGI. "What is LangGraph 2026." futureagi.com
- EastonDev. "LangGraph State Management: Checkpoints, Thread State, and Failure Recovery." eastondev.com
- Mastra. "LangGraph: The Complete Guide to Stateful AI Agent Orchestration." mastra.ai
- LangChain. "State of Agent Engineering Report, 2026." Referenced via Atlan and FutureAGI analysis.
- CloudSoftSol. "LangGraph Interview Questions and Answers." cloudsoftsol.com
- InterviewCoder. "LangGraph Interview Questions (2026)." interviewcoder.co






The state-machine framing here is the right mental model, and one question I would add to the list: what happens to your graph when the execution is interrupted by an external quota window rather than an error? Checkpointing is usually taught as crash recovery, but the same durability machinery is what saves you when a long agent run is cut by a usage limit mid-node, and graphs that do not checkpoint cleanly end up replaying side effects. Interview answers that distinguish recovery from interruption handling read as much more experienced. I maintain a small independent tracker for coding-agent usage windows and reset failures at codexreset.today, and interrupted-mid-node is one of the most common real-world cases logged there.