可观测性身份验证设置

代理 365 导出程序要求在导出遥测时使用令牌解析程序进行身份验证。 本指南介绍如何设置使用 Microsoft 365 智能体 SDK 构建的代理,涵盖支持 Agent 365 的代理以及基于 .NET、Python 和 Node.js 的自定义引擎代理。

有关发行版安装、常规配置和非代理 SDK 方案,请参阅 Microsoft OpenTelemetry 发行版

概述

有四种身份验证方案,具体取决于代理类型及其获取令牌的方式。 令牌获取可使用代表流 (OBO) 或服务对服务 (S2S)。 选择与设置匹配的方案:

场景 Description
通过 OBO 启用 Agent 365 该发行版内置的 AgenticTokenCache 会自动处理令牌获取。 不需要自定义解析程序。 这是为启用了 Agent 365 的代理推荐的方法。
使用 S2S 启用 Agent 365 代理通过使用代理身份链(getAgenticApplicationToken + Microsoft 身份验证库(MSAL))获取令牌。 需要自定义 TokenResolver。 当 OBO 不可用,或者您需要仅限应用的令牌时,请使用此方法。
使用 OBO 的自定义引擎 代理通过 Azure Bot OAuth 获取用户令牌,该令牌的范围限定为可观测性 API。 需要自定义 TokenResolver 和 Azure Bot OAuth 连接。
使用 S2S 的自定义引擎 智能体使用客户端凭据获取仅限应用的令牌。 需要自定义 TokenResolver。 应用注册必须是标准(非代理)应用。

通过 OBO 启用 Agent 365

启用 Agent 365 的智能体会从 Agent 365 平台接收带有智能体身份 (agenticAppId, agenticUserId) 的请求。 使用 OBO 时,发行版的内置 AgenticTokenCache 会自动处理令牌获取:无需自定义令牌解析程序。

先决条件

  • Entra 应用注册: 包含客户端 ID、客户端密码和租户 ID 的服务主体(应用注册)
  • 委派的 API 权限 :添加 Agent365.Observability.OtelWrite委派),授予管理员同意。 有关详细步骤,请参阅 “授予权限”。

Setup

在每一轮中,你的代理都会调用 RegisterObservability 函数,并传入当前轮次的上下文。 内置缓存使用来自 AgenticUserAuthorization 处理程序的用户委托令牌执行 OBO 交换,获取作用域为 Agent365.Observability.OtelWrite 的令牌。

有关完整的安装说明,包括包、配置和代码示例,请参阅 Agent Framework 应用的 Agentic 令牌缓存

使用 S2S 启用 Agent 365

启用了 Agent 365 的代理还可以使用 S2S(服务间)身份验证,而不是 OBO。 智能体通过两步智能体身份链使用其自身的服务主体身份获取令牌:

  1. getAgenticApplicationToken(tenantId, agentId):客户端凭据 + 联合托管身份 (FMI) 路径
  2. MSAL acquireTokenForClient,其中应用令牌为 clientAssertion 且作用域为 api://9b975845-388f-4429-889e-eab1ef63949c/.default

注释

联合托管标识(FMI)是一种体系结构,其中托管标识通过联合标识凭据参与工作负荷标识联合,基于标识之间的信任关系启用令牌交换和无机密身份验证。

您必须提供自定义的 TokenResolver,并设置 UseS2SEndpoint = true

先决条件

  • Entra 应用注册:一个包含客户端 ID、客户端密钥和租户 ID 的服务主体(应用注册)

  • 应用程序 API 权限 :添加 Agent365.Observability.OtelWrite应用程序),授予管理员同意

  • Agent365.Observability.OtelWrite 应用角色:智能体的服务主体必须在 Agent365 可观测性资源上被分配了 OtelWrite 角色。 使用代理 365 CLI:

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

    注释

    角色传播可能需要几分钟。 在此期间,导出终结点的初始 401 或 403 错误是预期的。

步骤 1:环境配置

以下代码示例演示如何在启用自定义 S2S 令牌流之前设置所需的连接、租户、客户端凭据和可观测性导出程序环境设置。

无需 AgenticUserAuthorization 处理程序。 S2S 使用手动智能体身份链 (get_agentic_application_token + MSAL acquire_token_for_client) 来获取作用域限定于可观测性资源的令牌。

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

步骤 2:使用自定义令牌解析程序配置发行版

以下示例演示如何启用代理 365 导出和注册自定义 TokenResolver ,以便导出程序可以检索每个代理和租户的 S2S 令牌。

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

步骤 3:获取和缓存 S2S 令牌

在每个传入消息上,通过代理标识链获取 S2S 令牌,并将其缓存给解析程序。

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

重要说明

