Configuración de autenticación de observabilidad

El exportador Agent 365 requiere un resolutor de tokens para autenticarse al exportar telemetría. Esta guía abarca la configuración para agentes creados con el SDK de agentes de Microsoft 365, incluyendo tanto agentes con Agent 365 habilitado como agentes con motor personalizado en .NET, Python y Node.js.

Para la instalación del distro, la configuración general y los escenarios fuera del SDK de Agentes, consulte Microsoft OpenTelemetry Distro.

Información general

Existen cuatro escenarios de autenticación, dependiendo del tipo de agente y la forma en que obtiene los tokens. La obtención de tokens puede usar flujo On-Behalf-Of (OBO) o el flujo de servicio a servicio (S2S). Elija el escenario que se adapte a su configuración:

Escenario Description
Habilitado para Agent 365 mediante OBO La distribución integrada AgenticTokenCache gestiona automáticamente la obtención de tokens. No se necesita un resolutor personalizado. Este es el enfoque recomendado para los agentes habilitados con Agent 365.
Habilitado para Agent 365 mediante S2S El agente adquiere un token utilizando la cadena de identidad agentica (getAgenticApplicationToken + Microsoft Authentication Libraries (MSAL)). Requiere un TokenResolver personalizado. Utilice este enfoque cuando OBO no esté disponible o necesite tokens exclusivos para la aplicación.
Motor personalizado usando OBO El agente obtiene un token de usuario a través de Azure Bot OAuth, con alcance en la API de observabilidad. Se necesita un componente personalizado TokenResolver y una conexión Azure Bot OAuth.
Motor personalizado usando S2S El agente adquiere un token de aplicación usando las credenciales del cliente. Requiere un TokenResolver personalizado. El registro de aplicaciones debe ser una aplicación estándar (no agentica).

Habilitado para Agent 365 mediante OBO

Los agentes habilitados para Agent 365 reciben solicitudes con identidad agéntica (agenticAppId, agenticUserId) desde la plataforma de Agent 365. Con OBO, el AgenticTokenCache integrado de la distribución se encarga automáticamente de obtener los tokens: no se necesita ningún resolutor de tokens personalizado.

Requisitos previos

  • Registro de aplicación de Entra: una entidad de servicio (registro de aplicación) con id. de cliente, secreto de cliente e id. de inquilino
  • Permisos delegados de API: Agregar Agent365.Observability.OtelWrite (Delegated), conceder consentimiento de administrador. Para los pasos detallados, consulte Conceder el permiso.

Instalación

En cada turno, el agente llama a la función RegisterObservability con el contexto del turno. La memoria caché integrada usa el token delegado del usuario del controlador AgenticUserAuthorization para realizar un intercambio OBO y adquirir un token con el ámbito de Agent365.Observability.OtelWrite.

Para obtener instrucciones de configuración completas, como paquetes, configuración y ejemplos de código, consulte Caché de tokens agente con aplicaciones de Agent Framework.

Habilitado para Agent 365 mediante S2S

Los agentes habilitados para Agent 365 también pueden usar autenticación S2S (servicio a servicio) en lugar de OBO. El agente obtiene un token mediante su propia identidad del principal de servicio a través de una cadena de identidad agéntica de dos pasos:

  1. getAgenticApplicationToken(tenantId, agentId): credenciales de cliente + ruta de acceso de identidad administrada federada (FMI)
  2. MSAL acquireTokenForClient con el token de la aplicación como clientAssertion y el ámbito api://9b975845-388f-4429-889e-eab1ef63949c/.default

Nota

La Identidad Gestionada Federada (FMI) es una arquitectura en la que una identidad gestionada participa en la federación de identidades de carga de trabajo mediante credenciales de identidad federada, habilitando el intercambio de tokens y la autenticación sin secretos basada en relaciones de confianza entre identidades.

Debe proporcionar TokenResolver personalizado y establecer UseS2SEndpoint = true.

