Skip to content

Tracing

tako-governance::otel wires tracing + opentelemetry-otlp so every orchestrator run, provider call, and tool invocation emits structured spans with both tako.* and gen_ai.* semconv attributes.

Initialisation

import tako

guard = tako.tracing.init_otlp(
    endpoint="http://otel-collector:4317",
    protocol="grpc",   # or "http"
    resource_attrs={"service.name": "my-agent", "deployment.env": "prod"},
)
# ... your application ...
tako.tracing.shutdown_otlp()  # flushes pending spans

init_otlp returns a process-global guard that flushes pending spans on interpreter exit. You only call this once per process.

For local dev or tests, the lighter tako.tracing.init() writes to stderr via tracing-subscriber.

What gets emitted

Span name Notable attributes
tako.orchestrator.run tako.orchestrator.kind, tako.principal.tenant_id, tako.principal.user_id
tako.orchestrator.dispatch worker.name, worker.provider.id (Conductor)
tako.provider.chat tako.provider.id, tako.provider.model, tako.tokens.input, tako.tokens.output, tako.cost.usd, plus gen_ai.system, gen_ai.request.model
tako.tool.invoke tako.tool.name, tako.tool.duration_ms
tako.policy.evaluate tako.policy.stage, tako.policy.decision

Spans are nested: orchestrator runs are the root; provider calls and tool invocations are children. The Conductor's per-worker dispatches are children of the orchestrator run.

OTLP sanity check

docker run -d -p 4317:4317 otel/opentelemetry-collector-contrib
RUST_LOG=tako=debug python examples/01_single_agent.py

Open the collector's debug exporter or hook up Jaeger / Honeycomb / Tempo / your collector of choice — any OTel backend works.