Subject: Issue with Gemini 2.5 Pro (Vertex AI): Looping / Incorrect Behavior on Sequential Tool Calls (read_file → write_file) in Single Turn
Model: gemini-2.5-pro-preview-03-25
API/Library: google-genai
Python SDK (via Vertex AI client)
Context: Single-stage workflow designed to test sequential tool execution.
Hello Gemini Team and Community,
We’re encountering persistent issues getting gemini-2.5-pro-preview-03-25
(accessed via Vertex AI) to reliably execute a mandatory two-step tool sequence (read_file
followed by write_file
) within a single turn, despite explicit prompt instructions. The goal is a simple file modification: read a file, modify its content (in memory by the model based on task instructions), and write the modified content back.
We’ve iterated on the system prompt, but the model consistently fails in one of two ways:
- Unnecessary Looping: Completes the read/write sequence but then loops back to
read_file
. - Incorrect Code Generation: Fails to call
write_file
, instead generating text representing the call.
The Task & Core Prompt Logic:
The system prompt instructs the model to act as an autonomous agent for a single-stage task, with a mandatory sequence:
- Call
read_file
. - Call
write_file
with modified content.
Critical constraints emphasized that both steps must happen in the single turn, that the write_file
tool call must be issued (not represented as text/code), and that this two-step sequence fulfills the entire task.
(See Prompt Variations below for specific wording attempts)
Observed Behavior & Evidence:
Attempt 1: Initial Prompt (Focus on sequence & single turn)
- Prompt Snippet:
Perform both tool executions within this single stage/turn.
- Behavior: Model correctly calls
read_file
(Loop 1) andwrite_file
(Loop 2), but then ignores task completion and callsread_file
again in Loop 3, getting stuck. - Evidence:
# Loop 2: write_file call issued correctly 2025-04-19 12:24:54,178 - services.llm_providers.google.client - DEBUG - Standardized Response: {'status': 'success', 'stop_reason': 'tool_use', 'content': [{'type': 'tool_call', 'id': 'gemini_func_write_file_1745058294_0', 'name': 'write_file', 'input': {'path': 'app.py', 'content': '# app.py\n...'}}], ...} # Loop 2: write_file execution success 2025-04-19 12:24:54,189 - services.tool_service - INFO - Tool 'write_file' executed successfully. # Loop 2: write_file result added to history 2025-04-19 12:24:54,190 - services.chat_service - DEBUG - CHAT_SERVICE: Loop 2: History *after* adding tool results (len=6): [{'role': 'tool', 'content': [{'type': 'tool_result', 'tool_use_id': 'gemini_func_write_file_1745058294_0', 'content': "Successfully wrote to file 'app.py'.", 'is_error': False}]}] # Loop 3: Model calls read_file AGAIN instead of stopping 2025-04-19 12:25:14,096 - services.llm_providers.google.client - DEBUG - Standardized Response: {'status': 'success', 'stop_reason': 'tool_use', 'content': [{'type': 'text', 'text': 'Okay, I will first read the content of `app.py`...'}, {'type': 'tool_call', 'id': 'gemini_func_read_file_1745058314_1', 'name': 'read_file', 'input': {'path': 'app.py'}}], ...} # ... continues looping ...
Attempt 2: Added Explicit Stop Instructions
- Prompt Snippet:
Task Completion: Once the write_file tool has been called and its result indicates success..., your task for this stage is complete.
andAfter the successful write_file result is processed, you MUST stop processing and end the turn.
- Behavior: Model called
read_file
(Loop 1), but in Loop 2, it generated textprint(default_api.write_file(...))
containing the correct content instead of issuing thewrite_file
tool call. It stopped withend_turn
. - Evidence:
# Loop 1: Calls read_file successfully # Loop 2: LLM generates text instead of calling write_file 2025-04-19 12:19:20,164 - services.llm_providers.google.client - INFO - Finished converting Gemini response. Final status: success, stop_reason: end_turn 2025-04-19 12:19:20,164 - services.llm_providers.google.client - DEBUG - Standardized Response: {'status': 'success', 'stop_reason': 'end_turn', 'content': [{'type': 'text', 'text': '```tool_code\nprint(default_api.write_file(path=\'app.py\', content=\'# app.py\\n...<modified content>...\'))'}], ...} # Chat service loop ends because stop_reason is end_turn 2025-04-19 12:19:20,167 - services.chat_service - INFO - Chat Service: LLM provided final response or no tools requested. Stop Reason: end_turn. Finishing turn.
Attempt 3: Removed Explicit Stop, Reinforced Task Scope
- Prompt Snippet:
Execution Sequence (Mandatory - Complete these two steps ONLY):
andCompleting the read_file and write_file tool calls fulfills the entire task for this turn. No further tool calls are needed or allowed after the write_file call is issued.
- Behavior: Reverted to the original looping behavior. It successfully called
read_file
(Loop 1) andwrite_file
(Loop 2), but then immediately calledread_file
again in Loop 3 and continued looping. - Evidence: (Same pattern as Attempt 1, shown in the latest logs provided)
# Loop 2: write_file call issued correctly 2025-04-19 12:24:54,178 - services.llm_providers.google.client - DEBUG - Standardized Response: {'status': 'success', 'stop_reason': 'tool_use', 'content': [{'type': 'tool_call', 'id': 'gemini_func_write_file_1745058294_0', ...}]} # Loop 2: write_file success result added 2025-04-19 12:24:54,190 - services.chat_service - DEBUG - CHAT_SERVICE: Loop 2: History *after* adding tool results (len=6): [...] # Loop 3: Calls read_file AGAIN 2025-04-19 12:25:14,096 - services.llm_providers.google.client - DEBUG - Standardized Response: {'status': 'success', 'stop_reason': 'tool_use', 'content': [{'type': 'text', 'text': 'Okay, I will first read...'}, {'type': 'tool_call', 'id': 'gemini_func_read_file_1745058314_1', 'name': 'read_file', ...}]} # ... continues looping (observed up to Loop 13) ...
Comparison:
We tested the exact same workflow structure and initial prompt logic with Anthropic’s claude-3-7-sonnet-20250219
model, which correctly executed the read_file
→ write_file
sequence and then stopped the turn with end_turn
as expected.
Questions:
- Is this looping behavior or the failure to call the second tool (generating code instead) a known issue or limitation with
gemini-2.5-pro-preview-03-25
when handling strictly sequential tool calls within a single turn, especially when the second call involves a large payload (write_file
content)? - Are there recommended prompting strategies or API parameters (beyond standard generation config) specifically for enforcing sequential tool execution and reliable task completion within a single turn for Gemini?
- Could the large size of the
content
argument in thewrite_file
call be contributing to the instability or failure to follow subsequent instructions?
We appreciate any insights or suggestions the community or Google team might have.
Thanks!