Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix potential infinite tool call loop by resetting tool_choice after … #263

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

mini-peanut
Copy link

Fix potential infinite tool call loop by resetting tool_choice after tool execution

Summary

This PR fixes an issue where setting tool_choice to "required" or a specific function name could cause models to get stuck in an infinite tool call loop.

When tool_choice is set to force tool usage, this setting persists across model invocations. This PR automatically resets tool_choice to "auto" after tool execution, allowing the model to decide whether to make additional tool calls in subsequent turns.

Unlike using tool_use_behavior="stop_on_first_tool", this approach lets the model continue processing tool results while preventing forced repeated tool calls.

Test plan

  • Added tests to verify tool_choice reset behavior for both agent and run_config settings
  • Added integration test to verify the solution prevents infinite loops
  • All tests pass

Checks

  • I've added new tests for the fix
  • I've updated the relevant documentation (added comment in code)
  • I've run make lint and make format
  • I've made sure tests pass

@rm-openai
Copy link
Collaborator

This is a good idea! What do you think about making it a configurable param, default to reset=True?

@mini-peanut
Copy link
Author

This is a good idea! What do you think about making it a configurable param, default to reset=True?

@rm-openai Thanks for the feedback! I considered adding a config parameter, but wonder if it might add complexity without clear use cases. Most users would want to prevent infinite loops by default, and those with specific needs could already implement custom behaviors through the existing API.

Unless you have specific scenarios in mind where maintaining forced tool calls is beneficial, perhaps the simpler approach is better?

@rm-openai
Copy link
Collaborator

rm-openai commented Mar 20, 2025

@mini-peanut, yeah one use case I had in mind was this:

Setup:

agent = Agent(
  instructions="Use the find_company tool to find the company info. Then use the search_directory tool to get the CEO's email.",
  tools=[find_company, search_directory],
  tool_choice="required",
  tool_use_behavior={"stop_at_tool_names": "search_directory"},

If we reset tool_choice, then we can't trust the Agent to reliably call the second tool.

Thoughts?

@mini-peanut
Copy link
Author

@mini-peanut, yeah one use case I had in mind was this:

Setup:

agent = Agent(
  instructions="Use the find_company tool to find the company info. Then use the search_directory tool to get the CEO's email.",
  tools=[find_company, search_directory],
  tool_choice="required",
  tool_use_behavior={"stop_at_tool_names": "search_directory"},

If we reset tool_choice, then we can't trust the Agent to reliably call the second tool.

Thoughts?

@rm-openai Thanks for sharing that use case. I'd like to refine my approach to focus on the specific problem we're solving.

The Problem: Setting tool_choice to "required" or a specific function name can inadvertently cause infinite loops.

Core Hypothesis: When a user forces a single specific function call, they rarely intend for that same function to be repeatedly called in an infinite loop. This differs from intentional sequential calling of different functions.

Problem Scenario: This issue typically manifests in two specific cases:

  1. When tool_choice is set to a specific function name, causing the same function to be called repeatedly
  2. When tool_choice="required" with only one available tool, which functionally behaves the same way

Concerns with Adding a Configuration Parameter:
Users with legitimate sequential tool usage would need to explicitly set reset_tool_choice_after_use to False.

Targeted Solution: We can address these specific scenarios without disrupting legitimate use cases:

# Only reset in the problematic scenarios where loops are likely unintentional
if (isinstance(tool_choice, str) and tool_choice not in ["auto", "required", "none"]) or 
   (tool_choice == "required" and len(tools) == 1):
    # Reset to "auto"

This approach precisely targets the infinite loop problem without affecting the multi-tool sequential calling pattern you described, and without requiring additional configuration.

rm-openai
rm-openai previously approved these changes Mar 21, 2025
@rm-openai
Copy link
Collaborator

lgtm - but would you mind fixing lint/typechecking please? can't merge without that

- Refactor tool_choice reset to target only problematic edge cases
- Replace manual ModelSettings recreation with dataclasses.replace
- Fix line length and error handling lint issues in tests
@mini-peanut
Copy link
Author

@rm-openai Fixed, and the code should pass the checks. Thanks for your patience

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants