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

# Agent Relationship Graph

> Visualize agent dependencies and agent-to-agent delegation, auto-populated from proxy telemetry and OpenTelemetry span parentage

The relationship graph is designed to map every agent's dependencies: which LLM models it calls, which providers it uses, and which agents it delegates work to. The schema (`relationship_type`, `target`, `request_count`, `last_seen_at`) supports all three today.

<Note>
  As currently deployed, only agent-to-agent `delegates_to` edges are actually written - derived from OpenTelemetry span parentage at ingest (see below). Model (`calls_model`) and provider (`uses_provider`) edges are supported by the schema and response shape but are not yet populated by proxy telemetry; until that write path ships, expect `GET /agents/{id}/relationships` and `GET /relationships/graph` to surface delegation edges only.
</Note>

## How It Works

1. At OTLP ingest, MeshAI walks each span's parentage to detect cross-agent delegation and records it as a `delegates_to` relationship (model/provider relationship capture from proxy telemetry is planned but not yet wired in).
2. Query individual agent dependencies or the full organization-wide graph.
3. The graph data is structured for direct use with visualization libraries like D3.js.

## Get Agent Relationships

Retrieve the models, providers, and other agents an individual agent depends on, as a flat list ordered by request count:

<CodeGroup>
  ```bash curl theme={null}
  curl https://api.meshai.dev/api/v1/agents/01ARZ3NDEKTSV4RRFFQ69G5FAV/relationships \
    -H "Authorization: Bearer msh_YOUR_API_KEY"
  ```

  ```python SDK theme={null}
  from meshai import MeshAI

  client = MeshAI(api_key="msh_...")

  relationships = client.get_agent_relationships("01ARZ3NDEKTSV4RRFFQ69G5FAV")
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  {
    "success": true,
    "data": [
      { "id": 101, "relationship_type": "calls_model", "target": "gpt-4o", "request_count": 980, "last_seen_at": "2026-03-18T14:00:00Z" },
      { "id": 102, "relationship_type": "calls_model", "target": "claude-sonnet-4-6", "request_count": 320, "last_seen_at": "2026-03-18T13:45:00Z" },
      { "id": 103, "relationship_type": "uses_provider", "target": "openai", "request_count": 1240, "last_seen_at": "2026-03-18T14:00:00Z" },
      { "id": 104, "relationship_type": "delegates_to", "target": "01BX5ZZKBKACTAV9WEVGEMMVRZ", "request_count": 42, "last_seen_at": "2026-03-18T12:00:00Z" }
    ]
  }
  ```
</ResponseExample>

Each item is `{id, relationship_type, target, request_count, last_seen_at}` - there is no separate `dependencies.providers` / `dependencies.models` grouping and no `agent_name` in this response.

## Get Full Relationship Graph

Retrieve the complete organization-wide graph with nodes and edges, suitable for visualization:

<CodeGroup>
  ```bash curl theme={null}
  curl https://api.meshai.dev/api/v1/relationships/graph \
    -H "Authorization: Bearer msh_YOUR_API_KEY"
  ```

  ```python SDK theme={null}
  graph = client.get_relationship_graph()
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  {
    "success": true,
    "data": {
      "nodes": [
        { "id": "01ARZ3NDEKTSV4RRFFQ69G5FAV", "name": "production-summarizer", "type": "agent", "status": "healthy", "blocked": false, "framework": "crewai" },
        { "id": "01BX5ZZKBKACTAV9WEVGEMMVRZ", "name": "support-bot", "type": "agent", "status": "healthy", "blocked": false, "framework": "langgraph" },
        { "id": "calls_model:gpt-4o", "name": "gpt-4o", "type": "model" },
        { "id": "calls_model:claude-sonnet-4-6", "name": "claude-sonnet-4-6", "type": "model" },
        { "id": "uses_provider:openai", "name": "openai", "type": "provider" }
      ],
      "edges": [
        { "source": "01ARZ3NDEKTSV4RRFFQ69G5FAV", "target": "calls_model:gpt-4o", "type": "calls_model", "request_count": 980 },
        { "source": "01ARZ3NDEKTSV4RRFFQ69G5FAV", "target": "calls_model:claude-sonnet-4-6", "type": "calls_model", "request_count": 320 },
        { "source": "01BX5ZZKBKACTAV9WEVGEMMVRZ", "target": "calls_model:gpt-4o", "type": "calls_model", "request_count": 450 },
        { "source": "01ARZ3NDEKTSV4RRFFQ69G5FAV", "target": "uses_provider:openai", "type": "uses_provider", "request_count": 1240 },
        { "source": "01ARZ3NDEKTSV4RRFFQ69G5FAV", "target": "01BX5ZZKBKACTAV9WEVGEMMVRZ", "type": "delegates_to", "request_count": 42 }
      ]
    }
  }
  ```
