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

This commit is contained in:
wehub-resource-sync
2026-07-13 13:25:13 +08:00
commit ec2b666284
2231 changed files with 491535 additions and 0 deletions
@@ -0,0 +1,87 @@
# Daytona Environment Sample
## Overview
A small data analysis agent that uses the `DaytonaEnvironment` with the
`EnvironmentToolset` to download public datasets and analyze them inside a
[Daytona](https://daytona.io) remote sandbox.
Instead of running on the local machine, all commands and file operations
execute in an isolated remote sandbox with internet access. Asked a question,
the agent downloads a public dataset (a GCS-hosted world population /
demographics dataset by default), installs `pandas` on demand, writes a short
analysis script, runs it, and reports the result — all without touching the
user's machine. This makes the sandbox a natural fit for running
model-generated code safely and keeping the host clean.
## Prerequisites
1. Install the `daytona` extra:
```bash
pip install google-adk[daytona]
```
1. Set your Daytona configuration. Get a server and API key by following the
Daytona installation guide (e.g. self-hosted or via Daytona Cloud).
If you are using Daytona Cloud, you only need to set:
```bash
export DAYTONA_API_KEY="your-api-key"
```
If you are using a self-hosted Daytona server, also set:
```bash
export DAYTONA_API_URL="your-api-url"
```
## Sample Inputs
- `Download the world demographics dataset and tell me which country has the largest population.`
The agent downloads the dataset, installs `pandas`, filters to country-level
rows, and finds the maximum. Expected: China (`CN`), ≈ 1.44 billion, just
ahead of India (`IN`) at ≈ 1.38 billion.
- `For the United States, what is the urban vs rural population split?`
A follow-up to the previous turn. Because the sandbox persists across the
session, the agent reuses the already-downloaded CSV and the installed
`pandas` — it only writes and runs a new script. Expected for `US`: urban
≈ 270.7 million vs rural ≈ 57.6 million (out of ≈ 331 million total).
- `Using https://storage.googleapis.com/cloud-samples-data/bigquery/us-states/us-states.csv, how many US states are listed?`
Demonstrates pointing the agent at your own dataset URL instead of the
default.
## Graph
```mermaid
graph TD
User -->|question| Agent[data_analysis_agent]
Agent -->|EnvironmentToolset| Sandbox[DaytonaEnvironment sandbox]
Sandbox -->|download / install / run| Agent
Agent -->|answer| User
```
## How To
The agent is a standalone `Agent` (no workflow graph) wired to a single
`EnvironmentToolset` whose `environment` is a `DaytonaEnvironment`:
```python
from google.adk.integrations.daytona import DaytonaEnvironment
from google.adk.tools.environment import EnvironmentToolset
EnvironmentToolset(
environment=DaytonaEnvironment(timeout=300),
)
```
- `timeout` bounds the sandbox lifetime in seconds.
- By default, it will spin up a sandbox from the built-in default Python snapshot.
If you want to use a custom Docker image instead, you can pass it to the
`image` parameter (e.g. `image="python:3.12"`).
@@ -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.
"""A data analysis agent that runs Python in a Daytona remote sandbox."""
from google.adk import Agent
from google.adk.integrations.daytona import DaytonaEnvironment
from google.adk.tools.environment import EnvironmentToolset
root_agent = Agent(
name="data_analysis_agent",
description=(
"A data analysis agent that downloads public datasets and analyzes"
" them inside a Daytona remote sandbox."
),
instruction="""\
You are a data analysis assistant. You work inside an isolated Daytona remote
sandbox that has internet access, where you can safely download data and run
Python, so you never touch the user's machine.
To analyze a dataset:
1. Download it from the internet into the working directory, e.g. with
`curl -O <url>` or `wget <url>`.
2. Install whatever you need on demand, e.g. `pip install pandas`.
3. Write a short Python script that loads the data and computes the answer.
4. Run the script and report the result, showing the numbers you found.
Prefer writing a script and executing it over guessing. If a command fails,
read the error, fix the script, and try again.
""",
tools=[EnvironmentToolset(environment=DaytonaEnvironment())],
)
@@ -0,0 +1,90 @@
# E2B Environment Sample
## Overview
A small data analysis agent that uses the `E2BEnvironment` with the
`EnvironmentToolset` to download public datasets and analyze them inside an
[E2B](https://e2b.dev) remote sandbox.
Instead of running on the local machine, all commands and file operations
execute in an isolated remote sandbox with internet access. Asked a question,
the agent downloads a public dataset (a GCS-hosted world population /
demographics dataset by default), installs `pandas` on demand, writes a short
analysis script, runs it, and reports the result — all without touching the
user's machine. This makes the sandbox a natural fit for running
model-generated code safely and keeping the host clean.
The sandbox has a bounded time-to-live (`timeout`, in seconds) to cap credit
usage. The TTL is reset on every operation, so an actively used workspace never
expires mid-task; after genuine idle it expires and is transparently recreated
on the next operation (note: workspace state such as installed packages and
files is lost on recreation).
## Prerequisites
1. Install the `e2b` extra:
```bash
pip install google-adk[e2b]
```
1. Set your E2B API key (get one at https://e2b.dev):
```bash
export E2B_API_KEY="your-api-key"
```
## Sample Inputs
- `Download the world demographics dataset and tell me which country has the largest population.`
The agent downloads the dataset, installs `pandas`, filters to country-level
rows, and finds the maximum. Expected: China (`CN`), ≈ 1.44 billion, just
ahead of India (`IN`) at ≈ 1.38 billion.
- `For the United States, what is the urban vs rural population split?`
A follow-up to the previous turn. Because the sandbox persists across the
session, the agent reuses the already-downloaded CSV and the installed
`pandas` — it only writes and runs a new script. Expected for `US`: urban
≈ 270.7 million vs rural ≈ 57.6 million (out of ≈ 331 million total).
- `Using https://storage.googleapis.com/cloud-samples-data/bigquery/us-states/us-states.csv, how many US states are listed?`
Demonstrates pointing the agent at your own dataset URL instead of the
default.
## Graph
```mermaid
graph TD
User -->|question| Agent[data_analysis_agent]
Agent -->|EnvironmentToolset| Sandbox[E2BEnvironment sandbox]
Sandbox -->|download / install / run| Agent
Agent -->|answer| User
```
## How To
The agent is a standalone `Agent` (no workflow graph) wired to a single
`EnvironmentToolset` whose `environment` is an `E2BEnvironment`:
```python
from google.adk.integrations.e2b import E2BEnvironment
from google.adk.tools.environment import EnvironmentToolset
EnvironmentToolset(
environment=E2BEnvironment(image="base", timeout=300),
)
```
- `image` selects the E2B template (defaults to the public `base` template).
- `timeout` bounds the sandbox lifetime in seconds to cap credit usage; it is
reset on every operation.
The default GCS-hosted demographics CSV is a standard CSV with a header row.
Each row is one location identified by `location_key`: country-level rows use a
two-letter ISO code (e.g. `US`, `CN`), while subregions use keys containing an
underscore (e.g. `US_CA`). The agent's instruction documents this schema — in
particular, to filter out underscore keys when a question is about countries —
so the generated analysis script parses and aggregates the file correctly.
@@ -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,52 @@
# 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.
"""A data analysis agent that runs Python in an E2B remote sandbox."""
from google.adk import Agent
from google.adk.integrations.e2b import E2BEnvironment
from google.adk.tools.environment import EnvironmentToolset
root_agent = Agent(
name="data_analysis_agent",
description=(
"A data analysis agent that downloads public datasets and analyzes"
" them inside an E2B remote sandbox."
),
instruction="""\
You are a data analysis assistant. You work inside an isolated E2B remote
sandbox that has internet access, where you can safely download data and run
Python, so you never touch the user's machine.
To analyze a dataset:
1. Download it from the internet into the working directory, e.g. with
`curl -O <url>` or `wget <url>`. If the user does not give a URL, use the
public world demographics dataset hosted on Google Cloud Storage at
https://storage.googleapis.com/covid19-open-data/v3/demographics.csv
2. Install whatever you need on demand, e.g. `pip install pandas`.
3. Write a short Python script that loads the data and computes the answer.
4. Run the script and report the result, showing the numbers you found.
Notes on the demographics CSV above: it is a proper CSV with a header row.
Each row is one location, identified by `location_key`. Country-level rows use
a two-letter ISO code (e.g. `US`, `CN`, `IN`); subregions use keys containing
an underscore (e.g. `US_CA`), so filter those out when you want countries only.
Useful columns include `population`, `population_male`, `population_female`,
`population_urban`, `population_rural`, and `population_density`.
Prefer writing a script and executing it over guessing. If a command fails,
read the error, fix the script, and try again.
""",
tools=[EnvironmentToolset(environment=E2BEnvironment())],
)
@@ -0,0 +1,21 @@
# Local Environment Sample
This sample demonstrates how to use the `LocalEnvironment` with the `EnvironmentToolset` to allow an agent to interact with the local filesystem and execute commands.
## Description
The agent is configured with the `EnvironmentToolset`, which provides tools for file I/O (reading, writing) and command execution within a local environment. This allows the agent to perform tasks that involve creating files, modifying them, and running local scripts or commands.
## Sample Usage
You can interact with the agent by providing prompts that require file operations and command execution.
### Example Prompt
> "Write a Python file named `hello.py` to the working directory that prints 'Hello from ADK!'. Then read the file to verify its contents, and finally execute it using a command."
### Expected Behavior
1. **Write File**: The agent uses a tool to write `hello.py` with the content `print("Hello from ADK!")`.
1. **Read File**: The agent uses a tool to read `hello.py` and verify the content.
1. **Execute Command**: The agent uses a tool to run `python3 hello.py` and returns the output.
@@ -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,34 @@
# 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.adk.environment import LocalEnvironment
from google.adk.tools.environment import EnvironmentToolset
root_agent = Agent(
model="gemini-2.5-pro",
name="local_environment_agent",
description="A simple agent that demonstrates local environment usage.",
instruction="""
You are a helpful AI assistant that can use the local environment to
execute commands and file I/O. Follow the rules of the environment and the
user's instructions.
""",
tools=[
EnvironmentToolset(
environment=LocalEnvironment(),
),
],
)
@@ -0,0 +1,24 @@
# Local Environment Skill Sample
This sample demonstrates how to use the `LocalEnvironment` with the `EnvironmentToolset` to allow an agent to manually discover and load skills from the environment, rather than using the pre-configured `SkillToolset`.
## Description
The agent is configured with the `EnvironmentToolset` and is initialized with a `LocalEnvironment` pointing to the agent's directory.
Instead of having skills pre-loaded, the agent uses system instructions that guide it to search for skills in the `skills/` folder and load them by reading their `SKILL.md` files using the `ReadFile` tool.
This demonstrates a "manual skill loading" pattern where the agent can acquire new capabilities dynamically by reading instructions from the environment.
## Sample Usage
You can interact with the agent by providing prompts that require a specific skill (like weather).
### Example Prompt
> "Can you check the weather in Sunnyvale?"
### Expected Behavior
1. **Find Skill**: The agent uses the `Execute` tool to search for all available skills by running `find skills -name SKILL.md`.
1. **Load Skill**: The agent identifies the relevant skill and uses the `ReadFile` tool to read its `SKILL.md` file.
1. **Execute Skill**: The agent follows the instructions in the skill file (e.g., reading references or running scripts) to answer the user's request.
@@ -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,63 @@
# 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 pathlib
from google.adk import Agent
from google.adk.environment import LocalEnvironment
from google.adk.tools.environment import EnvironmentToolset
def get_wind_speed(location: str) -> str:
"""Returns the current wind speed for a given location."""
return f"The wind speed in {location} is 10 mph."
BASE_INSTRUCTION = (
"You are a helpful AI assistant that can use the local environment to"
" execute commands and file I/O."
)
SKILL_USAGE_INSTRUCTION = """\
[SKILLS ACCESS]
You have access to specialized skills stored in the environment's `skills/` folder.
Each skill is a folder containing a `SKILL.md` file with instructions.
[MANDATORY PROCEDURE]
Before declaring that you cannot perform a task or answer a question (especially for domain-specific queries like weather), you MUST:
1. Use the `Execute` tool to search for all available skills by running: `find skills -name SKILL.md`
2. Review the list of found skills to see if any are relevant to the user's request.
3. If a relevant skill is found, use the `ReadFile` tool to read its `SKILL.md` file.
4. Follow the instructions in that file to complete the request.
*CRITICAL NOTE ON PATHS:* All file and script paths mentioned inside a `SKILL.md` file (e.g., `references/...` or `scripts/...`) are RELATIVE to that specific skill's folder. You MUST resolve them by prepending the skill's folder path (e.g., if the skill is at `skills/weather-skill/`, you must read `skills/weather-skill/references/weather_info.md`).
Failure to check the `skills/` directory before stating you cannot help is unacceptable.\
"""
root_agent = Agent(
model="gemini-2.5-pro",
name="local_environment_skill_agent",
description=(
"An agent that uses local environment tools to load and use skills."
),
instruction=f"{BASE_INSTRUCTION}\n\n{SKILL_USAGE_INSTRUCTION}",
tools=[
EnvironmentToolset(
environment=LocalEnvironment(
working_dir=pathlib.Path(__file__).parent
),
),
get_wind_speed,
],
)
@@ -0,0 +1,8 @@
______________________________________________________________________
## name: weather-skill description: A skill that provides weather information based on reference data.
Step 1: Check 'references/weather_info.md' for the current weather.
Step 2: If humidity is requested, use run 'scripts/get_humidity.py' with the `location` argument.
Step 3: If wind speed is requested, use the `get_wind_speed` tool.
Step 4: Provide the update to the user.
@@ -0,0 +1,17 @@
# Weather Information
- **Location:** San Francisco, CA
- **Condition:** Sunny ☀️
- **Temperature:** 72°F (22°C)
- **Forecast:** Clear skies all day.
- **Location:** Sunnyvale, CA
- **Condition:** Sunny ☀️
- **Temperature:** 75°F (24°C)
- **Forecast:** Warm and sunny.
@@ -0,0 +1,29 @@
# 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 argparse
def get_humidity(location: str) -> str:
"""Fetch live humidity for a given location. (Simulated)"""
print(f"Fetching live humidity for {location}...")
return "45% (Simulated)"
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--location", type=str, default="Mountain View")
args = parser.parse_args()
print(get_humidity(args.location))
@@ -0,0 +1,115 @@
# ADK Skills Agent Sample
## Overview
This sample demonstrates how to use **Skills** and the **SkillToolset** in ADK.
Skills are specialized folders of instructions, reference materials, assets, and scripts that extend an agent's capabilities. The agent can dynamically search for, load, and run resources/scripts from these skills depending on the user's query.
This sample showcases:
1. **Programmatic Skills**: Creating a skill directly within Python (`support-hours-skill`).
1. **Directory-based Skills**: Loading a skill from a directory structure (`weather-skill`).
1. **Skill Metadata & Additional Tools**: Declaring that a skill requires specific tools, making them dynamically active only when that skill is loaded.
1. **Script Execution**: Executing a Python script inside a skill using a code executor.
## Sample Inputs
- `What are the support hours for Tokyo?`
*Triggers the support-hours-skill which checks get_timezone and reads support_policy.txt*
- `What is the current weather in SF?`
*Loads weather-skill and reads weather_info.md reference file*
- `Can you fetch the current humidity for Mountain View?`
*Executes scripts/get_humidity.py via run_skill_script*
- `What is the wind speed in Seattle?`
*Loads weather-skill which dynamically activates and calls get_wind_speed*
## Graph
```mermaid
graph TD
Agent[Agent: skills_agent] --> Toolset[SkillToolset]
Toolset --> Skill1[support-hours-skill]
Toolset --> Skill2[weather-skill]
Skill1 --> Resource1["Resource: support_policy.txt"]
Skill1 --> Tool1["Dynamic Tool: get_timezone"]
Skill2 --> Resource2["Resource: weather_info.md"]
Skill2 --> Script1["Script: get_humidity.py"]
Skill2 --> Tool2["Dynamic Tool: get_wind_speed"]
```
## How To
### 1. Declaring a Skill Programmatically
You can declare a skill in Python code using `models.Skill`:
```python
from google.adk.skills import models
support_hours_skill = models.Skill(
frontmatter=models.Frontmatter(
name="support-hours-skill",
description="A skill to check customer support hours...",
metadata={"adk_additional_tools": ["get_timezone"]},
),
instructions="Step 1: Look up the timezone... Step 2: Read 'references/support_policy.txt'...",
resources=models.Resources(
references={
"support_policy.txt": "Customer support is available Monday through Friday...",
},
),
)
```
### 2. Loading a Skill from a Directory
Skills can be organized as folders. Each folder must contain a `SKILL.md` file. The folder structure typically looks like:
```
weather-skill/
├── SKILL.md
├── references/
│ └── weather_info.md
└── scripts/
└── get_humidity.py
```
To load a skill from a directory:
```python
from google.adk.skills import load_skill_from_dir
weather_skill = load_skill_from_dir(
pathlib.Path(__file__).parent / "skills" / "weather-skill"
)
```
### 3. Registering a SkillToolset
Use `SkillToolset` to bundle all your skills and any dynamic tools. Then pass this toolset to your agent's `tools` list:
```python
from google.adk.tools.skill_toolset import SkillToolset
from google.adk.code_executors.unsafe_local_code_executor import UnsafeLocalCodeExecutor
my_skill_toolset = SkillToolset(
skills=[support_hours_skill, weather_skill],
additional_tools=[GetTimezoneTool(), get_wind_speed],
code_executor=UnsafeLocalCodeExecutor(),
)
root_agent = Agent(
name="skills_agent",
tools=[my_skill_toolset],
)
```
@@ -0,0 +1,17 @@
# 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 __future__ import annotations
from . import agent
@@ -0,0 +1,108 @@
# 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 __future__ import annotations
import pathlib
from google.adk.agents import Agent
from google.adk.code_executors.unsafe_local_code_executor import UnsafeLocalCodeExecutor
from google.adk.skills import load_skill_from_dir
from google.adk.skills import models
from google.adk.tools.base_tool import BaseTool
from google.adk.tools.skill_toolset import SkillToolset
from google.genai import types
class GetTimezoneTool(BaseTool):
"""A tool to get the timezone for a given location."""
def __init__(self):
super().__init__(
name="get_timezone",
description="Returns the timezone for a given location.",
)
def _get_declaration(self) -> types.FunctionDeclaration | None:
return types.FunctionDeclaration(
name=self.name,
description=self.description,
parameters_json_schema={
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The location to get the timezone for.",
},
},
"required": ["location"],
},
)
async def run_async(self, *, args: dict, tool_context) -> str:
return f"The timezone for {args['location']} is UTC-08:00."
def get_wind_speed(location: str) -> str:
"""Returns the current wind speed for a given location."""
return f"The wind speed in {location} is 10 mph."
# 1. Define a skill programmatically
support_hours_skill = models.Skill(
frontmatter=models.Frontmatter(
name="support-hours-skill",
description=(
"A skill to check customer support hours for a given location."
),
metadata={"adk_additional_tools": ["get_timezone"]},
),
instructions=(
"Step 1: Look up the timezone for the user's location using"
" 'get_timezone'. Step 2: Read 'references/support_policy.txt' to"
" understand support hours policy. Step 3: Explain the support hours"
" relative to the location's timezone."
),
resources=models.Resources(
references={
"support_policy.txt": (
"Customer support is available Monday through Friday, "
"from 9:00 AM to 5:00 PM local time."
),
},
),
)
# 2. Load a skill from a directory
weather_skill = load_skill_from_dir(
pathlib.Path(__file__).parent / "skills" / "weather-skill"
)
# 3. Combine them into a SkillToolset
# NOTE: UnsafeLocalCodeExecutor has security concerns and should NOT
# be used in production environments.
my_skill_toolset = SkillToolset(
skills=[support_hours_skill, weather_skill],
additional_tools=[GetTimezoneTool(), get_wind_speed],
code_executor=UnsafeLocalCodeExecutor(),
)
# 4. Set up the agent with the toolset
root_agent = Agent(
name="skills_agent",
description="An agent that can use specialized skills.",
tools=[
my_skill_toolset,
],
)
@@ -0,0 +1,12 @@
---
name: weather-skill
description: A skill that provides weather information based on reference data and scripts.
metadata:
adk_additional_tools:
- get_wind_speed
---
Step 1: Check 'references/weather_info.md' for the current weather.
Step 2: If humidity is requested, run 'scripts/get_humidity.py' with the `location` argument.
Step 3: If wind speed is requested, use the `get_wind_speed` tool.
Step 4: Provide the complete weather update to the user.
@@ -0,0 +1,6 @@
# Weather Information
- **Location:** San Francisco, CA
- **Condition:** Sunny ☀️
- **Temperature:** 72°F (22°C)
- **Forecast:** Clear skies all day.
@@ -0,0 +1,29 @@
# 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 argparse
def get_humidity(location: str) -> str:
"""Fetch live humidity for a given location. (Simulated)"""
print(f"Fetching live humidity for {location}...")
return "45% (Simulated)"
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--location", type=str, default="Mountain View")
args = parser.parse_args()
print(get_humidity(args.location))
@@ -0,0 +1,201 @@
{
"events": [
{
"author": "user",
"content": {
"parts": [
{
"text": "Can you fetch the current humidity for Mountain View?"
}
],
"role": "user"
},
"id": "e-1",
"invocationId": "i-1",
"nodeInfo": {
"path": ""
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"functionCall": {
"args": {},
"id": "fc-1",
"name": "list_skills"
}
}
],
"role": "model"
},
"finishReason": "STOP",
"id": "e-2",
"invocationId": "i-1",
"longRunningToolIds": [],
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"functionResponse": {
"id": "fc-1",
"name": "list_skills",
"response": {
"result": "<available_skills>\n<skill>\n<name>\nsupport-hours-skill\n</name>\n<description>\nA skill to check customer support hours for a given location.\n</description>\n</skill>\n<skill>\n<name>\nweather-skill\n</name>\n<description>\nA skill that provides weather information based on reference data and scripts.\n</description>\n</skill>\n</available_skills>"
}
}
}
],
"role": "user"
},
"id": "e-3",
"invocationId": "i-1",
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"functionCall": {
"args": {
"skill_name": "weather-skill"
},
"id": "fc-2",
"name": "load_skill"
}
}
],
"role": "model"
},
"finishReason": "STOP",
"id": "e-4",
"invocationId": "i-1",
"longRunningToolIds": [],
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"actions": {
"stateDelta": {
"_adk_activated_skill_skills_agent": [
"weather-skill"
]
}
},
"author": "skills_agent",
"content": {
"parts": [
{
"functionResponse": {
"id": "fc-2",
"name": "load_skill",
"response": {
"frontmatter": {
"allowed_tools": null,
"compatibility": null,
"description": "A skill that provides weather information based on reference data and scripts.",
"license": null,
"metadata": {
"adk_additional_tools": [
"get_wind_speed"
]
},
"name": "weather-skill"
},
"instructions": "Step 1: Check 'references/weather_info.md' for the current weather.\nStep 2: If humidity is requested, run 'scripts/get_humidity.py' with the `location` argument.\nStep 3: If wind speed is requested, use the `get_wind_speed` tool.\nStep 4: Provide the complete weather update to the user.",
"skill_name": "weather-skill"
}
}
}
],
"role": "user"
},
"id": "e-5",
"invocationId": "i-1",
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"functionCall": {
"args": {
"args": {
"location": "Mountain View"
},
"file_path": "scripts/get_humidity.py",
"skill_name": "weather-skill"
},
"id": "fc-3",
"name": "run_skill_script"
}
}
],
"role": "model"
},
"finishReason": "STOP",
"id": "e-6",
"invocationId": "i-1",
"longRunningToolIds": [],
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"functionResponse": {
"id": "fc-3",
"name": "run_skill_script",
"response": {
"file_path": "scripts/get_humidity.py",
"skill_name": "weather-skill",
"status": "success",
"stderr": "",
"stdout": "Fetching live humidity for Mountain View...\n45% (Simulated)\n"
}
}
}
],
"role": "user"
},
"id": "e-7",
"invocationId": "i-1",
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"text": "The current humidity in Mountain View is 45% (Simulated)."
}
],
"role": "model"
},
"finishReason": "STOP",
"id": "e-8",
"invocationId": "i-1",
"nodeInfo": {
"path": "skills_agent@1"
}
}
]
}
@@ -0,0 +1,196 @@
{
"events": [
{
"author": "user",
"content": {
"parts": [
{
"text": "What is the current weather in SF?"
}
],
"role": "user"
},
"id": "e-1",
"invocationId": "i-1",
"nodeInfo": {
"path": ""
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"functionCall": {
"args": {},
"id": "fc-1",
"name": "list_skills"
}
}
],
"role": "model"
},
"finishReason": "STOP",
"id": "e-2",
"invocationId": "i-1",
"longRunningToolIds": [],
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"functionResponse": {
"id": "fc-1",
"name": "list_skills",
"response": {
"result": "<available_skills>\n<skill>\n<name>\nsupport-hours-skill\n</name>\n<description>\nA skill to check customer support hours for a given location.\n</description>\n</skill>\n<skill>\n<name>\nweather-skill\n</name>\n<description>\nA skill that provides weather information based on reference data and scripts.\n</description>\n</skill>\n</available_skills>"
}
}
}
],
"role": "user"
},
"id": "e-3",
"invocationId": "i-1",
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"functionCall": {
"args": {
"skill_name": "weather-skill"
},
"id": "fc-2",
"name": "load_skill"
}
}
],
"role": "model"
},
"finishReason": "STOP",
"id": "e-4",
"invocationId": "i-1",
"longRunningToolIds": [],
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"actions": {
"stateDelta": {
"_adk_activated_skill_skills_agent": [
"weather-skill"
]
}
},
"author": "skills_agent",
"content": {
"parts": [
{
"functionResponse": {
"id": "fc-2",
"name": "load_skill",
"response": {
"frontmatter": {
"allowed_tools": null,
"compatibility": null,
"description": "A skill that provides weather information based on reference data and scripts.",
"license": null,
"metadata": {
"adk_additional_tools": [
"get_wind_speed"
]
},
"name": "weather-skill"
},
"instructions": "Step 1: Check 'references/weather_info.md' for the current weather.\nStep 2: If humidity is requested, run 'scripts/get_humidity.py' with the `location` argument.\nStep 3: If wind speed is requested, use the `get_wind_speed` tool.\nStep 4: Provide the complete weather update to the user.",
"skill_name": "weather-skill"
}
}
}
],
"role": "user"
},
"id": "e-5",
"invocationId": "i-1",
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"functionCall": {
"args": {
"file_path": "references/weather_info.md",
"skill_name": "weather-skill"
},
"id": "fc-3",
"name": "load_skill_resource"
}
}
],
"role": "model"
},
"finishReason": "STOP",
"id": "e-6",
"invocationId": "i-1",
"longRunningToolIds": [],
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"functionResponse": {
"id": "fc-3",
"name": "load_skill_resource",
"response": {
"content": "# Weather Information\n\n- **Location:** San Francisco, CA\n- **Condition:** Sunny \u2600\ufe0f\n- **Temperature:** 72\u00b0F (22\u00b0C)\n- **Forecast:** Clear skies all day.\n",
"file_path": "references/weather_info.md",
"skill_name": "weather-skill"
}
}
}
],
"role": "user"
},
"id": "e-7",
"invocationId": "i-1",
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"text": "The current weather in San Francisco, CA is Sunny with a temperature of 72\u00b0F (22\u00b0C). The forecast is clear skies all day."
}
],
"role": "model"
},
"finishReason": "STOP",
"id": "e-8",
"invocationId": "i-1",
"nodeInfo": {
"path": "skills_agent@1"
}
}
]
}
@@ -0,0 +1,242 @@
{
"events": [
{
"author": "user",
"content": {
"parts": [
{
"text": "What are the support hours for Tokyo?"
}
],
"role": "user"
},
"id": "e-1",
"invocationId": "i-1",
"nodeInfo": {
"path": ""
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"functionCall": {
"args": {},
"id": "fc-1",
"name": "list_skills"
}
}
],
"role": "model"
},
"finishReason": "STOP",
"id": "e-2",
"invocationId": "i-1",
"longRunningToolIds": [],
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"functionResponse": {
"id": "fc-1",
"name": "list_skills",
"response": {
"result": "<available_skills>\n<skill>\n<name>\nsupport-hours-skill\n</name>\n<description>\nA skill to check customer support hours for a given location.\n</description>\n</skill>\n<skill>\n<name>\nweather-skill\n</name>\n<description>\nA skill that provides weather information based on reference data and scripts.\n</description>\n</skill>\n</available_skills>"
}
}
}
],
"role": "user"
},
"id": "e-3",
"invocationId": "i-1",
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"functionCall": {
"args": {
"skill_name": "support-hours-skill"
},
"id": "fc-2",
"name": "load_skill"
}
}
],
"role": "model"
},
"finishReason": "STOP",
"id": "e-4",
"invocationId": "i-1",
"longRunningToolIds": [],
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"actions": {
"stateDelta": {
"_adk_activated_skill_skills_agent": [
"support-hours-skill"
]
}
},
"author": "skills_agent",
"content": {
"parts": [
{
"functionResponse": {
"id": "fc-2",
"name": "load_skill",
"response": {
"frontmatter": {
"allowed_tools": null,
"compatibility": null,
"description": "A skill to check customer support hours for a given location.",
"license": null,
"metadata": {
"adk_additional_tools": [
"get_timezone"
]
},
"name": "support-hours-skill"
},
"instructions": "Step 1: Look up the timezone for the user's location using 'get_timezone'. Step 2: Read 'references/support_policy.txt' to understand support hours policy. Step 3: Explain the support hours relative to the location's timezone.",
"skill_name": "support-hours-skill"
}
}
}
],
"role": "user"
},
"id": "e-5",
"invocationId": "i-1",
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"functionCall": {
"args": {
"location": "Tokyo"
},
"id": "fc-3",
"name": "get_timezone"
}
}
],
"role": "model"
},
"finishReason": "STOP",
"id": "e-6",
"invocationId": "i-1",
"longRunningToolIds": [],
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"functionResponse": {
"id": "fc-3",
"name": "get_timezone",
"response": {
"result": "The timezone for Tokyo is UTC-08:00."
}
}
}
],
"role": "user"
},
"id": "e-7",
"invocationId": "i-1",
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"functionCall": {
"args": {
"file_path": "references/support_policy.txt",
"skill_name": "support-hours-skill"
},
"id": "fc-4",
"name": "load_skill_resource"
}
}
],
"role": "model"
},
"finishReason": "STOP",
"id": "e-8",
"invocationId": "i-1",
"longRunningToolIds": [],
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"functionResponse": {
"id": "fc-4",
"name": "load_skill_resource",
"response": {
"content": "Customer support is available Monday through Friday, from 9:00 AM to 5:00 PM local time.",
"file_path": "references/support_policy.txt",
"skill_name": "support-hours-skill"
}
}
}
],
"role": "user"
},
"id": "e-9",
"invocationId": "i-1",
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"text": "Customer support in Tokyo is available Monday through Friday, from 9:00 AM to 5:00 PM local time (UTC-08:00)."
}
],
"role": "model"
},
"finishReason": "STOP",
"id": "e-10",
"invocationId": "i-1",
"nodeInfo": {
"path": "skills_agent@1"
}
}
]
}
@@ -0,0 +1,193 @@
{
"events": [
{
"author": "user",
"content": {
"parts": [
{
"text": "What is the wind speed in Seattle?"
}
],
"role": "user"
},
"id": "e-1",
"invocationId": "i-1",
"nodeInfo": {
"path": ""
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"functionCall": {
"args": {},
"id": "fc-1",
"name": "list_skills"
}
}
],
"role": "model"
},
"finishReason": "STOP",
"id": "e-2",
"invocationId": "i-1",
"longRunningToolIds": [],
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"functionResponse": {
"id": "fc-1",
"name": "list_skills",
"response": {
"result": "<available_skills>\n<skill>\n<name>\nsupport-hours-skill\n</name>\n<description>\nA skill to check customer support hours for a given location.\n</description>\n</skill>\n<skill>\n<name>\nweather-skill\n</name>\n<description>\nA skill that provides weather information based on reference data and scripts.\n</description>\n</skill>\n</available_skills>"
}
}
}
],
"role": "user"
},
"id": "e-3",
"invocationId": "i-1",
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"functionCall": {
"args": {
"skill_name": "weather-skill"
},
"id": "fc-2",
"name": "load_skill"
}
}
],
"role": "model"
},
"finishReason": "STOP",
"id": "e-4",
"invocationId": "i-1",
"longRunningToolIds": [],
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"actions": {
"stateDelta": {
"_adk_activated_skill_skills_agent": [
"weather-skill"
]
}
},
"author": "skills_agent",
"content": {
"parts": [
{
"functionResponse": {
"id": "fc-2",
"name": "load_skill",
"response": {
"frontmatter": {
"allowed_tools": null,
"compatibility": null,
"description": "A skill that provides weather information based on reference data and scripts.",
"license": null,
"metadata": {
"adk_additional_tools": [
"get_wind_speed"
]
},
"name": "weather-skill"
},
"instructions": "Step 1: Check 'references/weather_info.md' for the current weather.\nStep 2: If humidity is requested, run 'scripts/get_humidity.py' with the `location` argument.\nStep 3: If wind speed is requested, use the `get_wind_speed` tool.\nStep 4: Provide the complete weather update to the user.",
"skill_name": "weather-skill"
}
}
}
],
"role": "user"
},
"id": "e-5",
"invocationId": "i-1",
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"functionCall": {
"args": {
"location": "Seattle"
},
"id": "fc-3",
"name": "get_wind_speed"
}
}
],
"role": "model"
},
"finishReason": "STOP",
"id": "e-6",
"invocationId": "i-1",
"longRunningToolIds": [],
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"functionResponse": {
"id": "fc-3",
"name": "get_wind_speed",
"response": {
"result": "The wind speed in Seattle is 10 mph."
}
}
}
],
"role": "user"
},
"id": "e-7",
"invocationId": "i-1",
"nodeInfo": {
"path": "skills_agent@1"
}
},
{
"author": "skills_agent",
"content": {
"parts": [
{
"text": "The wind speed in Seattle is 10 mph."
}
],
"role": "model"
},
"finishReason": "STOP",
"id": "e-8",
"invocationId": "i-1",
"nodeInfo": {
"path": "skills_agent@1"
}
}
]
}
@@ -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,100 @@
# 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.
"""Example agent demonstrating the use of SkillToolset."""
import pathlib
from google.adk import Agent
from google.adk.code_executors.unsafe_local_code_executor import UnsafeLocalCodeExecutor
from google.adk.skills import load_skill_from_dir
from google.adk.skills import models
from google.adk.tools.base_tool import BaseTool
from google.adk.tools.skill_toolset import SkillToolset
from google.genai import types
class GetTimezoneTool(BaseTool):
"""A tool to get the timezone for a given location."""
def __init__(self):
super().__init__(
name="get_timezone",
description="Returns the timezone for a given location.",
)
def _get_declaration(self) -> types.FunctionDeclaration | None:
return types.FunctionDeclaration(
name=self.name,
description=self.description,
parameters_json_schema={
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The location to get the timezone for.",
},
},
"required": ["location"],
},
)
async def run_async(self, *, args: dict, tool_context) -> str:
return f"The timezone for {args['location']} is UTC+00:00."
def get_wind_speed(location: str) -> str:
"""Returns the current wind speed for a given location."""
return f"The wind speed in {location} is 10 mph."
greeting_skill = models.Skill(
frontmatter=models.Frontmatter(
name="greeting-skill",
description=(
"A friendly greeting skill that can say hello to a specific person."
),
metadata={"adk_additional_tools": ["get_timezone"]},
),
instructions=(
"Step 1: Read the 'references/hello_world.txt' file to understand how"
" to greet the user. Step 2: Return a greeting based on the reference."
),
resources=models.Resources(
references={
"hello_world.txt": "Hello! 👋👋👋 So glad to have you here! ✨✨✨",
"example.md": "This is an example reference.",
},
),
)
weather_skill = load_skill_from_dir(
pathlib.Path(__file__).parent / "skills" / "weather-skill"
)
# WARNING: UnsafeLocalCodeExecutor has security concerns and should NOT
# be used in production environments.
my_skill_toolset = SkillToolset(
skills=[greeting_skill, weather_skill],
additional_tools=[GetTimezoneTool(), get_wind_speed],
code_executor=UnsafeLocalCodeExecutor(),
)
root_agent = Agent(
name="skill_user_agent",
description="An agent that can use specialized skills.",
tools=[
my_skill_toolset,
],
)
@@ -0,0 +1,12 @@
---
name: weather-skill
description: A skill that provides weather information based on reference data.
metadata:
adk_additional_tools:
- get_wind_speed
---
Step 1: Check 'references/weather_info.md' for the current weather.
Step 2: If humidity is requested, use run 'scripts/get_humidity.py' with the `location` argument.
Step 3: If wind speed is requested, use the `get_wind_speed` tool.
Step 4: Provide the update to the user.
@@ -0,0 +1,6 @@
# Weather Information
- **Location:** San Francisco, CA
- **Condition:** Sunny ☀️
- **Temperature:** 72°F (22°C)
- **Forecast:** Clear skies all day.
@@ -0,0 +1,29 @@
# 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 argparse
def get_humidity(location: str) -> str:
"""Fetch live humidity for a given location. (Simulated)"""
print(f"Fetching live humidity for {location}...")
return "45% (Simulated)"
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--location", type=str, default="Mountain View")
args = parser.parse_args()
print(get_humidity(args.location))
@@ -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,102 @@
# 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.
"""Example agent demonstrating the use of SkillToolset with GCS.
Set the following environment variables before running:
SAMPLE_SKILLS_SANDBOX_RESOURCE_NAME="projects/{PROJECT_NUMBER}/locations/{LOCATION}/reasoningEngines/{ENGINE_ID}/sandboxEnvironments/{SANDBOX_ID}"
SAMPLE_SKILLS_AGENT_ENGINE_RESOURCE_NAME="projects/{PROJECT_NUMBER}/locations/{LOCATION}/reasoningEngines/{ENGINE_ID}"
Go to parent directory and run with `adk web --host=0.0.0.0`.
"""
import asyncio
import logging
import os
from google.adk import Agent
from google.adk import Runner
from google.adk.code_executors.agent_engine_sandbox_code_executor import AgentEngineSandboxCodeExecutor
from google.adk.plugins import LoggingPlugin
from google.adk.skills import list_skills_in_gcs_dir
from google.adk.skills import load_skill_from_gcs_dir
from google.adk.tools.skill_toolset import SkillToolset
# Define the GCS bucket and skills prefix
BUCKET_NAME = "sample-skills"
SKILLS_PREFIX = "static-skills"
logging.info("Loading skills from gs://%s/%s...", BUCKET_NAME, SKILLS_PREFIX)
# List and load skills from GCS
skills = []
try:
available_skills = list_skills_in_gcs_dir(
bucket_name=BUCKET_NAME, skills_base_path=SKILLS_PREFIX
)
for skill_id in available_skills.keys():
skills.append(
load_skill_from_gcs_dir(
bucket_name=BUCKET_NAME,
skills_base_path=SKILLS_PREFIX,
skill_id=skill_id,
)
)
logging.info("Loaded %d skills successfully.", len(skills))
except Exception as e: # pylint: disable=broad-exception-caught
logging.error("Failed to load skills from GCS: %s", e)
# Create the SkillToolset
my_skill_toolset = SkillToolset(skills=skills)
# Create the Agent
root_agent = Agent(
model="gemini-3-flash-preview",
name="skill_user_agent",
description="An agent that can use specialized skills loaded from GCS.",
tools=[
my_skill_toolset,
],
code_executor=AgentEngineSandboxCodeExecutor(
sandbox_resource_name=os.getenv("SAMPLE_SKILLS_SANDBOX_RESOURCE_NAME"),
agent_engine_resource_name=os.getenv(
"SAMPLE_SKILLS_AGENT_ENGINE_RESOURCE_NAME"
),
),
)
async def main():
# Initialize the plugins
logging_plugin = LoggingPlugin()
# Create a Runner
runner = Runner(
agents=[root_agent],
plugins=[logging_plugin],
)
# Example run
print("Agent initialized with GCS skills. Sending a test prompt...")
# You can replace this with an interactive loop if needed.
responses = await runner.run(
user_input="Hello! What skills do you have access to?"
)
if responses and responses[-1].content and responses[-1].content.parts:
print(f"\nResponse: {responses[-1].content.parts[0].text}")
if __name__ == "__main__":
asyncio.run(main())