Skip to content

.Net New Feature: Expose configurable MaximumAutoInvokeAttempts for ToolCallBehavior #13205

@carllapierre

Description

@carllapierre

[.NET] Expose configurable MaximumAutoInvokeAttempts for ToolCallBehavior

The .NET SDK does not expose a way to configure MaximumAutoInvokeAttempts for function/tool calling, while the Python SDK provides this capability through the public maximum_auto_invoke_attempts property. This limits users' ability to control execution time and costs for agentic scenarios.

Motivation

When building agents that perform research tasks or other multi-step operations, developers need fine-grained control over the maximum number of auto-invoke attempts to:

  1. Control execution time: Research tasks can take too long if the model makes excessive tool calls
  2. Manage costs: Each auto-invoke attempt incurs API costs
  3. Prevent runaway loops: Custom limits help prevent scenarios where the agent gets stuck in inefficient patterns
  4. Match business requirements: Different use cases require different thresholds (e.g., 5 for quick searches, 20 for deep research)

Real-World Use Case

I'm building an agent that performs research tasks. Sometimes I want to limit it to 5 auto-invoke attempts for quick responses, while other times I might allow more for deeper research. Currently, I'm forced to use the hardcoded defaults (128 for OpenAI) with no ability to customize per scenario.

Current State

Python SDK ✅ (Has this feature)

The Python SDK exposes maximum_auto_invoke_attempts as a public, configurable property:

Location: python/semantic_kernel/connectors/ai/function_choice_behavior.py

@experimental
class FunctionChoiceBehavior(KernelBaseModel):
    enable_kernel_functions: bool = True
    maximum_auto_invoke_attempts: int = DEFAULT_MAX_AUTO_INVOKE_ATTEMPTS  # Default: 5
    # ... other properties

Usage Examples:

# Direct configuration
behavior = FunctionChoiceBehavior.Auto(maximum_auto_invoke_attempts=5)

# Runtime modification
behavior.maximum_auto_invoke_attempts = 10

# Via kwargs in factory methods
behavior = FunctionChoiceBehavior.Required(
    auto_invoke=True,
    maximum_auto_invoke_attempts=3
)

.NET SDK ❌ (Missing this feature)

The .NET SDK has MaximumAutoInvokeAttempts as an internal, read-only property with no public configuration API:

Locations:

  • dotnet/src/Connectors/Connectors.OpenAI/ToolCallBehavior.cs
  • dotnet/src/Connectors/Connectors.Google/GeminiToolCallBehavior.cs
  • dotnet/src/Connectors/Connectors.MistralAI/MistralAIToolCallBehavior.cs
  • dotnet/src/Agents/OpenAI/Internal/ResponseThreadActions.cs

Current Implementation:

// OpenAI/Google Connectors
private const int DefaultMaximumAutoInvokeAttempts = 128;
internal int MaximumAutoInvokeAttempts { get; }  // Internal, read-only

private ToolCallBehavior(bool autoInvoke)
{
    this.MaximumAutoInvokeAttempts = autoInvoke ? DefaultMaximumAutoInvokeAttempts : 0;
}

// MistralAI Connector
private const int DefaultMaximumAutoInvokeAttempts = 5;

// Agents
private const int MaximumAutoInvokeAttempts = 128;

Current Defaults (Hardcoded):

  • OpenAI Connector: 128
  • Google/Gemini Connector: 128
  • MistralAI Connector: 5
  • OpenAI Agents: 128

Proposed Solution

Make MaximumAutoInvokeAttempts configurable in the .NET SDK to match Python SDK capabilities.

Constructor Parameter

public abstract class ToolCallBehavior
{
    public static ToolCallBehavior AutoInvokeKernelFunctions => new KernelFunctions(autoInvoke: true);
    
    public static ToolCallBehavior AutoInvokeKernelFunctions(int maximumAutoInvokeAttempts) 
        => new KernelFunctions(autoInvoke: true, maximumAutoInvokeAttempts);
    
    // Property remains internal but value can be set
    internal int MaximumAutoInvokeAttempts { get; }
}

Usage:

var settings = new OpenAIPromptExecutionSettings
{
    ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions(maximumAutoInvokeAttempts: 5)
};

Benefits

  1. Feature Parity: Brings .NET SDK to parity with Python SDK
  2. User Control: Developers can optimize for their specific use cases
  3. Cost Management: Better control over API costs

Additional Context

From docs/decisions/0061-function-call-behavior.md, the design principle states that function call behavior should be:

"connector/model-agnostic function call behavior classes, enabling their use by all SK connectors that support function calling"

Making MaximumAutoInvokeAttempts configurable aligns with this principle by giving users consistent control across all connectors.

Metadata

Metadata

Labels

.NETIssue or Pull requests regarding .NET codepythonPull requests for the Python Semantic Kernel

Projects

Status

Bug

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions