Agent systems, from tool choice to safe action
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.
What you will understand
Section titled “What you will understand”| 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:
- 01Decide
- 02Act through a tool
- 03Observe the result
- 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.
"""A small agent loop whose authority stays in application code."""
from dataclasses import dataclassfrom 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 failEvaluator–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.
Interview rule
Section titled “Interview rule”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.
Interview answer in 30 seconds
Section titled “Interview answer in 30 seconds”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.
Footnotes
Section titled “Footnotes”-
Anthropic, “Building effective agents”, recommends starting with the simplest composable pattern and describes the latency, cost, control, and flexibility trade-offs. ↩ ↩2
-
Yao et al., “ReAct: Synergizing Reasoning and Acting in Language Models”, ICLR 2023. ↩
-
LangChain, LangGraph overview and persistence guide. ↩