Agent Development Kit (ADK) Crash Course

repository·main·Indexed 21 days ago

https://github.com/bhancockio/agent-development-kit-crash-course

A hands-on guide and example repository for Google's Agent Development Kit (ADK). It covers the implementation of LlmAgents, directory structures for agent discovery, and advanced orchestration patterns including Sequential Agents for deterministic pipelines, Parallel Agents for concurrent execution, and Loop Agents for iterative refinement using Exit Tools.

Tokens
14.7K
Snippets
47
Records
71
Agent score
75%

What's inside agent-development-kit-crash-course

  1. Overview of ADK Agent patterns and examples

    main

    This repository provides practical implementations of various Agent Development Kit (ADK) patterns. Use these examples to learn specific agent capabilities:

    • Basic Agent: Simple query-response agents.
    • Tool Agent: Agents equipped with tools to perform external actions.
    • LiteLLM Agent: Using LiteLLM to abstract LLM providers and switch models easily.
    • Structured Outputs: Using Pydantic models with output_schema for consistent responses.
    • Sessions and State: Maintaining memory across multiple interactions.
    • Persistent Storage: Storing agent data across restarts.
    • Multi-Agent Systems: Orchestrating specialized agents, including Stateful Multi-Agent workflows.
    • Agent Control Flow: Implementing Callbacks (real-time monitoring), Sequential Agents (pipelines), Parallel Agents (concurrency), and Loop Agents (iterative refinement via feedback loops).
  2. What is an ADK LlmAgent?

    main
    An LlmAgent (often aliased as Agent) is the core reasoning component in ADK. Unlike deterministic workflows, an LlmAgent uses a Large Language Model (LLM) to dynamically interpret instructions, make decisions, and decide which tools to use or when to transfer control to other agents. It is non-deterministic by nature, relying on the LLM's ability to understand natural language and context.
  3. What is a Tool Agent and how do its components work?

    main

    A Tool Agent in ADK extends a basic agent by incorporating tools that allow it to interact with external systems, retrieve information, or perform specific functions.

    There are two main types of tools:

    1. Built-in Tools: Pre-configured tools provided by ADK, such as google_search, code_execution, and vertex_ai_search.
    2. Custom Function Tools: Python functions defined by the user to extend capabilities.

    Tool Compatibility Rules

    Crucial Limitation: You cannot mix built-in tools and custom tools in a single agent. Additionally, a single agent can only support one built-in tool at a time. To combine these types, you must use a multi-agent architecture (the Agent Tool approach).

    Unsupported Patterns:

    • Multiple built-in tools: tools=[built_in_code_execution, google_search]
    • Mixing built-in and custom: tools=[google_search, get_current_time]
    # Example of what is NOT supported
    root_agent = Agent(
        name="RootAgent",
        model="gemini-2.0-flash",
        description="Root Agent",
        tools=[google_search, get_current_time],  # NOT SUPPORTED
    )
  4. How the Sequential and Loop Agent patterns work together

    main

    The LinkedIn Post Generator demonstrates how to combine SequentialAgent and LoopAgent to create complex workflows.

    In this pattern:

    1. A Sequential Pipeline (e.g., LinkedInPostGenerationPipeline) orchestrates the high-level stages (e.g., Stage 1: Initial Generation $\rightarrow$ Stage 2: Refinement Loop).
    2. A Loop Agent (e.g., PostRefinementLoop) handles iterative tasks within one of those stages, such as reviewing and refining content.
    3. Sub-Agents operate inside the loop to perform specific roles (e.g., a PostReviewer to evaluate and a PostRefiner to improve).

    This architecture allows for a clear separation between the initial creation of an asset and the iterative process of polishing it based on feedback.

    # Example Architecture Flow
    # 1. SequentialAgent (Root)
    #    |--> InitialPostGenerator (LlmAgent)
    #    |--> PostRefinementLoop (LoopAgent)
    #               |--> PostReviewer (Sub-Agent)
    #               |--> PostRefiner (Sub-Agent)
  5. What are Sequential Agents and when to use them

    main

    Sequential Agents are workflow agents in ADK designed for deterministic, step-by-step processes. They are characterized by three main behaviors:

    1. Fixed Execution Order: Sub-agents run in the exact sequence they are specified.
    2. Data Passing: They use state management to pass information from one sub-agent to the next.
    3. Pipeline Creation: They are ideal for scenarios where each step's logic depends on the output of the previous step.

    Comparison with other workflow agents:

    • Sequential Agents: Strict, ordered execution.
    • Loop Agents: Repeated execution based on specific conditions.
    • Parallel Agents: Concurrent execution of independent sub-agents.
  6. How Multi-Agent Systems work in ADK

    main

    A Multi-Agent System in ADK allows multiple specialized agents to collaborate on complex tasks. There are two primary architectural patterns for implementing this:

    1. Sub-Agent Delegation Model: The root agent uses the sub_agents parameter in the Agent constructor. In this model, the root agent acts as a router. When a task is delegated, the sub-agent takes full control of the conversation and its decision is final.

    2. Agent-as-a-Tool Model: Agents are wrapped using AgentTool and passed into the tools list of a root agent. In this model, the sub-agent returns results to the root agent, which maintains control and can incorporate the response into its own output or call multiple agent tools in a single turn.

    Use Delegation when you want a specialist to take over the entire interaction. Use Agent-as-a-Tool when you want the root agent to orchestrate multiple specialists and synthesize their findings.

    # Sub-Agent Delegation Model
    root_agent = Agent(
        name="manager",
        sub_agents=[stock_analyst, funny_nerd],
    )
    
    # Agent-as-a-Tool Model
    from google.adk.tools.agent_tool import AgentTool
    root_agent = Agent(
        name="manager",
        tools=[AgentTool(news_analyst)],
    )
  7. Use LlmResponse in Model Callbacks

    main

    The LlmResponse object is provided to the after_model_callback. It contains the output from the model, allowing you to transform or modify the response before it is returned to the user.

    Properties:

    • content: Content object containing the model's response.
    • tool_calls: Any tool calls the model wants to make.
    • usage_metadata: Metadata about the model usage (e.g., tokens).
    def after_model_callback(callback_context: CallbackContext, llm_response: LlmResponse):
        # Access the model's text response
        if llm_response.content and llm_response.content.parts:
            response_text = llm_response.content.parts[0].text
            
            # Modify the response
            modified_text = transform_text(response_text)
            llm_response.content.parts[0].text = modified_text
            
            return llm_response
  8. Key Components of an LlmAgent

    main

    When defining an LlmAgent, you configure four primary components:

    1. Identity:
      • name (Required): A unique string identifier.
      • description (Optional): A summary of capabilities used by other agents for routing tasks.
    2. Model (model): Specifies the powering LLM (e.g., gemini-2.0-flash). This dictates performance, cost, and capabilities.
    3. Instructions (instruction): The most critical parameter. It defines the agent's goal, persona, behavioral constraints, tool usage guidelines, and desired output format.
    4. Tools (tools): Optional capabilities that allow the agent to interact with external systems, perform calculations, or fetch real-time data.
  9. Use CallbackContext to manage agent state and logging

    main

    The CallbackContext is provided to agent-level callbacks. It allows you to:

    1. Access and modify state: Use the state attribute to store or retrieve data that persists across the agent's execution.
    2. Log execution details: Access the current agent and the specific invocation to track which agent is running and its current context.

    This is useful for building observability layers or maintaining long-term memory within a single session.

  10. What are Callbacks in ADK?

    main

    Callbacks are functions that execute at specific points in an agent's execution flow. They act as hooks that allow you to intercept and modify agent behavior.

    Common use cases include:

    • Monitor and Log: Track agent activity and performance metrics.
    • Filter Content: Block inappropriate requests or responses.
    • Transform Data: Modify inputs and outputs in the agent workflow.
    • Implement Security Policies: Enforce compliance and safety measures.
    • Add Custom Logic: Insert business-specific processing into the agent flow.
  11. Required Agent Structure for ADK Discovery

    main

    To ensure ADK can discover and run your agents (e.g., via adk web), you must follow a specific directory and file structure. ADK relies on specific naming conventions to locate the entry point of your agent.

    Directory Structure

    parent_folder/
        agent_folder/         # Your agent's package directory
            __init__.py       # Must import agent.py
            agent.py          # Must define root_agent
            .env              # Environment variables

    Essential Requirements

    1. __init__.py: Must contain from . import agent to make the agent discoverable.
    2. agent.py: Must define a variable named root_agent. This is the specific entry point ADK looks for.
    3. Execution Context: Always run adk commands from the parent directory containing your agent folder, not from within the agent folder itself.
  12. Use LlmRequest in Model Callbacks

    main

    The LlmRequest object is provided to before_model_callback. It allows you to inspect the request before it reaches the LLM, which is useful for content filtering or bypassing the model call entirely.

    Properties:

    • contents: List of Content objects representing the conversation history.
    • generation_config: Configuration for the model generation.
    • safety_settings: Safety settings for the model.
    • tools: Tools provided to the model.
    def before_model_callback(callback_context: CallbackContext, llm_request: LlmRequest):
        # Get the last user message for analysis
        last_message = None
        for content in reversed(llm_request.contents):
            if content.role == "user" and content.parts:
                last_message = content.parts[0].text
                break
                
        # Analyze the user's message
        if last_message and contains_sensitive_info(last_message):
            # Return a response that bypasses the model call
            return LlmResponse(...)