LangGraph: State, Nodes and Bounded Loops
LangGraph makes state transitions explicit when a workflow branches, retries or waits for a person. Ordinary Python can also implement loops; the reason to use a graph is controllable state and execution, not that loops are otherwise impossible.
#Run a graph without a model
Requires langgraph. This deterministic example has no external calls or writes. It demonstrates a bounded draft/review loop; it is not an AI quality evaluator.
pythonfrom typing import TypedDict from langgraph.graph import StateGraph, START, END class State(TypedDict): attempts: int draft: str accepted: bool def draft(state: State): attempt = state["attempts"] + 1 text = "A useful draft with a source reference." if attempt > 1 else "Short draft." return {"attempts": attempt, "draft": text} def review(state: State): return {"accepted": "source reference" in state["draft"]} def route(state: State): return END if state["accepted"] or state["attempts"] >= 2 else "draft" builder = StateGraph(State) builder.add_node("draft", draft) builder.add_node("review", review) builder.add_edge(START, "draft") builder.add_edge("draft", "review") builder.add_conditional_edges("review", route) app = builder.compile() result = app.invoke({"attempts": 0, "draft": "", "accepted": False}) assert result["accepted"] and result["attempts"] == 2 print(result)
Nodes read state and return updates. Edges control routing. Keep intermediate local calculations out of shared state unless another node needs them. For concurrent updates, define an appropriate reducer rather than assuming writes merge automatically.
#Persistence and approval
An in-memory checkpointer is useful for development, but does not survive a process restart. Durable checkpoint storage and an authorized thread ID are needed for reliable resume. Checkpointing does not make external actions exactly-once; protect those with idempotency and read-back.
Pause before consequential writes and store the proposed action so a reviewer sees a concrete result. Resume only with the intended approval and recheck stale state. Bound retries, time and usage even when a review node keeps rejecting output.