Arthur + Google ADK

How does a developer instrument a Google ADK application with Arthur in under 10 minutes? You install the Arthur SDK with the google-adk extra, initialize the Arthur client with your credentials, and call arthur.instrument_google_adk(). That single method call auto-instruments every Google ADK agent, runner, and tool invocation — capturing model decisions, tool calls, and conversation turns as OpenInference-compatible traces that flow into Arthur for observability and evaluation.


Overview

Google's Agent Development Kit (ADK) lets you build multi-step agents powered by Gemini models. Arthur's integration uses the openinference-instrumentation-google-adk library to automatically capture every layer of your agent execution. Once instrumented, you get full visibility into:

  • LLM calls — every Gemini model invocation with input/output tokens
  • Tool calls — each tool the agent invokes, including arguments and results
  • Agent orchestration — the full chain of decisions across sub-agents and runners
  • Conversation turns — user messages and agent responses within a session
  • Session and user context — group traces by conversation or end-user
sequenceDiagram
    participant App as Your Application
    participant SDK as Arthur SDK
    participant ADK as Google ADK
    participant Engine as Arthur GenAI Engine

    App->>SDK: arthur.instrument_google_adk()
    Note over SDK: Auto-instrumentation enabled
    App->>ADK: runner.run(...)
    ADK-->>App: Events
    SDK->>Engine: Trace (spans, attributes)
    Note over Engine: Traces visible in dashboard

Prerequisites:

  • Python 3.10+
  • An Arthur GenAI Engine instance (cloud or local)
  • An Arthur API key — see API Keys to create one

Installation

Install the Arthur Observability SDK with the google-adk extra:

pip install "arthur-observability-sdk[google-adk]"

Initialize Arthur

Create a single Arthur instance at application startup.

from arthur_observability_sdk import Arthur

arthur = Arthur(
    api_key="your-api-key",        # or set ARTHUR_API_KEY env var
    base_url="https://your-arthur-engine-instance",  # or set ARTHUR_BASE_URL env var
    task_id="<your-task-uuid>",    # Arthur task UUID
    service_name="my-adk-app",
)
ParameterDescription
api_keyYour Arthur Engine API key. Falls back to ARTHUR_API_KEY env var.
base_urlBase URL of your Arthur GenAI Engine. Falls back to ARTHUR_BASE_URL env var, then http://localhost:3030.
task_idArthur task UUID for associating traces with a specific task.
service_nameOpenTelemetry service.name resource attribute. Used to identify your application in the Arthur dashboard. Creates a new task based on service_name if task_id isn't specified.
📘

At least one of task_id or service_name must be provided. A new task with the service_name will be created if task_id is not specified.

⚠️

Use environment variables for secrets. Set ARTHUR_API_KEY and ARTHUR_BASE_URL as environment variables (e.g., in a .env file) rather than hardcoding them in your application.


Instrument Google ADK

After initializing Arthur, call instrument_google_adk() before creating your agents or runners. This registers the OpenInference instrumentor with the Arthur-managed TracerProvider.

from google.adk.agents import Agent
from google.adk.runners import InMemoryRunner
from google.genai import types
from arthur_observability_sdk import Arthur

# 1. Initialize Arthur
arthur = Arthur(
    api_key="your-api-key",
    base_url="https://your-arthur-engine-instance",
    task_id="<your-task-uuid>",
    service_name="my-adk-app",
)

# 2. Instrument Google ADK (one call — does everything)
arthur.instrument_google_adk()

# 3. Build your agent as usual — no code changes needed
agent = Agent(
    name="assistant",
    model="gemini-2.0-flash",
    instruction="You are a helpful assistant.",
)

runner = InMemoryRunner(agent=agent, app_name="my-adk-app")
session = runner.session_service.create_session(app_name="my-adk-app", user_id="user-42")

# 4. Run the agent — traces are captured automatically
for event in runner.run(
    user_id="user-42",
    session_id=session.id,
    new_message=types.Content(role="user", parts=[types.Part(text="Hello!")]),
):
    if event.content and event.content.parts:
        for part in event.content.parts:
            if part.text:
                print(part.text)

# 5. Flush remaining spans on shutdown
arthur.shutdown()

