Implementing a Multi-Agent State Machine for Automated Code Review using LangGraph

test_admin Beginner 5/9/2026 272 views 1 likes 2 min read

LangGraph is the first framework that actually makes multi-agent orchestration feel like writing a state machine rather than praying that a loop of LLM calls doesn't hallucinate into an infinite cycle. For a long time, I tried building automated code review pipelines using simple chains, but they always failed when the "Reviewer" agent found a bug and the "Fixer" agent introduced two more.

Implementing a Multi-Agent State Machine for Automated Code Review using LangGraph

The trick to a stable code review agent is treating the process as a directed graph where the state is the single source of truth. I've set up a workflow where the State object tracks the current code version, a list of identified issues, and a "review cycle count" to prevent infinite loops.

Here is the core logic of how I structured the graph:

from typing import Annotated, TypedDict, List
from langgraph.graph import StateGraph, END

class ReviewState(TypedDict):
    code: str
    critiques: List[str]
    iteration: int
    is_approved: bool

def reviewer_agent(state: ReviewState):
    # Prompt focuses on identifying bugs and style violations
    # Returns a list of critiques
    response = llm.invoke(f"Review this code: {state['code']}")
    return {"critiques": response.content, "iteration": state['iteration'] + 1}

def fixer_agent(state: ReviewState):
    # Prompt focuses on fixing the specific critiques provided
    response = llm.invoke(f"Fix these issues: {state['critiques']} in code: {state['code']}")
    return {"code": response.content, "critiques": []}

def check_approval(state: ReviewState):
    if not state['critiques'] or state['iteration'] > 3:
        return "approve"
    return "fix"

workflow = StateGraph(ReviewState)
workflow.add_node("reviewer", reviewer_agent)
workflow.add_node("fixer", fixer_agent)

workflow.set_entry_point("reviewer")
workflow.add_conditional_edges("reviewer", check_approval, {"approve": END, "fix": "fixer"})
workflow.add_edge("fixer", "reviewer")

app = workflow.compile()

One major "gotcha" I hit was the LLM's tendency to say "The code looks great!" while still leaving a minor syntax error. To fight this, I shifted from a general prompt to a Role-Specific System Prompt for the reviewer. Instead of asking "Is this correct?", I tell it: "You are a pedantic senior engineer. Your goal is to find at least one reason to reject this PR. If you cannot find any, only then mark it as approved."

To make this actually useful in a production CI/CD pipeline, I integrated this with Cursor's .cursorrules file so that when I manually trigger a review, the AI knows the exact schema the LangGraph agent expects.

Productivity gains I've noticed:
Reduced Manual Review Time: I no longer spend 10 minutes catching missing null checks; the agent catches them in 15 seconds.
Consistency: Unlike a human reviewer who might be tired on Friday afternoon, the agent applies the same strict linting rules every time.
State Persistence: Using LangGraph's checkpointer, I can pause the review, manually intervene in the code, and then resume the graph from the last state without re-running the entire chain.

If you're using Claude 3.5 Sonnet as the backbone, the reasoning capabilities are significantly better at the "Fixer" stage. It understands the context of the critique without needing the entire codebase injected into every single turn, which keeps the token cost down.

All Replies (0)

No replies yet — be the first!

Write a Reply

Markdown supported