OTel을 직접 활용하여 에이전트 가시성을 통합

이 가이드는 OpenTelemetry(OTLP/HTTP+JSON)를 통해 에이전트 원격 분석을 Agent 365로 직접 전송하는 전체 과정을 단계별로 안내합니다. 시작하기 전에 Agent 365 가시성 개념을 읽은 후, 모델, 인증 흐름, 그리고 데이터가 도달하는 위치를 이해하시기 바랍니다.

중요

직접 OTel 경로는 표준 경로가 아니라 예외적인 경우입니다. 이미 OpenTelemetry 파이프라인이 있거나, 프레임워크가 Agent 365 SDK를 사용할 수 없거나, 에이전트가 SDK에서 아직 지원하지 않는 언어(예시 Java)일 때만 사용하세요. 그 외의 경우에는 Microsoft OpenTelemetry 배포판이 권장되며, 이는 Agent 365, Microsoft Foundry, Azure Monitor 등에서 통합 가시성 SDK를 제공합니다. 이전 가시성 SDK는 호환성 문제 없이 계속 작동하지만, 새로운 통합에는 더 이상 권장되지 않습니다. 기존 SDK 사용자들을 위한 마이그레이션 가이드가 곧 제공될 예정입니다.

필수 구성 요소

원격 분석 흐름이 시작되기 전에 다음 설정이 완료되어 있는지 확인하세요.

누가 대상
테넌트 관리자 Agent 365에 가입하고 에이전트 앱에 대한 동의를 부여하세요. Agent 365 온보딩을 참조하세요. 라이선스가 있는 테넌트가 없으면 수집된 데이터는 아무런 오류 없이 조용히 폐기됩니다. 요청은 200 OKpartialSuccess: null를 반환하지만 데이터는 후속 시스템에 나타나지 않습니다.
테넌트 관리자 테넌트 내 최소 한 명의 사용자에게 Microsoft 365 E7 또는 Microsoft Agent 365 라이선스를 할당하세요. SKU가 존재하는 것만으로는 충분하지 않습니다. 사용자에게 라이선스를 할당하면 Defender 백엔드 워크플로가 시작되어 데이터 수집이 활성화됩니다. 라이선스가 할당되지 않으면, 요청은 200 OKpartialSuccess: null를 반환하고 데이터는 조용히 폐기됩니다.
테넌트 관리자 테넌트 동의를 승인하세요. Microsoft 365 리소스에 대한 에이전트 액세스 권한 부여를 참조하세요. 이 동의가 없으면 토큰이 역할/범위 없이 발급되고, 요청은 403를 반환합니다.
내 개발팀 앱을 등록하세요(표준 Microsoft Entra 앱 또는 청사진 앱). Agent 365 개발 시작하기를 참조하세요.
내 개발팀 API 권한 아래에 Agent365.Observability.OtelWrite를 추가하세요(S2S용 앱 역할, 위임용 범위). 청사진의 경우, 상속 권한 구성를 참조하세요. Agent 365 온보딩 팀과 조율하여 권한을 활성화하세요.

인증 레시피

네 가지 인증 방법 모두 표준 Microsoft Entra 토큰 엔드포인트를 사용합니다.

