可观测性身份验证设置

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

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

Overview

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

Scenario Description
通过 OBO 启用 Agent 365 发行版内置的 AgenticTokenCache 会自动处理委托路由上的令牌获取。 不需要自定义解析程序。 这条路由需要委派 Agent365.Observability.OtelWrite 权限和管理员同意,而 a365 setup all 不会为蓝图代理配置这些权限和同意。
使用 S2S 启用 Agent 365 代理通过代理身份链(getAgenticApplicationToken + Microsoft 认证库(MSAL))获取仅限应用的令牌。 需要自定义 TokenResolver 和S2S端点选项。 注册代理实例不需要可观察性权限或管理员同意,因此这是蓝图代理 a365 setup all设置时推荐的方法。
使用 OBO 的自定义引擎 代理通过 Azure Bot OAuth 获取用户令牌,该令牌的范围限定为可观测性 API。 需要自定义TokenResolver、Azure Bot OAuth 连接,以及管理员同意的委托Agent365.Observability.OtelWrite权限。
使用 S2S 的自定义引擎 代理使用客户端凭据获取仅限应用的令牌。 需要自定义 TokenResolver。 应用注册必须是标准(非代理)应用。 标准应用程序注册不是已注册代理实例,因此需要具有管理员同意的 Agent365.Observability.OtelWrite 应用程序权限。

使用 OBO 启用代理 365

启用了 Agent 365 的代理会从 Agent 365 平台接收带有智能体标识(agenticAppId, agenticUserId)的请求。 使用 OBO 后,发行版内置的 AgenticTokenCache 会自动处理委派路径上的令牌获取,所以你不需要自定义令牌解析器。

先决条件

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

  • 委派 API 权限:添加 Agent365.Observability.OtelWrite(委派)并授予管理员同意。 这个 a365 setup all 命令并没有为蓝图代理添加这个权限。 全局管理员可以通过运行以下命令添加它并授予管理员许可:

    a365 setup permissions custom --resource-app-id 9b975845-388f-4429-889e-eab1ef63949c --scopes Agent365.Observability.OtelWrite
    

    如需更多选项,包括 Microsoft Entra 管理中心,请参见“授予权限”。

小窍门

为了避免委派的可观测性权限和管理员同意,对于使用 a365 setup all 设置的蓝图代理,请使用 使用 S2S 启用的 Agent 365。

Setup

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

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

使用 S2S 启用 Agent 365

启用了 Agent 365 的代理还可以使用 S2S(服务间)身份验证,而不是 OBO。 建议采用此路径来设置带有 a365 setup all 的蓝图代理。 代理通过一个两步式的代理式身份链,使用其自身的服务主体身份获取令牌:

  1. 呼叫 getAgenticApplicationToken(tenantId, agentId) 以获取带有联邦托管身份(FMI)路径的客户端凭证令牌。
  2. 用应用令牌api://9b975845-388f-4429-889e-eab1ef63949c/.default和范围acquireTokenForClient调用 MSALclientAssertion。

Note

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

Microsoft OpenTelemetry 发行版默认使用委派路由,因此你必须为你的语言提供自定义TokenResolver并设置 S2S 端点选项,例如 UseS2SEndpoint = true、 useS2SEndpoint: true或 a365_use_s2s_endpoint=True。

先决条件

  • Agent 365 注册:代理实例必须在 Agent 365 中注册。 该 a365 setup all 命令用于注册蓝图代理实例。 仅创建 Microsoft Entra 身份并不会注册代理实例。
  • 用于FMI交换的蓝图凭证:使用蓝图客户端密码或托管身份获取用于api://AzureADTokenExchange/.default的联邦托管身份(FMI)令牌,然后将其交换为代理实例的可观测性令牌。
  • 身份值匹配:baggage 中的代理 ID 和租户 ID 以及导出 URL 必须与应用专用令牌中的代理实例客户端 ID 和租户相匹配。

Note

Agent 365 注册的代理实例可以使用不含 Agent365.Observability.OtelWrite 角色的仅应用令牌通过 S2S 路由导出。 它不需要 Observability 权限或管理员许可。 未注册身份(包括标准应用注册)仍然需要 Agent365.Observability.OtelWrite 应用角色和管理员同意。

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

Important

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 可能会在处理程序完成之前刷新:如果令牌尚未缓存,导出就会失败。
  • 用 BaggageBuilder 包裹所有 A365 范围,以便导出器知道要为哪个代理和租户解析令牌。 没有 baggage 时,span 会被静默丢弃,并显示“未找到带有租户/代理标识的 span。”

Important

在解析器返回并缓存最终的 Observability 令牌之前:

  • 验证该令牌仅限应用程序。 同意 idtyp=app。 如果 idtyp 不存在,则只接受具有非空 roles 声明或 oid 声明等于 sub 的非空标记。 拒绝具有 scp 声明或其他 idtyp 值的代币。
  • 检查 aud 是否为 9b975845-388f-4429-889e-eab1ef63949c 或 api://9b975845-388f-4429-889e-eab1ef63949c,并且令牌没有过期。
  • 切勿将中间蓝图断言、蓝图令牌、用户或 OBO 令牌发送给 Observability。
  • 缓存最后一个令牌并在它过期前刷新。 导出器在每个导出批次中,针对每个租户和代理身份调用一次解析器。

有关使用该发行版的完整实现,请参见 Agent 365 的 Node.js、Python 和 .NET 示例。

基于 OBO 的自定义引擎

自定义引擎代理使用标准应用注册以及 Azure Bot OAuth 连接,而不是 agentic identity chain。 通过 OBO,代理可通过 Azure Bot OAuth 获取用户令牌,该令牌的作用域已由 Bot Framework 令牌服务限定为 A365 可观测性 API。 单个 getToken 或 GetTurnTokenAsync 调用返回正确的作用域令牌,因此不需要 exchangeToken。

先决条件

Microsoft Entra 应用注册,使用委托 API 权限。 添加 Agent365.Observability.OtelWrite(委派)并授予管理员许可。

Important

令牌缓存中的 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

Important

load_configuration_from_env 将所有环境变量键名转换为大写。 处理程序名称会变为 OBOCONNECTIONPROFILE,你必须在 auth_handlers 和 get_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,
)

Note

OBO 模式要求在 jwt_authorization_middlewareaiohttp 上使用 Application(用于验证来自 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

Important

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

Note

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

基于 S2S 的自定义引擎

自定义引擎代理可以使用S2S(客户端凭证)通过服务连接凭证获取仅应用的令牌。 这种方法使用标准的MSAL客户端凭证——不需要agentic 身份链。

先决条件

  • Microsoft Entra 应用注册:应用注册必须是自定义引擎(标准)应用。 启用了 Agent 365 的应用注册不能将纯 client_credentials 用于可观测性资源(AADSTS82001)。
  • 应用权限:添加 Agent365.Observability.OtelWrite(应用,非委托),并授予管理员许可。 标准应用注册不是Agent 365注册代理实例,所以他们需要S2S路径上的应用角色。

Important

你用来缓存的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:为 Span 导出设置 Baggage

Agent365导出器要求在跨度上下文中提供baggage(租户 ID 和代理 ID)。 如果没有这些依赖项,导出器会丢弃 span 并记录该消息 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])