Google ADK Adapter

Build agents using Google's Agent Development Kit with the Band SDK

This tutorial shows you how to create an agent using the GoogleADKAdapter. This adapter integrates Google’s Agent Development Kit (ADK) with the Band platform, running Gemini-powered agents with automatic tool bridging and conversation history management.

Prerequisites

Before starting, make sure you’ve completed the Setup tutorial:

  • SDK installed with Google ADK support
  • Agent created on the platform
  • .env and agent_config.yaml configured
  • Verified your setup works

Install the Google ADK extra:

$uv add "band-sdk[google-adk]"

Set your Google API key:

$export GOOGLE_API_KEY="your-google-api-key"

Get an API key from Google AI Studio.

The key is resolved by the underlying google-genai client, which reads GOOGLE_API_KEY first and falls back to GEMINI_API_KEY. Setting both logs a warning and uses GOOGLE_API_KEY.


Create Your Agent

Create a file called agent.py:

agent.py
1import asyncio
2import logging
3import os
4from dotenv import load_dotenv
5from band import Agent, configure_logging
6from band.adapters import GoogleADKAdapter
7from band.config import load_agent_config
8
9logger = logging.getLogger(__name__)
10
11async def main():
12 load_dotenv()
13 configure_logging(root_level="INFO")
14
15 # Load agent credentials
16 agent_id, api_key = load_agent_config("my_agent")
17
18 # Create adapter with Gemini
19 adapter = GoogleADKAdapter(
20 model="gemini-2.5-flash",
21 custom_section="You are a helpful assistant. Be concise and friendly.",
22 )
23
24 # Create and run the agent
25 agent = Agent.create(
26 adapter=adapter,
27 agent_id=agent_id,
28 api_key=api_key,
29 ws_url=os.getenv("BAND_WS_URL", "wss://app.band.ai/api/v1/socket/websocket"),
30 rest_url=os.getenv("BAND_REST_URL", "https://app.band.ai"),
31 )
32
33 logger.info("Agent is running! Press Ctrl+C to stop.")
34 await agent.run()
35
36if __name__ == "__main__":
37 asyncio.run(main())

Run the Agent

Start your agent:

$uv run python agent.py

You should see:

2026-01-15 09:30:00 [INFO] __main__: Agent is running! Press Ctrl+C to stop.

Test Your Agent

1

Add Agent to a Chat Room

Go to Band and either create a new chat room or open an existing one. Add your agent as a participant, under the Remote section.

2

Send a Message

In the chat room, mention your agent:

@MyAgent Hello! Can you help me?
3

See the Response

Your agent will process the message and respond in the chat room.


How It Works

The Google ADK adapter uses ADK’s InMemoryRunner for the full tool loop:

  1. Fresh Runner Per Message — A new InMemoryRunner is created for each incoming message to avoid session state pollution. Conversation continuity is maintained through transcript injection.
  2. Tool Bridging — Band platform tools are automatically wrapped as ADK BaseTool subclasses, including recursive additionalProperties stripping for Gemini schema compatibility.
  3. History Management — Per-room message history is accumulated and injected as a text transcript into the ADK session, with character-based truncation (100K chars default) to prevent token overflow.
  4. Execution Reporting — Emits tool_call and tool_result events for visibility into the agent’s decision-making, plus per-turn token usage. Both are on by default; narrow them with emit.

Available Platform Tools:

ToolDescription
band_send_messageSend a message to the chat room
band_send_eventSend events (thought, error, etc.)
band_add_participantAdd a user or agent to the room
band_remove_participantRemove a participant
band_get_participantsList current room participants
band_lookup_peersFind available peers to add

Supported Models

The adapter works with any Gemini model available through Google’s generative AI API:

1# Fast and cost-effective
2adapter = GoogleADKAdapter(model="gemini-2.5-flash")
3
4# More capable
5adapter = GoogleADKAdapter(model="gemini-2.5-pro")

Gemini 2.5 Flash is a good default for most use cases. Use Gemini 2.5 Pro when you need stronger reasoning or more complex tool usage.


Configuration Options

The GoogleADKAdapter supports these configuration options:

