הגדרת אימות תצפית

המייצא של Agent 365 דורש פותר אסימונים לצורך אימות בעת ייצוא נתוני טלמטריה. מדריך זה עוסק בהגדרה עבור סוכנים שנבנו באמצעות Microsoft 365 Agents SDK, וכולל הן סוכנים התומכים ב-Agent 365 והן סוכנים בעלי מנוע מותאם אישית ב-.NET, ב-Python וב-Node.js.

לקבלת מידע על התקנת ההפצה, קביעת תצורה כללית ותרחישים שאינם מבוססי Agent SDK, ראה Microsoft OpenTelemetry Distro.

‏‫‏‫מבט כולל

קיימים ארבעה תרחישי אימות, בהתאם לסוג הסוכן ולאופן שבו הוא משיג אסימונים. לצורך השגת אסימונים ניתן להשתמש בזרימת On-Behalf-Of ‏(OBO) או ב-Service-to-Service ‏(S2S). בחר את התרחיש התואם להגדרה שלך:

תרחיש Description
תומך ב-Agent 365 ומשתמש ב-OBO ה-AgenticTokenCache המוכלל בהפצה מטפל באופן אוטומטי בהשגת אסימונים. אין צורך בפותר מותאם אישית. זוהי הגישה המומלצת עבור סוכנים התומכים ב-Agent 365.
תומך ב-Agent 365 ומשתמש ב-S2S הסוכן משיג אסימון באמצעות שרשרת זהויות הסוכן (getAgenticApplicationToken + ‏(MSAL)‏‏‏). נדרש TokenResolver מותאם אישית. השתמש בגישה זו כאשר OBO אינו זמין או כאשר דרושים לך אסימונים המיועדים לאפליקציה בלבד.
מנוע מותאם אישית המשתמש ב-OBO הסוכן מקבל אסימון משתמש באמצעות Azure Bot OAuth, עם טווח המוגדר ל-API של יכולת התצפית. נדרשים TokenResolver מותאם אישית וחיבור Azure Bot OAuth.
מנוע מותאם אישית המשתמש ב-S2S הסוכן משיג אסימון המיועד לאפליקציה בלבד באמצעות אישורי לקוח. נדרש TokenResolver מותאם אישית. רישום האפליקציה חייב להיות אפליקציה רגילה (שאינה אפליקציית סוכן).

תומך ב-Agent 365 ומשתמש ב-OBO

סוכנים התומכים ב-Agent 365 מקבלים מפלטפורמת Agent 365 בקשות הכוללות זהות סוכן (agenticAppId, agenticUserId). בעת שימוש ב-OBO, ה-AgenticTokenCache המוכלל בהפצה מטפל באופן אוטומטי בהשגת אסימונים : אין צורך בפותר אסימונים מותאם אישית.

‏‫דרישות מוקדמות‬

  • רישום אפליקציה ב-Entra: מנהל שירות (רישום אפליקציה) הכולל Client ID, ‏Client Secret ו-Tenant ID
  • הרשאות API מואצלות: הוסף את Agent365.Observability.OtelWrite (מואצלות) והענק הסכמת מנהל מערכת. לקבלת שלבים מפורטים, ראה הענקת ההרשאה.

הגדרה

בכל תור, הסוכן שלך קורא לפונקציה RegisterObservability עם הקשר התור. המטמון המוכלל משתמש באסימון המואצל של המשתמש מהמטפל AgenticUserAuthorization כדי לבצע החלפת OBO ולהשיג אסימון שהטווח שלו מוגדר ל-Agent365.Observability.OtelWrite.

לקבלת הוראות הגדרה מלאות, כולל חבילות, קביעת תצורה ודוגמאות קוד, ראה מטמון אסימונים של סוכן עם אפליקציות Agent Framework.

תומך ב-Agent 365 ומשתמש ב-S2S

סוכנים התומכים ב-Agent 365 יכולים להשתמש גם באימות S2S ‏(שירות-לשירות) במקום ב-OBO. הסוכן משיג אסימון באמצעות זהות מנהל השירות שלו, דרך שרשרת זהויות סוכן בת שני שלבים:

  1. getAgenticApplicationToken(tenantId, agentId): נתיב אישורי לקוח + Federated Managed Identity ‏(FMI)
  2. acquireTokenForClient של MSAL עם אסימון האפליקציה בתור clientAssertion והטווח api://9b975845-388f-4429-889e-eab1ef63949c/.default

הערה

Federated Managed Identity ‏(FMI) היא ארכיטקטורה שבה זהות מנוהלת משתתפת באיחוד זהויות של עומסי עבודה באמצעות אישורי זהות מאוחדת, וכך מתאפשרים החלפת אסימונים ואימות ללא סודות המבוססים על יחסי אמון בין זהויות.

עליך לספק TokenResolver מותאם אישית ולהגדיר UseS2SEndpoint = true.

‏‫דרישות מוקדמות‬

  • רישום אפליקציה ב-Entra: מנהל שירות (רישום אפליקציה) הכולל Client ID, ‏Client Secret ו-Tenant ID

  • הרשאות API של אפליקציה: הוסף את Agent365.Observability.OtelWrite (אפליקציה) והענק הסכמת מנהל מערכת

  • Agent365.Observability.OtelWriteתפקיד האפליקציה: למנהל השירות של הסוכן חייב להיות מוקצה התפקיד OtelWrite במשאב Agent365 Observability. השתמש ב-Agent 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: קבע את תצורת ההפצה באמצעות פותר אסימונים מותאם אישית

הדוגמאות הבאות מראות כיצד לאפשר ייצוא של Agent 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() כדי לקרוא את הסוכן ואת הדייר מהפעילות (הקריאה מתבצעת מתוך recipient, בהתאם למוסכמה של ה-SDK).
  • השג את אסימון ה-S2S ושמור אותו במטמון לפני יצירת טווחים. ה-BatchSpanProcessor של המייצא עשוי לבצע ריקון לפני שהמטפל מסיים : אם האסימון עדיין לא נשמר במטמון, הייצוא נכשל.
  • עטוף את כל הטווחים של A365 ב-BaggageBuilder, כדי שהמייצא ידע עבור איזה סוכן ודייר לפתור אסימונים. ללא מטען, טווחים מושמטים ללא הודעה, עם "No spans with tenant/agent identity found".

מנוע מותאם אישית המשתמש ב-OBO

סוכנים בעלי מנוע מותאם אישית משתמשים ברישומי אפליקציות רגילים עם חיבורי Azure Bot OAuth, ולא בשרשרת זהויות הסוכן. באמצעות OBO, הסוכן מקבל דרך Azure Bot OAuth אסימון משתמש שכבר הוגדר עבורו טווח ה-API של יכולת התצפית של A365 על-ידי Bot Framework Token Service. קריאה יחידה ל-getToken או ל-GetTurnTokenAsync מחזירה את האסימון בעל הטווח הנכון, ולכן אין צורך ב-exchangeToken.

‏‫דרישות מוקדמות‬

רישום אפליקציה ב-Entra עם הרשאות API מואצלות. הוסף את Agent365.Observability.OtelWrite (מואצלות) והענק הסכמת מנהל מערכת

חשוב

ה-agentId במטמון האסימונים חייב להתאים ל-Client ID של רישום האפליקציה - ולא ל-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_handlers ו-get_token(). היעדר TYPE גורם לשגיאה Auth handler ... not recognized or not configured בזמן ריצה.

שלב 2: קבע את תצורת ההפצה עבור OBO

הדוגמאות הבאות מראות כיצד לאפשר ייצוא של Agent 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 דורש את jwt_authorization_middleware ב-Application של aiohttp (מאמת את ה-JWT ‏(JSON Web Token) הנכנס מ-Bot Framework). הנתיב של S2S/האמולטור אינו אמור לכלול תוכנת תווכה זו.

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

שלב 3: השג את אסימון ה-OBO

הדוגמאות הבאות מראות כיצד לבקש אסימון OBO מואצל מחיבור Azure Bot OAuth שהוגדר, ולאחר מכן לשמור אותו במטמון לפי לקוח האפליקציה והדייר עבור המייצא.

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 Portal: חיבור Azure Bot OAuth בשם oboConnectionProfile יש להגדיר עםScopes המוגדר ל-api://9b975845-388f-4429-889e-eab1ef63949c/Agent365.Observability.OtelWrite. ללא הגדרה זו, טווח האסימון מוגדר לקהל של תוכנית ה-Bot עצמה (ללא הגדרה זו, טווח האסימון מוגדר לקהל של תוכנית ה-Bot עצמה (api://botid-...), והייצוא נכשל עם HTTP 401 InvalidAudience.), והייצוא נכשל עם InvalidAudience.

הערה

AGENT_APP.auth.get_token() מחזיר ישירות את האסימון בעל הטווח הנכון - אין צורך בקריאה ל-exchange_token(). Bot Framework Token Service מטפל בהחלפת OBO כאשר טווח חיבור ה-OAuth מכוון למשאב יכולת התצפית של A365.

מנוע מותאם אישית המשתמש ב-S2S

סוכנים בעלי מנוע מותאם אישית יכולים להשתמש ב-S2S ‏(אישורי לקוח) כדי להשיג אסימון המיועד לאפליקציה בלבד באמצעות אישורי חיבור השירות. שיטה זו משתמשת באישורי לקוח רגילים של MSAL - אין צורך בשרשרת זהויות סוכן.

‏‫דרישות מוקדמות‬

  • רישום אפליקציה ב-Azure AD: חייב להיות אפליקציית מנוע מותאם אישית (רגילה). רישומי אפליקציות התומכים ב-Agent 365 אינם יכולים להשתמש ב-client_credentials רגיל עבור משאב יכולת התצפית (AADSTS82001).
  • הרשאות אפליקציה: הוסף את Agent365.Observability.OtelWrite (אפליקציה, לא מואצלות) והענק הסכמת מנהל מערכת.

חשוב

ה-agentId המשמש לשמירה במטמון חייב להיות ה-ClientId של ה-ServiceConnection. כתובת ה-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

הדוגמאות הבאות מראות כיצד לאפשר ייצוא של Agent 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 דורש להגדיר מטען (מזהה דייר ומזהה סוכן) בהקשר הטווח. בלעדיו, המייצא משמיט טווחים ללא הודעה ומציג את ההודעה 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])