Configurazione dell'autenticazione dell'osservabilità

L'utilità di esportazione di Agent 365 richiede un resolver di token per l'autenticazione durante l'esportazione dei dati di telemetria. Questa guida illustra la configurazione degli agenti compilati con Managed Disks, che copre sia gli agenti abilitati per Agent 365 che gli agenti del motore personalizzati in .NET, Python e Node.js.

Per l'installazione della distro, la configurazione generale e gli scenari che non prevedono l'Agent SDK, vedere Microsoft OpenTelemetry Distro.

Overview

Esistono quattro scenari di autenticazione, a seconda del tipo di agente e del modo in cui acquisisce i token. L'acquisizione di token può avvenire tramite il flusso On-Behalf-Of (OBO) o Service-to-Service (S2S). Scegliere lo scenario corrispondente alla configurazione:

Scenario Description
Agente 365 abilitato con OBO La distribuzione integrata AgenticTokenCache gestisce automaticamente l'acquisizione dei token nella rotta delegata. Non è necessario alcun sistema di risoluzione personalizzato. Questa route richiede il permesso delegato Agent365.Observability.OtelWrite e il consenso dell'amministratore, che a365 setup all non configura per gli agenti blueprint.
Agente 365 abilitato con S2S L'agente acquisisce un token solo per l'app utilizzando la catena di identità agentica (getAgenticApplicationToken + Microsoft Authentication Libraries (MSAL)). Richiede un custom TokenResolver e l'opzione endpoint S2S. Un'istanza di registered agent non ha bisogno di un permesso di osservabilità o del consenso amministratore, quindi questo è l'approccio consigliato per gli agenti blueprint configurati con a365 setup all.
Motore personalizzato con OBO L'agente ottiene un token utente tramite Azure Bot OAuth, con ambito limitato all'API di osservabilità. Richiede un elemento personalizzato TokenResolver, una connessione Azure Bot OAuth e il permesso delegato Agent365.Observability.OtelWrite con il consenso dell'amministratore.
Motore personalizzato con S2S L'agente acquisisce un token di sola applicazione usando le credenziali del client. Richiede un elemento personalizzato TokenResolver. La registrazione dell'app deve essere un'app standard (non agentica). Le registrazioni standard delle app non sono istanze di agente registrato, quindi richiedono il permesso dell'applicazione Agent365.Observability.OtelWrite con il consenso dell'amministratore.

Agent 365 abilitato con OBO

Gli agenti abilitati per Agent 365 ricevono richieste con identità agente (agenticAppId, agenticUserId) dalla piattaforma Agent 365. Utilizzando OBO, la AgenticTokenCache integrata della distribuzione gestisce automaticamente l'acquisizione dei token sul flusso delegato, quindi non serve uno strumento di risoluzione personalizzato per i token.

Prerequisiti

  • Registrazione app Microsoft Entra: un'entità servizio (registrazione app) con ID client, Client Secret e ID tenant.

  • Permessi API delegati: Aggiungi Agent365.Observability.OtelWrite (Delegato) e concedi il consenso dell'amministratore. Il a365 setup all comando non aggiunge questo permesso per gli agenti blueprint. Un Amministratore Globale può aggiungerlo e concedere il consenso dell'amministratore eseguendo il seguente comando:

    a365 setup permissions custom --resource-app-id 9b975845-388f-4429-889e-eab1ef63949c --scopes Agent365.Observability.OtelWrite
    

    Per altre opzioni, incluso il Interfaccia di amministrazione di Microsoft Entra, vedi Concedere l'autorizzazione.

Tip

Per evitare l'autorizzazione Osservabilità delegata e il consenso dell'amministratore, usa Agent 365 abilitato tramite S2S per gli agenti del progetto configurati con a365 setup all.

Setup

