Building Autonomous Agents with the Google ADK
A deep dive into the architecture and capabilities of agents built with the Google Agent Development Kit (ADK), exploring skills, tools, and security.
Posted on: 2026-02-27 by AI Assistant
Introduction
The Google Agent Development Kit (ADK) provides a powerful framework for building autonomous AI agents. These agents can leverage a variety of tools and skills to perform complex tasks, from interacting with file systems to fetching data from the web. This post explores the architecture of an ADK-based agent, using a reference implementation to illustrate its key components and design principles.
Core Architecture
The foundation of an ADK agent is the Agent class, which is initialized with a large language model (LLM), a set of instructions, and a collection of tools. The agent’s behavior is guided by its instructions, which define its purpose and how it should interact with the available tools.
root_agent = Agent(
model="gemini-1.5-pro",
name="root_agent",
description="A helpful assistant for user questions.",
instruction=(
"You are a helpful assistant for software development tasks. "
"You have access to specialized skills and file system tools. "
"All file operations are restricted to the project directory. "
),
tools=[
skill_toolset,
run_skill_script,
read_file,
write_file,
list_directory,
run_terminal_command,
fetch_url,
],
)
Skills and the SkillToolset
A key feature of the ADK is the concept of “skills.” A skill is a self-contained unit of functionality that the agent can use. Skills are loaded from a directory, and each skill is defined by a SKILL.md file that describes its purpose and how to use it. The SkillToolset is responsible for managing these skills and making them available to the agent.
# --- Load all skills in the directory ---
skills = []
if skills_dir.exists():
for skill_path in skills_dir.iterdir():
if skill_path.is_dir() and (skill_path / "SKILL.md").exists():
skills.append(load_skill_from_dir(skill_path))
# --- Create the SkillToolset ---
skill_toolset = SkillToolset(skills=skills)
Essential Tools
The provided code equips the agent with a powerful set of tools that enable it to interact with its environment in a variety of ways:
run_skill_script: Executes Python scripts associated with a skill. This allows skills to encapsulate complex logic that can be triggered by the agent.- File System Operations (
read_file,write_file,list_directory): These tools provide the agent with the ability to read, write, and list files within a sandboxed project directory. This is crucial for tasks that involve code modification, data analysis, or content generation. fetch_url: Enables the agent to retrieve data from external websites, allowing it to access up-to-date information and interact with web-based APIs.run_terminal_command: Provides the agent with the ability to execute arbitrary terminal commands. This is a powerful tool that can be used for a wide range of tasks, but it also introduces security considerations.
Agent Tools in Detail
Here’s a closer look at the Python code for the essential tools provided to the agent.
run_skill_script
This tool allows the agent to execute a Python script from a skill’s dedicated ‘scripts’ directory. It’s the bridge between the agent’s reasoning and the execution of complex, pre-defined logic.
def run_skill_script(skill_name: str, script_name: str, args: str = "") -> str:
"""Executes a Python script from a skill's 'scripts' directory."""
script_path = skills_dir / skill_name / "scripts" / script_name
if not script_path.exists():
return f"Error: Script not found at {script_path}"
try:
cmd = [sys.executable, str(script_path)]
if args:
cmd.extend(shlex.split(args))
result = subprocess.run(
cmd,
capture_output=True,
text=True,
check=True,
encoding="utf-8",
errors="replace",
)
return result.stdout
except subprocess.CalledProcessError as e:
return f"Error executing script: {e.stderr or e.stdout}"
except Exception as e:
return f"Unexpected error: {str(e)}"
read_file
Provides the agent with the ability to read the content of a file within the sandboxed environment, which is essential for understanding the existing codebase or data.
def read_file(file_path: str) -> str:
"""Reads the content of a file within the project's sandboxed directory."""
full_path = PROJECT_ROOT_PATH / file_path
if not _is_in_sandbox(full_path):
return "Error: Path is outside the allowed sandbox directory."
try:
with open(full_path, "r", encoding="utf-8") as f:
return f.read()
except FileNotFoundError:
return f"Error: File not found at '{full_path}'"
except Exception as e:
return f"Error reading file: {e}"
write_file
Allows the agent to create new files or modify existing ones. This is a powerful capability that enables the agent to write code, update configurations, or generate content.
def write_file(file_path: str, content: str) -> str:
"""Writes or overwrites content to a file within the project's sandboxed directory."""
full_path = PROJECT_ROOT_PATH / file_path
if not _is_in_sandbox(full_path):
return "Error: Path is outside the allowed sandbox directory."
try:
full_path.parent.mkdir(parents=True, exist_ok=True)
with open(full_path, "w", encoding="utf-8") as f:
f.write(content)
return f"Successfully wrote {len(content)} characters to '{full_path}'"
except Exception as e:
return f"Error writing file: {e}"
list_directory
Enables the agent to explore the file system by listing the contents of a directory. This helps the agent to orient itself and discover relevant files.
def list_directory(dir_path: str = ".") -> str:
"""Lists the contents of a directory within the project's sandbox."""
full_path = PROJECT_ROOT_PATH / dir_path
if not _is_in_sandbox(full_path):
return "Error: Path is outside the allowed sandbox directory."
try:
PROJECT_ROOT_PATH.mkdir(parents=True, exist_ok=True)
entries = [str(p.relative_to(full_path)) for p in full_path.iterdir()]
if not entries:
return f"Directory '{dir_path}' in sandbox is empty."
return "\n".join(entries)
except FileNotFoundError:
return f"Error: Directory '{dir_path}' not found in sandbox."
except Exception as e:
return f"Error listing directory: {e}"
fetch_url
This tool gives the agent the ability to access the internet. It can be used to retrieve information from websites, query APIs, or download resources.
def fetch_url(url: str, timeout: int = 30) -> str:
"""Fetches content from a specified URL using an HTTP GET request."""
try:
response = requests.get(url, timeout=timeout)
response.raise_for_status()
return response.text
except requests.exceptions.HTTPError as http_err:
return f"HTTP error occurred: {http_err} - {response.status_code} {response.reason}"
except requests.exceptions.ConnectionError as conn_err:
return f"Connection error occurred: {conn_err}. Check the URL or network."
except requests.exceptions.Timeout as timeout_err:
return f"Request timed out after {timeout} seconds: {timeout_err}"
except requests.exceptions.RequestException as req_err:
return f"An unexpected request error occurred: {req_err}"
except Exception as e:
return f"An unknown error occurred: {e}"
run_terminal_command
A powerful and flexible tool that allows the agent to execute any command line instruction. This is useful for running build tools, version control commands, or other command-line utilities.
def run_terminal_command(command: str) -> str:
"""Executes a terminal command."""
try:
result = subprocess.run(
command,
shell=True,
capture_output=True,
text=True,
encoding="utf-8",
errors="replace",
)
return result.stdout + result.stderr
except Exception as e:
return f"Error executing command: {str(e)}"
Security and Sandboxing
A critical aspect of the agent’s design is its security model. The file system operations are restricted to a sandboxed directory, which prevents the agent from accessing or modifying files outside of the intended project scope. This is enforced by the _is_in_sandbox function, which checks if a given file path is within the allowed directory.
def _is_in_sandbox(target_path: pathlib.Path) -> bool:
"""Checks if the resolved absolute target_path is within the defined PROJECT_ROOT_PATH."""
try:
# Check if the target path is a subpath of the sandbox root
return target_path.is_relative_to(PROJECT_ROOT_PATH)
except Exception:
return False
This sandboxing mechanism is essential for ensuring that the agent operates in a safe and controlled manner, especially when executing potentially risky operations like writing files or running terminal commands.
Conclusion
The Google ADK provides a flexible and powerful framework for building sophisticated AI agents. By combining a core Agent class with a rich set of tools and a well-defined skill system, developers can create autonomous agents that can perform a wide range of tasks. The emphasis on security and sandboxing ensures that these agents can operate safely and reliably, making the ADK a promising platform for the future of AI-powered applications.