Build a Readable AI Agent Loop

Build and trace a ReAct agent loop with a hosted model and one tool.

Introduction

30 Second Summary

AI agents can feel unpredictable when their decisions disappear inside a framework. A readable trace turns each decision into something you can inspect.

In this project, you will build a command-line agent that follows the ReAct pattern. You will connect a hosted model to one tool through a loop that records every turn in a trace file.

What You'll Build

You will ask your agent a question in the terminal and watch it request a tool, read the result, and return a final answer while every turn lands in a trace file.

By the end of this project, you'll have:

  • A readable agent loop that sends questions to a hosted model and prints its replies in your terminal.
  • A working tool call that lets the model request a calculation or read a file before producing its final answer.
  • A set of trace files that reveal every model input, model output, tool request, and tool result across three different questions.
  • An optional Secret Mission: swap the hosted model in one line and compare how the same three questions produce different traces.

Are there any prerequisites?

You need basic Python or TypeScript experience, a text editor, and one OpenRouter API key. You will use the key to connect your script to a hosted model.

Before We Start

A readable agent loop depends on a setup you can test one piece at a time. Your terminal must run the script before the model can answer anything.

In this step, you prepare Python 3 in Visual Studio Code. You give the OpenAI Python SDK a hosted-model API key through your terminal.

In this step, get ready to:
  • Prepare your local Python tools.
  • Create secure hosted-model access.
  • Open an empty project folder in Visual Studio Code.
Prepare Python and Visual Studio Code

Python runs each module in your agent. Visual Studio Code gives the project folder a clear home while you build those modules.

macOS

  • Press Cmd+Space to open Spotlight.
  • Type Terminal into Spotlight.
  • Press Enter to open Terminal.
  • Check whether Python is available by running this command:
python3 --version

You should see a version line that starts with Python 3.

Windows

  • Press the Windows key to open Search.
  • Type PowerShell into Search.
  • Press Enter to open PowerShell.
  • Check whether Python is available by running this command:
py -3 --version

You should see a version line that starts with Python 3.

✔️ Python is available

Good. Python is ready to run the small modules you build next.

ⓧ Command not found

Python needs to be installed before the terminal can run your agent.

macOS

  • Download the current macOS installer from the official Python website.
  • Open the downloaded installer.
  • Complete the standard installation.
  • Close Terminal after the installation finishes.
  • Use Spotlight to open a fresh Terminal window.
  • Check the installation by running this command:
python3 --version

You should now see a version line that starts with Python 3.

Windows

  • Visit the official Python downloads page.
  • Install the Python Install Manager.
  • Close PowerShell after the installation finishes.
  • Use Windows Search to open a fresh PowerShell window.
  • Check the installation by running this command:
py -3 --version

You should now see a version line that starts with Python 3.

The editor is the other half of your local setup. Check whether Visual Studio Code is already available before installing it.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Visual Studio Code into the search bar.

✔️ Visual Studio Code appears

  • Press Enter to open Visual Studio Code.

Your editor is ready for the empty project folder you create later in this step.

ⓧ Visual Studio Code is missing

The official installer selects the correct download options for macOS or Windows.

  • Visit the Visual Studio Code download page.
  • Download the installer for your operating system.
  • Open the downloaded installer.
  • Complete the installation prompts.
  • Return to your operating system search bar.
  • Type Visual Studio Code into the search bar.
  • Press Enter to open Visual Studio Code.

You should now see the Visual Studio Code welcome screen.

Still stuck on local setup?

Restart your terminal after installing Python so it can detect the new command. Restart Visual Studio Code if the editor opened before Python was installed.

Share the command you ran plus the visible error with help me fix my Python or Visual Studio Code setup.

Create hosted-model access

OpenRouter provides hosted models through an OpenAI-compatible endpoint. One account lets your script call a model without running that model on your computer.

API keys can feel risky because they grant account access. This key stays outside your project files in a terminal environment variable.

  • Visit the OpenRouter key page.
  • Complete the account creation or sign-in flow.
  • Create a new key named readable-agent-loop.
  • Copy the key when OpenRouter reveals it.

OpenRouter shows the plaintext key only when it is created. Keep the copied value private.

macOS

  • Return to the Terminal window from earlier.
  • Replace your-api-key-here with the key you copied.
  • Set the key for this terminal session by running:
export OPENAI_API_KEY="your-api-key-here"

The OPENAI_API_KEY variable is now available to programs launched from this Terminal window.

Windows

  • Return to the PowerShell window from earlier.
  • Replace your-api-key-here with the key you copied.
  • Set the key for this PowerShell session by running:
$Env:OPENAI_API_KEY="your-api-key-here"

The OPENAI_API_KEY variable is now available to programs launched from this PowerShell window.

Why use a terminal variable?

The OpenAI Python SDK can read OPENAI_API_KEY from the terminal environment. Your code can authenticate without containing the secret.

This variable lasts for the current terminal session. Set it again after closing that terminal.

Variable not setting?

Keep the same terminal open after running the command. Check that you replaced the placeholder while preserving the surrounding quotation marks.

Get focused help with help me set OPENAI_API_KEY safely.

Install the SDK and open the project folder

The SDK turns your Python instructions into requests that the hosted model understands. Installing it now keeps the next step focused on the first model reply.

  • Install the OpenAI Python SDK from your terminal by running:
pip install openai

What does this install?

  • The openai package gives your script a Python client for OpenAI-compatible model APIs.
  • Its client handles request formatting plus response parsing for your model module.

The terminal should finish with output showing that the package was installed.

Seeing an installation error?

Confirm that the Python version check from earlier still works in this terminal. A missing package installer usually means the Python installation needs to be repaired.

Use help me install the OpenAI Python SDK.

Your project folder starts empty because the first four lines belong to the next step. Visual Studio Code may ask whether you trust the folder after opening it.

  • In Visual Studio Code, click File in the top menu bar.
  • Click Open Folder....
  • Select your Desktop.
  • Click New Folder.
  • Enter readable-agent-loop as the folder name.
  • Click Open (macOS) or Select Folder (Windows).
  • Select Yes, I trust the authors if the Workspace Trust prompt appears.

The Explorer sidebar should show readable-agent-loop with no project files inside it.

Before you check the setup, which two pieces do you think the terminal can prove without printing your key?

macOS

  • Return to the Terminal window where you set the API key.
  • Check the SDK plus the environment variable by running these commands:
python3 -c "print('Python SDK ready:', bool(__import__('openai')))"
python3 -c "print('API key set:', bool(__import__('os').environ.get('OPENAI_API_KEY')))"

What do these checks prove?

  • The first command proves Python can load the installed openai package.
  • The second command checks whether OPENAI_API_KEY exists without printing its value.

Windows

  • Return to the PowerShell window where you set the API key.
  • Check the SDK plus the environment variable by running these commands:
py -3 -c "print('Python SDK ready:', bool(__import__('openai')))"
py -3 -c "print('API key set:', bool(__import__('os').environ.get('OPENAI_API_KEY')))"

What do these checks prove?

  • The first command proves Python can load the installed openai package.
  • The second command checks whether OPENAI_API_KEY exists without printing its value.

You should see Python SDK ready: True. The next line should read API key set: True.

That is the setup locked in. Your terminal can load the SDK with a key available for the next step.

Seeing False or an import error?

If the SDK check fails, rerun the installation command in the same terminal. If the key check returns False, repeat the environment-variable command for your platform.

Use help me debug my final setup check.

✔️ Awesome, I've got everything!

Keep your terminal session running. Keep the empty readable-agent-loop folder open in Visual Studio Code.

ⓧ I'd like to double check the full code

Your first code arrives in the next step. For now, compare your setup with this state:

  • Python 3 is installed.
  • The OpenAI Python SDK is installed.
  • The OPENAI_API_KEY environment variable is configured in the terminal session.
  • An empty project folder is open in the editor.

Your local tools plus hosted-model access are ready. Next up, you will turn the ReAct pattern into four plain-English lines before writing any agent code.

Map the ReAct Loop

Agent frameworks can hide the moment a model asks for a tool. They can also hide the point where the loop decides to stop.

The ReAct pattern gives those decisions a readable order. You will map that order before any model code enters agent.py.

In this step, get ready to:
  • Trace the ReAct cycle from a question to a final answer.
  • Create agent.py with a four-line plain-English plan.
  • Verify that the plan includes a clear stopping point.
Trace the ReAct cycle

ReAct alternates model reasoning with an action. A tool result becomes the model's next input.

  • Read the ReAct paper summary to identify the repeated cycle.
  • Follow one example in the summary from its question to its final answer.

What is the ReAct pattern?

A ReAct loop lets a model choose an action based on the information it currently has. The action produces an observation that the model can use in its next turn.

The cycle finishes when the model provides a final answer instead of requesting another tool. That stopping condition keeps the loop bounded.

Write the readable map

A short plan keeps the control flow visible while the project grows. Each line represents one transition that you will turn into Python later.

  • Use the file-creation control in your editor's sidebar to create agent.py inside the open project folder.

You'll see agent.py listed in the editor's file sidebar.

  • Add the plan to the top of agent.py by entering these four lines:
# Send the model a question.
# Inspect the requested tool call.
# Run the tool and return its result to the model.
# Stop when the model provides a final answer.

How does this plan work?

  • The first line gives the model the question it needs to solve.
  • The second line checks whether the model requested a tool.
  • The third line turns the tool result into new model input.
  • The fourth line ends the cycle once the model supplies the answer.
  • Save agent.py.
  • Count the comment lines at the top of the file.

You should see exactly four comment lines. Together they map one complete ReAct cycle.

Plan missing a stage?

  • Make sure every line begins with # so Python treats the plan as comments.
  • Check that the third line returns the tool result to the model.
  • Check that the fourth line names the final answer as the stopping point.

Help me check my four-line ReAct plan.

✔️ Awesome, I've got everything!

Great. Your saved agent.py now contains a readable map for the loop you will build.

ⓧ I'd like to double check the full code

Compare the top of your agent.py with the complete file below:

# Send the model a question.
# Inspect the requested tool call.
# Run the tool and return its result to the model.
# Stop when the model provides a final answer.
Verify the stopping point

Before you check, predict which line should prevent the agent from running forever:

  • Return to the first line of agent.py.
  • Trace the plan from its first line to its fourth line.

You'll see exactly four comment lines at the top of agent.py. The last line stops when the model provides a final answer.

Your agent now has a readable map with a clear stopping point. Next up, you will connect agent.py to a hosted model for its first real reply.

Call the Hosted Model

Your four-line ReAct plan is now a readable map. This step connects its first line to a real hosted model.

You’ll use the OpenAI Python SDK to turn a list of messages into an assistant reply. Keeping that request inside ask_model(messages) gives the later loop one clear doorway to the model.

In this step, get ready to:
  • Connect agent.py to the hosted model through your existing API key.
  • Wrap each model request inside ask_model(messages).
  • Print a hosted-model reply from a hardcoded test message.
Connect the SDK to the hosted model

An API client carries your authentication into each model request. Reading the key from an environment variable keeps the credential out of agent.py.

  • Return to agent.py in the code editor from earlier.
  • Leave the four-line plan at the top of agent.py unchanged.
  • Add the client configuration directly beneath the plan using this code:
import os
from openai import OpenAI

# Read the API key from this terminal session.
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])

# Keep the model choice easy to swap later.
MODEL_NAME = "gpt-4o-mini"

What Does This Code Do?

  • The os module lets your script read values from the terminal environment.
  • OpenAI(...) creates the client that sends authenticated requests.
  • MODEL_NAME keeps the model choice in one place for the model swap later.
  • Save agent.py.
  • Confirm the client configuration loads by running this command in the terminal from earlier:
