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 callshutdown()on each invocation, because that releases the globalinit()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,
staleandgone. Rius recommendsheartbeat=Falsefor 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
-
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]" -
Initialize at module level with a service name and the heartbeat off, wrap your entrypoint, and flush in a
finallyblock 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) -
Call
handlerfrom your function’s entry point. Flush once per invocation, at the end, and never inside inner loops or request hot paths, becauseflush()blocks until the export completes. -
Name the agent in the places that matter:
service_nameininit()sets the name of the agent in Rius. Or drop the argument and setRIUS_SERVICE_NAMEinstead, or setRIUS_AGENT_NAMEif you want the agent grouped under a different name than the service.- The
nameinobserve(herehandle-ticket) names the root span of each trace. - The
nameyou giveAssistantAgent(heresupport_agent) appears on thecreate_agentandinvoke_agentspans.
-
Leave your tool functions unwrapped. AutoGen runs tools in a detached context, so an
observewrapper would split off as its own single-span trace. The framework’sexecute_toolspans already cover them. -
Make one request that calls a tool and open the trace in the console. You should see the root span,
invoke_agent,execute_tooland theChatCompletiongeneration with model, tokens and cost. -
Check the return value of
flush()while you set this up. It returnsFalseon timeout or when the latest export was rejected, for example because of a bad API key, soTruemeans 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-ticketroot span,create_agent,invoke_agent,execute_tooland aChatCompletiongeneration 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.