0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
27 small wins to finish your pathNext lesson →

An agent as a tool with as_tool

as_tool is an Agent method that turns an agent into a tool the calling agent can invoke: specialist.as_tool(tool_name=..., tool_description=...) runs the specialist as one step and returns control to the caller.

Last updated: 28 Sep, 2026 · openai-agents 0.22.3

A handoff gives the whole run away. Sometimes you want a specialist to do one piece, then keep going as the main agent. Wrapping the specialist as a tool does that: the caller stays in charge.

The as_tool method

Call as_tool on the specialist to get a FunctionTool. Add it to another agent's tools. When the caller invokes it, the specialist runs, its answer comes back as the tool result, and the caller carries on.

python
translator = Agent(name="Translator", instructions="Translate to Spanish.")
tool = translator.as_tool(tool_name="translate_to_spanish",
                          tool_description="Translate the given text.")
# tool is a FunctionTool; after it runs, control returns to the caller

The specialist agent

The specialist is a normal Agent with its own model. Here FixedModel is a stand-in that always answers Hola, mundo.

python
translator = Agent(name="Translator", instructions="Translate to Spanish.",
                   model=FixedModel())   # always returns "Hola, mundo"

Wrapping it as a tool

as_tool needs a name and a description, the way any tool does. The name is what the calling model uses to invoke it.

python
translate_tool = translator.as_tool(
    tool_name="translate_to_spanish",
    tool_description="Translate the given text to Spanish.",
)
print(translate_tool.name)   # translate_to_spanish

The parent that calls the tool

Give the tool to a parent agent. The parent's stand-in ParentModel calls the tool, then writes the final answer itself, which is how you see that control came back to the parent.

python
parent = Agent(name="Assistant", instructions="Use your tools to help.",
               tools=[translate_tool], model=ParentModel())
# ParentModel calls the tool, then answers, so the parent finishes the run

Calling a translator agent as a tool

The whole program. The parent calls the translator tool, gets Hola, mundo, and writes the final line itself.

Example
from agents import Agent, Runner, set_tracing_disabled
from agents.models.interface import Model
from agents.items import ModelResponse
from agents.usage import Usage
from openai.types.responses import (
    ResponseOutputMessage, ResponseOutputText, ResponseFunctionToolCall,
)
set_tracing_disabled(True)


def _message(text):
    return ResponseOutputMessage(
        id="msg", role="assistant", type="message", status="completed",
        content=[ResponseOutputText(text=text, type="output_text", annotations=[])],
    )


def _tool_call(name, arguments):
    return ResponseFunctionToolCall(
        id="fc", call_id="call_1", name=name, arguments=arguments, type="function_call",
    )


class FixedModel(Model):
    async def get_response(self, *a, **k):
        return ModelResponse(output=[_message("Hola, mundo")],
                             usage=Usage(), response_id=None)

    async def stream_response(self, *a, **k):
        raise NotImplementedError


class ParentModel(Model):
    async def get_response(self, system_instructions, input, model_settings, tools,
                           output_schema, handoffs, tracing, **k):
        if isinstance(input, list):
            for it in reversed(input):
                d = it if isinstance(it, dict) else it.__dict__
                if d.get("type") == "function_call_output":
                    return ModelResponse(
                        output=[_message("Here is your translation: " + d.get("output", ""))],
                        usage=Usage(), response_id=None)
        return ModelResponse(output=[_tool_call(tools[0].name, '{"input": "hello, world"}')],
                             usage=Usage(), response_id=None)

    async def stream_response(self, *a, **k):
        raise NotImplementedError


translator = Agent(name="Translator", instructions="Translate to Spanish.",
                   model=FixedModel())
translate_tool = translator.as_tool(
    tool_name="translate_to_spanish",
    tool_description="Translate the given text to Spanish.",
)

parent = Agent(name="Assistant", instructions="Use your tools to help.",
               tools=[translate_tool], model=ParentModel())

result = Runner.run_sync(parent, "please translate hello, world")
print("Tool name:", translate_tool.name)
print("Last agent:", result.last_agent.name)
print("Final output:", result.final_output)

What the run shows about control

  • The tool name is translate_to_spanish: as_tool made a tool the parent can call by that name.
  • last_agent is Assistant: the parent, not the translator, so control returned after the tool ran.
  • final_output came from the parent: it wrapped the translator's Hola, mundo in its own sentence.

as_tool vs a handoff

Handoffagent.as_tool
The specialist ishanded the runcalled as a step
After it runsit keeps the runcontrol returns to the caller
last_agentthe specialistthe calling agent
The final answerfrom the specialistfrom the caller

When an agent belongs as a tool

  • A sub-skill in the middle of a task: translate, then continue answering.
  • Fan-out: one agent calls several specialist tools and combines the results.
Watch out. A tool created by as_tool takes a single input string. The calling model must send {"input": "..."}, not the specialist's own field names.
Try it yourself
  • Change tool_name to "translate" and read the new first line.
  • Make FixedModel return "Bonjour" and see it flow into the parent's sentence.
  • Add a second specialist tool and have ParentModel call tools[1].name instead.

Little by little, you're building something great.