Configuração de autenticação de observabilidade

O exportador do Agent 365 requer um resolver de tokens para autenticar ao exportar a telemetria. Este guia aborda a configuração para os agentes criados com o SDK de Agentes do Microsoft 365, incluindo agentes ativados para o Agent 365 e agentes com motor personalizado em .NET, Python e Node.js.

Para instalação do distro, configuração geral e cenários sem o SDK de Agentes, consulte o Microsoft OpenTelemetry Distro.

Descrição geral

Existem quatro cenários de autenticação, dependendo do tipo de agente e de como adquire os tokens. A aquisição de tokens pode utilizar o fluxo On-Behalf-Of (OBO) ou Serviço a Serviço (S2S). Escolha o cenário que corresponde à sua configuração:

Cenário Descrição
Ativado para o Agent 365 utilizando o OBO O AgenticTokenCache incorporado no distro processa a aquisição de tokens automaticamente. Não é necessário um resolver personalizado. Esta é a abordagem recomendada para agentes ativados para o Agent 365.
Ativado para o Agent 365 utilizando o S2S O agente adquire um token utilizando a cadeia de identidade por meio de agentes (getAgenticApplicationToken + Microsoft Authentication Libraries (MSAL)). Requer um TokenResolver personalizado. Use esta abordagem quando o OBO não estiver disponível ou quando precisar de tokens apenas para a aplicação.
Motor personalizado utilizando o OBO O agente obtém um token de utilizador via Azure Bot OAuth, com o âmbito definido para a API de observabilidade. Requer um TokenResolver personalizado e uma ligação do Azure Bot OAuth.
Motor personalizado utilizando o S2S O agente adquire um token de aplicação utilizando as credenciais do cliente. Requer um TokenResolver personalizado. O registo da aplicação deve ser uma aplicação padrão (não por meio de agentes).

Ativado para o Agent 365 utilizando o OBO

Os agentes ativados para o Agent 365 recebem pedidos com identidade por meio de agentes (agenticAppId, agenticUserId) provenientes da plataforma do Agent 365. Com o OBO, o AgenticTokenCache incorporado do distro processa automaticamente a aquisição de tokens: não é necessário um resolver de tokens personalizado.

Pré-requisitos

  • Registo da aplicação Entra: um principal de serviço (registo da aplicação) com ID de Cliente, Segredo do Cliente e ID do Inquilino
  • Permissões de API delegadas : adicione Agent365.Observability.OtelWrite (Delegado) e conceda o consentimento do administrador. Para obter passos detalhados, consulte Conceder permissão.

Configurar

A cada turno, o seu agente chama a função RegisterObservability com o contexto do turno. A cache incorporada utiliza o token delegado do utilizador proveniente do processador AgenticUserAuthorization para realizar uma troca de OBO, adquirindo um token definido com âmbito como Agent365.Observability.OtelWrite.

Para obter instruções completas de configuração, incluindo pacotes, configuração e exemplos de código, consulte Cache de tokens por meio de agentes com aplicações Agent Framework.

Ativado para o Agent 365 utilizando o S2S

Os agentes ativados para o Agent 365 também podem utilizar uma autenticação S2S (serviço a serviço) em vez de um OBO. O agente adquire um token utilizando a sua própria identidade do principal de serviço através de uma cadeia de identidade por meio de agentes de dois passos:

  1. getAgenticApplicationToken(tenantId, agentId) : credenciais do cliente + caminho de Identidade Gerida Federada (FMI)
  2. MSAL acquireTokenForClient com o token da aplicação como clientAssertion e o âmbito api://9b975845-388f-4429-889e-eab1ef63949c/.default

Nota

A Identidade Gerida Federada (FMI) é uma arquitetura em que uma identidade gerida participa na federação de identidade de carga de trabalho através de credenciais de identidade federada, ativando a troca de tokens e autenticação sem segredos baseada em relações de confiança entre identidades.

Deve fornecer um TokenResolver personalizado e configurar UseS2SEndpoint = true.

Pré-requisitos

  • Registo da aplicação Entra: um principal de serviço (registo da aplicação) com ID de Cliente, Segredo do Cliente e ID do Inquilino

  • Permissões de API da aplicação: adicione Agent365.Observability.OtelWrite (Aplicação), conceda consentimento do administrador

  • Função de aplicação Agent365.Observability.OtelWrite: o principal de serviço do agente deve ter a função OtelWrite atribuída no recurso de Observabilidade do Agent365. Utilize a CLI do Agent 365:

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

    Nota

    A propagação de funções pode demorar alguns minutos. Esperam-se erros iniciais 401 ou 403 do ponto final de exportação durante este período.

Passo 1: Configuração do ambiente

Os exemplos de código a seguir mostram como definir as definições necessárias de ligação, inquilino, credenciais do cliente e ambiente do exportador de observabilidade antes de ativar o fluxo de token S2S personalizado.

Não é necessário um processador AgenticUserAuthorization. O S2S utiliza a cadeia de identidade manual do agente (get_agentic_application_token + MSAL acquire_token_for_client) para obter um token com âmbito para o recurso de observabilidade.

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

Passo 2: Configurar o distro com um resolver de token personalizado

Os exemplos seguintes mostram como ativar a exportação do Agent 365 e registar um TokenResolver personalizado para que o exportador possa obter 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,
)

Passo 3: Adquirir e armazenar em cache o token S2S

Em cada mensagem recebida, adquira o token S2S através da cadeia de identidade do agente e armazene-o em cache para o 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])

Importante

O fluxo manual em dois passos (get_agentic_application_token + MSAL acquire_token_for_client) é necessário para S2S. AgenticUserAuthorization.get_token() devolve um token com âmbito para 5a807f24-.../.default (Bot Framework), não para o recurso de observabilidade api://9b975845-.../.default: o ponto final S2S rejeita-o com 401 InvalidAudience.

  • Use context.activity.get_agentic_instance_id() e get_agentic_tenant_id() para ler o agente e o inquilino a partir da atividade (lê a partir de recipient de acordo com a convenção do SDK).
  • Adquira e armazene em cache o token S2S antes de criar spans. O BatchSpanProcessor do exportador pode ser libertado antes de o processador terminar: se o token ainda não estiver em cache, a exportação falhará.
  • Encapsule todos os âmbitos do A365 em BaggageBuilder para que o exportador saiba para que agente e inquilino resolver os tokens. Sem baggage, os spans são silenciosamente descartados com a mensagem "Nenhum span com identidade de inquilino/agente encontrado".

Motor personalizado utilizando o OBO

Os agentes de motor personalizado utilizam registos de aplicações padrão com ligações do Azure Bot OAuth, e não a cadeia de identidade do agente. Ao utilizar o OBO, o agente obtém um token de utilizador através do Azure Bot OAuth, que já está configurado para a API de observabilidade do A365 pelo Serviço de Tokens do Bot Framework. Uma única chamada de getToken ou GetTurnTokenAsync devolve o token com o âmbito correto, pelo que não necessita de exchangeToken.

Pré-requisitos

Registo da aplicação Entra com permissões delegadas da API. Adicione Agent365.Observability.OtelWrite (Delegado) e conceda consentimento do administrador

Importante

O agentId na cache de tokens deve coincidir com o ID do Cliente do registo da aplicação — e não com o agenticAppId da atividade, que não existe para agentes do motor personalizado. O URL de exportação inclui o agentId, e uma incompatibilidade causa HTTP 403.

Passo 1: Configuração do ambiente e da aplicação

Os exemplos seguintes mostram como configurar a sua aplicação e o ambiente de runtime, incluindo valores de ligação ao serviço, definições de inquilino e cliente, e os mapeamentos de autorização necessários.

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

O load_configuration_from_env converte todas as chaves das variáveis de ambiente em maiúsculas. O nome do processador é definido como OBOCONNECTIONPROFILE e deve ser referenciado exatamente com essa capitalização nas chamadas de auth_handlers e get_token(). A falta de TYPE causa Auth handler ... not recognized or not configured em runtime.

Passo 2: Configurar o distro para o OBO

Os exemplos a seguir mostram como ativar a exportação do Agent 365, manter o exportador no ponto final do OBO e registar um TokenResolver personalizado que devolve tokens delegados durante a exportação.

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

O modo OBO requer jwt_authorization_middleware em aiohttpApplication (valida o JWT (JSON Web Token) de entrada do Bot Framework). O caminho S2S/emulador não deve incluir este middleware.

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

Passo 3: Obter o token OBO

Os exemplos abaixo demonstram como pedir um token OBO delegado a partir da ligação do Azure Bot OAuth configurada e, posteriormente, como armazená-lo em cache por aplicação e inquilino para o 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

Pré-requisito do Portal do Azure: a ligação do Azure Bot OAuth denominada oboConnectionProfile deve ter os seus Âmbitos definidos como api://9b975845-388f-4429-889e-eab1ef63949c/Agent365.Observability.OtelWrite. Sem esta definição, o token fica limitado à audiência do bot (api://botid-...) e a exportação falha com HTTP 401 InvalidAudience.

Nota

AGENT_APP.auth.get_token() devolve diretamente o token com o âmbito correto – não é necessária uma chamada exchange_token(). O Serviço de Tokens do Bot Framework gere a troca de OBO quando o âmbito da ligação OAuth tem como alvo o recurso de observabilidade A365.

Motor personalizado utilizando o S2S

Os agentes de motor personalizado podem utilizar S2S (credenciais do cliente) para obter um token exclusivo da aplicação utilizando as credenciais de ligação de serviço. Este método utiliza credenciais padrão de cliente MSAL – não é necessária uma cadeia de identidade do agente.

Pré-requisitos

  • Registo de aplicação Azure AD : tem de ser uma aplicação de motor personalizado (padrão). Os registos de aplicações ativados para Agent 365 não podem utilizar client_credentials simples para o recurso de observabilidade (AADSTS82001).
  • Permissões de aplicação: adicionar Agent365.Observability.OtelWrite (Aplicação, não Delegado) e conceder consentimento do administrador.

Importante

O agentId utilizado para colocação em cache deve ser o ClientId do ServiceConnection. O URL de exportação é /observabilityService/tenants/{tenantId}/otlp/agents/{agentId}/traces : uma incompatibilidade causa HTTP 403.

Passo 1: Configuração do ambiente e da aplicação

Os exemplos seguintes mostram como configurar a sua aplicação e o ambiente de runtime, incluindo valores de ligação ao serviço, definições de inquilino e cliente, e os mapeamentos de autorização necessários.

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

Passo 2: Configurar o distro para o S2S

Os exemplos seguintes mostram como ativar a exportação do Agent 365, definir o exportador para o ponto final S2S e registar um TokenResolver personalizado para pesquisa de tokens durante a exportação.

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

Passo 3: Obter o token S2S

Os exemplos seguintes mostram como pedir um token de acesso exclusivo para aplicações ao recurso de observabilidade, utilizando as credenciais de ligação ao serviço, e depois colocá-lo em cache por agente e inquilino para o 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

Passo 4: Configurar baggage para exportação de spans

O exportador do Agent365 requer que o baggage (ID do inquilino e ID do agente) seja definido no contexto do span. Sem ele, o exportador descarta silenciosamente os spans com a mensagem 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])