Einrichtung der Observability-Authentifizierung

Der Agent 365-Exporter benötigt einen Token-Resolver, um sich beim Export von Telemetriedaten zu authentifizieren. In diesem Leitfaden werden die Einrichtung für Agents behandelt, die mit dem Microsoft 365 Agents SDK erstellt wurden und sowohl Agent 365-fähige Agents als auch benutzerdefinierte Modul-Agents in .NET, Python und Node.jsabdecken.

Informationen zur Einführungsinstallation, allgemeinen Konfiguration und Nicht-Agent SDK-Szenarien finden Sie unter Microsoft OpenTelemetry Distro.

Übersicht

Es gibt vier Authentifizierungsszenarien, abhängig von Ihrem Agenttyp und davon, wie Ihr Agent Token bezieht. Für den Tokenabruf kann On-Behalf-Of flow (OBO) oder Service-to-Service (S2S) verwendet werden. Wählen Sie das Szenario aus, das Ihrem Setup entspricht:

Szenario Description
Agent 365-fähig mithilfe von OBO Das in die Distro integrierte AgenticTokenCache übernimmt die Tokenbeschaffung automatisch. Kein benutzerdefinierter Resolver erforderlich. Dies ist der empfohlene Ansatz für Agent 365-fähige Agents.
Agent 365-aktiviert mit S2S Der Agent erwirbt ein Token mithilfe der agentischen Identitätskette (getAgenticApplicationToken + Microsoft Authentication Libraries (MSAL)). Erfordert eine benutzerdefinierte TokenResolver. Verwenden Sie diesen Ansatz, wenn OBO nicht verfügbar ist oder Sie nur App-Token benötigen.
Benutzerdefinierte Engine mit OBO Der Agent erhält über Azure Bot OAuth ein Benutzertoken mit Geltungsbereich für die Observability-API. Erfordert eine benutzerdefinierte TokenResolver und eine Azure Bot OAuth-Verbindung.
Benutzerdefinierte Engine mit S2S Der Agent erwirbt ein Nur-App-Token mit Clientanmeldeinformationen. Erfordert eine benutzerdefinierte TokenResolver. Die App-Registrierung muss eine standardmäßige (nicht agentische) App sein.

Agent 365-aktiviert mit OBO

Agent 365-fähige Agents empfangen Anforderungen mit agentischer Identität (agenticAppId, agenticUserId) von der Agent 365-Plattform. Mit OBO übernimmt die in der Distribution integrierte AgenticTokenCache den Tokenabruf automatisch: Es wird kein benutzerdefinierter Token-Resolver benötigt.

Voraussetzungen

  • Entra-App-Registrierung : Ein Dienstprinzipal (App-Registrierung) mit Client-ID, geheimem Clientschlüssel und Mandanten-ID
  • Delegierte API-Berechtigungen: Agent365.Observability.OtelWrite hinzufügen (Delegiert), Administratorzustimmung erteilen. Ausführliche Schritte finden Sie unter Erteilen der Berechtigung.

Einrichten

Bei jeder Interaktion ruft Ihr Agent die RegisterObservability-Funktion mit dem Kontext der Interaktion auf. Der integrierte Cache verwendet das delegierte Token des Benutzers aus dem AgenticUserAuthorization-Handler, um einen OBO-Austausch auszuführen und ein Token abzurufen, das für Agent365.Observability.OtelWrite gültig ist.

Vollständige Einrichtungsanweisungen, einschließlich Paketen, Konfigurationen und Codebeispielen, finden Sie unter Agentic Token Cache mit Agent Framework-Apps.

Agent 365-aktiviert mit S2S

Agent 365-fähige Agents können auch die S2S-Authentifizierung (Service-to-Service) anstelle von OBO verwenden. Der Agent erwirbt ein Token mithilfe einer eigenen Dienstprinzipalidentität über eine zweistufige agentische Identitätskette:

  1. getAgenticApplicationToken(tenantId, agentId) : Clientanmeldeinformationen + FMI-Pfad (Federated Managed Identity)
  2. MSAL acquireTokenForClient mit dem App-Token als clientAssertion und Bereich api://9b975845-388f-4429-889e-eab1ef63949c/.default

Note

Federated Managed Identity (FMI) ist eine Architektur, bei der eine verwaltete Identität mittels Verbundidentitätsanmeldeinformationen an der Verbundauthentifizierung für Workloadidentitäten teilnimmt und so den Tokenaustausch sowie eine geheimnislose Authentifizierung auf der Grundlage von Vertrauensbeziehungen zwischen Identitäten ermöglicht.

