Instrumenting Agentic AI with OpenTelemetry

Overview

Agentic AI frameworks such as CrewAI, AutoGen, and LangGraph orchestrate multiple LLM interactions, tool invocations, and autonomous decision-making across dynamic workflows. Unlike traditional applications with deterministic execution paths, agentic systems generate non-deterministic traces where the same input can result in different reasoning paths and tool sequences. This makes conventional observability insufficient for understanding and investigating AI-driven operations.

This blog demonstrates how OpenTelemetry (OTel) instrumentation, OTLP, and Elastic APM can be used to capture distributed traces, correlate AI decisions with tool execution, and provide end-to-end visibility into autonomous agent workflows for security analysis and post-incident investigations.

End-to-End Pipeline

The architecture above illustrates the full agentic red-team pipeline from CrewAI’s manager and specialist agent runtime through orchestration, Open telemetry sanitization, Elastic-backed observability, and ES|QL-driven detection.

Agentic AI infrastructure:

The Manager Agent coordinates workflow execution by creating an execution plan and delegating each task to the appropriate specialized agent based on its defined role.

The Manager Agent orchestrates the workflow by planning execution and assigning tasks to specialized agents.

Agents Span:
Each agent action emits three span types:

  • Crewai.task.execute
  • Tool.invocation
  • llm.completion

Span Schema Design

Span

Purpose

Key attributes

crewai.task.execute

Parent span, one per delegated task

ai.agent.role, crewai.task.id

tool.invocation

One per tool call

ai.tool.name, redteam.attack_chain_stage, redteam.target_host

llm.completion

One per model call

gen_ai.system, gen_ai.request.model, gen_ai.usage.input_tokens, gen_ai.usage.output_tokens

Orchestration Layer:

Coordinates planning, task routing, approvals, and state management across all agents.

The generated execution plan defines the tool sequence and parameters that guide autonomous agent execution.

Detailed Setup Steps for OpenTelemetry-Based Agent Observability

A. Configure the Agent Runtime for OpenTelemetry

Install the required OpenTelemetry SDK and OTLP exporter.

pip install \

opentelemetry-api \

opentelemetry-sdk \

opentelemetry-exporter-otlp-proto-grpc

Configure the environment variables that enable telemetry export.

ENABLE_TRACING=true

OTEL_EXPORTER_OTLP_ENDPOINT=http://<SIEM.IP>:8200

OTEL_SERVICE_NAME=redteam_crew

Within the application, initialize the OpenTelemetry TracerProvider. Depending on the tracing configuration, the runtime either exports spans directly to Elastic APM or disables telemetry using a NullExporter.

This configuration establishes a centralized tracing provider for every CrewAI agent.

B. Instrument CrewAI Agents with OpenTel:

Every manager and specialist agent should obtain a tracer instance.

from opentelemetry import trace

tracer = trace.get_tracer(“crewai.redteam.agent”)

Each agent operation is wrapped inside a tracing span.

tracer.start_as_current_span(“tool.invocation”):

This automatically generates distributed traces for every agent execution.

Instrument Tool Executions with OpenTel

Every tool invocation should be recorded as a dedicated OpenTelemetry span.

tracer.start_as_current_span(

   f”tool.invocation.{tool_name}”

) as span:

#Attach AI-specific metadata to every execution.

span.set_attribute(

   “gen_ai.tool.name”,

   tool_name

)

span.set_attribute(

   “gen_ai.agent.role”,

   agent_role

)

 

span.set_attribute(

   “redteam.attack_chain_stage”,

   get_current_stage()

)

span.set_attribute(

   “redteam.target_host”,

   params.get(“target”,”unknown”)

)

#If execution succeeds, store execution metadata.

span.set_attribute(

   “tool.result.status”,

   “success”

)

 

span.set_attribute(

   “tool.result.size_bytes”,

   len(str(result))

)

# If an exception occurs, record the error inside the trace.

span.record_exception(e)

 

span.set_status(

   Status(StatusCode.ERROR)

)

The redteam.attack_chain_stage attribute is particularly valuable because it associates every span with its corresponding phase of the attack workflow, enabling end-to-end correlation during investigations.

Configuring Elastic APM & OpenTelemetry Pipeline:

A.Install Elastic APM Server

curl -L -O https://artifacts.elastic.co/downloads/apm-server/apm-server-8.19.8-amd64.deb

B. Open the configuration file

sudo nano /etc/apm-server/apm-server.yml

C. Key configuration settings to modify

# Set the host to listen on all interfaces, port 8200

apm-server:

  host: “0.0.0.0:8200”

# Output to Elasticsearch