python -c "import agent; print(agent.MODEL_NAME)"

What Does This Check?

This command imports agent.py without sending a model request. It prints the configured model name after Python loads the SDK client.

You should see gpt-4o-mini in the terminal.

Client Configuration Not Loading?

Check that the terminal still has OPENAI_API_KEY set. The client cannot authenticate when that value is missing.

Confirm that OpenAI uses the same capitalization as the import. Python names are case-sensitive.

Help me debug why the OpenAI client in agent.py does not load.

Define the model call

The rest of your agent should not need to know how the hosted request works. The ask_model(messages) function creates that boundary.

  • Place this function below MODEL_NAME in agent.py:
def ask_model(messages):
    # Send the current conversation to the hosted model.
    response = client.chat.completions.create(
        model=MODEL_NAME,
        messages=messages,
    )

    # Return only the assistant text for now.
    return response.choices[0].message.content

How Does the Function Work?

  • messages carries the conversation that the model needs to continue.
  • client.chat.completions.create() sends that conversation to the model selected by MODEL_NAME.
  • response holds the structured result returned by the hosted service.
  • response.choices[0].message.content extracts the assistant text that the loop can use.
  • Save agent.py.
  • Confirm Python can find the new function by running this command:
python -c "import agent; print(callable(agent.ask_model))"

What Does This Check?

The command imports your updated file. It checks whether ask_model is available as a callable function.

You should see True in the terminal.

Function Check Failing?

Make sure def ask_model(messages): begins at the left edge of the file. Keep every line inside the function indented by four spaces.

Check that you saved agent.py before running the command again.

Help me fix the ask_model function in agent.py.

Send a test message

A model conversation is an ordered list of messages. Each message identifies its speaker through role and carries text through content.

  • Add this hardcoded test below ask_model(messages) in agent.py:
# Start with one user message so the model call is easy to verify.
messages = [
    {"role": "user", "content": "In one sentence, what does an AI agent do?"},
]

# Print the reply so this first model call is visible.
print(ask_model(messages))

What Does This Test Do?

  • messages starts the conversation with one question from the user.
  • ask_model(messages) sends that question through the function you just created.
  • print() makes the returned assistant text visible in your terminal.
  • Save agent.py.

Before you run it, do you expect the terminal to print Python data or a natural-language reply?

  • Run the model test in the terminal from earlier using this command:
python agent.py

What Does This Run Prove?

Python executes the complete agent.py file. A printed reply proves that the client authenticated successfully.

The same result also proves that ask_model(messages) can send a conversation to the hosted model.

You should see a natural-language sentence explaining what an AI agent does. The exact wording can vary between runs.

That first reply is a real milestone. Your Python function now completes a round trip to the hosted model.

No Model Reply?

If the request cannot authenticate, confirm that you are using the same terminal session where OPENAI_API_KEY was set.

If Python reports a problem inside ask_model, compare the indentation around client.chat.completions.create() with the code above.

Help me troubleshoot why agent.py does not print a hosted-model reply.

✔️ Awesome, I've got everything!

Your model call works. Double check that agent.py is saved before moving on.

ⓧ I'd like to double check the full code

Compare your complete agent.py with this version:

# Send the model a question.
# Inspect any requested tool call.
# Run the tool and return its result to the model.
# Stop when the model provides a final answer.

import os
from openai import OpenAI

# Read the API key from this terminal session.
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])

# Keep the model choice easy to swap later.
MODEL_NAME = "gpt-4o-mini"

def ask_model(messages):
    # Send the current conversation to the hosted model.
    response = client.chat.completions.create(
        model=MODEL_NAME,
        messages=messages,
    )

    # Return only the assistant text for now.
    return response.choices[0].message.content

# Start with one user message so the model call is easy to verify.
messages = [
    {"role": "user", "content": "In one sentence, what does an AI agent do?"},
]

