Einrichtung der Observability-Authentifizierung

Der Agent 365 Exporter benötigt einen Token-Auflöser zur Authentifizierung beim Export von Telemetriedaten. Dieser Leitfaden behandelt die Einrichtung für Agenten, die mit dem Microsoft 365 Agents SDK erstellt wurden, einschließlich Agent 365-fähiger Agenten und benutzerdefinierter Engine-Agenten für .NET, Python und Node.js.

Für Distro-Installation, allgemeine Konfiguration und Nicht-Agent-SDK-Szenarien siehe Microsoft OpenTelemetry Distro.

Übersicht

Es gibt vier Authentifizierungsszenarien, je nach Agententyp und Methode der Token-Erwerbung. Für den Tokenabruf kann On-Behalf-Of-Fluss (OBO) oder Service-to-Service (S2S) verwendet werden. Wählen Sie das Szenario, das zu Ihrer Einrichtung passt:

Szenario Beschreibung des Dataflows
Agent 365-fähig mit OBO Die integrierte Distro AgenticTokenCache übernimmt die Token-Beschaffung automatisch. Kein benutzerdefinierter Auflöser erforderlich. Dies ist der empfohlene Ansatz für Agent 365-fähige Agenten.
Agent 365-fähig mit S2S Der Agent erhält ein Token, indem er die agentische Identitätskette (getAgenticApplicationToken + Microsoft Authentication Libraries (MSAL)) verwendet. Erfordert einen benutzerdefinierten 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 ein Benutzertoken über Azure Bot OAuth, mit dem Umfang für die Observability-API. Erfordert eine benutzerdefinierte TokenResolver und eine Azure Bot OAuth-Verbindung.
Benutzerdefinierte Engine mit S2S Der Agent erhält ein Nur-App-Token mit Client-Zugangsdaten. Erfordert einen benutzerdefinierten TokenResolver. Die App-Registrierung muss eine Standard-App (keine agentenbasierte App) sein.

Agent 365-fähig mit OBO

Agent 365-fähige Agenten erhalten Anfragen mit agentischer Identität (agenticAppId, agenticUserId) von der Agent 365-Plattform. Mit OBO übernimmt der 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), Admin-Zustimmung erteilen. Für detaillierte Schritte, siehe Berechtigung erteilen.

Einstellungen

Bei jeder Interaktion ruft Ihr Agent die RegisterObservability-Funktion mit dem Kontext der Interaktion auf. Der integrierte Cache verwendet das vom AgenticUserAuthorization Handler erhaltene delegierte Token, um einen OBO-Austausch durchzuführen und ein Token mit Scope Agent365.Observability.OtelWrite zu erwerben.

Für vollständige Einrichtungsanweisungen einschließlich Pakete, Konfiguration und Codebeispiele siehe Agentic Token Cache mit Agent Framework Apps.

Agent 365-fähig mit S2S

Agenten mit Agent 365-Unterstützung können alternativ zur OBO-Authentifizierung auch die S2S-Authentifizierung (Service-to-Service) verwenden. Der Agent erhält ein Token unter Verwendung seiner eigenen Service Principal-Identität über eine zweistufige Agent-Identitätskette:

  1. getAgenticApplicationToken(tenantId, agentId) : Client-Anmeldeinformationen + verwalteter Verbundidentität (FMI)-Pfad
  2. MSAL acquireTokenForClient mit dem App-Token clientAssertion und dem Bereich api://9b975845-388f-4429-889e-eab1ef63949c/.default

Anmerkung

Federated Managed Identity (FMI) ist eine Architektur, bei der eine verwaltete Identität über föderierte Identitätsanmeldeinformationen an der Workload Identity Federation teilnimmt. Dadurch werden der Austausch von Token sowie eine geheimnisfreie Authentifizierung ermöglicht, die auf Vertrauensbeziehungen zwischen Identitäten basiert.

Sie müssen einen benutzerdefinierte TokenResolver bereitstellen und UseS2SEndpoint = true konfigurieren.

Voraussetzungen

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

  • Anwendungs-API-Berechtigungen : Fügen Sie Agent365.Observability.OtelWrite hinzu (Anwendung), Admin-Zustimmung erteilen

  • Agent365.Observability.OtelWrite-Anwendungsrolle : Dem Dienstprinzipal des Agents muss für die Agent365-Observability-Ressource die Rolle OtelWrite zugewiesen sein. Agent 365 CLI verwenden:

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

    Anmerkung

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

Schritt 1: Umgebung konfigurieren

Die folgenden Codebeispiele zeigen, wie die erforderlichen Verbindungs-, Mandanten-, Client-Anmeldeinformationen und Exporter-Umgebungseinstellungen für Observability konfiguriert werden, bevor der benutzerdefinierte S2S-Tokenfluss aktiviert wird.

Kein AgenticUserAuthorization Handler 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 Verteilung mit einem benutzerdefinierten Token-Auflöser

Die folgenden Beispiele zeigen, wie Sie Agent 365 Export aktivieren und einen benutzerdefinierten TokenResolver registrieren, damit der Exporter S2S-Tokens für jeden Agenten und Mandant 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: Erwerb und Cache des S2S-Token

Bei jeder eingehenden Nachricht wird das S2S-Token über die agentische Identitätskette abgerufen und für den Auflöser zwischengespeichert.

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

Wichtig