Ad ogni turno, l'agente chiama la funzione RegisterObservability passandole il contesto del turno. La cache integrata usa il token delegato dell'utente del gestore AgenticUserAuthorization per eseguire uno scambio OBO, acquisendo un token con ambito limitato a Agent365.Observability.OtelWrite.

Per istruzioni complete sull'installazione, inclusi pacchetti, configurazione ed esempi di codice, vedere Cache dei token agentic con le app di Agent Framework.

Agent 365 abilitato tramite S2S

Gli agenti con Agent 365 abilitato possono anche usare l'autenticazione S2S (servizio-servizio) invece di OBO. Questo percorso è consigliato per gli agenti blueprint configurati con a365 setup all. L'agente acquisisce un token utilizzando la propria identità dell'entità servizio tramite una catena di identità di agenti in due passaggi:

  1. Chiama getAgenticApplicationToken(tenantId, agentId) per ottenere un token credenziali client con il percorso Federated Managed Identity (FMI).
  2. Chiama MSAL acquireTokenForClient con il token app come clientAssertion e l'ambito api://9b975845-388f-4429-889e-eab1ef63949c/.default.

Note

L'identità gestita federata (FMI) è un'architettura in cui un'identità gestita partecipa alla federazione dell'identità del carico di lavoro tramite credenziali di identità federate, abilitando lo scambio di token e l'autenticazione senza segreto in base alle relazioni di trust tra le identità.

La distribuzione Microsoft OpenTelemetry utilizza di default la route delegata, quindi devi fornire un elemento personalizzato TokenResolver e impostare l'opzione endpoint S2S per la tua lingua, come UseS2SEndpoint = true, useS2SEndpoint: true, o a365_use_s2s_endpoint=True.

Prerequisiti

  • Registrazione Agent 365: L'istanza dell'agente deve essere registrata presso l'Agent 365. Il comando a365 setup all registra le istanze dell'agente blueprint. La sola creazione di un'identità Microsoft Entra non registra un'istanza dell'agente.
  • Credenziale Blueprint per lo scambio FMI: Usa un client secret Blueprint o un'identità gestita per ottenere il token Federated Managed Identity (FMI) per api://AzureADTokenExchange/.default, poi scambialo con il token Observability dell'istanza agente.
  • Corrispondenza dei valori di identità: L'ID dell'agente e l'ID tenant nel baggage e l'URL di esportazione devono corrispondere all'ID client dell'istanza dell'agente e al tenant nel token solo app.

Note

Un'istanza di agente registrata con Agent 365 può esportare sulla rotta S2S con un token solo app che non Agent365.Observability.OtelWrite ha alcun ruolo. Non ha bisogno di un permesso di Observability o del consenso dell'amministratore. Le identità non registrate, comprese le registrazioni standard delle app, richiedono comunque il Agent365.Observability.OtelWrite ruolo app e il consenso dell'amministratore.

Passaggio 1: Configurazione dell'ambiente

Gli esempi di codice seguenti illustrano come impostare le impostazioni di ambiente di esportazione di connessioni, tenant, credenziali client e osservabilità necessarie prima di abilitare il flusso del token S2S personalizzato.

Non è necessario alcun gestore AgenticUserAuthorization. S2S usa la catena di identità agentica manuale (get_agentic_application_token + MSAL acquire_token_for_client) per ottenere un token con ambito limitato alla risorsa di osservabilità.

CONNECTIONSMAP__0__SERVICEURL=*
CONNECTIONSMAP__0__CONNECTION=SERVICE_CONNECTION

CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=<your-client-id>
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=<your-client-secret>
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=<your-tenant-id>

ENABLE_A365_OBSERVABILITY=true
ENABLE_A365_OBSERVABILITY_EXPORTER=true

Passaggio 2: Configurare la distribuzione con il resolver di token personalizzato

Gli esempi seguenti mostrano come abilitare l'esportazione di Agent 365 e registrare un TokenResolver personalizzato affinché l'esportatore possa recuperare i token S2S per ogni agente e tenant.

