Configuração da autenticação de observabilidade

O exportador do Agente 365 requer um resolvedor de token para autenticar ao exportar telemetria. Este guia aborda a instalação de agentes criados com o SDK de Agentes do Microsoft 365, abrangendo agentes habilitados para Agente 365 e agentes de mecanismo personalizados em .NET, Python e Node.js.

Para a instalação da distribuição, a configuração geral e os cenários sem o SDK do Agente, consulte Microsoft OpenTelemetry Distro.

Visão geral

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

Scenario Descrição
Agente habilitado para o Microsoft 365 via OBO O recurso nativo da distribuição AgenticTokenCache cuida automaticamente da obtenção de token. Nenhum resolvedor personalizado é necessário. Essa é a abordagem recomendada para agentes habilitados para Agente 365.
Agente habilitado para 365 usando S2S O agente obtém um token usando a cadeia de identidade agêntica (getAgenticApplicationToken + Microsoft Authentication Libraries (MSAL)). Requer TokenResolver personalizado. Use essa abordagem quando o OBO não estiver disponível ou se você precisar de tokens somente de aplicativo.
Motor personalizado com OBO O agente obtém um token de usuário por meio do Azure Bot OAuth, com escopo voltado para a API de observabilidade. Requer um TokenResolver personalizado e uma conexão OAuth do Bot Azure.
Motor personalizado usando S2S O agente adquire um token somente de aplicativo usando credenciais de cliente. Requer TokenResolver personalizado. O registro de aplicativo deve ser do tipo padrão (não agêntico).

Agente habilitado para 365 usando OBO

Agentes habilitados para o Agent 365 recebem solicitações com identidade agêntica (agenticAppId, agenticUserId) da plataforma Agent 365. Com o OBO, o AgenticTokenCache interno da distro cuida automaticamente da obtenção do token: nenhum resolvedor personalizado de tokens é necessário.

Pré-requisitos

  • Registro do aplicativo Entra : Uma entidade de serviço (registro do aplicativo) com ID do Cliente, Segredo do Cliente e ID do Locatário
  • Permissões de API delegadas : Adicionar Agent365.Observability.OtelWrite (Delegado), conceder consentimento do administrador. Para obter etapas detalhadas, consulte Conceder a permissão.

Instalação

Em cada turno, seu agente chama a função RegisterObservability com o contexto do turno. O cache interno usa o token delegado do usuário do manipulador AgenticUserAuthorization para realizar uma troca OBO, adquirindo um token com escopo para Agent365.Observability.OtelWrite.

Para obter instruções completas para configuração, incluindo pacotes, configuração e exemplos de código, consulte Cache de tokens agêntico com apps do Agent Framework.

Agente habilitado para Microsoft 365 via S2S

Os agentes habilitados para o Agent 365 também podem usar a autenticação S2S (serviço a serviço) em vez de OBO. O agente obtém um token usando sua própria identidade da entidade de serviço por meio de uma cadeia de identidade agêntica de duas etapas:

  1. getAgenticApplicationToken(tenantId, agentId) : credenciais do cliente + caminho FMI (Identidade Gerenciada Federada)
  2. MSAL acquireTokenForClient usando o token do aplicativo como clientAssertion e o escopo api://9b975845-388f-4429-889e-eab1ef63949c/.default

Note

A FMI (Identidade Gerenciada Federada) é uma arquitetura em que uma identidade gerenciada participa da federação de identidade de carga de trabalho por meio de credenciais de identidade federadas, habilitando a troca de tokens e a autenticação sem segredo com base nas relações de confiança entre identidades.

Você deve fornecer um TokenResolver personalizado e definir UseS2SEndpoint = true.

Pré-requisitos

  • Registro do aplicativo Entra : Uma entidade de serviço (registro de aplicativo) com ID do Cliente, Segredo do Cliente e ID do Locatário

  • Permissões de API do aplicativo : Adicionar Agent365.Observability.OtelWrite (Aplicativo), conceder consentimento do administrador

  • Agent365.Observability.OtelWrite função de aplicativo: a entidade de serviço do agente deve ter a função OtelWrite atribuída no recurso Agent365 Observability. Use a CLI do Agente 365:

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

    Note

    A propagação de função pode levar alguns minutos. Erros 401 ou 403 iniciais do endpoint de exportação são esperados durante esse período.

Etapa 1: Configuração do ambiente

Os exemplos de código a seguir mostram como definir as configurações necessárias de ambiente de conexão, locatário, credencial de cliente e exportador de observabilidade antes de habilitar o fluxo de token S2S personalizado.

Nenhum AgenticUserAuthorization manipulador é necessário. O S2S usa a cadeia manual de identidade agêntica (get_agentic_application_token + MSAL acquire_token_for_client) para obter um token com escopo do 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

Etapa 2: Configurar a distribuição com resolvedor de token personalizado

Os exemplos a seguir mostram como habilitar a exportação do Agent 365 e registrar um TokenResolver personalizado para que o exportador possa obter tokens S2S para cada agente e locatário.

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

Etapa 3: Adquirir e armazenar em cache o token S2S

