-
Notifications
You must be signed in to change notification settings - Fork 923
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
Conversation
This is a good idea! What do you think about making it a configurable param, default to |
@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? |
@mini-peanut, yeah one use case I had in mind was this: Setup:
If we reset 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 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:
Concerns with Adding a Configuration Parameter: 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. |
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
@rm-openai Fixed, and the code should pass the checks. Thanks for your patience |
Unfortunately looks like typechecking is still not passing |
src/agents/_run_impl.py
Outdated
tools = agent.tools | ||
# Only reset in the problematic scenarios where loops are likely unintentional | ||
if cls._should_reset_tool_choice(agent.model_settings, tools): | ||
agent.model_settings = dataclasses.replace( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned earlier, it looks like the original agent instance is being modified. This means the agent may not be guaranteed to use the tool in subsequent runs. This may not be the intended behavior.
Example:
agent = Agent(
...
tools=[custom_tool],
model_settings=ModelSettings(tool_choice="required"),
)
await Runner.run(agent, "first run") # starts run with tool_choice="required"
await Runner.run(agent, "second run") # starts run with tool_choice="auto"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rm-openai Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm that's true, does feel like an issue with this solution. Maybe we should save it as internal state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@baderalfahad @rm-openai I've fixed the issue by creating new copies of the agent and model settings objects instead of modifying the original instances. The linting errors have also been resolved. This should pass the type checking now.
tool_choice="auto" | ||
) | ||
|
||
if ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If modifying the agent model settings is necessary, this should ideally only be done after checking the run config first. This is because the run config overrides agent-specific settings for a run.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. These two operations should actually be parallel rather than sequential, but putting the run_config check first does make more sense. I've made this change in the latest commit.
This fixes the issue where the original agent's model_settings was being directly modified during the tool choice reset process. The original implementation caused the agent's tool_choice to unintentionally reset to "auto" for subsequent runs, which could be unexpected behavior. The fix creates new copies of the agent and model settings objects using dataclasses.replace() instead of modifying the original objects. This ensures that the tool choice reset is limited to the current run only, maintaining the expected behavior for sequential runs with the same agent. Addresses feedback from @baderalfahad about the agent instance being modified when it should maintain its original state between runs.
This update moves the tool_choice reset logic to a more appropriate location within the RunImpl class, ensuring that the original agent's model_settings remains unmodified during the reset process. The logic now checks for problematic scenarios before creating a modified copy of the agent's settings, maintaining expected behavior across sequential runs. This change enhances clarity and efficiency in handling tool choices. Addresses previous feedback regarding the modification of the agent instance and improves the overall structure of the reset logic.
bc53072
to
0c747af
Compare
I'm merging this because it's mostly great. I think it will need a couple of followups:
I'll follow up with all of those! |
## Summary: #263 added this behavior. The goal was to prevent infinite loops when tool choice was set. The key change I'm making is: 1. Making it configurable on the agent. 2. Doing bookkeeping in the Runner to track this, to prevent mutating agents. 3. Not resetting the global tool choice in RunConfig. ## Test Plan: Unit tests. .
## Summary: #263 added this behavior. The goal was to prevent infinite loops when tool choice was set. The key change I'm making is: 1. Making it configurable on the agent. 2. Doing bookkeeping in the Runner to track this, to prevent mutating agents. 3. Not resetting the global tool choice in RunConfig. ## Test Plan: Unit tests. .
## Summary: #263 added this behavior. The goal was to prevent infinite loops when tool choice was set. The key change I'm making is: 1. Making it configurable on the agent. 2. Doing bookkeeping in the Runner to track this, to prevent mutating agents. 3. Not resetting the global tool choice in RunConfig. ## Test Plan: Unit tests. .
## Summary: #263 added this behavior. The goal was to prevent infinite loops when tool choice was set. The key change I'm making is: 1. Making it configurable on the agent. 2. Doing bookkeeping in the Runner to track this, to prevent mutating agents. 3. Not resetting the global tool choice in RunConfig. ## Test Plan: Unit tests. .
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 resetstool_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
Checks
make lint
andmake format