S2S 必须使用手动两步流程(get_agentic_application_token + MSAL acquire_token_for_client)。 AgenticUserAuthorization.get_token() 返回一个以 5a807f24-.../.default(Bot Framework)为作用域的令牌,而不是可观测性资源 api://9b975845-.../.default:S2S 端点会以 401 InvalidAudience 拒绝该令牌。

  • 使用 context.activity.get_agentic_instance_id()get_agentic_tenant_id() 从活动读取智能体和租户(根据 SDK 约定从 recipient 读取)。
  • 在创建范围 之前 获取和缓存 S2S 令牌。 导出器的 BatchSpanProcessor 可能会在处理程序完成之前刷新:如果令牌尚未缓存,导出就会失败。
  • 将所有 A365 作用域整合在 BaggageBuilder 中,以便导出器知道应为哪个智能体和租户解析令牌。 若未包含这些信息,跨度将被静默丢弃,并显示“未找到具有租户/智能体身份的跨度”。

基于 OBO 的自定义引擎

自定义引擎智能体使用带有 Azure Bot OAuth 连接的标准应用注册,而非智能体身份链。 通过 OBO,代理可通过 Azure Bot OAuth 获取用户令牌,该令牌的作用域已由 Bot Framework 令牌服务限定为 A365 可观测性 API。 单个 getTokenGetTurnTokenAsync 调用返回正确的作用域令牌,因此不需要 exchangeToken

先决条件

具有委派 API 权限Entra 应用注册。 添加 Agent365.Observability.OtelWrite (委派)并授予管理员同意

重要说明

令牌缓存中的 agentId 必须与应用注册中的 客户端 ID 匹配,而不是与活动的 agenticAppId 匹配;对于自定义引擎代理,这个 agenticAppId 并不存在。 导出 URL 包括 agentId,并且不匹配会导致 HTTP 403。

步骤 1:环境和应用配置

以下示例演示如何配置应用和运行时环境,包括服务连接值、租户和客户端设置以及所需的授权映射。

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

重要说明

load_configuration_from_env 将所有环境变量键名转换为大写。 处理程序名称会变为 OBOCONNECTIONPROFILE,你必须在 auth_handlersget_token() 调用中以完全相同的大小写引用它。 缺少 TYPE 会在运行时导致 Auth handler ... not recognized or not configured

步骤 2:为 OBO 配置发行版

以下示例演示如何启用代理 365 导出、将导出程序保留在 OBO 终结点上,以及注册在导出过程中返回委托令牌的自定义 TokenResolver

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

注释

OBO 模式要求在 aiohttpApplication 上使用 jwt_authorization_middleware(用于验证来自 Bot Framework 的入站 JWT(JSON Web 令牌))。 S2S/仿真器路径不应包含此中间件。

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

步骤 3:获取 OBO 令牌

以下示例演示如何从配置的 Azure Bot OAuth 连接请求委托的 OBO 令牌,然后由应用客户端和租户为导出程序缓存该令牌。

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

重要说明

Azure 门户 先决条件: 名为 oboConnectionProfile 的 Azure Bot OAuth 连接其Scopes必须设置为 api://9b975845-388f-4429-889e-eab1ef63949c/Agent365.Observability.OtelWrite。 如果没有此设置,令牌的范围限定为机器人自己的受众(api://botid-...),导出失败并出现 HTTP 401 InvalidAudience

注释

AGENT_APP.auth.get_token() 直接返回具有正确作用域的令牌——无需调用 exchange_token()。 当 OAuth 连接范围面向 A365 可观测性资源时,Bot Framework 令牌服务将处理 OBO 交换。

基于 S2S 的自定义引擎

自定义引擎智能体可通过 S2S(客户端凭据)使用服务连接凭据获取仅限应用的令牌。 此方法使用标准 MSAL 客户端凭据 - 不需要代理标识链。

先决条件

  • Azure AD 应用注册:必须是自定义引擎(标准)应用程序。 启用 Agent 365 的应用注册不能在可观测性资源 (AADSTS82001)中使用纯 client_credentials
  • 应用程序权限 :添加 Agent365.Observability.OtelWrite应用程序,未委派),并授予管理员同意。

重要说明

用于缓存的 agentId必须是 ServiceConnection 的 ClientId。 导出 URL 为 /observabilityService/tenants/{tenantId}/otlp/agents/{agentId}/traces :不匹配会导致 HTTP 403。

步骤 1:环境和应用配置

以下示例演示如何配置应用和运行时环境,包括服务连接值、租户和客户端设置以及所需的授权映射。

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

步骤 2:为 S2S 配置发行版

以下示例演示如何启用代理 365 导出、将导出程序设置为 S2S 终结点,以及如何在导出期间注册用于令牌查找的自定义 TokenResolver 项。

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

步骤 3:获取 S2S 令牌

以下示例演示如何使用服务连接凭据为可观测资源请求仅限应用的访问令牌,然后通过代理和租户为导出程序缓存该令牌。

# 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

步骤 4:为跨度导出设置负载

Agent365 导出器要求在跨度上下文中设置负载(租户 ID 和智能体 ID)。 如果没有它,导出程序会静默地丢弃跨度,并显示消息 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])