Skip to content

Agent systems, from tool choice to safe action

  • Book chapter
  • Intermediate
  • 28 min read
  • Checked 16 Aug 2026

Picture an on-call engineer asking:

Why did checkout failures increase after yesterday’s deployment?

The next useful check is not obvious. The system may inspect the deployment, read service metrics, search recent incidents, and change direction after each result. This uncertainty can justify an agent.

Now picture a refund workflow:

Look up the order, apply the written policy, request approval, and issue the refund.

The steps are known. Ordinary workflow code is easier to test and safer to operate.

This chapter teaches the boundary between those two systems.

Chapter Question you will be able to answer
1. Control Is this a tool call, workflow, or agent?
2. Capabilities Which agent patterns should be learned first?
3. Observation loop How does a result change the next action?
4. Choice When is an agent the wrong design?
5. Code boundary What may the model propose, and what must code enforce?
6. State When does LangGraph help?
7. Correction What evidence can safely drive another attempt?

Chapter 1: Decide who chooses the next step

Section titled “Chapter 1: Decide who chooses the next step”

These terms describe different amounts of model control.

Pattern Who chooses the next step? Tiny example
Tool call inside a workflow Application code Always look up an order, then summarize it
Workflow or chain Application code follows known branches Extract invoice → validate totals → request approval
Agent Model chooses among allowed actions from observations Investigate an incident whose next check depends on the last result

Anthropic draws the same practical boundary: workflows follow predefined code paths; agents dynamically direct their process and tool use.1

One tool call does not automatically make a system an agent. A model may fill the arguments for a tool while deterministic code controls every step around it.

Chapter 2: Learn capabilities as a ladder, not a ranking

Section titled “Chapter 2: Learn capabilities as a ladder, not a ranking”

There are no official “five levels of agents.” The following capabilities are a study order. Real systems combine them according to the problem, and later rows are not automatically better.

Capability What changes Add it when New failure
1. Scoped tool use Model proposes one allowed tool and arguments Natural language must select a capability Wrong tool or malformed arguments
2. Observation loop Model decides, acts, reads the result, and decides again The next action depends on evidence Endless loops and token cost
3. Plan and execute A large task becomes explicit subgoals Work has dependencies that are hard to discover up front Stale or unrealistic plans
4. Durable state and handoffs Work can pause, resume, or transfer to a specialist Tasks span steps, people, or processes State leakage and unsafe resume
5. Evaluated correction Tests or validators feed failures back for a bounded retry A trustworthy external signal can improve the result Repeated confident mistakes

Treat multi-agent design as another topology, not a higher rank. A single agent can have durable state; several routed specialists can remain stateless.

Chapter 3: Let observations change the next action

Section titled “Chapter 3: Let observations change the next action”

The ReAct paper interleaves reasoning with actions and observations so new evidence can change the next action.2 In product diagrams, the safer wording is:

  1. 01Decide
  2. 02Act through a tool
  3. 03Observe the result
  4. 04Decide again or stop

Example:

Question: Can I travel tomorrow without heavy rain?
Decide: Current weather is required.
Act: weather(city="Bengaluru", date="tomorrow")
Observe: Heavy rain is expected after 4 PM.
Decide: A morning option may work; check transport.
Act: train_search(route="Bengaluru-Mysuru", before="12:00")
Observe: Seats are available at 8 AM.
Answer: Recommend the morning option and cite both tool results.

The loop earns its cost because the second action depends on the first observation.

Chapter 4: Use an agent only where uncertainty earns it

Section titled “Chapter 4: Use an agent only where uncertainty earns it”
Situation Better starting point Why
Extract fields, validate them, write to an ERP Workflow Steps and validation rules are known
Translate, then run a fixed compliance check Workflow Predictable and easy to test
Investigate an unfamiliar production incident Bounded agent The next action depends on findings
Search several known systems in parallel Router and workers Sources are known; work can fan out
One uncertain research step inside a fixed process Hybrid Workflow outside, agent only at the uncertain node
Refund, deletion, or payment Deterministic gate plus approval A model must not be the authorization boundary

“If you can draw the workflow, do not use an agent” is a strong starting heuristic, not a law. A graph can contain one agentic node inside otherwise deterministic control flow.

