> ## Documentation Index
> Fetch the complete documentation index at: https://developers.agentx.so/llms.txt
> Use this file to discover all available pages before exploring further.

# Tracing

> Record production agent runs from any Python framework

The `Tracer` captures your agent's inputs, outputs, latency, and tool calls — with a single decorator or context manager. Traces appear in the **Live Traces** tab and can be evaluated against your test datasets.

## Decorator

The simplest usage. AgentX captures function arguments as `input`, the return value as `output`, and wall-clock time as `latencyMs`.

```python theme={null}
from agentx import AgentX

client = AgentX.from_env()
tracer = client.tracer

@tracer.trace("customer-support-agent", framework="langchain", model="gpt-4o")
def handle(query: str) -> str:
    return chain.invoke(query)

handle("How do I reset my password?")
```

Works with async functions too:

```python theme={null}
@tracer.trace("async-agent", framework="openai-agents")
async def handle_async(query: str) -> str:
    result = await runner.run(agent, query)
    return result.final_output
```

## Context manager

Use when you need to set input/output manually or record tool calls mid-span.

```python theme={null}
with tracer.trace("rag-agent", framework="langchain") as span:
    span.input = {"query": query, "user_id": user_id}

    kb_result = search_knowledge_base(query)
    span.add_tool_call("search_knowledge_base", input=query, output=kb_result, latency_ms=190)

    answer = llm.invoke(f"Context: {kb_result}\n\nQuery: {query}")
    span.output = answer
```

### `_TraceSpan` attributes and methods

| Name                                                     | Description                                         |
| -------------------------------------------------------- | --------------------------------------------------- |
| `span.input = value`                                     | Override the captured input                         |
| `span.output = value`                                    | Set the output (required in context manager mode)   |
| `span.add_tool_call(name, *, input, output, latency_ms)` | Record a tool call made during this span            |
| `span.set_error(message)`                                | Mark the span as failed with a custom error message |

## `tracer.trace()` parameters

| Parameter    | Type   | Description                                                            |
| ------------ | ------ | ---------------------------------------------------------------------- |
| `name`       | `str`  | Agent or operation label shown in the UI                               |
| `framework`  | `str`  | `"langchain"`, `"crewai"`, `"openai-agents"`, `"anthropic"`, or custom |
| `model`      | `str`  | LLM model used, e.g. `"gpt-4o"`, `"claude-sonnet-4-6"`                 |
| `session_id` | `str`  | Groups traces from the same user session or thread                     |
| `metadata`   | `dict` | Arbitrary key-value metadata (not indexed, max 16 KB)                  |

## Framework examples

<CodeGroup>
  ```python LangChain theme={null}
  from langchain_openai import ChatOpenAI
  from langchain_core.messages import HumanMessage
  from agentx import AgentX

  client = AgentX.from_env()

  llm = ChatOpenAI(model="gpt-4o")

  @client.tracer.trace("support-agent", framework="langchain", model="gpt-4o")
  def handle(query: str) -> str:
      return llm.invoke([HumanMessage(content=query)]).content

  handle("How do I reset my password?")
  client.tracer.flush(timeout=10)
  ```

  ```python CrewAI theme={null}
  from crewai import Agent, Task, Crew
  from agentx import AgentX

  client = AgentX.from_env()

  @client.tracer.trace("research-crew", framework="crewai")
  def run_crew(topic: str) -> str:
      agent = Agent(role="Researcher", goal=f"Research {topic}", backstory="...")
      task = Task(description=f"Research {topic}", agent=agent, expected_output="...")
      crew = Crew(agents=[agent], tasks=[task])
      return str(crew.kickoff())

  run_crew("What is the refund policy?")
  client.tracer.flush(timeout=10)
  ```

  ```python OpenAI Agents SDK theme={null}
  import asyncio
  from agents import Agent, Runner
  from agentx import AgentX

  client = AgentX.from_env()
  agent = Agent(name="Support", instructions="You are a helpful support agent.")

  @client.tracer.trace("openai-support-agent", framework="openai-agents", model="gpt-4o")
  async def run(query: str) -> str:
      result = await Runner.run(agent, query)
      return result.final_output

  asyncio.run(run("How do I cancel my subscription?"))
  client.tracer.flush(timeout=10)
  ```

  ```python Anthropic theme={null}
  import anthropic
  from agentx import AgentX

  agentx_client = AgentX.from_env()
  claude_client = anthropic.Anthropic()

  @agentx_client.tracer.trace(
      "claude-agent", framework="anthropic", model="claude-sonnet-4-6"
  )
  def run(query: str) -> str:
      msg = claude_client.messages.create(
          model="claude-sonnet-4-6",
          max_tokens=1024,
          messages=[{"role": "user", "content": query}],
      )
      return msg.content[0].text


  run("How do I cancel my subscription?")
  agentx_client.tracer.flush(timeout=10)
  ```

  ```python Google ADK theme={null}
  import asyncio
  from google.adk.agents import Agent
  from google.adk.runners import Runner
  from google.adk.sessions import InMemorySessionService
  from google.genai import types
  from agentx import AgentX

  client = AgentX.from_env()

  agent = Agent(
      name="support_agent",
      model="gemini-3.5-flash",
      instruction="You are a helpful support agent.",
  )
  runner = Runner(
      agent=agent,
      app_name="support-app",
      session_service=InMemorySessionService(),
  )

  @client.tracer.trace("google-support-agent", framework="google-adk", model="gemini-3.5-flash")
  async def run(query: str) -> str:
      session = runner.session_service.create_session_sync(
          app_name="support-app", user_id="user-1"
      )
      for event in runner.run(
          user_id="user-1",
          session_id=session.id,
          new_message=types.Content(role="user", parts=[types.Part(text=query)]),
      ):
          if event.is_final_response():
              return event.content.parts[0].text
      return ""

  asyncio.run(run("How do I cancel my subscription?"))
  client.tracer.flush(timeout=10)
  ```
</CodeGroup>

## Session grouping

Use `session_id` to link traces from the same user conversation:

```python theme={null}
import uuid

session_id = str(uuid.uuid4())

@tracer.trace("support-agent", session_id=session_id)
def handle(query: str) -> str:
    ...

handle("First question")
handle("Follow-up question")
# Both traces appear linked in the UI
```

## Error handling

Exceptions inside a traced function are captured as the `error` field and re-raised — the trace is still submitted:

```python theme={null}
@tracer.trace("my-agent")
def risky(query: str) -> str:
    raise ValueError("Model unavailable")

try:
    risky("test")
except ValueError:
    pass
# Trace submitted with error: "Model unavailable"
```

To set an error manually in context manager mode:

```python theme={null}
with tracer.trace("my-agent") as span:
    try:
        result = call_agent(query)
        span.output = result
    except Exception as e:
        span.set_error(str(e))
        raise
```

## Flushing

Traces are sent in a background thread. Call `flush()` before process exit in scripts:

```python theme={null}
tracer.flush(timeout=5.0)
```

## Evaluating a trace

Score a previously recorded trace against a dataset without re-running the agent:

```python theme={null}
result = tracer.evaluate_trace(
    trace_id="6876abc123def456789abc01",
    dataset_id="6876ddd222bbb333ccc444ee",
    question_index=0,
)

print(result["rating"])        # 1–5
print(result["justification"]) # LLM explanation
```

## Limits

| Limit                         | Value     |
| ----------------------------- | --------- |
| Traces per minute per API key | 300       |
| Max tool calls per trace      | 50        |
| Max `input` / `output` size   | 1 MB each |
| Max `metadata` size           | 16 KB     |
