Posted on
Your agent is a graph with a fancy name
Chain, graph, and agent aren't three different technologies, they're three answers to a single question -- who decides the next step, you or the model.
Who decides the next step: you or the model?
There’s a vocabulary confusion that costs a lot in the architecture of LLM systems. We call anything with a model inside it an “agent,” and in doing so we lose the one distinction that actually changes how you build the system: how it’s tested, what it costs, and what you can promise about its behavior.
Chain, graph, and agent aren’t three different technologies. They’re three answers to a single question:
Who decides what the next step is: you or the model?
Everything else (tools, MCP, RAG, memory) is scaffolding. The question above is the foundation.
To avoid arguing in the abstract, the whole piece runs on a single problem: an automated pull request reviewer. It takes a diff, looks for bugs, security risks, and design problems, and comments on the PR. It’s boring enough to warrant a chain, ambiguous enough to call for a graph, and open enough to tempt an agent. All three architectures handle it. In very different ways.
The starting point: the LLM is an unreliable function
An LLM does one thing: it takes a sequence of tokens and returns the next token, probabilistically. A pure function, no memory, no state, no guarantee of repeatability. There’s no system-level intelligence in there. There’s a useful, unreliable function.
LLM engineering is, in essence, the art of deciding where to put the constraints around that function. Chain, graph, and agent are three positions on that axis, from maximum constraint to maximum autonomy.
Chain: the order is yours, and it’s linear
A chain is a fixed sequence. Step 1 feeds step 2, which feeds step 3. You wrote the order into the code, and it doesn’t change at runtime.
extract diff → build prompt → call LLM → validate schema → post comment
The model is called like a function in the middle of an ordinary pipeline. It chooses nothing beyond the content of the text it returns. Same input, same execution path. Every time.
This is boring, and boring is great. A chain is cheap, predictable, trivial to test and to instrument. The problem is that reality is rarely linear.
Graph: the order is still yours, but it can branch
The graph is the natural evolution. You have nodes and edges, and the edges can be conditional: depending on the state, the flow goes down one path or another. A PR that only touches .md doesn’t need to go through the security specialist. A high-severity finding triggers an extra review node.
The temptation is to call this an agent, because “the system decides.” And here the confusion becomes legitimate, because the state feeding the edge can come from the model itself:
def route(state):
# state.severity was classified by the LLM in the previous node
if state.severity == "high":
return "deep_review"
return "judge"
The model influenced the routing. It didn’t choose the routing. The difference fits in one sentence: the model produced a value, and you wrote what to do with each possible value. It had no way to return "page the on-call team", because that destination doesn’t exist in your code. The space of paths was enumerated entirely by you, at write time.
That conditional is yours. It’s in your code, it’s auditable, it’s testable with an ordinary unit test. The graph is dynamic at runtime, but given the same state, the same routing happens. Every time.
Look at judge in the snippet: it’s the node that receives findings and consolidates them. Now widen the picture. Instead of one path arriving at it, put five specialists running in parallel, each with its own prompt, all converging there. You’ve just drawn the diagram that today gets sold as a “multi-agent system.”
Except each of those specialists receives a diff and returns findings. Input in, output out, and it chooses nothing beyond the text it produces. It’s the same creature from the previous section: the model called as a function in the middle of a pipeline. A graph node with a specialized prompt and a fancy name.
A multi-agent system may not have a single agent inside it. The plural is in the diagram, not in the architecture.
This isn’t a knock on it. It’s probably the right call. But it’s a call, and it’s worth being honest about it.
Agent: the model decides
The agent inverts the authority. You give the model a goal, a set of tools, and a context. Not the order of execution. The model enters a loop:
model → picks a tool → you execute → result returns to context →
model re-evaluates → picks another tool or decides it's done
The stopping condition is the model’s, too. That’s the ontological difference, not a matter of degree of sophistication.
A concrete example of what that changes. Suppose a PR that removes the feature flag ENABLE_LEGACY_CHECKOUT and deletes the dead branch. The diff is clean, cohesive, and the tests pass.
A fixed graph runs the specialists on it. No security issues, no obvious bugs, coverage maintained. It returns “no findings.” Correct within the scope you defined.
An agent asks a question that wasn’t in the task: who else reads this flag? It greps the code and finds nothing. It could stop here, and a graph node would stop. But flags are read by name, as strings, and not always from code. It searches the raw string across the entire repo and finds helm/values.yaml. It reads the file: the flag is true in staging. That raises a question that didn’t exist thirty seconds ago. It opens the deploy pipeline to understand what happens when the service boots with a flag nobody consumes anymore, discovers that the config parser rejects unknown keys, and posts a finding that wasn’t in the diff, wasn’t in the code, and wasn’t in the scope.
Look at the mechanism, not the result. Each answer generated the next question. The agent didn’t run a “flag consistency” check: it ran an investigation whose depth and whose shape depended on what each step returned.
“But a check_flag_removal node in the graph would have been enough.” It would have, for the first level. The third level doesn’t exist until the second returns that specific value. You can’t draw an edge to a state that only comes into existence after the model reads a YAML you didn’t know was there. A graph enumerates paths. An agent generates the path as it walks it.
This is agency: reasoning beyond the immediate task, using tools to reduce its own uncertainty, and revising its conclusion in light of what it found.
It’s not binary, it’s a spectrum
Three questions locate any system on this axis, and they don’t carry the same weight.
The first is decisive: does the model choose the sequence of steps? If not, you have a graph, and the matter ends there, however many tools and sophisticated prompts live inside the nodes.
The second is the one that confuses most, because the answer can be yes in a system that’s nothing close to agentic. Does the model choose which tools to call? A graph node can have tools. The security specialist can pull a file’s history before weighing in. Having tools isn’t having autonomy. Autonomy is choosing the order.
The third is the one that decides: is there a loop whose stopping condition belongs to the model? This is where the category flips. Notice that it absorbs the question almost everyone asks separately, about reflection. A system that generates, critiques, and revises across three fixed nodes has reflection and zero autonomy: you told it to reflect. What’s agentic isn’t reflecting, it’s the model deciding it isn’t done yet.
Which is why the most common architecture in practice isn’t a chain, or a graph, or an agent. It’s a fixed graph at the macro level with agentic nodes at the micro level. A deterministic backbone, autonomy granted at specific points. The question that remains is which points.
But isn’t this just model + harness?
Every time this distinction comes up, someone returns the same objection: in the end, an agent is just a model plus a harness. It’s a fair objection, and worth taking seriously. An agent does need a harness: the loop, the tool dispatch, the context management, the memory, the stopping criterion. The thing is, a graph has all of that too. If “model + harness” were enough to define an agent, a three-step chain with schema validation would fall into the same category, and something tells us it shouldn’t.
What repositions the objection sits inside the definition of harness itself. In the descriptions practitioners use, for instance Hugging Face’s agent glossary (2026), control flow and the stopping condition show up as components of the harness: retries, step limits, when to branch, when to give up. Which means the control flow is an item inside the harness, not something above it. And that item can be written by you or handed to the model. The difference lives there, inside the harness, not in whether one exists.
The canonical distinction says the same thing. According to Anthropic, in “Building Effective Agents” (2024), workflows are systems where LLMs and tools are orchestrated through predefined code paths, while agents are systems where the model directs its own process and decides how to use its tools. Barry Zhang, in the talk “How We Build Effective Agents” (2025), lands it in one image: in a workflow, the plumbing is yours; in an agent, the plumbing belongs to the model. Everyone has plumbing. Not everyone gives up the wheel.
And the loop that characterizes an agent isn’t just any loop. Yao et al., in “ReAct: Synergizing Reasoning and Acting in Language Models” (2023), formalized execution as a cycle of thought, action, and observation, where the model interprets the state, decides the next action, and feeds the next step with the result. What makes this an agent isn’t that the cycle exists. It’s that the model owns when it stops.
The price of autonomy
Every gram of autonomy costs one obvious thing and one almost nobody mentions.
The obvious one arrives in three invoices with different names: money, latency, and unpredictability. But it’s really one: you lose the ability to know, before you run it, what’s going to happen. A chain’s cost you calculate in a spreadsheet before you ship it, and its runtime too. An agent’s, you don’t. The loop runs until the model thinks it’s done, and how many times it will call itself is a property of the input, not of your code. You’re not buying a more expensive system. You’re buying a system whose cost you only find out afterward.
The one nobody mentions: autonomy destroys your ability to measure. If you have a golden dataset and want to know whether the change to the security specialist’s prompt improved recall, you need the rest of the system to hold still. If the execution path varies on its own between runs, you can’t attribute the quality variation to the prompt rather than to the routing. You lose the controlled experiment and, with it, the engineering.
So when is autonomy worth it?
Everything else in this piece was description. Here’s the one criterion I’d take into an architecture decision:
Autonomy pays off when there’s a cheap, objective verifier that closes the loop.
An agent that implements a fix operates in a world where the compiler, the tests, and the linter tell it, for free and without ambiguity, whether it got it right. It can get it wrong, get the error back, and try again. The loop has a reality signal at every iteration, and that’s why the exploration converges.
An agent that reviews code has none of that. There’s no compiler for “this observation is useful.” The only verification is human, expensive, and slow. Without a verifier, the loop doesn’t converge: it just burns tokens generating unevaluated variations.
And here the math gets uncomfortable, so it’s better to work it out in the open. The values.yaml finding was real. The graph would have missed it. That’s not rhetoric to sell the next paragraph: it’s a concrete loss, and choosing the graph means accepting that loss.
You accept it anyway because the same autonomy that produced that finding produces, on the next run, an agent that opened four irrelevant files and posted a confident, wrong concern about one of them. Both runs cost about the same, take about the same time, and arrive with the same air of certainty. Without a verifier, the only thing that can tell one from the other is a human reading both, which is exactly the work you were trying to automate.
It’s not that the reviewer agent is bad. It’s that it’s good and bad in ways you can’t tell apart at scale. A system whose value you can’t measure isn’t a system, it’s a bet.
Out of that comes a hybrid architecture that defends itself: a deterministic graph for the review, an agent for implementing the fix. Not because one is modern and the other is legacy, but because the nature of the feedback available is different on each side.
The takeaway worth keeping
Determinism wherever you can, autonomy only where you must.
When it comes to describing your own system, a little vocabulary precision pays off more than hype. “Multi-step LLM orchestration” or “agentic workflow” describes a fixed graph honestly. Reserve “autonomous agent” for when the model really does decide its own path.
Whoever understands this difference and can justify it demonstrates three things at once: command of the trade-off, evaluation discipline, and cost awareness. Whoever calls everything an agent demonstrates they read the press release.
References
- Anthropic. “Building Effective Agents.” 2024. anthropic.com/engineering/building-effective-agents
- Zhang, Barry. “How We Build Effective Agents.” AI Engineer Summit, 2025.
- Yao, S., Zhao, J., Yu, D., Du, N., Shafran, I., Narasimhan, K., Cao, Y. “ReAct: Synergizing Reasoning and Acting in Language Models.” ICLR 2023. arXiv:2210.03629.
- Hugging Face. “Agent Glossary.” 2026. / Masood, A. “Agent Harness Engineering: The Rise of the AI Control Plane.” 2026.