Skip to Content

How do I trace a Python AI agent running in a Docker container?

Rius traces a Python agent in a Docker container when you call rius.init() once at process start and pass the API key and service name to the container as environment variables. Spans are exported from a background thread, and you flush before the process exits or freezes. Once init runs, every installed integration is switched on automatically, so your model calls arrive as spans with tokens and cost.

How it works

  • One init per process. Call init() as early as possible in your entrypoint. A second global init() logs a warning and returns the existing client unchanged. To reconfigure, call client.shutdown() first.
  • Background export. Spans queue in memory and leave in batches from a background thread, so the network is never on your agent’s hot path. In Python, pending spans also flush at interpreter exit on a clean shutdown.
  • Short runs and hard stops. A container that runs one task and exits can finish before the first scheduled batch fires, so call client.flush() before it ends. If the container is killed hard, the last batch can be lost.
  • Freezing runtimes. If your container image runs in a Lambda-style runtime, the process is suspended between invocations and a frozen thread exports nothing. Call client.flush() at the end of every invocation and turn the heartbeat off with RIUS_HEARTBEAT=false.
  • Replicas. Each process start is a fresh instance under the same agent name, so three containers show as three instances of one agent. Keep service_name constant across replicas and deploys.
  • Forked workers. With gunicorn or multiprocessing inside the container, do nothing at fork time. The exporter re-creates its thread in each child, and each worker gets its own instance.

Set it up

  1. Install the SDK in your image, along with the library your agent calls. An extra installs only the instrumentation, never the library itself. The installation page covers the full setup.

    pip install "glassflow-rius[openai]" openai
  2. Pass the settings to the container as environment variables. init() reads them, and explicit arguments win over the environment:

    • RIUS_API_KEY: your key, which starts with ri_. Keep it out of the image and inject it at run time.
    • RIUS_SERVICE_NAME: a constant name for your agent. Without it, spans carry unknown_service.
    • RIUS_SERVICE_VERSION: the build this container runs, for example your image tag. It is stamped on spans as service.version.
    • RIUS_MAIN_AGENT_VERSION: the version of the agent definition (prompt, tools, policy). It is stamped as rius.main_agent.version, the version the Agents page compares.
  3. Call init() once in the entrypoint, run your agent, then flush and shut down at the end of a short-lived run:

    import rius client = rius.init() # reads RIUS_API_KEY, RIUS_SERVICE_NAME and the rest # ... run your agent ... client.flush(timeout_millis=30_000) # force an export client.shutdown() # drain, stop the thread, release init()

    For a long-running server, you can skip the explicit calls in Python and rely on the exit hook on clean shutdown. Never call flush() inside request handlers, because it blocks until the export completes.

  4. Keep the heartbeat at its default for a long-lived container. It is on by default, needs RIUS_API_KEY (or an Authorization header), and powers the Agents view. RIUS_HEARTBEAT_INTERVAL defaults to 15 seconds. Keep it at 15 or below, because the platform marks an instance stale after 30 seconds and gone after 60 seconds without a ping. Without credentials, or with RIUS_HEARTBEAT=false, the agent is marked no heartbeat and has no instances.

  5. If a busy agent drops spans, raise OTEL_BSP_MAX_QUEUE_SIZE (default 2048) before lowering the sample rate. For quicker shutdowns, lower OTEL_BSP_SCHEDULE_DELAY (default 5000 ms). The reliability page lists the batching variables and the retry rules.

What you get

  • Every model call and tool call from the container as a trace, with tokens and a list-price cost estimate in US dollars computed by Rius. The integrations page lists the libraries and frameworks that are traced automatically.
  • One agent in the Agents view, with one instance per container process.
  • A clean exit and a crash told apart. shutdown() sends a final stopped ping, while an unhandled SIGTERM or a SIGKILL sends none, so the instance reports gone. The SDK installs no signal handlers.
  • Spans from short jobs that would otherwise be lost, because you flush before the process ends.
  • Optional partial spans (RIUS_PARTIAL_SPANS), which export a content-free snapshot when each span starts. A run that crashes leaves a record of what it was doing.

When something else fits

  • If the container runs a Lambda-style function, flush at the end of every invocation and turn the heartbeat off, since heartbeats are a long-lived-process signal.
  • If the agent is written in TypeScript or JavaScript, use the TypeScript SDK, which registers no exit hook, so you must call await client.shutdown() yourself.

Still stuck? Get help

Last updated on