Der manuelle Zwei-Schritt-Ablauf (get_agentic_application_token + MSAL acquire_token_for_client) ist für S2S erforderlich. AgenticUserAuthorization.get_token() gibt ein Token mit dem Bereich 5a807f24-.../.default (Bot Framework) und nicht die Observability-Ressource api://9b975845-.../.default zurück: Der S2S-Endpunkt lehnt ihn mit 401 InvalidAudience ab.

  • Verwendet context.activity.get_agentic_instance_id() und get_agentic_tenant_id(), um den Agent und den Mandanten aus der Aktivität zu lesen (liest aus recipient gemäß SDK-Konvention).
  • Abrufen und Zwischenspeichern des S2S-Tokens vor dem Erstellen von Spans. Der Exporter BatchSpanProcessor kann Daten ausgeben, bevor der Handler abgeschlossen ist; wenn das Token noch nicht zwischengespeichert wurde, schlägt der Export fehl.
  • Umschließen Sie alle A365-Bereiche 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-/Agent-Identität gefunden“ verworfen.

Benutzerdefinierte Engine mit OBO

Benutzerdefinierte Engine-Agents verwenden Standard-App-Registrierungen mit Azure Bot-OAuth-Verbindungen, nicht die Agent-basierte 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 liefert das korrekt zugeordnete Token zurück, sodass exchangeToken nicht benötigt wird.

Voraussetzungen

Entra-App-Registrierung mit delegierten API-Berechtigungen. Fügen Sie Agent365.Observability.OtelWrite (Delegiert) hinzu und erteilen Sie Administratorzustimmung

Wichtig

Der agentId im Token-Cache muss mit der Client-ID der App-Registrierung übereinstimmen und nicht mit der agenticAppId Aktivität, die für benutzerdefinierte Engine-Agenten nicht existiert. Die Export-URL enthält die agentId, und eine Diskrepanz verursacht HTTP 403.

Schritt 1: Umgebung und App-Konfiguration

Die folgenden Beispiele zeigen, wie Sie Ihre App und die Laufzeitumgebung konfigurieren, einschließlich Serviceverbindungswerten, Mandanten- und Client-Einstellungen sowie erforderlichen 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

Wichtig

load_configuration_from_env wandelt alle Umgebungsvariablenschlüssel in Großbuchstaben um. 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: Konfigurieren die Distro für OBO

Die folgenden Beispiele zeigen, wie man den Export von Agent 365 aktiviert, den Exporter am OBO-Endpunkt behält und einen benutzerdefinierten TokenResolver registriert, der delegierte Token während des Exports 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,
)

Anmerkung

Der OBO-Modus erfordert jwt_authorization_middleware für aiohttpApplication (validiert das eingehende JWT (JSON Web Token) vom Bot Framework). Der S2S/Emulator-Pfad 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 ein delegiertes OBO-Token von der konfigurierten Azure Bot OAuth-Verbindung angefordert und anschließend pro App-Client und Mandant für den Exporter zwischengespeichert wird.

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

Wichtig

Azure-Portal-Voraussetzung: Die Azure Bot OAuth-Verbindung namens oboConnectionProfile muss ihren Umfang auf api://9b975845-388f-4429-889e-eab1ef63949c/Agent365.Observability.OtelWrite gesetzt haben. Ohne diese Einstellung ist das Token auf die eigene Benutzergruppe des Bots (api://botid-...) beschränkt und der Export schlägt mit HTTP 401 InvalidAudience fehl.

Anmerkung

AGENT_APP.auth.get_token() gibt das korrekt berechtigte Token direkt zurück – es ist kein exchange_token()-Aufruf erforderlich. Der Bot Framework-Tokendienst verarbeitet den OBO-Austausch, wenn der OAuth-Verbindungsbereich 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 Client-Anmeldeinformationen – eine agentische Identitätskette ist nicht erforderlich.

Voraussetzungen

  • Azure AD App-Registrierung : Muss eine benutzerdefinierte Engine (Standard)-App sein. App-Registrierungen mit Agent 365-Unterstützung können nicht direkt client_credentials für die Observability-Ressource (AADSTS82001) verwenden.
  • Anwendungsberechtigungen: Fügen Sie Agent365.Observability.OtelWrite hinzu (Anwendung, nicht delegiert) und erteilen Sie Administratorzustimmung.

Wichtig

Die agentId zum Zwischenspeichern muss die ClientId der ServiceConnection sein. Die Export-URL ist /observabilityService/tenants/{tenantId}/otlp/agents/{agentId}/traces: Eine Abweichung führt zu HTTP 403.

Schritt 1: Umgebung und App-Konfiguration

Die folgenden Beispiele zeigen, wie Sie Ihre App und die Laufzeitumgebung konfigurieren, einschließlich Serviceverbindungswerten, Mandanten- und Client-Einstellungen sowie erforderlichen 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 für den S2S-Endpunkt konfigurieren und eine benutzerdefinierte TokenResolver für die Token-Abfrage beim Export 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: S2S-Token abrufen

Die folgenden Beispiele zeigen, wie Sie mit den Service-Verbindungsdaten ein reines App-Zugriffstoken für die Einblicksressource anfordern und dieses anschließend agenten- und mandantenspezifisch 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: Baggage für den Span-Export festlegen

Der Agent365-Exporter erfordert, dass Baggage (Mandanten-ID und Agent-ID) im Span-Kontext festgelegt werden muss. Ohne 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])