Skip to Content

How do I trace an agent built with AutoGen and deployed to Vercel functions?

Rius traces an AutoGen agent in a Vercel function when you call rius.init() once at the top of your module, wrap the entrypoint with observe, and call client.flush() before each invocation ends. AutoGen’s own spans give you agent and tool structure, and the openai integration adds model calls with tokens and cost. Name the agent with service_name in init(), and turn the heartbeat off because a serverless process is frozen between invocations.

How it works

AutoGen emits create_agent, invoke_agent and execute_tool spans through the global tracer provider, which rius.init() installs. Model calls go through the OpenAI client from autogen-ext[openai], so the openai integration records each one as a ChatCompletion generation with model, tokens and cost. See the AutoGen integration for the full list of what lands where.

Three points matter in a function runtime:

  • Where init goes. Call rius.init() at module level, outside your handler, as in the AutoGen example. It runs once when the module loads, and every installed integration turns on with it. Do not call shutdown() on each invocation, because that releases the global init() slot.
  • Why you flush. Spans export from a background thread. A serverless runtime freezes the process between invocations, so a frozen thread exports nothing and the exit hook may never run. Calling client.flush() at the end of every invocation sends the spans before the freeze.
  • Why the heartbeat is off. The heartbeat is a signal for long-lived processes. Between invocations the pings stop and the instance flaps between alive, stale and gone. Rius recommends heartbeat=False for Lambda-style runtimes, and the same applies to a function runtime.

With the heartbeat off, the agent still appears from its traces, but it is marked no heartbeat and lists no instances.

Set it up

  1. Install the SDK with the OpenAI instrumentation and AutoGen. The full install options are on the Integrations page.

    pip install "glassflow-rius[openai]" "autogen-agentchat" "autogen-ext[openai]"
  2. Initialize at module level with a service name and the heartbeat off, wrap your entrypoint, and flush in a finally block so spans leave even when the run raises. The wrapper, agent and model names below are examples, so use your own.

    import rius from autogen_agentchat.agents import AssistantAgent from autogen_ext.models.openai import OpenAIChatCompletionClient from rius import SpanKind client = rius.init( api_key="ri_...", service_name="support-agent", heartbeat=False, ) def account_status(email: str) -> str: """Fetch a customer's account standing by email.""" return lookup(email) @rius.observe(name="handle-ticket", kind=SpanKind.AGENT) async def handle_ticket(ticket: str) -> str: agent = AssistantAgent( name="support_agent", model_client=OpenAIChatCompletionClient(model="gpt-5-mini"), tools=[account_status], system_message="Answer support questions; check accounts with the tool.", ) result = await agent.run(task=ticket) return str(result.messages[-1].content) async def handler(ticket: str) -> str: try: return await handle_ticket(ticket) finally: client.flush(timeout_millis=30_000)
  3. Call handler from your function’s entry point. Flush once per invocation, at the end, and never inside inner loops or request hot paths, because flush() blocks until the export completes.

  4. Name the agent in the places that matter:

    • service_name in init() sets the name of the agent in Rius. Or drop the argument and set RIUS_SERVICE_NAME instead, or set RIUS_AGENT_NAME if you want the agent grouped under a different name than the service.
    • The name in observe (here handle-ticket) names the root span of each trace.
    • The name you give AssistantAgent (here support_agent) appears on the create_agent and invoke_agent spans.
  5. Leave your tool functions unwrapped. AutoGen runs tools in a detached context, so an observe wrapper would split off as its own single-span trace. The framework’s execute_tool spans already cover them.

  6. Make one request that calls a tool and open the trace in the console. You should see the root span, invoke_agent, execute_tool and the ChatCompletion generation with model, tokens and cost.

  7. Check the return value of flush() while you set this up. It returns False on timeout or when the latest export was rejected, for example because of a bad API key, so True means your spans left the process. If a trial has ended or billing blocks ingest, exports are refused and those traces are lost. See Reliability for retries and the full flush rules.

What you get

  • One trace per invocation, with the handle-ticket root span, create_agent, invoke_agent, execute_tool and a ChatCompletion generation for each model call.
  • Tokens and a list-price cost estimate in US dollars for every model call, computed by Rius from the model and token counts.
  • An agent in Rius named from your service_name, with its traces, cost and errors, even though the heartbeat is off.
  • Spans that leave the process before the runtime freezes it, because each invocation ends with a flush.
  • Optional grouping by conversation: scope each run with rius.session() and reuse your conversation id so one conversation is one session.

When something else fits

  • If your Vercel function is written in TypeScript, AutoGen has no TypeScript integration. See the Integrations support matrix for what each language covers.
  • If you run the agent in a long-lived process instead, such as a server or worker, you can leave the heartbeat on and skip the per-invocation flush. Python flushes at exit for you.

Still stuck? Get help

Last updated on