---
title: "13 — AI Agents: Tools, ReAct, and Multi-Agent Systems"
uid: ai-agents-and-multi-agent-systems
tags: ["llm", "react", "tools", "multi-agent", "agents", "roadmap:ai-engineer", "function-calling"]
excerpt: "The leap from chatbot to agent is architectural, not magical: an LLM in a loop with tools, deciding its next action from what it observes. No new kind of model involved."
date: 2026-08-13T03:28:36+0000
source: https://www.aveshina.my.id/en/blog/ai-agents-and-multi-agent-systems
---

"An LLM that does stuff" was the whole extent of my agent model, and it was too vague to build with. The definition that stuck: **an agent is an LLM in a loop with tools, deciding what action to take next based on what it observes — and a multi-agent system is just several of those agents coordinated.** [1] There's no new kind of model involved. The leap from "chatbot" to "agent" is entirely architectural: a loop, a set of tools, and a memory of what's happened so far.

The framing that finally landed is a strict cycle. The model looks at the current state (the user's request plus everything that's happened so far), emits a _thought_ about what to do, takes an _action_ by calling a tool, receives an _observation_ of what the tool returned, and loops back. It keeps going until it has enough to produce a final answer. That cycle — Reason, Act, Observe — is the ReAct pattern, and almost every agent framework implements some version of it.

```figure
<svg viewBox="0 0 740 300" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="The agent loop. A circular flow: Thought → Action → Observation → back to Thought. The Action step branches out to tools (search, calculator, API). A separate small box on the right shows a multi-agent team of three agents exchanging messages.">
  <defs>
    <marker id="agentarrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">
      <path d="M0,0 L10,5 L0,10 z" fill="#64748b"/>
    </marker>
  </defs>
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">

    <!-- agent loop -->
    <text x="180" y="28" font-size="12" font-weight="700" fill="#1e1b4b" text-anchor="middle">The agent loop (ReAct)</text>

    <rect x="40" y="60" width="120" height="50" rx="8" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.5"/>
    <text x="100" y="82" font-size="12" font-weight="700" fill="#1e1b4b" text-anchor="middle">Thought</text>
    <text x="100" y="98" font-size="9.5" fill="#475569" text-anchor="middle">reason about state</text>

    <rect x="200" y="60" width="120" height="50" rx="8" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5"/>
    <text x="260" y="82" font-size="12" font-weight="700" fill="#422006" text-anchor="middle">Action</text>
    <text x="260" y="98" font-size="9.5" fill="#475569" text-anchor="middle">call a tool</text>

    <rect x="120" y="180" width="120" height="50" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
    <text x="180" y="202" font-size="12" font-weight="700" fill="#052e16" text-anchor="middle">Observation</text>
    <text x="180" y="218" font-size="9.5" fill="#475569" text-anchor="middle">tool result appended</text>

    <!-- loop arrows -->
    <line x1="160" y1="85" x2="198" y2="85" stroke="#64748b" stroke-width="1.5" marker-end="url(#agentarrow)"/>
    <path d="M260,110 C260,150 220,170 200,178" fill="none" stroke="#64748b" stroke-width="1.5" marker-end="url(#agentarrow)"/>
    <path d="M140,205 C100,180 90,130 90,112" fill="none" stroke="#64748b" stroke-width="1.5" marker-end="url(#agentarrow)"/>

    <!-- tools off Action -->
    <g font-size="9.5" fill="#422006" text-anchor="middle">
      <rect x="200" y="140" width="56" height="24" rx="4" fill="#fef3c7" stroke="#ca8a04"/><text x="228" y="156">search</text>
      <rect x="262" y="140" width="56" height="24" rx="4" fill="#fef3c7" stroke="#ca8a04"/><text x="290" y="156">calc</text>
      <rect x="200" y="168" width="56" height="20" rx="4" fill="#fef3c7" stroke="#ca8a04"/><text x="228" y="181">API</text>
    </g>

    <!-- multi-agent team -->
    <text x="600" y="28" font-size="12" font-weight="700" fill="#500724" text-anchor="middle">Multi-agent team</text>
    <g>
      <rect x="500" y="60" width="70" height="44" rx="8" fill="#fce7f3" stroke="#db2777" stroke-width="1.5"/>
      <text x="535" y="86" font-size="10.5" font-weight="700" fill="#500724" text-anchor="middle">agent A</text>
      <rect x="630" y="60" width="70" height="44" rx="8" fill="#fce7f3" stroke="#db2777" stroke-width="1.5"/>
      <text x="665" y="86" font-size="10.5" font-weight="700" fill="#500724" text-anchor="middle">agent B</text>
      <rect x="565" y="180" width="70" height="44" rx="8" fill="#fce7f3" stroke="#db2777" stroke-width="1.5"/>
      <text x="600" y="206" font-size="10.5" font-weight="700" fill="#500724" text-anchor="middle">agent C</text>
    </g>
    <line x1="570" y1="82" x2="630" y2="82" stroke="#64748b" stroke-width="1.3" marker-end="url(#agentarrow)"/>
    <line x1="630" y1="92" x2="570" y2="92" stroke="#64748b" stroke-width="1.3" marker-end="url(#agentarrow)"/>
    <path d="M535,104 C540,140 575,170 590,180" fill="none" stroke="#64748b" stroke-width="1.3" marker-end="url(#agentarrow)"/>
    <path d="M665,104 C660,140 625,170 610,180" fill="none" stroke="#64748b" stroke-width="1.3" marker-end="url(#agentarrow)"/>
    <text x="600" y="252" font-size="9.5" font-style="italic" fill="#500724" text-anchor="middle">several coordinated loops</text>
  </g>
</svg>
```

