Vocabulary for this course

The terms the chapters assume, in plain words. You can open this from any chapter.

checkpoint
A saved copy of the state after a step, stored under a thread so a run can resume or rewind.
compile
The step that checks a graph's connections and turns it into something that can be run.
conditional edge
A rule that reads the state and names which node runs next.
edge
A rule saying which node runs after the current one finishes.
entry point
The declared first node of a graph, marked by an edge from the start.
graph
A workflow drawn as nodes joined by edges, where the path can branch and loop.
interrupt
A planned pause before a node runs, so a person can approve, reject or edit first.
node
A function that reads the state and returns only the keys it changed.
reducer
A function that decides how an update is combined with the old value of one state key.
state
The shared record passed between nodes, holding every value the workflow needs to continue.
subgraph
A graph mounted inside another as a single node, with its own slice of state.
supervisor
A node that reads the state and chooses which specialist worker runs next, rather than answering itself.
thread
A named run whose checkpoints are stored together, so it can be listed, resumed or branched.

LangGraph from the ground up

Conditional edges and loops

A router function reads the state and names the next node. Point it back at an earlier node and the graph loops — which is powerful, and needs a stop condition.

Part 4 of 10

Intermediate16 min readBack to Courses

The router is a function

A conditional edge is a function of the state that returns the name of the next node. It does not call anything and it does not mutate anything; it decides. "revise" if the critique found a problem, "finish" if it did not.

Keeping the decision in one small function is what makes a graph readable. The nodes do the work; the routers say where the work goes. A router that also does work is a node in disguise.

A worked router reads the state in a fixed order. If attempts is three or more, return “give_up” first, so the cap cannot be overridden by a high score. Otherwise, if score is below 0.8, return “revise”. Otherwise return “finish”. Swap the first two checks and a failing third attempt revises again, past the cap, until the recursion limit.

A loop is just an edge backwards

There is no loop construct. You add an edge from a later node back to an earlier one, and the runtime follows it again. The state carries what changed, so the second pass starts from the first pass’s result.

That is exactly the draft, critique, revise pattern. Draft once, critique, and if the critique is unhappy, go back to the draft node with the critique in the state.

A worked pass: draft writes text and attempts one. Critique writes score 0.6 and a note. The router returns “revise”, the edge goes back to draft, and the second draft reads both the first text and the critique. If the critique is not in the state, the second draft is just another first draft with a higher counter. The critique also has to be replaced, not appended: a stale note from the first pass sends the second pass in the same direction.

Routing back

def should_revise(state) -> str:
    if state["attempts"] >= 3:
        return "give_up"
    if state["score"] < 0.8:
        return "revise"
    return "finish"

builder.add_conditional_edges("critique", should_revise, {
    "revise": "draft",
    "finish": "publish",
    "give_up": END,
})

A loop with a counter

Run it and watch the attempts rise. Then remove the counter check and see the step limit fire — that is the backstop, and it is not a stop condition.

Output
Click "Run" to execute your code

Every loop needs three things

A counter in the state, a condition that checks it, and a route out that is guaranteed to be taken. Without the counter a graph that keeps failing runs until the recursion limit, which is a crash rather than a decision.

The escape route should be a real node, not an exception. A graph that gives up should write that into the state, so the caller can see what happened and decide whether to retry later.

The three things are a counter, a check on it, and an exit that is always reachable. Write them into the state: attempts, status, and the reason. A give-up route that writes status “gave up” and the best draft is a result the caller can act on; an exception is not. The counter belongs in the state because the recursion limit is a crash, not a decision.

LangGraph stops a runaway loop with a recursion limit and raises. Treat that as a bug in the router, not as the stop condition — it is the backstop, not the design.

Guard the router, not the node

The router is the smallest function in the graph and the one most worth testing. Give it a table of states and expected routes: attempts 0 score 0.5 → revise; attempts 3 score 0.9 → give_up; attempts 1 score 0.9 → finish. The table runs in milliseconds.

A default branch is the other guard. If the router returns a name the graph does not know, the run fails at the edge rather than silently stopping. Map every return value explicitly and let an unknown value raise.

Loops change the cost model

A chain costs the same every time. A loop costs more when it runs more, and a bad input can triple the bill. That is another reason the counter belongs in the state from the first version rather than after the first surprise.

If the loop calls a model, consider escalating rather than repeating: the second attempt can use a stronger model or a longer prompt, since the first attempt has already failed.

A budget makes the cost concrete. Three attempts at two thousand tokens each is six thousand tokens per request, and a bad input reaches the cap every time. Set the cap from the worst case you can afford, not from the number that makes the demo pass.

Escalate instead of repeating

The first attempt uses a cheap model and the second uses a strong one. Run it, then make the strong model succeed on the first try and confirm the loop exits earlier.

Output
Click "Run" to execute your code

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 5

What is a conditional edge?