output.elasticsearch:

  hosts: [“https://your-elasticsearch-host:9200”]

  username: “elastic”

  password: “your_password”

  ssl:

    verification_mode: “full”

    certificate_authorities: [“/path/to/ca.crt”]

D.Access Kibana Dashboard

Navigate to Observability → Application/APM → Traces → Transactions → Logs will be present

Detecting an Agentic AI Red Team Attack with OpenTelemetry and Elastic APM

Stage 1 – Reconnaissance

MITRE ATT&CK: Reconnaissance (TA0043)

The AI red team agent begins by autonomously profiling the target environment. It identifies reachable hosts, exposed services, and network topology to understand the attack surface and determine the most promising path toward the Active Directory infrastructure.

ES|QL

FROM AItelemetry

| WHERE service.name == “redteam_crew”

AND labels.attack_stage == “Recon”

| KEEP span.id,

       trace.id

Filters OpenTelemetry spans tagged with the Recon attack stage to identify the initial reconnaissance phase and retrieve the corresponding trace.id and span.id for correlation.

Stage 2 – LDAP Enumeration

MITRE ATT&CK: Discovery – T1087.002 (Domain Account Discovery)

After locating the Active Directory environment, the agent enumerates domain users, groups, and mail-enabled accounts through LDAP. The gathered information helps the agent identify potential targets and prepare for subsequent authentication attempts.

ES|QL

FROM AItelemetry

| WHERE service.name == “redteam_crew”

  AND span.name == “EmailEnumTool”

| KEEP span.id,

       trace.id,

       service.name,

       service.framework.name,

       span.name,

       labels.command,

       labels.final_output,

       event.outcome

Retrieves telemetry for the EmailEnumTool span, exposing the executed LDAP enumeration command, tool output, and execution status captured within the distributed trace.

Stage 3 – Initial Access

MITRE ATT&CK: Credential Access – T1110.001 (Brute Force)

Using the discovered user accounts, the agent performs a brute-force authentication attack by systematically attempting multiple password combinations against a target account. Upon identifying valid credentials, the agent gains initial access to the environment and proceeds with the next stage of the attack chain.

ES|QL

FROM AItelemetry

| WHERE service.name == “redteam_crew”

  AND span.name == “PasswordSprayTool”

| KEEP span.id,

       trace.id,

       service.name,

       service.framework.name,

       labels.command,

       event.outcome

Filters telemetry generated by the brute-force authentication tool, capturing command execution details and the authentication outcome for the initial access attempt.

Stage 4 – Persistence

MITRE ATT&CK: Persistence – T1556.006 (Shadow Credentials)

Following successful authentication, the agent establishes persistence by injecting shadow credentials into the compromised account. This certificate-based technique enables future authentication without modifying the user’s password, providing a stealthier long-term access mechanism.

ES|QL

FROM AItelemetry

| WHERE observer.type == “apm-server”

  AND labels.user_request RLIKE “Inject shadow credentials.*”

  AND trace.id == “e88e7a3ce9ee80408150f46d8777e6ae”

Matches AI telemetry containing shadow credential injection requests, enabling validation of persistence activity through trace-level correlation.

Stage 5 – Credential Access

MITRE ATT&CK: Credential Access – T1003.006 (DCSync)

With elevated privileges available, the agent attempts to retrieve Active Directory credential material using the directory replication mechanism. This enables access to credential data for privileged accounts without directly interacting with those accounts.

ES|QL

FROM AItelemetry

| WHERE service.name == “redteam_crew”

  AND span.name == “DCSyncTool”

  AND trace.id == “a218cb0e149774819063f48950064cc3”

  AND span.id == “ee409b9583834c8f”

Identifies the DCSyncTool execution span using the associated trace.id and span.id, confirming the credential replication activity within the attack workflow.

Stage 6 – Lateral Movement

MITRE ATT&CK: Lateral Movement – T1550.002 (Pass-the-Hash)

Instead of relying on plaintext passwords, the agent reuses previously obtained authentication material to access additional systems. This enables lateral movement across the environment while reducing the need for repeated credential collection.

ES|QL

FROM AItelemetry

| WHERE service.name == “redteam_crew”

  AND span.name IN (“PassTheHashTool”, “pth_exec”)

Filters telemetry for PassTheHashTool and pth_exec spans to detect hash-based authentication used during lateral movement

Stage 7 – Lateral Movement (Execution)

MITRE ATT&CK: Execution – T1059.001 (PowerShell)

Then the agent pivots from the Domain Controller to the Exchange server using remote PowerShell execution. Administrative commands executed through PowerShell enable the agent to continue operations on the target system and progress toward its objective.

ES|QL

FROM AItelemetry

| WHERE winlog.channel == “Microsoft-Windows-PowerShell/Operational”

  AND event.code == “4104”

  AND powershell.file.script_block_text RLIKE “\\$secpass = ConvertTo-SecureString ‘John@123’ -AsPlainText -Force.*”

Queries PowerShell Operational logs (Event ID 4104) to identify remote PowerShell script execution associated with the lateral movement phase.

Stage 8 – Persistence / Collection Objective

MITRE ATT&CK: Collection – T1114 (Email Collection)

After reaching the Exchange environment, the agent completes its objective by establishing a covert mail-handling rule that provides continued access to organizational email traffic. This persistence mechanism allows ongoing collection while blending into normal Exchange operations.

ES|QL

FROM AItelemetry

| WHERE service.name == “redteam_crew”

  AND labels.final_output RLIKE “.*Journal rule.*”

| KEEP span.id,

       trace.id,

       service.name,

Filters AI telemetry for Journal Rule creation events by matching the final execution output, confirming successful completion of the Exchange persistence objective.

Common Investigation Pitfalls:

Correlate span-to-span, not span-in-isolation : a single tool.invocation means nothing; the sequence across llm.completion → tool.invocation → llm.completion is the signal

Watch for identical reasoning across sessions : repeated gen_ai.prompt hashes or near-identical tool call sequences across trace IDs indicate scripted, not autonomous, behavior

Track token/cost anomalies as behavioral drift — sudden spikes in gen_ai.usage.tokens per span often precede a pivot or escalation attempt

Never trust truncated tool output : full tool.output capture is the only way to catch injected instructions or malicious payloads hidden mid-response

Alert on attack-chain-stage sequence violations : an agent jumping from Recon straight to Credential Access without Enumeration in between is itself a detection

Conclusion

Watching what an AI agent does isn’t enough , you need to see the reasoning behind it, step by step, to know whether it behaved the way it should have. This pipeline captures that full picture, from the moment an agent starts investigating a target to the last action it takes deep inside the network, and ties it all together so the whole story can be reconstructed from one trace. That’s the real shift here: instead of piecing together scattered logs after the fact, you get one connected timeline showing exactly how an attack chain unfolded.