# Agent identity (/docs/govern/agent-identity)

Every trace that reaches AISquare carries an agent name. The gateway routes
each trace by the pair &#x2A;*(workspace, agent name)** to a studio and its Rule
Book, and the dashboard groups runs, verdicts, and cost by the same name. A
trace without a name has nowhere to go. This page is the contract: where the
name lives, how each integration path sets it, and what breaks when it is
missing.

## Why identity exists [#why-identity-exists]

Identity is written to the trace as `attributes["agent.name"]` on the trace's
**root span**. The gateway reads it once per trace and uses it three ways:

* **Routing.** Each trace routes by (workspace, agent name) to a studio. This
  is how a run ends up on the right dashboard.
* **Rule Book binding.** The name decides which Rule Book judges the run. Bind
  a Rule Book to `support-bot` and every run named `support-bot` is evaluated
  against it.
* **Grouping.** Runs, policy verdicts, and cost aggregate per agent. The name
  is the axis.

## Two channels, one name [#two-channels-one-name]

The SDK carries identity on two channels. They answer different questions, and
you should keep them equal.

* **Span identity** (`agent.name` on the root span) routes the trace and
  groups your dashboards.
* **Policy identity** (`AISQUARE_AGENT_NAME`) tells policy checks which Rule
  Book judges the agent.

From SDK 1.0.6 the two are unified: `AISQUARE_AGENT_NAME` also serves as the
**fallback trace identity**. When a span batch arrives with no explicit name
on its root span, the SDK stamps it with `AISQUARE_AGENT_NAME`. Explicit
naming in code always wins; the env var is the safety net. Keep it equal to
your `agent_name` so both channels agree.

## How identity is set, per path [#how-identity-is-set-per-path]

Explicit naming is the contract. Each integration path has exactly one place
to put the name.

| Path                                         | Where the name goes                                            |
| -------------------------------------------- | -------------------------------------------------------------- |
| Raw pipeline / custom spans                  | `AgentRunTracer(agent_name=...)`                               |
| Agno                                         | `Agent(name=...)` — required                                   |
| LangChain                                    | `metadata={"agent_name": ...}` in the invoke config            |
| Custom or OpenAI-wrapped via `GovernedAgent` | `agent_name=` on the wrapper; tracing is automatic (SDK 1.0.6) |
| Proxy                                        | `X-Agent-Name` header                                          |

**Raw pipelines.** Open an `AgentRunTracer` around the run. Its `agent_name`
lands on the root span and every child inherits the route.

```python
# .env: EXPLAINABILITY_GATEWAY_URL, EXPLAINABILITY_API_KEY,
# EXPLAINABILITY_AGENTS=support-bot   <- pre-registers; must equal agent_name below
import aisquare.explainability as sdk

sdk.init_from_env()

with sdk.AgentRunTracer(agent_name="support-bot"):
    with sdk.LLMCallTracer(model="gpt-4o", provider="openai") as llm:
        ...
sdk.flush()
```

**Agno.** The agent must be named. The SDK reads `Agent(name=...)` and uses it
as the trace identity; an unnamed Agno agent has no identity to send.

```python
my_agent = Agent(name="support-bot", ...)
```

**LangChain.** Pass the name in the invoke config's metadata. The callback
handler picks it up per call.

```python
chain.invoke(inputs, config={"metadata": {"agent_name": "support-bot"}})
```

**Custom and OpenAI-wrapped agents.** From SDK 1.0.6, `GovernedAgent`
auto-opens an `AgentRunTracer` around `.run()` and `.arun()`, so the wrapped
agent is traced and named in one move.

```python
agent = GovernedAgent.wrap(my_agent, agent_name="support-bot")
agent.run("...")   # traced automatically, routed as support-bot
```

<Callout intent="warning" title="Streaming run methods are not auto-traced">
  The automatic tracer wraps `.run()` and `.arun()`. If your run method is
  generator-shaped (it streams or yields), the run outlives the call and the
  wrapper cannot see its end. Open an `AgentRunTracer(agent_name=...)` around
  the code that consumes the stream instead.
</Callout>

**Proxy.** Identity comes from the `X-Agent-Name` header on each request. If
the header is absent, the proxy falls back to the pipeline id
(`X-Pipeline-Id`), then to `claude-code`. Set the header if you want the name
to mean something.

## Running more than one agent [#running-more-than-one-agent]

Name each agent in code. `AISQUARE_AGENT_NAME` holds **one** name; it is a
single-agent convenience, not a naming scheme.

<Callout intent="warning" title="The env var cannot name a fleet">
  If several agents run in one process and rely on the env-var fallback, they
  all inherit the same identity. Their traces collapse into one agent —
  one shared Rule Book, merged dashboards, blended cost — and nothing warns you.
  The runs still arrive; they are just indistinguishable. With multiple agents,
  set `agent_name` explicitly on every one.
</Callout>

## Pre-register your roster [#pre-register-your-roster]

`EXPLAINABILITY_AGENTS` takes a comma-separated list of agent names and
registers them at `init_from_env()`, so the first trace from each agent routes
immediately instead of waiting on registration.

```bash
export EXPLAINABILITY_AGENTS="support-bot,billing-bot"
```

The names must equal the `agent_name` values used in code, character for
character. A roster entry that matches nothing registers an agent that never
runs; a coded name missing from the roster still needs registration before its
first trace routes.

## Naming rules [#naming-rules]

* **Unique per workspace.** The name is the routing key; two agents sharing a
  name are one agent as far as AISquare is concerned.
* **Stable across deploys.** Rule Book bindings key off the name. Rename the
  agent and its bindings, history, and dashboards do not follow.
* **Allowed characters:** letters, digits, `.`, `_`, `-`, and spaces.

## What failure looks like [#what-failure-looks-like]

When identity is missing or unknown, trace ingest answers `409 Conflict` with
a machine-readable code in `detail.code`:

| Code                   | Meaning                                              | What to do                                                       |
| ---------------------- | ---------------------------------------------------- | ---------------------------------------------------------------- |
| `no_agent_identity`    | A root span arrived with no identity on it.          | Fix the integration — name the agent. Retrying will not succeed. |
| `awaiting_trace_route` | A child-only batch arrived before its root span.     | Transient. The SDK retries it automatically.                     |
| `agent_not_registered` | The name is set but not registered in the workspace. | Register the agent, or enable auto-discovery on the workspace.   |

Full remediation for each code is in the
[error reference](/docs/reference/errors#409-conflict--trace-ingest). If you
are not sure which case you are in, run `explainability-doctor`: its
`agent_identity` check flags naming gaps in your integration, and its
`delivery_backlog` check reads the local delivery queue and reports exactly
which of these failures is holding traces back.

## Next steps [#next-steps]

<Cards>
  <Card title="Connect your agents" href="/docs/govern/connect-your-agents" description="The SDK and proxy paths end to end, with naming in place." />

  <Card title="Errors" href="/docs/reference/errors" description="The 409 ingest codes with full remediation." />

  <Card title="Prevent" href="/docs/govern/prevent" description="Bind a Rule Book to the agent name and enforce at the decision point." />

  <Card title="Proxy integration" href="/docs/govern/proxy-integration" description="Header-based identity for the zero-code path." />
</Cards>
