Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Eksporter Agent 365 wymaga modułu rozwiązywania tokenów do uwierzytelniania podczas eksportu telemetrii. Niniejszy przewodnik obejmuje konfigurację dla agentów zbudowanych przy użyciu Zestaw SDK agentów usługi Microsoft 365, w tym zarówno agentów z obsługą Agent 365, jak i agentów z niestandardowym silnikiem na platformach .NET, Python i Node.js.
W przypadku instalacji, ogólnej konfiguracji oraz scenariuszy niewymagających Agent SDK, zobacz Microsoft OpenTelemetry Distro.
Omówienie
Istnieją cztery scenariusze uwierzytelniania, w zależności od typu agenta i sposobu pozyskiwania tokenów. Pozyskiwanie tokenów może wykorzystywać przepływ On-Behalf-Of (OBO) lub Service-to-Service (S2S). Wybierz scenariusz pasujący do Twojej konfiguracji:
| Scenariusz | Podpis |
|---|---|
| Agent 365 z obsługą OBO | Wbudowany komponent AgenticTokenCache dystrybucji automatycznie obsługuje pozyskiwanie tokenów. Nie jest wymagany niestandardowy resolver. To jest zalecane podejście dla agentów z obsługą Agent 365. |
| Agent 365 z obsługą S2S | Agent uzyskuje token, korzystając z łańcucha tożsamości agenta (getAgenticApplicationToken + Microsoft Authentication Libraries (MSAL)). Wymaga niestandardowego TokenResolver. Stosuj to podejście, gdy OBO nie jest dostępne lub potrzebujesz tokenów aplikacyjnych. |
| Niestandardowy silnik wykorzystujący OBO | Agent uzyskuje token użytkownika za pośrednictwem Azure Bot OAuth, przeznaczony dla API obserwowalności. Wymaga niestandardowego TokenResolver oraz połączenia Azure Bot OAuth. |
| Niestandardowy silnik wykorzystujący S2S | Agent pozyskuje token aplikacyjny, używając danych uwierzytelniających klienta. Wymaga niestandardowego TokenResolver. Rejestracja aplikacji musi być standardowa (non-agentic). |
Agent 365 z obsługą OBO
Agenci zintegrowani z Agent 365 otrzymują żądania z tożsamością agenticzną (agenticAppId, agenticUserId) z platformy Agent 365. W OBO wbudowany AgenticTokenCache dystrybucji automatycznie obsługuje pozyskiwanie tokenów: nie jest potrzebny niestandardowy resolver tokenów.
Wymagania wstępne
- Rejestracja aplikacji Entra : Service principal (rejestracja aplikacji) z Client ID, Client Secret i Tenant ID
-
Delegowane uprawnienia API : Dodaj
Agent365.Observability.OtelWrite(delegowane), uzyskaj zgodę administratora. Szczegółowe kroki można znaleźć w artykule Udziel zgody.
Ustawienia
W każdej turze agent wywołuje funkcję RegisterObservability z kontekstem tury. Wbudowana pamięć podręczna wykorzystuje delegowany token użytkownika z handlera AgenticUserAuthorization do przeprowadzenia wymiany OBO, uzyskując token o zakresie Agent365.Observability.OtelWrite.
Pełne instrukcje dotyczące konfiguracji, w tym pakiety, konfigurację i przykłady kodu, zobacz w Agentic token cache with Agent Framework apps.
Agent 365 z obsługą S2S
Agenci Agent 365 mogą również używać uwierzytelniania S2S (service-to-service) zamiast OBO. Agent uzyskuje token, korzystając ze swojej tożsamości usługi (service principal) poprzez dwustopniowy agentowy łańcuch tożsamości:
-
getAgenticApplicationToken(tenantId, agentId): poświadczenia klienta + ścieżka Federated Managed Identity (FMI) - MSAL
acquireTokenForClientz tokenem aplikacji jakoclientAssertioni zakresemapi://9b975845-388f-4429-889e-eab1ef63949c/.default
Notatka
Federated Managed Identity (FMI) to architektura, w której zarządzana tożsamość uczestniczy w federacji tożsamości obciążenia za pomocą federowanych poświadczeń tożsamości, umożliwiając wymianę tokenów oraz uwierzytelnianie bez konieczności stosowania sekretów, opierając się na relacjach zaufania między tożsamościami.
Musisz podać niestandardową TokenResolver i ustawić UseS2SEndpoint = true.
Wymagania wstępne
Rejestracja aplikacji Entra : Service principal (rejestracja aplikacji) z Client ID, Client Secret i Tenant ID
Uprawnienia API aplikacji : Dodaj
Agent365.Observability.OtelWrite(Aplikacja), udziel zgody administratoraAgent365.Observability.OtelWriterola aplikacji: Podmiot usługowy agenta musi mieć przypisanąOtelWriterolę na zasobie Agent365 Observability. Korzystanie z Agent 365 CLI:a365 setup permissions bot --config-dir "<path-to-config-dir>"Notatka
Propagacja roli może potrwać kilka minut. W tym okresie mogą wystąpić początkowe błędy 401 lub 403 z punktu końcowego eksportu.
Krok 1: Konfiguracja środowiska
Poniższe przykłady kodu pokazują, jak skonfigurować wymagane ustawienia środowiska połączenia, tenant, dane uwierzytelniające klienta oraz eksportera obserwacji przed włączeniem niestandardowego przepływu tokenów S2S.
Nie trzeba definiować AgenticUserAuthorization handlera. S2S wykorzystuje ręczny łańcuch tożsamości agentowej (get_agentic_application_token + MSAL acquire_token_for_client), aby uzyskać token z zakresem dla zasobu obserwowalności.
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
Krok 2: Skonfiguruj dystrybucję z niestandardowym resolverem tokenów
Poniższe przykłady pokazują, jak włączyć eksport i rejestrację niestandardowego TokenResolver przez Agent 365, aby eksporter mógł pobierać tokeny S2S dla każdego agenta i tenanta.
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,
)
Krok 3: Uzyskanie i buforowanie tokena S2S
Przy każdej przychodzącej wiadomości pobieraj token S2S przez łańcuch tożsamości agentycznej i buforuj go dla resolvera.
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])
Ważne
Ręczny dwuetapowy proces (get_agentic_application_token + MSAL acquire_token_for_client) jest wymagany w przypadku S2S.
AgenticUserAuthorization.get_token() zwraca token z przypisanym zakresem do 5a807f24-.../.default (Bot Framework), a nie do zasobu obserwowalności api://9b975845-.../.default; punkt końcowy S2S odrzuca go kodem 401 InvalidAudience.
- Wykorzystaj
context.activity.get_agentic_instance_id()orazget_agentic_tenant_id(), aby odczytać agenta i dzierżawcę z aktywności (odczyt zrecipientzgodnie z konwencją SDK). - Uzyskaj i zbuforuj token S2S przed utworzeniem spanów. Bufor eksportera
BatchSpanProcessormoże zostać opróżniony zanim handler zakończy pracę: jeśli token nie został jeszcze zbuforowany, eksport się nie powiedzie. - Otocz
BaggageBuilderwszystkie zakresy A365, aby eksporter wiedział, dla którego agenta i tenanta rozwiązywać tokeny. Bez baggage, spany są po cichu odrzucane z komunikatem: „Nie znaleziono spanów z tożsamością najemcy/agenta.”
Niestandardowy silnik wykorzystujący OBO
Agenci niestandardowego silnika używają standardowych rejestracji aplikacji z połączeniami Azure Bot OAuth, a nie łańcucha tożsamości agenta. Korzystając z OBO, agent otrzymuje token użytkownika przez Azure Bot OAuth, który ma już odpowiedni zakres do API obserwacyjnego A365, nadany przez Bot Framework Token Service. Pojedyncze wywołanie getToken lub GetTurnTokenAsync zwraca poprawnie zakreskowany token, więc nie potrzebujesz exchangeToken.
Wymagania wstępne
Rejestracja aplikacji Entra z delegowanymi uprawnieniami API. Dodaj Agent365.Observability.OtelWrite (delegowane) i uzyskaj zgodę administratora
Ważne
agentId w pamięci podręcznej musi być zgodny z „Client ID” rejestracji aplikacji – a nie agenticAppId aktywności, która nie istnieje dla agentów niestandardowych silników. URL eksportu zawiera agentId, a niedopasowanie powoduje błąd HTTP 403.
Krok 1: Konfiguracja środowiska i aplikacji
Poniższe przykłady pokazują, jak skonfigurować aplikację i środowisko uruchomieniowe, uwzględniając wartości połączeń z usługami, ustawienia dzierżawcy i klienta oraz wymagane mapowania autoryzacji.
# .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
Ważne
load_configuration_from_env konwertuje wszystkie klucze zmiennych środowiskowych na wielkie litery. Nazwa handlera przyjmuje wartość OBOCONNECTIONPROFILE i musisz odwołać się do niej, zachowując dokładnie taką pisownię, w wywołaniach auth_handlers i get_token(). Brak TYPE powoduje Auth handler ... not recognized or not configured podczas działania programu.
Krok 2: Skonfiguruj distro dla OBO
Poniższe przykłady pokazują, jak włączyć eksportowanie przez Agent 365, utrzymać eksportera na punkcie końcowym OBO oraz zarejestrować niestandardowy TokenResolver resolver, który zwraca tokeny delegowane podczas eksportu.
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,
)
Notatka
Tryb OBO wymaga obecności jwt_authorization_middleware na aiohttpApplication, który waliduje przychodzący token JWT (JSON Web Token) z Bot Framework. Ścieżka S2S/emulator nie powinna zawierać tego middleware.
from microsoft_agents.hosting.aiohttp import jwt_authorization_middleware
app = Application(middlewares=[jwt_authorization_middleware])
Krok 3: Uzyskaj token OBO
Poniższe przykłady pokazują, jak uzyskać delegowany token OBO ze skonfigurowanego połączenia Azure Bot OAuth, a następnie przechowywać go w pamięci podręcznej według klienta aplikacji i dzierżawcy dla eksportera.
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
Ważne
Wymóg dotyczący Azure PortalWyPołączenie Azure Bot OAuth o nazwie oboConnectionProfile musi mieć swoje zakresy (Scopes) ustawione na api://9b975845-388f-4429-889e-eab1ef63949c/Agent365.Observability.OtelWrite. Bez tego ustawienia token jest przypisany do odbiorcy bota (api://botid-...) i eksport kończy się błędem HTTP 401 InvalidAudience.
Notatka
AGENT_APP.auth.get_token() zwraca token z odpowiednim zakresem bezpośrednio – nie jest potrzebne wywołanie exchange_token(). Usługa Bot Framework Token Service obsługuje wymianę OBO, gdy zakres połączenia OAuth celuje w zasób obserwacji A365.
Niestandardowy silnik wykorzystujący S2S
Agenci niestandardowego silnika mogą używać przepływu S2S (poświadczeń klienta) w celu uzyskania tokena aplikacji, korzystając z danych uwierzytelniających połączenia usługi. Ta metoda wykorzystuje standardowe poświadczenia klienta MSAL – nie jest wymagany łańcuch tożsamości agenta.
Wymagania wstępne
-
Rejestracja aplikacji Azure AD: Musi to być aplikacja silnika niestandardowego (standardowa). Rejestracje aplikacji z włączonym Agent 365 nie mogą używać zwykłego
client_credentialsdla zasobu obserwowalności (AADSTS82001). -
Uprawnienia aplikacji: Dodaj
Agent365.Observability.OtelWrite(aplikacyjne, nie delegowane) i zatwierdź zgodę administratora.
Ważne
agentId użyty podczas buforowania musi być ClientId ServiceConnection. URL eksportu to /observabilityService/tenants/{tenantId}/otlp/agents/{agentId}/traces : niedopasowanie powoduje błąd HTTP 403.
Krok 1: Konfiguracja środowiska i aplikacji
Poniższe przykłady pokazują, jak skonfigurować aplikację i środowisko uruchomieniowe, uwzględniając wartości połączeń z usługami, ustawienia dzierżawcy i klienta oraz wymagane mapowania autoryzacji.
# .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
Krok 2: Skonfiguruj distro dla S2S
Poniższe przykłady pokazują, jak włączyć eksportowanie Agent 365, ustawić eksporter na punkt końcowy S2S oraz zarejestrować niestandardowy TokenResolver do wyszukiwania tokenów podczas eksportu.
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,
)
Krok 3: Uzyskaj token S2S
Poniższe przykłady pokazują, jak uzyskać token dostępu aplikacji do zasobu obserwowalności, korzystając z poświadczeń połączenia serwisowego, a następnie przechowywać go w pamięci podręcznej dla agenta i dzierżawcy na potrzeby eksportera.
# 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
Krok 4: Ustaw baggage dla eksportu spanów
Eksporter Agent365 wymaga ustawienia baggage (tenant ID i agent ID) w kontekście spanu. Jeśli tego zabraknie, eksporter bez ostrzeżenia odrzuca span'y z wiadomością 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])