> ## Documentation Index
> Fetch the complete documentation index at: https://docs.openlayer.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Google Agent Development Kit (ADK)

> Learn how to evaluate multi-agent systems built with Google Agent Development Kit using Openlayer

![Google Agent Development Kit (ADK) hero](https://mintcdn.com/openlayer-44/i_IVq7qGt9U6VbHF/images/integrations/google_mark_hero.png?fit=max\&auto=format\&n=i_IVq7qGt9U6VbHF\&q=85\&s=3773b5b18a62e36f6a9ffdfbe8a47d36)

If you are building AI systems with [Google Agent Development Kit (ADK)](https://developers.google.com/adk) and want to evaluate multi-agent conversations, handoffs, and tool usage, you can use the [SDKs](https://docs.openlayer.com/api-reference/sdk/overview.md) to make Openlayer part of your workflow.

This integration guide shows how you can comprehensively trace and monitor your multi-agent systems powered by Gemini models.

## Choosing an integration path

Openlayer supports **two** ways to instrument Google ADK. Both land traces in the same Openlayer pipeline; pick the one that fits your stack.

| Path | Setup | When to use it |
| - | - | - |
| **1. Openlayer tracer** (recommended) | One call — `init()` | You want the richest Openlayer-native metadata (agent instructions, tool I/O, session context) and the least setup. |
| **2. ADK OpenTelemetry** | Point ADK's built-in OTel exporter at Openlayer's OTLP endpoint (ADK 1.17.0+) | You already standardize on OpenTelemetry — for example an existing collector, or a policy that forbids vendor SDKs in application code. |

The rest of this page walks through each path. **If you're not sure, start with the Openlayer tracer.**

Do not enable both paths in the same process. See [Don't combine both paths](#dont-combine-both-paths).

## Path 1 — Openlayer tracer (recommended)

A single `init()` call auto-instruments Google ADK (it runs `trace_google_adk()` under the hood) along with any other installed LLM SDKs.
The tracer supports `google-adk` 1.x and 2.x, including 2.9 and 2.10.

Each `Runner.run_async` call produces one trace rooted at an `Agent turn: <app>` step. If the turn transfers control between agents, the trace includes `Handoff: A → B` steps and keeps the receiving agent in the same hierarchy.

> [!NOTE]
>
> `google-adk` 2.x support, handoff steps, and the `starting_agent`,
> `final_agent`, `handoff_count`, and `handoff_path` columns require the
> Openlayer Python SDK `openlayer` 0.36.0 or later.

### Monitoring

To use the [monitoring mode](https://docs.openlayer.com/monitoring/overview.md), you must instrument your code to publish the requests your AI system receives to the Openlayer platform.

To set it up, you must follow the steps in the code snippet below:

```python Python theme={null}
# 1. Set the environment variables
import os

os.environ["OPENLAYER_API_KEY"] = "YOUR_OPENLAYER_API_KEY_HERE"
os.environ["OPENLAYER_INFERENCE_PIPELINE_ID"] = "YOUR_OPENLAYER_INFERENCE_PIPELINE_ID_HERE"

# 2. Call `init` to auto-instrument the installed LLM SDKs BEFORE creating agents
from openlayer.lib import init

init()

# 3. Create your agent and runner
from google.genai import types
from google.adk.agents import LlmAgent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService

def get_weather(location: str) -> str:
    """Get weather for a location."""
    return f"Sunny and 72°F in {location}"

agent = LlmAgent(
    name="weather_agent",
    model="gemini-2.5-flash",
    instruction="You help users get weather information for any location.",
    tools=[get_weather]
)

session_service = InMemorySessionService()
runner = Runner(
    agent=agent,
    app_name="weather_app",
    session_service=session_service,
)

# 4. Run conversations with automatic tracing
async def run_conversation(user_input: str):
    await session_service.create_session(
        app_name="weather_app",
        user_id="user-456",
        session_id="session-123",
    )
    message = types.Content(role="user", parts=[types.Part(text=user_input)])

    async for event in runner.run_async(
        session_id="session-123",
        user_id="user-456",
        new_message=message,
    ):
        if event.is_final_response() and event.content:
            print(event.content)

# From now on, all agent conversations, handoffs, and tool calls
# are automatically traced by Openlayer
await run_conversation("What's the weather in San Francisco?")
```

> [card] [See full Python example](https://colab.research.google.com/github/openlayer-ai/openlayer-python/blob/main/examples/tracing/google-adk/google_adk_tracing.ipynb)

Once the code is instrumented, all your Google ADK interactions are automatically published to Openlayer, including:

* **Agent execution** with agent names, descriptions, and instructions
* **LLM calls** to Gemini models with messages and configurations
* **Token usage** including prompt tokens, completion tokens, and totals
* **Tool calls** with function names, arguments, and results
* **Agent transfers** as handoff steps between agents in the trace hierarchy
* **Session context** including user IDs, session IDs, and invocation tracking
* **Metadata** such as latency and timestamps for all operations

Each trace also includes `starting_agent`, `final_agent`, `handoff_count`, and `handoff_path` as row columns. You can use these columns to filter data and write tests on the data source. When one `@trace` call wraps several runner turns, the columns combine their handoff paths in order.

If you navigate to the "Data" page of your Openlayer data source, you can see the complete traces for each multi-agent conversation.

> [!NOTE]
>
> The Google ADK integration automatically captures the full agent workflow,
> including sub-agent handoffs and tool usage. You can use this together with
> [tracing](https://docs.openlayer.com/monitoring/tracing) to monitor complex multi-agent systems as part
> of larger AI workflows. Make sure to call `init()` **before** creating any
> agents.

After your AI system requests are continuously published and logged by Openlayer, you can [create tests](https://docs.openlayer.com/tests/overview.md) that run at a regular cadence on top of them.

Refer to the [Monitoring overview](https://docs.openlayer.com/monitoring/overview.md), for details on Openlayer's monitoring mode, to the [Publishing data guide](https://docs.openlayer.com/monitoring/publishing-data), for more information on setting it up, or to the [Tracing guide](https://docs.openlayer.com/monitoring/tracing), to understand how to trace more complex systems.

## Path 2 — ADK OpenTelemetry

As of ADK 1.17.0, the framework ships its own OpenTelemetry instrumentation ([ADK traces](https://github.com/google/adk-docs/blob/main/docs/observability/traces.md), [Cloud Observability](https://docs.cloud.google.com/stackdriver/docs/instrumentation/ai-agent-adk)) and can export traces over OTLP to any backend — including Openlayer. This is the path to use when you already run an OTel collector, or when application code is not allowed to depend on a vendor tracing SDK.

Openlayer ingests the spans via its [OpenTelemetry endpoint](https://docs.openlayer.com/integrations/opentelemetry.md). ADK emits [GenAI semantic-convention](https://opentelemetry.io/docs/specs/semconv/gen-ai/) spans such as `invoke_agent`, `execute_tool`, and `generate_content`, which Openlayer maps onto agent, tool, and chat-completion steps.

### Install packages

```bash theme={null}
pip install "google-adk>=1.17.0" \
            opentelemetry-instrumentation-google-genai \
            opentelemetry-instrumentation-vertexai \
            opentelemetry-exporter-otlp-proto-http
```

> [!NOTE]
>
> Openlayer's OTel endpoint accepts **OTLP HTTP/protobuf** (not gRPC). Install
> `opentelemetry-exporter-otlp-proto-http`, not the gRPC exporter Google's Cloud
> Trace samples use. See the [OpenTelemetry integration
> page](https://docs.openlayer.com/integrations/opentelemetry.md) for the full endpoint reference.

The Google GenAI and Vertex AI instrumentors enrich the underlying model calls (tokens, model name, messages). ADK itself emits the agent and tool spans.

### Configure the environment

Set the following environment variables **before** constructing any ADK agents. You can put them in the process environment, a dotenv file, or `os.environ` at process start.

```bash theme={null}
# OTLP/HTTP -> Openlayer (traces only)
OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="https://api.openlayer.com/v1/otel/v1/traces"
OTEL_EXPORTER_OTLP_TRACES_HEADERS="Authorization=Bearer YOUR_OPENLAYER_API_KEY, x-bt-parent=pipeline_id:YOUR_OPENLAYER_PIPELINE_ID"
OTEL_TRACES_EXPORTER="otlp"
OTEL_METRICS_EXPORTER="none"
OTEL_LOGS_EXPORTER="none"

# Latest GenAI semantic conventions (required for SPAN_ONLY / EVENT_ONLY)
OTEL_SEMCONV_STABILITY_OPT_IN="gen_ai_latest_experimental"

# Put prompts and responses on span attributes so Openlayer can read them
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT="SPAN_ONLY"

# Required: put message content on ADK's own spans. Without this, Input /
# Output are empty and Openlayer cannot evaluate the traces.
ADK_CAPTURE_MESSAGE_CONTENT_IN_SPANS="true"

# Optional: resource label only — does not select the Openlayer pipeline
OTEL_SERVICE_NAME="adk-agent"
```

Replace `YOUR_OPENLAYER_API_KEY` and `YOUR_OPENLAYER_PIPELINE_ID` with values from your Openlayer workspace. The `x-bt-parent` header is what routes traces to the correct inference pipeline — not `OTEL_SERVICE_NAME`.

> [!WARNING]
>
> **Do not set `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true` when
> using the latest GenAI semantic conventions.** `true` is not a valid value for
> that mode. The GenAI instrumentor silently captures **no** prompt or response
> content — traces still arrive, but Input / Output in Openlayer are empty. Use
> `SPAN_ONLY` (for Openlayer) or `EVENT_ONLY` (for log-oriented backends such as
> Cloud Logging).

> [!NOTE]
>
> Google's Cloud Observability docs recommend
> `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=EVENT_ONLY` and
> `ADK_CAPTURE_MESSAGE_CONTENT_IN_SPANS=false`, which keep prompts and responses
> off span attributes (they go to log records instead). Openlayer's OTel
> endpoint ingests **traces**, not logs, and tests need Input / Output on those
> traces. Use `SPAN_ONLY` **and** `ADK_CAPTURE_MESSAGE_CONTENT_IN_SPANS=true` so
> content lands on the spans Openlayer maps (`gen_ai.input.messages` /
> `gen_ai.output.messages` and ADK's own span attributes).

Openlayer does not ingest OTLP logs or metrics, so `OTEL_LOGS_EXPORTER=none` and `OTEL_METRICS_EXPORTER=none` avoid failed export attempts. Google's `OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED` setting is for Cloud Logging and is not needed here.

### Wire the exporter

Call ADK's `maybe_set_otel_providers()` **before** importing or constructing agents. It reads the `OTEL_EXPORTER_OTLP_*` environment variables and installs HTTP OTLP exporters.

```python Python theme={null}
from google.adk.telemetry.setup import maybe_set_otel_providers
from opentelemetry.instrumentation.google_genai import GoogleGenAiSdkInstrumentor

# Environment variables above must already be set.
maybe_set_otel_providers()
GoogleGenAiSdkInstrumentor().instrument()

from google.adk.agents import Agent
from google.adk.runners import Runner

def get_weather(location: str) -> str:
    """Get weather for a location."""
    return f"Sunny and 72°F in {location}"

agent = Agent(
    name="weather_agent",
    model="gemini-2.0-flash-exp",
    description="Provides weather information",
    instructions="You help users get weather information for any location.",
    tools=[get_weather],
)

async def run_conversation(user_input: str):
    runner = Runner(agent)
    async for event in runner.run_async(
        session_id="session-123",
        user_id="user-456",
        message=user_input,
    ):
        if event.is_final_response():
            print(event.content)

await run_conversation("What's the weather in San Francisco?")
```

If you already configure a global `TracerProvider` (for example via `opentelemetry-instrument` or an in-process collector), skip `maybe_set_otel_providers()` — ADK will use the provider that is already set. If you call Vertex AI through the `vertexai` SDK rather than `google.genai`, also run `VertexAIInstrumentor().instrument()` from `opentelemetry.instrumentation.vertexai`.

To send traces through an existing OpenTelemetry Collector instead of exporting from the app, keep ADK pointed at the collector and add an `otlphttp` exporter for Openlayer:

```yaml theme={null}
exporters:
  otlphttp/openlayer:
    endpoint: https://api.openlayer.com/v1/otel
    headers:
      Authorization: "Bearer YOUR_OPENLAYER_API_KEY"
      x-bt-parent: "pipeline_id:YOUR_OPENLAYER_PIPELINE_ID"

service:
  pipelines:
    traces:
      exporters: [otlphttp/openlayer]
```

The exporter appends `/v1/traces` to `endpoint`. See [Using an OpenTelemetry Collector](https://docs.openlayer.com/integrations/opentelemetry.md#using-an-opentelemetry-collector) for details.

### `adk web` / `adk api_server`

You do not need Google's `--otel_to_cloud` flag. That flag wires GCP exporters specifically. For an arbitrary OTLP backend, set the environment variables above and run:

```bash theme={null}
adk web path/to/your/agents_dir
```

ADK picks up `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` (or `OTEL_EXPORTER_OTLP_ENDPOINT`) and exports spans over OTLP HTTP.

### What the traces look like

A typical ADK OTel trace is hierarchical, not flat:

| Span | Openlayer step type | What it captures |
| - | - | - |
| `invoke_agent {agent.name}` | Agent | Agent name, description, conversation / session id |
| `generate_content {model}` | Chat completion | Model, tokens, prompts / responses |
| `execute_tool {tool.name}` | Tool | Tool name, description, arguments, result |

Sub-agents appear as nested `invoke_agent` spans under the parent agent. That maps onto Openlayer's agent-trace hierarchy the same way other GenAI OTel sources do.

## Don't combine both paths

The Openlayer tracer replaces ADK's own OpenTelemetry tracer with a no-op so ADK does not emit a second copy of agent and tool spans. The Google GenAI / Vertex AI instrumentors patch the model SDK independently, so LLM spans can still be duplicated.

Enable **either** `init()` **or** ADK's OTel exporters pointed at Openlayer — not both. `init()` already calls `trace_google_adk()`, so running it together with Path 2 duplicates traces in Openlayer. If you switch from the native tracer to OTel, call `unpatch_google_adk()` (or restart the process without `init()`).

## Comparison: which path produces what

| | Openlayer tracer | ADK OpenTelemetry |
| - | - | - |
| Setup | One call | Env vars + `maybe_set_otel_providers()` |
| Vendor SDK in app code | Yes (`openlayer`) | No |
| Agent hierarchy | ✅ | ✅ (`invoke_agent` parent spans) |
| Tool calls | ✅ | ✅ (`execute_tool`) |
| Tokens | ✅ | ✅ (`gen_ai.usage.*`) |
| Prompt / response content | ✅ | ✅ with `SPAN_ONLY` + `ADK_CAPTURE_MESSAGE_CONTENT_IN_SPANS=true`; ❌ if either is off |
| Agent instructions / descriptions | ✅ | ✅ (`gen_ai.agent.*`) |
| Session / user ids | ✅ | ⚠️ conversation id on agent spans; user id depends on ADK version |
| Supported `google-adk` versions | 1.x and 2.x (including 2.9 and 2.10) | 1.17.0+ |
| Cost | Estimated by Openlayer from tokens and model | Estimated the same way when tokens and model are present |
| Portable to other OTel backends | ❌ | ✅ |

## Development

In [development mode](https://docs.openlayer.com/development/overview.md), Openlayer becomes a step in your CI/CD pipeline, and your tests get automatically evaluated after being triggered by some events.

Openlayer tests often rely on your AI system's outputs on a validation dataset. As discussed in the [Configuring output generation guide](https://docs.openlayer.com/development/configuring-output-generation.md), you have two options:

1. either provide a way for Openlayer to run your AI system on your datasets, or
2. before pushing, generate the model outputs yourself and push them alongside your artifacts.

For AI systems built with Google ADK, if you are **not** computing your system's outputs yourself, you must provide your **API credentials** for Google's Gemini models.

To do so, navigate to "**Workspace settings**" -> "**Environment variables**," and click on "Add secret" to add the required Google API credentials (such as `GOOGLE_API_KEY` or appropriate service account credentials).

If you don't add the required API credentials, you'll encounter a "Missing API key" error when Openlayer tries to run your AI system to get its outputs.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.