1adapter = GoogleADKAdapter(
2 # Gemini model to use
3 model="gemini-2.5-flash",
4
5 # Custom instructions appended to the system prompt
6 custom_section="You are a helpful assistant.",
7
8 # Override the entire system prompt
9 system_prompt=None,
10
11 # Narrow the events reported into the room, and add the memory tools
12 # (store/retrieve agent memory)
13 # emit={Emit.TOOL_CALLS},
14 # capabilities={Capability.MEMORY},
15
16 # Maximum number of history messages to retain per room
17 max_history_messages=50,
18
19 # Maximum characters for the transcript injected into ADK sessions
20 max_transcript_chars=100_000,
21
22 # Custom tools as (PydanticModel, handler) tuples
23 additional_tools=None,
24)

Add Custom Instructions

Customize your agent’s behavior with the custom_section parameter:

1adapter = GoogleADKAdapter(
2 model="gemini-2.5-flash",
3 custom_section="""
4 You are a research assistant specializing in summarizing information.
5 Always provide sources when possible and be thorough but concise.
6 """,
7)

You can also load instructions from a file:

1from pathlib import Path
2
3prompt = Path("prompts/research.md").read_text()
4
5adapter = GoogleADKAdapter(
6 model="gemini-2.5-pro",
7 custom_section=prompt,
8)

Override the System Prompt

For full control over the system prompt, use the system_prompt parameter:

1custom_prompt = """You are a technical support agent.
2
3Guidelines:
4- Be patient and thorough
5- Ask clarifying questions before providing solutions
6- Always verify the user's environment
7- Escalate to humans if you cannot resolve the issue"""
8
9adapter = GoogleADKAdapter(
10 model="gemini-2.5-pro",
11 system_prompt=custom_prompt,
12)

When using system_prompt, you bypass the default Band platform instructions. Make sure your prompt includes guidance on using the band_send_message tool to respond.


Custom Tools

Extend your agent with custom tools using the additional_tools parameter. Each tool is defined as a tuple of a Pydantic model (input schema) and a handler function.

1from pydantic import BaseModel, Field
2
3class CalculatorInput(BaseModel):
4 """Perform a mathematical calculation."""
5 operation: str = Field(
6 description='The operation: "add", "subtract", "multiply", or "divide"'
7 )
8 left: float = Field(description="The first number")
9 right: float = Field(description="The second number")
10
11def calculator(operation: str, left: float, right: float) -> str:
12 ops = {
13 "add": lambda a, b: a + b,
14 "subtract": lambda a, b: a - b,
15 "multiply": lambda a, b: a * b,
16 "divide": lambda a, b: "Error: division by zero" if b == 0 else a / b,
17 }
18 fn = ops.get(operation)
19 if fn is None:
20 return f"Unknown operation '{operation}'. Use: add, subtract, multiply, divide"
21 return str(fn(left, right))
22
23adapter = GoogleADKAdapter(
24 model="gemini-2.5-flash",
25 additional_tools=[
26 (CalculatorInput, calculator),
27 ],
28 custom_section="You have access to a calculator tool in addition to the platform tools.",
29)

The tool name is derived from the Pydantic model class name, and the description comes from the model’s docstring. Tool parameters are automatically converted to Gemini-compatible schemas.


Execution Reporting

The adapter reports each tool interaction into the room by default. GoogleADKAdapter supports Emit.TOOL_CALLS and Emit.USAGE, and omitting emit resolves to both:

1from band import Emit
2from band.adapters import GoogleADKAdapter
3
4# Tool calls only, no per-turn usage records
5adapter = GoogleADKAdapter(
6 model="gemini-2.5-flash",
7 emit={Emit.TOOL_CALLS},
8)
9
10# Nothing reported into the room
11quiet = GoogleADKAdapter(model="gemini-2.5-flash", emit=())

Naming an Emit member outside that pair, Emit.THOUGHTS or Emit.TASK_EVENTS, raises BandConfigError at construction.

With Emit.TOOL_CALLS the adapter sends:

  • tool_call events when a tool is invoked (includes tool name and arguments)
  • tool_result events when a tool returns (includes output)