# Print the reply so this first model call is visible.
print(ask_model(messages))

Your agent can now ask the model a question. Next up, you’ll give it one tool to call for work the model should not do alone.

Add a Calculator Tool

Your model module can already turn a message into a reply. The next gap is action.

A tool gives your ReAct loop a concrete result to use between model turns. In this step, you will add a calculator that handles the restricted arithmetic expressions used by this project.

In this step, get ready to:
  • Add a restricted calculator to agent.py.
  • Call calculate(expression) directly with a test expression.
  • Confirm the calculator prints an arithmetic result.
Define the calculator tool

The calculator receives an arithmetic expression as text. A character check keeps the input within the numbers plus operators this project uses.

  • Switch back to agent.py in your code editor.
  • Find the hardcoded model test at the bottom of the file.
  • Replace that test with the calculator plus its direct check by pasting this code:
def calculate(expression):
    # Keep tool inputs limited to the arithmetic this project needs
    allowed_characters = set("0123456789+-*/(). ")
    if not set(expression).issubset(allowed_characters):
        return "Error: only basic arithmetic is supported."

    # Return text so the result can become a later model message
    try:
        return str(eval(expression, {"__builtins__": {}}, {}))
    except (SyntaxError, ZeroDivisionError):
        return "Error: invalid arithmetic expression."

# Test the tool without involving the hosted model
print(calculate("12 * (3 + 2)"))

What does this code do?

  • The allowed_characters set defines the input characters that the calculator accepts.
  • The issubset() check rejects expressions containing names or unsupported characters.
  • The restricted eval() call evaluates the expression without access to Python built-ins.
  • The error handler returns readable text for invalid arithmetic or division by zero.
  • The str() conversion prepares the result for a later model message.
  • The final print() call tests the calculator without sending another hosted-model request.
  • Save agent.py.
Test the tool directly

A direct test isolates the calculator from the hosted model. This proves the tool works before the agent loop depends on it.

Before you run the file, pause to predict the number the expression produces.

  • Return to the terminal from the previous step.
  • Press the Up Arrow key to recall the Python command from the previous step.
  • Press Enter to run agent.py again.

You will see 60 in the terminal. That output proves the calculator works independently of the model.

That is a useful milestone. Your agent now has a real action it can perform between model turns.

Calculator Result Missing?

  • Remove the earlier hardcoded model test if a model reply appears before the calculator result.
  • Check the indentation inside calculate(expression) if Python points to the function body.
  • Compare the test expression with the allowed character set if the calculator returns an input warning.

Help me debug my calculator tool.

✔️ Awesome, I've got everything!

Great. Double check that agent.py is saved before moving on.

ⓧ I'd like to double check the full code

Compare your agent.py with this complete version.

# 1. Send the user's question to the model.
# 2. Read the model's response.
# 3. Run a tool when the model asks for one.
# 4. Repeat until the model gives a final answer.

import os
from openai import OpenAI

# Create a client using the key from the terminal environment
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
MODEL_NAME = "gpt-4o-mini"

def ask_model(messages):
    # Send the current conversation to the hosted model
    response = client.chat.completions.create(
        model=MODEL_NAME,
        messages=messages,
    )
    return response.choices[0].message

def calculate(expression):
    # Keep tool inputs limited to the arithmetic this project needs
    allowed_characters = set("0123456789+-*/(). ")
    if not set(expression).issubset(allowed_characters):
        return "Error: only basic arithmetic is supported."

    # Return text so the result can become a later model message
    try:
        return str(eval(expression, {"__builtins__": {}}, {}))
    except (SyntaxError, ZeroDivisionError):
        return "Error: invalid arithmetic expression."

# Test the tool without involving the hosted model
print(calculate("12 * (3 + 2)"))

You now have both halves: a model that answers plus a calculator that acts. Next, you will connect them with the loop that passes tool requests between turns.

Run the Agent Loop

Your calculator now solves a restricted arithmetic expression on its own. Your model already responds through ask_model(messages).

