How to Use the Tracing Module to Observe and Debug Agent Executions in AgentScope

Enable tracing by initializing AgentScope with a tracing_url, then apply decorators like @trace_llm or @trace_reply to capture OpenTelemetry spans for every agent interaction.

AgentScope is an open-source multi-agent framework that provides built-in observability through its tracing module. By leveraging OpenTelemetry, you can use the tracing module to observe and debug agent executions in real-time, capturing the full lifecycle of LLM calls, tool invocations, and agent responses. This guide walks through enabling tracing, decorating your components, and interpreting the resulting telemetry data.

How Tracing Works in AgentScope

The tracing system in AgentScope is built on OpenTelemetry and consists of several coordinated components. When trace_enabled is set to True (either via agentscope.init or manual configuration), the system activates span creation for decorated functions.

The core workflow involves:

  1. Setup: The setup_tracing function in src/agentscope/tracing/_setup.py configures the OTLP exporter with your provided endpoint and registers a BatchSpanProcessor to batch and transmit spans efficiently.

  2. Tracer Retrieval: The _get_tracer() function retrieves a tracer named "agentscope" (versioned with __version__) from the same setup module.

  3. Enablement Check: Before creating any span, _check_tracing_enabled() in src/agentscope/tracing/_trace.py reads the global flag _config.trace_enabled to determine if tracing should proceed.

  4. Decoration: Specialized decorators like trace, trace_llm, trace_reply, trace_format, trace_toolkit, and trace_embedding (all defined in src/agentscope/tracing/_trace.py) wrap target functions to start spans, capture request/response attributes, and set status codes.

  5. Attribute Extraction: The _get_*_attributes utilities in src/agentscope/tracing/_extractor.py convert internal AgentScope objects (messages, tool calls, embeddings) into OpenTelemetry-compatible attribute dictionaries.

  6. Attribute Definitions: Constants for attribute keys are centralized in src/agentscope/tracing/_attributes.py via SpanAttributes and OperationNameValues classes.

When a decorated function executes, the decorator creates a span with a generated operation name, records request attributes, executes the function, then records response attributes or exceptions, naturally forming a parent-child hierarchy that mirrors your call chain.

Enabling Tracing in Your Application

Before you can observe agent executions, you must install the optional dependencies and initialize the tracing system.

Installing OpenTelemetry Dependencies

Tracing requires the OpenTelemetry Python SDK. Install it via pip:

pip install opentelemetry-sdk opentelemetry-exporter-otlp

These packages are declared as optional extras in the AgentScope repository, so you may also install them as extras if supported by your distribution.

Initializing with agentscope.init

The simplest way to enable tracing is to provide a tracing_url when initializing AgentScope:

import agentscope

# Connect to Langfuse, Arize Phoenix, or any OTLP-compatible backend

agentscope.init(tracing_url="https://cloud.langfuse.com/api/public/otel/v1/traces")

This call internally executes setup_tracing from src/agentscope/tracing/_setup.py, configures the OTLP exporter with your endpoint, registers a BatchSpanProcessor, and sets the global _config.trace_enabled = True.

Manual Setup

If you need finer control, import and call setup_tracing directly:

from agentscope.tracing import setup_tracing

setup_tracing(
    endpoint="http://localhost:4318/v1/traces",
    service_name="my-agent-service"
)

Decorating Components for Observability

Once tracing is enabled, apply decorators to the functions and methods you want to observe. The src/agentscope/tracing/_trace.py file provides specialized decorators for different component types.

General Function Tracing

Use the @trace decorator for arbitrary functions:

from agentscope.tracing import trace

@trace(name="my_custom_func")
async def my_custom_func(x: int) -> int:
    return x * 2

This creates a span named my_custom_func and records the input x and the output value as attributes.

LLM Calls

Decorate your model's __call__ method with @trace_llm to capture full request and response payloads:

from agentscope.tracing import trace_llm
from agentscope.model import ChatModelBase, ChatResponse

class MyLLM(ChatModelBase):
    @trace_llm
    async def __call__(self, messages: list[dict]) -> ChatResponse:
        # Simulated response

        return ChatResponse(id="msg", content=[])

Spans are labeled with the operation name chat and include the full request/response payloads. See the implementation in trace_llm.

Agent Reply

Track agent response generation with @trace_reply:

from agentscope.tracing import trace_reply
from agentscope.agent import AgentBase
from agentscope.message import Msg, TextBlock

class EchoAgent(AgentBase):
    @trace_reply
    async def reply(self) -> Msg:
        return Msg("assistant", [TextBlock(type="text", text="Hello!")])

Formatter

Capture formatting logic with @trace_format:

from agentscope.tracing import trace_format
from agentscope.formatter import FormatterBase

class SimpleFormatter(FormatterBase):
    @trace_format
    async def format(self) -> list[dict]:
        return [{"role": "user", "content": "Hello"}]

Toolkit (Tool Calls)

Trace tool invocations with @trace_toolkit:

from agentscope.tracing import trace_toolkit
from agentscope.tool import Toolkit, ToolResponse
from agentscope.message import TextBlock

class MyToolkit(Toolkit):
    @trace_toolkit
    async def call_tool_function(self, tool_call):
        # Your tool logic here

        return ToolResponse(content=[TextBlock(type="text", text="Done")])

Embedding Model

Monitor embedding generation with @trace_embedding:

from agentscope.tracing import trace_embedding
from agentscope.embedding import EmbeddingModelBase

class MyEmbedding(EmbeddingModelBase):
    @trace_embedding
    async def __call__(self, texts: list[str]) -> list[list[float]]:
        return [[0.1, 0.2, 0.3] for _ in texts]

Viewing and Interpreting Traces

