# This automatically adds callbacks to the coordinator and ALL sub-agents
from opik.integrations.adk import track_adk_agent_recursive
track_adk_agent_recursive(coordinator_agent, multi_agent_tracer)
```

The trace can now be viewed in the UI, showing the complete hierarchy:

<Frame>
  <img src="https://files.buildwithfern.com/https://opik.docs.buildwithfern.com/docs/opik/2026-01-30T17:33:07.444Z/img/cookbook/google_adk_integration_multi_agent.png" />
</Frame>

The `track_adk_agent_recursive` approach is particularly powerful for:

* **Multi-agent systems** with coordinator and specialist agents
* **Sequential agents** with multiple processing steps
* **Parallel agents** executing tasks concurrently
* **Loop agents** with iterative workflows
* **Agent tools** that contain nested agents
* **Complex hierarchies** with deeply nested agent structures

By calling `track_adk_agent_recursive` once on the top-level agent, all child agents and their operations are automatically instrumented without any additional code

## Cost Tracking

Opik automatically tracks token usage and cost for all LLM calls during the agent execution, not only for the Gemini LLMs, but including the models accessed via `LiteLLM`.

<Tip>
  View the complete list of supported models and providers on the [Supported Models](/tracing/supported_models) page.
</Tip>

## Agent Graph Visualization

Opik automatically generates visual representations of your agent workflows using Mermaid diagrams. The graph shows:

* **Agent hierarchy** and relationships
* **Sequential execution** flows
* **Parallel processing** branches
* **Loop structures** and iterations
* **Tool connections** and dependencies

The graph is automatically computed and stored with each trace, providing a clear visual understanding of your agent's execution flow:

For weather time agent the graph will look like that:

<Frame>
  <img src="https://files.buildwithfern.com/https://opik.docs.buildwithfern.com/docs/opik/2026-01-30T17:33:07.444Z/img/tracing/adk/adk_weather_time_graph_screenshot.png" />
</Frame>

For more complex agent architectures displaying a graph may be even more beneficial:

<Frame>
  <img src="https://files.buildwithfern.com/https://opik.docs.buildwithfern.com/docs/opik/2026-01-30T17:33:07.444Z/img/tracing/adk/adk_code_assistant_graph_screenshot.png" />
</Frame>

## Example 4: Hybrid Tracing - Combining Opik Decorators with ADK Callbacks

This advanced example shows how to combine Opik's `@opik.track` decorator with ADK's callback system. This is powerful when you have complex multi-step tools that perform their own internal operations that you want to trace separately, while still maintaining the overall agent trace context.

You can use `track_adk_agent_recursive` together with `@opik.track` decorators on your tool functions for maximum visibility:

```python
from opik import track

@track(name="weather_data_processing", tags=["data-processing", "weather"])
def process_weather_data(raw_data: dict) -> dict:
    """Process raw weather data with additional computations."""
    # Simulate some data processing steps that we want to trace separately
    processed = {
        "temperature_celsius": raw_data.get("temp_c", 0),
        "temperature_fahrenheit": raw_data.get("temp_c", 0) * 9/5 + 32,
        "conditions": raw_data.get("condition", "unknown"),
        "comfort_index": "comfortable" if 18 <= raw_data.get("temp_c", 0) <= 25 else "less comfortable"
    }
    return processed

@track(name="location_validation", tags=["validation", "location"])
def validate_location(city: str) -> dict:
    """Validate and normalize city names."""
    # Simulate location validation logic that we want to trace
    normalized_cities = {
        "nyc": "New York",
        "ny": "New York",
        "new york city": "New York",
        "london uk": "London",
        "london england": "London",
        "tokyo japan": "Tokyo"
    }

    city_lower = city.lower().strip()
    validated_city = normalized_cities.get(city_lower, city.title())

    return {
        "original": city,
        "validated": validated_city,
        "is_valid": city_lower in ["new york", "london", "tokyo"] or city_lower in normalized_cities
    }

@track(name="advanced_weather_lookup", tags=["weather", "api-simulation"])
def get_advanced_weather(city: str) -> dict:
    """Get weather with internal processing steps tracked by Opik decorators."""

    # Step 1: Validate location (traced by @opik.track)
    location_result = validate_location(city)

    if not location_result["is_valid"]:
        return {
            "status": "error",
            "error_message": f"Invalid location: {city}"
        }

    validated_city = location_result["validated"]

    # Step 2: Get raw weather data (simulated)
    raw_weather_data = {
        "New York": {"temp_c": 25, "condition": "sunny", "humidity": 65},
        "London": {"temp_c": 18, "condition": "cloudy", "humidity": 78},
        "Tokyo": {"temp_c": 22, "condition": "partly cloudy", "humidity": 70}
    }

    if validated_city not in raw_weather_data:
        return {
            "status": "error",
            "error_message": f"Weather data unavailable for {validated_city}"
        }

    raw_data = raw_weather_data[validated_city]

    # Step 3: Process the data (traced by @opik.track)
    processed_data = process_weather_data(raw_data)

    return {
        "status": "success",
        "city": validated_city,
        "report": f"Weather in {validated_city}: {processed_data['conditions']}, {processed_data['temperature_celsius']}°C ({processed_data['temperature_fahrenheit']:.1f}°F). Comfort level: {processed_data['comfort_index']}.",
        "raw_humidity": raw_data["humidity"]
    }