Skip to content

feat(Tracing): add agent (invoke_agent) and tool (execute_tool) spans#1

Closed
cdaguerre wants to merge 6 commits into
csanchezworldia:feat/instrumentation_iafrom
worldia:feature/ai-agent-tracing
Closed

feat(Tracing): add agent (invoke_agent) and tool (execute_tool) spans#1
cdaguerre wants to merge 6 commits into
csanchezworldia:feat/instrumentation_iafrom
worldia:feature/ai-agent-tracing

Conversation

@cdaguerre

Copy link
Copy Markdown

Targets worldia#86 (feat/instrumentation_ia), building on its platform-level tracing.

What

Adds the two higher levels of the Symfony AI stack to the OTel instrumentation, so a single agent run produces a complete trace tree:

invoke_agent <name>        (INTERNAL)   ← TracingAgent      (this PR)
 ├─ chat <system>          (CLIENT)     ← TracingPlatform   (#86)
 ├─ execute_tool <name>    (INTERNAL)   ← TracingToolbox    (this PR)
 └─ chat <system>          (CLIENT)     ← TracingPlatform   (#86)
  • TracingAgent decorates ai.agent-tagged services and wraps AgentInterface::call() in an invoke_agent span (gen_ai.operation.name=invoke_agent, gen_ai.agent.name). INTERNAL kind — the agent orchestrates in-process and parents the platform's CLIENT chat spans.
  • TracingToolbox decorates ai.toolbox-tagged services and wraps ToolboxInterface::execute() in an execute_tool span (gen_ai.operation.name=execute_tool, gen_ai.tool.name, gen_ai.tool.call.id).
  • AIAgentTracingCompilerPass mirrors AIPlatformTracingCompilerPass: abstract services in config/tracing/ai.php, decoration keyed on the ai.agent / ai.toolbox tags at priority -512 (outermost, so the span covers inner decorators including the profiler's traceable wrappers at -1024).

Gated by the existing tracing.ai.enabled toggle — no new config node. symfony/ai-agent added as a require-dev / suggest optional dependency, consistent with how symfony/ai-platform is declared.

Conventions / notes

  • Attribute names use OpenTelemetry\SemConv\TraceAttributes, values use TraceAttributeValues (the invoke_agent / execute_tool operation values live there). Worth aligning the existing TracingPlatform onto these constants too (it currently uses string literals) in a follow-up.
  • Both decorators end the span in a finally and record exceptions — synchronous calls, no deferred-result lifecycle to manage here (unlike the platform's DeferredResult).
  • Follow-up candidates (not in this PR): extract the inline gen_ai.* attributes into swappable AttributeProviders to match the rest of the bundle, and the token-usage metrics histogram discussed on feat(Tracing): add token usage for AI worldia/instrumentation-bundle#86.

Tests

19 AI tracing tests pass (tests/Tracing/AI/), including new TracingAgentTest and TracingToolboxTest (span name/kind, gen_ai attributes, OK status, exception recording, delegation). Full suite green; new files clean under PHPStan.

🤖 Generated with Claude Code

cdaguerre and others added 6 commits June 19, 2026 12:47
Extends the AI platform tracing with the two higher levels of the
Symfony AI stack, following the OTel gen_ai semantic conventions:

- TracingAgent decorates ai.agent services, emitting an INTERNAL
  `invoke_agent` span (gen_ai.agent.name) that parents the platform's
  CLIENT `chat` spans.
- TracingToolbox decorates ai.toolbox services, emitting an INTERNAL
  `execute_tool` span (gen_ai.tool.name, gen_ai.tool.call.id) per call.

Both are wired via AIAgentTracingCompilerPass, mirroring the platform
pass: abstract services in config/tracing/ai.php, decoration keyed on
the ai.agent / ai.toolbox tags at priority -512 (outermost). Attribute
names and values use OpenTelemetry\SemConv constants.

symfony/ai-agent added as a dev/suggested optional dependency.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…roviders for AI agent/tool

Aligns the agent/tool tracing decorators with the bundle's conventions:

- Inject TracerProviderInterface (via TracerAwareTrait) instead of the
  static Tracing facade. The static accessor is intended for application
  code outside the bundle; in-bundle services use constructor injection.
- Extract the inline gen_ai.* attributes into AgentAttributeProvider /
  ToolAttributeProvider behind interfaces registered in semconv.php, so
  consumers can override them (matching the other instrumentations).

Attribute names/values use OpenTelemetry\SemConv constants.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…form calls

Adds an opt-in (metrics.ai.enabled) MeteringPlatform decorator that records
the OTel gen_ai semconv histogram gen_ai.client.token.usage, split by
gen_ai.token.type (input/output) with gen_ai.system / gen_ai.request.model /
gen_ai.operation.name attributes.

Mirrors the tracing platform wiring: an abstract service in config/metrics/ai.php
decorated onto ai.platform tagged services by AIPlatformMetricsCompilerPass.
Metrics and tracing stay independent (separate toggle, separate decorator),
each wrapping the result converter to read token usage; they compose when
both are enabled.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Inject TracerProviderInterface (via TracerAwareTrait) into TracingPlatform
  instead of the static Tracing facade.
- Extract span attributes into a swappable PlatformAttributeProvider behind an
  interface registered in semconv.php, consistent with the agent/tool/request/
  doctrine providers.
- Use OpenTelemetry\SemConv constants for the gen_ai.* attribute names
  (including the token usage attributes in TracingTokenUsageExtractor).
- Drop the nullable from TracingResultConverter::getTokenUsageExtractor():
  it always returns a wrapper, so the previous ?type tripped PHPStan.

Also resolves the pre-existing PHPStan (return.unusedType) and cs-fixer
failures on the base branch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e resolvers for AI

- TracingResultConverter gets a __destruct backstop that ends the platform
  span if the deferred result is never consumed (Span::end is idempotent, so
  it is a no-op on the normal path). Mirrors HttpClient's TracedResponse and
  addresses the original review's span-leak concern.
- Span naming moves out of the decorators into swappable
  Platform/Agent/Tool OperationNameResolver services (registered in semconv.php),
  matching the request/command/message/httpclient resolvers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…AI platform calls

Adds the second OTel gen_ai client metric (semconv): operation duration in
seconds, measured from platform invoke() to result conversion, with error.type
set when the call fails. Token usage and duration recording are centralised in
a shared AiMetricRecorder used across the metering decorator chain.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cdaguerre

Copy link
Copy Markdown
Author

Superseded by worldia#89, which targets master directly (full AI instrumentation: platform + agent + tool + metrics) so CI runs without cross-repo approval.

@cdaguerre cdaguerre closed this Jun 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant