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

# Traces

> Wrap one agent execution in one trace with input and output

A **trace** is one end-to-end agent execution — from the user's input to the final response. It is the unit Lemma searches, debugs, and monitors. Everything your agent does inside that execution (LLM calls, tool calls, retrieval, app logic) becomes a **child span** of the trace's root.

<Note>
  **One agent execution = one trace.** Create the root span first, then do your work inside its callback so child spans nest automatically.
</Note>

## The root span

Open the trace with `startActiveObservation`. The callback is the active context: any span you create inside it becomes a child of this root.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    import { startActiveObservation } from "@langfuse/tracing";

    await startActiveObservation("support-agent", async (root) => {
      root.update({ input: userMessage });

      const answer = await runAgent(userMessage);

      root.update({ output: answer });
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    from langfuse import get_client

    langfuse = get_client()

    with langfuse.start_as_current_span(name="support-agent") as root:
        root.update(input=user_message)

        answer = run_agent(user_message)

        root.update(output=answer)
    ```
  </Tab>
</Tabs>

This already produces a useful trace: a named execution with input, output, and duration.

## Always record input and output

The root **input** and **output** are what make a trace debuggable. Record the input as soon as you have it, and the output (or error) before the trace closes.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    await startActiveObservation("support-agent", async (root) => {
      root.update({ input: userMessage });
      try {
        const answer = await runAgent(userMessage);
        root.update({ output: answer });
        return answer;
      } catch (error) {
        root.update({
          level: "ERROR",
          statusMessage: error instanceof Error ? error.message : String(error),
        });
        throw error;
      }
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    with langfuse.start_as_current_span(name="support-agent") as root:
        root.update(input=user_message)
        try:
            answer = run_agent(user_message)
            root.update(output=answer)
        except Exception as error:
            root.update(level="ERROR", status_message=str(error))
            raise
    ```
  </Tab>
</Tabs>

## Name the agent

Give the trace a stable agent name so traces are groupable and filterable by workflow. Set trace-level attributes once and they apply to the whole trace.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    import { propagateAttributes, startActiveObservation } from "@langfuse/tracing";

    await startActiveObservation("support-agent", async (root) => {
      root.update({ input: userMessage });

      await propagateAttributes(
        {
          traceName: "support-agent",
          metadata: { "gen_ai.agent.name": "support-agent" },
        },
        async () => {
          const answer = await runAgent(userMessage);
          root.update({ output: answer });
        },
      );
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    with langfuse.start_as_current_span(name="support-agent") as root:
        root.update(input=user_message)
        langfuse.update_current_trace(
            name="support-agent",
            metadata={"gen_ai.agent.name": "support-agent"},
        )
        answer = run_agent(user_message)
        root.update(output=answer)
    ```
  </Tab>
</Tabs>

For threads, users, and other context, see [Threads & context](/tracing/instrumentation/context).

## Add the work inside

The root on its own gives you input/output/duration. Add detail by nesting child spans **inside the root callback**:

* [Generations](/tracing/instrumentation/generations) for LLM calls (model, tokens, prompt, completion).
* [Tool calls](/tracing/instrumentation/tool-calls) for tool invocations (name, args, result).
* [Spans](/tracing/instrumentation/spans) for everything else (retrieval, ranking, app logic).

```text theme={null}
support-agent              ← trace root (input, output)
├─ draft-reply             ← generation
├─ search_docs             ← tool call
└─ final-answer            ← generation
```

## Next steps

<CardGroup cols={2}>
  <Card title="Generations" icon="sparkles" href="/tracing/instrumentation/generations">
    Capture LLM calls with model and token usage.
  </Card>

  <Card title="Tool calls" icon="wrench" href="/tracing/instrumentation/tool-calls">
    Record tool arguments and results.
  </Card>

  <Card title="Threads & context" icon="users" href="/tracing/instrumentation/context">
    Group conversations and attach users, sessions, and metadata.
  </Card>

  <Card title="Trace contract" icon="file-check" href="/reference/trace-contract">
    The exact shape Lemma reads.
  </Card>
</CardGroup>