Requisitos previos

  • Registro de aplicación de Entra: una entidad de servicio (registro de aplicación) con id. de cliente, secreto de cliente e id. de inquilino

  • Permisos de la API de la aplicación: Agregar Agent365.Observability.OtelWrite (Aplicación), conceder consentimiento del administrador

  • Rol de la aplicación Agent365.Observability.OtelWrite: la entidad de servicio del agente debe tener asignado el rol OtelWrite en el recurso Agent365 observabilidad. Utilice la CLI del Agent 365:

    a365 setup permissions bot --config-dir "<path-to-config-dir>"
    

    Nota

    La propagación de roles puede tardar unos minutos. Se esperan errores iniciales 401 o 403 desde el punto de conexión de exportación durante este período.

Paso 1: Configuración de entorno

Los siguientes ejemplos de código muestran cómo configurar las conexiones, el inquilino, las credenciales del cliente y los ajustes de entorno requeridos para el exportador de observabilidad antes de habilitar el flujo personalizado de tokens S2S.

No se necesita controlador AgenticUserAuthorization. S2S usa la cadena manual de identidad agéntica (get_agentic_application_token + MSAL acquire_token_for_client) para obtener un token con alcance para el recurso de observabilidad.

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

Paso 2: Configure la distro con un resolutor personalizado de tokens

Los siguientes ejemplos muestran cómo habilitar la exportación de Agent 365 y registrar un TokenResolver personalizado para que el exportador pueda recuperar tokens S2S para cada agente e inquilino.

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,
)

Paso 3: Obtenga y guarde en caché el token S2S

En cada mensaje entrante, adquiera el token S2S a través de la cadena de identidad de agente y guárdelo en caché para el resolutor.

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])

Importante

El flujo manual de dos pasos (get_agentic_application_token + MSAL acquire_token_for_client) se requiere para S2S. AgenticUserAuthorization.get_token() devuelve un token con alcance a 5a807f24-.../.default (Bot Framework), no al recurso de observabilidad api://9b975845-.../.default: el punto de conexión S2S lo rechaza con 401 InvalidAudience.

  • Usa context.activity.get_agentic_instance_id() y get_agentic_tenant_id() para leer el agente y el inquilino a partir de la actividad (lee desde recipient según la convención del SDK).
  • Adquiera y almacene en caché el token S2S antes de crear intervalos. El exportador BatchSpanProcessor puede realizar un vaciado antes de que termine el controlador: si el token aún no está en caché, la exportación falla.
  • Envuelve todos los ámbitos de permisos de A365 en BaggageBuilder para que el exportador sepa para qué agente e inquilino debe resolver los tokens. Sin equipaje, los intervalos se quitan silenciosamente con "No se encuentran intervalos con la identidad del inquilino o agente encontrado".

Motor personalizado usando OBO

Los agentes de motor personalizado usan registros de aplicaciones estándar con conexiones OAuth de Azure Bot, no la cadena de identidad agéntica. Mediante el uso de OBO, el agente obtiene un token de usuario a través de Azure Bot OAuth que ya está en el ámbito de la API de observabilidad de A365 mediante el servicio de token de Bot Framework. Una sola llamada a getToken o GetTurnTokenAsync devuelve el token con el alcance correcto, por lo que no necesita exchangeToken.

Requisitos previos

Registro de la aplicación en Entra con permisos de API delegados. Agregar Agent365.Observability.OtelWrite (Delegado) y otorgar el consentimiento de administrador

Importante

El agentId de la caché de tokens debe coincidir con el identificador de cliente del registro de la aplicación, no con el agenticAppId de la actividad, que no existe para los agentes de motor personalizados. La URL de exportación incluye el agentId, y una incongruencia provoca un error HTTP 403.

Paso 1: Entorno y configuración de la aplicación

Los siguientes ejemplos muestran cómo configurar su aplicación y el entorno de ejecución, incluyendo los valores de conexión de servicio, configuraciones de inquilino y cliente, y las asignaciones de autorización requeridas.

