Walkthrough

Orchestration: LangChain & LangGraph

When a framework earns its keep — and the LangGraph core loop: state, nodes, edges, checkpoints, human-in-the-loop.

Steps · 0 / 4 done
  1. Decide if you need a framework at all

    The stack: LangChain provides integrations and abstractions (models, retrievers, tools); LangGraph — the part that matters for agents — models your app as a graph of nodes sharing typed state, with persistence built in. Both hit 1.0 in late 2025. The honest decision rule: a raw tool-loop (aa-03) for simple agents; LangGraph when you need durable state, branching, resumability, or human approval mid-run; alternatives (OpenAI Agents SDK, Pydantic AI, CrewAI) are real — the concepts here transfer to all of them.

    VerifyYou can name the three features that justify a graph framework: persistence, branching control flow, human-in-the-loop.
  2. Model the app as state + nodes + edges

    Nodes are functions that read and update shared typed state; edges (including conditional ones) decide what runs next. This makes control flow explicit and testable:

    from langgraph.graph import StateGraph, START, END
    from typing import TypedDict
    
    class State(TypedDict):
        question: str
        draft: str
        approved: bool
    
    def research(state: State) -> dict: ...
    def write(state: State) -> dict: ...
    def review(state: State) -> dict: ...   # sets approved
    
    g = StateGraph(State)
    g.add_node("research", research); g.add_node("write", write); g.add_node("review", review)
    g.add_edge(START, "research"); g.add_edge("research", "write"); g.add_edge("write", "review")
    g.add_conditional_edges("review", lambda s: END if s["approved"] else "write")
    app = g.compile()
    VerifyYou can trace the write→review→write loop in the code — the evaluator-optimizer pattern from aa-11, now explicit and inspectable.
  3. Add checkpointing and a human gate

    Compile with a checkpointer and every step persists — runs survive crashes, and you can interrupt for approval and resume later:

    from langgraph.checkpoint.sqlite import SqliteSaver
    
    app = g.compile(
        checkpointer=SqliteSaver.from_conn_string("state.db"),
        interrupt_before=["publish"],   # pause for human approval
    )
    cfg = {"configurable": {"thread_id": "run-42"}}
    app.invoke({"question": q}, cfg)   # runs until the gate
    # human approves out-of-band, then:
    app.invoke(None, cfg)              # resumes exactly where it paused
    VerifyKill the process mid-run and invoke again with the same thread_id — it resumes from the checkpoint instead of restarting.
  4. Keep the framework honest

    Framework failure modes: abstraction layers that hide which prompt actually ran (log assembled prompts anyway — aa-06), version churn (pin versions; test upgrades against your evals), and graph spaghetti (if your graph needs a diagram to survive review, simplify it). The framework structures your control flow; quality still comes from the context you assemble and the evals you run.

    VerifyYour traces show the exact prompt each node sent — the framework never became a black box.
Check your understanding
Q1. The clearest signal you've outgrown a raw tool-loop and want LangGraph:
Q2. In LangGraph, conditional edges are how you implement:
· Tick off the 4 step(s) above.
· Score 100% on the quiz.