From f0d6ce69c218ed4c3c11659830106936dccb7677 Mon Sep 17 00:00:00 2001 From: Julio Menendez Gonzalez Date: Tue, 28 Apr 2026 17:05:12 -0600 Subject: [PATCH 1/5] docs: add guide for integrating SDK with existing OpenTelemetry New top-level doc that explains the recommended init order, two minimal patterns (Azure Monitor and manual OTel SDK), the auto vs manual instrumentation matrix, expected span types, a verification recipe, three common pitfalls, and an exporter-combination table. Adds a short callout at the top of the observability-core README that links to the new guide so customers landing on PyPI / GitHub discover it. Co-Authored-By: Claude Opus 4.7 (1M context) --- ...integrating-with-existing-opentelemetry.md | 138 ++++++++++++++++++ .../README.md | 2 + 2 files changed, 140 insertions(+) create mode 100644 docs/integrating-with-existing-opentelemetry.md diff --git a/docs/integrating-with-existing-opentelemetry.md b/docs/integrating-with-existing-opentelemetry.md new file mode 100644 index 00000000..b11771f3 --- /dev/null +++ b/docs/integrating-with-existing-opentelemetry.md @@ -0,0 +1,138 @@ +# Integrating with existing OpenTelemetry + +This guide is for developers whose application **already** initializes OpenTelemetry — for example with `azure-monitor-opentelemetry`, an OTLP collector, or a vendor-specific exporter — and who want Agent 365 spans to flow alongside their existing telemetry. If you're starting fresh, see the [observability-core README](../libraries/microsoft-agents-a365-observability-core/README.md) for the standalone setup. + +## The integration rule + +> **Initialize your existing OpenTelemetry stack first, then call Agent 365's `configure()`.** The SDK detects the existing `TracerProvider` and adds its processors to it, so both your existing backend and the Agent 365 backend receive every span. + +The detection happens in [`config.py`](../libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/config.py): if a real (non-no-op) `TracerProvider` is already set, `configure()` adds its `BatchSpanProcessor` and baggage `SpanProcessor` to that provider rather than creating a new one. + +## Two minimal patterns + +### Pattern A — `azure-monitor-opentelemetry` + +```python +from azure.monitor.opentelemetry import configure_azure_monitor +from microsoft_agents_a365.observability.core import configure + +# 1. Existing OTel: Azure Monitor sets up a TracerProvider + AM exporter. +configure_azure_monitor(connection_string=os.environ["APPLICATIONINSIGHTS_CONNECTION_STRING"]) + +# 2. Agent 365 attaches its processors to that same TracerProvider. +configure( + service_name="my-agent", + service_namespace="my-namespace", + token_resolver=my_token_resolver, +) +``` + +→ Runnable version: [`observability-with-azure-monitor`](https://github.com/microsoft/Agent365-Samples/tree/main/python/observability-with-azure-monitor) sample. + +### Pattern B — manual OTel SDK + OTLP exporter + +```python +from opentelemetry import trace +from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter +from opentelemetry.sdk.resources import SERVICE_NAME, Resource +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace.export import BatchSpanProcessor + +from microsoft_agents_a365.observability.core import configure + +# 1. Existing OTel: build provider + OTLP exporter explicitly. +provider = TracerProvider(resource=Resource.create({SERVICE_NAME: "my-agent"})) +provider.add_span_processor( + BatchSpanProcessor(OTLPSpanExporter(endpoint=os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"])) +) +trace.set_tracer_provider(provider) + +# 2. Agent 365 attaches to that same provider. +configure( + service_name="my-agent", + service_namespace="my-namespace", + token_resolver=my_token_resolver, +) +``` + +→ Runnable version: [`observability-with-otlp`](https://github.com/microsoft/Agent365-Samples/tree/main/python/observability-with-otlp) sample (defaults to `ConsoleSpanExporter` for zero setup). + +## Auto-instrumentation vs. manual instrumentation + +The OTel **backend** (where spans go) and the **instrumentation style** (how spans are produced) are independent axes. You can mix them freely. + +| | Auto (extension package) | Manual (`InvokeAgentScope` / `InferenceScope` / `ExecuteToolScope`) | +|--------------------------|-------------------------------------------------------------------|--------------------------------------------------------------------| +| **Azure Monitor** | Demonstrated by `observability-with-azure-monitor` sample | Same `configure()`; replace agent code with manual scope wrapping | +| **OTLP / vendor-neutral** | Same `configure()`; install your framework's extension package | Demonstrated by `observability-with-otlp` sample | + +For auto-instrumentation, install the framework-specific extension package — for example: + +- OpenAI Agents SDK → `microsoft-agents-a365-observability-extensions-openai` +- LangChain → `microsoft-agents-a365-observability-extensions-langchain` +- Semantic Kernel → `microsoft-agents-a365-observability-extensions-semantickernel` +- Microsoft Agent Framework → `microsoft-agents-a365-observability-extensions-agentframework` + +For the OpenAI Agents SDK extension, instantiate `OpenAIAgentsTraceInstrumentor()` and call `.instrument()` **after** `configure()`. The instrumentor raises `RuntimeError` if Agent 365 isn't configured first. + +## What spans should I expect to see? + +The SDK produces three core span kinds. Your backend should show them in this typical hierarchy: + +| Span operation name | Produced by | Typical parent | Notes | +|---------------------|---------------------------------------------------|----------------|-------| +| `invoke_agent` | `InvokeAgentScope` (one per user turn) | (root or app) | Activity name suffixed with the agent name when set | +| `inference` | `InferenceScope` (one per LLM call) | `invoke_agent` | Records model name, token counts, finish reasons | +| `execute_tool` | `ExecuteToolScope` (one per tool invocation) | `invoke_agent` | Records tool name, args, and result | + +Filter your backend by the `gen_ai.operation.name` attribute, or by span name, to find these. + +## Verifying the integration + +If you've called `configure()` but don't see Agent 365 spans in your backend, isolate the problem by adding a `ConsoleSpanExporter` temporarily: + +```python +from opentelemetry import trace +from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter + +# After configure() has run: +trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(ConsoleSpanExporter())) +``` + +Run a single turn. If you see `invoke_agent` / `inference` / `execute_tool` JSON dumps on stdout, the SDK is producing spans correctly — the issue is in your backend exporter (network, auth, sampling). If you don't see them, the integration itself is wrong; check the pitfalls below. + +## Common pitfalls + +### Pitfall 1: Calling `configure_azure_monitor()` after Agent 365 `configure()` + +**Symptom:** Agent 365 spans don't appear in any backend. + +**Cause:** `configure_azure_monitor` (and many vendor packages) replace the global `TracerProvider`. If they run *after* `configure()`, the provider with our processors is discarded. + +**Fix:** Always initialize Azure Monitor (or any OTel setup) **before** calling Agent 365 `configure()`. + +### Pitfall 2: Calling Agent 365 `configure()` before app's OTel setup + +**Symptom:** Same as above — Agent 365 spans are missing. + +**Cause:** `configure()` creates its own `TracerProvider` (no existing one detected). Your app's later OTel init replaces it, dropping our processors. + +**Fix:** Same as Pitfall 1 — OTel first, then Agent 365. + +### Pitfall 3: `OTEL_SDK_DISABLED=true` or `OTEL_TRACES_EXPORTER=none` + +**Symptom:** Nothing exports — neither your existing backend nor Agent 365. + +**Cause:** These environment variables disable OpenTelemetry SDK-wide. They suppress Agent 365 spans alongside everything else. + +**Fix:** Use sampling (`OTEL_TRACES_SAMPLER`) or per-exporter configuration instead of the global disable. If you intentionally want to disable tracing in a particular environment, that's fine — just understand it disables Agent 365 too. + +## Exporter combinations + +| Combination | What's installed | What to call | Gotchas | +|-----------------------------------------|---------------------------------------------------------------------------|---------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------| +| Azure Monitor only | `azure-monitor-opentelemetry` | `configure_azure_monitor(...)` | Standard Azure Monitor — no Agent 365 spans flow. | +| Azure Monitor + Agent 365 | `azure-monitor-opentelemetry`, `microsoft-agents-a365-observability-core` | `configure_azure_monitor(...)` then `configure(...)` | Order matters (see Pitfall 1). | +| OTLP collector + Agent 365 | `opentelemetry-sdk`, `opentelemetry-exporter-otlp-*`, A365 core | Build provider + `BatchSpanProcessor(OTLPSpanExporter(...))` then `configure(...)` | Set `OTEL_EXPORTER_OTLP_ENDPOINT`; collector must be reachable. | +| Agent 365 only | `microsoft-agents-a365-observability-core` | `configure(...)` only | SDK creates its own `TracerProvider`; spans go to Agent 365 backend only. | +| OTLP + Azure Monitor + Agent 365 | All of the above | Configure Azure Monitor first; add OTLP `BatchSpanProcessor` to the provider; call `configure(...)` | All three exporters receive every span. Watch out for duplicate processors if Azure Monitor itself adds OTLP. | diff --git a/libraries/microsoft-agents-a365-observability-core/README.md b/libraries/microsoft-agents-a365-observability-core/README.md index 25164f71..0baf59a7 100644 --- a/libraries/microsoft-agents-a365-observability-core/README.md +++ b/libraries/microsoft-agents-a365-observability-core/README.md @@ -5,6 +5,8 @@ Telemetry, tracing, and monitoring components for AI agents built on OpenTelemetry. This package provides structured spans for agent invocation, tool execution, and LLM inference with context propagation and pluggable exporters. +> **Already using OpenTelemetry?** This SDK detects an existing `TracerProvider` and adds its processors to it — your spans flow to both your existing backend (Azure Monitor, OTLP collector, vendor exporter, etc.) and Agent 365. See [Integrating with existing OpenTelemetry](../../docs/integrating-with-existing-opentelemetry.md) for setup patterns and troubleshooting. + ## Installation ```bash From 7f0756918a3c30cac3be8f0d441e7aa4e22ffda5 Mon Sep 17 00:00:00 2001 From: Julio Menendez Gonzalez Date: Tue, 28 Apr 2026 17:09:22 -0600 Subject: [PATCH 2/5] docs: correct inference span name in integration guide The 'What spans should I expect to see?' table incorrectly listed 'inference' as the gen_ai.operation.name value. InferenceScope actually uses InferenceOperationType.value (e.g. Chat / TextCompletion / GenerateContent) for both the attribute and the span name. Updated the table and the trailing paragraph to reflect actual SDK behavior. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/integrating-with-existing-opentelemetry.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/integrating-with-existing-opentelemetry.md b/docs/integrating-with-existing-opentelemetry.md index b11771f3..f402fb3d 100644 --- a/docs/integrating-with-existing-opentelemetry.md +++ b/docs/integrating-with-existing-opentelemetry.md @@ -79,13 +79,13 @@ For the OpenAI Agents SDK extension, instantiate `OpenAIAgentsTraceInstrumentor( The SDK produces three core span kinds. Your backend should show them in this typical hierarchy: -| Span operation name | Produced by | Typical parent | Notes | -|---------------------|---------------------------------------------------|----------------|-------| -| `invoke_agent` | `InvokeAgentScope` (one per user turn) | (root or app) | Activity name suffixed with the agent name when set | -| `inference` | `InferenceScope` (one per LLM call) | `invoke_agent` | Records model name, token counts, finish reasons | -| `execute_tool` | `ExecuteToolScope` (one per tool invocation) | `invoke_agent` | Records tool name, args, and result | +| `gen_ai.operation.name` | Produced by | Typical parent | Span name (default) | Notes | +|-------------------------|---------------------------------------------------|----------------|---------------------|-------| +| `invoke_agent` | `InvokeAgentScope` (one per user turn) | (root or app) | `invoke_agent ` when set, else `invoke_agent` | | +| `Chat` / `TextCompletion` / `GenerateContent` | `InferenceScope` (one per LLM call) | `invoke_agent` | ` ` (e.g. `Chat gpt-4o-mini`) | Value matches the chosen `InferenceOperationType`. Records model, token counts, finish reasons. | +| `execute_tool` | `ExecuteToolScope` (one per tool invocation) | `invoke_agent` | `execute_tool ` when set, else `execute_tool` | Records tool name, args, and result. | -Filter your backend by the `gen_ai.operation.name` attribute, or by span name, to find these. +Filter your backend by the `gen_ai.operation.name` attribute or by span name. Note that `inference` is *not* the literal attribute value — that value comes from `InferenceOperationType` (`Chat`, `TextCompletion`, or `GenerateContent`). ## Verifying the integration From 427d5b5c3996b665e71551c808290841ea3587c9 Mon Sep 17 00:00:00 2001 From: Julio Menendez Gonzalez Date: Wed, 29 Apr 2026 11:18:04 -0600 Subject: [PATCH 3/5] docs: add Pitfall 4 (ENABLE_OBSERVABILITY) to integration guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Agent 365 SDK gates scope-driven span creation behind ENABLE_OBSERVABILITY (or ENABLE_A365_OBSERVABILITY) env var. Without it, the user's existing OTel backend works fine but Agent 365 scopes silently produce zero spans — a confusing failure mode that wasn't covered in the original troubleshooting section. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/integrating-with-existing-opentelemetry.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/integrating-with-existing-opentelemetry.md b/docs/integrating-with-existing-opentelemetry.md index f402fb3d..78da4b94 100644 --- a/docs/integrating-with-existing-opentelemetry.md +++ b/docs/integrating-with-existing-opentelemetry.md @@ -127,6 +127,14 @@ Run a single turn. If you see `invoke_agent` / `inference` / `execute_tool` JSON **Fix:** Use sampling (`OTEL_TRACES_SAMPLER`) or per-exporter configuration instead of the global disable. If you intentionally want to disable tracing in a particular environment, that's fine — just understand it disables Agent 365 too. +### Pitfall 4: `ENABLE_OBSERVABILITY` not set + +**Symptom:** Your existing OTel backend works (Azure Monitor / OTLP / etc. show spans), but Agent 365 scope blocks (`InvokeAgentScope`, `InferenceScope`, `ExecuteToolScope`) produce zero spans. No errors, no warnings. + +**Cause:** Agent 365's scope classes gate span creation on the `ENABLE_OBSERVABILITY` (or `ENABLE_A365_OBSERVABILITY`) environment variable. If neither is set to `true` / `1` / `yes` / `on`, every scope's `__init__` skips span creation entirely. This is **independent** of OTel's own enable/disable mechanism — your existing OTel telemetry continues to flow normally. + +**Fix:** Set `ENABLE_OBSERVABILITY=true` (or `ENABLE_A365_OBSERVABILITY=true`) in your environment before the SDK is imported. Both runnable samples include this in their `.env.template`. + ## Exporter combinations | Combination | What's installed | What to call | Gotchas | From 57cf04ecf4f4170f809ca8ecfbbacf2abcfd1953 Mon Sep 17 00:00:00 2001 From: Julio Menendez Gonzalez Date: Wed, 29 Apr 2026 11:32:00 -0600 Subject: [PATCH 4/5] docs: clarify Chat vs chat casing discrepancy in span name table Manual instrumentation produces InferenceOperationType.value (e.g. "Chat", capitalized) while auto-instrumentation extensions produce the lowercase OTel GenAI semconv form (e.g. "chat"). Tracked as an SDK issue; documented here so users can interpret what they see in their backend. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/integrating-with-existing-opentelemetry.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/integrating-with-existing-opentelemetry.md b/docs/integrating-with-existing-opentelemetry.md index 78da4b94..befe913d 100644 --- a/docs/integrating-with-existing-opentelemetry.md +++ b/docs/integrating-with-existing-opentelemetry.md @@ -82,10 +82,10 @@ The SDK produces three core span kinds. Your backend should show them in this ty | `gen_ai.operation.name` | Produced by | Typical parent | Span name (default) | Notes | |-------------------------|---------------------------------------------------|----------------|---------------------|-------| | `invoke_agent` | `InvokeAgentScope` (one per user turn) | (root or app) | `invoke_agent ` when set, else `invoke_agent` | | -| `Chat` / `TextCompletion` / `GenerateContent` | `InferenceScope` (one per LLM call) | `invoke_agent` | ` ` (e.g. `Chat gpt-4o-mini`) | Value matches the chosen `InferenceOperationType`. Records model, token counts, finish reasons. | +| (varies — see notes) | `InferenceScope` (one per LLM call) | `invoke_agent` | ` ` | **Manual instrumentation** uses `InferenceOperationType.value` (currently `Chat` / `TextCompletion` / `GenerateContent`, capitalized). **Auto-instrumentation** (e.g. `OpenAIAgentsTraceInstrumentor`) uses lowercase per the [OTel GenAI semconv](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/) (e.g. `chat`). The two are inconsistent today. | | `execute_tool` | `ExecuteToolScope` (one per tool invocation) | `invoke_agent` | `execute_tool ` when set, else `execute_tool` | Records tool name, args, and result. | -Filter your backend by the `gen_ai.operation.name` attribute or by span name. Note that `inference` is *not* the literal attribute value — that value comes from `InferenceOperationType` (`Chat`, `TextCompletion`, or `GenerateContent`). +Filter your backend by the `gen_ai.operation.name` attribute or by span name. Note that `inference` is *not* the literal attribute value — manual instrumentation produces `Chat` / `TextCompletion` / `GenerateContent` (the `InferenceOperationType.value`), while auto-instrumentation extension packages produce the lowercase OTel-spec form (e.g. `chat`). This casing discrepancy is tracked as an SDK issue. ## Verifying the integration From 749acbccdd6a609fc68d7a32fb69c38932dbd360 Mon Sep 17 00:00:00 2001 From: Julio Menendez Gonzalez Date: Mon, 18 May 2026 17:30:40 -0600 Subject: [PATCH 5/5] docs: address review feedback on integration guide - Clarify that Agent 365 backend export requires ENABLE_A365_OBSERVABILITY_EXPORTER + token_resolver - Fix processor description: _EnrichingBatchSpanProcessor + SpanProcessor (not baggage) - Add missing 'import os' to both code snippets - Fix ExecuteToolScope span name (always includes tool_name) - Fix verification section: use actual span names (Chat/chat, not 'inference') - Fix Pitfall 4: env var checked at scope construction, not import time Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/integrating-with-existing-opentelemetry.md | 14 +++++++++----- .../README.md | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/integrating-with-existing-opentelemetry.md b/docs/integrating-with-existing-opentelemetry.md index befe913d..9e3fb123 100644 --- a/docs/integrating-with-existing-opentelemetry.md +++ b/docs/integrating-with-existing-opentelemetry.md @@ -4,15 +4,17 @@ This guide is for developers whose application **already** initializes OpenTelem ## The integration rule -> **Initialize your existing OpenTelemetry stack first, then call Agent 365's `configure()`.** The SDK detects the existing `TracerProvider` and adds its processors to it, so both your existing backend and the Agent 365 backend receive every span. +> **Initialize your existing OpenTelemetry stack first, then call Agent 365's `configure()`.** The SDK detects the existing `TracerProvider` and adds its processors to it. Your existing backend receives every span; the Agent 365 backend also receives spans when `ENABLE_A365_OBSERVABILITY_EXPORTER=true` and a `token_resolver` is provided (otherwise `configure()` falls back to `ConsoleSpanExporter`). -The detection happens in [`config.py`](../libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/config.py): if a real (non-no-op) `TracerProvider` is already set, `configure()` adds its `BatchSpanProcessor` and baggage `SpanProcessor` to that provider rather than creating a new one. +The detection happens in [`config.py`](../libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/config.py): if a real (non-no-op) `TracerProvider` is already set (detected via a non-None `resource` attribute), `configure()` adds an `_EnrichingBatchSpanProcessor` (wrapping the configured exporter) and a custom `SpanProcessor` to that provider rather than creating a new one. ## Two minimal patterns ### Pattern A — `azure-monitor-opentelemetry` ```python +import os + from azure.monitor.opentelemetry import configure_azure_monitor from microsoft_agents_a365.observability.core import configure @@ -32,6 +34,8 @@ configure( ### Pattern B — manual OTel SDK + OTLP exporter ```python +import os + from opentelemetry import trace from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter from opentelemetry.sdk.resources import SERVICE_NAME, Resource @@ -83,7 +87,7 @@ The SDK produces three core span kinds. Your backend should show them in this ty |-------------------------|---------------------------------------------------|----------------|---------------------|-------| | `invoke_agent` | `InvokeAgentScope` (one per user turn) | (root or app) | `invoke_agent ` when set, else `invoke_agent` | | | (varies — see notes) | `InferenceScope` (one per LLM call) | `invoke_agent` | ` ` | **Manual instrumentation** uses `InferenceOperationType.value` (currently `Chat` / `TextCompletion` / `GenerateContent`, capitalized). **Auto-instrumentation** (e.g. `OpenAIAgentsTraceInstrumentor`) uses lowercase per the [OTel GenAI semconv](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/) (e.g. `chat`). The two are inconsistent today. | -| `execute_tool` | `ExecuteToolScope` (one per tool invocation) | `invoke_agent` | `execute_tool ` when set, else `execute_tool` | Records tool name, args, and result. | +| `execute_tool` | `ExecuteToolScope` (one per tool invocation) | `invoke_agent` | `execute_tool ` (always includes the tool name) | Records tool name, args, and result. | Filter your backend by the `gen_ai.operation.name` attribute or by span name. Note that `inference` is *not* the literal attribute value — manual instrumentation produces `Chat` / `TextCompletion` / `GenerateContent` (the `InferenceOperationType.value`), while auto-instrumentation extension packages produce the lowercase OTel-spec form (e.g. `chat`). This casing discrepancy is tracked as an SDK issue. @@ -99,7 +103,7 @@ from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExport trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(ConsoleSpanExporter())) ``` -Run a single turn. If you see `invoke_agent` / `inference` / `execute_tool` JSON dumps on stdout, the SDK is producing spans correctly — the issue is in your backend exporter (network, auth, sampling). If you don't see them, the integration itself is wrong; check the pitfalls below. +Run a single turn. If you see `invoke_agent` / `Chat` (or `chat`) / `execute_tool` JSON dumps on stdout, the SDK is producing spans correctly — the issue is in your backend exporter (network, auth, sampling). If you don't see them, the integration itself is wrong; check the pitfalls below. ## Common pitfalls @@ -133,7 +137,7 @@ Run a single turn. If you see `invoke_agent` / `inference` / `execute_tool` JSON **Cause:** Agent 365's scope classes gate span creation on the `ENABLE_OBSERVABILITY` (or `ENABLE_A365_OBSERVABILITY`) environment variable. If neither is set to `true` / `1` / `yes` / `on`, every scope's `__init__` skips span creation entirely. This is **independent** of OTel's own enable/disable mechanism — your existing OTel telemetry continues to flow normally. -**Fix:** Set `ENABLE_OBSERVABILITY=true` (or `ENABLE_A365_OBSERVABILITY=true`) in your environment before the SDK is imported. Both runnable samples include this in their `.env.template`. +**Fix:** Set `ENABLE_OBSERVABILITY=true` (or `ENABLE_A365_OBSERVABILITY=true`) in your environment before creating any scopes / emitting spans (the check happens at scope construction time, not import time). Both runnable samples include this in their `.env.template`. ## Exporter combinations diff --git a/libraries/microsoft-agents-a365-observability-core/README.md b/libraries/microsoft-agents-a365-observability-core/README.md index 0baf59a7..529d74a9 100644 --- a/libraries/microsoft-agents-a365-observability-core/README.md +++ b/libraries/microsoft-agents-a365-observability-core/README.md @@ -5,7 +5,7 @@ Telemetry, tracing, and monitoring components for AI agents built on OpenTelemetry. This package provides structured spans for agent invocation, tool execution, and LLM inference with context propagation and pluggable exporters. -> **Already using OpenTelemetry?** This SDK detects an existing `TracerProvider` and adds its processors to it — your spans flow to both your existing backend (Azure Monitor, OTLP collector, vendor exporter, etc.) and Agent 365. See [Integrating with existing OpenTelemetry](../../docs/integrating-with-existing-opentelemetry.md) for setup patterns and troubleshooting. +> **Already using OpenTelemetry?** This SDK detects an existing `TracerProvider` and adds its processors to it — your spans flow to your existing backend (Azure Monitor, OTLP collector, vendor exporter, etc.) and, when `ENABLE_A365_OBSERVABILITY_EXPORTER` is enabled with a configured `token_resolver`, also to the Agent 365 backend. See [Integrating with existing OpenTelemetry](../../docs/integrating-with-existing-opentelemetry.md) for setup patterns and troubleshooting. ## Installation