Those pieces still act independently. The ReAct loop passes a tool request to the calculator before it returns the result to the model.

In this step, get ready to:
  • Create the conversation state for run_agent(question).
  • Route each tool_request through calculate(expression).
  • Run one calculator question until final_answer appears.
Build the message loop

The messages list stores the complete conversation. Each turn adds either a model output or a calculator result.

  • In agent.py, place your cursor beneath the existing calculator section.
  • Add run_agent(question) by pasting the code below:
def run_agent(question):
    # Give the model the tool protocol and the learner's question
    messages = [
        {"role": "system", "content": (
            "Use the calculator for every arithmetic question. "
            "Return a tool_request with an expression first. "
            "After a tool_result, return a final_answer."
        )},
        {"role": "user", "content": question},
    ]

    # Continue until the model provides its final answer
    while True:
        model_output = ask_model(messages)
        print("Model:", model_output)
        messages.append({"role": "assistant", "content": str(model_output)})

        # Return immediately when the model has finished
        if "final_answer" in model_output:
            return model_output["final_answer"]

        # Run the requested calculation and return its result
        tool_request = model_output["tool_request"]
        expression = tool_request["expression"]
        tool_result = calculate(expression)
        print("Tool:", tool_result)
        messages.append({
            "role": "user",
            "content": str({"tool_result": tool_result}),
        })

What Does This Loop Do?

  • The messages list begins with the tool rules. It also stores the learner's question.
  • The while True loop gives the model another turn after every calculator result.
  • The tool_request provides the arithmetic expression. calculate(expression) produces the result.
  • The final_answer branch ends the loop. Its value returns to the code that asked the question.
  • Save agent.py.
  • Return to the terminal from the previous step.
  • Rerun the same command you used to test the calculator.

You should still see the direct calculator result from the previous step. This confirms Python loaded run_agent(question) without a syntax problem.

Script Stops Before the Calculator Result?

Check that every line inside run_agent(question) is indented beneath the function definition. Make sure the three lines inside the final messages.append() call stay inside its braces.

Help me fix my agent loop code.

Send a question through the loop

A calculator question gives the model a reason to produce tool_request. The returned tool_result gives it enough information to produce final_answer.

  • In agent.py, place your cursor below run_agent(question).
  • Send one calculator question through the loop by pasting the code below:
# Send one calculator question through the complete loop
question = "What is (24 * 17) + 6?"
final_answer = run_agent(question)
print("Final answer:", final_answer)

What Does This Test Prove?

  • The question variable gives the loop an arithmetic task.
  • The run_agent(question) call keeps control until the model returns final_answer.
  • The final print() call makes the completed answer visible in your terminal.
  • Save agent.py.
  • Return to the terminal from earlier.

Before you run it, do you expect the model to call the calculator before answering? Hold that prediction for the next check.

  • Rerun the same command you used to test agent.py.

After the earlier direct calculator result, you should see a model turn containing tool_request. You should then see a tool turn showing 414.

The next model turn should contain final_answer. The final line should also show 414.

That is the core agent behavior working. Your model can now delegate arithmetic to the calculator.

Loop Not Reaching the Final Answer?

If the model answers immediately, compare the system message with the code above. It must tell the model to return tool_request first.

If the loop keeps printing model turns, confirm the model output uses the exact final_answer key. That key activates the return branch.

Help me debug the agent loop.

✔️ Awesome, I've got everything!

Great. Make sure agent.py is saved with the loop test at the bottom.

ⓧ I'd like to double check the full code

Compare the loop section you added to agent.py with this reference:

