Skip to content
Blog

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.

Published on February 27, 2026

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-2.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.