Learnixo
Back to blog
AI Systemsintermediate

Interview: LangGraph Fundamentals

8 Q&A pairs on LangGraph core concepts: StateGraph vs DAG, conditional edges, cycles, checkpointing, human-in-the-loop, and when to use LangGraph.

Asma Hafeez KhanMay 16, 20265 min read
LangGraphInterviewAgentsPython
Share:𝕏

Q1: What is LangGraph and how does it differ from a simple LangChain chain?

A: LangGraph is a framework for building stateful, cyclic agent workflows as graphs. A LangChain chain is a linear pipeline: A → B → C → output. LangGraph allows cycles (A → B → A → B → ...), branching (A → B or C based on state), and persistent state across multiple steps.

Key concepts:

  • StateGraph: The core class. You define a TypedDict as state and nodes as functions that update state
  • Nodes: Python functions that receive state and return updated state
  • Edges: Connections between nodes — fixed or conditional (routing based on state)
  • State: Persists across nodes and is the primary communication mechanism

LangChain chains are appropriate for simple, predictable pipelines. LangGraph is designed for agents that need to reason, iterate, and make decisions based on intermediate results.


Q2: What is the difference between an edge and a conditional edge?

A:

Edge: Always routes to the same next node.

Python
graph.add_edge("research", "write")  # Always goes to write after research

Conditional edge: Routes to different nodes based on the current state, via a routing function.

Python
def route(state) -> str:
    if state["confidence"] > 0.8:
        return "publish"
    return "review"  # Route to human review if unsure

graph.add_conditional_edges(
    "evaluate",
    route,
    {"publish": "publish_node", "review": "review_node"},
)

The routing function receives the state and returns a string key. The map argument ({"publish": "publish_node", ...}) translates that key to an actual node name. This indirection allows the routing function to return logical names decoupled from implementation node names.


Q3: How does state accumulation work in LangGraph?

A: By default, each node's return value replaces the corresponding state keys. To accumulate values (e.g., build a list), use Annotated with operator.add:

Python
from typing import TypedDict, Annotated
import operator

class State(TypedDict):
    messages: Annotated[list[str], operator.add]  # Appends on each update
    summary: str  # Replaced on each update

def node_a(state: State) -> State:
    return {"messages": ["message from A"]}  # Appended to existing list

def node_b(state: State) -> State:
    return {"messages": ["message from B"]}  # Also appended

After node_a and node_b both run, state["messages"] is ["message from A", "message from B"].

Without Annotated[list, operator.add], node_b's return would replace node_a's output.


Q4: What is checkpointing in LangGraph and why does it matter?

A: Checkpointing saves the full graph state after every node execution. This enables:

  1. Resume after crash: If your agent process dies mid-run, you can resume from the last checkpoint
  2. Human-in-the-loop: Pause execution, show state to a human for review, then resume
  3. Time travel: Step backward through execution history to debug
  4. Branching: Explore different continuations from the same checkpoint
Python
from langgraph.checkpoint.memory import MemorySaver

checkpointer = MemorySaver()  # In-memory (dev/testing)
# or: SqliteSaver (persistent local storage)
# or: PostgresSaver (production)

app = graph.compile(checkpointer=checkpointer)

config = {"configurable": {"thread_id": "session_abc"}}
result = app.invoke(state, config=config)

# Resume with same thread_id continues from where it left off
result2 = app.invoke(new_state, config=config)

The thread_id is the identifier for a conversation/session. Each thread has its own state history.


Q5: How do you implement human-in-the-loop approval in LangGraph?

A: Use interrupt_before or interrupt_after at compile time to pause execution at specific nodes:

Python
app = graph.compile(
    checkpointer=checkpointer,
    interrupt_before=["approve_action"],  # Pause BEFORE this node
)

config = {"configurable": {"thread_id": "session_1"}}

# Run until interrupt
app.invoke(initial_state, config=config)
# Execution pauses before "approve_action"

# Human reviews the state
current_state = app.get_state(config)
print(current_state.values)  # Show what's pending approval

# Human approves — resume
app.invoke(None, config=config)  # None continues from checkpoint

For programmatic resume with modified state:

Python
# Update state before resuming
app.update_state(config, {"approved": True}, as_node="approve_action")
app.invoke(None, config=config)

Q6: When would you use LangGraph instead of a simpler agent framework?

A: Use LangGraph when:

  • Cycles required: Your agent needs to retry, refine answers iteratively, or loop over a list of items
  • Complex branching: Multiple different paths based on intermediate results (not just one if/else)
  • Human-in-the-loop: Production workflows where humans must approve actions before execution
  • Long-running workflows: Multi-step processes that might crash and need to be resumed
  • Multi-agent coordination: Supervisor agents that route work between specialist subagents
  • Auditability: You need to inspect, replay, or branch from any point in execution history

Simpler alternatives (OpenAI Assistants API, direct LLM calls with tool loops) are sufficient for: single-turn tool use, simple chat without state management, or quick prototypes where robustness isn't critical.


Q7: How does LangGraph handle parallel node execution?

A: LangGraph supports parallel (fan-out/fan-in) execution using Send:

Python
from langgraph.types import Send

def fan_out(state) -> list[Send]:
    """Dispatch to multiple parallel workers."""
    return [
        Send("worker", {"task": drug, "results": []})
        for drug in state["drug_list"]
    ]

graph.add_conditional_edges("router", fan_out)

Each Send creates an independent branch with its own state. After all branches complete, they fan back in to a collector node. This is how you process multiple items in parallel within a single graph execution.


Q8: System design — design a LangGraph workflow for a drug safety review system.

A:

Requirement: An agent that reviews a proposed drug regimen (list of drugs), checks for interactions, and requires human pharmacist approval before issuing a safety report.

State:

Python
class SafetyReviewState(TypedDict):
    drug_list: list[str]
    interactions: Annotated[list[str], operator.add]
    risk_level: str  # "low", "moderate", "high"
    pharmacist_approved: bool
    final_report: str

Graph:

[start] → [screen_for_interactions] → [assess_risk]
    → if risk == "high": [flag_for_pharmacist_review] → [INTERRUPT]
    → human approves → [generate_report] → [END]
    → if risk in ("low", "moderate"): [generate_report] → [END]

Key decisions:

  • interrupt_before=["generate_report"] if risk is high — pharmacist must review before any report is generated
  • Thread ID = patient encounter ID — state persists for the session
  • PostgresSaver checkpointer for production durability
  • Recursion limit: 10 (simple linear flow, shouldn't recurse)
  • pharmacist_approved in state ensures the interrupt actually adds human judgment, not just a button click

This pattern is the standard LangGraph human-in-the-loop design for regulated workflows.

Enjoyed this article?

Explore the AI Systems learning path for more.

Found this helpful?

Share:𝕏

Leave a comment

Have a question, correction, or just found this helpful? Leave a note below.