Sie müssen ein benutzerdefiniertes TokenResolver angeben und UseS2SEndpoint = true festlegen.

Voraussetzungen

  • Entra-App-Registrierung : Ein Dienstprinzipal (App-Registrierung) mit Client-ID, geheimem Clientschlüssel und Mandanten-ID

  • Anwendungs-API-Berechtigungen : Hinzufügen Agent365.Observability.OtelWrite (Anwendung), Erteilen der Administratorzustimmung

  • Agent365.Observability.OtelWrite Anwendungsrolle : Dem Dienstprinzipal des Agenten muss für die Agent365-Observability-Ressource die Rolle OtelWrite zugewiesen sein. Verwenden Sie den Agent 365 CLI:

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

    Note

    Die Rollenverteilung kann einige Minuten dauern. Anfängliche 401- oder 403-Fehler vom Exportendpunkt werden in diesem Zeitraum erwartet.

Schritt 1: Umgebungskonfiguration

Die folgenden Codebeispiele zeigen, wie Sie die erforderlichen Verbindungs-, Mandanten-, Clientanmeldeinformationen- und Observability-Exporterumgebungseinstellungen festlegen, bevor Sie den benutzerdefinierten S2S-Tokenfluss aktivieren.

Kein AgenticUserAuthorization-Behandler erforderlich. S2S verwendet die manuelle Agent-Identitätskette (get_agentic_application_token + MSAL acquire_token_for_client), um ein Token abzurufen, das für die Observability-Ressource vorgesehen ist.

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

Schritt 2: Konfigurieren Sie die Distribution mit benutzerdefiniertem Token-Resolver

Die folgenden Beispiele zeigen, wie Sie das Exportieren von Agent 365 aktivieren und eine benutzerdefinierte TokenResolver Registrierung durchführen, damit der Exporter S2S-Token für jeden Agent und Mandanten abrufen kann.

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

Schritt 3: Abrufen und Zwischenspeichern des S2S-Tokens

Rufen Sie für jede eingehende Nachricht das S2S-Token über die Agenten-Identitätskette ab und zwischenspeichern Sie es für den Resolver.

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

Der manuelle zweistufige Fluss (get_agentic_application_token + MSAL acquire_token_for_client) ist für S2S erforderlich. AgenticUserAuthorization.get_token() gibt ein Token mit Bereich 5a807f24-.../.default (Bot Framework) und nicht die Observability-Ressource api://9b975845-.../.default zurück: Der S2S-Endpunkt lehnt es mit 401 InvalidAudienceab.

  • Verwenden Sie context.activity.get_agentic_instance_id() und get_agentic_tenant_id(), um den Agenten und den Mandanten aus der Aktivität zu lesen (gemäß SDK-Konvention aus recipient).
  • Abrufen und Zwischenspeichern des S2S-Tokens vor dem Erstellen von Spans. Der Exporter BatchSpanProcessor wird möglicherweise geleert, bevor der Handler fertig ist: Wenn das Token noch nicht zwischengespeichert ist, schlägt der Export fehl.
  • Umschließen Sie alle A365-Scopes mit BaggageBuilder, damit der Exporter weiß, für welchen Agent und Mandanten Tokens aufgelöst werden sollen. Ohne Baggage werden Spans stillschweigend mit „Keine Spans mit Mandanten-/Agenten-Identität gefunden.“ verworfen.

Benutzerdefinierte Engine unter Verwendung von OBO

Benutzerdefinierte Engine-Agents verwenden Standard-App-Registrierungen mit Azure Bot OAuth-Verbindungen, nicht die agentenbasierte Identitätskette. Mithilfe von OBO ruft der Agent ein Benutzertoken über Azure Bot OAuth ab, das bereits auf die A365-Observability-API vom Bot Framework-Tokendienst begrenzt ist. Ein einzelner getToken- oder GetTurnTokenAsync-Aufruf gibt das korrekt abgegrenzte Token zurück, sodass Sie exchangeToken nicht benötigen.

Voraussetzungen

Entra-App-Registrierung mit delegierten API-Berechtigungen. Hinzufügen von Agent365.Observability.OtelWrite (delegiert) und Erteilen der Administratorgenehmigung

Important

Das agentId im Token-Cache muss mit der Client-ID der App-Registrierung übereinstimmen, nicht mit dem agenticAppId der Aktivität, das es für benutzerdefinierte Engine-Agents nicht gibt. Die Export-URL enthält das agentIdUnd ein Konflikt führt zu HTTP 403.

Schritt 1: Umgebungs- und App-Konfiguration

