How to Control Agent Tool Usage with tool_choice and tool_call_limit in Agno
Agno agents use tool_choice to control whether the LLM can invoke tools (or force a specific tool) and tool_call_limit to restrict the total number of tool calls allowed during a single execution.
When building autonomous agents with the Agno framework (agno-agi/agno), deterministic control over tool execution prevents runaway loops and ensures compliance with business constraints. The tool_choice and tool_call_limit parameters provide granular governance over when and how often functions execute, as implemented in the core agent and model classes.
Understanding tool_choice
The tool_choice parameter determines the tool-calling behavior permitted for the underlying LLM. It accepts either a string directive or an explicit function dictionary that forces a specific tool invocation.
Configuration Options
"none" – Prohibits the model from calling any tools. The agent must generate a plain text response regardless of available tools. This is the default when no tools are attached to the agent.
"auto" – Allows the model to decide between generating a natural language response or invoking available tools. This is the default when tools are present, enabling flexible autonomous behavior.
Explicit function dictionary – Forces the model to call a specific tool by passing a JSON-compatible dict matching the OpenAI/Anthropic function schema:
{
"type": "function",
"function": {"name": "search_web"}
}
When configured with an explicit dict, the model must call the identified tool; otherwise, the request returns an error.
Source Implementation
Both Agent and Team classes expose the tool_choice attribute. In libs/agno/agno/agent/agent.py, the field is defined as:
class Agent:
...
tool_choice: Optional[Union[str, Dict[str, Any]]] = None
...
The value propagates to the model request during execution. In libs/agno/agno/team/_run.py, the framework forwards the configuration:
model_response = team.model.response(
messages=accumulated_messages,
tools=_tools,
tool_choice=team.tool_choice, # Propagated here
tool_call_limit=team.tool_call_limit,
...
)
Managing Execution with tool_call_limit
The tool_call_limit parameter caps the total number of tool calls an agent may execute in a single run. This prevents infinite loops and controls API costs when working with expensive tool integrations.
Enforcement Mechanism
The limit is enforced in libs/agno/agno/models/base.py within the run_function_calls method. When the execution count exceeds the configured limit, the framework injects a synthetic error result:
if current_function_call_count > function_call_limit:
function_call_results.append(self.create_tool_call_limit_error_result(fc))
continue
Limit Reached Behavior
When the cap is exceeded, the agent receives a system message indicating termination:
Tool call limit reached. Tool call <name> not executed. Don't try to execute it again.
This ensures graceful degradation rather than silent failure or infinite recursion. The default value is None, allowing unlimited tool calls unless explicitly restricted.
Practical Implementation Examples
1. Auto Tool Selection with Execution Cap
Configure an agent to automatically select tools while restricting total invocations to two:
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.tools.yfinance import YFinanceTools
agent = Agent(
model=OpenAIChat(id="gpt-4o-mini"),
tools=[YFinanceTools(cache_results=True)],
tool_choice="auto", # Let the model decide
tool_call_limit=2, # Maximum two tool invocations
)
response = agent.run("What is the current price of TSLA and the market cap of AAPL?")
print(response.content)
print(f"Tools used: {len(response.tools)}") # Output: ≤ 2
2. Forcing a Specific Tool
Override autonomous selection to mandate execution of a particular function:
forced_choice = {
"type": "function",
"function": {"name": "get_current_stock_price"}
}
agent = Agent(
model=OpenAIChat(id="gpt-4o-mini"),
tools=[YFinanceTools()],
tool_choice=forced_choice, # Model must call get_current_stock_price
)
response = agent.run("Give me the price of TSLA.")
assert response.tools[0].tool_name == "get_current_stock_price"
3. Team-Level Configuration
Apply constraints to collaborative multi-agent teams:
from agno.team import Team
from agno.models.anthropic import AnthropicChat
from agno.tools.websearch import WebSearchTools
team = Team(
model=AnthropicChat(id="claude-3-5-sonnet-20240620"),
tools=[WebSearchTools()],
tool_choice="auto",
tool_call_limit=1, # Only a single search allowed per run
)
result = team.run("Summarize the latest news about electric vehicles.")
print(f"Tools used: {len(result.tools)}") # Output: 0 or 1
4. Advanced Per-Run Overrides
For dynamic workflows, temporarily override the static configuration by interacting directly with the model interface:
# Low-level override during custom workflow execution
model_response = team.model.response(
messages=msgs,
tools=team.tools,
tool_choice={"type": "function", "function": {"name": "my_special_tool"}},
tool_call_limit=team.tool_call_limit,
)
Direct calls to model.response are internal; standard use cases should configure tool_choice on the Agent or Team instances.
Summary
tool_choiceaccepts"none"(prohibit tools),"auto"(model decides), or an explicit function dictionary (force specific tool) to govern invocation permissions.tool_call_limitenforces a hard cap on tool executions per run, preventing infinite loops and controlling costs via the logic inlibs/agno/agno/models/base.py.- Both parameters are defined in
libs/agno/agno/agent/agent.pyandlibs/agno/agno/team/team.py, then forwarded to model requests inlibs/agno/agno/team/_run.py. - When limits are exceeded, Agno injects a synthetic error message to terminate the run gracefully rather than failing silently.
Frequently Asked Questions
What is the default behavior for tool_choice in Agno?
When no tools are attached to an agent, tool_choice defaults to "none", preventing any tool calls. If tools are present, the default switches to "auto", allowing the model to decide whether to invoke tools or respond with natural language.
How does tool_call_limit prevent infinite tool loops?
The framework tracks the current function call count during execution in run_function_calls. Once the count exceeds tool_call_limit, Agno appends a synthetic error result to the conversation history and stops further tool execution, forcing the agent to conclude its response.
Can I force multiple specific tools using tool_choice?
No, the explicit dictionary format for tool_choice supports forcing a single specific tool per run. To execute multiple specific tools sequentially, you must either allow "auto" mode or chain multiple agent runs with specific forced choices for each step.
Does tool_call_limit apply per agent or per team run?
The limit applies to the specific Agent or Team instance during a single run() execution. For Team objects, the aggregate tool calls across all member agents count toward the limit defined on the Team itself, as the Team forwards its tool_call_limit to the underlying model request.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →