chore: import upstream snapshot with attribution
Continuous Integration / Pre-commit Linter (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.10) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.11) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.12) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.10) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.11) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.12) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.14) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Waiting to run
Copybara PR Handler / close-imported-pr (push) Waiting to run
Continuous Integration / Pre-commit Linter (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.10) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.11) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.12) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.10) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.11) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.12) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.14) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Waiting to run
Copybara PR Handler / close-imported-pr (push) Waiting to run
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
# ADK Agent with Plugin
|
||||
|
||||
### What is ADK Plugin?
|
||||
|
||||
At its core, ADK extensibility is built on
|
||||
[**callbacks**](https://google.github.io/adk-docs/callbacks/): functions you
|
||||
write that ADK automatically executes at key stages of an agent's lifecycle.
|
||||
**A Plugin is simply a class that packages these individual callback functions
|
||||
together for a broader purpose.**
|
||||
|
||||
While a standard Agent Callback is configured on a *single agent, a single tool*
|
||||
for a *specific task*, a Plugin is registered *once* on the `Runner` and its
|
||||
callbacks apply *globally* to every agent, tool, and LLM call managed by that
|
||||
runner. This makes Plugins the ideal solution for implementing horizontal
|
||||
features that cut across your entire application.
|
||||
|
||||
### What can plugins do?
|
||||
|
||||
Plugins are incredibly versatile. By implementing different callback methods, you
|
||||
can achieve a wide range of functionalities.
|
||||
|
||||
- **Logging & Tracing**: Create detailed logs of agent, tool, and LLM activity
|
||||
for debugging and performance analysis.
|
||||
- **Policy Enforcement**: Implement security guardrails. For example, a
|
||||
before_tool_callback can check if a user is authorized to use a specific
|
||||
tool and prevent its execution by returning a value.
|
||||
- **Monitoring & Metrics**: Collect and export metrics on token usage,
|
||||
execution times, and invocation counts to monitoring systems like Prometheus
|
||||
or Stackdriver.
|
||||
- **Caching**: In before_model_callback or before_tool_callback, you can
|
||||
check if a request has been made before. If so, you can return a cached
|
||||
response, skipping the expensive LLM or tool call entirely.
|
||||
- **Request/Response Modification**: Dynamically add information to LLM prompts
|
||||
(e.g., in before_model_callback) or standardize tool outputs (e.g., in
|
||||
after_tool_callback).
|
||||
|
||||
### Run the agent
|
||||
|
||||
**Note: Plugin is NOT supported in `adk web`yet.**
|
||||
|
||||
Use following command to run the main.py
|
||||
|
||||
```bash
|
||||
python3 -m contributing.samples.plugin_basic.main
|
||||
```
|
||||
|
||||
It should output the following content. Note that the outputs from plugin are
|
||||
printed.
|
||||
|
||||
```bash
|
||||
[Plugin] Agent run count: 1
|
||||
[Plugin] LLM request count: 1
|
||||
** Got event from hello_world
|
||||
Hello world: query is [hello world]
|
||||
** Got event from hello_world
|
||||
[Plugin] LLM request count: 2
|
||||
** Got event from hello_world
|
||||
```
|
||||
@@ -0,0 +1,15 @@
|
||||
# Copyright 2026 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 .main import root_agent
|
||||
@@ -0,0 +1,43 @@
|
||||
# Copyright 2026 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 google.adk.agents.base_agent import BaseAgent
|
||||
from google.adk.agents.callback_context import CallbackContext
|
||||
from google.adk.models.llm_request import LlmRequest
|
||||
from google.adk.plugins.base_plugin import BasePlugin
|
||||
|
||||
|
||||
class CountInvocationPlugin(BasePlugin):
|
||||
"""A custom plugin that counts agent and tool invocations."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the plugin with counters."""
|
||||
super().__init__(name="count_invocation")
|
||||
self.agent_count: int = 0
|
||||
self.tool_count: int = 0
|
||||
self.llm_request_count: int = 0
|
||||
|
||||
async def before_agent_callback(
|
||||
self, *, agent: BaseAgent, callback_context: CallbackContext
|
||||
) -> None:
|
||||
"""Count agent runs."""
|
||||
self.agent_count += 1
|
||||
print(f"[Plugin] Agent run count: {self.agent_count}")
|
||||
|
||||
async def before_model_callback(
|
||||
self, *, callback_context: CallbackContext, llm_request: LlmRequest
|
||||
) -> None:
|
||||
"""Count LLM requests."""
|
||||
self.llm_request_count += 1
|
||||
print(f"[Plugin] LLM request count: {self.llm_request_count}")
|
||||
@@ -0,0 +1,64 @@
|
||||
# Copyright 2026 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.
|
||||
|
||||
import asyncio
|
||||
|
||||
from google.adk import Agent
|
||||
from google.adk.runners import InMemoryRunner
|
||||
from google.adk.tools.tool_context import ToolContext
|
||||
from google.genai import types
|
||||
|
||||
# [Step 2] Import the plugin.
|
||||
from .count_plugin import CountInvocationPlugin
|
||||
|
||||
|
||||
async def hello_world(tool_context: ToolContext, query: str):
|
||||
print(f'Hello world: query is [{query}]')
|
||||
|
||||
|
||||
root_agent = Agent(
|
||||
name='hello_world',
|
||||
description='Prints hello world with user query.',
|
||||
instruction="""Use hello_world tool to print hello world and user query.
|
||||
""",
|
||||
tools=[hello_world],
|
||||
)
|
||||
|
||||
|
||||
async def main():
|
||||
"""Main entry point for the agent."""
|
||||
prompt = 'hello world'
|
||||
runner = InMemoryRunner(
|
||||
agent=root_agent,
|
||||
app_name='test_app_with_plugin',
|
||||
# [Step 2] Add your plugin here. You can add multiple plugins.
|
||||
plugins=[CountInvocationPlugin()],
|
||||
)
|
||||
session = await runner.session_service.create_session(
|
||||
user_id='user',
|
||||
app_name='test_app_with_plugin',
|
||||
)
|
||||
|
||||
async for event in runner.run_async(
|
||||
user_id='user',
|
||||
session_id=session.id,
|
||||
new_message=types.Content(
|
||||
role='user', parts=[types.Part.from_text(text=prompt)]
|
||||
),
|
||||
):
|
||||
print(f'** Got event from {event.author}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,15 @@
|
||||
# Copyright 2026 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,123 @@
|
||||
# Copyright 2026 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.
|
||||
|
||||
"""Sample agent demonstrating DebugLoggingPlugin usage.
|
||||
|
||||
This sample shows how to use the DebugLoggingPlugin to capture complete
|
||||
debug information (LLM requests/responses, tool calls, events, session state)
|
||||
to a YAML file for debugging purposes.
|
||||
|
||||
Usage:
|
||||
adk run contributing/samples/plugin_debug_logging
|
||||
|
||||
After running, check the generated `adk_debug.yaml` file for detailed logs.
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from google.adk.agents import LlmAgent
|
||||
from google.adk.apps import App
|
||||
from google.adk.plugins import DebugLoggingPlugin
|
||||
|
||||
|
||||
def get_weather(city: str) -> dict[str, Any]:
|
||||
"""Get the current weather for a city.
|
||||
|
||||
Args:
|
||||
city: The name of the city to get weather for.
|
||||
|
||||
Returns:
|
||||
A dictionary containing weather information.
|
||||
"""
|
||||
# Simulated weather data
|
||||
weather_data = {
|
||||
"new york": {"temperature": 22, "condition": "sunny", "humidity": 45},
|
||||
"london": {"temperature": 15, "condition": "cloudy", "humidity": 70},
|
||||
"tokyo": {"temperature": 28, "condition": "humid", "humidity": 85},
|
||||
"paris": {"temperature": 18, "condition": "rainy", "humidity": 80},
|
||||
}
|
||||
|
||||
city_lower = city.lower()
|
||||
if city_lower in weather_data:
|
||||
data = weather_data[city_lower]
|
||||
return {
|
||||
"city": city,
|
||||
"temperature_celsius": data["temperature"],
|
||||
"condition": data["condition"],
|
||||
"humidity_percent": data["humidity"],
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"city": city,
|
||||
"error": f"Weather data not available for {city}",
|
||||
}
|
||||
|
||||
|
||||
def calculate(expression: str) -> dict[str, Any]:
|
||||
"""Evaluate a simple mathematical expression.
|
||||
|
||||
Args:
|
||||
expression: A mathematical expression to evaluate (e.g., "2 + 2").
|
||||
|
||||
Returns:
|
||||
A dictionary containing the result or error.
|
||||
"""
|
||||
try:
|
||||
# Only allow safe mathematical operations
|
||||
allowed_chars = set("0123456789+-*/.() ")
|
||||
if not all(c in allowed_chars for c in expression):
|
||||
return {"error": "Invalid characters in expression"}
|
||||
|
||||
result = eval(expression) # Safe due to character restriction
|
||||
return {"expression": expression, "result": result}
|
||||
except Exception as e:
|
||||
return {"expression": expression, "error": str(e)}
|
||||
|
||||
|
||||
# Sample queries to try:
|
||||
# - "What's the weather in Tokyo?"
|
||||
# - "Calculate 15 * 7 + 3"
|
||||
# - "What's the weather in London and calculate 100 / 4"
|
||||
root_agent = LlmAgent(
|
||||
name="debug_demo_agent",
|
||||
description="A demo agent that shows DebugLoggingPlugin capabilities",
|
||||
instruction="""You are a helpful assistant that can:
|
||||
1. Get weather information for cities (New York, London, Tokyo, Paris)
|
||||
2. Perform simple calculations
|
||||
|
||||
When asked about weather, use the get_weather tool.
|
||||
When asked to calculate, use the calculate tool.
|
||||
Be concise in your responses.""",
|
||||
tools=[get_weather, calculate],
|
||||
)
|
||||
|
||||
|
||||
# Create the app with DebugLoggingPlugin
|
||||
# The plugin will write detailed debug information to adk_debug.yaml
|
||||
app = App(
|
||||
name="plugin_debug_logging",
|
||||
root_agent=root_agent,
|
||||
plugins=[
|
||||
# DebugLoggingPlugin captures complete interaction data to a YAML file
|
||||
# Options:
|
||||
# output_path: Path to output file (default: "adk_debug.yaml")
|
||||
# include_session_state: Include session state snapshot (default: True)
|
||||
# include_system_instruction: Include full system instruction (default: True)
|
||||
DebugLoggingPlugin(
|
||||
output_path="adk_debug.yaml",
|
||||
include_session_state=True,
|
||||
include_system_instruction=True,
|
||||
),
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,75 @@
|
||||
# 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
|
||||
```
|
||||
|
||||
Select "basic" and 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.
|
||||
```
|
||||
|
||||
### Hallucinating tool calls
|
||||
|
||||
The "hallucinating_func_name" agent is an example to show the plugin can retry
|
||||
hallucinating tool calls.
|
||||
|
||||
For example, we used the `after_model_callback` to hack a tool call with the
|
||||
wrong name then the agent can retry calling with the right tool name.
|
||||
|
||||
You can run the agent with:
|
||||
|
||||
```bash
|
||||
$ adk web contributing/samples/plugin_reflect_tool_retry
|
||||
```
|
||||
|
||||
Select "hallucinating_func_name" and provide the following prompt to see the
|
||||
agent retrying tool calls:
|
||||
|
||||
```
|
||||
Roll a 6 sided die
|
||||
```
|
||||
@@ -0,0 +1,15 @@
|
||||
# Copyright 2026 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,83 @@
|
||||
# Copyright 2026 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
|
||||
|
||||
|
||||
# Sample query: "guess a number between 1 and 50"
|
||||
root_agent = LlmAgent(
|
||||
name="hello_world",
|
||||
description="Helpful agent",
|
||||
instruction="""Your goal is to guess a secret positive integer by using the
|
||||
`guess_number_tool`.
|
||||
The tool will provide feedback on each guess.
|
||||
Your objective is to keep guessing until guess_number_tool returns
|
||||
'status: success'.
|
||||
Start by guessing 50, and use the tool's feedback to adjust your guesses
|
||||
and find the target number.""",
|
||||
tools=[guess_number_tool],
|
||||
)
|
||||
|
||||
|
||||
app = App(
|
||||
name=APP_NAME,
|
||||
root_agent=root_agent,
|
||||
plugins=[
|
||||
CustomRetryPlugin(
|
||||
max_retries=20, throw_exception_if_retry_exceeded=False
|
||||
),
|
||||
LoggingPlugin(),
|
||||
],
|
||||
)
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
# Copyright 2026 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
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
# Copyright 2026 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.
|
||||
|
||||
import random
|
||||
|
||||
from google.adk.agents import LlmAgent
|
||||
from google.adk.agents.callback_context import CallbackContext
|
||||
from google.adk.apps.app import App
|
||||
from google.adk.models.llm_response import LlmResponse
|
||||
from google.adk.plugins import ReflectAndRetryToolPlugin
|
||||
from google.adk.tools.tool_context import ToolContext
|
||||
|
||||
APP_NAME = "hallucinating_func_name"
|
||||
USER_ID = "test_user"
|
||||
|
||||
hallucinated = False # Whether the tool name is hallucinated
|
||||
|
||||
|
||||
def roll_die(sides: int, tool_context: ToolContext) -> int:
|
||||
"""Roll a die and return the rolled result.
|
||||
|
||||
Args:
|
||||
sides: The integer number of sides the die has.
|
||||
|
||||
Returns:
|
||||
An integer of the result of rolling the die.
|
||||
"""
|
||||
result = random.randint(1, sides)
|
||||
if not "rolls" in tool_context.state:
|
||||
tool_context.state["rolls"] = []
|
||||
|
||||
tool_context.state["rolls"] = tool_context.state["rolls"] + [result]
|
||||
return result
|
||||
|
||||
|
||||
def after_model_callback(
|
||||
callback_context: CallbackContext, llm_response: LlmResponse
|
||||
):
|
||||
"""After model callback to produce one hallucinating tool call."""
|
||||
global hallucinated
|
||||
|
||||
if hallucinated:
|
||||
return None
|
||||
|
||||
if (
|
||||
llm_response.content
|
||||
and llm_response.content.parts
|
||||
and llm_response.content.parts[0].function_call
|
||||
and llm_response.content.parts[0].function_call.name == "roll_die"
|
||||
):
|
||||
llm_response.content.parts[0].function_call.name = "roll_die_wrong_name"
|
||||
hallucinated = True
|
||||
return None
|
||||
|
||||
|
||||
root_agent = LlmAgent(
|
||||
name="hello_world",
|
||||
description="Helpful agent",
|
||||
instruction="""Use guess_number_tool to guess a number.""",
|
||||
tools=[roll_die],
|
||||
after_model_callback=after_model_callback,
|
||||
)
|
||||
|
||||
|
||||
app = App(
|
||||
name=APP_NAME,
|
||||
root_agent=root_agent,
|
||||
plugins=[
|
||||
ReflectAndRetryToolPlugin(max_retries=3),
|
||||
],
|
||||
)
|
||||
Reference in New Issue
Block a user