def run_agent(question):
    # Give the model the tool protocol and the learner's question
    messages = [
        {"role": "system", "content": (
            "Use the calculator for every arithmetic question. "
            "Return a tool_request with an expression first. "
            "After a tool_result, return a final_answer."
        )},
        {"role": "user", "content": question},
    ]

    # Continue until the model provides its final answer
    while True:
        model_output = ask_model(messages)
        print("Model:", model_output)
        messages.append({"role": "assistant", "content": str(model_output)})

        # Return immediately when the model has finished
        if "final_answer" in model_output:
            return model_output["final_answer"]

        # Run the requested calculation and return its result
        tool_request = model_output["tool_request"]
        expression = tool_request["expression"]
        tool_result = calculate(expression)
        print("Tool:", tool_result)
        messages.append({
            "role": "user",
            "content": str({"tool_result": tool_result}),
        })

# Send one calculator question through the complete loop
question = "What is (24 * 17) + 6?"
final_answer = run_agent(question)
print("Final answer:", final_answer)

Your agent can now decide when to calculate. Next, you'll record each turn so extra work becomes visible.

Trace and Compare Runs

Your agent loop can now answer a calculator question. The hosted model requests calculator work before returning the final answer.

A successful answer gives you the result. A trace file preserves each turn so you can explain why one run took longer.

In this step, get ready to:
  • Create a traces folder beside agent.py.
  • Log every model event plus every tool event in timestamped files.
  • Run three calculator questions before comparing their event sequences.
Prepare the trace destination

Each run needs its own file before the loop starts recording events. Keeping those files beside agent.py gives you one place to inspect the agent's history.

What belongs in a trace?

  • The model input captures the conversation sent into ask_model(messages).
  • The model output captures the response returned from ask_model(messages).
  • A tool_request event records the requested calculator expression.
  • A tool_result event records the value returned by calculate(expression).
  • A final_answer event records the answer returned by run_agent(question).
  • Use the file-tree folder control in your editor to create a folder named traces beside agent.py.

You should see agent.py plus the empty traces folder at the same level in the file tree.

Record every loop event

The event order turns the loop into a timeline. Each model call shows what the agent knew at that moment.

How the trace fits the loop

  • TRACE_PATH holds one timestamped path inside traces for the current run.
  • log_trace(event) appends one event to the file at TRACE_PATH.
  • The model input is logged immediately before ask_model(messages).
  • The model output is logged immediately after ask_model(messages).
  • tool_request is logged before calculate(expression) runs.
  • tool_result is logged after the calculator returns its value.
  • final_answer is logged before run_agent(question) returns.
  • Implement the trace sequence above in agent.py with What is 18 * 7? as the current question.
  • Save agent.py.
  • Switch back to the terminal from the previous step.

Before you run the agent, which event do you expect to appear immediately before tool_result?

  • Run agent.py with the same command you used in the previous step.

You should see the familiar model turns plus calculator turn in the terminal. You should also see one timestamped file inside traces.

Good work. Your agent now leaves a readable record of the path it took to reach 126.

Trace file missing events?

Confirm that TRACE_PATH points inside the existing traces folder. Check that log_trace(event) appends to the file instead of replacing earlier events.

Check the logging calls around ask_model(messages) if the model input or output is missing. Check each response branch if a tool event or final answer is missing.

Help me debug the missing events in my agent trace.

Compare three traced runs

One trace shows a single journey through the loop. Three traces reveal where the model chose a longer route.

  • Replace the current question in agent.py with What is (45 + 15) / 3?.
  • Save agent.py.
  • Switch back to the terminal from earlier.
  • Rerun agent.py with the same command.

You should see the final answer 20 in the terminal. A second timestamped file should appear inside traces.

  • Replace the current question with Use the calculator in two separate tool calls. First calculate 81 / 9. Then multiply that tool result by 7..
  • Save agent.py.
  • Switch back to the terminal from earlier.

Before you run this question, do you expect its trace to contain the same number of model turns as the first two traces?

  • Rerun agent.py with the same command.

You should see the final answer 63 in the terminal. The traces folder should now contain three timestamped files.

Before you compare the files, where do you expect the extra model turn to appear?

  • Return to the editor file tree from earlier.
  • Open the first timestamped trace file in one editor group.
  • Open the second timestamped trace file in a separate editor group.
  • Open the third timestamped trace file in another editor group.
  • Arrange the three trace files side by side.
  • Compare the ordered events across the three files.
  • Identify the file containing the extra model turn.