A cada mensagem recebida, adquira o token S2S por meio da cadeia de identidade agêntica e armazene-o em cache para uso do resolvedor.

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 de duas etapas (get_agentic_application_token + MSAL acquire_token_for_client) é necessário para S2S. AgenticUserAuthorization.get_token() retorna um token restrito ao escopo de 5a807f24-.../.default (Bot Framework), e não para o recurso de observabilidade api://9b975845-.../.default: o endpoint S2S o rejeita com 401 InvalidAudience.

  • Use context.activity.get_agentic_instance_id() e get_agentic_tenant_id() para ler o agente e o tenant da atividade (lendo de recipient conforme a convenção do SDK).
  • Adquira e armazene em cache o token S2S antes de criar intervalos. O BatchSpanProcessor do exportador pode ser descarregado antes que o manipulador termine: se o token ainda não estiver em cache, a exportação falhará.
  • Encapsule todos os escopos BaggageBuilder do A365 para que o exportador saiba para qual agente e locatário resolver tokens. Na ausência de bagagem, os spans são descartados silenciosamente com a mensagem "Nenhum span com identidade de locatário/agente encontrado."

Mecanismo personalizado usando o OBO

Os agentes de mecanismo customizado usam registros de aplicativo padrão com conexões OAuth do Azure Bot, não a cadeia de identidade agêntica. Usando o OBO, o agente obtém um token de usuário por meio de Azure Bot OAuth que já está no escopo da API de observabilidade do A365 pelo Serviço de Token do Bot Framework. Uma única chamada a getToken ou GetTurnTokenAsync retorna o token no escopo correto, portanto você não precisa de exchangeToken.

Pré-requisitos

Registro de aplicativo do Entra com permissões delegadas de API. Adicionar Agent365.Observability.OtelWrite (delegado) e conceder consentimento do administrador

Importante

O agentId no cache de token deve corresponder ao ID do cliente do registro do aplicativo, e não ao agenticAppId da atividade, que não existe para agentes de mecanismo personalizados. A URL de exportação inclui o agentId, e uma incompatibilidade causa HTTP 403.

Etapa 1: Configuração de ambiente e aplicativo

Os exemplos a seguir mostram como configurar seu aplicativo e ambiente de runtime, incluindo valores de conexão de serviço, configurações de locatário e cliente e 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

load_configuration_from_env converte todas as chaves das variáveis de ambiente em maiúsculas. O nome do manipulador passa a ser OBOCONNECTIONPROFILE e você deve referenciá-lo com essa capitalização exata nas chamadas auth_handlers e get_token(). A ausência de TYPE causa Auth handler ... not recognized or not configured em tempo de execução.

Etapa 2: Configurar a distribuição para OBO

Os exemplos a seguir mostram como habilitar a exportação do Agent 365, manter o exportador no endpoint OBO e registrar um TokenResolver personalizado que retorna 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,
)

Note

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

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

Etapa 3: Adquirir o token OBO

Os exemplos a seguir mostram como solicitar um token OBO delegado da conexão Azure Bot OAuth configurada e, em seguida, armazená-lo em cache por cliente de aplicativo e locatário 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

portal do Azure pré-requisito: A conexão OAuth do Bot Azure denominada oboConnectionProfile deve ter seu Scopes definido como api://9b975845-388f-4429-889e-eab1ef63949c/Agent365.Observability.OtelWrite. Sem essa configuração, o token tem como escopo o próprio público-alvo do bot (api://botid-...) e a exportação falha com HTTP 401 InvalidAudience.

Note

AGENT_APP.auth.get_token() retorna o token com escopo correto diretamente – nenhuma exchange_token() chamada é necessária. O Serviço de Token do Bot Framework manipula a troca de OBO quando o escopo da conexão OAuth tem como destino o recurso de observabilidade do A365.

Motor personalizado com S2S

Os agentes personalizados do mecanismo podem usar S2S (credenciais do cliente) para adquirir um token somente de aplicativo usando as credenciais de conexão de serviço. Este método usa credenciais de cliente padrão do MSAL - não requer cadeia de identidade agêntica.

Pré-requisitos

  • Registro de aplicativo do Azure AD: deve ser um aplicativo de mecanismo personalizado (padrão). Os registros de aplicativo com o Agent 365 habilitado não podem usar apenas client_credentials para o recurso de observabilidade (AADSTS82001).
  • Permissões de aplicativo: Adicionar Agent365.Observability.OtelWrite (Aplicativo, não Delegado) e conceder consentimento do administrador.

Importante

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

Etapa 1: Configuração de ambiente e aplicativo

Os exemplos a seguir mostram como configurar seu aplicativo e ambiente de runtime, incluindo valores de conexão de serviço, configurações de locatário e cliente e 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

Etapa 2: Configurar a distribuição para S2S

Os exemplos a seguir mostram como habilitar a exportação do Agent 365, definir o exportador para usar o endpoint S2S e registrar um TokenResolver personalizado para busca de token 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,
)

Etapa 3: Adquirir o token S2S

Os exemplos a seguir mostram como solicitar um token de acesso somente para aplicativo para o recurso de observabilidade ao usar as credenciais da conexão de serviço e, em seguida, armazená-lo em cache por agente e por locatário 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

Etapa 4: Definir baggage para exportação de spans

O exportador Agent365 exige que a bagagem (ID do locatário e ID do agente) esteja definida no contexto do span. Sem ele, o exportador descarta spans sem aviso, exibindo 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])