Once your application runs with decorated components, the OpenTelemetry exporter sends span data to your configured backend. In tools like Langfuse or Arize Phoenix, you will see a hierarchical trace view:


Agent.reply
 ├─ LLM.chat (or LLM.embed)
 ├─ Toolkit.call_tool_function
 └─ Formatter.format

Each node displays request attributes (e.g., message content, tool input parameters) and response attributes (e.g., generated text, tool output). Errors appear with a red ERROR status and include the captured exception message and stack trace. This structure allows you to pinpoint latency bottlenecks, inspect payload contents, and debug failures across the entire agent execution chain.

Complete End-to-End Example

Here is a runnable example that ties together initialization, decoration, and execution:

import agentscope
from agentscope.tracing import trace_reply, trace_llm
from agentscope.agent import AgentBase
from agentscope.model import ChatModelBase, ChatResponse
from agentscope.message import Msg, TextBlock

# 1️⃣ Initialise tracing (replace with your backend URL)

agentscope.init(tracing_url="https://cloud.langfuse.com/api/public/otel/v1/traces")

# 2️⃣ Define a traced LLM

class EchoLLM(ChatModelBase):
    @trace_llm
    async def __call__(self, messages, **kwargs):
        # Echo the last user message

        user_msg = messages[-1]["content"]
        return ChatResponse(
            id="msg",
            content=[TextBlock(type="text", text=user_msg)]
        )

# 3️⃣ Define a traced agent that uses the LLM

class EchoAgent(AgentBase):
    def __init__(self):
        super().__init__(name="echo")
        self.llm = EchoLLM(stream=False)

    @trace_reply
    async def reply(self) -> Msg:
        # Simple single‑turn conversation

        user_msg = [{"role": "user", "content": "Hello"}]
        llm_res = await self.llm(user_msg)
        return Msg("assistant", llm_res.content, "assistant")

# 4️⃣ Run the agent

agent = EchoAgent()
response = await agent()
print(response.content)   # → [TextBlock(type='text', text='Hello')]

After executing this script, your tracing backend (e.g., Langfuse) displays:

  • EchoAgent.reply as the parent span.
  • EchoLLM.__call__ as a child span containing request attribute "user_message": "Hello" and response attribute "generated_text": "Hello".

This visibility allows you to debug execution flows, inspect message payloads, and identify performance bottlenecks in real time.

Key Source Files

The tracing implementation spans several files in the src/agentscope/tracing/ directory:

File Role Direct Link
_setup.py Configures the OTLP exporter and registers the BatchSpanProcessor. [_setup.py](https://github.com/agentscope-ai/agentscope/blob/main/src/agentscope/tracing/_setup.py)
_trace.py Implements the trace decorator and specialized variants (trace_llm, trace_reply, etc.). [_trace.py](https://github.com/agentscope-ai/agentscope/blob/main/src/agentscope/tracing/_trace.py)
_extractor.py Serializes AgentScope objects into OpenTelemetry-compatible attributes. [_extractor.py](https://github.com/agentscope-ai/agentscope/blob/main/src/agentscope/tracing/_extractor.py)
_attributes.py Defines attribute key constants (SpanAttributes, OperationNameValues). [_attributes.py](https://github.com/agentscope-ai/agentscope/blob/main/src/agentscope/tracing/_attributes.py)
__init__.py Public API exports (setup_tracing, decorators). [__init__.py](https://github.com/agentscope-ai/agentscope/blob/main/src/agentscope/tracing/__init__.py)
tests/tracing_test.py Unit tests demonstrating usage patterns. [tracing_test.py](https://github.com/agentscope-ai/agentscope/blob/main/tests/tracing_test.py)

Summary

  • Enable tracing by passing tracing_url to agentscope.init() or calling setup_tracing() directly from src/agentscope/tracing/_setup.py.
  • Decorate components using specialized decorators (@trace_llm, @trace_reply, @trace_toolkit, etc.) from src/agentscope/tracing/_trace.py to capture request/response data.
  • Observe hierarchies in OTLP-compatible backends like Langfuse or Arize Phoenix, where parent spans (agents) contain child spans (LLMs, tools, formatters).
  • Debug effectively by inspecting span attributes for inputs, outputs, and exceptions without modifying your core business logic.

Frequently Asked Questions

Do I need to modify my existing agent code to enable tracing?

No. You only need to initialize tracing with agentscope.init(tracing_url="...") and add decorators to the methods you want to observe. The decorators in src/agentscope/tracing/_trace.py wrap your existing functions without changing their signatures or behavior, capturing inputs and outputs only when trace_enabled is True.

What backends are compatible with AgentScope tracing?

AgentScope uses the OpenTelemetry Protocol (OTLP) to export spans. Any OTLP-compatible backend works, including Langfuse, Arize Phoenix, Jaeger, Zipkin (via OpenTelemetry Collector), and the standard OpenTelemetry Collector itself. You simply provide the OTLP endpoint URL when calling setup_tracing() or agentscope.init().

How do I trace synchronous functions?

The decorators in src/agentscope/tracing/_trace.py handle both synchronous and asynchronous functions automatically. When you apply @trace, @trace_llm, or any other specialized decorator to a sync function, the wrapper detects the function type and manages the span context appropriately. You do not need separate decorators for sync and async code.

Can I disable tracing for specific components while keeping it enabled globally?

Yes. Tracing is controlled by the global _config.trace_enabled flag, but you decide which methods to decorate. Simply omit the @trace_* decorators on components you want to exclude from telemetry. Alternatively, conditionally apply decorators based on your own configuration logic. Since the decorators in src/agentscope/tracing/_trace.py only create spans when the global flag is enabled, removing the decorator entirely is the most reliable way to exclude specific functions from tracing.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →