chore: import upstream snapshot with attribution
Continuous Integration / Pre-commit Linter (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.10) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.11) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.12) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.13) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.10) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.11) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.12) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.13) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.14) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Has been cancelled
Copybara PR Handler / close-imported-pr (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Has been cancelled
Continuous Integration / Pre-commit Linter (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.10) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.11) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.12) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.13) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.10) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.11) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.12) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.13) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.14) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Has been cancelled
Copybara PR Handler / close-imported-pr (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
# 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.
|
||||
@@ -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,88 @@
|
||||
# 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 import Agent
|
||||
from google.genai import types
|
||||
|
||||
new_message = types.Content(
|
||||
role="user",
|
||||
parts=[types.Part.from_text(text="Count a number")],
|
||||
)
|
||||
|
||||
google_agent_1 = Agent(
|
||||
model="gemini-2.5-flash",
|
||||
name="agent_1",
|
||||
description="The first agent in the team.",
|
||||
instruction="Just say 1",
|
||||
generate_content_config=types.GenerateContentConfig(
|
||||
temperature=0.1,
|
||||
),
|
||||
)
|
||||
|
||||
google_agent_2 = Agent(
|
||||
model="gemini-2.5-flash",
|
||||
name="agent_2",
|
||||
description="The second agent in the team.",
|
||||
instruction="Just say 2",
|
||||
generate_content_config=types.GenerateContentConfig(
|
||||
temperature=0.2,
|
||||
safety_settings=[{
|
||||
"category": "HARM_CATEGORY_HATE_SPEECH",
|
||||
"threshold": "BLOCK_ONLY_HIGH",
|
||||
}],
|
||||
),
|
||||
)
|
||||
|
||||
google_agent_3 = Agent(
|
||||
model="gemini-2.5-flash",
|
||||
name="agent_3",
|
||||
description="The third agent in the team.",
|
||||
instruction="Just say 3",
|
||||
generate_content_config=types.GenerateContentConfig(
|
||||
temperature=0.5,
|
||||
safety_settings=[{
|
||||
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
||||
"threshold": "BLOCK_NONE",
|
||||
}],
|
||||
),
|
||||
)
|
||||
|
||||
google_agent_with_instruction_in_config = Agent(
|
||||
model="gemini-2.5-flash",
|
||||
name="agent",
|
||||
generate_content_config=types.GenerateContentConfig(
|
||||
temperature=0.5, system_instruction="Count 1"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def function():
|
||||
pass
|
||||
|
||||
|
||||
google_agent_with_tools_in_config = Agent(
|
||||
model="gemini-2.5-flash",
|
||||
name="agent",
|
||||
generate_content_config=types.GenerateContentConfig(
|
||||
temperature=0.5, tools=[function]
|
||||
),
|
||||
)
|
||||
|
||||
google_agent_with_response_schema_in_config = Agent(
|
||||
model="gemini-2.5-flash",
|
||||
name="agent",
|
||||
generate_content_config=types.GenerateContentConfig(
|
||||
temperature=0.5, response_schema={"key": "value"}
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,67 @@
|
||||
# Instructions
|
||||
|
||||
## Run Evaluation
|
||||
|
||||
1. Set environment variables in your terminal:
|
||||
|
||||
```shell
|
||||
export GOOGLE_GENAI_USE_ENTERPRISE=FALSE
|
||||
export GOOGLE_API_KEY=<your_api_key>
|
||||
export GOOGLE_CLOUD_PROJECT=<your_bigquery_project>
|
||||
```
|
||||
1. Change to the current directory:
|
||||
|
||||
```shell
|
||||
cd tests/integration/fixture/bigquery_agent/
|
||||
```
|
||||
1. Customize the evaluation dataset to the environment `GOOGLE_CLOUD_PROJECT`
|
||||
by replacing the placeholder to the real project set in your environment:
|
||||
|
||||
```shell
|
||||
sed -e "s:\${GOOGLE_CLOUD_PROJECT}:${GOOGLE_CLOUD_PROJECT}:g" simple.test.json -i
|
||||
```
|
||||
1. Run the following command as per https://google.github.io/adk-docs/evaluate/#3-adk-eval-run-evaluations-via-the-cli:
|
||||
|
||||
```shell
|
||||
adk eval . simple.test.json --config_file_path=test_config.json
|
||||
```
|
||||
|
||||
If it fails, re-run with `--print_detailed_results` flag to see more details
|
||||
on turn-by-turn evaluation.
|
||||
|
||||
## Generate Evaluation dataset
|
||||
|
||||
1. Set environment variables in your terminal:
|
||||
|
||||
```shell
|
||||
export GOOGLE_GENAI_USE_ENTERPRISE=FALSE
|
||||
export GOOGLE_API_KEY=<your_api_key>
|
||||
export GOOGLE_CLOUD_PROJECT=<your_bigquery_project>
|
||||
```
|
||||
1. Set up google [application default credentials](https://cloud.google.com/docs/authentication/provide-credentials-adc)
|
||||
on your machine.
|
||||
|
||||
```shell
|
||||
gcloud auth application-default login
|
||||
```
|
||||
1. Change to the directory containing agent folder:
|
||||
|
||||
```shell
|
||||
cd tests/integration/fixture/
|
||||
```
|
||||
1. Run the following command to start the ADK web app:
|
||||
|
||||
```shell
|
||||
adk web
|
||||
```
|
||||
1. Open the ADK web UI in your browser http://127.0.0.1:8000/dev-ui/?app=bigquery_agent.
|
||||
1. Create an evaluation dataset by following [these steps](https://google.github.io/adk-docs/evaluate/#1-adk-web-run-evaluations-via-the-web-ui).
|
||||
This would generate file `bigquery_agent/simple.evalset.json`.
|
||||
1. Note that this evaluation data would be tied to the agent interaction in the
|
||||
`GOOGLE_CLOUD_PROJECT` set in your environment. To normalize it by replacing
|
||||
the real project set in your environment to a placeholder, let's run the
|
||||
following command:
|
||||
|
||||
```shell
|
||||
sed -e "s:${GOOGLE_CLOUD_PROJECT}:\${GOOGLE_CLOUD_PROJECT}:g" bigquery_agent/simple.evalset.json > bigquery_agent/simple.test.json
|
||||
```
|
||||
@@ -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,75 @@
|
||||
# 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 os
|
||||
|
||||
from google.adk.agents.llm_agent import LlmAgent
|
||||
from google.adk.tools.bigquery.bigquery_credentials import BigQueryCredentialsConfig
|
||||
from google.adk.tools.bigquery.bigquery_toolset import BigQueryToolset
|
||||
from google.adk.tools.bigquery.config import BigQueryToolConfig
|
||||
from google.adk.tools.bigquery.config import WriteMode
|
||||
import google.auth
|
||||
|
||||
# Check necessary environment variables
|
||||
if not (google_cloud_project_id := os.getenv("GOOGLE_CLOUD_PROJECT")):
|
||||
raise ValueError(
|
||||
"GOOGLE_CLOUD_PROJECT environment variable is not set. Please set it"
|
||||
" to the GCP project ID where your BigQuery jobs would be run."
|
||||
)
|
||||
|
||||
# Define an appropriate application name
|
||||
BIGQUERY_AGENT_NAME = "adk_eval_bigquery_agent"
|
||||
|
||||
|
||||
# Define BigQuery tool config with write mode set to allowed. Note that this is
|
||||
# only to demonstrate the full capability of the BigQuery tools. In production
|
||||
# you may want to change to BLOCKED (default write mode, effectively makes the
|
||||
# tool read-only) or PROTECTED (only allows writes in the anonymous dataset of a
|
||||
# BigQuery session) write mode.
|
||||
tool_config = BigQueryToolConfig(
|
||||
write_mode=WriteMode.BLOCKED,
|
||||
application_name=BIGQUERY_AGENT_NAME,
|
||||
compute_project_id=google_cloud_project_id,
|
||||
)
|
||||
|
||||
# Initialize the tools to use the application default credentials.
|
||||
# https://cloud.google.com/docs/authentication/provide-credentials-adc
|
||||
application_default_credentials, _ = google.auth.default()
|
||||
credentials_config = BigQueryCredentialsConfig(
|
||||
credentials=application_default_credentials
|
||||
)
|
||||
|
||||
bigquery_toolset = BigQueryToolset(
|
||||
credentials_config=credentials_config, bigquery_tool_config=tool_config
|
||||
)
|
||||
|
||||
# The variable name `root_agent` determines what your root agent is for the
|
||||
# debug CLI
|
||||
root_agent = LlmAgent(
|
||||
model="gemini-2.5-flash",
|
||||
name=BIGQUERY_AGENT_NAME,
|
||||
description=(
|
||||
"Agent to answer questions about BigQuery data and models and execute"
|
||||
" SQL queries."
|
||||
),
|
||||
instruction=f"""\
|
||||
You are a data science agent with access to several BigQuery tools.
|
||||
Make use of those tools to answer the user's questions.
|
||||
|
||||
You must use the project id {google_cloud_project_id} for running SQL
|
||||
queries and generating data insights
|
||||
""",
|
||||
tools=[bigquery_toolset],
|
||||
)
|
||||
@@ -0,0 +1,538 @@
|
||||
{
|
||||
"eval_set_id": "simple",
|
||||
"name": "simple",
|
||||
"description": null,
|
||||
"eval_cases": [
|
||||
{
|
||||
"eval_id": "penguins exploration",
|
||||
"conversation": [
|
||||
{
|
||||
"invocation_id": "e-81734a2f-60dc-4c7e-9b05-29a2c18b7651",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"inline_data": null,
|
||||
"file_data": null,
|
||||
"thought_signature": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"text": "Hi, what can you do?"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"inline_data": null,
|
||||
"file_data": null,
|
||||
"thought_signature": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"text": "I can help you with BigQuery. I can:\n* List datasets and tables in a project\n* Get information about datasets and tables\n* Execute SQL queries\n* Forecast time series data\n* Answer questions about data in BigQuery tables using natural language.\n\nWhat would you like to do?\n"
|
||||
}
|
||||
],
|
||||
"role": null
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [],
|
||||
"tool_responses": [],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1757645225.080524
|
||||
},
|
||||
{
|
||||
"invocation_id": "e-78026d6b-f3d5-4807-8122-8872efb024d6",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"inline_data": null,
|
||||
"file_data": null,
|
||||
"thought_signature": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"text": "which tools do you have access to?"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"inline_data": null,
|
||||
"file_data": null,
|
||||
"thought_signature": "CqsCAdHtim-ZZuTPQdEgbCYXWniB28PuU35grogIwz7a2X0PG9eopcLAT0hXOv90Zu5b9Q4iREsIAcV3znUrCjwMwRrW_G3QYH7jfaK5oEvunfD-yOUj-V9wjc_hAyKon4ogXs7nxX9v_sAfz1uh48cmiMBcNcJd1dkiBbCufChWQbHFsVghXAnetSRm21H2rRgPuxvpf4_mWXzxgYZddOuf6bYmUI3kEcwQDqbocOk2u-ghG44KnFAXOhqBBu8eUeJM8m7uUWevEtxXIclJ14crWCjWADzAof80VX_rLucQ5sPE5wfTvlFIDECuGapnxmxAVB8hNCw9iwlJEhNUVVSbPPfvjwDSq9s99w-Rbu8yLDA5YwSru-q1qIrxbXTWuNlh9fwCdKDQAgiXUo0=",
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"text": "I have access to the following tools:\n\n* `get_dataset_info(project_id, dataset_id)`: Get metadata information about a BigQuery dataset.\n* `get_table_info(project_id, dataset_id, table_id)`: Get metadata information about a BigQuery table.\n* `list_dataset_ids(project_id)`: List BigQuery dataset IDs in a Google Cloud project.\n* `list_table_ids(project_id, dataset_id)`: List table IDs in a BigQuery dataset.\n* `execute_sql(project_id, query)`: Run a BigQuery or BigQuery ML SQL query in the project and return the result.\n* `forecast(project_id, history_data, timestamp_col, data_col, horizon, id_cols=None)`: Run a BigQuery AI time series forecast using AI.FORECAST.\n* `ask_data_insights(project_id, user_query_with_context, table_references)`: Answers questions about structured data in BigQuery tables using natural language."
|
||||
}
|
||||
],
|
||||
"role": null
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [],
|
||||
"tool_responses": [],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1757645280.602244
|
||||
},
|
||||
{
|
||||
"invocation_id": "e-47aa5d37-6bba-4a2d-8eae-e79f47f347c4",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"inline_data": null,
|
||||
"file_data": null,
|
||||
"thought_signature": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"text": "Are there any ML datasets in the bigquery public data?"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"inline_data": null,
|
||||
"file_data": null,
|
||||
"thought_signature": "Cq8DAdHtim9T93wEXUnP9hCJ0SGm79kvsT5VJBWIL1xr8Z0TBLYIQSVqogdU3mB3XkwHkqKT7hBGBY11yuHwfcohBakiOco71gRXOlhf0XGlNZNIUUObnOdY2swLsmpJDbOtRLxgU9OZ0JhHlC0fkrQj9Ab2wt5A8VFkuBUQaEB-XcJYes8Zo0TfU79nrlKrfIINPHsXEuBdq4biDipPss57EZwgs8HiwdeUCXeMwcYS1NFquYUmFLqnsAf9Xik5k3yEx9iQyF7VtOPNGp9sKC2zh7Euz5bpgiHjRTV41hw5QWQk8Q38hKOS7G2jLLXPO8v63sn9LgIzCHNJUd9j-IU8v5gtgU9CNfG4o7icU7GD77KrJx6etaxHQiwSfMjPR_6tZ_ft4-eIh7kRQdJo-GrtyDJJBNV-5s61G0qMGqbS7JY4RfH5_cT8UUSEamkU1eJGN7pZIAKc7FXaCHlGUOQClx-9D7XXQMDd2gVk1pyQvMeruzmykywSGPffnDZJQ9kVwW9pl6urnk8hX4ZGnXM_DTjaFZFF_Gb1dhpr0Uuy0LWk1IqcfbS95YJIFipFXPo=",
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"text": "Yes, there are a few ML-related datasets in the `bigquery-public-data` project:\n\n* `aml_ai_input_dataset`\n* `bigqueryml_ncaa`\n* `ml_datasets`\n* `ml_datasets_uscentral1`"
|
||||
}
|
||||
],
|
||||
"role": null
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [
|
||||
{
|
||||
"id": "adk-4d524ad4-6a3b-49dd-a44e-763f956f89c1",
|
||||
"args": {
|
||||
"project_id": "bigquery-public-data"
|
||||
},
|
||||
"name": "list_dataset_ids"
|
||||
}
|
||||
],
|
||||
"tool_responses": [],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1757645323.568069
|
||||
},
|
||||
{
|
||||
"invocation_id": "e-76115967-fe7b-4aee-92c8-39d3fd1b35c0",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"inline_data": null,
|
||||
"file_data": null,
|
||||
"thought_signature": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"text": "When was ml_datasets created and which location it is in?"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"inline_data": null,
|
||||
"file_data": null,
|
||||
"thought_signature": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"text": "The `ml_datasets` dataset was created on `1553208775542` (which is March 21, 2019, 19:12:55 UTC) and is located in the `US` region."
|
||||
}
|
||||
],
|
||||
"role": null
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [
|
||||
{
|
||||
"id": "adk-2cba4269-dde6-4b62-a38a-1004f05452a0",
|
||||
"args": {
|
||||
"dataset_id": "ml_datasets",
|
||||
"project_id": "bigquery-public-data"
|
||||
},
|
||||
"name": "get_dataset_info"
|
||||
}
|
||||
],
|
||||
"tool_responses": [],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1757645409.36703
|
||||
},
|
||||
{
|
||||
"invocation_id": "e-9a5e0a24-f7cb-4e50-9a61-430a11837e82",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"inline_data": null,
|
||||
"file_data": null,
|
||||
"thought_signature": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"text": "alright, which tables does it have?"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"inline_data": null,
|
||||
"file_data": null,
|
||||
"thought_signature": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"text": "The `ml_datasets` dataset contains the following tables:\n\n* `census_adult_income`\n* `census_adult_income_kmeans_predictions`\n* `credit_card_default`\n* `holidays_and_events_for_forecasting`\n* `iris`\n* `penguins`\n* `ulb_fraud_detection`"
|
||||
}
|
||||
],
|
||||
"role": null
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [
|
||||
{
|
||||
"id": "adk-089bf143-422f-4516-b6d9-810773b03de7",
|
||||
"args": {
|
||||
"dataset_id": "ml_datasets",
|
||||
"project_id": "bigquery-public-data"
|
||||
},
|
||||
"name": "list_table_ids"
|
||||
}
|
||||
],
|
||||
"tool_responses": [],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1757645478.923206
|
||||
},
|
||||
{
|
||||
"invocation_id": "e-05b241ed-9678-49dc-8ead-25baab6d4002",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"inline_data": null,
|
||||
"file_data": null,
|
||||
"thought_signature": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"text": "tell me more details about the penguins table"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"inline_data": null,
|
||||
"file_data": null,
|
||||
"thought_signature": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"text": "The `penguins` table was created on `1619804743188` (which is April 30, 2021, 00:25:43 UTC) and contains 344 rows. It has the following schema:\n\n* `species`: STRING (REQUIRED)\n* `island`: STRING (NULLABLE)\n* `culmen_length_mm`: FLOAT (NULLABLE)\n* `culmen_depth_mm`: FLOAT (NULLABLE)\n* `flipper_length_mm`: FLOAT (NULLABLE)\n* `body_mass_g`: FLOAT (NULLABLE)\n* `sex`: STRING (NULLABLE)"
|
||||
}
|
||||
],
|
||||
"role": null
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [
|
||||
{
|
||||
"id": "adk-8893d916-9445-4458-8998-ea3e283d534d",
|
||||
"args": {
|
||||
"dataset_id": "ml_datasets",
|
||||
"project_id": "bigquery-public-data",
|
||||
"table_id": "penguins"
|
||||
},
|
||||
"name": "get_table_info"
|
||||
}
|
||||
],
|
||||
"tool_responses": [],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1757645526.958228
|
||||
},
|
||||
{
|
||||
"invocation_id": "e-d7b85acb-b7f3-4b61-b66d-bba77c01e13e",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"inline_data": null,
|
||||
"file_data": null,
|
||||
"thought_signature": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"text": "can you tell me population of penguins per island?"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"inline_data": null,
|
||||
"file_data": null,
|
||||
"thought_signature": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"text": "Here is the population of penguins per island:\n\n* **Dream:** 124\n* **Biscoe:** 168\n* **Torgersen:** 52"
|
||||
}
|
||||
],
|
||||
"role": null
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [
|
||||
{
|
||||
"id": "adk-33ff1809-bbfb-45e7-a07c-25fc9c932260",
|
||||
"args": {
|
||||
"query": "SELECT island, COUNT(*) AS population FROM bigquery-public-data.ml_datasets.penguins GROUP BY island",
|
||||
"project_id": "${GOOGLE_CLOUD_PROJECT}"
|
||||
},
|
||||
"name": "execute_sql"
|
||||
}
|
||||
],
|
||||
"tool_responses": [],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1757645563.777316
|
||||
},
|
||||
{
|
||||
"invocation_id": "e-3b46dd99-4d25-45e5-b16c-2f055de56f47",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"inline_data": null,
|
||||
"file_data": null,
|
||||
"thought_signature": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"text": "can you get me insights about any correlation between body mass and flipper length of penguins?"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"inline_data": null,
|
||||
"file_data": null,
|
||||
"thought_signature": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"text": "Based on the data, there appears to be a positive correlation between the body mass and flipper length of penguins. Generally, as the flipper length in millimeters increases, the body mass in grams also tends to increase."
|
||||
}
|
||||
],
|
||||
"role": null
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [
|
||||
{
|
||||
"id": "adk-266b6452-50b8-4d3b-ae23-d09effa41e0b",
|
||||
"args": {
|
||||
"project_id": "${GOOGLE_CLOUD_PROJECT}",
|
||||
"user_query_with_context": "Is there any correlation between body mass and flipper length of penguins?",
|
||||
"table_references": [
|
||||
{
|
||||
"datasetId": "ml_datasets",
|
||||
"tableId": "penguins",
|
||||
"projectId": "bigquery-public-data"
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "ask_data_insights"
|
||||
}
|
||||
],
|
||||
"tool_responses": [],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1757645706.447186
|
||||
},
|
||||
{
|
||||
"invocation_id": "e-357fb858-adae-4a7c-80fc-fcadf52a20fd",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"inline_data": null,
|
||||
"file_data": null,
|
||||
"thought_signature": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"text": "how do the penguins tables in ml_datasets and ml_datasets_uscentral compare?"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"inline_data": null,
|
||||
"file_data": null,
|
||||
"thought_signature": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"text": "Here's a comparison of the `penguins` tables in `ml_datasets` and `ml_datasets_uscentral1`:\n\n**`bigquery-public-data:ml_datasets.penguins`**\n\n* **Creation Time:** 1619804743188 (April 30, 2021, 00:25:43 UTC)\n* **Location:** US\n* **Number of Rows:** 344\n* **Schema:**\n * `species`: STRING (REQUIRED)\n * `island`: STRING (NULLABLE)\n * `culmen_length_mm`: FLOAT (NULLABLE)\n * `culmen_depth_mm`: FLOAT (NULLABLE)\n * `flipper_length_mm`: FLOAT (NULLABLE)\n * `body_mass_g`: FLOAT (NULLABLE)\n * `sex`: STRING (NULLABLE)\n\n**`bigquery-public-data:ml_datasets_uscentral1.penguins`**\n\n* **Creation Time:** 1634583926374 (October 18, 2021, 23:05:26 UTC)\n* **Location:** us-central1\n* **Number of Rows:** 344\n* **Schema:** (Identical to `ml_datasets.penguins`)\n * `species`: STRING (REQUIRED)\n * `island`: STRING (NULLABLE)\n * `culmen_length_mm`: FLOAT (NULLABLE)\n * `culmen_depth_mm`: FLOAT (NULLABLE)\n * `flipper_length_mm`: FLOAT (NULLABLE)\n * `body_mass_g`: FLOAT (NULLABLE)\n * `sex`: STRING (NULLABLE)\n\n**Key Differences:**\n\n* **Creation Time:** The table in `ml_datasets` was created earlier (April 2021) than the one in `ml_datasets_uscentral1` (October 2021).\n* **Location:** The `ml_datasets.penguins` table is in the `US` multi-region, while `ml_datasets_uscentral1.penguins` is specifically in the `us-central1` region.\n\n**Similarities:**\n\n* Both tables have the **same number of rows (344)**.\n* Both tables have an **identical schema**, with the same column names, types, and modes.\n\nIt appears that `bigquery-public-data:ml_datasets_uscentral1.penguins` is likely a regional copy or a later version of the `bigquery-public-data:ml_datasets.penguins` table."
|
||||
}
|
||||
],
|
||||
"role": null
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [
|
||||
{
|
||||
"id": "adk-8ff49a41-b42a-4f2b-8408-bda00712791b",
|
||||
"args": {
|
||||
"project_id": "bigquery-public-data",
|
||||
"dataset_id": "ml_datasets",
|
||||
"table_id": "penguins"
|
||||
},
|
||||
"name": "get_table_info"
|
||||
},
|
||||
{
|
||||
"id": "adk-c0e9a831-300d-4805-a127-6df1d7afc712",
|
||||
"args": {
|
||||
"project_id": "bigquery-public-data",
|
||||
"dataset_id": "ml_datasets_uscentral1",
|
||||
"table_id": "penguins"
|
||||
},
|
||||
"name": "get_table_info"
|
||||
}
|
||||
],
|
||||
"tool_responses": [],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1757646232.887191
|
||||
},
|
||||
{
|
||||
"invocation_id": "e-72ddf0dd-4fee-41dd-adb6-26cd98baeb25",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"inline_data": null,
|
||||
"file_data": null,
|
||||
"thought_signature": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"text": "can you tell the population per island from the uscentral1 table?"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"inline_data": null,
|
||||
"file_data": null,
|
||||
"thought_signature": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"text": "Here is the population of penguins per island from the `ml_datasets_uscentral1` table:\n\n* **Dream:** 124\n* **Biscoe:** 168\n* **Torgersen:** 52"
|
||||
}
|
||||
],
|
||||
"role": null
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [
|
||||
{
|
||||
"id": "adk-febf7cf1-3465-4393-9882-ef860502238f",
|
||||
"args": {
|
||||
"project_id": "${GOOGLE_CLOUD_PROJECT}",
|
||||
"query": "SELECT island, COUNT(*) AS population FROM `bigquery-public-data`.`ml_datasets_uscentral1`.`penguins` GROUP BY island"
|
||||
},
|
||||
"name": "execute_sql"
|
||||
}
|
||||
],
|
||||
"tool_responses": [],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1757646339.73431
|
||||
}
|
||||
],
|
||||
"session_input": {
|
||||
"app_name": "bigquery_agent",
|
||||
"user_id": "user",
|
||||
"state": {}
|
||||
},
|
||||
"creation_timestamp": 1757648114.6285758
|
||||
}
|
||||
],
|
||||
"creation_timestamp": 1757648101.7927744
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"criteria": {
|
||||
"tool_trajectory_avg_score": 0.7,
|
||||
"response_match_score": 0.7
|
||||
}
|
||||
}
|
||||
@@ -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,105 @@
|
||||
# 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 Optional
|
||||
|
||||
from google.adk.agents.callback_context import CallbackContext
|
||||
from google.adk.agents.invocation_context import InvocationContext
|
||||
from google.adk.agents.llm_agent import Agent
|
||||
from google.adk.models.llm_request import LlmRequest
|
||||
from google.adk.models.llm_response import LlmResponse
|
||||
from google.genai import types
|
||||
|
||||
|
||||
def before_agent_call_end_invocation(
|
||||
callback_context: CallbackContext,
|
||||
) -> types.Content:
|
||||
return types.Content(
|
||||
role='model',
|
||||
parts=[types.Part(text='End invocation event before agent call.')],
|
||||
)
|
||||
|
||||
|
||||
def before_agent_call(
|
||||
invocation_context: InvocationContext,
|
||||
) -> types.Content:
|
||||
return types.Content(
|
||||
role='model',
|
||||
parts=[types.Part.from_text(text='Plain text event before agent call.')],
|
||||
)
|
||||
|
||||
|
||||
def before_model_call_end_invocation(
|
||||
callback_context: CallbackContext, llm_request: LlmRequest
|
||||
) -> LlmResponse:
|
||||
return LlmResponse(
|
||||
content=types.Content(
|
||||
role='model',
|
||||
parts=[
|
||||
types.Part.from_text(
|
||||
text='End invocation event before model call.'
|
||||
)
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def before_model_call(
|
||||
invocation_context: InvocationContext, request: LlmRequest
|
||||
) -> LlmResponse:
|
||||
request.config.system_instruction = 'Just return 999 as response.'
|
||||
return LlmResponse(
|
||||
content=types.Content(
|
||||
role='model',
|
||||
parts=[
|
||||
types.Part.from_text(
|
||||
text='Update request event before model call.'
|
||||
)
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def after_model_call(
|
||||
callback_context: CallbackContext,
|
||||
llm_response: LlmResponse,
|
||||
) -> Optional[LlmResponse]:
|
||||
content = llm_response.content
|
||||
if not content or not content.parts or not content.parts[0].text:
|
||||
return
|
||||
|
||||
content.parts[0].text += 'Update response event after model call.'
|
||||
return llm_response
|
||||
|
||||
|
||||
before_agent_callback_agent = Agent(
|
||||
model='gemini-2.5-flash',
|
||||
name='before_agent_callback_agent',
|
||||
instruction='echo 1',
|
||||
before_agent_callback=before_agent_call_end_invocation,
|
||||
)
|
||||
|
||||
before_model_callback_agent = Agent(
|
||||
model='gemini-2.5-flash',
|
||||
name='before_model_callback_agent',
|
||||
instruction='echo 2',
|
||||
before_model_callback=before_model_call_end_invocation,
|
||||
)
|
||||
|
||||
after_model_callback_agent = Agent(
|
||||
model='gemini-2.5-flash',
|
||||
name='after_model_callback_agent',
|
||||
instruction='Say hello',
|
||||
after_model_callback=after_model_call,
|
||||
)
|
||||
@@ -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,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 typing import List
|
||||
from typing import Union
|
||||
|
||||
from google.adk import Agent
|
||||
from google.adk.tools.tool_context import ToolContext
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
def update_fc(
|
||||
data_one: str,
|
||||
data_two: Union[int, float, str],
|
||||
data_three: list[str],
|
||||
data_four: List[Union[int, float, str]],
|
||||
tool_context: ToolContext,
|
||||
):
|
||||
"""Simply ask to update these variables in the context"""
|
||||
tool_context.actions.update_state("data_one", data_one)
|
||||
tool_context.actions.update_state("data_two", data_two)
|
||||
tool_context.actions.update_state("data_three", data_three)
|
||||
tool_context.actions.update_state("data_four", data_four)
|
||||
|
||||
|
||||
root_agent = Agent(
|
||||
model="gemini-2.5-flash",
|
||||
name="root_agent",
|
||||
instruction="Call tools",
|
||||
flow="auto",
|
||||
tools=[update_fc],
|
||||
)
|
||||
@@ -0,0 +1,582 @@
|
||||
{
|
||||
"id": "ead43200-b575-4241-9248-233b4be4f29a",
|
||||
"context": {
|
||||
"_time": "2024-12-01 09:02:43.531503",
|
||||
"data_one": "RRRR",
|
||||
"data_two": "3.141529",
|
||||
"data_three": [
|
||||
"apple",
|
||||
"banana"
|
||||
],
|
||||
"data_four": [
|
||||
"1",
|
||||
"hello",
|
||||
"3.14"
|
||||
]
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"invocation_id": "6BGrtKJu",
|
||||
"author": "user",
|
||||
"content": {
|
||||
"parts": [
|
||||
{
|
||||
"text": "hi"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"options": {},
|
||||
"id": "ltzQTqR4",
|
||||
"timestamp": 1733043686.8428597
|
||||
},
|
||||
{
|
||||
"invocation_id": "6BGrtKJu",
|
||||
"author": "root_agent",
|
||||
"content": {
|
||||
"parts": [
|
||||
{
|
||||
"text": "Hello! 👋 How can I help you today? \n"
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"options": {
|
||||
"partial": false
|
||||
},
|
||||
"id": "ClSROx8b",
|
||||
"timestamp": 1733043688.1030986
|
||||
},
|
||||
{
|
||||
"invocation_id": "M3dUcVa8",
|
||||
"author": "user",
|
||||
"content": {
|
||||
"parts": [
|
||||
{
|
||||
"text": "update data_one to be RRRR, data_two to be 3.141529, data_three to be apple and banana, data_four to be 1, hello, and 3.14"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"options": {},
|
||||
"id": "yxigGwIZ",
|
||||
"timestamp": 1733043745.9900541
|
||||
},
|
||||
{
|
||||
"invocation_id": "M3dUcVa8",
|
||||
"author": "root_agent",
|
||||
"content": {
|
||||
"parts": [
|
||||
{
|
||||
"function_call": {
|
||||
"args": {
|
||||
"data_four": [
|
||||
"1",
|
||||
"hello",
|
||||
"3.14"
|
||||
],
|
||||
"data_two": "3.141529",
|
||||
"data_three": [
|
||||
"apple",
|
||||
"banana"
|
||||
],
|
||||
"data_one": "RRRR"
|
||||
},
|
||||
"name": "update_fc"
|
||||
}
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"options": {
|
||||
"partial": false
|
||||
},
|
||||
"id": "8V6de8th",
|
||||
"timestamp": 1733043747.4545543
|
||||
},
|
||||
{
|
||||
"invocation_id": "M3dUcVa8",
|
||||
"author": "root_agent",
|
||||
"content": {
|
||||
"parts": [
|
||||
{
|
||||
"function_response": {
|
||||
"name": "update_fc",
|
||||
"response": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"options": {
|
||||
"update_context": {
|
||||
"data_one": "RRRR",
|
||||
"data_two": "3.141529",
|
||||
"data_three": [
|
||||
"apple",
|
||||
"banana"
|
||||
],
|
||||
"data_four": [
|
||||
"1",
|
||||
"hello",
|
||||
"3.14"
|
||||
]
|
||||
},
|
||||
"function_call_event_id": "8V6de8th"
|
||||
},
|
||||
"id": "dkTj5v8B",
|
||||
"timestamp": 1733043747.457031
|
||||
},
|
||||
{
|
||||
"invocation_id": "M3dUcVa8",
|
||||
"author": "root_agent",
|
||||
"content": {
|
||||
"parts": [
|
||||
{
|
||||
"text": "OK. I've updated the data. Anything else? \n"
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"options": {
|
||||
"partial": false
|
||||
},
|
||||
"id": "OZ77XR41",
|
||||
"timestamp": 1733043748.7901294
|
||||
}
|
||||
],
|
||||
"past_events": [],
|
||||
"pending_events": {},
|
||||
"artifacts": {},
|
||||
"event_logs": [
|
||||
{
|
||||
"invocation_id": "6BGrtKJu",
|
||||
"event_id": "ClSROx8b",
|
||||
"model_request": {
|
||||
"model": "gemini-2.5-flash",
|
||||
"contents": [
|
||||
{
|
||||
"parts": [
|
||||
{
|
||||
"text": "hi"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
}
|
||||
],
|
||||
"config": {
|
||||
"system_instruction": "You are an agent. Your name is root_agent.\nCall tools",
|
||||
"tools": [
|
||||
{
|
||||
"function_declarations": [
|
||||
{
|
||||
"description": "Hello",
|
||||
"name": "update_fc",
|
||||
"parameters": {
|
||||
"type": "OBJECT",
|
||||
"properties": {
|
||||
"data_one": {
|
||||
"type": "STRING"
|
||||
},
|
||||
"data_two": {
|
||||
"type": "STRING"
|
||||
},
|
||||
"data_three": {
|
||||
"type": "ARRAY",
|
||||
"items": {
|
||||
"type": "STRING"
|
||||
}
|
||||
},
|
||||
"data_four": {
|
||||
"type": "ARRAY",
|
||||
"items": {
|
||||
"any_of": [
|
||||
{
|
||||
"type": "INTEGER"
|
||||
},
|
||||
{
|
||||
"type": "NUMBER"
|
||||
},
|
||||
{
|
||||
"type": "STRING"
|
||||
}
|
||||
],
|
||||
"type": "STRING"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"model_response": {
|
||||
"candidates": [
|
||||
{
|
||||
"content": {
|
||||
"parts": [
|
||||
{
|
||||
"text": "Hello! 👋 How can I help you today? \n"
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"avg_logprobs": -0.15831730915949896,
|
||||
"finish_reason": "STOP",
|
||||
"safety_ratings": [
|
||||
{
|
||||
"category": "HARM_CATEGORY_HATE_SPEECH",
|
||||
"probability": "NEGLIGIBLE",
|
||||
"probability_score": 0.071777344,
|
||||
"severity": "HARM_SEVERITY_NEGLIGIBLE",
|
||||
"severity_score": 0.07080078
|
||||
},
|
||||
{
|
||||
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
||||
"probability": "NEGLIGIBLE",
|
||||
"probability_score": 0.16308594,
|
||||
"severity": "HARM_SEVERITY_NEGLIGIBLE",
|
||||
"severity_score": 0.14160156
|
||||
},
|
||||
{
|
||||
"category": "HARM_CATEGORY_HARASSMENT",
|
||||
"probability": "NEGLIGIBLE",
|
||||
"probability_score": 0.09423828,
|
||||
"severity": "HARM_SEVERITY_NEGLIGIBLE",
|
||||
"severity_score": 0.037841797
|
||||
},
|
||||
{
|
||||
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
||||
"probability": "NEGLIGIBLE",
|
||||
"probability_score": 0.059326172,
|
||||
"severity": "HARM_SEVERITY_NEGLIGIBLE",
|
||||
"severity_score": 0.02368164
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"model_version": "gemini-2.5-flash",
|
||||
"usage_metadata": {
|
||||
"candidates_token_count": 13,
|
||||
"prompt_token_count": 32,
|
||||
"total_token_count": 45
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"invocation_id": "M3dUcVa8",
|
||||
"event_id": "8V6de8th",
|
||||
"model_request": {
|
||||
"model": "gemini-2.5-flash",
|
||||
"contents": [
|
||||
{
|
||||
"parts": [
|
||||
{
|
||||
"text": "hi"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
{
|
||||
"parts": [
|
||||
{
|
||||
"text": "Hello! 👋 How can I help you today? \n"
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
{
|
||||
"parts": [
|
||||
{
|
||||
"text": "update data_one to be RRRR, data_two to be 3.141529, data_three to be apple and banana, data_four to be 1, hello, and 3.14"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
}
|
||||
],
|
||||
"config": {
|
||||
"system_instruction": "You are an agent. Your name is root_agent.\nCall tools",
|
||||
"tools": [
|
||||
{
|
||||
"function_declarations": [
|
||||
{
|
||||
"description": "Hello",
|
||||
"name": "update_fc",
|
||||
"parameters": {
|
||||
"type": "OBJECT",
|
||||
"properties": {
|
||||
"data_one": {
|
||||
"type": "STRING"
|
||||
},
|
||||
"data_two": {
|
||||
"type": "STRING"
|
||||
},
|
||||
"data_three": {
|
||||
"type": "ARRAY",
|
||||
"items": {
|
||||
"type": "STRING"
|
||||
}
|
||||
},
|
||||
"data_four": {
|
||||
"type": "ARRAY",
|
||||
"items": {
|
||||
"any_of": [
|
||||
{
|
||||
"type": "INTEGER"
|
||||
},
|
||||
{
|
||||
"type": "NUMBER"
|
||||
},
|
||||
{
|
||||
"type": "STRING"
|
||||
}
|
||||
],
|
||||
"type": "STRING"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"model_response": {
|
||||
"candidates": [
|
||||
{
|
||||
"content": {
|
||||
"parts": [
|
||||
{
|
||||
"function_call": {
|
||||
"args": {
|
||||
"data_four": [
|
||||
"1",
|
||||
"hello",
|
||||
"3.14"
|
||||
],
|
||||
"data_two": "3.141529",
|
||||
"data_three": [
|
||||
"apple",
|
||||
"banana"
|
||||
],
|
||||
"data_one": "RRRR"
|
||||
},
|
||||
"name": "update_fc"
|
||||
}
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"avg_logprobs": -2.100960955431219e-6,
|
||||
"finish_reason": "STOP",
|
||||
"safety_ratings": [
|
||||
{
|
||||
"category": "HARM_CATEGORY_HATE_SPEECH",
|
||||
"probability": "NEGLIGIBLE",
|
||||
"probability_score": 0.12158203,
|
||||
"severity": "HARM_SEVERITY_NEGLIGIBLE",
|
||||
"severity_score": 0.13671875
|
||||
},
|
||||
{
|
||||
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
||||
"probability": "NEGLIGIBLE",
|
||||
"probability_score": 0.421875,
|
||||
"severity": "HARM_SEVERITY_LOW",
|
||||
"severity_score": 0.24511719
|
||||
},
|
||||
{
|
||||
"category": "HARM_CATEGORY_HARASSMENT",
|
||||
"probability": "NEGLIGIBLE",
|
||||
"probability_score": 0.15722656,
|
||||
"severity": "HARM_SEVERITY_NEGLIGIBLE",
|
||||
"severity_score": 0.072753906
|
||||
},
|
||||
{
|
||||
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
||||
"probability": "NEGLIGIBLE",
|
||||
"probability_score": 0.083984375,
|
||||
"severity": "HARM_SEVERITY_NEGLIGIBLE",
|
||||
"severity_score": 0.03564453
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"model_version": "gemini-2.5-flash",
|
||||
"usage_metadata": {
|
||||
"candidates_token_count": 32,
|
||||
"prompt_token_count": 94,
|
||||
"total_token_count": 126
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"invocation_id": "M3dUcVa8",
|
||||
"event_id": "OZ77XR41",
|
||||
"model_request": {
|
||||
"model": "gemini-2.5-flash",
|
||||
"contents": [
|
||||
{
|
||||
"parts": [
|
||||
{
|
||||
"text": "hi"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
{
|
||||
"parts": [
|
||||
{
|
||||
"text": "Hello! 👋 How can I help you today? \n"
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
{
|
||||
"parts": [
|
||||
{
|
||||
"text": "update data_one to be RRRR, data_two to be 3.141529, data_three to be apple and banana, data_four to be 1, hello, and 3.14"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
{
|
||||
"parts": [
|
||||
{
|
||||
"function_call": {
|
||||
"args": {
|
||||
"data_four": [
|
||||
"1",
|
||||
"hello",
|
||||
"3.14"
|
||||
],
|
||||
"data_two": "3.141529",
|
||||
"data_three": [
|
||||
"apple",
|
||||
"banana"
|
||||
],
|
||||
"data_one": "RRRR"
|
||||
},
|
||||
"name": "update_fc"
|
||||
}
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
{
|
||||
"parts": [
|
||||
{
|
||||
"function_response": {
|
||||
"name": "update_fc",
|
||||
"response": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
}
|
||||
],
|
||||
"config": {
|
||||
"system_instruction": "You are an agent. Your name is root_agent.\nCall tools",
|
||||
"tools": [
|
||||
{
|
||||
"function_declarations": [
|
||||
{
|
||||
"description": "Hello",
|
||||
"name": "update_fc",
|
||||
"parameters": {
|
||||
"type": "OBJECT",
|
||||
"properties": {
|
||||
"data_one": {
|
||||
"type": "STRING"
|
||||
},
|
||||
"data_two": {
|
||||
"type": "STRING"
|
||||
},
|
||||
"data_three": {
|
||||
"type": "ARRAY",
|
||||
"items": {
|
||||
"type": "STRING"
|
||||
}
|
||||
},
|
||||
"data_four": {
|
||||
"type": "ARRAY",
|
||||
"items": {
|
||||
"any_of": [
|
||||
{
|
||||
"type": "INTEGER"
|
||||
},
|
||||
{
|
||||
"type": "NUMBER"
|
||||
},
|
||||
{
|
||||
"type": "STRING"
|
||||
}
|
||||
],
|
||||
"type": "STRING"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"model_response": {
|
||||
"candidates": [
|
||||
{
|
||||
"content": {
|
||||
"parts": [
|
||||
{
|
||||
"text": "OK. I've updated the data. Anything else? \n"
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"avg_logprobs": -0.22089435373033797,
|
||||
"finish_reason": "STOP",
|
||||
"safety_ratings": [
|
||||
{
|
||||
"category": "HARM_CATEGORY_HATE_SPEECH",
|
||||
"probability": "NEGLIGIBLE",
|
||||
"probability_score": 0.04663086,
|
||||
"severity": "HARM_SEVERITY_NEGLIGIBLE",
|
||||
"severity_score": 0.09423828
|
||||
},
|
||||
{
|
||||
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
||||
"probability": "NEGLIGIBLE",
|
||||
"probability_score": 0.18554688,
|
||||
"severity": "HARM_SEVERITY_NEGLIGIBLE",
|
||||
"severity_score": 0.111328125
|
||||
},
|
||||
{
|
||||
"category": "HARM_CATEGORY_HARASSMENT",
|
||||
"probability": "NEGLIGIBLE",
|
||||
"probability_score": 0.071777344,
|
||||
"severity": "HARM_SEVERITY_NEGLIGIBLE",
|
||||
"severity_score": 0.03112793
|
||||
},
|
||||
{
|
||||
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
||||
"probability": "NEGLIGIBLE",
|
||||
"probability_score": 0.043945313,
|
||||
"severity": "HARM_SEVERITY_NEGLIGIBLE",
|
||||
"severity_score": 0.057373047
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"model_version": "gemini-2.5-flash",
|
||||
"usage_metadata": {
|
||||
"candidates_token_count": 14,
|
||||
"prompt_token_count": 129,
|
||||
"total_token_count": 143
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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,115 @@
|
||||
# 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 List
|
||||
from typing import Union
|
||||
|
||||
from google.adk import Agent
|
||||
from google.adk.agents.invocation_context import InvocationContext
|
||||
from google.adk.planners.plan_re_act_planner import PlanReActPlanner
|
||||
from google.adk.tools.tool_context import ToolContext
|
||||
|
||||
|
||||
def update_fc(
|
||||
data_one: str,
|
||||
data_two: Union[int, float, str],
|
||||
data_three: list[str],
|
||||
data_four: List[Union[int, float, str]],
|
||||
tool_context: ToolContext,
|
||||
) -> str:
|
||||
"""Simply ask to update these variables in the context"""
|
||||
tool_context.actions.update_state('data_one', data_one)
|
||||
tool_context.actions.update_state('data_two', data_two)
|
||||
tool_context.actions.update_state('data_three', data_three)
|
||||
tool_context.actions.update_state('data_four', data_four)
|
||||
return 'The function `update_fc` executed successfully'
|
||||
|
||||
|
||||
def echo_info(customer_id: str) -> str:
|
||||
"""Echo the context variable"""
|
||||
return customer_id
|
||||
|
||||
|
||||
def build_global_instruction(invocation_context: InvocationContext) -> str:
|
||||
return (
|
||||
'This is the global agent instruction for invocation:'
|
||||
f' {invocation_context.invocation_id}.'
|
||||
)
|
||||
|
||||
|
||||
def build_sub_agent_instruction(invocation_context: InvocationContext) -> str:
|
||||
return 'This is the plain text sub agent instruction.'
|
||||
|
||||
|
||||
context_variable_echo_agent = Agent(
|
||||
model='gemini-2.5-flash',
|
||||
name='context_variable_echo_agent',
|
||||
instruction=(
|
||||
'Use the echo_info tool to echo {customerId}, {customerInt},'
|
||||
' {customerFloat}, and {customerJson}. Ask for it if you need to.'
|
||||
),
|
||||
flow='auto',
|
||||
tools=[echo_info],
|
||||
)
|
||||
|
||||
context_variable_with_complicated_format_agent = Agent(
|
||||
model='gemini-2.5-flash',
|
||||
name='context_variable_echo_agent',
|
||||
instruction=(
|
||||
'Use the echo_info tool to echo { customerId }, {{customer_int }, { '
|
||||
" non-identifier-float}}, {artifact.fileName}, {'key1': 'value1'} and"
|
||||
" {{'key2': 'value2'}}. Ask for it if you need to."
|
||||
),
|
||||
flow='auto',
|
||||
tools=[echo_info],
|
||||
)
|
||||
|
||||
context_variable_with_nl_planner_agent = Agent(
|
||||
model='gemini-2.5-flash',
|
||||
name='context_variable_with_nl_planner_agent',
|
||||
instruction=(
|
||||
'Use the echo_info tool to echo {customerId}. Ask for it if you'
|
||||
' need to.'
|
||||
),
|
||||
flow='auto',
|
||||
planner=PlanReActPlanner(),
|
||||
tools=[echo_info],
|
||||
)
|
||||
|
||||
context_variable_with_function_instruction_agent = Agent(
|
||||
model='gemini-2.5-flash',
|
||||
name='context_variable_with_function_instruction_agent',
|
||||
instruction=build_sub_agent_instruction,
|
||||
flow='auto',
|
||||
)
|
||||
|
||||
context_variable_update_agent = Agent(
|
||||
model='gemini-2.5-flash',
|
||||
name='context_variable_update_agent',
|
||||
instruction='Call tools',
|
||||
flow='auto',
|
||||
tools=[update_fc],
|
||||
)
|
||||
|
||||
root_agent = Agent(
|
||||
model='gemini-2.5-flash',
|
||||
name='root_agent',
|
||||
description='The root agent.',
|
||||
flow='auto',
|
||||
global_instruction=build_global_instruction,
|
||||
sub_agents=[
|
||||
context_variable_with_nl_planner_agent,
|
||||
context_variable_update_agent,
|
||||
],
|
||||
)
|
||||
@@ -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,338 @@
|
||||
# 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 import Agent
|
||||
|
||||
# A lightweight in-memory mock database
|
||||
ORDER_DB = {
|
||||
"1": "FINISHED",
|
||||
"2": "CANCELED",
|
||||
"3": "PENDING",
|
||||
"4": "PENDING",
|
||||
} # Order id to status mapping. Available states: 'FINISHED', 'PENDING', and 'CANCELED'
|
||||
USER_TO_ORDER_DB = {
|
||||
"user_a": ["1", "4"],
|
||||
"user_b": ["2"],
|
||||
"user_c": ["3"],
|
||||
} # User id to Order id mapping
|
||||
TICKET_DB = [{
|
||||
"ticket_id": "1",
|
||||
"user_id": "user_a",
|
||||
"issue_type": "LOGIN_ISSUE",
|
||||
"status": "OPEN",
|
||||
}] # Available states: 'OPEN', 'CLOSED', 'ESCALATED'
|
||||
USER_INFO_DB = {
|
||||
"user_a": {"name": "Alice", "email": "alice@example.com"},
|
||||
"user_b": {"name": "Bob", "email": "bob@example.com"},
|
||||
}
|
||||
|
||||
|
||||
def reset_data():
|
||||
global ORDER_DB
|
||||
global USER_TO_ORDER_DB
|
||||
global TICKET_DB
|
||||
global USER_INFO_DB
|
||||
ORDER_DB = {
|
||||
"1": "FINISHED",
|
||||
"2": "CANCELED",
|
||||
"3": "PENDING",
|
||||
"4": "PENDING",
|
||||
}
|
||||
USER_TO_ORDER_DB = {
|
||||
"user_a": ["1", "4"],
|
||||
"user_b": ["2"],
|
||||
"user_c": ["3"],
|
||||
}
|
||||
TICKET_DB = [{
|
||||
"ticket_id": "1",
|
||||
"user_id": "user_a",
|
||||
"issue_type": "LOGIN_ISSUE",
|
||||
"status": "OPEN",
|
||||
}]
|
||||
USER_INFO_DB = {
|
||||
"user_a": {"name": "Alice", "email": "alice@example.com"},
|
||||
"user_b": {"name": "Bob", "email": "bob@example.com"},
|
||||
}
|
||||
|
||||
|
||||
def get_order_status(order_id: str) -> str:
|
||||
"""Get the status of an order.
|
||||
|
||||
Args:
|
||||
order_id (str): The unique identifier of the order.
|
||||
|
||||
Returns:
|
||||
str: The status of the order (e.g., 'FINISHED', 'CANCELED', 'PENDING'),
|
||||
or 'Order not found' if the order_id does not exist.
|
||||
"""
|
||||
return ORDER_DB.get(order_id, "Order not found")
|
||||
|
||||
|
||||
def get_order_ids_for_user(user_id: str) -> list:
|
||||
"""Get the list of order IDs assigned to a specific transaction associated with a user.
|
||||
|
||||
Args:
|
||||
user_id (str): The unique identifier of the user.
|
||||
|
||||
Returns:
|
||||
List[str]: A list of order IDs associated with the user, or an empty list
|
||||
if no orders are found.
|
||||
"""
|
||||
return USER_TO_ORDER_DB.get(user_id, [])
|
||||
|
||||
|
||||
def cancel_order(order_id: str) -> str:
|
||||
"""Cancel an order if it is in a 'PENDING' state.
|
||||
|
||||
You should call "get_order_status" to check the status first, before calling
|
||||
this tool.
|
||||
|
||||
Args:
|
||||
order_id (str): The unique identifier of the order to be canceled.
|
||||
|
||||
Returns:
|
||||
str: A message indicating whether the order was successfully canceled or
|
||||
not.
|
||||
"""
|
||||
if order_id in ORDER_DB and ORDER_DB[order_id] == "PENDING":
|
||||
ORDER_DB[order_id] = "CANCELED"
|
||||
return f"Order {order_id} has been canceled."
|
||||
return f"Order {order_id} cannot be canceled."
|
||||
|
||||
|
||||
def refund_order(order_id: str) -> str:
|
||||
"""Process a refund for an order if it is in a 'CANCELED' state.
|
||||
|
||||
You should call "get_order_status" to check if status first, before calling
|
||||
this tool.
|
||||
|
||||
Args:
|
||||
order_id (str): The unique identifier of the order to be refunded.
|
||||
|
||||
Returns:
|
||||
str: A message indicating whether the order was successfully refunded or
|
||||
not.
|
||||
"""
|
||||
if order_id in ORDER_DB and ORDER_DB[order_id] == "CANCELED":
|
||||
return f"Order {order_id} has been refunded."
|
||||
return f"Order {order_id} cannot be refunded."
|
||||
|
||||
|
||||
def create_ticket(user_id: str, issue_type: str) -> str:
|
||||
"""Create a new support ticket for a user.
|
||||
|
||||
Args:
|
||||
user_id (str): The unique identifier of the user creating the ticket.
|
||||
issue_type (str): An issue type the user is facing. Available types:
|
||||
'LOGIN_ISSUE', 'ORDER_ISSUE', 'OTHER'.
|
||||
|
||||
Returns:
|
||||
str: A message indicating that the ticket was created successfully,
|
||||
including the ticket ID.
|
||||
"""
|
||||
ticket_id = str(len(TICKET_DB) + 1)
|
||||
TICKET_DB.append({
|
||||
"ticket_id": ticket_id,
|
||||
"user_id": user_id,
|
||||
"issue_type": issue_type,
|
||||
"status": "OPEN",
|
||||
})
|
||||
return f"Ticket {ticket_id} created successfully."
|
||||
|
||||
|
||||
def get_ticket_info(ticket_id: str) -> str:
|
||||
"""Retrieve the information of a support ticket.
|
||||
|
||||
current status of a support ticket.
|
||||
|
||||
Args:
|
||||
ticket_id (str): The unique identifier of the ticket.
|
||||
|
||||
Returns:
|
||||
A dictionary contains the following fields, or 'Ticket not found' if the
|
||||
ticket_id does not exist:
|
||||
- "ticket_id": str, the current ticket id
|
||||
- "user_id": str, the associated user id
|
||||
- "issue": str, the issue type
|
||||
- "status": The current status of the ticket (e.g., 'OPEN', 'CLOSED',
|
||||
'ESCALATED')
|
||||
|
||||
Example: {"ticket_id": "1", "user_id": "user_a", "issue": "Login issue",
|
||||
"status": "OPEN"}
|
||||
"""
|
||||
for ticket in TICKET_DB:
|
||||
if ticket["ticket_id"] == ticket_id:
|
||||
return ticket
|
||||
return "Ticket not found"
|
||||
|
||||
|
||||
def get_tickets_for_user(user_id: str) -> list:
|
||||
"""Get all the ticket IDs associated with a user.
|
||||
|
||||
Args:
|
||||
user_id (str): The unique identifier of the user.
|
||||
|
||||
Returns:
|
||||
List[str]: A list of ticket IDs associated with the user.
|
||||
If no tickets are found, returns an empty list.
|
||||
"""
|
||||
return [
|
||||
ticket["ticket_id"]
|
||||
for ticket in TICKET_DB
|
||||
if ticket["user_id"] == user_id
|
||||
]
|
||||
|
||||
|
||||
def update_ticket_status(ticket_id: str, status: str) -> str:
|
||||
"""Update the status of a support ticket.
|
||||
|
||||
Args:
|
||||
ticket_id (str): The unique identifier of the ticket.
|
||||
status (str): The new status to assign to the ticket (e.g., 'OPEN',
|
||||
'CLOSED', 'ESCALATED').
|
||||
|
||||
Returns:
|
||||
str: A message indicating whether the ticket status was successfully
|
||||
updated.
|
||||
"""
|
||||
for ticket in TICKET_DB:
|
||||
if ticket["ticket_id"] == ticket_id:
|
||||
ticket["status"] = status
|
||||
return f"Ticket {ticket_id} status updated to {status}."
|
||||
return "Ticket not found"
|
||||
|
||||
|
||||
def get_user_info(user_id: str) -> dict:
|
||||
"""Retrieve information (name, email) about a user.
|
||||
|
||||
Args:
|
||||
user_id (str): The unique identifier of the user.
|
||||
|
||||
Returns:
|
||||
dict or str: A dictionary containing user information of the following
|
||||
fields, or 'User not found' if the user_id does not exist:
|
||||
|
||||
- name: The name of the user
|
||||
- email: The email address of the user
|
||||
|
||||
For example, {"name": "Chelsea", "email": "123@example.com"}
|
||||
"""
|
||||
return USER_INFO_DB.get(user_id, "User not found")
|
||||
|
||||
|
||||
def send_email(user_id: str, email: str) -> list:
|
||||
"""Send email to user for notification.
|
||||
|
||||
Args:
|
||||
user_id (str): The unique identifier of the user.
|
||||
email (str): The email address of the user.
|
||||
|
||||
Returns:
|
||||
str: A message indicating whether the email was successfully sent.
|
||||
"""
|
||||
if user_id in USER_INFO_DB:
|
||||
return f"Email sent to {email} for user id {user_id}"
|
||||
return "Cannot find this user"
|
||||
|
||||
|
||||
# def update_user_info(user_id: str, new_info: dict[str, str]) -> str:
|
||||
def update_user_info(user_id: str, email: str, name: str) -> str:
|
||||
"""Update a user's information.
|
||||
|
||||
Args:
|
||||
user_id (str): The unique identifier of the user.
|
||||
new_info (dict): A dictionary containing the fields to be updated (e.g.,
|
||||
{'email': 'new_email@example.com'}). Available field keys: 'email' and
|
||||
'name'.
|
||||
|
||||
Returns:
|
||||
str: A message indicating whether the user's information was successfully
|
||||
updated or not.
|
||||
"""
|
||||
if user_id in USER_INFO_DB:
|
||||
# USER_INFO_DB[user_id].update(new_info)
|
||||
if email and name:
|
||||
USER_INFO_DB[user_id].update({"email": email, "name": name})
|
||||
elif email:
|
||||
USER_INFO_DB[user_id].update({"email": email})
|
||||
elif name:
|
||||
USER_INFO_DB[user_id].update({"name": name})
|
||||
else:
|
||||
raise ValueError("this should not happen.")
|
||||
return f"User {user_id} information updated."
|
||||
return "User not found"
|
||||
|
||||
|
||||
def get_user_id_from_cookie() -> str:
|
||||
"""Get user ID(username) from the cookie.
|
||||
|
||||
Only use this function when you do not know user ID(username).
|
||||
|
||||
Args: None
|
||||
|
||||
Returns:
|
||||
str: The user ID.
|
||||
"""
|
||||
return "user_a"
|
||||
|
||||
|
||||
root_agent = Agent(
|
||||
model="gemini-2.5-flash",
|
||||
name="Ecommerce_Customer_Service",
|
||||
instruction="""
|
||||
You are an intelligent customer service assistant for an e-commerce platform. Your goal is to accurately understand user queries and use the appropriate tools to fulfill requests. Follow these guidelines:
|
||||
|
||||
1. **Understand the Query**:
|
||||
- Identify actions and conditions (e.g., create a ticket only for pending orders).
|
||||
- Extract necessary details (e.g., user ID, order ID) from the query or infer them from the context.
|
||||
|
||||
2. **Plan Multi-Step Workflows**:
|
||||
- Break down complex queries into sequential steps. For example
|
||||
- typical workflow:
|
||||
- Retrieve IDs or references first (e.g., orders for a user).
|
||||
- Evaluate conditions (e.g., check order status).
|
||||
- Perform actions (e.g., create a ticket) only when conditions are met.
|
||||
- another typical workflows - order cancellation and refund:
|
||||
- Retrieve all orders for the user (`get_order_ids_for_user`).
|
||||
- Cancel pending orders (`cancel_order`).
|
||||
- Refund canceled orders (`refund_order`).
|
||||
- Notify the user (`send_email`).
|
||||
- another typical workflows - send user report:
|
||||
- Get user id.
|
||||
- Get user info(like emails)
|
||||
- Send email to user.
|
||||
|
||||
3. **Avoid Skipping Steps**:
|
||||
- Ensure each intermediate step is completed before moving to the next.
|
||||
- Do not create tickets or take other actions without verifying the conditions specified in the query.
|
||||
|
||||
4. **Provide Clear Responses**:
|
||||
- Confirm the actions performed, including details like ticket ID or pending orders.
|
||||
- Ensure the response aligns with the steps taken and query intent.
|
||||
""",
|
||||
tools=[
|
||||
get_order_status,
|
||||
cancel_order,
|
||||
get_order_ids_for_user,
|
||||
refund_order,
|
||||
create_ticket,
|
||||
update_ticket_status,
|
||||
get_tickets_for_user,
|
||||
get_ticket_info,
|
||||
get_user_info,
|
||||
send_email,
|
||||
update_user_info,
|
||||
get_user_id_from_cookie,
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,229 @@
|
||||
{
|
||||
"eval_set_id": "a1157c01-851f-48a8-b956-83cf7f463510",
|
||||
"name": "a1157c01-851f-48a8-b956-83cf7f463510",
|
||||
"description": null,
|
||||
"eval_cases": [
|
||||
{
|
||||
"eval_id": "tests/integration/fixture/ecommerce_customer_service_agent/order_query.test.json",
|
||||
"conversation": [
|
||||
{
|
||||
"invocation_id": "38d54523-d789-4873-8cc0-d38826c7feb4",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "Send an email to user user_a whose email address is alice@example.com"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "Email sent to alice@example.com for user id user_a."
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [
|
||||
{
|
||||
"id": null,
|
||||
"args": {
|
||||
"email": "alice@example.com",
|
||||
"user_id": "user_a"
|
||||
},
|
||||
"name": "send_email"
|
||||
}
|
||||
],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1747341706.6240807
|
||||
},
|
||||
{
|
||||
"invocation_id": "916393ab-0bce-4cb0-98de-6573d4e8e25c",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "Can you tell me the status of my order with ID 1?"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "Your order with ID 1 is FINISHED."
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [
|
||||
{
|
||||
"id": null,
|
||||
"args": {
|
||||
"order_id": "1"
|
||||
},
|
||||
"name": "get_order_status"
|
||||
}
|
||||
],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1747341706.6241167
|
||||
},
|
||||
{
|
||||
"invocation_id": "511b23d9-56f9-423b-9c31-7626f3411c32",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "Cancel all pending order for the user with user id user_a"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "I have checked your orders and order 4 was in pending status, so I have cancelled it. Order 1 was already finished and couldn't be cancelled.\n"
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [
|
||||
{
|
||||
"id": null,
|
||||
"args": {
|
||||
"user_id": "user_a"
|
||||
},
|
||||
"name": "get_order_ids_for_user"
|
||||
},
|
||||
{
|
||||
"id": null,
|
||||
"args": {
|
||||
"order_id": "1"
|
||||
},
|
||||
"name": "get_order_status"
|
||||
},
|
||||
{
|
||||
"id": null,
|
||||
"args": {
|
||||
"order_id": "4"
|
||||
},
|
||||
"name": "get_order_status"
|
||||
},
|
||||
{
|
||||
"id": null,
|
||||
"args": {
|
||||
"order_id": "4"
|
||||
},
|
||||
"name": "cancel_order"
|
||||
}
|
||||
],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1747341706.6241703
|
||||
},
|
||||
{
|
||||
"invocation_id": "dcdf4b6d-96dd-4602-8c14-0563c6f6b5d0",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "What orders have I placed under the username user_b?"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "User user_b has placed one order with order ID 2.\n"
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [
|
||||
{
|
||||
"id": null,
|
||||
"args": {
|
||||
"user_id": "user_b"
|
||||
},
|
||||
"name": "get_order_ids_for_user"
|
||||
}
|
||||
],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1747341706.624196
|
||||
}
|
||||
],
|
||||
"session_input": null,
|
||||
"creation_timestamp": 1747341706.6242023
|
||||
}
|
||||
],
|
||||
"creation_timestamp": 1747341706.6242158
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"criteria": {
|
||||
"tool_trajectory_avg_score": 0.7,
|
||||
"response_match_score": 0.5
|
||||
}
|
||||
}
|
||||
@@ -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,182 @@
|
||||
# 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 import Agent
|
||||
from google.genai import types
|
||||
|
||||
research_plan_agent = Agent(
|
||||
model="gemini-2.5-flash",
|
||||
name="research_plan_agent",
|
||||
description="I can help generate research plan.",
|
||||
instruction="""\
|
||||
Your task is to create a research plan according to the user's query.
|
||||
|
||||
# Here are the instructions for creating the research plan:
|
||||
|
||||
+ Focus on finding specific things, e.g. products, data, etc.
|
||||
+ Have the personality of a work colleague that is very helpful and explains things very nicely.
|
||||
+ Don't mention your name unless you are asked.
|
||||
+ Think about the most common things that you would need to research.
|
||||
+ Think about possible answers when creating the plan.
|
||||
+ Your task is to create the sections that should be researched. You will output high level headers, preceded by ##
|
||||
+ Underneath each header, write a short sentence on what we want to find there.
|
||||
+ The headers will follow the logical analysis pattern, as well as logical exploration pattern.
|
||||
+ The headers should be a statement, not be in the form of questions.
|
||||
+ The header will not include roman numerals or anything of the sort, e.g. ":", etc
|
||||
+ Do not include things that you cannot possibly know about from using Google Search: e.g. sales forecasting, competitors, profitability analysis, etc.
|
||||
+ Do not have an executive summary
|
||||
+ In each section describe specifically what will be researched.
|
||||
+ Never use "we will", but rather "I will".
|
||||
+ Don't ask for clarifications from the user.
|
||||
+ Do not ask the user for clarifications or if they have any other questions.
|
||||
+ All headers should be bolded.
|
||||
+ If you have steps in the plan that depend on other information, make sure they are 2 different sections in the plan.
|
||||
+ At the end mention that you will start researching.
|
||||
|
||||
# Instruction on replying format
|
||||
|
||||
+ Start with your name as "[research_plan_agent]: ".
|
||||
+ Output the content you want to say.
|
||||
|
||||
Output summary:
|
||||
""",
|
||||
flow="single",
|
||||
sub_agents=[],
|
||||
generate_content_config=types.GenerateContentConfig(
|
||||
temperature=0.1,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
question_generation_agent = Agent(
|
||||
model="gemini-2.5-flash",
|
||||
name="question_generation_agent",
|
||||
description="I can help generate questions related to user's question.",
|
||||
instruction="""\
|
||||
Generate questions related to the research plan generated by research_plan_agent.
|
||||
|
||||
# Instruction on replying format
|
||||
|
||||
Your reply should be a numbered list.
|
||||
|
||||
For each question, reply in the following format: "[question_generation_agent]: [generated questions]"
|
||||
|
||||
Here is an example of the generated question list:
|
||||
|
||||
1. [question_generation_agent]: which state is San Jose in?
|
||||
2. [question_generation_agent]: how google website is designed?
|
||||
""",
|
||||
flow="single",
|
||||
sub_agents=[],
|
||||
generate_content_config=types.GenerateContentConfig(
|
||||
temperature=0.1,
|
||||
),
|
||||
)
|
||||
|
||||
information_retrieval_agent = Agent(
|
||||
model="gemini-2.5-flash",
|
||||
name="information_retrieval_agent",
|
||||
description=(
|
||||
"I can help retrieve information related to question_generation_agent's"
|
||||
" question."
|
||||
),
|
||||
instruction="""\
|
||||
Inspect all the questions after "[question_generation_agent]: " and answer them.
|
||||
|
||||
# Instruction on replying format
|
||||
|
||||
Always start with "[information_retrieval_agent]: "
|
||||
|
||||
For the answer of one question:
|
||||
|
||||
- Start with a title with one line summary of the reply.
|
||||
- The title line should be bolded and starts with No.x of the corresponding question.
|
||||
- Have a paragraph of detailed explain.
|
||||
|
||||
# Instruction on exiting the loop
|
||||
|
||||
- If you see there are less than 20 questions by "question_generation_agent", do not say "[exit]".
|
||||
- If you see there are already great or equal to 20 questions asked by "question_generation_agent", say "[exit]" at last to exit the loop.
|
||||
""",
|
||||
flow="single",
|
||||
sub_agents=[],
|
||||
generate_content_config=types.GenerateContentConfig(
|
||||
temperature=0.1,
|
||||
),
|
||||
)
|
||||
|
||||
question_sources_generation_agent = Agent(
|
||||
model="gemini-2.5-flash",
|
||||
name="question_sources_generation_agent",
|
||||
description=(
|
||||
"I can help generate questions and retrieve related information."
|
||||
),
|
||||
instruction="Generate questions and retrieve information.",
|
||||
flow="loop",
|
||||
sub_agents=[
|
||||
question_generation_agent,
|
||||
information_retrieval_agent,
|
||||
],
|
||||
generate_content_config=types.GenerateContentConfig(
|
||||
temperature=0.1,
|
||||
),
|
||||
)
|
||||
|
||||
summary_agent = Agent(
|
||||
model="gemini-2.5-flash",
|
||||
name="summary_agent",
|
||||
description="I can help summarize information of previous content.",
|
||||
instruction="""\
|
||||
Summarize information in all historical messages that were replied by "question_generation_agent" and "information_retrieval_agent".
|
||||
|
||||
# Instruction on replying format
|
||||
|
||||
- The output should be like an essay that has a title, an abstract, multiple paragraphs for each topic and a conclusion.
|
||||
- Each paragraph should maps to one or more question in historical content.
|
||||
""",
|
||||
flow="single",
|
||||
generate_content_config=types.GenerateContentConfig(
|
||||
temperature=0.8,
|
||||
),
|
||||
)
|
||||
|
||||
research_assistant = Agent(
|
||||
model="gemini-2.5-flash",
|
||||
name="research_assistant",
|
||||
description="I can help with research question.",
|
||||
instruction="Help customers with their need.",
|
||||
flow="sequential",
|
||||
sub_agents=[
|
||||
research_plan_agent,
|
||||
question_sources_generation_agent,
|
||||
summary_agent,
|
||||
],
|
||||
generate_content_config=types.GenerateContentConfig(
|
||||
temperature=0.1,
|
||||
),
|
||||
)
|
||||
|
||||
spark_agent = Agent(
|
||||
model="gemini-2.5-flash",
|
||||
name="spark_assistant",
|
||||
description="I can help with non-research question.",
|
||||
instruction="Help customers with their need.",
|
||||
flow="auto",
|
||||
sub_agents=[research_assistant],
|
||||
generate_content_config=types.GenerateContentConfig(
|
||||
temperature=0.1,
|
||||
),
|
||||
)
|
||||
|
||||
root_agent = spark_agent
|
||||
File diff suppressed because one or more lines are too long
@@ -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,95 @@
|
||||
# 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.
|
||||
|
||||
# Hello world agent from agent 1.0 - https://colab.sandbox.google.com/drive/1Zq-nqmgK0nCERCv8jKIaoeTTgbNn6oSo?resourcekey=0-GYaz9pFT4wY8CI8Cvjy5GA#scrollTo=u3X3XwDOaCv9
|
||||
import random
|
||||
|
||||
from google.adk import Agent
|
||||
from google.genai import types
|
||||
|
||||
|
||||
def roll_die(sides: int) -> 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.
|
||||
"""
|
||||
return random.randint(1, sides)
|
||||
|
||||
|
||||
def check_prime(nums: list[int]) -> list[str]:
|
||||
"""Check if a given list of numbers are prime.
|
||||
|
||||
Args:
|
||||
nums: The list of numbers to check.
|
||||
|
||||
Returns:
|
||||
A str indicating which number is prime.
|
||||
"""
|
||||
primes = set()
|
||||
for number in nums:
|
||||
number = int(number)
|
||||
if number <= 1:
|
||||
continue
|
||||
is_prime = True
|
||||
for i in range(2, int(number**0.5) + 1):
|
||||
if number % i == 0:
|
||||
is_prime = False
|
||||
break
|
||||
if is_prime:
|
||||
primes.add(number)
|
||||
return (
|
||||
'No prime numbers found.'
|
||||
if not primes
|
||||
else f"{', '.join(str(num) for num in primes)} are prime numbers."
|
||||
)
|
||||
|
||||
|
||||
root_agent = Agent(
|
||||
model='gemini-2.5-flash',
|
||||
name='data_processing_agent',
|
||||
instruction="""
|
||||
You roll dice and answer questions about the outcome of the dice rolls.
|
||||
You can roll dice of different sizes.
|
||||
You can use multiple tools in parallel by calling functions in parallel(in one request and in one round).
|
||||
The only things you do are roll dice for the user and discuss the outcomes.
|
||||
It is ok to discuss previous dice roles, and comment on the dice rolls.
|
||||
When you are asked to roll a die, you must call the roll_die tool with the number of sides. Be sure to pass in an integer. Do not pass in a string.
|
||||
You should never roll a die on your own.
|
||||
When checking prime numbers, call the check_prime tool with a list of integers. Be sure to pass in a list of integers. You should never pass in a string.
|
||||
You should not check prime numbers before calling the tool.
|
||||
When you are asked to roll a die and check prime numbers, you should always make the following two function calls:
|
||||
1. You should first call the roll_die tool to get a roll. Wait for the function response before calling the check_prime tool.
|
||||
2. After you get the function response from roll_die tool, you should call the check_prime tool with the roll_die result.
|
||||
2.1 If user asks you to check primes based on previous rolls, make sure you include the previous rolls in the list.
|
||||
3. When you respond, you must include the roll_die result from step 1.
|
||||
You should always perform the previous 3 steps when asking for a roll and checking prime numbers.
|
||||
You should not rely on the previous history on prime results.
|
||||
""",
|
||||
tools=[
|
||||
roll_die,
|
||||
check_prime,
|
||||
],
|
||||
generate_content_config=types.GenerateContentConfig(
|
||||
safety_settings=[
|
||||
types.SafetySetting( # avoid false alarm about rolling dice.
|
||||
category=types.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
|
||||
threshold=types.HarmBlockThreshold.OFF,
|
||||
),
|
||||
]
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,143 @@
|
||||
{
|
||||
"eval_set_id": "56540925-a5ff-49fe-a4e1-589fe78066f2",
|
||||
"name": "56540925-a5ff-49fe-a4e1-589fe78066f2",
|
||||
"description": null,
|
||||
"eval_cases": [
|
||||
{
|
||||
"eval_id": "tests/integration/fixture/hello_world_agent/roll_die.test.json",
|
||||
"conversation": [
|
||||
{
|
||||
"invocation_id": "b01f67f0-9f23-44d6-bbe4-36ea235cb9fb",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "Hi who are you?"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "I am a data processing agent. I can roll dice and check if the results are prime numbers. What would you like me to do? \n"
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1747341775.8937013
|
||||
},
|
||||
{
|
||||
"invocation_id": "13be0093-ac29-4828-98c6-5bbd570c010c",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "What can you do?"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "I can roll dice for you of different sizes, and I can check if the results are prime numbers. I can also remember previous rolls if you'd like to check those for primes as well. What would you like me to do? \n"
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1747341775.8937378
|
||||
},
|
||||
{
|
||||
"invocation_id": "7deda353-c936-4c21-b242-9fa75e45b6a7",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "Can you roll a die with 6 sides"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": null
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [
|
||||
{
|
||||
"id": null,
|
||||
"args": {
|
||||
"sides": 6
|
||||
},
|
||||
"name": "roll_die"
|
||||
}
|
||||
],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1747341775.8937788
|
||||
}
|
||||
],
|
||||
"session_input": null,
|
||||
"creation_timestamp": 1747341775.8937826
|
||||
}
|
||||
],
|
||||
"creation_timestamp": 1747341775.8937957
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"criteria": {
|
||||
"tool_trajectory_avg_score": 1.0,
|
||||
"response_match_score": 0.5
|
||||
}
|
||||
}
|
||||
@@ -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,104 @@
|
||||
# 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.
|
||||
|
||||
# Hello world agent from agent 1.0 revised to be defined with get_agent_async
|
||||
# instead of root_agent - https://colab.sandbox.google.com/drive/1Zq-nqmgK0nCERCv8jKIaoeTTgbNn6oSo?resourcekey=0-GYaz9pFT4wY8CI8Cvjy5GA#scrollTo=u3X3XwDOaCv9
|
||||
import contextlib
|
||||
import random
|
||||
from typing import Optional
|
||||
|
||||
from google.adk import Agent
|
||||
from google.adk.agents import llm_agent
|
||||
from google.genai import types
|
||||
|
||||
|
||||
def roll_die(sides: int) -> 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.
|
||||
"""
|
||||
return random.randint(1, sides)
|
||||
|
||||
|
||||
def check_prime(nums: list[int]) -> list[str]:
|
||||
"""Check if a given list of numbers are prime.
|
||||
|
||||
Args:
|
||||
nums: The list of numbers to check.
|
||||
|
||||
Returns:
|
||||
A str indicating which number is prime.
|
||||
"""
|
||||
primes = set()
|
||||
for number in nums:
|
||||
number = int(number)
|
||||
if number <= 1:
|
||||
continue
|
||||
is_prime = True
|
||||
for i in range(2, int(number**0.5) + 1):
|
||||
if number % i == 0:
|
||||
is_prime = False
|
||||
break
|
||||
if is_prime:
|
||||
primes.add(number)
|
||||
return (
|
||||
'No prime numbers found.'
|
||||
if not primes
|
||||
else f"{', '.join(str(num) for num in primes)} are prime numbers."
|
||||
)
|
||||
|
||||
|
||||
async def get_agent_async() -> (
|
||||
tuple[llm_agent.LlmAgent, Optional[contextlib.AsyncExitStack]]
|
||||
):
|
||||
"""Returns the root agent."""
|
||||
root_agent = Agent(
|
||||
model='gemini-2.5-flash',
|
||||
name='data_processing_agent',
|
||||
instruction="""
|
||||
You roll dice and answer questions about the outcome of the dice rolls.
|
||||
You can roll dice of different sizes.
|
||||
You can use multiple tools in parallel by calling functions in parallel(in one request and in one round).
|
||||
The only things you do are roll dice for the user and discuss the outcomes.
|
||||
It is ok to discuss previous dice roles, and comment on the dice rolls.
|
||||
When you are asked to roll a die, you must call the roll_die tool with the number of sides. Be sure to pass in an integer. Do not pass in a string.
|
||||
You should never roll a die on your own.
|
||||
When checking prime numbers, call the check_prime tool with a list of integers. Be sure to pass in a list of integers. You should never pass in a string.
|
||||
You should not check prime numbers before calling the tool.
|
||||
When you are asked to roll a die and check prime numbers, you should always make the following two function calls:
|
||||
1. You should first call the roll_die tool to get a roll. Wait for the function response before calling the check_prime tool.
|
||||
2. After you get the function response from roll_die tool, you should call the check_prime tool with the roll_die result.
|
||||
2.1 If user asks you to check primes based on previous rolls, make sure you include the previous rolls in the list.
|
||||
3. When you respond, you must include the roll_die result from step 1.
|
||||
You should always perform the previous 3 steps when asking for a roll and checking prime numbers.
|
||||
You should not rely on the previous history on prime results.
|
||||
""",
|
||||
tools=[
|
||||
roll_die,
|
||||
check_prime,
|
||||
],
|
||||
generate_content_config=types.GenerateContentConfig(
|
||||
safety_settings=[
|
||||
types.SafetySetting( # avoid false alarm about rolling dice.
|
||||
category=types.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
|
||||
threshold=types.HarmBlockThreshold.OFF,
|
||||
),
|
||||
]
|
||||
),
|
||||
)
|
||||
return root_agent, None
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"eval_set_id": "56540925-a5ff-49fe-a4e1-589fe78066f2",
|
||||
"name": "56540925-a5ff-49fe-a4e1-589fe78066f2",
|
||||
"description": null,
|
||||
"eval_cases": [
|
||||
{
|
||||
"eval_id": "tests/integration/fixture/hello_world_agent_async/roll_die.test.json",
|
||||
"conversation": [
|
||||
{
|
||||
"invocation_id": "b01f67f0-9f23-44d6-bbe4-36ea235cb9fb",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "Hi who are you?"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "I am a data processing agent. I can roll dice and check if the results are prime numbers. What would you like me to do? \n"
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1747341775.8937013
|
||||
}
|
||||
],
|
||||
"session_input": null,
|
||||
"creation_timestamp": 1747341775.8937826
|
||||
}
|
||||
],
|
||||
"creation_timestamp": 1747341775.8937957
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"criteria": {
|
||||
"tool_trajectory_avg_score": 1.0,
|
||||
"response_match_score": 0.5
|
||||
}
|
||||
}
|
||||
@@ -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,304 @@
|
||||
# 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 os
|
||||
import sys
|
||||
|
||||
from google.adk import Agent
|
||||
|
||||
DEVICE_DB = {
|
||||
"device_1": {"status": "ON", "location": "Living Room"},
|
||||
"device_2": {"status": "OFF", "location": "Bedroom"},
|
||||
"device_3": {"status": "OFF", "location": "Kitchen"},
|
||||
}
|
||||
|
||||
TEMPERATURE_DB = {
|
||||
"Living Room": 22,
|
||||
"Bedroom": 20,
|
||||
"Kitchen": 24,
|
||||
}
|
||||
|
||||
SCHEDULE_DB = {
|
||||
"device_1": {"time": "18:00", "status": "ON"},
|
||||
"device_2": {"time": "22:00", "status": "OFF"},
|
||||
}
|
||||
|
||||
USER_PREFERENCES_DB = {
|
||||
"user_x": {"preferred_temp": 21, "location": "Bedroom"},
|
||||
"user_x": {"preferred_temp": 21, "location": "Living Room"},
|
||||
"user_y": {"preferred_temp": 23, "location": "Living Room"},
|
||||
}
|
||||
|
||||
|
||||
def reset_data():
|
||||
global DEVICE_DB
|
||||
global TEMPERATURE_DB
|
||||
global SCHEDULE_DB
|
||||
global USER_PREFERENCES_DB
|
||||
DEVICE_DB = {
|
||||
"device_1": {"status": "ON", "location": "Living Room"},
|
||||
"device_2": {"status": "OFF", "location": "Bedroom"},
|
||||
"device_3": {"status": "OFF", "location": "Kitchen"},
|
||||
}
|
||||
|
||||
TEMPERATURE_DB = {
|
||||
"Living Room": 22,
|
||||
"Bedroom": 20,
|
||||
"Kitchen": 24,
|
||||
}
|
||||
|
||||
SCHEDULE_DB = {
|
||||
"device_1": {"time": "18:00", "status": "ON"},
|
||||
"device_2": {"time": "22:00", "status": "OFF"},
|
||||
}
|
||||
|
||||
USER_PREFERENCES_DB = {
|
||||
"user_x": {"preferred_temp": 21, "location": "Bedroom"},
|
||||
"user_x": {"preferred_temp": 21, "location": "Living Room"},
|
||||
"user_y": {"preferred_temp": 23, "location": "Living Room"},
|
||||
}
|
||||
|
||||
|
||||
def get_device_info(device_id: str) -> dict:
|
||||
"""Get the current status and location of a AC device.
|
||||
|
||||
Args:
|
||||
device_id (str): The unique identifier of the device.
|
||||
|
||||
Returns:
|
||||
dict: A dictionary containing the following fields, or 'Device not found'
|
||||
if the device_id does not exist:
|
||||
- status: The current status of the device (e.g., 'ON', 'OFF')
|
||||
- location: The location where the device is installed (e.g., 'Living
|
||||
Room', 'Bedroom', ''Kitchen')
|
||||
"""
|
||||
return DEVICE_DB.get(device_id, "Device not found")
|
||||
|
||||
|
||||
# def set_device_info(device_id: str, updates: dict) -> str:
|
||||
# """Update the information of a AC device, specifically its status and/or location.
|
||||
|
||||
# Args:
|
||||
# device_id (str): Required. The unique identifier of the device.
|
||||
# updates (dict): Required. A dictionary containing the fields to be
|
||||
# updated. Supported keys: - "status" (str): The new status to set for the
|
||||
# device. Accepted values: 'ON', 'OFF'. **Only these values are allowed.**
|
||||
# - "location" (str): The new location to set for the device. Accepted
|
||||
# values: 'Living Room', 'Bedroom', 'Kitchen'. **Only these values are
|
||||
# allowed.**
|
||||
|
||||
|
||||
# Returns:
|
||||
# str: A message indicating whether the device information was successfully
|
||||
# updated.
|
||||
# """
|
||||
# if device_id in DEVICE_DB:
|
||||
# if "status" in updates:
|
||||
# DEVICE_DB[device_id]["status"] = updates["status"]
|
||||
# if "location" in updates:
|
||||
# DEVICE_DB[device_id]["location"] = updates["location"]
|
||||
# return f"Device {device_id} information updated: {updates}."
|
||||
# return "Device not found"
|
||||
def set_device_info(
|
||||
device_id: str, status: str = "", location: str = ""
|
||||
) -> str:
|
||||
"""Update the information of a AC device, specifically its status and/or location.
|
||||
|
||||
Args:
|
||||
device_id (str): Required. The unique identifier of the device.
|
||||
status (str): The new status to set for the
|
||||
device. Accepted values: 'ON', 'OFF'. **Only these values are allowed.**
|
||||
location (str): The new location to set for the device. Accepted
|
||||
values: 'Living Room', 'Bedroom', 'Kitchen'. **Only these values are
|
||||
allowed.**
|
||||
|
||||
Returns:
|
||||
str: A message indicating whether the device information was successfully
|
||||
updated.
|
||||
"""
|
||||
if device_id in DEVICE_DB:
|
||||
if status:
|
||||
DEVICE_DB[device_id]["status"] = status
|
||||
return f"Device {device_id} information updated: status -> {status}."
|
||||
if location:
|
||||
DEVICE_DB[device_id]["location"] = location
|
||||
return f"Device {device_id} information updated: location -> {location}."
|
||||
return "Device not found"
|
||||
|
||||
|
||||
def get_temperature(location: str) -> int:
|
||||
"""Get the current temperature in Celsius of a location (e.g., 'Living Room', 'Bedroom', 'Kitchen').
|
||||
|
||||
Args:
|
||||
location (str): The location for which to retrieve the temperature (e.g.,
|
||||
'Living Room', 'Bedroom', 'Kitchen').
|
||||
|
||||
Returns:
|
||||
int: The current temperature in Celsius in the specified location, or
|
||||
'Location not found' if the location does not exist.
|
||||
"""
|
||||
return TEMPERATURE_DB.get(location, "Location not found")
|
||||
|
||||
|
||||
def set_temperature(location: str, temperature: int) -> str:
|
||||
"""Set the desired temperature in Celsius for a location.
|
||||
|
||||
Acceptable range of temperature: 18-30 Celsius. If it's out of the range, do
|
||||
not call this tool.
|
||||
|
||||
Args:
|
||||
location (str): The location where the temperature should be set.
|
||||
temperature (int): The desired temperature as integer to set in Celsius.
|
||||
Acceptable range: 18-30 Celsius.
|
||||
|
||||
Returns:
|
||||
str: A message indicating whether the temperature was successfully set.
|
||||
"""
|
||||
if location in TEMPERATURE_DB:
|
||||
TEMPERATURE_DB[location] = temperature
|
||||
return f"Temperature in {location} set to {temperature}°C."
|
||||
return "Location not found"
|
||||
|
||||
|
||||
def get_user_preferences(user_id: str) -> dict:
|
||||
"""Get the temperature preferences and preferred location of a user_id.
|
||||
|
||||
user_id must be provided.
|
||||
|
||||
Args:
|
||||
user_id (str): The unique identifier of the user.
|
||||
|
||||
Returns:
|
||||
dict: A dictionary containing the following fields, or 'User not found' if
|
||||
the user_id does not exist:
|
||||
- preferred_temp: The user's preferred temperature.
|
||||
- location: The location where the user prefers to be.
|
||||
"""
|
||||
return USER_PREFERENCES_DB.get(user_id, "User not found")
|
||||
|
||||
|
||||
def set_device_schedule(device_id: str, time: str, status: str) -> str:
|
||||
"""Schedule a device to change its status at a specific time.
|
||||
|
||||
Args:
|
||||
device_id (str): The unique identifier of the device.
|
||||
time (str): The time at which the device should change its status (format:
|
||||
'HH:MM').
|
||||
status (str): The status to set for the device at the specified time
|
||||
(e.g., 'ON', 'OFF').
|
||||
|
||||
Returns:
|
||||
str: A message indicating whether the schedule was successfully set.
|
||||
"""
|
||||
if device_id in DEVICE_DB:
|
||||
SCHEDULE_DB[device_id] = {"time": time, "status": status}
|
||||
return f"Device {device_id} scheduled to turn {status} at {time}."
|
||||
return "Device not found"
|
||||
|
||||
|
||||
def get_device_schedule(device_id: str) -> dict:
|
||||
"""Retrieve the schedule of a device.
|
||||
|
||||
Args:
|
||||
device_id (str): The unique identifier of the device.
|
||||
|
||||
Returns:
|
||||
dict: A dictionary containing the following fields, or 'Schedule not
|
||||
found' if the device_id does not exist:
|
||||
- time: The scheduled time for the device to change its status (format:
|
||||
'HH:MM').
|
||||
- status: The status that will be set at the scheduled time (e.g., 'ON',
|
||||
'OFF').
|
||||
"""
|
||||
return SCHEDULE_DB.get(device_id, "Schedule not found")
|
||||
|
||||
|
||||
def celsius_to_fahrenheit(celsius: int) -> float:
|
||||
"""Convert Celsius to Fahrenheit.
|
||||
|
||||
You must call this to do the conversion of temperature, so you can get the
|
||||
precise number in required format.
|
||||
|
||||
Args:
|
||||
celsius (int): Temperature in Celsius.
|
||||
|
||||
Returns:
|
||||
float: Temperature in Fahrenheit.
|
||||
"""
|
||||
return (celsius * 9 / 5) + 32
|
||||
|
||||
|
||||
def fahrenheit_to_celsius(fahrenheit: float) -> int:
|
||||
"""Convert Fahrenheit to Celsius.
|
||||
|
||||
You must call this to do the conversion of temperature, so you can get the
|
||||
precise number in required format.
|
||||
|
||||
Args:
|
||||
fahrenheit (float): Temperature in Fahrenheit.
|
||||
|
||||
Returns:
|
||||
int: Temperature in Celsius.
|
||||
"""
|
||||
return int((fahrenheit - 32) * 5 / 9)
|
||||
|
||||
|
||||
def list_devices(status: str = "", location: str = "") -> list:
|
||||
"""Retrieve a list of AC devices, filtered by status and/or location when provided.
|
||||
|
||||
For cost efficiency, always apply as many filters (status and location) as
|
||||
available in the input arguments.
|
||||
|
||||
Args:
|
||||
status (str, optional): The status to filter devices by (e.g., 'ON',
|
||||
'OFF'). Defaults to None.
|
||||
location (str, optional): The location to filter devices by (e.g., 'Living
|
||||
Room', 'Bedroom', ''Kitchen'). Defaults to None.
|
||||
|
||||
Returns:
|
||||
list: A list of dictionaries, each containing the device ID, status, and
|
||||
location, or an empty list if no devices match the criteria.
|
||||
"""
|
||||
devices = []
|
||||
for device_id, info in DEVICE_DB.items():
|
||||
if ((not status) or info["status"] == status) and (
|
||||
(not location) or info["location"] == location
|
||||
):
|
||||
devices.append({
|
||||
"device_id": device_id,
|
||||
"status": info["status"],
|
||||
"location": info["location"],
|
||||
})
|
||||
return devices if devices else "No devices found matching the criteria."
|
||||
|
||||
|
||||
root_agent = Agent(
|
||||
model="gemini-2.5-flash",
|
||||
name="Home_automation_agent",
|
||||
instruction="""
|
||||
You are Home Automation Agent. You are responsible for controlling the devices in the home.
|
||||
""",
|
||||
tools=[
|
||||
get_device_info,
|
||||
set_device_info,
|
||||
get_temperature,
|
||||
set_temperature,
|
||||
get_user_preferences,
|
||||
set_device_schedule,
|
||||
get_device_schedule,
|
||||
celsius_to_fahrenheit,
|
||||
fahrenheit_to_celsius,
|
||||
list_devices,
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"eval_set_id": "b305bd06-38c5-4796-b9c7-d9c7454338b9",
|
||||
"name": "b305bd06-38c5-4796-b9c7-d9c7454338b9",
|
||||
"description": null,
|
||||
"eval_cases": [
|
||||
{
|
||||
"eval_id": "tests/integration/fixture/home_automation_agent/simple_test.test.json",
|
||||
"conversation": [
|
||||
{
|
||||
"invocation_id": "b7982664-0ab6-47cc-ab13-326656afdf75",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "Turn off device_2 in the Bedroom."
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "I have set the device_2 status to off."
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [
|
||||
{
|
||||
"id": null,
|
||||
"args": {
|
||||
"location": "Bedroom",
|
||||
"device_id": "device_2",
|
||||
"status": "OFF"
|
||||
},
|
||||
"name": "set_device_info"
|
||||
}
|
||||
],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1747337309.2360144
|
||||
}
|
||||
],
|
||||
"session_input": null,
|
||||
"creation_timestamp": 1747337309.2360282
|
||||
}
|
||||
],
|
||||
"creation_timestamp": 1747337309.2360387
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
[{
|
||||
"query": "Turn off device_3 in the Bedroom.",
|
||||
"expected_tool_use": [{"tool_name": "set_device_info", "tool_input": {"location": "Bedroom", "device_id": "device_3", "status": "OFF"}}],
|
||||
"reference": "I have set the device_3 status to off."
|
||||
}]
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"criteria": {
|
||||
"tool_trajectory_avg_score": 1.0,
|
||||
"response_match_score": 0.3
|
||||
}
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"eval_set_id": "1be50511-ff75-4d68-b2d7-2165cbdc1044",
|
||||
"name": "1be50511-ff75-4d68-b2d7-2165cbdc1044",
|
||||
"description": null,
|
||||
"eval_cases": [
|
||||
{
|
||||
"eval_id": "tests/integration/fixture/home_automation_agent/test_files/dependent_tool_calls.test.json",
|
||||
"conversation": [
|
||||
{
|
||||
"invocation_id": "cbece1c0-3811-45c0-96fc-9a4279075483",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "Turn off device_2 in the Bedroom."
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "I have set the device 2 status to off."
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [
|
||||
{
|
||||
"id": null,
|
||||
"args": {
|
||||
"location": "Bedroom",
|
||||
"status": "OFF",
|
||||
"device_id": "device_2"
|
||||
},
|
||||
"name": "set_device_info"
|
||||
}
|
||||
],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1747340826.1082227
|
||||
},
|
||||
{
|
||||
"invocation_id": "cc85cdae-4258-4b94-8fe7-a985b8356190",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "What's the status of device_2 in the Bedroom?"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "Status of device_2 is off."
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [
|
||||
{
|
||||
"id": null,
|
||||
"args": {
|
||||
"device_id": "device_2"
|
||||
},
|
||||
"name": "get_device_info"
|
||||
}
|
||||
],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1747340826.1082554
|
||||
}
|
||||
],
|
||||
"session_input": null,
|
||||
"creation_timestamp": 1747340826.108262
|
||||
}
|
||||
],
|
||||
"creation_timestamp": 1747340826.108275
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"eval_set_id": "94553685-5f19-492b-bc44-f3bc775955e9",
|
||||
"name": "94553685-5f19-492b-bc44-f3bc775955e9",
|
||||
"description": null,
|
||||
"eval_cases": [
|
||||
{
|
||||
"eval_id": "tests/integration/fixture/home_automation_agent/test_files/memorizing_past_events/eval_data.test.json",
|
||||
"conversation": [
|
||||
{
|
||||
"invocation_id": "a958b622-21d3-4a6c-9c15-1274bbb8a6b6",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "Turn off device_2 in the Bedroom."
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "OK. I've turned off device_2 in the Bedroom. Anything else?\n"
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [
|
||||
{
|
||||
"id": null,
|
||||
"args": {
|
||||
"location": "Bedroom",
|
||||
"device_id": "device_2",
|
||||
"status": "OFF"
|
||||
},
|
||||
"name": "set_device_info"
|
||||
}
|
||||
],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1747340865.7043095
|
||||
},
|
||||
{
|
||||
"invocation_id": "1c07123d-4bed-4eb0-9e55-c7f80c70dadf",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "What's the command I just issued?"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "You asked me to turn off device_2 in the Bedroom.\n"
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1747340865.7043421
|
||||
}
|
||||
],
|
||||
"session_input": null,
|
||||
"creation_timestamp": 1747340865.7043483
|
||||
}
|
||||
],
|
||||
"creation_timestamp": 1747340865.704361
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"criteria": {
|
||||
"tool_trajectory_avg_score": 1.0,
|
||||
"response_match_score": 0.5
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
{
|
||||
"eval_set_id": "4412cca6-dfcd-43ab-bbc5-9155380c7137",
|
||||
"name": "4412cca6-dfcd-43ab-bbc5-9155380c7137",
|
||||
"description": null,
|
||||
"eval_cases": [
|
||||
{
|
||||
"eval_id": "tests/integration/fixture/home_automation_agent/test_files/simple_multi_turn_conversation.test.json",
|
||||
"conversation": [
|
||||
{
|
||||
"invocation_id": "9f51a1ac-56a4-4b4a-9878-36ff1ae312ce",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "Turn off device_2 in the Bedroom."
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "I have set the device 2 status to off."
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [
|
||||
{
|
||||
"id": null,
|
||||
"args": {
|
||||
"location": "Bedroom",
|
||||
"device_id": "device_2",
|
||||
"status": "OFF"
|
||||
},
|
||||
"name": "set_device_info"
|
||||
}
|
||||
],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1747340791.7353904
|
||||
},
|
||||
{
|
||||
"invocation_id": "c82d54d0-5fa8-4f79-a6dc-692090f0d42b",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "Turn on device_2 in the Bedroom."
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "I have set the device 2 status to on."
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [
|
||||
{
|
||||
"id": null,
|
||||
"args": {
|
||||
"location": "Bedroom",
|
||||
"status": "ON",
|
||||
"device_id": "device_2"
|
||||
},
|
||||
"name": "set_device_info"
|
||||
}
|
||||
],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1747340791.7354295
|
||||
}
|
||||
],
|
||||
"session_input": null,
|
||||
"creation_timestamp": 1747340791.7354348
|
||||
}
|
||||
],
|
||||
"creation_timestamp": 1747340791.735446
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"eval_set_id": "9100bfc9-cc28-4ab9-b920-2dc72e138997",
|
||||
"name": "9100bfc9-cc28-4ab9-b920-2dc72e138997",
|
||||
"description": null,
|
||||
"eval_cases": [
|
||||
{
|
||||
"eval_id": "tests/integration/fixture/home_automation_agent/test_files/simple_test.test.json",
|
||||
"conversation": [
|
||||
{
|
||||
"invocation_id": "9f5e8d91-8e51-41d6-addf-196a828168c5",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "Turn off device_2 in the Bedroom."
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "OK. I've turned off device_2 in the Bedroom. Anything else?\n"
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [
|
||||
{
|
||||
"id": null,
|
||||
"args": {
|
||||
"location": "Bedroom",
|
||||
"device_id": "device_2",
|
||||
"status": "OFF"
|
||||
},
|
||||
"name": "set_device_info"
|
||||
}
|
||||
],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1747340849.0429707
|
||||
},
|
||||
{
|
||||
"invocation_id": "767b2451-5f7b-4c73-aeaf-a82c71e15788",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "What's the command I just issued?"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "You asked me to turn off device_2 in the Bedroom.\n"
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1747340849.0429986
|
||||
}
|
||||
],
|
||||
"session_input": null,
|
||||
"creation_timestamp": 1747340849.0430045
|
||||
}
|
||||
],
|
||||
"creation_timestamp": 1747340849.0430162
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"eval_set_id": "e141f90b-9e7e-4f06-94d7-bbe7e8080ead",
|
||||
"name": "e141f90b-9e7e-4f06-94d7-bbe7e8080ead",
|
||||
"description": null,
|
||||
"eval_cases": [
|
||||
{
|
||||
"eval_id": "tests/integration/fixture/home_automation_agent/test_files/simple_test2.test.json",
|
||||
"conversation": [
|
||||
{
|
||||
"invocation_id": "c35582f7-838a-460f-b783-039e278165e0",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "Turn off device_3 in the Bedroom."
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "I have set the device_3 status to off."
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [
|
||||
{
|
||||
"id": null,
|
||||
"args": {
|
||||
"location": "Bedroom",
|
||||
"device_id": "device_3",
|
||||
"status": "OFF"
|
||||
},
|
||||
"name": "set_device_info"
|
||||
}
|
||||
],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1747340814.8645504
|
||||
}
|
||||
],
|
||||
"session_input": null,
|
||||
"creation_timestamp": 1747340814.86456
|
||||
}
|
||||
],
|
||||
"creation_timestamp": 1747340814.864572
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"criteria": {
|
||||
"tool_trajectory_avg_score": 1.0
|
||||
}
|
||||
}
|
||||
@@ -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,218 @@
|
||||
# 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 os
|
||||
from typing import Any
|
||||
|
||||
from crewai_tools import DirectoryReadTool
|
||||
from google.adk import Agent
|
||||
from google.adk.tools.agent_tool import AgentTool
|
||||
from google.adk.tools.crewai_tool import CrewaiTool
|
||||
from google.adk.tools.langchain_tool import LangchainTool
|
||||
from google.adk.tools.retrieval.files_retrieval import FilesRetrieval
|
||||
from google.adk.tools.retrieval.vertex_ai_rag_retrieval import VertexAiRagRetrieval
|
||||
from langchain_community.tools import ShellTool
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class TestCase(BaseModel):
|
||||
case: str
|
||||
|
||||
|
||||
class Test(BaseModel):
|
||||
test_title: list[str]
|
||||
|
||||
|
||||
def simple_function(param: str) -> str:
|
||||
if isinstance(param, str):
|
||||
return "Called simple function successfully"
|
||||
return "Called simple function with wrong param type"
|
||||
|
||||
|
||||
def no_param_function() -> str:
|
||||
return "Called no param function successfully"
|
||||
|
||||
|
||||
def no_output_function(param: str):
|
||||
return
|
||||
|
||||
|
||||
def multiple_param_types_function(
|
||||
param1: str, param2: int, param3: float, param4: bool
|
||||
) -> str:
|
||||
if (
|
||||
isinstance(param1, str)
|
||||
and isinstance(param2, int)
|
||||
and isinstance(param3, float)
|
||||
and isinstance(param4, bool)
|
||||
):
|
||||
return "Called multiple param types function successfully"
|
||||
return "Called multiple param types function with wrong param types"
|
||||
|
||||
|
||||
def throw_error_function(param: str) -> str:
|
||||
raise ValueError("Error thrown by throw_error_function")
|
||||
|
||||
|
||||
def list_str_param_function(param: list[str]) -> str:
|
||||
if isinstance(param, list) and all(isinstance(item, str) for item in param):
|
||||
return "Called list str param function successfully"
|
||||
return "Called list str param function with wrong param type"
|
||||
|
||||
|
||||
def return_list_str_function(param: str) -> list[str]:
|
||||
return ["Called return list str function successfully"]
|
||||
|
||||
|
||||
def complex_function_list_dict(
|
||||
param1: dict[str, Any], param2: list[dict[str, Any]]
|
||||
) -> list[Test]:
|
||||
if (
|
||||
isinstance(param1, dict)
|
||||
and isinstance(param2, list)
|
||||
and all(isinstance(item, dict) for item in param2)
|
||||
):
|
||||
return [
|
||||
Test(test_title=["function test 1", "function test 2"]),
|
||||
Test(test_title=["retrieval test"]),
|
||||
]
|
||||
raise ValueError("Wrong param")
|
||||
|
||||
|
||||
def repetitive_call_1(param: str):
|
||||
return f"Call repetitive_call_2 tool with param {param + '_repetitive'}"
|
||||
|
||||
|
||||
def repetitive_call_2(param: str):
|
||||
return param
|
||||
|
||||
|
||||
test_case_retrieval = FilesRetrieval(
|
||||
name="test_case_retrieval",
|
||||
description="General guidance for agent test cases",
|
||||
input_dir=os.path.join(os.path.dirname(__file__), "files"),
|
||||
)
|
||||
|
||||
valid_rag_retrieval = VertexAiRagRetrieval(
|
||||
name="valid_rag_retrieval",
|
||||
rag_corpora=[
|
||||
"projects/1096655024998/locations/us-central1/ragCorpora/4985766262475849728"
|
||||
],
|
||||
description="General guidance for agent test cases",
|
||||
)
|
||||
|
||||
invalid_rag_retrieval = VertexAiRagRetrieval(
|
||||
name="invalid_rag_retrieval",
|
||||
rag_corpora=[
|
||||
"projects/1096655024998/locations/us-central1/InValidRagCorporas/4985766262475849728"
|
||||
],
|
||||
description="Invalid rag retrieval resource name",
|
||||
)
|
||||
|
||||
non_exist_rag_retrieval = VertexAiRagRetrieval(
|
||||
name="non_exist_rag_retrieval",
|
||||
rag_corpora=[
|
||||
"projects/1096655024998/locations/us-central1/RagCorpora/1234567"
|
||||
],
|
||||
description="Non exist rag retrieval resource name",
|
||||
)
|
||||
|
||||
shell_tool = LangchainTool(ShellTool())
|
||||
|
||||
docs_tool = CrewaiTool(
|
||||
name="directory_read_tool",
|
||||
description="use this to find files for you.",
|
||||
tool=DirectoryReadTool(directory="."),
|
||||
)
|
||||
|
||||
no_schema_agent = Agent(
|
||||
model="gemini-2.5-flash",
|
||||
name="no_schema_agent",
|
||||
instruction="""Just say 'Hi'
|
||||
""",
|
||||
)
|
||||
|
||||
schema_agent = Agent(
|
||||
model="gemini-2.5-flash",
|
||||
name="schema_agent",
|
||||
instruction="""
|
||||
You will be given a test case.
|
||||
Return a list of the received test case appended with '_success' and '_failure' as test_titles
|
||||
""",
|
||||
input_schema=TestCase,
|
||||
output_schema=Test,
|
||||
)
|
||||
|
||||
no_input_schema_agent = Agent(
|
||||
model="gemini-2.5-flash",
|
||||
name="no_input_schema_agent",
|
||||
instruction="""
|
||||
Just return ['Tools_success, Tools_failure']
|
||||
""",
|
||||
output_schema=Test,
|
||||
)
|
||||
|
||||
no_output_schema_agent = Agent(
|
||||
model="gemini-2.5-flash",
|
||||
name="no_output_schema_agent",
|
||||
instruction="""
|
||||
Just say 'Hi'
|
||||
""",
|
||||
input_schema=TestCase,
|
||||
)
|
||||
|
||||
single_function_agent = Agent(
|
||||
model="gemini-2.5-flash",
|
||||
name="single_function_agent",
|
||||
description="An agent that calls a single function",
|
||||
instruction="When calling tools, just return what the tool returns.",
|
||||
tools=[simple_function],
|
||||
)
|
||||
|
||||
root_agent = Agent(
|
||||
model="gemini-2.5-flash",
|
||||
name="tool_agent",
|
||||
description="An agent that can call other tools",
|
||||
instruction="When calling tools, just return what the tool returns.",
|
||||
tools=[
|
||||
simple_function,
|
||||
no_param_function,
|
||||
no_output_function,
|
||||
multiple_param_types_function,
|
||||
throw_error_function,
|
||||
list_str_param_function,
|
||||
return_list_str_function,
|
||||
# complex_function_list_dict,
|
||||
repetitive_call_1,
|
||||
repetitive_call_2,
|
||||
test_case_retrieval,
|
||||
valid_rag_retrieval,
|
||||
invalid_rag_retrieval,
|
||||
non_exist_rag_retrieval,
|
||||
shell_tool,
|
||||
docs_tool,
|
||||
AgentTool(
|
||||
agent=no_schema_agent,
|
||||
),
|
||||
AgentTool(
|
||||
agent=schema_agent,
|
||||
),
|
||||
AgentTool(
|
||||
agent=no_input_schema_agent,
|
||||
),
|
||||
AgentTool(
|
||||
agent=no_output_schema_agent,
|
||||
),
|
||||
],
|
||||
)
|
||||
Binary file not shown.
@@ -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,110 @@
|
||||
# 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.
|
||||
|
||||
# https://github.com/crewAIInc/crewAI-examples/tree/main/trip_planner
|
||||
|
||||
from google.adk import Agent
|
||||
|
||||
# Agent that selects the best city for the trip.
|
||||
identify_agent = Agent(
|
||||
name='identify_agent',
|
||||
description='Select the best city based on weather, season, and prices.',
|
||||
instruction="""
|
||||
Analyze and select the best city for the trip based
|
||||
on specific criteria such as weather patterns, seasonal
|
||||
events, and travel costs. This task involves comparing
|
||||
multiple cities, considering factors like current weather
|
||||
conditions, upcoming cultural or seasonal events, and
|
||||
overall travel expenses.
|
||||
|
||||
Your final answer must be a detailed
|
||||
report on the chosen city, and everything you found out
|
||||
about it, including the actual flight costs, weather
|
||||
forecast and attractions.
|
||||
|
||||
Traveling from: {origin}
|
||||
City Options: {cities}
|
||||
Trip Date: {range}
|
||||
Traveler Interests: {interests}
|
||||
""",
|
||||
)
|
||||
|
||||
# Agent that gathers information about the city.
|
||||
gather_agent = Agent(
|
||||
name='gather_agent',
|
||||
description='Provide the BEST insights about the selected city',
|
||||
instruction="""
|
||||
As a local expert on this city you must compile an
|
||||
in-depth guide for someone traveling there and wanting
|
||||
to have THE BEST trip ever!
|
||||
Gather information about key attractions, local customs,
|
||||
special events, and daily activity recommendations.
|
||||
Find the best spots to go to, the kind of place only a
|
||||
local would know.
|
||||
This guide should provide a thorough overview of what
|
||||
the city has to offer, including hidden gems, cultural
|
||||
hotspots, must-visit landmarks, weather forecasts, and
|
||||
high level costs.
|
||||
|
||||
The final answer must be a comprehensive city guide,
|
||||
rich in cultural insights and practical tips,
|
||||
tailored to enhance the travel experience.
|
||||
|
||||
Trip Date: {range}
|
||||
Traveling from: {origin}
|
||||
Traveler Interests: {interests}
|
||||
""",
|
||||
)
|
||||
|
||||
# Agent that plans the trip.
|
||||
plan_agent = Agent(
|
||||
name='plan_agent',
|
||||
description="""Create the most amazing travel itineraries with budget and
|
||||
packing suggestions for the city""",
|
||||
instruction="""
|
||||
Expand this guide into a full 7-day travel
|
||||
itinerary with detailed per-day plans, including
|
||||
weather forecasts, places to eat, packing suggestions,
|
||||
and a budget breakdown.
|
||||
|
||||
You MUST suggest actual places to visit, actual hotels
|
||||
to stay and actual restaurants to go to.
|
||||
|
||||
This itinerary should cover all aspects of the trip,
|
||||
from arrival to departure, integrating the city guide
|
||||
information with practical travel logistics.
|
||||
|
||||
Your final answer MUST be a complete expanded travel plan,
|
||||
formatted as markdown, encompassing a daily schedule,
|
||||
anticipated weather conditions, recommended clothing and
|
||||
items to pack, and a detailed budget, ensuring THE BEST
|
||||
TRIP EVER. Be specific and give it a reason why you picked
|
||||
each place, what makes them special!
|
||||
|
||||
Trip Date: {range}
|
||||
Traveling from: {origin}
|
||||
Traveler Interests: {interests}
|
||||
""",
|
||||
)
|
||||
|
||||
root_agent = Agent(
|
||||
model='gemini-2.5-flash',
|
||||
name='trip_planner',
|
||||
description='Plan the best trip ever',
|
||||
instruction="""
|
||||
Your goal is to plan the best trip according to information listed above.
|
||||
You describe why did you choose the city, list top 3
|
||||
attractions and provide a detailed itinerary for each day.""",
|
||||
sub_agents=[identify_agent, gather_agent, plan_agent],
|
||||
)
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"criteria": {
|
||||
"response_match_score": 0.5
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"criteria": {
|
||||
"response_match_score": 0.5
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"eval_set_id": "189d6856-9b90-4b9c-bda8-7cec899507ae",
|
||||
"name": "189d6856-9b90-4b9c-bda8-7cec899507ae",
|
||||
"description": null,
|
||||
"eval_cases": [
|
||||
{
|
||||
"eval_id": "tests/integration/fixture/trip_planner_agent/test_files/trip_inquiry_sub_agent.test.json",
|
||||
"conversation": [
|
||||
{
|
||||
"invocation_id": "1c2e8003-d19c-4912-b0ae-17b9d568f8fb",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "Based on my interests, where should I go, Yosemite national park or Los Angeles?"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"file_data": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"inline_data": null,
|
||||
"text": "Given your interests in food, shopping, and museums, Los Angeles would be a better choice than Yosemite National Park. Yosemite is primarily focused on outdoor activities and natural landscapes, while Los Angeles offers a diverse range of culinary experiences, shopping districts, and world-class museums. I will now gather information to create an in-depth guide for your trip to Los Angeles.\n"
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1747339378.484014
|
||||
}
|
||||
],
|
||||
"session_input": {
|
||||
"app_name": "trip_planner_agent",
|
||||
"user_id": "test_user",
|
||||
"state": {
|
||||
"origin": "San Francisco",
|
||||
"interests": "Food, Shopping, Museums",
|
||||
"range": "1000 miles",
|
||||
"cities": ""
|
||||
}
|
||||
},
|
||||
"creation_timestamp": 1747339378.484044
|
||||
}
|
||||
],
|
||||
"creation_timestamp": 1747339378.484056
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
{
|
||||
"eval_set_id": "e7996ccc-16bc-46bf-9a24-0a3ecc3dacd7",
|
||||
"name": "e7996ccc-16bc-46bf-9a24-0a3ecc3dacd7",
|
||||
"description": null,
|
||||
"eval_cases": [
|
||||
{
|
||||
"eval_id": "tests/integration/fixture/trip_planner_agent/trip_inquiry.test.json",
|
||||
"conversation": [
|
||||
{
|
||||
"invocation_id": "d7ff8ec1-290b-48c5-b3aa-05cb8f27b8ae",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"inline_data": null,
|
||||
"file_data": null,
|
||||
"thought_signature": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"text": "Hi, who are you? What can you do?"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"inline_data": null,
|
||||
"file_data": null,
|
||||
"thought_signature": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"text": "I am trip_planner, and my goal is to plan the best trip ever. I can describe why a city was chosen, list its top attractions, and provide a detailed itinerary for each day of the trip.\n"
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1750190885.419684
|
||||
},
|
||||
{
|
||||
"invocation_id": "f515ff57-ff21-488f-ab92-7d7de5bb76fe",
|
||||
"user_content": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"inline_data": null,
|
||||
"file_data": null,
|
||||
"thought_signature": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"text": "I want to travel from San Francisco to an European country in fall next year. I am considering London and Paris. What is your advice?"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"final_response": {
|
||||
"parts": [
|
||||
{
|
||||
"video_metadata": null,
|
||||
"thought": null,
|
||||
"inline_data": null,
|
||||
"file_data": null,
|
||||
"thought_signature": null,
|
||||
"code_execution_result": null,
|
||||
"executable_code": null,
|
||||
"function_call": null,
|
||||
"function_response": null,
|
||||
"text": "Okay, I can help you analyze London and Paris to determine which city is better for your trip next fall. I will consider weather patterns, seasonal events, travel costs (including flights from San Francisco), and your interests (food, shopping, and museums). After gathering this information, I'll provide a detailed report on my chosen city.\n"
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"intermediate_data": {
|
||||
"tool_uses": [
|
||||
{
|
||||
"id": null,
|
||||
"args": {
|
||||
"agent_name": "identify_agent"
|
||||
},
|
||||
"name": "transfer_to_agent"
|
||||
}
|
||||
],
|
||||
"intermediate_responses": []
|
||||
},
|
||||
"creation_timestamp": 1750190885.4197457
|
||||
}
|
||||
],
|
||||
"session_input": {
|
||||
"app_name": "trip_planner_agent",
|
||||
"user_id": "test_user",
|
||||
"state": {
|
||||
"origin": "San Francisco",
|
||||
"interests": "Food, Shopping, Museums",
|
||||
"range": "1000 miles",
|
||||
"cities": ""
|
||||
}
|
||||
},
|
||||
"creation_timestamp": 1750190885.4197533
|
||||
}
|
||||
],
|
||||
"creation_timestamp": 1750190885.4197605
|
||||
}
|
||||
Reference in New Issue
Block a user