## What makes something an agent

The boundary between "an LLM call" and "an agent" is the loop and the tools. A single LLM call takes a prompt and returns a response — no actions, no observations, no memory beyond the prompt. An agent adds three things on top:

- **A persistent state** — the running memory of the request, the thoughts, the actions taken, and the observations received.
- **Tools** — external functions the model can decide to call: a search API, a calculator, a database query, a code interpreter.
- **A control loop** — the model decides, at each step, whether to call another tool or produce a final answer.

That's it. The model itself is the same next-token-predictor covered earlier. The agent architecture is what turns that predictor into something that can take real actions in the world, and the quality of the agent is mostly the quality of its tools and its loop, not the raw capability of its model.

## Tools and function calling: how actions happen

The mechanism that makes agents practical is **function calling**. Instead of the model producing free text that I then have to parse, I declare a set of functions with typed schemas, and the model emits a structured call — function name plus arguments — when it decides an action is needed [2]. The system executes the function and appends the result to the context as an observation.

The reliability this gives is the whole reason agents are buildable at all. Free-text tool calls are fragile — the model might phrase the call slightly differently each time, and parsing breaks. Schema-constrained function calling turns "the model wants to use a tool" into a deterministic, typed event my code can act on. From Hugging Face's agents course, the unifying idea is that a tool is just a function the model can invoke, and good tool design — clear names, tight schemas, single responsibilities — is what makes an agent capable [3].

## ReAct: the canonical loop

**ReAct** (Reason and Act) is the pattern most agent frameworks implement, and it's worth seeing named because it makes the loop explicit [4]. Each cycle:

1. **Thought** — the model reasons about the current state and what to do next.
2. **Action** — it calls a tool (via function calling).
3. **Observation** — the tool's result is appended to the context.
4. Repeat until the model emits a final answer instead of another action.

The Thought steps are the model writing its own scratchpad into the context, the same chain-of-thought idea from the context-engineering post. The Actions are where it touches the real world. The Observations are how it learns what happened. ReAct works because externalizing reasoning into written thoughts, combined with real observations from tools, lets the model decompose problems it couldn't solve in a single shot.

## Agent use cases

Where agents earn their place is anywhere the task needs multiple steps, real-world data, or decisions about what to do next based on intermediate results [5]:

- **Customer support** that can look up an order, check a policy, issue a refund — a sequence of real actions, not one answer.
- **Workflow automation** that orchestrates several systems (CRM, email, database) to complete a business process.
- **Research** that searches, reads, summarizes, and follows citations across multiple sources.
- **Cybersecurity, finance, sales** — domains where the value comes from acting on current information the model wasn't trained on.

The pattern across all of them: a single LLM call can't do it, because the task requires iterating based on what's observed. That's the signal that an agent — not a prompt — is the right shape.

