Important
You need to be part of the Frontier preview program to get early access to Microsoft Agent 365. Frontier connects you directly with Microsoft’s latest AI innovations. Frontier previews are subject to the existing preview terms of your customer agreements. As these features are still in development, their availability and capabilities may change over time.
To participate in the Agent 365 ecosystem, add Agent 365 Observability capabilities to your agent. Agent 365 Observability builds on OpenTelemetry (OTel) and provides a unified framework for capturing telemetry consistently and securely across all agent platforms. By implementing this required component, you enable IT admins to monitor your agent's activity in Microsoft admin center and allow security teams to use Defender and Purview for compliance and threat detection.
Key benefits
- End-to-end visibility: Capture comprehensive telemetry for every agent invocation, including sessions, tool calls, and exceptions, giving you full traceability across platforms.
- Security and compliance enablement: Feed unified audit logs into Defender and Purview, enabling advanced security scenarios and compliance reporting for your agent.
- Cross-platform flexibility: Build on OTel standards and support diverse runtimes and platforms like Copilot Studio, Foundry, and future agent frameworks.
- Operational efficiency for admins: Provide centralized observability in Microsoft 365 admin center, reducing troubleshooting time and improving governance with role-based access controls for IT teams managing your agent.
Installation
Use these commands to install the observability modules for the languages supported by Agent 365.
Install the core observability and runtime packages. All agents that use Agent 365 Observability need these packages.
pip install microsoft-agents-a365-observability-core
pip install microsoft-agents-a365-runtime
If your agent uses the Microsoft Agents Hosting package, install the hosting integration package. It provides middleware that automatically populates baggage and scopes from the TurnContext, and includes token caching for the observability exporter.
pip install microsoft-agents-a365-observability-hosting
If your agent uses one of the supported AI frameworks, install the corresponding auto-instrumentation extension to automatically capture telemetry without manual instrumentation code. For configuration details, see Auto-instrumentation.
# For Semantic Kernel
pip install microsoft-agents-a365-observability-extensions-semantic-kernel
# For OpenAI Agents SDK
pip install microsoft-agents-a365-observability-extensions-openai
# For Microsoft Agent Framework
pip install microsoft-agents-a365-observability-extensions-agent-framework
# For LangChain
pip install microsoft-agents-a365-observability-extensions-langchain
Install the core observability and runtime packages. All agents that use Agent 365 Observability need these packages.
npm install @microsoft/agents-a365-observability
npm install @microsoft/agents-a365-runtime
If your agent uses the @microsoft/agents-hosting package, install the hosting integration package. It provides middleware that automatically populates baggage and scopes from the TurnContext, and includes token caching for the observability exporter.
npm install @microsoft/agents-a365-observability-hosting
If your agent uses one of the supported AI frameworks, install the corresponding auto-instrumentation extension to automatically capture telemetry without manual instrumentation code. For configuration details, see Auto-instrumentation.
// For OpenAI Agents SDK
npm install @microsoft/agents-a365-observability-extensions-openai
// For LangChain
npm install @microsoft/agents-a365-observability-extensions-langchain
Install the core observability and runtime package. All agents that use Agent 365 Observability need this package.
dotnet add package Microsoft.Agents.A365.Observability.Runtime
If your agent uses the Microsoft.Agents.A365.Observability.Hosting NuGet package, install the hosting integration package. It provides middleware that automatically populates baggage from the TurnContext, and includes token caching for the observability exporter.
dotnet add package Microsoft.Agents.A365.Observability.Hosting
If your agent uses one of the supported AI frameworks, install the corresponding auto-instrumentation extension to automatically capture telemetry without manual instrumentation code. For configuration details, see Auto-instrumentation.
// For Semantic Kernel
dotnet add package Microsoft.Agents.A365.Observability.Extensions.SemanticKernel
// For OpenAI
dotnet add package Microsoft.Agents.A365.Observability.Extensions.OpenAI
// For Agent Framework
dotnet add package Microsoft.Agents.A365.Observability.Extensions.AgentFramework
Configuration
Use the following settings to enable and customize Agent 365 Observability for your agent.
Set the ENABLE_A365_OBSERVABILITY_EXPORTER environment variable to true for observability. This setting exports logs to the service and requires a token_resolver to be provided. Otherwise, the console exporter is used.
from microsoft_agents_a365.observability.core import configure
def token_resolver(agent_id: str, tenant_id: str) -> str | None:
# Implement secure token retrieval here
return "Bearer <token>"
configure(
service_name="my-agent-service",
service_namespace="my.namespace",
token_resolver=token_resolver,
)
The token resolver is excluded from logging to the console.
You can customize the exporter behavior by passing an Agent365ExporterOptions instance to exporter_options. When exporter_options is provided, it takes precedence over the token_resolver and cluster_category parameters.
from microsoft_agents_a365.observability.core import configure, Agent365ExporterOptions
configure(
service_name="my-agent-service",
service_namespace="my.namespace",
exporter_options=Agent365ExporterOptions(
cluster_category="prod",
token_resolver=token_resolver,
),
suppress_invoke_agent_input=True,
)
The following table describes the optional parameters for configure().
| Parameter |
Description |
Default |
logger_name |
Name of the Python logger used for debugging and console log output. |
microsoft_agents_a365.observability.core |
exporter_options |
An Agent365ExporterOptions instance that configures the token resolver and cluster category together. |
None |
suppress_invoke_agent_input |
When True, suppresses input messages on InvokeAgent spans. |
False |
The following table describes the optional properties for Agent365ExporterOptions.
| Property |
Description |
Default |
use_s2s_endpoint |
When True, uses the service-to-service endpoint path. |
False |
max_queue_size |
Maximum queue size for the batch processor. |
2048 |
scheduled_delay_ms |
Delay in milliseconds between export batches. |
5000 |
exporter_timeout_ms |
Timeout in milliseconds for the export operation. |
30000 |
max_export_batch_size |
Maximum batch size for export operations. |
512 |
Set the ENABLE_A365_OBSERVABILITY_EXPORTER environment variable to true for observability. This setting exports logs to the service and requires a token resolver to be provided. Otherwise, the console exporter is used.
import { ObservabilityManager } from '@microsoft/agents-a365-observability';
// Define a token resolver to authenticate with the observability service for exporting logs
const tokenResolver = (agentId, tenantId) => {
// Your token resolution logic here
return "your-token";
};
// Advanced configuration with builder pattern
const builder = ObservabilityManager.configure(builder =>
builder
.withService('my-agent-service', '1.0.0')
.withTokenResolver((agentId, tenantId) => {
return tokenResolver(agentId, tenantId);
})
);
builder.start();
As an alternative to environment variables, you can configure observability programmatically by using a configuration provider through withConfigurationProvider method. If you also use individual builder methods (such as withExporterOptions or withClusterCategory), the individual builder methods take precedence over values from the configuration provider.
import { ObservabilityManager } from '@microsoft/agents-a365-observability';
import { ObservabilityConfiguration } from '@microsoft/agents-a365-observability';
const configProvider = new ObservabilityConfiguration({
isObservabilityExporterEnabled: () => true,
// Set log levels as pipe-separated values (for example, 'info|warn|error')
observabilityLogLevel: () => 'info|warn|error',
});
const builder = ObservabilityManager.configure(builder =>
builder
.withService('my-agent-service', '1.0.0')
.withConfigurationProvider(configProvider)
.withTokenResolver((agentId, tenantId) => {
return tokenResolver(agentId, tenantId);
})
);
builder.start();
You can customize the exporter behavior by passing an Agent365ExporterOptions instance to withExporterOptions. This option allows you to control batching, timeouts, and the route mode.
import {
ObservabilityManager,
Agent365ExporterOptions,
} from '@microsoft/agents-a365-observability';
import { ClusterCategory } from '@microsoft/agents-a365-runtime';
const exporterOptions = new Agent365ExporterOptions();
exporterOptions.maxQueueSize = 10;
const builder = ObservabilityManager.configure(builder =>
builder
.withService('my-agent-service', '1.0.0')
.withClusterCategory(ClusterCategory.prod)
.withExporterOptions(exporterOptions)
.withTokenResolver(tokenResolver)
);
builder.start();
The following table describes the optional properties for Agent365ExporterOptions.
| Property |
Description |
Default |
useS2SEndpoint |
When true, uses the service-to-service endpoint path. |
false |
maxQueueSize |
Maximum queue size for the batch processor. |
2048 |
scheduledDelayMilliseconds |
Delay in milliseconds between export batches. |
5000 |
exporterTimeoutMilliseconds |
Timeout in milliseconds for the entire export operation. |
90000 |
httpRequestTimeoutMilliseconds |
Timeout in milliseconds for each individual HTTP request to the backend. |
30000 |
maxExportBatchSize |
Maximum batch size for export operations. |
512 |
You can also provide a custom logger that implements the ILogger interface (info, warn, error, event). Pass it to the builder by using withCustomLogger.
const builder = ObservabilityManager.configure(builder =>
builder
.withService('my-agent-service', '1.0.0')
.withCustomLogger({
info: (message, ...args) => { /* your logging logic */ },
warn: (message, ...args) => { /* your logging logic */ },
error: (message, ...args) => { /* your logging logic */ },
event: (eventName, success, durationMs, message, details) => { /* your logging logic */ }
})
.withTokenResolver((agentId, tenantId) => {
return tokenResolver(agentId, tenantId);
})
);
builder.start();
Set EnableAgent365Exporter to true in appsettings.json.
In Program.cs, add Agent365ExporterOptions to the service collection. This change configures the delegate the trace exporter uses to retrieve the token.
Add observability related dependencies by using AddA365Tracing().
using Microsoft.Agents.A365.Observability.Runtime;
using Microsoft.Agents.A365.Observability.Runtime.Tracing.Exporters;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton(sp =>
{
return new Agent365ExporterOptions
{
TokenResolver = async (agentId, tenantId) =>
{
// It's recommended to implement caching in your token provider for performance.
var token = await tokenProvider.GetObservabilityTokenAsync(agentId, tenantId);
return token;
}
};
});
builder.AddA365Tracing();
You can customize the exporter behavior by passing a configure delegate and exporter type to AddA365Tracing().
builder.AddA365Tracing(
configure: tracingBuilder =>
{
// Use the builder to add extensions (e.g., WithSemanticKernel(), WithOpenAI())
},
agent365ExporterType: Agent365ExporterType.Agent365ExporterAsync
);
The following table describes the optional properties for Agent365ExporterOptions.
| Property |
Description |
Default |
UseS2SEndpoint |
When true, uses the service-to-service endpoint path. |
false |
MaxQueueSize |
Maximum queue size for the batch processor. |
2048 |
ScheduledDelayMilliseconds |
Delay in milliseconds between export batches. |
5000 |
ExporterTimeoutMilliseconds |
Timeout in milliseconds for the export operation. |
30000 |
MaxExportBatchSize |
Maximum batch size for export operations. |
512 |
Baggage attributes
Use BaggageBuilder to set contextual information that flows through all spans in a request.
The SDK implements a SpanProcessor that copies all nonempty baggage entries to newly started spans without overwriting existing attributes.
from microsoft_agents_a365.observability.core import BaggageBuilder
with (
BaggageBuilder()
.tenant_id("tenant-123")
.agent_id("agent-456")
.conversation_id("conv-789")
.build()
):
# Any spans started in this context will receive these as attributes
pass
To auto-populate the BaggageBuilder from the TurnContext, use the populate helper in the microsoft-agents-a365-observability-hosting package. This helper automatically extracts caller, agent, tenant, channel, and conversation details from the activity.
from microsoft_agents.hosting.core.turn_context import TurnContext
from microsoft_agents_a365.observability.core import BaggageBuilder
from microsoft_agents_a365.observability.hosting.scope_helpers.populate_baggage import populate
builder = BaggageBuilder()
populate(builder, turn_context)
with builder.build():
# Baggage is auto-populated from the TurnContext activity
pass
import { BaggageBuilder } from '@microsoft/agents-a365-observability';
// Create and apply baggage context
const baggageScope = new BaggageBuilder()
// Core identifiers
.tenantId('tenant-123')
.agentId('agent-456')
.conversationId('conv-789')
.build();
// Execute operations within the baggage context
baggageScope.run(() => {
// All spans created within this context will inherit the baggage values
// Invoke another agent
const agentScope = InvokeAgentScope.start(request, scopeDetails, agentDetails);
// ... agent logic
// Execute tools
const toolScope = ExecuteToolScope.start(request, toolDetails, agentDetails);
// ... tool logic
});
To auto-populate the BaggageBuilder from the TurnContext, use the fromTurnContext helper in the @microsoft/agents-a365-observability-hosting package. This helper automatically extracts caller, agent, tenant, channel, and conversation details from the activity.
import { BaggageBuilder } from '@microsoft/agents-a365-observability';
import { BaggageBuilderUtils } from '@microsoft/agents-a365-observability-hosting';
const baggageScope = BaggageBuilderUtils.fromTurnContext(new BaggageBuilder(), context)
.invokeAgentServer(context.activity.serviceUrl, 3978)
.build();
await baggageScope.run(async () => {
// Baggage is auto-populated from the TurnContext activity
});
using Microsoft.Agents.A365.Observability.Runtime.Common;
using var baggageScope = new BaggageBuilder()
.TenantId("tenant-123")
.AgentId("agent-456")
.ConversationId("conv-789")
.Build();
// Any spans started in this context will receive them as attributes.
To auto-populate the BaggageBuilder from the ITurnContext, use the FromTurnContext extension method in the Microsoft.Agents.A365.Observability.Hosting package. This method automatically extracts caller, agent, tenant, channel, and conversation details from the activity.
using Microsoft.Agents.A365.Observability.Runtime.Common;
using Microsoft.Agents.A365.Observability.Hosting.Extensions;
using var baggageScope = new BaggageBuilder()
.FromTurnContext(turnContext)
.Build();
Token resolver
When you use the Agent 365 exporter, you must provide a token resolver function that returns an authentication token.
When you use the Agent 365 Observability SDK with the Agent Hosting framework, you can generate tokens by using the TurnContext from agent activities.
from microsoft_agents.activity import load_configuration_from_env
from microsoft_agents.authentication.msal import MsalConnectionManager
from microsoft_agents.hosting.aiohttp import CloudAdapter
from microsoft_agents.hosting.core import (
AgentApplication,
Authorization,
MemoryStorage,
TurnContext,
TurnState,
)
from microsoft_agents_a365.runtime import (
get_observability_authentication_scope,
)
agents_sdk_config = load_configuration_from_env(environ)
STORAGE = MemoryStorage()
CONNECTION_MANAGER = MsalConnectionManager(**agents_sdk_config)
ADAPTER = CloudAdapter(connection_manager=CONNECTION_MANAGER)
ADAPTER.use(TranscriptLoggerMiddleware(ConsoleTranscriptLogger()))
AUTHORIZATION = Authorization(STORAGE, CONNECTION_MANAGER, **agents_sdk_config)
AGENT_APP = AgentApplication[TurnState](
storage=STORAGE, adapter=ADAPTER, authorization=AUTHORIZATION, **agents_sdk_config
)
@AGENT_APP.activity("message", auth_handlers=["AGENTIC"])
async def on_message(context: TurnContext, _state: TurnState):
aau_auth_token = await AGENT_APP.auth.exchange_token(
context,
scopes=get_observability_authentication_scope(),
auth_handler_id="AGENTIC",
)
# cache this auth token and return via token resolver
For the digital worker scenario, if your agent uses the Microsoft Agent 365 Observability Hosting Library package, use AgenticTokenCache to handle token caching automatically. Register the token once per agent and tenant during an activity handler, and pass cache.get_observability_token as the token_resolver in your observability configuration.
from microsoft_agents_a365.observability.core import configure
from microsoft_agents_a365.observability.hosting.token_cache_helpers import (
AgenticTokenCache,
AgenticTokenStruct,
)
from microsoft_agents_a365.runtime import get_observability_authentication_scope
# Create a shared cache instance
token_cache = AgenticTokenCache()
# Use the cache as your token resolver in configure()
configure(
service_name="my-agent-service",
service_namespace="my.namespace",
token_resolver=token_cache.get_observability_token,
)
@AGENT_APP.activity("message", auth_handlers=["AGENTIC"])
async def on_message(context: TurnContext, _state: TurnState):
token_cache.register_observability(
agent_id="agent-456",
tenant_id="tenant-123",
token_generator=AgenticTokenStruct(
authorization=AGENT_APP.auth,
turn_context=context,
),
observability_scopes=get_observability_authentication_scope(),
)
import {
TurnState,
AgentApplication,
MemoryStorage,
TurnContext,
} from '@microsoft/agents-hosting';
import { ActivityTypes } from '@microsoft/agents-activity';
import { getObservabilityAuthenticationScope } from '@microsoft/agents-a365-runtime';
interface ConversationState {
count: number;
}
type ApplicationTurnState = TurnState<ConversationState>;
const storage = new MemoryStorage();
export const agentApplication = new AgentApplication<ApplicationTurnState>({
authorization: {
agentic: {}, // We have the type and scopes set in the .env file
},
storage,
});
agentApplication.onActivity(
ActivityTypes.Message,
async (context: TurnContext, state: ApplicationTurnState) => {
const aauAuthToken = await agentApplication.authorization.exchangeToken(context, 'agentic', {
scopes: getObservabilityAuthenticationScope()
});
// cache this auth token and return via token resolver
}
);
For the digital worker scenario, if your agent uses the @microsoft/agents-a365-observability-hosting package, use AgenticTokenCacheInstance to handle token caching automatically. Call RefreshObservabilityToken once per agent and tenant during an activity handler, and pass AgenticTokenCacheInstance.getObservabilityToken as the tokenResolver in your observability configuration.
import { ObservabilityManager } from '@microsoft/agents-a365-observability';
import { AgenticTokenCacheInstance } from '@microsoft/agents-a365-observability-hosting';
import { getObservabilityAuthenticationScope } from '@microsoft/agents-a365-runtime';
// Use the cache as your token resolver in configure()
const builder = ObservabilityManager.configure(builder =>
builder
.withService('my-agent-service', '1.0.0')
.withTokenResolver((agentId, tenantId) =>
AgenticTokenCacheInstance.getObservabilityToken(agentId, tenantId)
)
);
builder.start();
agentApplication.onActivity(
ActivityTypes.Message,
async (context: TurnContext, state: ApplicationTurnState) => {
const agentId = context.activity.recipient?.agenticAppId || '';
const tenantId = context.activity.recipient?.tenantId || '';
await AgenticTokenCacheInstance.RefreshObservabilityToken(
agentId,
tenantId,
context,
agentApplication.authorization,
getObservabilityAuthenticationScope()
);
}
);
Provide a token resolver by registering Agent365ExporterOptions with a TokenResolver Delegate. The delegate receives the agentId and tenantId and returns an authentication token.
using Microsoft.Agents.A365.Observability.Runtime.Tracing.Exporters;
builder.Services.AddSingleton(sp =>
{
return new Agent365ExporterOptions
{
TokenResolver = async (agentId, tenantId) =>
{
// Implement your token retrieval logic here
return await GetTokenAsync(agentId, tenantId);
}
};
});
For the digital worker scenario, if your agent uses the Microsoft.Agents.A365.Observability.Hosting NuGet package, use the AddAgenticTracingExporter() method to handle token caching automatically through dependency injection.
using Microsoft.Agents.A365.Observability.Hosting;
builder.Services.AddAgenticTracingExporter();
In the agent application, register the token.
using Microsoft.Agents.Builder;
using Microsoft.Agents.Builder.App.UserAuth;
using Microsoft.Extensions.Logging;
using Microsoft.Agents.A365.Observability.Hosting.Caching;
using Microsoft.Agents.A365.Observability.Runtime.Common;
using System;
using System.Threading.Tasks;
public class MyAgent : AgentApplication
{
private readonly IExporterTokenCache<AgenticTokenStruct> _agentTokenCache;
private readonly ILogger<MyAgent> _logger;
public MyAgent(AgentApplicationOptions options, IExporterTokenCache<AgenticTokenStruct> agentTokenCache, ILogger<MyAgent> logger)
: base(options)
{
_agentTokenCache = agentTokenCache ?? throw new ArgumentNullException(nameof(agentTokenCache));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
protected async Task MessageActivityAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken cancellationToken)
{
using var baggageScope = new BaggageBuilder()
.TenantId(turnContext.Activity.Recipient.TenantId)
.AgentId(turnContext.Activity.Recipient.AgenticAppId)
.Build();
try
{
_agentTokenCache.RegisterObservability(
turnContext.Activity.Recipient.AgenticAppId,
turnContext.Activity.Recipient.TenantId,
new AgenticTokenStruct(
userAuthorization: UserAuthorization,
turnContext: turnContext,
authHandlerName: "AGENTIC"
),
EnvironmentUtils.GetObservabilityAuthenticationScope()
);
}
catch (Exception ex)
{
_logger.LogWarning($"Error registering for observability: {ex.Message}");
}
}
}
Auto-instrumentation
Auto-instrumentation automatically listens to agentic frameworks (SDKs) existing telemetry signals for traces and forwards them to Agent 365 observability service. This feature eliminates the need for developers to write monitoring code manually, simplifies setup, and ensures consistent performance tracking.
Multiple SDKs and platforms support auto-instrumentation:
Note
Support for auto-instrumentation varies by platform and SDK implementation.
Semantic Kernel
Auto instrumentation requires the use of baggage builder. Set agent ID and tenant ID by using BaggageBuilder.
Install the package.
pip install microsoft-agents-a365-observability-extensions-semantic-kernel
Configure observability.
from microsoft_agents_a365.observability.core import configure
from microsoft_agents_a365.observability.extensions.semantickernel.trace_instrumentor import SemanticKernelInstrumentor
# Configure observability
configure(
service_name="my-semantic-kernel-agent",
service_namespace="ai.agents"
)
# Enable auto-instrumentation
instrumentor = SemanticKernelInstrumentor()
instrumentor.instrument()
# Your Semantic Kernel code is now automatically traced
Semantic Kernel isn't supported with JavaScript.
Add dependencies to the service collection.
using Microsoft.Agents.A365.Observability.Extensions.SemanticKernel;
builder.AddA365Tracing(configure: config => config.WithSemanticKernel());
Set AgentId and TenantId by using BaggageBuilder. Ensure that the ID you use when creating a ChatCompletionAgent matches the agent ID you pass to BaggageBuilder.
using Microsoft.Agents.A365.Observability.Extensions.SemanticKernel;
using Microsoft.Agents.A365.Observability.Runtime.Common;
public class MyAgent
{
public async Task<AgentResponse> ProcessUserRequest(string userInput)
{
using var baggageScope = new BaggageBuilder()
.AgentId(<your-agent-id>) // NOTE: This will be the agent ID with which the TokenResolver delegate is invoked.
.TenantId(<your-tenant-id>) // NOTE: This will be the tenant ID with which the TokenResolver delegate is invoked.
.Build();
var chatCompletionAgent = new ChatCompletionAgent
{
// NOTE: This will be the agent ID with which the TokenResolver delegate is invoked. Should match above.
Id = <your-agent-id>,
...
};
}
}
OpenAI
Auto instrumentation requires the use of baggage builder. Set agent ID and tenant ID by using BaggageBuilder.
Install the package.
pip install microsoft-agents-a365-observability-extensions-openai
Configure observability.
from microsoft_agents_a365.observability.core import configure
from microsoft_agents_a365.observability.extensions.openai import OpenAIAgentsTraceInstrumentor
# Configure observability
configure(
service_name="my-openai-agent",
service_namespace="ai.agents"
)
# Enable auto-instrumentation
instrumentor = OpenAIAgentsTraceInstrumentor()
instrumentor.instrument()
# Your OpenAI Agents code is now automatically traced
Install the package.
npm install @microsoft/agents-a365-observability-extensions-openai
Configure observability.
import { ObservabilityManager } from '@microsoft/agents-a365-observability';
import { OpenAIAgentsTraceInstrumentor } from '@microsoft/agents-a365-observability-extensions-openai';
// Configure observability first
const sdk = ObservabilityManager.configure((builder) =>
builder
.withService('My Agent Service', '1.0.0')
);
// Create and enable the instrumentor
const instrumentor = new OpenAIAgentsTraceInstrumentor({
enabled: true,
tracerName: 'openai-agents-tracer',
tracerVersion: '1.0.0'
});
sdk.start();
instrumentor.enable();
Add dependencies to the service collection.
using Microsoft.Agents.A365.Observability.Extensions.OpenAI;
builder.AddA365Tracing(configure: config => config.WithOpenAI());
Set AgentId and TenantId by using BaggageBuilder. For tool calls, start a trace by using Trace() on a ChatToolCall instance.
using Microsoft.Agents.A365.Observability.Extensions.OpenAI;
using Microsoft.Agents.A365.Observability.Runtime.Common;
public class MyAgent
{
public async Task<AgentResponse> ProcessUserRequest(string userInput)
{
using var baggageScope = new BaggageBuilder()
.AgentId(<your-agent-id>) // NOTE: This will be the agent ID with which the TokenResolver delegate is invoked.
.TenantId(<your-tenant-id>) // NOTE: This will be the tenant ID with which the TokenResolver delegate is invoked.
.Build();
// NOTE: This will be the agent and tenant ID with which the TokenResolver delegate will be invoked.
using var scope = chatToolCall.Trace(agentId: <your-agent-id>, <your-tenant-id>);
}
}
Agent Framework
Auto instrumentation requires the use of baggage builder. Set agent ID and tenant ID by using BaggageBuilder.
Install the package.
pip install microsoft-agents-a365-observability-extensions-agent-framework
Configure observability.
from microsoft_agents_a365.observability.core import configure
from microsoft_agents_a365.observability.extensions.agentframework import (
AgentFrameworkInstrumentor,
)
# Configure observability
configure(
service_name="AgentFrameworkTracingWithAzureOpenAI",
service_namespace="AgentFrameworkTesting",
)
# Enable auto-instrumentation
AgentFrameworkInstrumentor().instrument()
Agent Framework isn't supported with JavaScript.
Add dependencies to the service collection.
using Microsoft.Agents.A365.Observability.Extensions.AgentFramework;
builder.AddA365Tracing(configure: config => config.WithAgentFramework());
Set AgentId and TenantId by using BaggageBuilder.
using Microsoft.Agents.A365.Observability.Runtime.Common;
public class MyAgent : AgentApplication
{
protected async Task MessageActivityAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken cancellationToken)
{
using var baggageScope = new BaggageBuilder()
.AgentId(<your-agent-id>) // NOTE: This will be the agent ID with which the TokenResolver delegate is invoked.
.TenantId(<your-tenant-id>) // NOTE: This will be the tenant ID with which the TokenResolver delegate is invoked.
.Build();
}
}
LangChain Framework
Auto-instrumentation requires the use of baggage builder. Set agent ID and tenant ID by using BaggageBuilder.
Install the package.
pip install microsoft-agents-a365-observability-extensions-langchain
Configure observability.
from microsoft_agents_a365.observability.core.config import configure
from microsoft_agents_a365.observability.extensions.langchain import CustomLangChainInstrumentor
# Configure observability
configure(
service_name="my-langchain-agent",
service_namespace="ai.agents"
)
# Enable auto-instrumentation
CustomLangChainInstrumentor()
# Your LangChain code is now automatically traced
Install the package.
npm install @microsoft/agents-a365-observability-extensions-langchain
Configure observability.
import { ObservabilityManager } from '@microsoft/agents-a365-observability';
import { LangChainTraceInstrumentor } from '@microsoft/agents-a365-observability-extensions-langchain';
import * as LangChainCallbacks from '@langchain/core/callbacks/manager';
// Configure observability first
const sdk = ObservabilityManager.configure((builder) =>
builder
.withService('My Agent Service', '1.0.0')
);
sdk.start();
// Enable LangChain auto-instrumentation
LangChainTraceInstrumentor.instrument(LangChainCallbacks);
// Your LangChain code is now automatically traced
LangChain isn't supported with .NET.
Manual Instrumentation
Use Agent 365 observability SDK to understand the internal working of the agent.
The SDK provides scopes that you can start: InvokeAgentScope, ExecuteToolScope, InferenceScope, and OutputScope.
Agent invocation
Use this scope at the start of your agent process. By using the invoke agent scope, you can capture properties like the current agent being invoked, agent user data, and more.
from microsoft_agents_a365.observability.core import (
InvokeAgentScope,
InvokeAgentScopeDetails,
AgentDetails,
CallerDetails,
UserDetails,
Channel,
Request,
ServiceEndpoint,
)
agent_details = AgentDetails(
agent_id="agent-456",
agent_name="My Agent",
agent_description="An AI agent powered by Azure OpenAI",
agentic_user_id="auid-123",
agentic_user_email="agent@contoso.com",
agent_blueprint_id="blueprint-789",
tenant_id="tenant-123",
)
scope_details = InvokeAgentScopeDetails(
endpoint=ServiceEndpoint(hostname="myagent.contoso.com", port=443),
)
request = Request(
content="User asks a question",
session_id="session-42",
conversation_id="conv-xyz",
channel=Channel(name="msteams"),
)
caller_details = CallerDetails(
user_details=UserDetails(
user_id="user-123",
user_email="jane.doe@contoso.com",
user_name="Jane Doe",
),
)
with InvokeAgentScope.start(request, scope_details, agent_details, caller_details):
# Perform agent invocation logic
response = call_agent(...)
import {
InvokeAgentScope,
InvokeAgentScopeDetails,
AgentDetails,
CallerDetails,
UserDetails,
Channel,
Request,
ServiceEndpoint,
} from '@microsoft/agents-a365-observability';
// Use the same agentDetails instance across all scopes in a request
const agentDetails: AgentDetails = {
agentId: 'agent-456',
agentName: 'Email Assistant',
agentDescription: 'An AI agent powered by Azure OpenAI',
agentAuid: 'auid-123',
agentEmail: 'agent@contoso.com',
agentBlueprintId: 'blueprint-789',
tenantId: 'tenant-123',
};
const scopeDetails: InvokeAgentScopeDetails = {
endpoint: { host: 'myagent.contoso.com', port: 443 } as ServiceEndpoint,
};
// Use the same request instance across all scopes in a request
const request: Request = {
content: 'Please help me organize my emails',
sessionId: 'session-42',
conversationId: 'conv-xyz',
channel: { name: 'msteams' } as Channel,
};
// Optional: Caller details (human user, or agent-to-agent)
const callerDetails: CallerDetails = {
userDetails: {
userId: 'user-123',
userEmail: 'jane.doe@contoso.com',
userName: 'Jane Doe',
} as UserDetails,
};
const scope = InvokeAgentScope.start(request, scopeDetails, agentDetails, callerDetails);
try {
await scope.withActiveSpanAsync(async () => {
// Record input messages
scope.recordInputMessages(['Please help me organize my emails', 'Focus on urgent items']);
// Your agent invocation logic here
const response = await invokeAgent(request.content);
// Record output messages
scope.recordOutputMessages(['I found 15 urgent emails', 'Here is your organized inbox']);
});
} catch (error) {
scope.recordError(error as Error);
throw error;
} finally {
scope.dispose();
}
If your agent uses the @microsoft/agents-a365-observability-hosting package, you can use ScopeUtils.populateInvokeAgentScopeFromTurnContext to create the scope with agent details, caller details, and channel information automatically derived from the TurnContext.
import { InvokeAgentScopeDetails, AgentDetails, ServiceEndpoint } from '@microsoft/agents-a365-observability';
import { ScopeUtils } from '@microsoft/agents-a365-observability-hosting';
const agentDetails: AgentDetails = { agentId: 'agent-456' };
const scopeDetails: InvokeAgentScopeDetails = {
endpoint: { host: 'myagent.contoso.com', port: 443 } as ServiceEndpoint,
};
const scope = ScopeUtils.populateInvokeAgentScopeFromTurnContext(
agentDetails,
scopeDetails,
context, // TurnContext
authToken // authentication token string
);
try {
await scope.withActiveSpanAsync(async () => {
// Agent details, caller details, and channel are auto-populated from context
const response = await invokeAgent(context.activity.text);
scope.recordOutputMessages([response]);
});
} finally {
scope.dispose();
}
using System;
using System.Threading.Tasks;
using Microsoft.Agents.A365.Observability.Runtime.Tracing.Contracts;
using Microsoft.Agents.A365.Observability.Runtime.Tracing.Scopes;
public class MyAgent
{
public async Task<AgentResponse> ProcessUserRequest(string userInput)
{
var agentDetails = new AgentDetails(
agentId: "agent-456",
agentName: "MyAgent",
agentDescription: "Handles user requests.",
agenticUserId: "auid-123",
agenticUserEmail: "agent@contoso.com",
agentBlueprintId: "blueprint-789",
tenantId: "tenant-123"
);
var scopeDetails = new InvokeAgentScopeDetails(
endpoint: new Uri("https://myagent.contoso.com")
);
var request = new Request(
content: userInput,
sessionId: "session-abc",
channel: new Channel("msteams"),
conversationId: "conv-xyz"
);
var callerDetails = new CallerDetails(
userDetails: new UserDetails(
userId: "user-123",
userEmail: "jane.doe@contoso.com",
userName: "Jane Doe"
)
);
// Start the scope
using var scope = InvokeAgentScope.Start(
request: request,
scopeDetails: scopeDetails,
agentDetails: agentDetails,
callerDetails: callerDetails
);
// Record input messages
scope.RecordInputMessages(new[] { userInput });
// ... your agent logic here ...
var output = $"Processed: {userInput}";
scope.RecordOutputMessages(new[] { output });
return new AgentResponse { Content = output };
}
}
public class AgentResponse
{
public string Content { get; set; }
}
The following examples show how to add observability tracking to your agent's tool execution. This tracking captures telemetry for monitoring and auditing purposes.
from microsoft_agents_a365.observability.core import (
ExecuteToolScope,
ToolCallDetails,
Request,
ServiceEndpoint,
)
# Use the same agent_details and request instances from the InvokeAgentScope example above
tool_details = ToolCallDetails(
tool_name="summarize",
tool_type="function",
tool_call_id="tc-001",
arguments="{'text': '...'}",
description="Summarize provided text",
endpoint=ServiceEndpoint(hostname="tools.contoso.com", port=8080),
)
with ExecuteToolScope.start(request, tool_details, agent_details) as scope:
result = run_tool(tool_details)
scope.record_response(result)
import { ExecuteToolScope, ToolCallDetails } from '@microsoft/agents-a365-observability';
// Use the same agentDetails and request instances from the InvokeAgentScope example above
const toolDetails: ToolCallDetails = {
toolName: 'email-search',
arguments: JSON.stringify({ query: 'from:boss@company.com', limit: 10 }),
toolCallId: 'tool-call-456',
description: 'Search emails by criteria',
toolType: 'function',
endpoint: {
host: 'tools.contoso.com',
port: 8080, // Will be recorded since not 443
protocol: 'https'
},
};
const scope = ExecuteToolScope.start(request, toolDetails, agentDetails);
try {
return await scope.withActiveSpanAsync(async () => {
// Execute the tool
const result = await searchEmails(toolDetails.arguments);
// Record the tool execution result
scope.recordResponse(result);
return result;
});
} catch (error) {
scope.recordError(error as Error);
throw error;
} finally {
scope.dispose();
}
If your agent uses the @microsoft/agents-a365-observability-hosting package, use ScopeUtils.populateExecuteToolScopeFromTurnContext to create the scope with agent details automatically derived from the TurnContext.
import { ToolCallDetails } from '@microsoft/agents-a365-observability';
import { ScopeUtils } from '@microsoft/agents-a365-observability-hosting';
const toolDetails: ToolCallDetails = {
toolName: 'email-search',
arguments: JSON.stringify({ query: 'from:boss@company.com' }),
toolCallId: 'tool-call-456',
toolType: 'function',
};
const scope = ScopeUtils.populateExecuteToolScopeFromTurnContext(
toolDetails,
context, // TurnContext
authToken // authentication token string
);
try {
await scope.withActiveSpanAsync(async () => {
const result = await searchEmails(toolDetails.arguments);
scope.recordResponse(JSON.stringify(result));
});
} finally {
scope.dispose();
}
using System;
using System.Threading.Tasks;
using Microsoft.Agents.A365.Observability.Runtime.Tracing.Contracts;
using Microsoft.Agents.A365.Observability.Runtime.Tracing.Scopes;
// Use the same agentDetails and request instances from the InvokeAgentScope example above
var toolCallDetails = new ToolCallDetails(
toolName: "summarize",
arguments: "{\"text\": \"...\"}",
toolCallId: "tc-001",
description: "Summarize provided text",
toolType: "function",
endpoint: new Uri("https://tools.contoso.com:8080")
);
using var scope = ExecuteToolScope.Start(
request: request,
details: toolCallDetails,
agentDetails: agentDetails
);
// ... your tool logic here ...
scope.RecordResponse("{\"summary\": \"The text was summarized.\"}");
Inference
The following examples show how to instrument AI model inference calls with observability tracking to capture token usage, model details, and response metadata.
from microsoft_agents_a365.observability.core import (
InferenceScope,
InferenceCallDetails,
InferenceOperationType,
)
# Use the same agent_details and request instances from the InvokeAgentScope example above
inference_details = InferenceCallDetails(
operationName=InferenceOperationType.CHAT,
model="gpt-4o-mini",
providerName="azure-openai",
inputTokens=123,
outputTokens=456,
finishReasons=["stop"],
)
with InferenceScope.start(request, inference_details, agent_details) as scope:
completion = call_llm(...)
scope.record_output_messages([completion.text])
scope.record_input_tokens(completion.usage.input_tokens)
scope.record_output_tokens(completion.usage.output_tokens)
import { InferenceScope, InferenceDetails, InferenceOperationType } from '@microsoft/agents-a365-observability';
// Use the same agentDetails and request instances from the InvokeAgentScope example above
const inferenceDetails: InferenceDetails = {
operationName: InferenceOperationType.CHAT,
model: 'gpt-4o-mini',
providerName: 'azure-openai',
};
const scope = InferenceScope.start(request, inferenceDetails, agentDetails);
try {
return await scope.withActiveSpanAsync(async () => {
// Record input messages
scope.recordInputMessages(['Summarize the following emails for me...']);
// Call the LLM
const response = await callLLM();
// Record detailed telemetry with granular methods
scope.recordOutputMessages(['Here is your email summary...']);
scope.recordInputTokens(145);
scope.recordOutputTokens(82);
scope.recordFinishReasons(['stop']);
return response.text;
});
} catch (error) {
scope.recordError(error as Error);
throw error;
} finally {
scope.dispose();
}
If your agent uses the @microsoft/agents-a365-observability-hosting package, you can use ScopeUtils.populateInferenceScopeFromTurnContext to create the scope with agent details automatically derived from the TurnContext.
import { InferenceDetails, InferenceOperationType } from '@microsoft/agents-a365-observability';
import { ScopeUtils } from '@microsoft/agents-a365-observability-hosting';
const inferenceDetails: InferenceDetails = {
operationName: InferenceOperationType.CHAT,
model: 'gpt-4o-mini',
providerName: 'azure-openai',
};
const scope = ScopeUtils.populateInferenceScopeFromTurnContext(
inferenceDetails,
context, // TurnContext
authToken // authentication token string
);
try {
await scope.withActiveSpanAsync(async () => {
const response = await callLLM();
scope.recordOutputMessages([response.text]);
scope.recordInputTokens(response.usage.inputTokens);
scope.recordOutputTokens(response.usage.outputTokens);
});
} finally {
scope.dispose();
}
using Microsoft.Agents.A365.Observability.Runtime.Tracing.Contracts;
using Microsoft.Agents.A365.Observability.Runtime.Tracing.Scopes;
// Use the same agentDetails and request instances from the InvokeAgentScope example above
var inferenceDetails = new InferenceCallDetails(
operationName: InferenceOperationType.Chat,
model: "gpt-4o-mini",
providerName: "Azure OpenAI",
inputTokens: 123,
outputTokens: 456,
finishReasons: new[] { "stop" }
);
using var scope = InferenceScope.Start(
request: request,
details: inferenceDetails,
agentDetails: agentDetails
);
// ... your inference logic here ...
scope.RecordOutputMessages(new[] { "AI response message" });
scope.RecordInputTokens(123);
scope.RecordOutputTokens(456);
Output
Use this scope for asynchronous scenarios where InvokeAgentScope, ExecuteToolScope, or InferenceScope can't capture output data synchronously. Start OutputScope as a child span to record the final output messages after the parent scope finishes.
from microsoft_agents_a365.observability.core import (
OutputScope,
Response,
SpanDetails,
)
# Use the same agent_details and request instances from the InvokeAgentScope example above
# Get the parent context from the originating scope
parent_context = invoke_scope.get_context()
response = Response(messages=["Here is your organized inbox with 15 urgent emails."])
with OutputScope.start(
request,
response,
agent_details,
span_details=SpanDetails(parent_context=parent_context),
):
# Output messages are recorded automatically from the response
pass
import { OutputScope, OutputResponse, SpanDetails } from '@microsoft/agents-a365-observability';
// Use the same agentDetails and request instances from the InvokeAgentScope example above
// Get the parent context from the originating scope
const parentContext = invokeScope.getSpanContext();
const response: OutputResponse = {
messages: ['Here is your organized inbox with 15 urgent emails.'],
};
const scope = OutputScope.start(
request,
response,
agentDetails,
undefined, // userDetails
{ parentContext } as SpanDetails
);
// Output messages are recorded automatically from the response
scope.dispose();
using Microsoft.Agents.A365.Observability.Runtime.Tracing.Contracts;
using Microsoft.Agents.A365.Observability.Runtime.Tracing.Scopes;
// Use the same agentDetails and request instances from the InvokeAgentScope example above
// Get the parent context from the originating scope
var parentContext = invokeScope.GetActivityContext();
var response = new Response(new[] { "Here is your organized inbox with 15 urgent emails." });
using var scope = OutputScope.Start(
request: request,
response: response,
agentDetails: agentDetails,
spanDetails: new SpanDetails(parentContext: parentContext)
);
// Output messages are recorded automatically from the response
Validate locally
To verify that you successfully integrated with the observability SDK, examine the console logs generated by your agent.
Set the environment variable ENABLE_A365_OBSERVABILITY_EXPORTER to false. This setting exports spans (traces) to the console.
To investigate export failures, enable verbose logging by setting ENABLE_A365_OBSERVABILITY_EXPORTER to true and configuring debug logging in your application startup:
import logging
logging.basicConfig(level=logging.DEBUG)
logging.getLogger("microsoft_agents_a365.observability.core").setLevel(logging.DEBUG)
# Or target only the exporter:
logging.getLogger(
"microsoft_agents_a365.observability.core.exporters.agent365_exporter"
).setLevel(logging.DEBUG)
Key log messages:
DEBUG Token resolved for agent {agentId} tenant {tenantId}
DEBUG Exporting {n} spans to {url}
DEBUG HTTP 200 - correlation ID: abc-123
ERROR Token resolution failed: {error}
ERROR HTTP 401 exporting spans - correlation ID: abc-123
INFO No spans with tenant/agent identity found; nothing exported.
Set the environment variable ENABLE_A365_OBSERVABILITY_EXPORTER to false. This setting exports spans (traces) to the console.
To investigate export failures, set environment variables in .env or shell:
# Enable the Agent365 exporter
ENABLE_A365_OBSERVABILITY_EXPORTER=true
# Enable verbose logging
A365_OBSERVABILITY_LOG_LEVEL=info|warn|error
Review console output for messages like:
[INFO] [Agent365Exporter] Exporting 245 spans
[INFO] [Agent365Exporter] Partitioned into 3 identity groups (2 spans skipped)
[INFO] [Agent365Exporter] Token resolved successfully via tokenResolver
[EVENT] export-group succeeded in 98ms {"tenantId":"...","agentId":"...","correlationId":"abc-123"}
[ERROR] [Agent365Exporter] Failed with status 401, correlation ID: abc-123
[WARN] export-partition-span-missing-identity: 5 spans skipped due to missing tenant or agent ID
Optionally, use a custom logger to capture export events to a file:
import { setLogger, ExporterEventNames } from '@microsoft/agents-a365-observability';
setLogger({
info: (msg, ...args) => myLogger.info(msg, ...args),
warn: (msg, ...args) => myLogger.warn(msg, ...args),
error: (msg, ...args) => myLogger.error(msg, ...args),
event: (eventType: ExporterEventNames, isSuccess: boolean, durationMs: number,
message?: string, details?: Record<string, string>) => {
myLogger.info({ eventType, isSuccess, durationMs, message, ...details });
}
});
Set EnableAgent365Exporter to false in appsettings.json. This setting exports spans (traces) to the console.
To investigate export failures, configure verbose logging in appsettings.json:
{
"EnableAgent365Exporter": "True",
"Logging": {
"LogLevel": {
"Microsoft.Agents.A365.Observability": "Debug"
}
}
}
Or set environment variables:
EnableAgent365Exporter=True
A365_OBSERVABILITY_DOMAIN_OVERRIDE=https://your-test-endpoint.example.com
A365_OBSERVABILITY_SCOPE_OVERRIDE=https://api.powerplatform.com/.default
Key log messages:
info: Agent365ExporterCore: Obtained token for agent {agentId} tenant {tenantId}.
info: Agent365ExporterCore: Sending {count} spans to {requestUri} for agent {agentId} tenant {tenantId}.
info: Agent365ExporterCore: HTTP {statusCode} exporting spans. 'x-ms-correlation-id': '{correlationId}'.
error: Agent365Exporter: Exception exporting spans: {exception}
warn: Agent365ExporterCore: No token obtained for agent {agentId} tenant {tenantId}. Skipping export.
Note
If you don't register an ILoggerFactory in DI, the exporter automatically falls back to a console logger.
Prerequisites for viewing exported logs
Before you can view agent telemetry in Microsoft Purview or Microsoft Defender, make sure the following prerequisites are met:
Observability requirements
IT administrators use the data you set in your code to monitor your agent's activity. Incomplete data means you don't fully realize the benefits of observability. Agents must provide the required data to receive all expected benefits. A validation process verifies that this data exists.
Within telemetry, there are concepts of scope or context. Each operation your agent performs exists within a different scope. You must include the data within the BaggageScope created by using Baggage attributes, or within the individual scopes as described in Manual Instrumentation.
To validate your implementation, follow the instructions to validate locally to generate console logs for your instrumentation. Then review the Validate for store publishing section to identify which attributes are required and which are optional. You must set all required attributes to successfully pass validation.
Review the required properties and parameter values described for these classes:
Properties you set by using the BaggageBuilder class might be set or overridden by the properties for the respective scopes.
Set the properties in the following table by using the InvokeAgentScope.start method.
| Data |
Description |
invoke_agent_details.details.agent_id |
The unique identifier for the AI agent |
invoke_agent_details.details.agent_name |
The human-readable name of the AI agent |
invoke_agent_details.details.agent_auid |
The agent user ID (AUID) |
invoke_agent_details.details.agent_upn |
The agent user principal name (UPN) |
invoke_agent_details.details.agent_blueprint_id |
The agent blueprint/application ID |
invoke_agent_details.details.tenant_id |
The tenant ID for the agent |
invoke_agent_details.details.conversation_id |
The identifier for the conversation or session |
invoke_agent_details.endpoint |
The endpoint for the agent invocation |
tenant_details.tenant_id |
The unique identifier for the tenant |
request.content |
The payload content sent to the agent for invocation |
request.execution_type |
Invocation type indicating request origin (for example, HumanToAgent or AgentToAgent) |
caller_details.caller_id |
The unique identifier for the caller |
caller_details.caller_upn |
The user principal name (UPN) of the caller |
caller_details.caller_user_id |
The user ID of the caller |
caller_details.tenant_id |
The tenant ID of the caller |
Set the properties in the following table by using the ExecuteToolScope.start method.
| Data |
Description |
details.tool_name |
The name of the tool being executed |
details.arguments |
Tool arguments or parameters |
details.tool_call_id |
The unique identifier for the tool call |
details.tool_type |
The type of the tool being executed |
details.endpoint |
If an external tool call is made |
agent_details.agent_id |
The unique identifier for the AI agent |
agent_details.agent_name |
The human-readable name of the AI agent |
agent_details.agent_auid |
The agent user ID |
agent_details.agent_upn |
The agent user principal name (UPN) |
agent_details.agent_blueprint_id |
The agent blueprint or application ID |
agent_details.tenant_id |
Tenant ID for the agent. |
agent_details.conversation_id |
The conversation ID for the agent invocation. |
tenant_details.tenant_id |
Tenant ID for the agent. |
Set the properties in the following table by using the InferenceScope.start method.
| Data |
Description |
details.operationName |
The operation name or type for the inference |
details.model |
The model name or identifier |
details.providerName |
The provider name |
agent_details.agent_id |
The unique identifier for the AI agent |
agent_details.agent_name |
The human-readable name of the AI agent |
agent_details.agent_auid |
The agent user ID (AUID) |
agent_details.agent_upn |
The agent user principal name (UPN) |
agent_details.agent_blueprint_id |
The agent blueprint or application ID |
agent_details.tenant_id |
The unique identifier for the tenant |
agent_details.conversation_id |
The identifier for the conversation or session |
tenant_details.tenant_id |
The unique identifier for the tenant |
request.content |
The payload content sent to the agent for inference |
request.execution_type |
Invocation type indicating request origin (for example, HumanToAgent or AgentToAgent) |
request.source_metadata |
Represent the channel information |
Properties that you set by using the BaggageBuilder class might be set or overridden by the properties for the respective scopes.
Set the properties in the following table by using the InvokeAgentScope.start method.
| Data |
Description |
agentDetails.agentId |
The unique identifier for the AI agent |
agentDetails.agentName |
The human-readable name of the AI agent |
agentDetails.agentAuid |
The agent user ID (AUID) |
agentDetails.agentEmail |
The agent email address |
agentDetails.agentBlueprintId |
The agent blueprint/application ID |
agentDetails.tenantId |
The tenant ID for the agent |
scopeDetails.endpoint.host |
The host address of the endpoint for the agent invocation |
scopeDetails.endpoint.port |
The port number of the endpoint for the agent invocation |
request.content |
The content of the request payload for the agent invocation |
request.conversationId |
The identifier for the conversation or session |
request.sessionId |
The session ID for the agent invocation |
request.channel.name |
Channel information like (msteams, email, and so on) |
callerDetails.userDetails.userId |
The unique identifier for the caller |
callerDetails.userDetails.userName |
The display name of the caller |
callerDetails.userDetails.userEmail |
The email address of the caller |
Set the properties in the following table by using the ExecuteToolScope.start method.
| Data |
Description |
details.toolName |
The name of the tool being executed |
details.arguments |
Tool arguments or parameters |
details.toolCallId |
The unique identifier for the tool call |
details.toolType |
The type of the tool being executed |
details.endpoint.host |
The host address of the endpoint for the tool execution. Required if external call is made |
details.endpoint.port |
The port number of the endpoint for the tool execution. Required if external call is made |
agentDetails.agentId |
The unique identifier for the AI agent |
agentDetails.agentName |
The human-readable name of the AI agent |
agentDetails.tenantId |
The tenant ID for the agent |
request.conversationId |
The identifier for the conversation or session |
Set the properties in the following table by using the InferenceScope.start method.
| Data |
Description |
details.operationName |
The operation name or type for the inference |
details.model |
The model name or identifier |
details.providerName |
The provider name |
agentDetails.agentId |
The unique identifier for the AI agent |
agentDetails.agentName |
The human-readable name of the AI agent |
agentDetails.tenantId |
The tenant ID for the agent |
request.conversationId |
The identifier for the conversation or session |
Properties that you set by using the BaggageBuilder class might be set or overridden by the properties for the respective scopes.
Set the properties in the following table by using the InvokeAgentScope.Start method.
| Data |
Description |
invokeAgentDetails.details.agentId |
The unique identifier for the agent. |
invokeAgentDetails.details.agentName |
Display name for the agent. |
invokeAgentDetails.details.agentAUID |
Azure User ID (AUID) for the agent. |
invokeAgentDetails.details.agentUPN |
User Principal Name (UPN) for the agent. |
invokeAgentDetails.details.agentBlueprintId |
Blueprint/Application ID for the agent. |
invokeAgentDetails.details.tenantId |
Tenant ID for the agent. |
invokeAgentDetails.endpoint |
endpoint URI of the agent to invoke. |
tenantDetails.tenantId |
The identifier of the tenant. |
request.content |
The payload content supplied to the agent. |
request.executionType |
execution type describing the request. |
request.sourceMetadata.name |
Human-readable name of the source. Channel name. |
conversationId |
The conversation ID for the agent invocation. |
Set the properties in the following table by using the ExecuteToolScope.Start method.
| Data |
Description |
details.toolName |
Name of the tool being invoked. |
details.arguments |
Serialized arguments passed to the tool. |
details.toolCallId |
Identifier for the tool invocation. |
details.toolType |
Type classification for the tool. |
details.endpoint |
Required endpoint for remote tool execution. |
agentDetails.agentId |
The unique identifier for the agent. |
agentDetails.agentName |
Display name for the agent. |
agentDetails.agentAUID |
Azure User ID (AUID) for the agent. |
agentDetails.agentUPN |
User Principal Name (UPN) for the agent. |
agentDetails.agentBlueprintId |
Blueprint/Application ID for the agent. |
agentDetails.tenantId |
Tenant ID for the agent. |
tenantDetails.tenantId |
The identifier of the tenant. |
Set the properties in the following table by using the InferenceScope.Start method.
| Data |
Description |
details.operationName |
Telemetry identifier for the inference operation. |
details.model |
Model name used to satisfy the inference request. |
details.providerName |
Provider responsible for the inference call. |
agentDetails.agentId |
The unique identifier for the agent. |
agentDetails.agentName |
Display name for the agent. |
agentDetails.agentAUID |
Azure User ID (AUID) for the agent. |
agentDetails.agentUPN |
User Principal Name (UPN) for the agent. |
agentDetails.agentBlueprintId |
Blueprint/Application ID for the agent. |
agentDetails.tenantId |
Tenant ID for the agent. |
tenantDetails.tenantId |
The identifier of the tenant. |
parentId |
For explicit management of parent scope. Not required in most cases. |
Validate for store publishing
Before publishing, use console logs to validate your observability integration for the agent by implementing the required invoke agent, execute tool, inference, and output scopes. Then compare your agent's logs against the following attribute lists to verify all required attributes are present. Capture attributes on each scope or through the baggage builder, and include optional attributes at your discretion.
For more information about store publishing requirements, see store validation guidelines.
InvokeAgentScope attributes
The following list summarizes the required and optional telemetry attributes recorded when you start an InvokeAgentScope.
"attributes": {
"error.type": "Optional",
"microsoft.a365.agent.blueprint.id": "Required",
"gen_ai.agent.description": "Optional",
"gen_ai.agent.id": "Required",
"gen_ai.agent.name": "Required",
"microsoft.a365.agent.platform.id": "Optional",
"microsoft.agent.user.email": "Required",
"microsoft.agent.user.id": "Required",
"gen_ai.agent.version": "Optional",
"microsoft.a365.caller.agent.blueprint.id": "Optional",
"microsoft.a365.caller.agent.id": "Optional",
"microsoft.a365.caller.agent.name": "Optional",
"microsoft.a365.caller.agent.platform.id": "Optional",
"microsoft.a365.caller.agent.user.email": "Optional",
"microsoft.a365.caller.agent.user.id": "Optional",
"microsoft.a365.caller.agent.version": "Optional",
"client.address": "Required",
"user.id": "Required",
"user.name": "Optional",
"user.email": "Required",
"microsoft.channel.link": "Optional",
"microsoft.channel.name": "Required",
"gen_ai.conversation.id": "Required",
"microsoft.conversation.item.link": "Optional",
"gen_ai.input.messages": "Required",
"gen_ai.operation.name": "Required",
"gen_ai.output.messages": "Required",
"server.address": "Required",
"server.port": "Required",
"microsoft.session.id": "Optional",
"microsoft.session.description": "Optional",
"microsoft.tenant.id": "Required"
}
The following list summarizes the required and optional telemetry attributes recorded when you start an ExecuteToolScope.
"attributes": {
"error.type": "Optional",
"microsoft.a365.agent.blueprint.id": "Required",
"gen_ai.agent.description": "Optional",
"gen_ai.agent.id": "Required",
"gen_ai.agent.name": "Required",
"microsoft.a365.agent.platform.id": "Optional",
"microsoft.agent.user.email": "Required",
"microsoft.agent.user.id": "Required",
"gen_ai.agent.version": "Optional",
"client.address": "Required",
"user.id": "Required",
"user.name": "Optional",
"user.email": "Required",
"microsoft.channel.link": "Optional",
"microsoft.channel.name": "Required",
"gen_ai.conversation.id": "Required",
"microsoft.conversation.item.link": "Optional",
"gen_ai.operation.name": "Required",
"gen_ai.tool.call.arguments": "Required",
"gen_ai.tool.call.id": "Required",
"gen_ai.tool.call.result": "Required",
"gen_ai.tool.description": "Optional",
"gen_ai.tool.name": "Required",
"gen_ai.tool.type": "Required",
"server.address": "Optional",
"server.port": "Optional",
"microsoft.session.id": "Optional",
"microsoft.session.description": "Optional",
"microsoft.tenant.id": "Required"
}
InferenceScope attributes
The following list summarizes the required and optional telemetry attributes recorded when you start an InferenceScope.
"attributes": {
"error.type": "Optional",
"microsoft.a365.agent.blueprint.id": "Required",
"gen_ai.agent.description": "Optional",
"gen_ai.agent.id": "Required",
"gen_ai.agent.name": "Required",
"microsoft.a365.agent.platform.id": "Optional",
"microsoft.a365.agent.thought.process": "Optional",
"microsoft.agent.user.email": "Required",
"microsoft.agent.user.id": "Required",
"gen_ai.agent.version": "Optional",
"client.address": "Required",
"user.id": "Required",
"user.name": "Optional",
"user.email": "Required",
"microsoft.channel.link": "Optional",
"microsoft.channel.name": "Required",
"gen_ai.conversation.id": "Required",
"microsoft.conversation.item.link": "Optional",
"gen_ai.input.messages": "Required",
"gen_ai.operation.name": "Required",
"gen_ai.output.messages": "Required",
"gen_ai.provider.name": "Required",
"gen_ai.request.model": "Required",
"gen_ai.response.finish_reasons": "Optional",
"gen_ai.usage.input_tokens": "Optional",
"gen_ai.usage.output_tokens": "Optional",
"server.address": "Optional",
"server.port": "Optional",
"microsoft.session.description": "Optional",
"microsoft.session.id": "Optional",
"microsoft.tenant.id": "Required"
}
OutputScope attributes
The following list summarizes the required and optional telemetry attributes recorded when you start an OutputScope. Use this scope for asynchronous scenarios where the parent scope can't capture output data synchronously.
"attributes": {
"microsoft.a365.agent.blueprint.id": "Required",
"gen_ai.agent.description": "Optional",
"gen_ai.agent.id": "Required",
"gen_ai.agent.name": "Required",
"microsoft.a365.agent.platform.id": "Optional",
"microsoft.agent.user.email": "Required",
"microsoft.agent.user.id": "Required",
"gen_ai.agent.version": "Optional",
"client.address": "Required",
"user.id": "Required",
"user.name": "Optional",
"user.email": "Required",
"microsoft.channel.link": "Optional",
"microsoft.channel.name": "Required",
"gen_ai.conversation.id": "Required",
"microsoft.conversation.item.link": "Optional",
"gen_ai.operation.name": "Required",
"gen_ai.output.messages": "Required",
"microsoft.session.id": "Optional",
"microsoft.session.description": "Optional",
"microsoft.tenant.id": "Required"
}
Test your agent with observability
After you implement observability in your agent, test it to make sure it captures telemetry correctly. Follow the testing guide to set up your environment. Then, focus primarily on the View observability logs section to validate your observability implementation is working as expected.
Verification:
- Go to:
https://admin.cloud.microsoft/#/agents/all
- Select your agent > Activity
- You see sessions and tool calls
Troubleshooting
This section describes common problems when implementing and using observability.
Tip
Agent 365 Troubleshooting Guide contains high-level troubleshooting recommendations, best practices, and links to troubleshooting content for each part of the Agent 365 development lifecycle.
Observability data doesn't appear
Symptoms:
- Agent is running
- No telemetry in admin center
- Can't see agent activity
Root cause:
- Observability isn't enabled
- Configuration errors
- Token resolver problems
Solutions: Try the following steps to resolve the problem:
Verify observability is enabled
Enable observability flags in your environment.
# .env file
ENABLE_A365_OBSERVABILITY_EXPORTER=true
Check token resolver configuration
Make sure your code properly implements the token resolver. Check the latest code in the SDK directly.
Check for errors in logs
Use the az webapp log tail command to search logs for observability-related errors.
# Look for observability-related errors
az webapp log tail --name <your-app-name> --resource-group <your-resource-group> | Select-String "observability"
Verify telemetry export
Confirm telemetry is generated and exported as expected.
- Add console exporter for testing
- Check if telemetry is generated locally
Learn more about testing observability: