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

# Observe (Bring Your Own Agent)

> Instrument existing agent frameworks with one call — optional agent name, intent, and session stamps so multi-agent apps stay distinct in the Portal.

Already running LangChain, LangGraph, CrewAI, OpenAI Agents, or another supported framework? Call **`maitai.observe()`** once at process start. Maitai instruments installed frameworks and exports OpenInference/OTel spans to `POST /api/v1/traces`. Observed runs show up as agents in the Portal (same Runs / session / cost surfaces as native agents).

<Info>
  Requires the optional extra: `pip install 'maitai-python[observe]'`. Capture is fire-and-forget — never raises into your application. Use `MAITAI_LOGGING_ENABLED` / sample-rate env vars the same way as `maitai.log()`.
</Info>

## One-line setup

```python theme={null}
import maitai

maitai.observe(application="support-app")
# your existing agent.invoke() / graph.invoke() / crew.kickoff() …
```

First trace with an unknown agent name auto-creates an observed agent — no pre-registration.

## Agent name (multi-agent processes)

When frameworks emit generic span names, two agents in one process can collapse into a single observed agent. Stamp an explicit name:

```python theme={null}
import maitai

maitai.observe(application="support-app")

maitai.set_agent("triage")          # sticky until cleared
# …

with maitai.agent("billing"):       # scoped (callable module — same attr as hosted agent client)
    billing_graph.invoke(payload)
```

Stamps OpenInference `agent.name` on spans (does not override a name the framework already set). The backend already prefers `agent.name` when resolving the observed agent.

<Note>
  `maitai.agent` is also the hosted-agent client package (`Completions`). Calling it as `maitai.agent("name")` enters the observe naming context; importing `from maitai.agent import Completions` is unchanged.
</Note>

## Intent override

Observed LLM steps default `action_type` to the span name. Override when you want a stable Maitai intent:

```python theme={null}
with maitai.intent("classify_ticket"):
    graph.invoke(ticket)
```

Also available as sticky `maitai.set_intent("classify_ticket")`. Backend reads `maitai.intent` when re-logging LLM steps through the BYO chat-log path.

## Session contract

| Situation                                                                                    | `session_id`                                               |
| -------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| You call `maitai.set_session(...)` / `with maitai.session(...):` (or `observe(session=...)`) | Your id — groups many invocations into one Portal session  |
| Framework sets `session.id` / conversation id attrs                                          | That value                                                 |
| Nothing set                                                                                  | **`session_id = trace_id`** (one invocation = one session) |

```python theme={null}
maitai.set_session("user-42-thread-7")
agent.invoke(...)   # same session
agent.invoke(...)
```

## Workflows vs agents

A fixed pipeline should be a **workflow**, not an observed agent. Declare it:

```python theme={null}
maitai.observe(
    application="checkout-app",
    workflows=["order-graph"],   # stamps maitai.run_type=workflow when names match
)
# or: maitai.observe(application="checkout-app", run_type="workflow")
```

When a workflow graph also contains nested agent boundaries (for example a LangGraph sub-agent inside a fixed pipeline), ingestion **dual-persists**: a `workflow_run` for the container plus observed agents for nested boundaries. Profile spans carry `refs` / `nav_path` so the Portal can drill into those agents. The workflow root is not double-counted as an agent.

See [Workflow logging (BYOW)](/sdk/workflow_logging) for code-native and no-code workflow capture without `observe()`.
