Intégrer l’observabilité de l’agent via OTel directement

Ce guide vous guide tout au long de l’envoi de la télémétrie de l’agent à l’agent 365 directement via OpenTelemetry (OTLP/HTTP+JSON). Avant de commencer, lisez les concepts d’observabilité d’Agent 365 pour comprendre le modèle, les flux d’authentification et les interfaces dans lesquelles vos données atterrissent.

Important

Le chemin OTel direct est l’exception, et non la valeur par défaut. Utilisez-le uniquement si vous disposez déjà d'un pipeline OpenTelemetry, que votre infrastructure ne peut pas utiliser le Kit de développement logiciel (SDK) Agent 365, ou que votre agent se trouve dans un langage que le Kit de développement logiciel (SDK) ne prend pas encore en charge (par exemple, Java). Pour tous les autres, l’approche recommandée est Microsoft OpenTelemetry Distro, qui fournit un SDK d’observabilité unifié pour Agent 365, Microsoft Foundry, Azure Monitor et plus encore. L’Observability SDK précédent continue de fonctionner sans changement cassant, mais n’est plus recommandé pour les nouvelles intégrations ; des recommandations de migration pour les utilisateurs actuels du SDK seront bientôt disponibles.

Prerequisites

Vérifiez que les configurations suivantes sont en place avant les flux de télémétrie.

Qui What
Administrateur de locataire Inscrivez-vous à Agent 365 et accordez votre consentement pour votre application agent. Voir Intégration à l’agent 365. Sans locataire sous licence, l’ingestion est supprimée en mode silencieux : la requête retourne 200 OK avec partialSuccess: null , mais les données ne s’affichent jamais en aval.
Administrateur de locataire Attribuez une licence Microsoft 365 E7 ou Microsoft Agent 365 à au moins un utilisateur du locataire. La référence SKU présente n’est pas suffisante. L’affectation à un utilisateur démarre le flux de travail principal Defender qui active l’ingestion. Sans licence attribuée, les requêtes renvoient 200 OK avec partialSuccess: null et les données sont supprimées silencieusement.
Administrateur de locataire Accordez le consentement du locataire. Consultez Autoriser les agents à accéder aux ressources Microsoft 365. Sans cela, les jetons sont émis sans le rôle/la portée, et les requêtes renvoient 403.
Votre équipe de développement Inscrivez votre application (application standard Microsoft Entra ou blueprint). Consultez Prise en main du développement Agent 365.
Votre équipe de développement Ajouter Agent365.Observability.OtelWrite dans Autorisations de l’API (rôle d’application pour S2S, portée pour les autorisations déléguées). Pour les blueprints, consultez Configurer les autorisations héritées. Coordonnez-vous avec l’équipe d’intégration Agent 365 pour activer l’autorisation.

Recettes d’authentification

Les quatre recettes utilisent le point de terminaison standard des jetons Microsoft Entra :