# .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

Importante

load_configuration_from_env convierte en mayúsculas todas las claves de variables de entorno. El nombre del gestor pasa a ser OBOCONNECTIONPROFILE y debe referirse a él respetando exactamente las mayúsculas y minúsculas en las llamadas a auth_handlers y get_token(). Si falta TYPE se produce Auth handler ... not recognized or not configured en tiempo de ejecución.

Paso 2: Configure la distro para OBO

Los siguientes ejemplos muestran cómo habilitar la exportación de Agent 365, mantener el exportador en el punto de conexión OBO y registrar un TokenResolver personalizado que devuelve tokens delegados durante la exportación.

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,
)

Nota

El modo OBO requiere jwt_authorization_middleware en aiohttpApplication (valida el JWT entrante (JSON Web Token) proveniente de Bot Framework). La ruta S2S/emulador no debería incluir este middleware.

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

Paso 3: Adquiera el token OBO

En los ejemplos siguientes se muestra cómo solicitar un token de OBO delegado desde la conexión OAuth de bot configurada Azure y, a continuación, almacenarla en caché por cliente de aplicación e inquilino para el exportador.

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

Importante

Requisito previo del portal de Azure: La conexión de OAuth de Azure Bot llamada oboConnectionProfile debe tener los Ámbitos configurados como api://9b975845-388f-4429-889e-eab1ef63949c/Agent365.Observability.OtelWrite. Sin esta configuración, el token se asigna a la audiencia del bot (api://botid-...) y la exportación falla con HTTP 401 InvalidAudience.

Nota

AGENT_APP.auth.get_token() devuelve el token con el ámbito correcto directamente; no se necesita una llamada a exchange_token(). El servicio de token de Bot Framework gestiona el intercambio de OBO cuando el ámbito de la conexión de OAuth tiene como destino el recurso de observabilidad A365.

Motor personalizado usando S2S

Los agentes del motor personalizado pueden usar S2S (credenciales de cliente) para adquirir un token de solo aplicación mediante las credenciales de conexión de servicio. Este método utiliza credenciales de cliente estándar de MSAL; no se requiere cadena de identidad de agente.

Requisitos previos

  • Registro de aplicaciones de Azure AD: Debe ser una aplicación de motor personalizado (estándar). Los registros de aplicaciones habilitadas para Agent 365 no pueden usar client_credentials simples para el recurso de observabilidad (AADSTS82001).
  • Permisos de aplicación: Agregar Agent365.Observability.OtelWrite (Aplicación, no Delegado) y conceder consentimiento del administrador.

Importante

El agentId utilizado para almacenar en caché debe ser el ClientId de ServiceConnection. La URL de exportación es /observabilityService/tenants/{tenantId}/otlp/agents/{agentId}/traces: una incompatibilidad causa HTTP 403.

Paso 1: Entorno y configuración de la aplicación

Los siguientes ejemplos muestran cómo configurar su aplicación y el entorno de ejecución, incluyendo los valores de conexión de servicio, configuraciones de inquilino y cliente, y las asignaciones de autorización requeridas.

# .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

Paso 2: Configure la distro para S2S

Los siguientes ejemplos muestran cómo habilitar la exportación de Agent 365, configurar el exportador al punto de conexión S2S y registrar un TokenResolver personalizado para la obtención de tokens durante la exportación.

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,
)

Paso 3: Adquiera el token S2S

Los siguientes ejemplos muestran cómo solicitar un token de acceso de solo aplicación para el recurso de observabilidad utilizando las credenciales de conexión de servicio, y luego almacenarlo en caché por agente e inquilino para el exportador.

# 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

Paso 4: Configurar equipaje para la exportación de intervalos

El exportador de Agent365 requiere que el equipaje (ID del inquilino e ID del agente) esté configurado en el contexto del intervalo. Sin él, el exportador quita silenciosamente intervalos con el mensaje 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])