# How to Control Agent Tool Usage with tool_choice and tool_call_limit in Agno

> Learn to control Agno agent tool usage with tool_choice and tool_call_limit. Select which tools your LLM can invoke and set call limits for efficient executions.

- Repository: [Agno/agno](https://github.com/agno-agi/agno)
- Tags: how-to-guide
- Published: 2026-02-23

---

**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:

```python
{
    "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`](https://github.com/agno-agi/agno/blob/main/libs/agno/agno/agent/agent.py), the field is defined as:

```python
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`](https://github.com/agno-agi/agno/blob/main/libs/agno/agno/team/_run.py), the framework forwards the configuration:

```python
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`](https://github.com/agno-agi/agno/blob/main/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:

```python
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:

```python
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:

```python
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:

```python
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:

```python

# 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_choice`** accepts `"none"` (prohibit tools), `"auto"` (model decides), or an explicit function dictionary (force specific tool) to govern invocation permissions.
- **`tool_call_limit`** enforces a hard cap on tool executions per run, preventing infinite loops and controlling costs via the logic in [`libs/agno/agno/models/base.py`](https://github.com/agno-agi/agno/blob/main/libs/agno/agno/models/base.py).
- Both parameters are defined in [`libs/agno/agno/agent/agent.py`](https://github.com/agno-agi/agno/blob/main/libs/agno/agno/agent/agent.py) and [`libs/agno/agno/team/team.py`](https://github.com/agno-agi/agno/blob/main/libs/agno/agno/team/team.py), then forwarded to model requests in [`libs/agno/agno/team/_run.py`](https://github.com/agno-agi/agno/blob/main/libs/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.