Champ Valeur
Point de terminaison de jeton https://login.microsoftonline.com/{your-tenant-id}/oauth2/v2.0/token
Ressource (aud dans le jeton retourné) 9b975845-388f-4429-889e-eab1ef63949c (accepte également api://9b975845-388f-4429-889e-eab1ef63949c)
Étendue S2S 9b975845-388f-4429-889e-eab1ef63949c/.default
Étendue OBO 9b975845-388f-4429-889e-eab1ef63949c/Agent365.Observability.OtelWrite

Les recettes ci-dessous montrent http brut pour plus de clarté. En production, préférez Microsoft. Identity.Web ou une autre bibliothèque MSAL, qui gère l’actualisation et la mise en cache des jetons.

Quelle recette ai-je besoin ?

Mon modèle d’application Mon flux OAuth Accéder à
Inscription d’application Microsoft Entra standard S2S (informations d’identification du client) S2S, application Microsoft Entra standard
Inscription d’application Microsoft Entra standard OBO (délégué) OBO, application standard Microsoft Entra
Identité de l’agent issue d’un modèle S2S (informations d’identification du client) Identité de l’agent dérivé de Blueprint S2S
Identité de l’agent dérivée du modèle OBO / Collègue d’IA OBO, identité d’agent dérivée de Blueprint

S2S, application Microsoft Entra standard

Un POST vers le point de terminaison de jeton du locataire avec grant_type=client_credentials. Authentifiez l’application à l’aide d’une clé secrète client, d’un certificat (assertion JWT signée) ou d’une identité managée ou d’informations d’identification fédérées.

POST https://login.microsoftonline.com/{your-tenant-id}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded

client_id={your-app-id}
&scope=9b975845-388f-4429-889e-eab1ef63949c%2F.default
&client_secret={secret}
&grant_type=client_credentials

Le jeton retourné a appid/azp = {your-app-id}, roles contenant Agent365.Observability.OtelWriteet .aud = 9b975845-... Utilisez-le sur l’itinéraire /observabilityService/.../traces .

Pour l’authentification basée sur un certificat, remplacez client_secret={secret} par client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion={signed-jwt}.

Identité d’agent S2S dérivée de Blueprint

Les identités d’agent n’ont pas d'identifiants propres. Le modèle d’identité de l’agent contient les informations d’identification (identité managée FIC, certificat ou secret client) et émet des jetons pour le compte de ses identités d’agent enfants au moyen d’un échange en deux étapes. Pour plus d’informations, consultez le flux OAuth pour application autonome.

  1. Le plan s’authentifie et obtient un jeton d’échange d’identité fédérée T1 :

    • {blueprint-credential} est le jeton MSI du blueprint, le JWT signé par certificat ou l’assertion de jeton d’échange secret par configuration de blueprint.
    POST https://login.microsoftonline.com/{your-tenant-id}/oauth2/v2.0/token
    Content-Type: application/x-www-form-urlencoded
    
    client_id={blueprint-app-id}
    &scope=api%3A%2F%2FAzureADTokenExchange%2F.default
    &fmi_path={agent-identity-app-id}
    &client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
    &client_assertion={blueprint-credential}
    &grant_type=client_credentials
    
  2. L’identité de l’agent échange T1 contre le jeton de la ressource Agent 365 Observability :

    POST https://login.microsoftonline.com/{your-tenant-id}/oauth2/v2.0/token
    Content-Type: application/x-www-form-urlencoded
    
    client_id={agent-identity-app-id}
    &scope=9b975845-388f-4429-889e-eab1ef63949c%2F.default
    &client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
    &client_assertion={T1}
    &grant_type=client_credentials
    
    • Le jeton retourné a appid/azp = {agent-identity-app-id}, roles contenant Agent365.Observability.OtelWriteet .aud = 9b975845-...
    • Utilisez ce jeton sur l’itinéraire /observabilityService/.../traces .
    • L’URL {agentId} correspond à l’appId d’identité de l’agent, et non à l’appId du plan directeur.

OBO, application Microsoft Entra standard

Recevez le jeton utilisateur entrant Tc de l’appelant en amont (Bearer ou PFAT), puis échangez-le :

POST https://login.microsoftonline.com/{your-tenant-id}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded

client_id={your-app-id}
&scope=9b975845-388f-4429-889e-eab1ef63949c%2FAgent365.Observability.OtelWrite
&client_secret={secret}
&grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&assertion={Tc}
&requested_token_use=on_behalf_of

Pour l’authentification de certificat, remplacez client_secret={secret} par la même client_assertion_type + client_assertion paire que dans S2S.

Le jeton retourné a appid/azp = {your-app-id}, scp contenant Agent365.Observability.OtelWriteet .aud = 9b975845-... Utilisez-le sur l’itinéraire /observability/.../traces . Un jeton d’actualisation est retourné en même temps ; mettez en cache et réutilisez-le au lieu de réexécuter l’échange sur chaque appel.

Identité de l’agent issue du blueprint OBO (y compris le coéquipier IA)

Le flux On-Behalf-Of comporte trois étapes principales. Pour plus d’informations, consultez Les flux OAuth de l’agent : pour le compte du flux.

  1. Recevez le jeton utilisateur Tc. Pour un coéquipier IA, ce jeton représente le propre compte d’utilisateur de l’agent ; sinon, il représente l’appelant humain.

  2. Le blueprint s’authentifie et obtient T1, identique au flux d’identité de l’agent dérivé du blueprint S2S.

  3. L’identité de l’agent échange T1 et Tc pour un jeton de ressource délégué :

    POST https://login.microsoftonline.com/{your-tenant-id}/oauth2/v2.0/token
    Content-Type: application/x-www-form-urlencoded
    
    client_id={agent-identity-app-id}
    &scope=9b975845-388f-4429-889e-eab1ef63949c%2FAgent365.Observability.OtelWrite
    &client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
    &client_assertion={T1}
    &grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
    &assertion={Tc}
    &requested_token_use=on_behalf_of
    

Le jeton retourné a appid/azp = {agent-identity-app-id}, scp contenant Agent365.Observability.OtelWriteet représente l’utilisateur de l’agent. Utilisez-le sur l’itinéraire /observability/.../traces . L’URL {agentId} est l’appId de l’identité de l’agent, et non l’appId du plan. Un jeton d’actualisation est retourné en même temps ; mettez en cache et réutilisez-le.

Revendications requises sur le jeton retourné

Route S2S (/observabilityService/...) - jeton d’application uniquement :

Réclamation Valeur requise
aud 9b975845-388f-4429-889e-eab1ef63949c (ou api://9b975845-...)
roles Doit contenir Agent365.Observability.OtelWrite
appid (v1) ou azp (v2) Doit correspondre à l’URL {agentId}
scp Doit être absent

Itinéraire délégué (/observability/...) - jeton délégué utilisateur (Bearer ou PFAT):

Réclamation Valeur requise
aud 9b975845-388f-4429-889e-eab1ef63949c (ou api://9b975845-...)
scp Doit contenir Agent365.Observability.OtelWrite
appid / azp Doit correspondre à l’URL {agentId}

La route déléguée accepte à la fois les jetons Bearer et MSAuth1.0 PFAT. Les appelants directs doivent utiliser Bearer. Si vous ne savez pas lequel vous avez, utilisez Bearer.

Points de terminaison

Deux itinéraires ; choisissez la façon dont votre service s’authentifie, et non par ce que fait l’utilisateur :

POST https://agent365.svc.cloud.microsoft/observabilityService/tenants/{tenantId}/otlp/agents/{agentId}/traces?api-version=1   # S2S
POST https://agent365.svc.cloud.microsoft/observability/tenants/{tenantId}/otlp/agents/{agentId}/traces?api-version=1          # OBO

En-têtes :

Authorization: Bearer <token>      # or MSAuth1.0 ... for delegated PFAT
Content-Type: application/json

Paramètres URL

  • {tenantId} - GUID du tenant client. Le serveur considère cela comme une source faisant autorité ; si vos spans définissent microsoft.tenant.id et qu’il n’est pas d’accord, la requête est rejetée.
  • {agentId} - appId de l’application appelante (également OAuth client_id). Pour les identités dérivées du modèle, il s’agit de l’appId de l’identité de l’agent, et non de l’appId du modèle. Doit être égal à la appid / azp revendication de votre jeton.
  • api-version=1 -Obligatoire.

Encodage du corps de la requête

Le corps suit la structure OTLP/HTTP+JSON standard : un ExportTraceServiceRequest avec resourceSpansscopeSpansspans. Gardez à l’esprit les détails suivants :

  • traceId (16 octets) et spanId (8 octets) sont envoyés sous forme de chaînes hexadécimaux minuscules.
  • startTimeUnixNano / endTimeUnixNano sont des chaînes contenant des nanosecondes d’époque Unix.
  • kind est la valeur entière de l’énumération OTLP (par exemple 1 pour INTERNAL) ; status.code est l’énumération entière (par exemple 1 pour OK, 2 pour ERROR).
  • Toutes les valeurs d’attribut sont envoyées en tant que stringValue.

Forme de réponse

Un appel réussi retourne 200 OK:

{ "partialSuccess": null }

Si certains segments ont été rejetés par le filtre par segment :

{
  "partialSuccess": {
    "rejectedSpans": 2,
    "errorMessage": "Dropped 2 non-A365 span(s) ..."
  }
}

Les noms de champs sont camelCase sur le fil. Vérifiez toujours partialSuccess : un code 200 avec tous vos spans rejetés est un résultat réel que vous devez signaler. Limites et conditions d’abandon répertorie les cas d’abandon silencieux dans lesquels un code 200 est renvoyé avec partialSuccess: null, bien qu’aucune donnée n’apparaisse en aval.

Demande la plus petite possible

Le test de bout en bout le plus simple envoie un unique invoke_agent span. Ce span est le plus petit élément qui arrive dans Microsoft Defender.

Étape 1. Obtenez un jeton Bearer. Pour S2S, utilisez les identifiants client avec la portée 9b975845-388f-4429-889e-eab1ef63949c/.default (voir Recettes d’authentification pour la procédure complète).

Étape 2. POST un seul segment :

TOKEN="$(./get-token.sh)"
TENANT_ID="<customer-tenant-guid>"
AGENT_ID="<your-agent-app-id>"

curl -i -X POST \
  "https://agent365.svc.cloud.microsoft/observabilityService/tenants/${TENANT_ID}/otlp/agents/${AGENT_ID}/traces?api-version=1" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  --data @- <<EOF
{
  "resourceSpans": [{
    "scopeSpans": [{
      "scope": { "name": "my-instrumentation", "version": "1.0.0" },
      "spans": [{
        "traceId": "0102030405060708090a0b0c0d0e0f10",
        "spanId":  "1111111111111111",
        "parentSpanId": "",
        "name": "invoke_agent",
        "kind": 1,
        "startTimeUnixNano": "1736175600000000000",
        "endTimeUnixNano":   "1736175601500000000",
        "status": { "code": 1 },
        "attributes": [
          { "key": "gen_ai.operation.name", "value": { "stringValue": "invoke_agent" } },
          { "key": "gen_ai.agent.id",       "value": { "stringValue": "${AGENT_ID}" } },
          { "key": "gen_ai.agent.name",     "value": { "stringValue": "MyAgent" } },
          { "key": "microsoft.a365.agent.blueprint.id", "value": { "stringValue": "${AGENT_ID}" } },
          { "key": "gen_ai.conversation.id","value": { "stringValue": "conv-001" } },
          { "key": "microsoft.channel.name","value": { "stringValue": "web" } },
          { "key": "user.id",               "value": { "stringValue": "<entra-user-objectid>" } },
          { "key": "client.address",        "value": { "stringValue": "10.1.2.80" } },
          { "key": "server.address",        "value": { "stringValue": "myagent.example.com" } },
          { "key": "server.port",           "value": { "stringValue": "443" } },
          { "key": "gen_ai.input.messages", "value": { "stringValue": "[{\"role\":\"user\",\"content\":\"hi\"}]" } },
          { "key": "gen_ai.output.messages","value": { "stringValue": "[{\"role\":\"assistant\",\"content\":\"hello\"}]" } }
        ]
      }]
    }]
  }]
}
EOF

Étape 3. Attendez 200 OK avec le corps suivant :

{ "partialSuccess": null }

Étape 4. Vérifiez que les données ont réellement atterri. Un code 200 OK ne prouve pas l’ingestion ; Vérifier l’ingestion explique le processus de vérification. Pour effectuer une requête POST avec un fichier de corps de requête enregistré à la place, remplacez --data @- <<EOF ... EOF par --data @./otlp-request.json.

Exemple d’exécution de l’agent

Un utilisateur sur Microsoft Teams demande « Quelle est la météo à Seattle ? ». Votre agent appelle une fonction GetWeather, demande à un LLM de mettre en forme la réponse, puis répond. Cette seule exécution comporte quatre segments :

graph TD
    A["<b>invoke_agent</b> · spanId=A · parentSpanId=∅<br/><i>root - the run itself</i>"]
    B["<b>chat</b> · spanId=B · parentSpanId=A<br/><i>LLM picks the tool / formats reply</i>"]
    C["<b>execute_tool</b> · spanId=C · parentSpanId=A<br/><i>the GetWeather call</i>"]
    D["<b>output_messages</b> · spanId=D · parentSpanId=A<br/><i>final reply emitted to the user</i>"]
    A --> B
    A --> C
    A --> D

Attributs de l’exécution entière définis sur chaque span :

Caractéristique Exemple de valeur
traceId 0102030405060708090a0b0c0d0e0f10
gen_ai.conversation.id 19:abc@thread.tacv2
microsoft.session.id session-1234
microsoft.channel.name msteams
gen_ai.agent.id <AGENT_APP_ID>
gen_ai.agent.name WeatherBot
microsoft.a365.agent.blueprint.id <BLUEPRINT_APP_ID>
user.id <entra-user-objectid>
client.address 10.1.2.80
server.address weatherbot.example.com
server.port 443

Important

Ces attributs à l’échelle de l’exécution ne sont pas propagés automatiquement. Vous devez définir gen_ai.conversation.id, microsoft.channel.nameet microsoft.session.id sur chaque étendue vous-même.

Étendue A : invoke_agent (racine)

{
  "traceId": "0102030405060708090a0b0c0d0e0f10",
  "spanId": "1111111111111111",
  "parentSpanId": "",
  "name": "invoke_agent",
  "kind": 1,
  "startTimeUnixNano": "1736175600000000000",
  "endTimeUnixNano":   "1736175601500000000",
  "status": { "code": 1 },
  "attributes": [
    { "key": "gen_ai.operation.name",   "value": { "stringValue": "invoke_agent" } },
    { "key": "gen_ai.execution.type",   "value": { "stringValue": "HumanToAgent" } },
    { "key": "gen_ai.input.messages",   "value": { "stringValue": "[{\"role\":\"user\",\"content\":\"What's the weather in Seattle?\"}]" } },
    { "key": "gen_ai.output.messages",  "value": { "stringValue": "[{\"role\":\"assistant\",\"content\":\"It's 65F and partly cloudy in Seattle.\"}]" } },
    { "key": "user.email",              "value": { "stringValue": "alice@contoso.com" } }
    /* plus all the run-wide attributes listed above */
  ]
}

Étendue B : chat (appel LLM)

{
  "traceId": "0102030405060708090a0b0c0d0e0f10",
  "spanId": "2222222222222222",
  "parentSpanId": "1111111111111111",
  "name": "chat",
  "kind": 1,
  "startTimeUnixNano": "1736175600200000000",
  "endTimeUnixNano":   "1736175600900000000",
  "status": { "code": 1 },
  "attributes": [
    { "key": "gen_ai.operation.name",      "value": { "stringValue": "chat" } },
    { "key": "gen_ai.request.model",       "value": { "stringValue": "gpt-4o" } },
    { "key": "gen_ai.provider.name",       "value": { "stringValue": "openai" } },
    { "key": "gen_ai.usage.input_tokens",  "value": { "stringValue": "42" } },
    { "key": "gen_ai.usage.output_tokens", "value": { "stringValue": "23" } }
    /* plus all the run-wide attributes */
  ]
}

Étendue C : execute_tool

{
  "traceId": "0102030405060708090a0b0c0d0e0f10",
  "spanId": "3333333333333333",
  "parentSpanId": "1111111111111111",
  "name": "execute_tool",
  "kind": 1,
  "startTimeUnixNano": "1736175600950000000",
  "endTimeUnixNano":   "1736175601200000000",
  "status": { "code": 1 },
  "attributes": [
    { "key": "gen_ai.operation.name",      "value": { "stringValue": "execute_tool" } },
    { "key": "gen_ai.tool.name",           "value": { "stringValue": "GetWeather" } },
    { "key": "gen_ai.tool.type",           "value": { "stringValue": "function" } },
    { "key": "gen_ai.tool.call.id",        "value": { "stringValue": "call-001" } },
    { "key": "gen_ai.tool.call.arguments", "value": { "stringValue": "{\"location\":\"Seattle\"}" } },
    { "key": "gen_ai.tool.call.result",    "value": { "stringValue": "{\"tempF\":65,\"condition\":\"partly cloudy\"}" } }
    /* plus all the run-wide attributes */
  ]
}

Étendue D : output_messages

{
  "traceId": "0102030405060708090a0b0c0d0e0f10",
  "spanId": "4444444444444444",
  "parentSpanId": "1111111111111111",
  "name": "output_messages",
  "kind": 1,
  "startTimeUnixNano": "1736175601400000000",
  "endTimeUnixNano":   "1736175601500000000",
  "status": { "code": 1 },
  "attributes": [
    { "key": "gen_ai.operation.name",  "value": { "stringValue": "output_messages" } },
    { "key": "gen_ai.output.messages", "value": { "stringValue": "[{\"role\":\"assistant\",\"content\":\"It's 65F and partly cloudy in Seattle.\"}]" } }
    /* plus all the run-wide attributes */
  ]
}

Envoi de données de télémétrie

Utilisation d’un Kit de développement logiciel (SDK) OTel

La plupart des partenaires envoient des traces via un SDK OTel plutôt que via une implémentation HTTP codée à la main. Le Kit de développement logiciel (SDK) gère le traitement par lots, les nouvelles tentatives et l’encodage OTLP/HTTP+JSON pour vous. Définissez le point de terminaison de l’exportateur et injectez l’en-tête Authorization .

Le point de terminaison de l’exportateur est l’URL de routage elle-même, y compris la chaîne de requête :

https://agent365.svc.cloud.microsoft/observabilityService/tenants/{tenantId}/otlp/agents/{agentId}/traces?api-version=1

(Utilisez /observability/... plutôt que /observabilityService/... pour l’itinéraire délégué.)

Python

from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter

exporter = OTLPSpanExporter(
    endpoint="https://agent365.svc.cloud.microsoft/observabilityService/tenants/{tenantId}/otlp/agents/{agentId}/traces?api-version=1",
    headers={"Authorization": f"Bearer {token}"},
)

Paquet : opentelemetry-exporter-otlp-proto-http.

Node.js / TypeScript

import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";

const exporter = new OTLPTraceExporter({
  url: "https://agent365.svc.cloud.microsoft/observabilityService/tenants/{tenantId}/otlp/agents/{agentId}/traces?api-version=1",
  headers: { Authorization: `Bearer ${token}` },
});

Paquet : @opentelemetry/exporter-trace-otlp-http.

.NET

using OpenTelemetry.Exporter;

services.AddOpenTelemetry().WithTracing(b => b
    .AddOtlpExporter(o =>
    {
        o.Endpoint = new Uri("https://agent365.svc.cloud.microsoft/observabilityService/tenants/{tenantId}/otlp/agents/{agentId}/traces?api-version=1");
        o.Headers = $"Authorization=Bearer {token}";
        o.Protocol = OtlpExportProtocol.HttpJson;
    }));

Paquet : OpenTelemetry.Exporter.OpenTelemetryProtocol.

HTTP manuel

Si vous ne pouvez pas ou ne souhaitez pas utiliser un SDK OTel, générez la requête OTLP/HTTP+JSON vous-même et postez-la. La structure du corps est définie par la spécification OpenTelemetry OTLP/HTTP+JSON :

{
  "resourceSpans": [{
    "resource":  { "attributes": [ ... ] },          // optional
    "scopeSpans": [{
      "scope":  { "name": "<your-instrumentation>", "version": "1.0.0" },
      "spans":  [ <span>, <span>, ... ]
    }]
  }]
}

Chaque <span> est un objet dont les champs obligatoires sont traceId, spanId, name, kind, startTimeUnixNano, endTimeUnixNano, attributes et, (pour les spans non racine) parentSpanId. Consultez les points de terminaison et l’encodage du corps de la requête pour les règles d’encodage (heures encodées par chaîne, hexadécimaltraceId / spanId, entierkind / status.code, toutes les valeurs d’attribut en tant que ).stringValue

L’ensemble d’attributs à définir sur chaque étendue est défini dans les contrats de message. Consultez la référence d’attribut pour la liste complète des attributs. Consultez l’exemple d’exécution de l’Agent pour voir un exemple fonctionnel de bout en bout avec le jeton Bearer dans l’en-tête et le corps inclus directement.

Vous pouvez envoyer tous les spans d’un run dans un seul corps de requête POST (de préférence : une requête, une trace) ou dans plusieurs requêtes POST. Le serveur reconstruit l’exécution à partir de traceId + parentSpanId + gen_ai.conversation.id, de sorte que chaque span contient suffisamment d’informations pour être corrélé dans les deux sens.

Contrats de messages

Cette section définit quels spans vous pouvez générer et quels attributs sont associés à chacun d’eux. Pour obtenir la spécification d’attribut par attribut complète, consultez la référence d’attribut.

Types d’opération

Chaque span que vous envoyez doit avoir gen_ai.operation.name défini sur l’une de ces quatre valeurs (sans distinction de casse). Toute étendue avec une valeur manquante ou non reconnue est supprimée en mode silencieux et comptée dans partialSuccess.rejectedSpans.

gen_ai.operation.name Sens Le piège le plus recherché sur Google
invoke_agent Appel d’un agent. La « root » d’une exécution d’agent. Obligatoire pour que l’exécution apparaisse dans les vues d’activité de l’agent de Microsoft Defender ou dans le centre d’administration Microsoft 365. Sans cela, la télémétrie n’est transmise qu’à la fonctionnalité de recherche avancée de Microsoft Defender (CloudAppEvents).
execute_tool Un appel d’outil / de fonction effectué par un agent. --
chat Appel d’inférence LLM. Utilisez le littéral chat, NOT inference.
output_messages Un message de sortie final émis. --

Hiérarchie des spans et regroupement des runs

Agent 365 reconstruit une exécution à partir du graphe standard des spans OTLP (traceId, spanId, parentSpanId), ainsi que des attributs globaux de l’exécution issus de la référence des attributs.

Six règles :

  1. Toujours définir parentSpanId sur chaque étendue non racine. Sans cela, il est impossible de reconstituer l’arborescence de l’exécution.
  2. Réutiliser le même traceId pour chaque plage d’un run.
  3. Définissez gen_ai.conversation.id sur chaque étendue avec la même valeur. Il s’agit de la clé de jointure principale pour « tous les segments de cette exécution ». Elle n’est pas propagée automatiquement.
  4. Définissez microsoft.channel.name sur chaque étendue avec la même valeur. Les spans d’outil auxquels il manque le canal / la conversation ne peuvent en hériter de leur parent invoke_agentque si ce parent se trouve dans la même requête OTLP ; définissez-les donc vous-même sur chaque span.
  5. Définissez microsoft.session.id sur chaque span lorsque vous avez une session logique.
  6. Pour les appels d’agent à agent où l’agent enfant est dans une requête distincte, réutilisez le même gen_ai.conversation.id et utilisez les attributs microsoft.a365.caller.agent.* (voir Référence des attributs) pour capturer le contexte de l’agent appelant.

L’arborescence à quatre étendues de l’exemple d’exécution de l’Agent est la forme canonique.

Types d’exécution courants

Forme Étendues à émettre Remarques
Chatbot à agent unique (aucun outil, aucune étendue LLM) Un invoke_agent seul Définir les attributs globaux de l’exécution ainsi que gen_ai.input.messages et gen_ai.output.messages. Identique à la demande la plus petite possible.
Agent avec outils (le plus courant) invoke_agent racine + chat, execute_tool, output_messages enfants Tous les enfants partagent la racine traceId et définissent parentSpanId = root.spanId. Tous portent les mêmes attributs pour l’ensemble de l’exécution. Consultez l’exemple d’exécution de l’agent pour obtenir un exemple complet.
Agent à agent Chaque agent émet son propre invoke_agent Réutilisez le même gen_ai.conversation.id dans les deux agents. Sur le invoke_agent de la cible, définissez gen_ai.execution.type = "Agent2Agent" et les attributs microsoft.a365.caller.agent.* (le appId de l’agent appelant, le nom, le blueprint appId, l’identifiant utilisateur et l’e-mail). Si l’agent appelant n’a pas d’inscription Entra, utilisez microsoft.a365.caller.agent.platform.id et gen_ai.caller.agent.type à la place.

Liste de contrôle d’intégration

Exécutez cette liste de contrôle avant d’aller en production.

Catégorie Vérifier
Authentification Votre application Entra (ou plan) est enregistrée et vous pouvez générer des jetons pour celle-ci.
Authentification Votre application s’est vu accorder Agent365.Observability.OtelWrite (rôle d’application pour S2S, portée pour l’autorisation déléguée).
Authentification Chaque agent possède son propre ID d’application Entra appId, sous la forme de {agentId} dans l’URL. Pour les identités dérivées du blueprint, cet appId est l’appId d’identité de l’agent, et non l’appId de blueprint. Si l’agent n’a pas d’enregistrement Entra, consultez Choix des valeurs.
Authentification Un administrateur du locataire a donné son consentement pour Agent365.Observability.OtelWrite. Sans consentement, les jetons sont émis sans le rôle/étendue et les demandes sont rejetées avec 403.
Gestion des licences Au moins un utilisateur du locataire client s’est vu attribuer une licence Microsoft 365 E7 ou Microsoft Agent 365 (affectation effective, et pas seulement présence de la référence SKU dans le locataire). Sans licence affectée, l’ingestion est supprimée en mode silencieux. Consultez les Conditions préalables.
Travées Chaque span définit les éléments essentiels communs à l’ensemble du run (Hiérarchie des spans et regroupement des runs).
Travées invoke_agent ensembles de plages gen_ai.input.messages et gen_ai.output.messages.
Travées execute_toolétendues définies gen_ai.tool.name, , gen_ai.tool.typegen_ai.tool.call.id, gen_ai.tool.call.arguments, gen_ai.tool.call.result.
Travées chat définit les étendues gen_ai.request.model et gen_ai.provider.name (et idéalement gen_ai.usage.input_tokens / gen_ai.usage.output_tokens - encodés sous forme de chaîne).
Travées Tous les spans non racine définissent parentSpanId ; tous les spans d’une exécution partagent le même traceId.
Charge utile Le corps de la demande est ≤ 1 Mo.
Vérification Vous analysez partialSuccess dans chaque réponse et consignez les rejets.
Vérification Vous avez exécuté le flux de vérification dans Vérification de l’ingestion sur vos premières exécutions.

Étapes suivantes