LangGraph Agents

Course Content

LangGraph Agents

7 sections · 49 lessons

How do you implement a loop (reflection / critique-revise / retry) safely without infinite cycles?


The search loop that ran 900 timesrewrite querysearch: emptygrade:insufficientattemptsless than 3?same queryas before?the missing guardFix: counter in state, repeated-query check, recursion_limit 40 per run.
The 1.x recursion limit is far above 25, so a loop without its own exit can run for a very long time.

What you need to know

Python
from typing import TypedDictfrom langgraph.graph import StateGraph, START, ENDclass State(TypedDict):    draft: str    approved: bool    revisions: int    feedback: strdef write(state: State) -> dict:    return {"draft": f"draft v{state['revisions'] + 1}", "revisions": state["revisions"] + 1}def critique(state: State) -> dict:    ok = state["revisions"] >= 2          # stand-in for an LLM critic    return {"approved": ok, "feedback": "" if ok else "add a source for the figure"}def should_continue(state: State) -> str:    if state["approved"] or state["revisions"] >= 3:        return "finalise"    return "write"builder = StateGraph(State)builder.add_node("write", write)builder.add_node("critique", critique)builder.add_node("finalise", lambda s: {})builder.add_edge(START, "write")builder.add_edge("write", "critique")builder.add_conditional_edges("critique", should_continue, ["write", "finalise"])builder.add_edge("finalise", END)graph = builder.compile()print(graph.invoke({"draft": "", "approved": False, "revisions": 0, "feedback": ""},                   {"recursion_limit": 20}))

The guards

  1. Semantic exit — approved from a structured critic.
  2. Budget — revisions >= 3, incremented by a node, stored in state.
  3. Backstop — recursion_limit in the config. Each super-step counts as one step. When exceeded, the run raises GraphRecursionError. In LangGraph 1.x the default is no longer 25 but a very large number, so a missing exit can burn money for a long time unless you set it.

Useful extras

  • No-progress detection — stop if the new draft or the critic's feedback repeats the last one.
  • RemainingSteps — a managed state value (remaining_steps: RemainingSteps) that tells a node how many steps are left before the limit, so it can finish gracefully.
  • Best-so-far — keep the highest-scoring draft in state and return it when the budget ends.

A real-life example

A research agent writes market summaries and retries searches when evidence is thin. One night a news API returned the same empty result for a new company name, and the agent looped "rewrite query, search, grade: insufficient" 900 times before anyone noticed, costing about Rs 18,000. The fix had three parts: search_attempts in state with an exit at 3, a check that stops if the rewritten query equals a previous one, and recursion_limit: 40 in every run's config. The next empty-result case ended after three attempts with "I could not find reliable sources for this company".

Follow-up questions to expect

  • "Why keep the counter in state and not a Python variable?" — State is checkpointed. After a resume or on another server, a local variable resets to zero and the loop runs forever.
  • "How do you choose the budget?" — From evals: measure how often attempt 2, 3 and 4 improve the score. Most gains come early.
  • "Is GraphRecursionError a good way to stop?" — No. It is a crash. Design a normal exit; the limit is only a safety net.