feat(samples): add sample for skill activation via environment tools
Merge https://github.com/google/adk-python/pull/5218 ### Link to Issue or Description of Change **1. Link to an existing issue (if applicable):** (None, this is a new sample) **2. Or, if no issue exists, describe the change:** **Problem:** There was no sample demonstrating how to use `LocalEnvironment` with `EnvironmentToolset` for manual skill activation (discovering and loading skills by reading files) instead of using the pre-configured `SkillToolset`. **Solution:** Created a new sample `local_environment_skill` that demonstrates this pattern: - Agent is instructed to find skills in `skills/` folder using the `find` command. - Agent loads skills by reading `SKILL.md` using the `ReadFile` tool. - Added a weather skill sample and reference data to demonstrate usage. ### Testing Plan **Unit Tests:** *(This is a sample/demonstration, so no new unit tests were added for the framework itself.)* **Manual End-to-End (E2E) Tests:** 1. Ran `adk web contributing/samples`. 2. Interacted with the agent in the Web UI. 3. Prompt: "Can you check the weather in Sunnyvale?" 4. Verified that the agent successfully used `find` to locate the skill, read `SKILL.md`, read `weather_info.md`, and answered correctly. ### Checklist - [x] I have read the [CONTRIBUTING.md](https://github.com/google/adk-python/blob/main/CONTRIBUTING.md) document. - [x] I have performed a self-review of my own code. - [x] I have commented my code, particularly in hard-to-understand areas. - [x] I have manually tested my changes end-to-end. COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/5218 from google:samples/local_env_skill 39404309e8b9237a279ecd6ed14bb87eb84098f0 PiperOrigin-RevId: 897229752
This commit is contained in:
committed by
Copybara-Service
parent
cb4dd42eff
commit
2cbb523069
@@ -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`.
|
||||
2. **Load Skill**: The agent identifies the relevant skill and uses the `ReadFile` tool to read its `SKILL.md` file.
|
||||
3. **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,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.
|
||||
import pathlib
|
||||
|
||||
from google.adk import Agent
|
||||
from google.adk.environment import LocalEnvironment
|
||||
from google.adk.tools.base_tool import BaseTool
|
||||
from google.adk.tools.environment import EnvironmentToolset
|
||||
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."
|
||||
|
||||
|
||||
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
|
||||
),
|
||||
),
|
||||
GetTimezoneTool(),
|
||||
get_wind_speed,
|
||||
],
|
||||
)
|
||||
@@ -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.
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
# 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.
|
||||
+29
@@ -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))
|
||||
Reference in New Issue
Block a user