from microsoft.opentelemetry import use_microsoft_opentelemetry

_token_cache: dict[str, str] = {}

def token_resolver(agent_id: str, tenant_id: str) -> str | None:
    return _token_cache.get(f"{agent_id}:{tenant_id}")

use_microsoft_opentelemetry(
    enable_a365=True,
    a365_token_resolver=token_resolver,
    a365_use_s2s_endpoint=True,
    a365_enable_observability_exporter=True,
)

Passaggio 3: Acquisire e memorizzare nella cache il token S2S

In ogni messaggio in arrivo acquisire il token S2S tramite la catena di identità agentic e memorizzarlo nella cache per il sistema di risoluzione.

import asyncio
from msal import ConfidentialClientApplication
from microsoft.opentelemetry.a365.core import BaggageBuilder, InvokeAgentScope, InvokeAgentScopeDetails, Request

OBSERVABILITY_S2S_SCOPE = "api://9b975845-388f-4429-889e-eab1ef63949c/.default"

async def get_agentic_s2s_token(connection, tenant_id: str, agent_id: str) -> str:
    # Step 1: Get agentic application token (client_credentials + fmi_path)
    app_token = await connection.get_agentic_application_token(tenant_id, agent_id)
    if not app_token:
        raise ValueError(f"Failed to get agentic app token for agent {agent_id}")

    # Step 2: Exchange for observability-scoped token
    cca = ConfidentialClientApplication(
        client_id=agent_id,
        authority=f"https://login.microsoftonline.com/{tenant_id}",
        client_credential={"client_assertion": app_token},
    )
    result = await asyncio.to_thread(
        lambda: cca.acquire_token_for_client(scopes=[OBSERVABILITY_S2S_SCOPE])
    )
    if not result or "access_token" not in result:
        raise ValueError(f"Token acquisition failed: {result}")
    return result["access_token"]

# In your message handler : use SDK helpers to get agent/tenant from the activity:
@AGENT_APP.activity("message")
async def on_message(context: TurnContext, _state: TurnState):
    # get_agentic_instance_id reads from recipient (SDK convention)
    agent_id = context.activity.get_agentic_instance_id()
    tenant_id = context.activity.get_agentic_tenant_id()

    # Acquire S2S token and cache BEFORE creating spans
    connection = CONNECTION_MANAGER.get_connection("SERVICE_CONNECTION")
    token = await get_agentic_s2s_token(connection, tenant_id, agent_id)
    _token_cache[f"{agent_id}:{tenant_id}"] = token

    # Wrap spans in BaggageBuilder so the exporter can resolve the token
    request = Request(content=user_message, session_id=None)
    with BaggageBuilder().tenant_id(tenant_id).agent_id(agent_id).build():
        invoke_scope = InvokeAgentScope.start(request, InvokeAgentScopeDetails(), agent_details)
        with invoke_scope:
            invoke_scope.record_input_messages([user_message])
            invoke_scope.record_output_messages([response])

Important

