Skip to Content
DocsTracesMulti-Agent Tracing

Multi-Agent Tracing

Multi-agent tracing captures how agents in a system interact — which agent ran, what it received and produced, and when control passed from one agent to another. Each agent becomes a distinct span in the trace, with handoffs recorded as first-class events.

There are two ways to instrument a multi-agent system: auto-instrumentation (zero-config for supported frameworks) and manual decoration (works with any framework or custom code).

Auto-Instrumentation

For supported frameworks, auto_instrument() handles all span creation automatically — no decorator changes required.

app.py
from rhesis.sdk import RhesisClient
from rhesis.sdk.telemetry import auto_instrument

client = RhesisClient(api_key="your-api-key", project_id="your-project-id")
auto_instrument("langgraph")

LangGraph

With LangGraph auto-instrumentation, every graph.invoke(), graph.ainvoke(), graph.stream(), and graph.astream() call is traced automatically. Nodes are classified as agents based on their name.

A node is treated as an agent if its name contains one of these keywords (case-insensitive):

KeywordExample node name
agentresearch_agent, agent_node
specialistbilling_specialist
orchestratormain_orchestrator
coordinatortask_coordinator
supervisorsupervisor

Nodes that don’t match are traced as regular spans. To explicitly mark a node, set agent_name or is_agent in the invocation metadata:

app.py
# Explicit agent name — overrides the node name in traces
graph.invoke(state, config={"metadata": {"agent_name": "researcher"}})

# Mark as agent without renaming
graph.invoke(state, config={"metadata": {"is_agent": True}})

Agent name resolution

The agent name on each ai.agent.invoke span is resolved with this priority order. The first source that produces a value wins:

  1. metadata.agent_name — explicit override passed in the invocation config.
  2. metadata.langgraph_node — set automatically by LangGraph for each node.
  3. serialized.name — provided by the framework when available.
  4. Last segment of serialized.id — falls back to the class path (for example ChatGoogleGenerativeAI).
  5. "unknown" — when none of the above is set.

Handoff detection

Handoffs are detected automatically in two ways:

  • transfer_to_* tools — any tool whose name starts with transfer_to_ creates an ai.agent.handoff span. Detection runs in the LangChain callback’s tool path, so it works for any LangChain-based system (including LangGraph and the LangGraph prebuilt agents that emit these tools), not just LangGraph specifically.
  • Sequential transitions — when one agent ends and a different agent starts, a handoff span is created between them.

More Frameworks

LangGraph and Microsoft Agent Framework both produce native ai.agent.invoke and ai.agent.handoff spans. For MAF, handoffs in a HandoffBuilder workflow are detected and synthesized automatically - see Microsoft Agent Framework. LangChain is supported for general tracing (LLM calls, tools, chains), but agent and handoff span detection there requires LangGraph node naming conventions or transfer_to_* tool names. Support for additional frameworks will be added over time. Use manual decoration for any framework not yet covered.

Manual Decoration

Use @observe with the ai.agent.invoke span name to instrument any agent function, regardless of how it’s built:

app.py
from rhesis.sdk import RhesisClient, observe
from rhesis.telemetry.schemas import AIOperationType
from rhesis.sdk.telemetry.attributes import AIAttributes

client = RhesisClient(api_key="your-api-key", project_id="your-project-id")

@observe(
    span_name=AIOperationType.AGENT_INVOKE,
    **{AIAttributes.AGENT_NAME: "research_agent"}
)
def research_agent(query: str) -> str:
    # Your agent logic here — LLM calls, tool use, etc.
    return run_research(query)

@observe(
    span_name=AIOperationType.AGENT_INVOKE,
    **{AIAttributes.AGENT_NAME: "analyst_agent"}
)
def analyst_agent(findings: str) -> str:
    return run_analysis(findings)

Recording Handoffs

To explicitly record when one agent hands off to another, create a handoff span around the transition:

app.py
from rhesis.telemetry.schemas import AIOperationType
from rhesis.sdk.telemetry.attributes import AIAttributes

@observe(
    span_name=AIOperationType.AGENT_HANDOFF,
    **{
        AIAttributes.AGENT_HANDOFF_FROM: "research_agent",
        AIAttributes.AGENT_HANDOFF_TO: "analyst_agent",
    }
)
def handoff_to_analyst(findings: str) -> str:
    return analyst_agent(findings)

Full Manual Example

multi_agent.py
from rhesis.sdk import RhesisClient, endpoint, observe
from rhesis.telemetry.schemas import AIOperationType
from rhesis.sdk.telemetry.attributes import AIAttributes

client = RhesisClient(api_key="your-api-key", project_id="your-project-id")

@observe(span_name=AIOperationType.AGENT_INVOKE,
         **{AIAttributes.AGENT_NAME: "research_agent"})
def research_agent(query: str) -> str:
    return call_llm(f"Research this topic: {query}")

@observe(span_name=AIOperationType.AGENT_HANDOFF,
         **{AIAttributes.AGENT_HANDOFF_FROM: "research_agent",
            AIAttributes.AGENT_HANDOFF_TO: "analyst_agent"})
def handoff(findings: str) -> str:
    return analyst_agent(findings)

@observe(span_name=AIOperationType.AGENT_INVOKE,
         **{AIAttributes.AGENT_NAME: "analyst_agent"})
def analyst_agent(findings: str) -> str:
    return call_llm(f"Analyze these findings: {findings}")

@endpoint()
def run_pipeline(input: str) -> dict:
    research = research_agent(input)
    result = handoff(research)
    return {"output": result}

Trace Visualization

The Graph View in Rhesis renders agents, tools, and handoffs as nodes and edges, with turn markers for multi-turn conversations:

Multi-agent trace graph view

Span Reference

ai.agent.invoke

AttributeKeyDescription
Operation typeai.operation.typeagent.invoke
Agent nameai.agent.nameAgent identifier
Event: inputai.agent.inputAgent input
Event: outputai.agent.outputAgent output

ai.agent.handoff

AttributeKeyDescription
Operation typeai.operation.typeagent.handoff
From agentai.agent.handoff.fromAgent initiating the handoff
To agentai.agent.handoff.toAgent receiving control

See Semantic Conventions for the full attribute reference.


Related: