Skip to Content

How do I trace a document processing pipeline built with the OpenAI Agents SDK?

Rius traces a document processing pipeline built with the OpenAI Agents SDK in Python: the openai extra captures model calls and observe wrappers add your stages. Every model call arrives as an LLM span with model, tokens and cost. Your wrappers add the run root, a span for each stage and a retrieval span. Set a session per document and a user per requester so related traces group together.

How it works

The framework drives the OpenAI client underneath, so the openai integration captures each model call with no framework-specific code. The agent loop, tool executions and handoffs run inside the framework, so the structure of your pipeline comes from observe wrappers. The OpenAI Agents SDK integration page covers the framework in full, and the tracing guide covers the wrappers.

For a document pipeline, the trace needs these layers on top of the model calls:

  • A run root. Wrap the entrypoint that handles one document with observe and SpanKind.AGENT. It becomes the root span, under the name you pass.
  • A span per stage. Wrap each stage, such as extraction, chunking or summarizing, with a bare observe. Each becomes a span named after its function.
  • A retrieval span. Open a span with SpanKind.RETRIEVER around each search. Pass data_source_id for the index and top_k for how many documents you asked for. It records document ids and scores, not document text.
  • Tool spans. Wrap each tool body with observe and SpanKind.TOOL, so the span is named execute_tool plus the tool name.

The framework’s own tracing uploads to the OpenAI platform and is independent of OpenTelemetry. Turn it off with set_tracing_disabled(True) if Rius should be your only telemetry path.

Set it up

  1. Install the SDK with the openai extra and the framework. Integrations lists the install line for every framework.

    pip install "glassflow-rius[openai]" openai-agents
  2. Initialize Rius once at startup, then wrap your pipeline. Put @function_tool outermost on a tool and observe on the function body, because the framework reads the function signature for its tool schema.

    import rius from agents import Agent, Runner, function_tool, set_tracing_disabled from rius import SpanKind rius.init(api_key="ri_...", service_name="doc-pipeline") set_tracing_disabled(True) @rius.observe def extract_text(path: str) -> str: ... @function_tool @rius.observe(kind=SpanKind.TOOL) def lookup_clause(query: str) -> str: """Find the clause that matches the query.""" with rius.start_as_current_span( kind=SpanKind.RETRIEVER, data_source_id="contract-kb", top_k=5 ) as obs: obs.set_input(query) docs = search(query) obs.set_retrieved_documents([{"id": d.id, "score": d.score} for d in docs]) return render(docs) summary_agent = Agent( name="summary_agent", instructions="Summarize the document; check clauses with the tool.", tools=[lookup_clause], model="gpt-5-mini", ) @rius.observe(name="process-document", kind=SpanKind.AGENT) def process_document(path: str) -> str: text = extract_text(path) result = Runner.run_sync(summary_agent, text) return str(result.final_output)
  3. Set the session and the user around each document. Reuse your document or job id as the session id, so every trace for that document groups together. Pass an opaque id for the user rather than an email.

    with rius.session(document_id), rius.user(requester_id): process_document(path)
  4. Use underscores in agent names. Hyphens are rewritten in the generated handoff tool name, and the framework warns on every run.

  5. Run one document that calls a tool, then open the trace in the console. You should see the root AGENT span, your stage spans, the retrieval span, the TOOL span and the LLM generations.

What you get

  • One trace per document, with the run root on top and your stages, retrieval, tool calls and model calls nested under it.
  • A generation for each model call with model, tokens and cost. Cost is a list-price estimate in US dollars, computed by Rius, so you never send it.
  • Retrieval spans that show which index was searched, how many documents were asked for and which ids came back with what score.
  • Sessions that group every trace for one document, and a Users view that ties work to each requester.
  • The tool spans carry the kind that Rius uses to recognize tool calls, because you marked them with SpanKind.TOOL.

When something else fits

  • The OpenAI Agents SDK integration is Python only. In TypeScript, trace the model calls with manual generations.
  • A handoff produces no span of its own, and responses are all named Response. If you need to see which agent spoke, add an observe wrapper at each agent entry.
  • Streamed runs carry token counts only when usage is enabled on the underlying client.

Still stuck? Get help

Last updated on