chore: Add an sample agent for the ReflectAndRetryToolPlugin

PiperOrigin-RevId: 817024977
This commit is contained in:
Xuan Yang
2025-10-08 23:05:25 -07:00
committed by Copybara-Service
parent cac9fae829
commit 9b8a4aad6f
3 changed files with 145 additions and 0 deletions
@@ -0,0 +1,53 @@
# Reflect And Retry Tool Plugin
`ReflectAndRetryToolPlugin` provides self-healing, concurrent-safe error
recovery for tool failures.
**Key Features:**
- **Concurrency Safe:** Uses locking to safely handle parallel tool
executions
- **Configurable Scope:** Tracks failures per-invocation (default) or globally
using the `TrackingScope` enum.
- **Extensible Scoping:** The `_get_scope_key` method can be overridden to
implement custom tracking logic (e.g., per-user or per-session).
- **Granular Tracking:** Failure counts are tracked per-tool within the
defined scope. A success with one tool resets its counter without affecting
others.
- **Custom Error Extraction:** Supports detecting errors in normal tool
responses that don't throw exceptions, by overriding the
`extract_error_from_result` method.
## Samples
Here are some sample agents to demonstrate the usage of the plugin.
### Basic Usage
This is a hello world example to show the basic usage of the plugin. The
`guess_number_tool` is hacked with both Exceptions and error responses. With the
help of the `CustomRetryPlugin`, both above error types can lead to retries.
For example, here is the output from agent:
```
I'll guess the number 50. Let's see how it is!
My guess of 50 was too high! I'll try a smaller number this time. Let's go with 25.
My guess of 25 was still too high! I'm going smaller. How about 10?
Still too high! My guess of 10 was also too large. I'll try 5 this time.
My guess of 5 is "almost valid"! That's good news, it means I'm getting very close. I'll try 4.
My guess of 4 is still "almost valid," just like 5. It seems I'm still hovering around the right answer. Let's try 3!
I guessed the number 3, and it is valid! I found it!
```
You can run the agent with:
```bash
$ adk web contributing/samples/plugin_reflect_tool_retry
```
You can provide the following prompt to see the agent retrying tool calls:
```
Please guess a number! Tell me what number you guess and how is it.
```
@@ -0,0 +1,15 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from . import agent
@@ -0,0 +1,77 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Any
from google.adk.agents import LlmAgent
from google.adk.apps.app import App
from google.adk.plugins import LoggingPlugin
from google.adk.plugins import ReflectAndRetryToolPlugin
APP_NAME = "basic"
USER_ID = "test_user"
def guess_number_tool(query: int) -> dict[str, Any]:
"""A tool that guesses a number.
Args:
query: The number to guess.
Returns:
A dictionary containing the status and result of the tool execution.
"""
target_number = 3
if query == target_number:
return {"status": "success", "result": "Number is valid."}
if abs(query - target_number) <= 2:
return {"status": "error", "error_message": "Number is almost valid."}
if query > target_number:
raise ValueError("Number is too large.")
if query < target_number:
raise ValueError("Number is too small.")
raise ValueError("Number is invalid.")
class CustomRetryPlugin(ReflectAndRetryToolPlugin):
async def extract_error_from_result(
self, *, tool, tool_args, tool_context, result
):
return result if result.get("status") == "error" else None
root_agent = LlmAgent(
name="hello_world",
description="Helpful agent",
instruction="""Use guess_number_tool to guess a number.""",
model="gemini-2.5-flash",
tools=[guess_number_tool],
)
app = App(
name=APP_NAME,
root_agent=root_agent,
plugins=[
CustomRetryPlugin(
max_retries=6, throw_exception_if_retry_exceeded=False
),
LoggingPlugin(),
],
)