You should find that the two-call question contains an extra model input plus model output pair. Its trace also contains a second tool_request plus tool_result pair before final_answer.

You now have evidence for every decision your loop makes. Those traces give you a concrete way to debug longer runs.

Secret mission

Compare Model Behavior Through Traces

Swap the model in one configuration line. Rerun the same three calculator questions to compare model behavior while your agent loop stays fixed.

Clean Up Your Resources

Clean Up Your Resources

Choose whether to keep the project files, pause your workspace, or remove the artifacts entirely. The local files have no ongoing cost.

Resources you used:

  • Agent script agent.py containing the ReAct plan, model module, calculator tool, trace logger, and agent loop.
  • Trace directory traces/ containing six timestamped trace files from the two model configurations.

Keep everything running

No action is needed. Choose this if you are still comparing model behavior or changing the agent loop.

  • Keep agent.py in the project folder so you can rerun or modify the loop.
  • Keep traces/ beside agent.py so the six runs remain available for comparison.
  • Keep the terminal session from earlier open if you want to reuse its OPENAI_API_KEY value.

Pause - I'll come back to this later

Close your local workspace to free its editor and terminal memory while preserving the code and traces. The completed agent has no background process to stop.

  • Close the terminal session from earlier when you finish reviewing the output.
  • Close the code editor after you finish comparing the trace files.
  • Leave agent.py in the project folder.
  • Leave traces/ beside agent.py.
  • Set OPENAI_API_KEY again in your next terminal session before you rerun the agent.

Delete - I don't want to use this again

Deleting the local artifacts is permanent. Your hosted-model account and API key remain untouched.

macOS

  • Return to the terminal from earlier.
  • Delete agent.py and traces/ by running these commands:
# Remove the agent script from the current project folder
rm -f agent.py

# Remove every saved trace and the traces folder
rm -rf traces
What does this cleanup remove?

The rm -f command removes agent.py from the current project folder.

The rm -rf command removes traces/ with all six trace files inside it.

  • Switch back to the project folder in your code editor.

You should no longer see agent.py or traces/. That clears the readable agent loop and all six trace files from your machine.

Still see the project artifacts?
  • Confirm the terminal is inside the same project folder that is open in your code editor.
  • Check whether another program is using agent.py or a file inside traces/.

Help me remove these project artifacts safely.

  • Close the terminal session from earlier to clear its session-scoped OPENAI_API_KEY value.

Windows

  • Return to the terminal from earlier.
  • Delete agent.py and traces/ by running these commands:
# Remove the agent script from the current project folder
Remove-Item agent.py -Force

# Remove every saved trace and the traces folder
Remove-Item traces -Recurse -Force
What does this cleanup remove?

The first Remove-Item command removes agent.py from the current project folder.

The second Remove-Item command removes traces/ with all six trace files inside it.

  • Switch back to the project folder in your code editor.

You should no longer see agent.py or traces/. That clears the readable agent loop and all six trace files from your machine.

Still see the project artifacts?
  • Confirm the terminal is inside the same project folder that is open in your code editor.
  • Check whether another program is using agent.py or a file inside traces/.

Help me remove these project artifacts safely.

  • Close the terminal session from earlier to clear its session-scoped OPENAI_API_KEY value.

Nice Work!

Nice Work!

You did it. Your Python agent now completes calculator questions through a readable ReAct loop.

You've learned how to:

  • Built a readable agent loop that sends each question to a hosted model. The loop stops when the model produces its final answer.
  • Connected a calculator tool to run_agent(question). Each tool result returns to the model for the next turn.
  • Recorded timestamped traces for the same three questions under two model configurations. You can compare the files side by side to spot extra turns.
  • Secret Mission: completed an optional challenge to push your agent-loop skills further.

Ready to quiz yourself?