A lot of the “agentic” work I see is a prompt wrapper with better marketing. Most developers are still in the Retrieval-Augmented Generation (RAG) mindset: feed a PDF to an LLM, get a summary back, hope it does not hallucinate. That holds up until the business logic gets complicated. Linear RAG keeps no memory of its own decision-making, which is another way of saying it has no state.
That is what building a LangGraph agent fixes. A graph-based agent can loop, branch and carry a persistent state across nodes, none of which a linear chain does. It is the difference between a script and a state machine. After 14 years of untangling complex workflows in WordPress, it is the most interesting shift to land on my desk in a long time.
The bottleneck of linear RAG
Standard RAG is fine for simple Q&A. It jams when the solution path is not known in advance. Ask an LLM to “summarize a soccer player’s season” and a standard pipeline fetches one set of stats and calls it done. A real reporter would work through jersey numbers, club history and injury records in order, adjusting the next search based on what the last one turned up.
I wrote about how this plays out in larger systems in Agentic AI Anomaly Detection.
Architecting the StateGraph
You start with a StateGraph. You define nodes, which are functions, and edges, which are the paths between them. The state is a shared object that every node can read from and write to. The mistake I see most often from junior devs is stuffing data into the prompt instead of putting it in a clean state schema.
A Pydantic model keeps that state honest, so the agent does not lose track of its findings halfway through a run:
from pydantic import BaseModel
from typing import Optional, List
class PlayerState(BaseModel):
question: str
selected_tools: Optional[List[str]] = None
name: Optional[str] = None
club: Optional[str] = None
summary: Optional[str] = None
Defining the nodes and planner
A node is a Python function that takes the current state and returns an update. The planner node is the one that earns its keep. Instead of asking the LLM to do everything in one shot, you ask it which tools it needs. Token usage drops and the model has fewer instructions to juggle.
Graph construction then looks like this, with START and END marking the entry and exit points of the logic:
from langgraph.graph import StateGraph, START, END
graph_builder = StateGraph(PlayerState)
# Add our logic nodes
graph_builder.add_node('extract_name', bbioon_extract_name_fn)
graph_builder.add_node('planner', bbioon_planner_fn)
graph_builder.add_node('write_summary', bbioon_write_summary_fn)
# Define the flow
graph_builder.add_edge(START, 'extract_name')
graph_builder.add_edge('extract_name', 'planner')
graph_builder.add_edge('write_summary', END)
Conditional edges
The reason to reach for a graph rather than a basic script is the add_conditional_edges method. The agent decides at runtime which node to visit next, based on the state. It is if/else logic where the condition comes out of LLM reasoning.
If the planner needs a jersey number, it routes to that node. If the data is already in state, it jumps straight to writing the summary. A linear chain has to walk every step either way.
The official LangGraph documentation goes deeper into orchestration.
If LangGraph work is eating your dev hours, I can take it on. I have been wrestling with WordPress and complex backend logic since the 4.x days.
Thinking in graphs
A longer prompt is a brittle and expensive way to solve a hard problem. Nodes, edges and state give you something you can step through and debug: software architecture that uses an LLM as one component, rather than a chatbot that has to get everything right in a single pass.