Chapter 5: Put the control boundary in code

Section titled “Chapter 5: Put the control boundary in code”

This skeleton gives the model two tools. Application code checks the tool name, validates arguments, reserves authorization for itself, and stops the loop after five steps.

src/examples/agents/bounded_tool_loop.py
"""A small agent loop whose authority stays in application code."""
from dataclasses import dataclass
from typing import Protocol
@dataclass(frozen=True)
class ToolCall:
name: str
arguments: dict[str, str]
@dataclass(frozen=True)
class FinalAnswer:
text: str
Decision = ToolCall | FinalAnswer
class AgentModel(Protocol):
def decide(
self, messages: list[dict[str, str]], tools: tuple[str, ...]
) -> Decision: ...
def search_knowledge_base(query: str) -> str:
return f"Approved knowledge-base results for: {query}"
def lookup_order(order_id: str) -> str:
return f"Authorized status for order: {order_id}"
def execute_tool(call: ToolCall) -> str:
"""Validate model-produced arguments before executing a scoped tool."""
if call.name == "search_knowledge_base":
query = call.arguments.get("query", "").strip()
if not query:
raise ValueError("query is required")
return search_knowledge_base(query)
if call.name == "lookup_order":
order_id = call.arguments.get("order_id", "").strip()
if not order_id.startswith("ORD-"):
raise ValueError("order_id must start with ORD-")
# A real application would also verify that the user owns this order.
return lookup_order(order_id)
raise ValueError(f"Tool is not allowed: {call.name}")
def run_agent(question: str, *, model: AgentModel, max_steps: int = 5) -> str:
messages = [{"role": "user", "content": question}]
allowed_tools = ("search_knowledge_base", "lookup_order")
for _ in range(max_steps):
decision = model.decide(messages, allowed_tools)
if isinstance(decision, FinalAnswer):
return decision.text
observation = execute_tool(decision)
messages.append({"role": "assistant", "content": repr(decision)})
messages.append({"role": "tool", "content": observation})
return "Stopped after the step limit; a human should review this task."

The model proposes. The application executes. For tools with side effects, add idempotency keys, approval, audit records, and a result check before the model claims success.

Chapter 6: Add explicit state when the work must survive

Section titled “Chapter 6: Add explicit state when the work must survive”

LangGraph makes state, nodes, edges, checkpoints, and resume behavior explicit.3 Use it when the workflow needs branching, loops, human approval, durable execution, or a mixture of deterministic and model-driven steps.

It does not make an unbounded loop safe by itself. You must still define:

  • the state schema and tenant boundary;
  • allowed transitions and maximum steps;
  • which nodes may create side effects;
  • which failures may retry;
  • where a person must approve or correct state;
  • how old checkpoints and long-term memories are deleted.

Chapter 7: Correct from evidence, not confidence

Section titled “Chapter 7: Correct from evidence, not confidence”

Weak correction asks a model to review its own answer and accepts “looks good.” Strong correction supplies evidence:

generate code → run deterministic tests → return failing output
→ retry at most twice → escalate if tests still fail

Evaluator–optimizer loops work best when the criteria are clear and iteration produces measurable improvement.1 Without tests, validators, retrieved evidence, or a calibrated grader, another model pass can repeat the same error.

Name the uncertain decision. If no step requires model judgment, begin with normal code. If one step is uncertain, confine the agent there and keep the rest deterministic.

A workflow follows steps the application defines. An agent lets a model choose the next action from a bounded set of tools and observations. I use an agent only when the next useful step cannot be mapped reliably in advance. The application still validates arguments, enforces authorization, controls side effects, limits steps and cost, records state, and decides when to stop or ask a person. For fixed work such as validation, payment, deletion, or approval, I keep the path deterministic.

Next: resolve agent disagreement, follow an MCP tool call, or learn LangGraph state and persistence.

  1. Anthropic, “Building effective agents”, recommends starting with the simplest composable pattern and describes the latency, cost, control, and flexibility trade-offs. 2

  2. Yao et al., “ReAct: Synergizing Reasoning and Acting in Language Models”, ICLR 2023.

  3. LangChain, LangGraph overview and persistence guide.