Chapter 4: Implementing a basic ReAct agent
This introduction presents ReAct as the repeating reasoning-and-action pattern that combines an LLM with tools to solve problems.
After this chapter you will be able to
- ReAct reasoning and action cycle. Explain that ReAct is a way of designing agents, not a framework: an agent assesses its situation, decides what information or action it needs, acts, observes the result, and repeats as it works toward a solution.
- ReAct reasoning and action cycle. Explain how a ReAct agent alternates between identifying what it needs to answer a question and taking actions to obtain it, using the marathon-to-the-Moon example.
- Think-act-observe cycle. Trace how a ReAct agent states its thinking, takes an action, observes the result, and repeats until it has enough information to answer.
- Adaptive reasoning and tool actions. Explain how observations let a ReAct agent change its approach, with the LLM handling reasoning and tools handling actions, rather than following a fixed script.
- From parsed text to tool calls. Explain why early ReAct agents were fragile and how tool calling changes the way an LLM specifies an action.
- Reasoning within tool-calling ReAct. Explain why an agent remains a ReAct agent when tool calling removes the explicit written thought step.
- Agent interface and actions. Describe what the agent does behind its simple interface: it receives a question, decides whether to use a tool, executes the tool if needed, and returns a final answer.
- Centralized execution state. Explain why a message list is insufficient for agent execution and why ExecutionContext holds the additional state in one container.
- Context-centered information flow. Trace how information moves between ExecutionContext, an LLM call, and tools, including how tools access execution state.
- Curated LLM request. Distinguish the complete state in ExecutionContext from the selected information placed in LlmRequest for a particular LLM call.
- Centralized agent state. Explain that ExecutionContext keeps agent state together rather than scattered across variables, making it easier for components to share information.
- Agent execution content types. Distinguish messages, tool calls, and tool results in the Kipchoge example, and describe what each records.
- Event metadata and audit trail. Explain why content items are wrapped as Events and how event metadata supports tracing an agent run.
- Execution identity and progress fields. Describe how execution_id, events, current_step, and final_result identify an execution, record its history, limit its steps, and hold its answer.
- Limits of function-based tools. Explain that simple Python functions and a utility that converts them to LLM tool definitions worked for learning tool-calling basics, but do not handle the practical challenges of building a complete agent framework well.
- Unified tool interface rationale. Explain why bundling a tool’s function with its metadata avoids the manual coordination required when an agent sends tool definitions to an LLM and maps returned names back to functions for execution.
- BaseTool interface contract. Describe the properties and execution method every BaseTool subclass must provide, including what the tool_definition schema tells the LLM.
- Execution context propagation. Explain why execute always takes ExecutionContext first, even when a particular tool does not use it.
- Conditional context propagation. Explain how FunctionTool lets the agent pass ExecutionContext to every tool without requiring every wrapped function to accept a context parameter.
- MCP tool adaptation. Trace how MCP tools become usable by the agent: load_mcp_tools discovers them, and a server-calling wrapper for each tool is wrapped in FunctionTool. Explain why this lets local and remote tools be used through the same BaseTool interface.
- Communication layer purpose. Explain why the agent needs an LLM communication layer alongside ExecutionContext and BaseTool.
- Need for a communication layer. Explain why the agent needs a communication layer even when it uses LiteLLM: ExecutionContext data must be translated into API messages, and requests and responses still need consistent handling.
- Communication component responsibilities. Describe what LlmRequest, LlmClient, and LlmResponse each do, and how their separation limits what each component must know.
- LlmRequest outbound fields. Describe the four components of LlmRequest and what each contributes to an LLM call: combined instructions, conversation contents, available tools, and tool-selection behavior.
- Agent-owned context selection. Explain why LlmRequest does not hold ExecutionContext or Events directly and how the agent supplies its contents.
- Standardized response content. Explain how LlmResponse standardizes responses from different providers and represents their output as ContentItem objects.
- LlmClient provider adapter. Explain how LlmClient uses LiteLLM to connect LlmRequest and LlmResponse to multiple LLM providers, and describe what generate does when an API call fails.
- Request message conversion. Describe how _build_messages converts stored ContentItems into API messages, especially the placement of assistant tool calls and tool results.
- API response parsing. Describe how _parse_response converts API text and tool-call outputs into the agent’s standard ContentItem list.
- Communication layer request flow. Trace how an LlmClient, LlmRequest, and LlmResponse work together to obtain an answer, and explain the responsibility of each component.
- Agent orchestration flow. Trace how the Agent coordinates its components: it sets up tools, creates an ExecutionContext, and repeats steps in which the LLM decides what to do and requested tools are executed.
- Agent constructor inputs. Explain what the Agent constructor receives: an LlmClient for LLM communication, BaseTool instances the agent can use, instructions that define its behavior, and a max_steps limit that guards against endless tool calls. Describe that the constructor stores these components for execution.
- Run method execution flow. Trace how run() prepares the execution context, records the user input, and repeatedly calls step() until it finds a final result or reaches the maximum step limit.
- Final response detection. Explain how run() identifies a final response after a step and extracts the assistant's answer from it.
- Step cycle sequence. Trace how step() prepares a request, gets and records the LLM’s decision, executes and records any requested tool calls, and increments the step counter.
- History and request separation. Distinguish the complete history in ExecutionContext from the selected information in LlmRequest, and explain why keeping them separate allows context engineering without changing the stored history.
- Focused LLM decision handoff. Explain that think() sends a prepared LlmRequest to LlmClient and returns its response, leaving request construction and response parsing to other components.
- Requested tool execution. Trace how act() looks up each requested tool by name, executes each ToolCall with its arguments and ExecutionContext, and collects the results.
- Tool failure feedback. Explain how act() captures tool failures as error results and how the resulting error message lets the LLM adapt in the next step rather than stopping the agent.
- Need for structured agent results. Explain why production uses such as feedback analysis or report generation may require structured agent output rather than free-form text.
- Tools as output formatters. Explain how a special tool can enforce a structured final answer: its input uses the desired output schema, and the agent calls it with structured data instead of generating free text to parse afterward. Explain why this reuses the agent’s tool-calling mechanism and the LLM’s ability to produce schema-matching JSON.
- Schema-backed final answer tool. Explain how an output_type Pydantic model leads the agent to create a final_answer tool whose input follows that model, and how the tool call is validated.
- Required tool choice for output. Explain how the agent changes its LLM request when structured output is required, and what happens otherwise.
- Structured completion and result extraction. Describe when the agent considers a structured answer complete and what it returns to calling code.
- Typed final answer output. Explain how the customer-feedback agent returns structured sentiment data and how an application accesses it. Describe why the same pattern can support other output schemas.
- GAIA baseline limitations. Explain why LLMs tested directly on GAIA problems had low accuracy.
- GAIA evaluation agent substitution. Explain how the GAIA evaluation replaces direct LLM calls with a tool-equipped agent while reusing the earlier evaluation structure. Describe how the agent's answer is obtained from its result.
- Web search experiment results. Describe the controlled GAIA comparison and explain what its results show about web search tools.
What it builds on
- How tool calling works (chapter 3) · The ability of an LLM to call tools supplies the actions that the agent can take.
- Agent: LLM-directed flow (chapter 1) · The distinction between an LLM-directed agent and a developer-defined flow.
- End-to-end tool execution (chapter 3) · Tool execution and returning its result.
- Conversation management: Handling stateless APIs (chapter 2) · Managing conversation history for LLM calls.
- From prompt engineering to context engineering (chapter 1) · The idea of context engineering.
- Converting to tool definitions (chapter 3) · The utility that converts Python functions to LLM tool definitions.
- Understanding the MCP client (chapter 3) · MCP clients discover and call tools on servers.
- Unifying providers with LiteLLM (chapter 2) · LiteLLM's role in abstracting provider differences.
Section by section
Introduction
This introduction presents ReAct as the repeating reasoning-and-action pattern that combines an LLM with tools to solve problems.
- ReAct reasoning and action cycle: Explain that ReAct is a way of designing agents, not a framework: an agent assesses its situation, decides what information or action it needs, acts, observes the result, and repeats as it works toward a solution.
Why it matters: This pattern explains how reasoning and tool use become an agent that can work through a problem in multiple steps.
Key terms: ReAct, reasoning and acting, agent loop
4.1 How ReAct agents work
Introduces ReAct as a pattern in which an agent reasons about what it needs, then acts to obtain it, rather than answering immediately.
- ReAct reasoning and action cycle: Explain how a ReAct agent alternates between identifying what it needs to answer a question and taking actions to obtain it, using the marathon-to-the-Moon example.
Why it matters: This cycle is the basic pattern the chapter builds on when implementing an agent.
Key terms: ReAct, thinking and acting
4.1.1 The think-act cycle
This section explains how a ReAct agent repeats a think-act-observe cycle and adapts its next step to what it discovers.
- Think-act-observe cycle: Trace how a ReAct agent states its thinking, takes an action, observes the result, and repeats until it has enough information to answer.
Why it matters: This cycle is the basic pattern behind the agent’s execution. - Adaptive reasoning and tool actions: Explain how observations let a ReAct agent change its approach, with the LLM handling reasoning and tools handling actions, rather than following a fixed script.
Why it matters: Adapting to results lets the agent continue when an initial action does not help.
Key terms: ReAct agent, think-act cycle, LLM, tools, fixed script
4.1.2 From text parsing to tool calling
This section explains how API-supported tool calling replaces fragile text parsing in ReAct agents while preserving their reasoning-and-action cycle.
- From parsed text to tool calls: Explain why early ReAct agents were fragile and how tool calling changes the way an LLM specifies an action.
Why it matters: This distinction explains why the agent can use tools without relying on brittle prompt-enforced text formats. - Reasoning within tool-calling ReAct: Explain why an agent remains a ReAct agent when tool calling removes the explicit written thought step.
Why it matters: It keeps the focus on the agent’s adaptive cycle rather than on whether its reasoning appears as parseable text.
Key terms: text parsing, tool calling, structured outputs, arguments, ReAct, thought, reasoning, tool selection, observe
4.2 Agent architecture overview
This section introduces the upcoming overview of the agent architecture before its components are explained.
4.2.1 The completed agent
A simple agent interface hides the work of deciding whether a question needs a tool, using one if necessary, and returning an answer.
- Agent interface and actions: Describe what the agent does behind its simple interface: it receives a question, decides whether to use a tool, executes the tool if needed, and returns a final answer.
Why it matters: This gives you the basic behavior that the agent architecture must support.
Key terms: agent, tools, final answer, calculator tool
4.2.2 Information flow: The core design
This section explains how an agent’s execution state flows through an ExecutionContext-centered design and why LLM requests contain only a selected part of that state.
- Agent execution cycle: Trace the agent’s basic cycle from user input through LLM decisions, optional tool execution, and results returned to the LLM until it gives a final answer.
Why it matters: This cycle explains why the agent needs to retain information across multiple steps. - Centralized execution state: Explain why a message list is insufficient for agent execution and why ExecutionContext holds the additional state in one container.
Why it matters: Central storage keeps the agent’s methods manageable as execution state grows. - Context-centered information flow: Trace how information moves between ExecutionContext, an LLM call, and tools, including how tools access execution state.
Why it matters: Following this flow shows how LLM calls and tools share a consistent execution state. - Curated LLM request: Distinguish the complete state in ExecutionContext from the selected information placed in LlmRequest for a particular LLM call.
Why it matters: The distinction prevents the agent’s full internal state from being treated as the LLM’s prompt.
Key terms: LLM, tools, final answer, ExecutionContext, events, current_step, execution_id, state, final_result, LlmRequest
4.2.3 Components we have to build
This section outlines the order in which the agent’s components will be implemented.
4.3 ExecutionContext: The agent’s central storage
This section explains why an agent needs ExecutionContext as central storage for shared state.
- Centralized agent state: Explain that ExecutionContext keeps agent state together rather than scattered across variables, making it easier for components to share information.
Why it matters: Keeping shared state in one place makes the agent’s components easier to coordinate.
Key terms: ExecutionContext, state
4.3.1 What happens during agent execution
The section turns an agent’s sequence of messages and tool use into identifiable content types and events that can be traced during debugging.
- Agent execution content types: Distinguish messages, tool calls, and tool results in the Kipchoge example, and describe what each records.
Why it matters: These distinctions let you describe what happened at each step of an agent run. - Tool content identification and linkage: Explain how content types are identified and how a tool result is matched to its originating call.
Why it matters: A result must remain attributable to the right request even when several tools execute at once. - Event metadata and audit trail: Explain why content items are wrapped as Events and how event metadata supports tracing an agent run.
Why it matters: Events make it possible to trace and debug a complete problem-solving session rather than inspect isolated content items.
Key terms: Messages, Tool calls, Tool results, type field, discriminator, tool_call_id, ToolResult, ToolCall, Event, metadata
4.3.2 ExecutionContext implementation
This section identifies the data stored in ExecutionContext and explains how tools may use it to access execution state.
- Execution identity and progress fields: Describe how execution_id, events, current_step, and final_result identify an execution, record its history, limit its steps, and hold its answer.
Why it matters: These fields let the agent keep track of an execution from its first step to its final answer. - Custom state and tool access: Explain what the state field stores and what a tool can access when it receives ExecutionContext.
Why it matters: Passing context gives a tool access to relevant data and execution history.
Key terms: ExecutionContext, execution_id, events, current_step, max_steps, final_result, state
4.4 Tool abstraction
This introduction explains why the function-based tools used to learn tool calling are not sufficient for a complete agent framework.
- Limits of function-based tools: Explain that simple Python functions and a utility that converts them to LLM tool definitions worked for learning tool-calling basics, but do not handle the practical challenges of building a complete agent framework well.
Why it matters: It explains why the book moves beyond the earlier function-based approach.
Key terms: Python functions, tool definitions, tool calling, agent framework
4.4.1 Why we need a unified tool interface
This section explains why an agent needs a unified tool interface instead of managing tool functions and their metadata separately.
- Unified tool interface rationale: Explain why bundling a tool’s function with its metadata avoids the manual coordination required when an agent sends tool definitions to an LLM and maps returned names back to functions for execution.
Why it matters: Bundling a tool with its definition removes a coordination burden when building the agent.
Key terms: tool abstraction, tool definitions, metadata
4.4.2 BaseTool: The foundation
This section defines the BaseTool contract: the information every tool exposes, how it executes with shared context, and a few conveniences for implementing tools.
- BaseTool interface contract: Describe the properties and execution method every BaseTool subclass must provide, including what the tool_definition schema tells the LLM.
Why it matters: A shared contract lets the agent work with different tools through the same interface. - Execution context propagation: Explain why execute always takes ExecutionContext first, even when a particular tool does not use it.
Why it matters: Passing context consistently gives tools access to execution state without requiring every tool to use it. - BaseTool implementation conveniences: Describe the defaults and calling shorthand BaseTool supplies, and explain how its schema storage accommodates subclasses.
Why it matters: These defaults and shorthand make simple tools easier to define and invoke.
Key terms: BaseTool, name, description, tool_definition, execute, ExecutionContext, context propagation, Agent, class name, docstring
4.4.3 FunctionTool: Wrapping functions
This section explains how FunctionTool wraps ordinary functions as agent tools, handles context only when a function accepts it, generates tool definitions, and offers @tool as a convenient wrapper.
- Conditional context propagation: Explain how FunctionTool lets the agent pass ExecutionContext to every tool without requiring every wrapped function to accept a context parameter.
Why it matters: This is how one tool interface accommodates both simple functions and functions that need execution state. - Automatic tool definitions: Describe how FunctionTool creates a tool definition from a wrapped function rather than requiring its metadata and input schema to be written separately.
Why it matters: Automatic definitions make existing functions available as tools with less duplicated specification. - Tool decorator convenience: Explain what @tool changes compared with constructing a FunctionTool directly, including its optional metadata overrides.
Why it matters: The decorator offers concise tool registration without changing the underlying FunctionTool behavior.
Key terms: FunctionTool, BaseTool, ExecutionContext, context propagation, execute, _generate_definition, function_to_input_schema, tool schema, type hints, @tool
4.4.4 Integrating MCP tools
This section shows how to adapt remote MCP tools to the agent’s existing tool interface and notes a connection overhead in the simple implementation.
- MCP tool adaptation: Trace how MCP tools become usable by the agent: load_mcp_tools discovers them, and a server-calling wrapper for each tool is wrapped in FunctionTool. Explain why this lets local and remote tools be used through the same BaseTool interface.
Why it matters: The adapter lets the agent use server-hosted tools without a separate tool interface. - Server-provided tool schema: Explain why _create_mcp_tool passes tool_definition explicitly when it creates a FunctionTool.
Why it matters: Using the server-provided schema preserves the definition supplied for the remote tool. - Per-call connection overhead: Describe the connection tradeoff in this MCP adapter and the alternative suggested for frequent tool use.
Why it matters: Connection setup can become costly when an agent calls MCP tools often.
Key terms: MCP tools, load_mcp_tools, FunctionTool, BaseTool, session.call_tool(), tool_definition, mcp_tool.inputSchema, new connection, persistent session, connection setup
4.5 LLM communication layer
Introduces the LLM communication layer as the bridge between the agent’s internal data structures and the LLM API.
- Communication layer purpose: Explain why the agent needs an LLM communication layer alongside ExecutionContext and BaseTool.
Why it matters: It clarifies the distinct role of the communication layer before its implementation is introduced.
Key terms: ExecutionContext, BaseTool, LLM communication layer
4.5.1 Reasons for adding a communication layer
The section explains why an agent needs a communication layer between its execution data and LLM APIs, and divides that layer into request, client, and response components.
- Need for a communication layer: Explain why the agent needs a communication layer even when it uses LiteLLM: ExecutionContext data must be translated into API messages, and requests and responses still need consistent handling.
Why it matters: It explains why API calls need their own layer rather than being handled throughout the Agent class. - Communication component responsibilities: Describe what LlmRequest, LlmClient, and LlmResponse each do, and how their separation limits what each component must know.
Why it matters: The division gives each part of the LLM exchange one clear job.
Key terms: ExecutionContext, Events, ContentItems, LiteLLM, communication layer, LlmRequest, LlmClient, LlmResponse
4.5.2 LlmRequest: Selecting what to send
This section defines what LlmRequest sends in a single LLM call and explains why the agent, rather than the request object, selects that information.
- LlmRequest outbound fields: Describe the four components of LlmRequest and what each contributes to an LLM call: combined instructions, conversation contents, available tools, and tool-selection behavior.
Why it matters: These fields determine the information and tool options available to the LLM for a call. - Agent-owned context selection: Explain why LlmRequest does not hold ExecutionContext or Events directly and how the agent supplies its contents.
Why it matters: Separating selection from the request object makes clear where the agent decides what context the LLM sees.
Key terms: LlmRequest, instructions, contents, ContentItem, ToolCalls, ToolResults, ExecutionContext, Events, context engineering
4.5.3 LlmResponse: Standardizing what we receive
This section explains how LlmResponse gives the agent a consistent way to receive LLM output, failures, and token-usage information.
- Standardized response content: Explain how LlmResponse standardizes responses from different providers and represents their output as ContentItem objects.
Why it matters: The agent can work with the same response types regardless of the LLM provider. - Captured API failures: Describe how LlmResponse records API failures and why the agent can use that information.
Why it matters: A failed API call can be handled as part of the agent’s response rather than simply interrupting it. - Stored token usage: Explain what usage_metadata records and why the information is useful.
Why it matters: Keeping token counts makes it possible to investigate usage and costs later.
Key terms: LlmResponse, content, ContentItem, Message, ToolCalls, error_message, usage_metadata, input and output tokens
4.5.4 LlmClient: The provider adapter
This section explains how LlmClient translates between the agent’s request and response formats and LLM APIs, including tool calls and results.
- LlmClient provider adapter: Explain how LlmClient uses LiteLLM to connect LlmRequest and LlmResponse to multiple LLM providers, and describe what generate does when an API call fails.
Why it matters: This adapter lets the rest of the agent use consistent request and response types across providers. - Request message conversion: Describe how _build_messages converts stored ContentItems into API messages, especially the placement of assistant tool calls and tool results.
Why it matters: Correct message conversion preserves the relationship between an assistant’s tool request and the tool’s result. - API response parsing: Describe how _parse_response converts API text and tool-call outputs into the agent’s standard ContentItem list.
Why it matters: Parsing gives the agent one consistent way to handle responses containing text, tool requests, or both.
Key terms: LlmClient, LlmRequest, LlmResponse, LiteLLM, generate, acompletion, _build_messages, ContentItems, Message, ToolCall
4.5.5 Putting it together
This section shows how the three LLM communication-layer components work together and what each is responsible for.
- Communication layer request flow: Trace how an LlmClient, LlmRequest, and LlmResponse work together to obtain an answer, and explain the responsibility of each component.
Why it matters: This separation makes it clear where to prepare an LLM call and where to read its result.
Key terms: LlmRequest, LlmClient, generate(), LlmResponse, ContentItem, content list
4.6 Implementing the agent
Introduces how the Agent class connects state, tools, and LLM communication in a repeated think–act cycle.
- Agent orchestration flow: Trace how the Agent coordinates its components: it sets up tools, creates an ExecutionContext, and repeats steps in which the LLM decides what to do and requested tools are executed.
Why it matters: This flow gives you a map for following the Agent methods as they are implemented.
Key terms: Agent, ExecutionContext, BaseTool, LlmClient, think–act cycle
4.6.1 Agent class structure
This subsection defines the Agent class’s constructor inputs and its initial tool-setup method.
- Agent constructor inputs: Explain what the Agent constructor receives: an LlmClient for LLM communication, BaseTool instances the agent can use, instructions that define its behavior, and a max_steps limit that guards against endless tool calls. Describe that the constructor stores these components for execution.
Why it matters: These inputs establish what the agent can use and how its execution is bounded. - Initial tool setup: Describe what _setup_tools() does now and why the method exists despite that simple behavior.
Why it matters: It distinguishes the method’s current behavior from its intended role as an extension point.
Key terms: Agent, LlmClient, BaseTool, instructions, max_steps, _setup_tools(), tools list
4.6.2 The run() method
This section explains how run() starts and controls agent execution, detects a final answer, and returns both the answer and its execution trace.
- Run method execution flow: Trace how run() prepares the execution context, records the user input, and repeatedly calls step() until it finds a final result or reaches the maximum step limit.
Why it matters: This is the control flow that turns a user request into a sequence of agent steps. - Final response detection: Explain how run() identifies a final response after a step and extracts the assistant's answer from it.
Why it matters: The agent needs to distinguish a direct answer from an event that still involves a tool. - Answer and trace result: Describe what AgentResult gives the caller and why the ExecutionContext is returned alongside the output.
Why it matters: Keeping the trace beside the answer makes an agent run easier to debug and analyze.
Key terms: run(), ExecutionContext, Event, step(), maximum step limit, _is_final_response(), _extract_final_result(), tool calls, tool results, assistant's message
4.6.3 The step() method
This section explains how step() runs one think–act cycle and how it turns stored execution history into an LLM request.
- Step cycle sequence: Trace how step() prepares a request, gets and records the LLM’s decision, executes and records any requested tool calls, and increments the step counter.
Why it matters: This sequence shows what the agent does during a single step and what it records. - LLM request preparation: Describe how _prepare_llm_request() builds an LlmRequest from ExecutionContext, including event content, agent instructions, available tools, and automatic tool choice.
Why it matters: It explains what information the LLM receives before making its decision. - History and request separation: Distinguish the complete history in ExecutionContext from the selected information in LlmRequest, and explain why keeping them separate allows context engineering without changing the stored history.
Why it matters: Separating storage from the request makes it possible to change the LLM’s input without losing the original execution history.
Key terms: step(), think(), act(), Event, step counter, _prepare_llm_request(), ExecutionContext, LlmRequest, content items, tool choice
4.6.4 The think() and act() methods
This section explains how an agent step obtains an LLM decision, executes requested tools, and turns tool failures into feedback for the next step.
- Focused LLM decision handoff: Explain that think() sends a prepared LlmRequest to LlmClient and returns its response, leaving request construction and response parsing to other components.
Why it matters: This separation makes it clear where the agent obtains its decision and where request preparation and response parsing belong. - Requested tool execution: Trace how act() looks up each requested tool by name, executes each ToolCall with its arguments and ExecutionContext, and collects the results.
Why it matters: This is the point where the LLM’s requested actions become actual tool executions. - Tool failure feedback: Explain how act() captures tool failures as error results and how the resulting error message lets the LLM adapt in the next step rather than stopping the agent.
Why it matters: Making failures visible to the LLM allows the agent to try a different approach instead of stopping immediately.
Key terms: think(), LlmRequest, LlmClient, _prepare_llm_request(), act(), ToolCall, ExecutionContext, try-except, error results, conversation history
4.7 Adding structured output
This section contrasts structured output from standalone LLM calls with the agent’s free-form final answers and explains why some applications need structured agent results.
- Current agent output behavior: Describe how the current agent produces its final result and distinguish it from the earlier standalone LLM calls that used structured output.
Why it matters: The distinction identifies what the existing agent does not yet provide. - Need for structured agent results: Explain why production uses such as feedback analysis or report generation may require structured agent output rather than free-form text.
Why it matters: A predictable result lets another system consume the agent’s answer without depending on its wording.
Key terms: structured output, GaiaOutput, response_format, _extract_final_result(), free-form text, structured data, downstream systems, fragile parsing logic
4.7.1 The approach: Tools as output formatters
This section explains how an agent can produce structured final answers by using its existing tool-calling mechanism as an output formatter.
- Tools as output formatters: Explain how a special tool can enforce a structured final answer: its input uses the desired output schema, and the agent calls it with structured data instead of generating free text to parse afterward. Explain why this reuses the agent’s tool-calling mechanism and the LLM’s ability to produce schema-matching JSON.
Why it matters: The agent can deliver an answer in the required format without a separate free-text parsing step.
Key terms: tool-calling infrastructure, structured outputs, output schema, type safety, format compliance
4.7.2 Modifying the agent
This section modifies the agent to produce schema-validated structured output through a dynamically created final-answer tool.
- Schema-backed final answer tool: Explain how an output_type Pydantic model leads the agent to create a final_answer tool whose input follows that model, and how the tool call is validated.
Why it matters: The tool gives the agent a schema-checked way to deliver its final answer. - Required tool choice for output: Explain how the agent changes its LLM request when structured output is required, and what happens otherwise.
Why it matters: Requiring tool use prevents the model from bypassing the structured-output path with plain text. - Structured completion and result extraction: Describe when the agent considers a structured answer complete and what it returns to calling code.
Why it matters: The caller receives a typed result only after the designated output tool has succeeded.
Key terms: output_type, Pydantic model, final_answer, @tool, output_tool_name, _prepare_llm_request(), tool_choice, required, output tool, _is_final_response()
4.7.3 Using structured output in practice
This subsection shows how an agent returns a typed sentiment-analysis result and why the same structured-output pattern works for other applications.
- Typed final answer output: Explain how the customer-feedback agent returns structured sentiment data and how an application accesses it. Describe why the same pattern can support other output schemas.
Why it matters: Applications can use agent results as typed data instead of parsing a free-form response.
Key terms: final_answer tool, SentimentAnalysis, Pydantic model, result.output, output schemas
4.8 Testing with the GAIA benchmark
This section recalls why direct LLM testing on GAIA produced low accuracy before introducing a test of the tool-equipped agent.
- GAIA baseline limitations: Explain why LLMs tested directly on GAIA problems had low accuracy.
Why it matters: This gives you a baseline for understanding why the book next tests an agent equipped with tools.
Key terms: GAIA, LLM, low accuracy
4.8.1 From LLM to agent
This section adapts the earlier GAIA evaluation from direct LLM calls to a tool-equipped agent while keeping most of the evaluation pipeline intact.
- GAIA evaluation agent substitution: Explain how the GAIA evaluation replaces direct LLM calls with a tool-equipped agent while reusing the earlier evaluation structure. Describe how the agent's answer is obtained from its result.
Why it matters: This identifies what changes—and what stays reusable—when evaluating an agent instead of a bare LLM. - Concurrent agent execution limit: Explain what the semaphore limits in the agent-based evaluation and why that limit protects the APIs.
Why it matters: An agent run can generate more API traffic than a single LLM call, so evaluation concurrency must be controlled at the run level.
Key terms: GAIA, GaiaOutput, Tavily MCP server, solve_problem, agent.run(), AgentResult, semaphore, Agent executions, internal LLM calls
4.8.2 Results
This section uses execution traces and a controlled GAIA comparison to show the value of web search tools and the limits of an agent that cannot process attached files.
- Execution traces for debugging: Explain how an agent’s execution trace helps diagnose a failed problem.
Why it matters: A final wrong answer does not show which step caused the failure, but a trace can. - Web search experiment results: Describe the controlled GAIA comparison and explain what its results show about web search tools.
Why it matters: The comparison isolates the practical benefit of giving an agent access to web information on tasks that require it. - Remaining file processing limitation: Explain why web search alone does not let the current agent solve all GAIA level-1 problems.
Why it matters: It identifies a concrete boundary of the agent evaluated in this section.
Key terms: AgentResult, ExecutionContext, display_trace(), execution traces, GAIA level-1, web search tools, GPT-5, 50 percentage-point improvement, is_solvable=False, attached files
4.9 Summary
This section summarizes how the ReAct loop, shared execution state, tool and LLM interfaces, and structured output fit into the chapter’s agent implementation.
Where this is used later
- Implementing the reflection tool (chapter 7) · Assumes the agent records execution information in its context.
- Implementing the planning tool (chapter 7) · The earlier tool abstraction provides the background for creating a tool.
- Connecting the sandbox to the agent (chapter 8) · Assumes functions can be exposed to the agent as tools.
- Extending FunctionTool (chapter 8) · FunctionTool wraps Python functions.
- Separating storage from presentation (chapter 6) · Context management builds on the distinction between stored information and what is presented to the LLM.
- Pillars of observability: Metrics, traces, and logs (chapter 10) · Monitoring can draw on recorded usage information.
- A simple case: Single file (chapter 5) · Assumes an agent to which a file-reading tool can be added.
- (chapter intro) (chapter 9) · Introduces the single Agent whose responsibilities can grow.
Concepts explained in this chapter
- execution contextAn execution context is a structured container for an agent execution session's identifier, event history, step counter, mutable state, and final result.
- LlmRequestLlmRequest is a structured container for a single language-model request, including instructions, conversation contents, available tools, and tool-selection settings.
- structured dataData organized according to a defined schema with predictable fields, types, or relationships.
- ReActThe ReAct pattern is an agent design in which reasoning and action alternate so the agent can incorporate tool results and adapt its subsequent behavior.
- BaseToolBaseTool is the framework's abstract base class that defines the required interface and behavior for executable tools.
- eventAn event is a structured record that wraps an execution content item with metadata about its producer and occurrence time.
- ContentItemA ContentItem is a typed structured unit representing one piece of conversational, language-model, or tool-interaction content.
- LlmResponseLlmResponse is a structured representation of a language-model response, including generated content, tool calls, errors, and usage information.
- FunctionToolFunctionTool is the book's tool wrapper that adapts a callable function and its tool definition to the BaseTool interface.
- run()run() is an Agent method that manages the overall agent execution loop and returns its execution result.
- tool usageA tool call is a model-generated specification requesting invocation of a particular external tool with particular arguments.
- AI agentAn AI agent is a software system that uses artificial intelligence to perceive information, perform tasks, and make or execute decisions on a user's or organization's behalf.
- Agent classThe Agent class is the book's abstract programming interface for representing an agent loop.
- trajectoryAn execution trace is a chronological record of events and intermediate activity produced during program or agent execution.
- tool resultA tool result is the output produced by an external tool after processing an invocation and its arguments, including whether execution succeeded or failed.
- tool descriptionA tool definition is a structured declaration of a callable tool's name, description, parameters, types, and required arguments for access by a language model.
- schemaA schema is a formal specification of the fields, types, and constraints that valid structured data must satisfy.
- GAIAThe GAIA dataset is a collection of benchmark problems, metadata, and associated task files for evaluating general-purpose AI assistants.
- context engineeringThe discipline of designing which information is provided to a language model, when it is provided, and how it is represented for task completion.
- exceptionError handling is the detection and management of failures arising during program execution or external requests.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
