Skip to content

Python: Logical flaws in max_invocations state scope & max_iterations ambiguity #2329

@anguzo

Description

@anguzo

I have encountered two related design issues regarding how function execution limits are enforced. These behaviors make it difficult to safely deploy agents in long-running (e.g., web server) environments using standard patterns.

1. The max_invocations "Time Bomb" (State Scope Issue)

The max_invocations counter is stored on the Tool/AIFunction instance itself (self.invocation_count).

I assume that when using the standard @ai_function decorator at a module level the tool instance is global and persistent. This is definitely the case if registering tool through dependency injection as singleton (DI is preferred for proper testing). In a long-running application, this counter never resets between requests.

# Defined at module level (Singleton)
@ai_function(max_invocations=100) 
def search_tool(query: str): ...

# Request A (Day 1): Uses 50 calls. (Tool count: 50)
# Request B (Day 2): Uses 50 calls. (Tool count: 100)
# Request C (Day 3): CRASH. ToolException limit reached.

Implication: Setting max_invocations creates a "time bomb" that guarantees functionality failure after N calls globally, rather than rate-limiting per request/conversation or somehow externally (based on actual cost calculations).

2. max_iterations vs. Actual Tool Usage

The FunctionInvocationConfiguration.max_iterations setting (default 40) limits LLM roundtrips/turns, not actual function invocations.

The _tools.py code shows that _try_execute_function_calls is called from a max iterations bound loop in _handle_function_calls_response. Because _try_execute_function_calls triggers several tools in parallel this means that FunctionInvocationConfiguration.max_iterations does not limit function invocation as stated and the following can happen:

config.max_iterations = 3

# Iteration 1: LLM requests 10 parallel searches -> 10 executions
# Iteration 2: LLM requests 10 parallel searches -> 10 executions
# Iteration 3: LLM requests 10 parallel searches -> 10 executions

# Result: 3 Iterations, but 30+ Function Calls.

Implication: max_iterations does not protect against cost overruns or runaway execution loops within a single turn.

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions