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:

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.