</ResponseExample>

## Graph Structure

The graph uses a node-edge model:

### Node Types and IDs

| Type                                        | Node id                                                      | Description                                                                                                         |
| ------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- |
| `agent`                                     | The agent's own id                                           | A registered agent in your organization; carries `status`, `blocked`, and `framework`                               |
| model / provider / tool (non-agent targets) | `"{relationship_type}:{target}"` (e.g. `calls_model:gpt-4o`) | A synthetic node keyed by relationship type and target name - there are no separate `mdl_*` / `prv_*` id namespaces |

Agent-to-agent (`delegates_to`) edges resolve to the real target agent's own node id - they never get a synthetic node.

### Edge Semantics

Edges represent usage relationships accumulated over time (not scoped to a queried time window). Each edge has a `type` (the relationship type) and a `request_count` - there is no `weight` field.

* **agent -> model** (`calls_model`): The agent called this model N times.
* **agent -> provider** (`uses_provider`): The agent used this provider N times.
* **agent -> agent** (`delegates_to`): The source agent delegated work to the target agent N times. See below.

## Agent-to-Agent Delegation

When one agent invokes another (an orchestrator handing work to a worker), MeshAI records a `delegates_to` edge between the two real agents. Delegation is derived from standard OpenTelemetry span parentage in your OTLP telemetry: when an agent's LLM span sits under a span started by a different agent, that is a delegation, and the edge's request count increments.

How it works:

1. Your agents export traces with standard `parentSpanId` links (every OTel SDK does this by default; both JSON and protobuf exports are supported).
2. At ingest, MeshAI walks each LLM span's ancestor chain. Chains are followed through the agent's own internal spans, and parents that arrived in an earlier export batch are resolved too.
3. Cross-agent links become persisted `delegates_to` edges with running request counts and last-seen timestamps. Re-exported batches never double-count: only spans that pass ingest deduplication increment edges.
4. In the graph response, delegation edges connect the two real agent nodes, so the fleet-wide delegation topology renders directly.

No proprietary SDK or per-framework adapter is required; any framework that emits OpenTelemetry with normal span parentage produces the delegation map.

<Note>
  Why this matters for governance: emergent multi-agent risks cannot be detected through single-agent evaluation. The delegation map shows which agents act on behalf of which, so you can scope policies, budgets, and risk classifications to the chains that actually exist rather than to agents in isolation.
</Note>

## D3.js Visualization

The graph response is structured for direct use with D3.js force-directed graphs:

```javascript theme={null}
const response = await fetch("https://api.meshai.dev/api/v1/relationships/graph", {
  headers: { Authorization: "Bearer msh_YOUR_API_KEY" },
});
const { data } = await response.json();

// data.nodes and data.edges are ready for D3
const simulation = d3.forceSimulation(data.nodes)
  .force("link", d3.forceLink(data.edges).id(d => d.id))
  .force("charge", d3.forceManyBody())
  .force("center", d3.forceCenter(width / 2, height / 2));
```

## Use Cases

* **Impact analysis** - Before blocking a provider, see which agents depend on it.
* **Cost optimization** - Identify agents that use expensive models and could be routed to cheaper alternatives.
* **Compliance reporting** - Document all model/provider dependencies for EU AI Act Article 12.
* **Incident response** - Quickly trace which agents are affected by a provider outage.