This is useful for debugging and for visibility into your agent’s decision-making process. To reduce the noise without going silent, keep Emit.USAGE and drop Emit.TOOL_CALLS.


Complete Example

Here’s a full example with custom instructions, custom tools, and tool events narrowed to tool calls:

agent.py
1import asyncio
2import logging
3import os
4from dotenv import load_dotenv
5from pydantic import BaseModel, Field
6from band import Agent, Emit, configure_logging
7from band.adapters import GoogleADKAdapter
8from band.config import load_agent_config
9
10logger = logging.getLogger(__name__)
11
12class WeatherInput(BaseModel):
13 """Get current weather for a city."""
14 city: str = Field(description="Name of the city")
15
16def weather(city: str) -> str:
17 return f"Weather in {city}: Sunny, 22 C"
18
19async def main():
20 load_dotenv()
21 configure_logging(root_level="INFO")
22 agent_id, api_key = load_agent_config("my_agent")
23
24 adapter = GoogleADKAdapter(
25 model="gemini-2.5-pro",
26 custom_section="""
27 You are a helpful assistant with access to weather data.
28 When users ask about weather, use the weather tool.
29 Be concise and friendly in your responses.
30 """,
31 additional_tools=[
32 (WeatherInput, weather),
33 ],
34 emit={Emit.TOOL_CALLS},
35 )
36
37 agent = Agent.create(
38 adapter=adapter,
39 agent_id=agent_id,
40 api_key=api_key,
41 ws_url=os.getenv("BAND_WS_URL", "wss://app.band.ai/api/v1/socket/websocket"),
42 rest_url=os.getenv("BAND_REST_URL", "https://app.band.ai"),
43 )
44
45 logger.info("Google ADK agent is running! Press Ctrl+C to stop.")
46 await agent.run()
47
48if __name__ == "__main__":
49 asyncio.run(main())

Debug Mode

If your agent isn’t responding as expected, enable debug logging:

agent_debug.py
1import asyncio
2import logging
3import os
4from dotenv import load_dotenv
5from band import Agent, configure_logging
6from band.adapters import GoogleADKAdapter
7from band.config import load_agent_config
8
9logger = logging.getLogger(__name__)
10
11async def main():
12 load_dotenv()
13 # Enable debug logging for the SDK
14 configure_logging(level="DEBUG", root_level="INFO")
15 agent_id, api_key = load_agent_config("my_agent")
16
17 adapter = GoogleADKAdapter(
18 model="gemini-2.5-flash",
19 )
20
21 agent = Agent.create(
22 adapter=adapter,
23 agent_id=agent_id,
24 api_key=api_key,
25 ws_url=os.getenv("BAND_WS_URL", "wss://app.band.ai/api/v1/socket/websocket"),
26 rest_url=os.getenv("BAND_REST_URL", "https://app.band.ai"),
27 )
28
29 logger.info("Agent running with DEBUG logging. Press Ctrl+C to stop.")
30 await agent.run()
31
32if __name__ == "__main__":
33 asyncio.run(main())

With debug logging enabled, you’ll see detailed output including:

  • ADK runner creation and session management
  • Tool bridge construction and schema conversion
  • History transcript injection
  • Tool call dispatch and results
  • Message processing lifecycle

Architecture Notes

The Google ADK adapter differs from other adapters in a few key ways:

Fresh Runner Per Message:

  • A new InMemoryRunner is created for each incoming message
  • This avoids session state pollution between turns
  • Conversation continuity is achieved by injecting accumulated history as a text transcript

Tool Bridging:

  • Platform tools are wrapped as ADK BaseTool subclasses (_BandToolBridge)
  • Schemas are converted from OpenAI format to Gemini format by stripping unsupported additionalProperties keys
  • The bridge probes multiple candidate method names on BaseTool for forward compatibility with ADK API changes

History Management:

  • Per-room history is accumulated across messages
  • A sliding window limits history to max_history_messages (default 50)
  • The text transcript is truncated at newline boundaries to max_transcript_chars (default 100K characters)
  • Thread-safe via the runtime’s sequential-per-room execution guarantee

Next Steps