-
Couldn't load subscription status.
- Fork 2.1k
Description
Describe the bug
An LlmAgent using certain Gemini models (e.g., gemini-2.5-flash-preview-04-17) fails to correctly extract and use a parameter from its input JSON (as described in its instruction) when making a tool call. Instead, it appears to use a hallucinated or example parameter value. When the tool consequently returns an error message string (e.g., "Tool Error: Item not found"), the subsequent LLM call (intended to generate a final natural language response based on the tool's error) fails with a 400 INVALID_ARGUMENT error. The error message is: "Please ensure that function call turn comes immediately after a user turn or after a function response turn." This suggests an issue with how the tool's string error output is handled in the conversation history for the LLM.
To Reproduce
Steps to reproduce the behavior:
- Define an
LlmAgentwith:- An
input_schema(e.g., Pydantic model foruser_queryanditem_id). - An
instructionexplicitly telling the agent to:- Extract
item_idfrom the input JSON string. - Call a tool (e.g.,
get_item_details_tool) using this exact extracteditem_id. - Extract
user_queryfrom the input JSON. - Analyze the tool's output to answer the
user_query. - Respond in natural language, or with a specific string (e.g., "$I don't know$") if the tool fails or the answer isn't found.
- Extract
- The tool (e.g.,
get_item_details_tool) should be anasync deffunction decorated with@FunctionTool. It should be designed to return an error string like "Tool Error: Item [item_id] not found." if the item isn't found.
- An
- Configure the agent to use a model like
gemini-2.5-flash-preview-04-17. - Invoke the agent using
Runner.run_async()with an input JSON containing a specificitem_id(e.g.,"item_id": "actual_id_123") and auser_query. - Observe the logs:
- The agent's tool call log will show it attempting to use an incorrect
item_id(e.g., "sample_id", "test_id") instead of "actual_id_123". - The tool will log that it cannot find this incorrect
item_idand will return its error string. - The
httpxlogs will show the second call togenerateContentfor the model failing with aHTTP/1.1 400 Bad Request. - The application logs will show the
google.genai.errors.ClientError: 400 INVALID_ARGUMENT. {'error': {'code': 400, 'message': 'Please ensure that function call turn comes immediately after a user turn or after a function response turn.', 'status': 'INVALID_ARGUMENT'}}.
- The agent's tool call log will show it attempting to use an incorrect
Expected behavior
- The
LlmAgentshould correctly extract theitem_idfrom the input JSON string as per its instructions. - The agent should call the tool using this correct
item_id. - If the tool returns an error string (e.g., "Tool Error: Item actual_id_123 not found."), the ADK framework should correctly package this string as the "function response" in the conversation history.
- The subsequent LLM call (to generate the final natural language answer) should proceed without a
400 INVALID_ARGUMENTerror. The LLM should then use the tool's error message to formulate its final response according to the agent's instructions (e.g., outputting "$I don't know$").
Desktop :
- OS: macOS
- Python version: 3.11
- ADK version: 0.5.0
Additional context
- The issue seems to be twofold:
- The LLM's failure to adhere to instructions to use the specific ID from the input for the tool call.
- The
400 INVALID_ARGUMENTerror when a tool returns a simple error string, suggesting the model is very strict about the format/content of the "function response" part of the turn history. This error was specifically observed withgemini-2.5-flash-preview-04-17.
- The problem persists despite multiple iterations of increasingly explicit prompt engineering for the agent's instructions.
- The setup involves an
async deftool decorated with@FunctionTool, and the agent is invoked viaRunner.run_async(). The tool is designed to return a string in all cases (data or error message).