Title: Lecture 2 — Tool Calling, ReAct, and Agent Planning Patterns
Course: AIINFRA 300: Agentic AI & the Model Context Protocol (MCP) > Section: Week 02 — Tool Calling, ReAct, and Agent Planning Patterns

📖 Lecture — Tool Calling, ReAct, and Agent Planning Patterns

Last week you met the idea of an "agent." This week we open the hood on the mechanism that actually lets an LLM do things instead of only say things: tool calling, and the reasoning loop that decides which tool to call, when, and what to do with the result.

Two ways to connect a model to the world

The simplest connection is function (tool) calling. You describe a function's name, parameters, and purpose in a schema; the model decides whether to call it and with what arguments; your code executes the real function and returns the result. This is a single round trip: reasoning happens once, execution happens once, and you get an answer. It is fast, cheap, and reliable for simple, well-bounded tasks — "what's the weather in LA?" doesn't need a multi-step plan. ReAct (Yao et al., 2022) is what you reach for when a task needs more than one round trip. ReAct interleaves reasoning traces and actions in a repeating loop:

1. Thought — the model reasons in natural language about what it knows and what it needs next.
2. Action — the model calls a tool based on that thought.
3. Observation — the tool's result is appended back into the model's context.

The loop repeats — Thought → Action → Observation → Thought → ... — until the model decides it has enough information to produce a final answer. Because the model's reasoning is written down at every step, you get a legible trace of why it did what it did, which matters enormously for debugging and for trust. In the original paper, this interleaving reduced hallucination and error propagation compared to chain-of-thought prompting alone on multi-hop QA benchmarks (HotpotQA, FEVER), and it beat imitation- and reinforcement-learning baselines by 34% and 10% absolute success rate on the interactive ALFWorld and WebShop benchmarks, respectively — because the model could course-correct mid-task instead of committing to one reasoning chain blind.

The modern tool-calling loop

Strip away the framework branding and every ReAct-style agent runs the same loop:

1. Send the conversation so far to a chat model that has tools bound to it.
2. If the model's response contains no tool calls, you're done — return the response to the user.
3. If the response does contain tool calls, execute each one, append the results back into the conversation as tool messages, and go to step 1.

That's it — it's a while loop with a model call and a conditional branch. LangChain's own guidance has shifted accordingly: the legacy AgentExecutor class hard-coded a lot of this loop's behavior and was hard to customize, so LangChain now recommends building ReAct-style agents directly in LangGraph, where you define the loop yourself as an explicit graph of nodes (a "model" node, a "tools" node) and edges (conditional: tool calls → tools node; no tool calls → end). You'll build exactly this graph in this week's lab.

Function calling vs. ReAct — how to choose

Dimension | Function/Tool Calling (single-shot) | ReAct (looped)
Reasoning vs. execution | Separated — model reasons once, then hands off | Fused — reasoning and action alternate
Best for | Simple, bounded tasks with one clear tool need | Multi-hop tasks, tasks needing self-correction
Latency / cost | Low (one round trip) | Higher (N round trips)
Traceability | Minimal — you see the call, not the "why" | High — every step has a Thought you can audit
Failure recovery | None built in | Can observe a bad result and try a different action

Use plain function calling when the task is a single lookup or transformation. Reach for ReAct when the task requires multiple dependent steps, when the right tool to use next depends on what a previous tool returned, or when you need an audit trail of the agent's reasoning.

Structured outputs make tool arguments reliable

A tool call is only as good as its arguments. OpenAI's strict: true mode for structured outputs forces the model to produce arguments that exactly match a JSON Schema: every property must appear in required (mark truly optional fields as nullable rather than omitting them), and additionalProperties: false must be set so the model can't invent extra fields. Both the OpenAI SDK and LangChain support defining these schemas natively with Pydantic (Python) or Zod (TypeScript), so you rarely hand-write raw JSON Schema yourself — you define a typed class, and the framework generates the strict schema for you.

Beyond basic ReAct

Plain ReAct is powerful but not the only planning pattern, and each alternative makes a different trade-off between token cost and reliability:

- ReWOO (Reasoning WithOut Observation) separates planning from execution: the model writes a full multi-step plan up front, then executes it, rather than re-reasoning after every single observation. This uses far fewer tokens on long multi-step tasks, at the cost of being less able to react mid-plan to a surprising result.
- Reflexion adds a self-reflection step after a failed attempt: the agent writes a short critique of what went wrong and carries that critique into its next attempt, improving over successive tries without retraining.
- Tree-of-Thoughts explores multiple reasoning branches in parallel (like a search tree) instead of committing to one linear path, trading significantly higher token cost for better performance on problems with many plausible next steps.

None of these replace ReAct outright — they're tools for when ReAct's one-step-at-a-time approach is too expensive (use ReWOO) or too failure-prone (use Reflexion) or too narrow (use Tree-of-Thoughts) for your specific task.

Correcting three common misconceptions

"ReAct agents plan the whole task ahead of time." Not so. Classic ReAct is inherently short-term: at each step the model only reasons about the next action given what it has observed so far — it has no holistic view of the entire task graph. This is exactly why it can take an inefficient path through a task with interdependent steps, and exactly why ReWOO exists as an alternative when you need real upfront planning. "A failed tool call just gets retried and fixed automatically." There is no free error handling. If your tool node doesn't explicitly catch and surface errors, an agent can loop on the same failed action indefinitely, or a noisy/malformed observation can get appended to context and quietly poison every reasoning step that follows. Explicit error handling — catching exceptions, returning a clear error message as the observation, and giving the model a chance to try something different — is not optional polish; it's a core part of building the loop. "Give the agent every tool available so it can do anything." More tools is not strictly better. Once an agent has roughly 20–30+ tools bound to it, tool selection itself degrades: overlapping or vaguely-worded tool descriptions cause the model to pick the wrong tool, loop between plausible candidates, or hesitate — and every tool's schema sits in the context window whether it's used or not, silently eating your token budget. Curate a small, well-described toolset for the task at hand rather than exposing everything you have. You now have the conceptual and practical grounding to build a ReAct agent yourself. Let's do it.