Il flusso manuale in due passaggi (get_agentic_application_token + MSAL acquire_token_for_client) è necessario per S2S. AgenticUserAuthorization.get_token() restituisce un token con ambito 5a807f24-.../.default (Bot Framework), non relativo alla risorsa di osservabilità api://9b975845-.../.default: l'endpoint S2S lo rifiuta con 401 InvalidAudience.

  • Usa context.activity.get_agentic_instance_id() e get_agentic_tenant_id() per recuperare l'agente e il tenant dall'attività (recuperandoli da recipient secondo la convenzione dell'SDK).
  • Acquisire e memorizzare nella cache il token S2S prima di creare intervalli. Il BatchSpanProcessor dell'esportatore potrebbe svuotarsi prima che il gestore termini : se il token non è ancora stato memorizzato nella cache, l'esportazione ha esito negativo.
  • Racchiudi tutti gli scope A365 tra i tag BaggageBuilder in modo che l'utilità di esportazione sappia per quali agente e tenant risolvere i token. Senza baggage, gli intervalli vengono rilasciati automaticamente con "Nessun intervallo con identità tenant/agente trovato".

Important

Prima che il tuo resolver restituisca e memorizzi nella cache il token finale di Osservabilità:

  • Verifica che il token sia solo per l'applicazione. Accetta idtyp=app. Se idtyp è assente, accettiamo solo un token che abbia un claim roles non vuoto o un claim oid non vuoto uguale a sub. Rifiuta i token che hanno un'attestazione scp o un altro valore idtyp.
  • Controlla che aud sia 9b975845-388f-4429-889e-eab1ef63949c o api://9b975845-388f-4429-889e-eab1ef63949c, e che il token non sia scaduto.
  • Non inviare mai l'asserzione intermedia del blueprint, un token blueprint o un token utente o OBO a Observability.
  • Memorizza il token finale e aggiornalo prima che scada. L'esportatore chiama il resolver una volta per ogni identità tenant e agent in ogni lotto di esportazione.

Per implementazioni complete che utilizzano la distro, consulta i campioni di Agent 365 per Node.js, Python e .NET.

Motore personalizzato con OBO

Gli agenti del motore personalizzato usano registrazioni delle app standard con connessioni OAuth di Azure Bot, non la catena di identità di agenti. Usando OBO, l'agente ottiene un token utente tramite Azure Bot OAuth con ambito già limitato all'API di osservabilità A365 dal servizio token di Bot Framework. Una singola getToken chiamata o GetTurnTokenAsync restituisce il token con ambito corretto, quindi non è necessario exchangeToken.

Prerequisiti

Registrazione dell'app Microsoft Entra con permessi API delegati. Aggiungi Agent365.Observability.OtelWrite (delegato) e concedi il consenso dell'amministratore.

Important

L'agentId nella cache dei token deve corrispondere all'ID client della registrazione dell'app, non a agenticAppId dell'attività, che non esiste per gli agenti del motore personalizzato. L'URL di esportazione include agentId e una mancata corrispondenza provoca un errore HTTP 403.

Passaggio 1: Configurazione dell'ambiente e dell'app

Gli esempi seguenti illustrano come configurare l'app e l'ambiente di runtime, inclusi i valori di connessione del servizio, le impostazioni del tenant e del client e i mapping di autorizzazione necessari.

# .env
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=<your-client-id>
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=<your-client-secret>
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=<your-tenant-id>

CONNECTIONSMAP__0__CONNECTION=SERVICE_CONNECTION
CONNECTIONSMAP__0__SERVICEURL=*

# Auth handler config : TYPE is required, name is uppercased by load_configuration_from_env
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__OBOCONNECTIONPROFILE__TYPE=UserAuthorization
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__OBOCONNECTIONPROFILE__SETTINGS__AZUREBOTOAUTHCONNECTIONNAME=oboConnectionProfile
AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__OBOCONNECTIONPROFILE__SETTINGS__SCOPES=api://9b975845-388f-4429-889e-eab1ef63949c/Agent365.Observability.OtelWrite

ENABLE_A365_OBSERVABILITY=true
ENABLE_A365_OBSERVABILITY_EXPORTER=true

Important

load_configuration_from_env converte in maiuscolo tutte le chiavi delle variabili di ambiente. Il nome del gestore diventa OBOCONNECTIONPROFILE e devi farvi riferimento rispettando esattamente la distinzione tra maiuscole e minuscole nelle chiamate a auth_handlers e get_token(). L'assenza di TYPE causa Auth handler ... not recognized or not configured in fase di esecuzione.

Passaggio 2: Configurare la distro per OBO

Gli esempi seguenti mostrano come abilitare l'esportazione in Agent 365, mantenere l'esportatore sull'endpoint OBO e registrare un elemento personalizzato TokenResolver che restituisce token delegati durante l'esportazione.

from microsoft.opentelemetry import use_microsoft_opentelemetry

_token_cache: dict[str, str] = {}

def token_resolver(agent_id: str, tenant_id: str) -> str | None:
    return _token_cache.get(f"{agent_id}:{tenant_id}")

environ["ENABLE_A365_OBSERVABILITY_EXPORTER"] = "true"

use_microsoft_opentelemetry(
    enable_a365=True,
    a365_token_resolver=token_resolver,
    a365_use_s2s_endpoint=False,  # OBO uses /observability endpoint
    a365_enable_observability_exporter=True,
)

Note

La modalità OBO richiede jwt_authorization_middleware in aiohttpApplication (convalida il token JWT in ingresso (token Web JSON) da Bot Framework. Il percorso S2S/emulatore non deve includere questo middleware.

from microsoft_agents.hosting.aiohttp import jwt_authorization_middleware
app = Application(middlewares=[jwt_authorization_middleware])

Passaggio 3: Acquisire il token OBO

Gli esempi seguenti mostrano come richiedere un token OBO delegato dalla connessione OAuth di Azure Bot configurata, quindi memorizzarlo nella cache per client app e tenant per l'esportatore.

from microsoft_agents.hosting.core import (
    AgentApplication, Authorization, MemoryStorage, TurnContext, TurnState,
)
from microsoft_agents.activity import load_configuration_from_env
from microsoft_agents.authentication.msal import MsalConnectionManager
from microsoft_agents.hosting.aiohttp import CloudAdapter

# Auth handlers are loaded from .env via load_configuration_from_env (see Environment config above)
agents_sdk_config = load_configuration_from_env(environ)

STORAGE = MemoryStorage()
CONNECTION_MANAGER = MsalConnectionManager(**agents_sdk_config)
ADAPTER = CloudAdapter(connection_manager=CONNECTION_MANAGER)
AUTHORIZATION = Authorization(STORAGE, CONNECTION_MANAGER, **agents_sdk_config)

AGENT_APP = AgentApplication[TurnState](
    storage=STORAGE, adapter=ADAPTER, authorization=AUTHORIZATION, **agents_sdk_config,
)

CLIENT_ID = environ.get("CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID", "")
TENANT_ID = environ.get("CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID", "")

# Message handler : get_token returns a token already scoped to the observability API.
# The Azure Bot Token Service performs the OBO exchange internally based on the
# OAuth connection's configured scope. No manual MSAL exchange_token call is needed.
@AGENT_APP.activity("message", auth_handlers=["OBOCONNECTIONPROFILE"])
async def on_message(context: TurnContext, _state: TurnState):
    token_response = await AGENT_APP.auth.get_token(context, "OBOCONNECTIONPROFILE")
    # token_response.token has aud=<a365-observability-app-id>,
    # scp=Agent365.Observability.OtelWrite
    _token_cache[f"{CLIENT_ID}:{TENANT_ID}"] = token_response.token

Important

Prerequisito del portale Azure: Imposta gli Scopes per la connessione Azure Bot OAuth nominata oboConnectionProfile a api://9b975845-388f-4429-889e-eab1ef63949c/Agent365.Observability.OtelWrite. Senza questa impostazione, il token ha come ambito il gruppo di destinatariapi://botid-... () del bot e l'esportazione ha esito negativo con HTTP 401 InvalidAudience.

Note

AGENT_APP.auth.get_token() restituisce direttamente il token con ambito corretto: non è necessaria alcuna chiamata a exchange_token(). Il servizio token di Bot Framework gestisce lo scambio OBO quando l'ambito della connessione OAuth ha come destinazione la risorsa di osservabilità A365.

Motore personalizzato con S2S

Gli agenti custom engine possono usare S2S (credenziali client) per ottenere un token solo per app utilizzando le credenziali di connessione del servizio. Questo metodo utilizza le credenziali client standard di MSAL: non necessita di una agentic identity chain.

Prerequisiti

  • Registrazione dell'app Microsoft Entra: La registrazione dell'app deve essere un'app motore personalizzato (standard). Le registrazioni dell'app abilitate per Agent 365 non possono usare semplicemente client_credentials per la risorsa di osservabilità (AADSTS82001).
  • Permessi di applicazione: Aggiungi Agent365.Observability.OtelWrite (Applicazione, non Delegata) e concedi il consenso dell'amministratore. Le registrazioni standard delle app non sono istanze di agente registrate con Agent 365, quindi hanno bisogno di questo ruolo nell'app nel percorso S2S.

Important

L'agentId che usi per la cache deve essere quello del ClientId di ServiceConnection. L'URL di esportazione è /observabilityService/tenants/{tenantId}/otlp/agents/{agentId}/traces : una mancata corrispondenza causa HTTP 403.

Passaggio 1: Configurazione dell'ambiente e dell'app

Gli esempi seguenti illustrano come configurare l'app e l'ambiente di runtime, inclusi i valori di connessione del servizio, le impostazioni del tenant e del client e i mapping di autorizzazione necessari.

# .env
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=<your-client-id>
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=<your-client-secret>
CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=<your-tenant-id>

CONNECTIONSMAP__0__CONNECTION=SERVICE_CONNECTION
CONNECTIONSMAP__0__SERVICEURL=*

ENABLE_A365_OBSERVABILITY=true
ENABLE_A365_OBSERVABILITY_EXPORTER=true

Passaggio 2: Configurare la distribuzione per S2S

Gli esempi seguenti illustrano come abilitare l'esportazione di Agent 365, impostare l'utilità di esportazione sull'endpoint S2S e registrare un personalizzato TokenResolver per la ricerca di token durante l'esportazione.

from microsoft.opentelemetry import use_microsoft_opentelemetry

_token_cache: dict[str, str] = {}

def token_resolver(agent_id: str, tenant_id: str) -> str | None:
    return _token_cache.get(f"{agent_id}:{tenant_id}")

use_microsoft_opentelemetry(
    enable_a365=True,
    a365_token_resolver=token_resolver,
    a365_use_s2s_endpoint=True,  # S2S uses /observabilityService endpoint
    a365_enable_observability_exporter=True,
)

Passaggio 3: Acquisire il token S2S

Gli esempi seguenti illustrano come richiedere un token di accesso per sola app per la risorsa di osservabilità usando le credenziali della connessione al servizio, quindi memorizzarlo nella cache in base all'agente e al tenant per l'esportatore.

# Force agentId to ServiceConnection ClientId (custom engine agents have no agenticAppId)
agent_id = os.environ.get("CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID")
tenant_id = os.environ.get("CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID")

connection = CONNECTION_MANAGER.get_connection("SERVICE_CONNECTION")
token = await connection.get_access_token(
    resource_url="https://login.microsoftonline.com",
    scopes=["api://9b975845-388f-4429-889e-eab1ef63949c/.default"],
)
_token_cache[f"{agent_id}:{tenant_id}"] = token

Passaggio 4: Impostare il baggage per l'esportazione degli span

L'esportatore Agent365 richiede baggage (tenant ID e agent ID) nel contesto span. Senza questo baggage, l'esportatore scarta gli span e registra il messaggio No spans with tenant/agent identity found..

from microsoft.opentelemetry.a365.core import BaggageBuilder, InvokeAgentScope

# Baggage must wrap the span as a context manager
with BaggageBuilder().tenant_id(tenant_id).agent_id(agent_id).build():
    invoke_scope = InvokeAgentScope.start(request, InvokeAgentScopeDetails(), agent_details)
    with invoke_scope:
        invoke_scope.record_input_messages([user_message])
        invoke_scope.record_output_messages([response])