Die folgenden Beispiele zeigen, wie Sie Ihre App und Laufzeitumgebung konfigurieren, einschließlich Dienstverbindungswerte, Mandanten- und Clienteinstellungen sowie erforderliche Autorisierungszuordnungen.

# .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 schreibt alle Schlüssel von Umgebungsvariablen in Großbuchstaben. Der Handlername lautet dann OBOCONNECTIONPROFILE, und Sie müssen in Aufrufen von auth_handlers und get_token() mit genau dieser Groß- und Kleinschreibung darauf verweisen. Das Fehlen von TYPE verursacht Auth handler ... not recognized or not configured zur Laufzeit.

Schritt 2: Die Distribution für OBO konfigurieren

Die folgenden Beispiele zeigen, wie Sie den Agent 365-Export aktivieren, den Exporter auf dem OBO-Endpunkt beibehalten und einen benutzerdefinierten Code TokenResolver registrieren, der während des Exports delegierte Token zurückgibt.

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

Der OBO-Modus erfordert jwt_authorization_middleware für das aiohttpApplication (validiert das eingehende JWT (JSON Web Token) von Bot Framework). Der S2S-/Emulatorpfad sollte diese Middleware nicht enthalten.

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

Schritt 3: OBO-Token abrufen

Die folgenden Beispiele zeigen, wie Sie ein delegiertes OBO-Token aus der konfigurierten Azure Bot OAuth-Verbindung anfordern und dann nach App-Client und Mandant für den Exporter zwischenspeichern.

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

Voraussetzung für das Azure-Portal: Bei der Azure Bot-OAuth-Verbindung mit dem Namen oboConnectionProfile müssen die Scopes auf api://9b975845-388f-4429-889e-eab1ef63949c/Agent365.Observability.OtelWrite festgelegt sein. Ohne diese Einstellung ist das Token auf die eigene Zielgruppe (api://botid-...) des Bots beschränkt und der Export schlägt mit HTTP 401 InvalidAudiencefehl.

Note

AGENT_APP.auth.get_token() gibt das Token mit dem richtigen Gültigkeitsbereich direkt zurück – ein exchange_token()-Aufruf ist nicht erforderlich. Der Bot Framework-Tokendienst verarbeitet den OBO-Austausch, wenn der OAuth-Verbindungsumfang auf die A365-Observability-Ressource ausgerichtet ist.

Benutzerdefinierte Engine mit S2S

Benutzerdefinierte Engine-Agents können S2S (Clientanmeldeinformationen) verwenden, um mithilfe der Anmeldeinformationen der Dienstverbindung ein reines App-Token abzurufen. Diese Methode verwendet standardmäßige MSAL-Clientanmeldeinformationen – keine agentische Identitätskette erforderlich.

Voraussetzungen

  • Azure AD-App-Registrierung: Muss ein custom engine (Standard)-App sein. Für Agent 365 aktivierte App-Registrierungen können kein einfaches client_credentials für die Observability-Ressource (AADSTS82001) verwenden.
  • Anwendungsberechtigungen : Hinzufügen Agent365.Observability.OtelWrite (Anwendung, nicht delegiert) und Erteilen der Administratorzustimmung.

Important

Das agentId zum Zwischenspeichern muss das ClientId der ServiceConnection sein. Die Export-URL lautet /observabilityService/tenants/{tenantId}/otlp/agents/{agentId}/traces : Ein Konflikt verursacht HTTP 403.

Schritt 1: Umgebungs- und App-Konfiguration

Die folgenden Beispiele zeigen, wie Sie Ihre App und Laufzeitumgebung konfigurieren, einschließlich Dienstverbindungswerte, Mandanten- und Clienteinstellungen sowie erforderliche Autorisierungszuordnungen.

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

Schritt 2: Konfigurieren der Distro für S2S

Die folgenden Beispiele zeigen, wie Sie den Export von Agent 365 aktivieren, den Exporter auf den S2S-Endpunkt festlegen und eine benutzerdefinierte TokenResolver für die Token-Suche während des Exports registrieren.

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

Schritt 3: Abrufen des S2S-Tokens

Die folgenden Beispiele zeigen, wie Sie ein Nur-App-Zugriffstoken für die Observability-Ressource mithilfe der Dienstverbindungsanmeldeinformationen anfordern und dann nach Agent und Mandant für den Exporter zwischenspeichern.

# 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

Schritt 4: Festlegen des Gepäcks für den Spanexport

Der Agent365-Exporter erfordert, dass Gepäck (Mandanten-ID und Agent-ID) im Span-Kontext festgelegt werden muss. Ohne sie verwirft der Exporter stillschweigend Spans mit der Nachricht 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])