## Multi-agent systems: coordination on top of the loop

A **multi-agent system** layers one more idea on top: instead of one agent doing everything, several agents interact — cooperating or competing — to achieve individual or collective goals [6]. Each agent is still just the loop above; the new part is the coordination between them.

Why bother with multiple agents? Specialization. A single generalist agent trying to do planning, retrieval, coding, and verification tends to do none of them well. Splitting the work across specialized agents — one plans, one retrieves, one writes code, one reviews — lets each focus on its job, and the coordination layer routes messages between them [6]. The cost is orchestration complexity: now I have to design how the agents communicate, who goes next, and how conflicts are resolved. Multi-agent systems earn their complexity when the task is genuinely decomposable into specialized roles; for simpler tasks, a single well-tooled agent is almost always simpler and good enough.

## How I use this

The discipline is to reach for agents only when a single LLM call provably can't do the job — when the task needs iteration based on observation. For everything else, a good prompt with retrieval is cheaper, faster, and easier to debug. When I do build an agent, I invest most of my effort in the tools (clear schemas, single responsibilities) and the loop (when to stop, how to handle tool failures, how to keep the context window from bloating). I reach for multi-agent architectures only when the task has clearly separable roles that would step on each other in a single agent's context. And I always log the full Thought-Action-Observation trace, because debugging an agent without seeing its reasoning is mostly guesswork. The agent isn't a smarter model — it's a loop with good tools, and treating it that way keeps the complexity honest.

## References

[1] LangChain, "Building an AI Agent Tutorial," 2024. [Online]. Available: [https://python.langchain.com/docs/tutorials/agents/](https://python.langchain.com/docs/tutorials/agents/)

[2] The New Stack, "A Comprehensive Guide to Function Calling in LLMs," 2024. [Online]. Available: [https://thenewstack.io/a-comprehensive-guide-to-function-calling-in-llms/](https://thenewstack.io/a-comprehensive-guide-to-function-calling-in-llms/)

[3] Hugging Face, "What are Tools?," Agents Course, 2025. [Online]. Available: [https://huggingface.co/learn/agents-course/en/unit1/tools](https://huggingface.co/learn/agents-course/en/unit1/tools)

[4] Prompting Guide, "ReAct Prompting," 2024. [Online]. Available: [https://www.promptingguide.ai/techniques/react](https://www.promptingguide.ai/techniques/react)

[5] DigitalOcean, "AI Agents and Their Types," 2024. [Online]. Available: [https://www.digitalocean.com/resources/articles/types-of-ai-agents](https://www.digitalocean.com/resources/articles/types-of-ai-agents)

[6] IBM, "What is a multi-agent system?," 2024. [Online]. Available: [https://www.ibm.com/think/topics/multiagent-system](https://www.ibm.com/think/topics/multiagent-system)

```quiz
Q: The leap from "chatbot" to "agent" is…
- a new kind of model
- architectural: a loop, a set of tools, and a memory of what's happened so far, on top of the same LLM
correct: 1
explain: Agents don't use a different model. They add persistent state, tools, and a control loop that decides when to act and when to answer.

Q: Function calling matters for agents because…
- it lets the model train new tools at runtime
- it gives a structured, typed way for the model to invoke tools, replacing fragile free-text parsing
correct: 1
explain: Schema-constrained function calls are deterministic and parseable. That reliability is what makes tool-using agents buildable.

Q: The ReAct loop is…
- Train, Evaluate, Act, Compute
- Thought → Action → Observation, repeated until a final answer
correct: 1
explain: ReAct externalizes reasoning (Thought), takes real actions (Action), and appends results (Observation). The model loops until it can answer.

Q: A multi-agent system differs from a single agent in that…
- it uses a different kind of model
- it coordinates several specialized agents, each still being the same loop, to handle separable roles
correct: 1
explain: Multi-agent adds a coordination layer on top of the same agent loop. The payoff is specialization; the cost is orchestration complexity.

Q: When should you reach for an agent rather than a single LLM call?
- always; agents are strictly better
- when the task provably needs iteration based on observation (multi-step, real-world data, conditional next steps)
correct: 1
explain: If one call can do it, a prompt is cheaper and easier to debug. Agents earn their complexity only when iteration on observations is required.
```
