Tool Search: Dynamic Tools
On this page
Tool Search: Dynamic Tools
A demo agent gets by just fine with three or four tools. An enterprise agent, however, inevitably accumulates dozens, sometimes hundreds: billing, HR, CRM, documents, scheduling. At that scale, sending the entire catalog to the model with every request becomes inefficient and expensive in terms of tokens. Tool Search solves this problem by dynamically selecting the relevant tools for each request.
The Problem with Too Many Tools
Imagine an agent with 50 tools. On every API call, the schemas for all 50 tools are sent in the prompt. This consumes thousands of tokens and can confuse the model: the longer the list, the more similar the descriptions become, and the more the model hesitates between neighboring tools. Costs rise while precision drops—the worst possible combination.
# Problem: too many tools
agent = Agent(
name="Super Agent",
instructions="You can do anything.",
tools=[tool_1, tool_2, tool_3, ... , tool_50], # 50 tools = lots of tokens
)
Enabling Tool Search
Tool Search uses a semantic index to select relevant tools based on the request. You declare your tools as usual, but the parameter tool_use_behavior="auto" lets the model pick from the catalog instead of loading everything upfront. In the example below, a request to send a payment reminder won’t trigger HR tools, and vice versa.
from agents import Agent, Runner, function_tool
# Define many tools
@function_tool
def create_invoice(client: str, amount: float) -> str:
"""Creates an invoice for a client."""
return f"Invoice created for {client}: €{amount}"
@function_tool
def send_reminder(invoice_id: str) -> str:
"""Sends a payment reminder for an unpaid invoice."""
return f"Reminder sent for invoice {invoice_id}"
@function_tool
def generate_hr_report(month: str) -> str:
"""Generates a human resources report for a given month."""
return f"HR report generated for {month}"
@function_tool
def schedule_interview(candidate: str, date: str) -> str:
"""Schedules a job interview with a candidate."""
return f"Interview scheduled with {candidate} on {date}"
# Many other tools...
agent = Agent(
name="Enterprise Agent",
instructions="You manage company operations.",
tools=[
create_invoice,
send_reminder,
generate_hr_report,
schedule_interview,
# ... 50 other tools
],
tool_use_behavior="auto", # The model selects dynamically
)
Pattern: Manual Tool Selection
If you want total control over tool selection, implement your own logic. The principle is simple: a small router agent reads the request and simply names the relevant domain. You then build the specialized agent on the fly with only the tools for that domain. A phrase like “send a reminder for invoice F-2026-114” comes out of the router with the response accounting, and the execution agent never sees the HR tools.
from agents import Agent, Runner, function_tool
# Group tools by domain
accounting_tools = [create_invoice, send_reminder]
hr_tools = [generate_hr_report, schedule_interview]
# A router agent that selects the domain
router = Agent(
name="Router",
instructions="""Determine the domain of the request.
Respond only with 'accounting' or 'hr'.""",
model="gpt-5.6-terra", # Fast and cheap for routing
output_type=str,
)
async def handle_request(message: str):
# Step 1: Determine the domain
route = await Runner.run(router, message)
domain = route.final_output.strip().lower()
# Step 2: Create the specialized agent with the right tools
if domain == "accounting":
tools = accounting_tools
instructions = "You manage the company's accounting."
elif domain == "hr":
tools = hr_tools
instructions = "You manage human resources."
else:
tools = accounting_tools + hr_tools
instructions = "You manage company operations."
specialized_agent = Agent(
name=f"{domain.capitalize()} Agent",
instructions=instructions,
tools=tools,
model="gpt-5.6-terra",
)
result = await Runner.run(specialized_agent, message)
return result.final_output
Using gpt-5.6-terra for both routing and execution belongs to the previous generation of models; the reasoning behind the pattern remains identical with the GPT-5.6 family, where effort settings distinguish the fast router from the executor.
Pattern: Context-Aware Dynamic Tools
Selection doesn’t depend only on the question asked; it also depends on who is asking. You can adapt available tools based on the logged-in user, killing two birds with one stone: fewer tokens sent, and, more importantly, access controls enforced before the model even reasons. A sales employee gets quoting tools, but delete_account simply doesn’t exist in their agent.
from dataclasses import dataclass
@dataclass
class UserContext:
role: str # "admin", "manager", "employee"
department: str
def get_tools_for_role(ctx: UserContext) -> list:
"""Returns authorized tools based on role."""
base_tools = [search_document, view_schedule]
if ctx.role in ("admin", "manager"):
base_tools.extend([create_invoice, generate_hr_report])
if ctx.role == "admin":
base_tools.extend([modify_permissions, delete_account])
if ctx.department == "sales":
base_tools.extend([search_client, create_quote])
return base_tools
async def create_agent_for_user(ctx: UserContext):
tools = get_tools_for_role(ctx)
return Agent(
name="Custom Agent",
instructions=f"You assist a {ctx.role} in the {ctx.department} department.",
tools=tools,
model="gpt-5.6-terra",
)
Optimizing Tool Descriptions
Whatever selection mechanism you choose, it relies on a single material: the text of your descriptions. The quality of these descriptions directly impacts selection relevance. Compare the two declarations below: the first says nothing about the usage scenario, while the second names the inputs, the output, and when to use it.
# Bad: vague description
@function_tool
def process_data(data: str) -> str:
"""Processes data."""
return "OK"
# Good: precise and contextualized description
@function_tool
def calculate_gross_margin(revenue: float, production_cost: float) -> str:
"""Calculates gross margin as a percentage from revenue
and production cost. Useful for quarterly financial analysis."""
margin = ((revenue - production_cost) / revenue) * 100
return f"Gross margin: {margin:.1f}% (Revenue: €{revenue}, Costs: €{production_cost})"
A good tool description is like a good docstring: it explains when to use the tool, not just what it does. Take ten minutes to review your agent’s descriptions, asking yourself for each one whether a colleague could choose the right tool by reading only that sentence. That is exactly the exercise the model performs.
Key Takeaways
- Tool Search dynamically selects relevant tools for each request
- With many tools, a router agent can determine the domain upfront
- Adapt available tools based on the user’s role and context
- Tool description quality impacts selection accuracy
- Use a lightweight model for fast routing and a capable model for execution