필드 가치
토큰 엔드포인트 https://login.microsoftonline.com/{your-tenant-id}/oauth2/v2.0/token
리소스(aud가 반환된 토큰에 포함됨) 9b975845-388f-4429-889e-eab1ef63949c(api://9b975845-388f-4429-889e-eab1ef63949c도 허용됨)
S2S 범위 9b975845-388f-4429-889e-eab1ef63949c/.default
OBO 범위 9b975845-388f-4429-889e-eab1ef63949c/Agent365.Observability.OtelWrite

아래의 레시피는 명확성을 위해 원시 HTTP를 보여줍니다. 운영 환경에서는 Microsoft.Identity.Web 또는 토큰 새로고침과 캐싱을 처리하는 다른 MSAL 라이브러리를 사용하는 것이 좋습니다.

어떤 레시피가 필요할까요?

나의 앱 모델 나의 OAuth 흐름 이동
표준 Microsoft Entra 앱 등록 S2S(클라이언트 자격 증명) S2S, 표준 Microsoft Entra 앱
표준 Microsoft Entra 앱 등록 OBO(위임됨) OBO, 표준 Microsoft Entra 앱
청사진 기반 에이전트 ID S2S(클라이언트 자격 증명) S2S, 청사진 기반 에이전트 ID
청사진 기반 에이전트 ID OBO / AI 팀원 OBO, 청사진 기반 에이전트 ID

S2S, 표준 Microsoft Entra 앱

grant_type=client_credentials를 사용하여 테넌트의 토큰 엔드포인트에 POST 요청을 한 번 수행합니다. 클라이언트 암호, 인증서(서명된 JWT 어설션), 관리형 ID 또는 페더레이션 자격 증명을 사용하여 앱을 인증합니다.

POST https://login.microsoftonline.com/{your-tenant-id}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded

client_id={your-app-id}
&scope=9b975845-388f-4429-889e-eab1ef63949c%2F.default
&client_secret={secret}
&grant_type=client_credentials

반환된 토큰에는 Agent365.Observability.OtelWrite, aud = 9b975845-...가 포함된 appid/azp = {your-app-id}, roles가 있습니다. /observabilityService/.../traces 경로에서 사용하십시오.

인증서 기반 인증의 경우, client_secret={secret}client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion={signed-jwt}로 대체하십시오.

S2S, 청사진 기반 에이전트 ID

에이전트 ID에는 자체 자격 증명이 없습니다. 에이전트 ID 청사진은 자격 증명(관리 ID FIC, 인증서 또는 클라이언트 암호)을 보유하고 있으며, 2단계 교환을 통해 자식 에이전트 ID를 대신하여 토큰을 발행합니다. 자세한 내용은 자율 앱 OAuth 흐름을 참조하십시오.

  1. 청사진은 인증하고 페더레이션 ID 교환 토큰 T1을 획득합니다.

    • {blueprint-credential}는 청사진의 MSI 토큰, 인증서 서명된 JWT, 또는 청사진 구성에 따른 비밀 교환 토큰 어설션입니다.
    POST https://login.microsoftonline.com/{your-tenant-id}/oauth2/v2.0/token
    Content-Type: application/x-www-form-urlencoded
    
    client_id={blueprint-app-id}
    &scope=api%3A%2F%2FAzureADTokenExchange%2F.default
    &fmi_path={agent-identity-app-id}
    &client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
    &client_assertion={blueprint-credential}
    &grant_type=client_credentials
    
  2. 에이전트 ID는 T1을 Agent 365 가시성 리소스 토큰으로 교환합니다.

    POST https://login.microsoftonline.com/{your-tenant-id}/oauth2/v2.0/token
    Content-Type: application/x-www-form-urlencoded
    
    client_id={agent-identity-app-id}
    &scope=9b975845-388f-4429-889e-eab1ef63949c%2F.default
    &client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
    &client_assertion={T1}
    &grant_type=client_credentials
    
    • 반환된 토큰에는 Agent365.Observability.OtelWrite, aud = 9b975845-...가 포함된 appid/azp = {agent-identity-app-id}, roles가 있습니다.
    • 이 토큰을 /observabilityService/.../traces 경로에 사용하십시오.
    • URL {agentId}에이전트 ID appId이며, 청사진 appId가 아닙니다.

OBO, 표준 Microsoft Entra 앱

상류 호출자(베어러 또는 PFAT)로부터 사용자의 들어오는 토큰 Tc을 받은 후, 이를 교환합니다.

POST https://login.microsoftonline.com/{your-tenant-id}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded

client_id={your-app-id}
&scope=9b975845-388f-4429-889e-eab1ef63949c%2FAgent365.Observability.OtelWrite
&client_secret={secret}
&grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&assertion={Tc}
&requested_token_use=on_behalf_of

인증서 인증의 경우, client_secret={secret}을 S2S 방식과 동일한 client_assertion_type + client_assertion 쌍으로 대체하십시오.

반환된 토큰에는 Agent365.Observability.OtelWrite, aud = 9b975845-...가 포함된 appid/azp = {your-app-id}, scp가 있습니다. /observability/.../traces 경로에서 사용하십시오. 새로 고침 토큰이 함께 반환됩니다. 매번 교환을 다시 실행하는 대신 캐시에 저장하여 재사용하세요.

OBO, 청사진 기반 에이전트 ID(AI 팀원 포함)

On-Behalf-Of 흐름에는 세 가지 주요 단계가 있습니다. 자세한 내용은 에이전트 OAuth 흐름: On-Behalf-Of 흐름을 참조하세요.

  1. 사용자 토큰 Tc을 받습니다. AI 팀원의 경우, 이 토큰은 에이전트의 자체 사용자 계정을 나타내며, 그렇지 않은 경우에는 실제 사용자를 나타냅니다.

  2. 청사진은 인증하고 S2S 설계도에서 파생된 에이전트 ID 흐름과 동일하게 T1을 획득합니다.

  3. 에이전트 ID는 T1Tc를 위임된 리소스 토큰과 교환합니다.

    POST https://login.microsoftonline.com/{your-tenant-id}/oauth2/v2.0/token
    Content-Type: application/x-www-form-urlencoded
    
    client_id={agent-identity-app-id}
    &scope=9b975845-388f-4429-889e-eab1ef63949c%2FAgent365.Observability.OtelWrite
    &client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
    &client_assertion={T1}
    &grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
    &assertion={Tc}
    &requested_token_use=on_behalf_of
    

반환된 토큰은 Agent365.Observability.OtelWrite가 포한된 appid/azp = {agent-identity-app-id}, scp를 가지며, 해당 에이전트의 사용자를 나타냅니다. /observability/.../traces 경로에서 사용하십시오. URL {agentId}에이전트 ID appId이며, 청사진 appId가 아닙니다. 새로 고침 토큰이 함께 반환됩니다. 캐시하고 재사용하세요.

반환된 토큰의 필수 클레임

S2S 경로(/observabilityService/...) - 앱 전용 토큰:

클레임 필수 값
aud 9b975845-388f-4429-889e-eab1ef63949c (또는 api://9b975845-...)
roles Agent365.Observability.OtelWrite를 반드시 포함해야 합니다
appid (v1) 또는 azp (v2) URL이 {agentId}와 같아야 합니다
scp 존재하지 않아야 합니다

위임된 경로 (/observability/...) - 사용자 위임 토큰 (베어러 또는 PFAT):

클레임 필수 값
aud 9b975845-388f-4429-889e-eab1ef63949c (또는 api://9b975845-...)
scp Agent365.Observability.OtelWrite를 반드시 포함해야 합니다
appid / azp URL이 {agentId}와 같아야 합니다

위임된 경로는 BearerMSAuth1.0 PFAT 토큰을 모두 허용합니다. 직접 호출자는 Bearer를 사용해야 합니다. 어떤 것을 가지고 있는지 모르면 Bearer를 사용하세요.

엔드포인트

두 가지 경로가 있습니다. 사용자가 무엇을 하고 있는지가 아니라, 귀하의 서비스가 어떻게 인증하는지에 따라 선택하십시오.

POST https://agent365.svc.cloud.microsoft/observabilityService/tenants/{tenantId}/otlp/agents/{agentId}/traces?api-version=1   # S2S
POST https://agent365.svc.cloud.microsoft/observability/tenants/{tenantId}/otlp/agents/{agentId}/traces?api-version=1          # OBO

헤더:

Authorization: Bearer <token>      # or MSAuth1.0 ... for delegated PFAT
Content-Type: application/json

URL 매개 변수

  • {tenantId} - 고객 테넌트 GUID. 서버는 이를 신뢰 기준으로 간주합니다; 스팬이 microsoft.tenant.id로 설정되어 있고 값이 일치하지 않으면 요청이 거부됩니다.
  • {agentId} - 호출 애플리케이션의 appId(OAuth client_id에도 해당됨). 청사진에서 파생된 ID의 경우, 이는 에이전트 ID appId이며, 청사진 appId가 아닙니다. 토큰의 appid / azp 클레임과 일치해야 합니다.
  • api-version=1 - 필수

요청 본문 인코딩

본문은 표준 OTLP/HTTP+JSON 형식입니다: ExportTraceServiceRequestresourceSpansscopeSpansspans가 포함되어 있습니다. 다음 사항을 유의하십시오.

  • traceId(16바이트) 및 spanId(8바이트)는 소문자 16진수 문자열로 전송됩니다.
  • startTimeUnixNano / endTimeUnixNano는 Unix 에포크 나노초 값을 담고 있는 문자열입니다.
  • kind는 정수형 OTLP 열거형 값입니다(예시 1INTERNAL의 경우). status.code는 정수형 열거형입니다(예시 1OK의 경우, 2ERROR의 경우).
  • 모든 속성 값은 stringValue로 전송됩니다.

응답 형태

성공적인 호출은 200 OK를 반환합니다.

{ "partialSuccess": null }

일부 스팬이 각 스팬 필터에 의해 거부된 경우:

{
  "partialSuccess": {
    "rejectedSpans": 2,
    "errorMessage": "Dropped 2 non-A365 span(s) ..."
  }
}

필드 이름은 데이터 전송 시 camelCase로 사용됩니다. 항상 partialSuccess 확인: 모든 스팬이 거부된 200 응답은 반드시 표시해야 하는 실제 결과입니다. 제한 및 드롭 조건은 다운스트림으로 데이터가 전달되지 않았음에도 불구하고 partialSuccess: null을 포함한 200 응답이 반환되는 사일런트 드롭 사례를 나열합니다.

가장 작은 요청

가장 단순한 엔드투엔드 테스트는 단일 invoke_agent 스팬을 전송합니다. 이 스팬은 Microsoft Defender에 전달되는 가장 작은 본문입니다.

1단계. 전달자 토큰을 가져옵니다. S2S의 경우, 클라이언트 자격 증명과 범위 9b975845-388f-4429-889e-eab1ef63949c/.default (전체 레시피는 인증 레시피 참조)를 사용하세요.

2단계. 단일 스팬 POST:

TOKEN="$(./get-token.sh)"
TENANT_ID="<customer-tenant-guid>"
AGENT_ID="<your-agent-app-id>"

curl -i -X POST \
  "https://agent365.svc.cloud.microsoft/observabilityService/tenants/${TENANT_ID}/otlp/agents/${AGENT_ID}/traces?api-version=1" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  --data @- <<EOF
{
  "resourceSpans": [{
    "scopeSpans": [{
      "scope": { "name": "my-instrumentation", "version": "1.0.0" },
      "spans": [{
        "traceId": "0102030405060708090a0b0c0d0e0f10",
        "spanId":  "1111111111111111",
        "parentSpanId": "",
        "name": "invoke_agent",
        "kind": 1,
        "startTimeUnixNano": "1736175600000000000",
        "endTimeUnixNano":   "1736175601500000000",
        "status": { "code": 1 },
        "attributes": [
          { "key": "gen_ai.operation.name", "value": { "stringValue": "invoke_agent" } },
          { "key": "gen_ai.agent.id",       "value": { "stringValue": "${AGENT_ID}" } },
          { "key": "gen_ai.agent.name",     "value": { "stringValue": "MyAgent" } },
          { "key": "microsoft.a365.agent.blueprint.id", "value": { "stringValue": "${AGENT_ID}" } },
          { "key": "gen_ai.conversation.id","value": { "stringValue": "conv-001" } },
          { "key": "microsoft.channel.name","value": { "stringValue": "web" } },
          { "key": "user.id",               "value": { "stringValue": "<entra-user-objectid>" } },
          { "key": "client.address",        "value": { "stringValue": "10.1.2.80" } },
          { "key": "server.address",        "value": { "stringValue": "myagent.example.com" } },
          { "key": "server.port",           "value": { "stringValue": "443" } },
          { "key": "gen_ai.input.messages", "value": { "stringValue": "[{\"role\":\"user\",\"content\":\"hi\"}]" } },
          { "key": "gen_ai.output.messages","value": { "stringValue": "[{\"role\":\"assistant\",\"content\":\"hello\"}]" } }
        ]
      }]
    }]
  }]
}
EOF

3단계. 아래와 같은 본문으로 200 OK가 반환됩니다.

{ "partialSuccess": null }

4단계. 데이터가 실제로 도착했는지 확인하세요. 200 OK 응답이 곧 데이터 수집이 완료되었음을 보장하는 것은 아닙니다. 데이터 수집 확인 섹션에서 확인 절차를 안내합니다. 저장된 본체 파일을 POST하려면 .--data @- <<EOF ... EOF--data @./otlp-request.json로 교체하세요.

에이전트 실행 예시

Microsoft Teams의 사용자가 "시애틀의 날씨가 어떤가요?"라고 묻습니다. 에이전트가 GetWeather 함수를 호출하고, LLM에게 답변을 포맷하도록 요청한 뒤 응답합니다. 해당 단일 실행은 네 개의 스팬으로 구성됩니다.

graph TD
    A["<b>invoke_agent</b> · spanId=A · parentSpanId=∅<br/><i>root - the run itself</i>"]
    B["<b>chat</b> · spanId=B · parentSpanId=A<br/><i>LLM picks the tool / formats reply</i>"]
    C["<b>execute_tool</b> · spanId=C · parentSpanId=A<br/><i>the GetWeather call</i>"]
    D["<b>output_messages</b> · spanId=D · parentSpanId=A<br/><i>final reply emitted to the user</i>"]
    A --> B
    A --> C
    A --> D

실행 전체에 적용되는 특성이 모든 스팬에 설정됨:

특성 예제 값
traceId 0102030405060708090a0b0c0d0e0f10
gen_ai.conversation.id 19:abc@thread.tacv2
microsoft.session.id session-1234
microsoft.channel.name msteams
gen_ai.agent.id <AGENT_APP_ID>
gen_ai.agent.name WeatherBot
microsoft.a365.agent.blueprint.id <BLUEPRINT_APP_ID>
user.id <entra-user-objectid>
client.address 10.1.2.80
server.address weatherbot.example.com
server.port 443

중요

이러한 실행 전체 특성들은 자동으로 전파되지 않습니다. 모든 스팬에 gen_ai.conversation.id, microsoft.channel.name, microsoft.session.id를 직접 설정해야 합니다.

스팬 A: invoke_agent(루트)

{
  "traceId": "0102030405060708090a0b0c0d0e0f10",
  "spanId": "1111111111111111",
  "parentSpanId": "",
  "name": "invoke_agent",
  "kind": 1,
  "startTimeUnixNano": "1736175600000000000",
  "endTimeUnixNano":   "1736175601500000000",
  "status": { "code": 1 },
  "attributes": [
    { "key": "gen_ai.operation.name",   "value": { "stringValue": "invoke_agent" } },
    { "key": "gen_ai.execution.type",   "value": { "stringValue": "HumanToAgent" } },
    { "key": "gen_ai.input.messages",   "value": { "stringValue": "[{\"role\":\"user\",\"content\":\"What's the weather in Seattle?\"}]" } },
    { "key": "gen_ai.output.messages",  "value": { "stringValue": "[{\"role\":\"assistant\",\"content\":\"It's 65F and partly cloudy in Seattle.\"}]" } },
    { "key": "user.email",              "value": { "stringValue": "alice@contoso.com" } }
    /* plus all the run-wide attributes listed above */
  ]
}

스팬 B: chat(LLM 호출)

{
  "traceId": "0102030405060708090a0b0c0d0e0f10",
  "spanId": "2222222222222222",
  "parentSpanId": "1111111111111111",
  "name": "chat",
  "kind": 1,
  "startTimeUnixNano": "1736175600200000000",
  "endTimeUnixNano":   "1736175600900000000",
  "status": { "code": 1 },
  "attributes": [
    { "key": "gen_ai.operation.name",      "value": { "stringValue": "chat" } },
    { "key": "gen_ai.request.model",       "value": { "stringValue": "gpt-4o" } },
    { "key": "gen_ai.provider.name",       "value": { "stringValue": "openai" } },
    { "key": "gen_ai.usage.input_tokens",  "value": { "stringValue": "42" } },
    { "key": "gen_ai.usage.output_tokens", "value": { "stringValue": "23" } }
    /* plus all the run-wide attributes */
  ]
}

스팬 C: execute_tool

{
  "traceId": "0102030405060708090a0b0c0d0e0f10",
  "spanId": "3333333333333333",
  "parentSpanId": "1111111111111111",
  "name": "execute_tool",
  "kind": 1,
  "startTimeUnixNano": "1736175600950000000",
  "endTimeUnixNano":   "1736175601200000000",
  "status": { "code": 1 },
  "attributes": [
    { "key": "gen_ai.operation.name",      "value": { "stringValue": "execute_tool" } },
    { "key": "gen_ai.tool.name",           "value": { "stringValue": "GetWeather" } },
    { "key": "gen_ai.tool.type",           "value": { "stringValue": "function" } },
    { "key": "gen_ai.tool.call.id",        "value": { "stringValue": "call-001" } },
    { "key": "gen_ai.tool.call.arguments", "value": { "stringValue": "{\"location\":\"Seattle\"}" } },
    { "key": "gen_ai.tool.call.result",    "value": { "stringValue": "{\"tempF\":65,\"condition\":\"partly cloudy\"}" } }
    /* plus all the run-wide attributes */
  ]
}

스팬 D: output_messages

{
  "traceId": "0102030405060708090a0b0c0d0e0f10",
  "spanId": "4444444444444444",
  "parentSpanId": "1111111111111111",
  "name": "output_messages",
  "kind": 1,
  "startTimeUnixNano": "1736175601400000000",
  "endTimeUnixNano":   "1736175601500000000",
  "status": { "code": 1 },
  "attributes": [
    { "key": "gen_ai.operation.name",  "value": { "stringValue": "output_messages" } },
    { "key": "gen_ai.output.messages", "value": { "stringValue": "[{\"role\":\"assistant\",\"content\":\"It's 65F and partly cloudy in Seattle.\"}]" } }
    /* plus all the run-wide attributes */
  ]
}

원격 분석 전송

OTel SDK 사용

대부분의 파트너는 직접 구현한 HTTP보다는 OTel SDK를 통해 트레이스를 전송합니다. SDK는 배치, 재시도, OTLP/HTTP+JSON 인코딩을 대신 처리해줍니다. 익스포터 엔드포인트를 설정하고 Authorization 헤더를 주입하세요.

쿼리 문자열까지 포함된 경로 URL입니다.

https://agent365.svc.cloud.microsoft/observabilityService/tenants/{tenantId}/otlp/agents/{agentId}/traces?api-version=1

(위임된 경로에는 /observability/.../observabilityService/... 대신 사용하십시오.)

Python

from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter

exporter = OTLPSpanExporter(
    endpoint="https://agent365.svc.cloud.microsoft/observabilityService/tenants/{tenantId}/otlp/agents/{agentId}/traces?api-version=1",
    headers={"Authorization": f"Bearer {token}"},
)

패키지: opentelemetry-exporter-otlp-proto-http.

Node.js / TypeScript

import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";

const exporter = new OTLPTraceExporter({
  url: "https://agent365.svc.cloud.microsoft/observabilityService/tenants/{tenantId}/otlp/agents/{agentId}/traces?api-version=1",
  headers: { Authorization: `Bearer ${token}` },
});

패키지: @opentelemetry/exporter-trace-otlp-http.

.NET

using OpenTelemetry.Exporter;

services.AddOpenTelemetry().WithTracing(b => b
    .AddOtlpExporter(o =>
    {
        o.Endpoint = new Uri("https://agent365.svc.cloud.microsoft/observabilityService/tenants/{tenantId}/otlp/agents/{agentId}/traces?api-version=1");
        o.Headers = $"Authorization=Bearer {token}";
        o.Protocol = OtlpExportProtocol.HttpJson;
    }));

패키지: OpenTelemetry.Exporter.OpenTelemetryProtocol.

수동 HTTP

OTel SDK를 사용할 수 없거나 사용하고 싶지 않다면, OTLP/HTTP+JSON 요청을 직접 생성하여 POST하십시오. 요청 본문 형식은 OpenTelemetryOTLP/HTTP+JSON 사양에 의해 정의됩니다:

{
  "resourceSpans": [{
    "resource":  { "attributes": [ ... ] },          // optional
    "scopeSpans": [{
      "scope":  { "name": "<your-instrumentation>", "version": "1.0.0" },
      "spans":  [ <span>, <span>, ... ]
    }]
  }]
}

<span> 객체에는 필수 필드로 traceId, spanId, name, kind, startTimeUnixNano, endTimeUnixNano, attributes가 있으며, (비-루트 스팬의 경우) parentSpanId도 필요합니다. 인코딩 규칙(문자열로 인코딩된 시간, 16진수 traceId / spanId, 정수형 kind / status.codestringValue로 표현된 모든 특성 값)은 엔드포인트요청 본문 인코딩을 참조하세요.

설정해야 하는 특성의 집합은 메시지 계약에서 정의됩니다. 전체 특성 목록은 특성 참조를 확인하세요. 전달자 토큰이 헤더에 있고 본문이 인라인으로 포함된 전체 동작 예제를 보려면 에이전트 실행 예제를 참조하세요.

실행의 모든 스팬을 하나의 POST 본문에 담아 보낼 수 있습니다(권장 - 하나의 요청, 하나의 트레이스), 또는 여러 번의 POST로 나누어 보낼 수도 있습니다. 서버는 traceId + parentSpanId + gen_ai.conversation.id를 기반으로 실행을 재구성하므로, 각 스팬에는 어느 방식으로 전송하더라도 상관관계를 위해 충분한 정보가 포함되어 있습니다.

메시지 계약

이 섹션에서는 전송할 수 있는 스팬과 각 스팬에 포함해야 하는 특성을 정의합니다. 특성별 전체 사양은 특성 참조에서 확인할 수 있습니다.

작업 유형

보내는 모든 스팬에는 gen_ai.operation.name가 이 네 가지 값 중 하나로 설정되어야 합니다(대소문자 구분 없음). 누락되거나 인식되지 않는 값이 있는 모든 스팬은 조용히 삭제되어 partialSuccess.rejectedSpans에 포함됩니다.

gen_ai.operation.name 의미 가장 많이 검색되는 문제점
invoke_agent 에이전트 호출. 에이전트 실행의 "루트". 해당 실행이 Microsoft Defender 에이전트 활동 뷰 또는 Microsoft 365 관리 센터에 표시되기 위해 반드시 필요합니다. 이 항목이 없으면 원격 분석은 Microsoft Defender 고급 헌팅(CloudAppEvents)에만 기록됩니다.
execute_tool 에이전트가 수행하는 도구/함수 호출. --
chat LLM 추론 호출. 문자 그대로 chat를 사용하고 inference는 사용하지 마세요.
output_messages 최종 출력 메시지. --

스팬 계층 구조와 실행 그룹화

Agent 365는 표준 OTLP 스팬 그래프(traceId, spanId, parentSpanId)와 특성 참조에서 가져온 실행 전체 특성을 기반으로 실행을 재구성합니다.

여섯 가지 규칙:

  1. 항상 모든 비-루트 스팬에 parentSpanId를 설정해야 합니다. 이 정보가 없으면 실행의 트리 구조를 재구성할 수 없습니다.
  2. 실행에 속한 모든 스팬에서 동일한 traceId을 재사용하세요.
  3. 동일한 값으로 모든 스팬에서 gen_ai.conversation.id를 설정하세요. 이는 "해당 실행의 모든 스팬"에 대한 기본 조인 키입니다. 자동으로 전파되지 않습니다.
  4. 동일한 값으로 모든 스팬에서 microsoft.channel.name를 설정하세요. 채널이나 대화를 포괄하지 않는 도구 스팬은 상위 invoke_agent스팬으로부터 해당 정보를 상속받을 수 있습니다. 단, 이는 부모 스팬이 동일한 OTLP 요청 내에 있는 경우에만 가능하므로, 모든 스팬에 직접 해당 정보를 설정해야 합니다.
  5. 논리 세션이 있는 경우 모든 스팬에 microsoft.session.id를 설정하세요.
  6. 자식 에이전트가 별도의 요청으로 처리되는 에이전트 간 통화의 경우, 동일한 gen_ai.conversation.id를 재사용하고 microsoft.a365.caller.agent.* 특성(특성 참조 참고)을 사용하여 호출 에이전트의 컨텍스트를 캡처하십시오.

에이전트 실행 예시의 4스팬 트리가 표준 구조입니다.

일반적인 실행 형태

도형 방출할 스팬 참고
단일 에이전트 챗봇 (도구 없음, LLM 스팬 없음) invoke_agent 하나만 생성 실행 전체 특성과 gen_ai.input.messagesgen_ai.output.messages를 설정하세요. 가능한 가장 작은 요청과 동일합니다.
도구를 가진 에이전트(가장 일반적) invoke_agent 루트 + chat, execute_tool, output_messages 하위 항목 모든 하위 항목은 루트의 traceId를 공유하고 parentSpanId = root.spanId를 설정합니다. 모두 동일한 실행 전체 특성을 공유합니다. 전체 예시를 보려면 에이전트 실행 예시를 참조하세요.
에이전트 간 각 에이전트는 고유한 invoke_agent를 내보냅니다 양쪽 에이전트에서 동일한 gen_ai.conversation.id를 재사용하세요. 대상의 invoke_agent에서 gen_ai.execution.type = "Agent2Agent"microsoft.a365.caller.agent.* 특성(호출하는 에이전트의 appId, 이름, 청사진 appId, 사용자 ID 및 이메일)을 설정하십시오. 호출 에이전트에 Entra 등록이 없는 경우, 대신 microsoft.a365.caller.agent.platform.idgen_ai.caller.agent.type을 사용하세요.

온보딩 체크리스트

운영 환경에 배포하기 전에 이 체크리스트를 점검하십시오.

카테고리 수표
인증 Entra 앱(또는 청사진) 등록 완료 및 토큰 발행 가능
인증 귀하의 앱에 Agent365.Observability.OtelWrite 권한이 부여되었습니다(S2S는 앱 역할, 위임은 범위).
인증 각 에이전트는 URL 내 {agentId} 위치에 고유한 Entra appId를 갖습니다. 청사진 파생 ID에서는 appId는 청사진 appId가 아니라 에이전트 ID appId입니다. 에이전트에 Entra 등록이 없는 경우, 피킹 값을 참조하세요.
인증 테넌트 관리자가 Agent365.Observability.OtelWrite에 대한 동의를 부여했습니다. 동의가 없으면 토큰이 역할/범위 없이 발급되며, 요청은 403로 거부됩니다.
라이선싱 고객 테넌트 내의 사용자 중 최소 한 명에게 Microsoft 365 E7 또는 Microsoft Agent 365 라이선스가 할당되어 있어야 합니다(단순히 SKU가 테넌트에 존재하는 것만으로는 충분하지 않고, 실제로 할당되어야 합니다). 라이선스가 할당되지 않으면 수집이 알림 없이 중단됩니다. 필수 조건을 참조하세요.
스팬 모든 스팬은 실행 전체의 필수 요소(스팬 계층 구조 및 실행 그룹화)를 설정합니다.
스팬 invoke_agent 스팬은 gen_ai.input.messagesgen_ai.output.messages를 설정합니다.
스팬 execute_tool스팬은 gen_ai.tool.name, gen_ai.tool.type, gen_ai.tool.call.id, gen_ai.tool.call.arguments, gen_ai.tool.call.result를 설정합니다.
스팬 chat 스팬은 gen_ai.request.modelgen_ai.provider.name로 설정하며, 이상적으로는 gen_ai.usage.input_tokens / gen_ai.usage.output_tokens(문자열로 인코딩됨)도 설정합니다.
스팬 모든 비-루트 스팬은 parentSpanId를 설정되며, 실행 내의 모든 스팬은 동일한 traceId를 공유합니다.
페이로드 요청 본문은 1MB 이하입니다.
확인 모든 응답에서 partialSuccess를 구문 분석하고 거절을 로그로 남깁니다.
확인 첫 실행에 대해 수집 확인에서 검증 흐름을 실행했습니다.

다음 단계