Key points:

  • instrument_google_adk() must be called before instantiating any ADK Agent or Runner.
  • All LLM calls, tool invocations, and agent orchestration steps are automatically traced.
  • Call arthur.shutdown() when your application exits to flush any remaining traces.
⚠️

Order matters. The instrumentor patches ADK internals at import time — agents created before instrumentation won't be traced.


Trace Agent Actions and Tool Calls

Once instrumented, Arthur automatically captures spans for every layer of your ADK agent:

flowchart TD
    A[Root Span: Agent Run] --> B[LLM Span: Gemini Call]
    A --> C[Tool Span: search_web]
    C --> D[LLM Span: Gemini Call]
    A --> E[Tool Span: calculate]
    E --> F[LLM Span: Gemini Call]
    A --> G[LLM Span: Final Response]

Each span includes:

  • LLM spans — model name, input messages, output completion, token counts
  • Tool spans — tool name, input arguments, output results, duration
  • Agent spans — orchestration decisions, sub-agent delegation

When your ADK agent uses tools, each tool invocation is captured as a child span with full input/output details:

def get_weather(city: str) -> str:
    """Get the current weather for a city."""
    return f"The weather in {city} is sunny, 72°F."

agent = Agent(
    name="weather-assistant",
    model="gemini-2.0-flash",
    instruction="You help users check the weather. Use the get_weather tool.",
    tools=[get_weather],
)

runner = InMemoryRunner(agent=agent, app_name="my-adk-app")
session = runner.session_service.create_session(app_name="my-adk-app", user_id="user-42")

# Tool calls are automatically traced — no extra code needed
for event in runner.run(
    user_id="user-42",
    session_id=session.id,
    new_message=types.Content(
        role="user",
        parts=[types.Part(text="What's the weather in San Francisco?")]
    ),
):
    if event.content and event.content.parts:
        for part in event.content.parts:
            if part.text:
                print(part.text)

Add Session and User Context

Use arthur.attributes() to attach session and user metadata to all spans within a context block.

with arthur.attributes(session_id="sess-1", user_id="user-42"):
    for event in runner.run(
        user_id="user-42",
        session_id=session.id,
        new_message=types.Content(
            role="user",
            parts=[types.Part(text="What's the weather in NYC?")]
        ),
    ):
        if event.content and event.content.parts:
            for part in event.content.parts:
                if part.text:
                    print(part.text)

This is especially useful for:

  • Multi-turn conversations — trace an entire chat session end-to-end
  • Per-user analytics — understand how individual users interact with your application
  • Debugging — filter traces in the Arthur dashboard by session or user

Verify in Arthur

After running your instrumented agent, traces appear in the Arthur GenAI Engine within seconds.

Traces viewed on the Arthur Engine UI

What to look for in the dashboard:

  • Trace list — each runner.run() call appears as a trace with the full agent execution timeline
  • Session grouping — if you used arthur.attributes(session_id=...), traces are grouped by session
  • User filtering — filter by user_id to see a specific user's interactions
  • Tool calls — click into a tool span to see arguments and return values

You can also query traces programmatically:

curl -X GET "${ARTHUR_BASE_URL}/api/v1/traces?task_ids=${ARTHUR_TASK_ID}" \
  -H "Authorization: Bearer ${ARTHUR_API_KEY}"

Troubleshooting

SymptomFix
No traces appearEnsure instrument_google_adk() is called before any Agent() or Runner() instantiation.
Missing tool spansDefine tools before or at the same time as the agent — tools added after instrumentation are not patched.
Connection refusedVerify ARTHUR_BASE_URL points to a reachable Arthur Engine instance.
401 UnauthorizedRegenerate your API key in the Arthur UI and update your environment.
Traces delayedCall arthur.shutdown() to flush the batch exporter, or wait for the next batch interval.

Next Steps

Now that your Google ADK agent is instrumented and sending traces to Arthur, explore these capabilities:

flowchart LR
    A[Instrument Google ADK] --> B[View Traces]
    B --> C[Add Evaluations]
    B --> D[Manage Prompts]
    C --> E[Set Up Continuous Evals]
    D --> F[A/B Test Prompts]
    E --> G[Production Monitoring]
    F --> G