How the agent runs a tool then answers
The agent loop is the cycle the SDK runs: the model asks for a tool, the SDK runs that tool and feeds the result back, and the model turns it into a final answer.
Last updated: 28 Sep, 2026 · openai-agents 0.22.3
Lesson 5 built a tool and ran it by hand. Here the model decides to call it. You give the tool to the agent, ask about an order, and the SDK does the round trip: tool call, tool result, final message. Reading new_items from lesson 4 shows all three steps.
Giving the agent a tool
The loop needs three things: a tool, an agent that holds it, and a message that makes the model want it.
agent = Agent(name="Shop", instructions="Help with orders.",
tools=[lookup_order], model=ShopModel())
result = Runner.run_sync(agent, "where is my order?") # triggers the loopDefining the order tool
The tool returns a fixed order status. When the SDK runs it, this string is what goes back to the model.
@function_tool
def lookup_order(order_id: str) -> str:
"""Return the status of an order by its id."""
return "Order A17: shipped on 3 March."Handing the tool to the agent
Put the tool in the agent's tools list. Now the model may request it during a run.
agent = Agent(name="Shop", instructions="Help with orders.",
tools=[lookup_order], model=ShopModel()) # tool attachedAsking about an order
The message mentions an order, so ShopModel returns a call to lookup_order. The SDK runs it, sends the result back, and the model answers with it.
result = Runner.run_sync(agent, "where is my order?")
print(result.final_output) # the status the tool returnedOne tool call, then the answer
This imports ShopModel from lesson 3. The model asks for the tool, the SDK runs it, and the final answer is the order status. The three items show the whole loop.
from agents import Agent, Runner, function_tool, set_tracing_disabled
from shop_model import ShopModel
set_tracing_disabled(True)
@function_tool
def lookup_order(order_id: str) -> str:
"""Return the status of an order by its id."""
return "Order A17: shipped on 3 March."
agent = Agent(name="Shop", instructions="Help with orders.",
tools=[lookup_order], model=ShopModel())
result = Runner.run_sync(agent, "where is my order?")
print("answer:", result.final_output)
for item in result.new_items:
print("-", type(item).__name__)What the three new_items are
- ToolCallItem is the model's request to run
lookup_orderwith the order id. - ToolCallOutputItem is what the tool returned, fed back to the model.
- MessageOutputItem is the final answer the model wrote from that output.
A tool run vs a plain reply
| Plain reply (lesson 4) | Tool run (this lesson) | |
|---|---|---|
| Model output | A message | A tool call, then a message |
| new_items count | 1 | 3 |
| Loop passes | One | Two: call, then answer |
Where the loop repeats
- Answering a question that needs a lookup before the model can reply.
- Chaining several tool calls, each result feeding the next request.
- Any run where the model gathers facts with tools before it answers.
max_turns, covered in its own lesson later.Related
- Previous: function_tool: a function the model can call
- Next: Structured output with output_type
- Reference: Running agents
- Send
"hello"instead; the tool branch is skipped and only one item appears. - Add a print inside
lookup_orderand watch it fire mid-run. - Change the tool's return string and rerun; the final answer changes with it.
Little by little, you're building something great.