<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Manpreet Singh</title>
    <description>The latest articles on DEV Community by Manpreet Singh (@manpreet_ss).</description>
    <link>https://dev.to/manpreet_ss</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4036752%2F8bb0a695-7a98-4c7a-97d0-f170a342ad5c.png</url>
      <title>DEV Community: Manpreet Singh</title>
      <link>https://dev.to/manpreet_ss</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/manpreet_ss"/>
    <language>en</language>
    <item>
      <title>Agents of SigNoz</title>
      <dc:creator>Manpreet Singh</dc:creator>
      <pubDate>Sun, 19 Jul 2026 16:51:15 +0000</pubDate>
      <link>https://dev.to/manpreet_ss/agents-of-signoz-4d2d</link>
      <guid>https://dev.to/manpreet_ss/agents-of-signoz-4d2d</guid>
      <description>&lt;h2&gt;
  
  
  Section 1: The Observability Gap in Autonomous Frameworks
&lt;/h2&gt;

&lt;p&gt;Agentic systems built on frameworks like LangChain or CrewAI do not execute as single linear requests. A single user prompt can trigger multiple asynchronous sub-processes: parallel tool calls, recursive self-correction loops, and delegation to sub-agents that each make independent LLM calls.&lt;/p&gt;

&lt;p&gt;Traditional structured logging assumes a request maps to a single execution path with a predictable start and end. Agent execution violates this assumption in three specific ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Asynchronous execution loops.&lt;/strong&gt; An agent can re-invoke a tool or re-query an LLM multiple times within a single logical "turn," with no fixed upper bound on iteration count. Logs record each call as an isolated event with no inherent link back to the originating decision.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-hop sub-agent delegation.&lt;/strong&gt; When one agent hands a task to another agent, the resulting call chain spans multiple processes or services. Without a shared execution context, there is no way to associate a sub-agent's actions with the parent task that spawned them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Variable token cost and latency per step.&lt;/strong&gt; LLM calls do not have fixed cost or duration. A retry, a longer context window, or a larger tool response can each independently spike cost or latency. Aggregate logs show total cost or total duration, but not which specific step in the chain caused the deviation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result: standard logs can confirm that an agent ran, but cannot answer which step failed, which step looped, or which step drove cost. This requires a data model built around causality and hierarchy, not just timestamped events. That data model is distributed tracing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Section 2: System Architecture
&lt;/h2&gt;

&lt;p&gt;The following flow shows how agent telemetry moves from generation to storage to query, using a single vertical execution path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────┐
│      Agent Runtime          │
│  (LangChain / CrewAI, etc.) │
└─────────────┬───────────────┘
              │
              ▼
┌─────────────────────────────┐
│   OpenTelemetry SDK          │
│   (span creation, context     │
│    propagation, attributes)  │
└─────────────┬───────────────┘
              │
              ▼
┌─────────────────────────────┐
│  OTLP Exporter (gRPC/HTTP)   │
└─────────────┬───────────────┘
              │
              ▼
┌─────────────────────────────┐
│  OpenTelemetry Collector      │
│  (receive, batch, process)   │
└─────────────┬───────────────┘
              │
              ▼
┌─────────────────────────────┐
│      ClickHouse               │
│  (columnar store: traces,     │
│   logs, metrics)               │
└─────────────┬───────────────┘
              │
              ▼
┌─────────────────────────────┐
│     SigNoz Dashboards          │
│  Trace Explorer / Logs /       │
│  Metrics / Service Map          │
└─────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;OTLP collection layer.&lt;/strong&gt; The OpenTelemetry Collector is a standalone process that receives telemetry over the OTLP protocol (gRPC or HTTP), batches it, and forwards it to a storage backend. It is decoupled from the application: the agent process only needs to know the Collector's endpoint, not any details of the downstream storage engine. In a self-hosted SigNoz deployment, the Collector listens on port &lt;code&gt;4317&lt;/code&gt; for OTLP/gRPC and port &lt;code&gt;4318&lt;/code&gt; for OTLP/HTTP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ClickHouse as the storage engine.&lt;/strong&gt; SigNoz uses ClickHouse, a columnar database, to store traces, logs, and metrics. Columnar storage is suited to observability workloads because queries typically aggregate over a small number of fields (duration, status, service name) across a large number of rows, rather than reading full rows. This gives SigNoz fast query performance on high-cardinality trace data, which is relevant given the volume of spans an agent with many sub-steps can generate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trace-to-log correlation.&lt;/strong&gt; Every span generated by the OpenTelemetry SDK carries a trace ID and span ID. If the agent's logging output is also instrumented to include these IDs, SigNoz can associate log lines with the exact span during which they were emitted. This allows a query to start from a specific span in a trace (for example, a slow tool call) and retrieve only the log lines emitted during that span's execution window, rather than searching logs by timestamp across the entire trace.&lt;/p&gt;




&lt;h2&gt;
  
  
  Section 3: Setting Up Self-Hosted SigNoz
&lt;/h2&gt;

&lt;p&gt;Self-hosted SigNoz is currently installed and managed through &lt;strong&gt;Foundry&lt;/strong&gt;, a CLI (&lt;code&gt;foundryctl&lt;/code&gt;) that generates and deploys the full stack from a single declarative configuration file. The previous &lt;code&gt;install.sh&lt;/code&gt; script and the bundled &lt;code&gt;deploy/&lt;/code&gt; Docker Compose files are deprecated as of SigNoz v0.130.0 and are no longer maintained.&lt;/p&gt;

&lt;p&gt;Install &lt;code&gt;foundryctl&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://signoz.io/foundry.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a minimal &lt;code&gt;casting.yaml&lt;/code&gt; for a single-machine Docker Compose deployment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Installation&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;signoz&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deployment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;flavor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;compose&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deploy the stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;foundryctl cast &lt;span class="nt"&gt;-f&lt;/span&gt; casting.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single command validates prerequisites, renders the Docker Compose files into a &lt;code&gt;pours/&lt;/code&gt; directory, and starts the containers, which include SigNoz, the OpenTelemetry Collector, ClickHouse, ClickHouse Keeper, and PostgreSQL.&lt;/p&gt;

&lt;p&gt;Once running, the deployment exposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Port &lt;code&gt;8080&lt;/code&gt;&lt;/strong&gt; — the SigNoz UI (Trace Explorer, Logs, Metrics, Service Map)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port &lt;code&gt;4317&lt;/code&gt;&lt;/strong&gt; — OTLP gRPC ingestion&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port &lt;code&gt;4318&lt;/code&gt;&lt;/strong&gt; — OTLP HTTP ingestion&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you prefer to manage the containers directly instead of letting &lt;code&gt;cast&lt;/code&gt; handle deployment, the equivalent two-step process is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;foundryctl gauge &lt;span class="nt"&gt;-f&lt;/span&gt; casting.yaml   &lt;span class="c"&gt;# validate prerequisites&lt;/span&gt;
foundryctl forge &lt;span class="nt"&gt;-f&lt;/span&gt; casting.yaml   &lt;span class="c"&gt;# generate compose files&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;pours/deployment &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At least 4GB of memory allocated to Docker is required. Once the containers are healthy, the UI is reachable at &lt;code&gt;http://localhost:8080&lt;/code&gt;, and your agent's OTLP exporter should point at &lt;code&gt;http://localhost:4317&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Section 4: Trace Tree Causality
&lt;/h2&gt;

&lt;p&gt;A single agent turn produces one root span with child spans representing each sub-step. Below is the flat causal breakdown for one example execution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Trace ID: 7f3a9c21
Root Span: user_prompt_handler          duration: 4.20s

Span: llm_call_planning                  duration: 0.82s
  attributes: tokens_in=512, tokens_out=128

Span: vector_db_search                   duration: 0.14s
  attributes: top_k=5, collection=docs_v2

Span: agent_tool_execution               duration: 2.10s
  attributes: tool_name=web_search, status=success

Span: http_request_external              duration: 1.90s
  parent_span: agent_tool_execution
  attributes: status_code=200

Span: llm_call_final_answer               duration: 1.10s
  attributes: tokens_in=2048, tokens_out=340
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each span records its own duration and attributes independently. The &lt;code&gt;parent_span&lt;/code&gt; field establishes the hierarchy without requiring nested formatting: &lt;code&gt;http_request_external&lt;/code&gt; is a child of &lt;code&gt;agent_tool_execution&lt;/code&gt;, meaning the external HTTP call occurred as part of that tool's execution and its duration is included within the tool span's total duration. This structure allows direct identification of which specific span accounts for the majority of total trace duration or cost, rather than inferring it from aggregate values.&lt;/p&gt;




&lt;h2&gt;
  
  
  Section 5: Conceptual OpenTelemetry Implementation
&lt;/h2&gt;

&lt;p&gt;The example below initializes an OpenTelemetry tracer, configures it to export to a local SigNoz OTel Collector on port &lt;code&gt;4317&lt;/code&gt;, and wraps each sub-step of an agent's execution — retrieval, tool call, and generation — in its own span.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# filename: agent.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;opentelemetry&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;opentelemetry.sdk.trace&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TracerProvider&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;opentelemetry.sdk.trace.export&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BatchSpanProcessor&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;opentelemetry.exporter.otlp.proto.grpc.trace_exporter&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OTLPSpanExporter&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;opentelemetry.sdk.resources&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Resource&lt;/span&gt;

&lt;span class="c1"&gt;# 1. Setup OpenTelemetry Resource (The Service Name)
&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;service.name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent-researcher-v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;service.version&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;# 2. Configure the Tracer to send data to SigNoz (Localhost)
&lt;/span&gt;&lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_tracer_provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TracerProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;tracer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_tracer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent.main&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Send traces to SigNoz OTel Collector on port 4317
&lt;/span&gt;&lt;span class="n"&gt;otlp_exporter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OTLPSpanExporter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:4317&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;insecure&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_tracer_provider&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;add_span_processor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BatchSpanProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;otlp_exporter&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_agent_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_prompt&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Start a root span for the entire user interaction
&lt;/span&gt;    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_as_current_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent_execution_workflow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user.prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Thinking about: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_prompt&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Simulate a sub-step (Tool Call)
&lt;/span&gt;        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_as_current_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool_search_vector_db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="c1"&gt;# Your vector DB logic here
&lt;/span&gt;            &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Found 3 relevant documents&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Simulate LLM Generation
&lt;/span&gt;        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_as_current_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llm_generate_response&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llm.model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llm.tokens_used&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;run_agent_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Why is my Kubernetes pod crashing?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the required packages before running this script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap &lt;span class="nt"&gt;--action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;opentelemetry-distro&lt;/code&gt; provides the SDK and a mechanism to auto-configure common OpenTelemetry defaults. &lt;code&gt;opentelemetry-exporter-otlp&lt;/code&gt; installs the OTLP exporters, including the gRPC exporter used in this script (&lt;code&gt;opentelemetry-exporter-otlp-proto-grpc&lt;/code&gt;). &lt;code&gt;opentelemetry-bootstrap&lt;/code&gt; inspects installed dependencies and adds any relevant auto-instrumentation packages for libraries already present in the environment.&lt;/p&gt;

&lt;p&gt;Implementation notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each &lt;code&gt;tracer.start_as_current_span(...)&lt;/code&gt; call opens a new span under whatever span is currently active, which is what produces the parent-child hierarchy shown in Section 4.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;span.set_attribute&lt;/code&gt; attaches structured, queryable metadata directly to a span — &lt;code&gt;llm.model&lt;/code&gt;, &lt;code&gt;llm.tokens_used&lt;/code&gt;, &lt;code&gt;user.prompt&lt;/code&gt; — without needing to parse log text later.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;span.add_event&lt;/code&gt; records a discrete point-in-time event inside a span, useful for marking a state change (such as "documents found") without opening a new span.&lt;/li&gt;
&lt;li&gt;The same pattern extends to real tool dispatch logic, vector database clients, and LLM SDK calls: replace the placeholder logic inside each &lt;code&gt;with&lt;/code&gt; block with the actual function call, and set attributes relevant to that step.&lt;/li&gt;
&lt;li&gt;Frameworks such as LangChain and CrewAI expose callback interfaces that can invoke this instrumentation automatically at each execution step, without modifying core framework logic.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Section 6: Why This Matters for Agentic Workflows Specifically
&lt;/h2&gt;

&lt;p&gt;SigNoz is built as an OpenTelemetry-native platform, meaning it consumes OTLP data directly with no proprietary translation layer. The core platform is open source under the MIT license, with only the &lt;code&gt;ee/&lt;/code&gt; directory (enterprise-only features) under a separate license. This has two direct implications for agent instrumentation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No vendor lock-in.&lt;/strong&gt; Instrumentation written with the standard OpenTelemetry SDK, as shown in Section 5, is portable to any OTLP-compatible backend. Switching backends later requires changing an exporter endpoint, not rewriting instrumentation code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full data ownership for sensitive agent data.&lt;/strong&gt; Self-hosting keeps prompts, tool outputs, and retrieved documents inside your own infrastructure, since the Collector and ClickHouse both run on infrastructure you control.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SigNoz's more recent development has also moved directly toward agent-facing tooling: the project maintains an MCP (Model Context Protocol) server that exposes traces, logs, metrics, and service topology to coding agents such as Claude Code, allowing an agent to query production telemetry as part of its own debugging workflow. This closes the loop described in this post: not only can you trace what an AI agent does, you can also let an AI agent query that same trace data to diagnose issues directly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Section 7: Building It in the Hackathon
&lt;/h2&gt;

&lt;p&gt;Everything above is the blueprint: the trace shape, the Collector pipeline, the span design, and the exact install and instrumentation commands. The Agents of SigNoz hackathon main track is where this gets built for real — a live agent, fully instrumented, traces flowing into a self-hosted SigNoz instance, with dashboards showing exactly where latency and cost are going per tool call.&lt;/p&gt;

&lt;p&gt;If your agents have ever run up a bill you couldn't explain, or failed quietly in a way you couldn't trace back, this is the architecture that answers those questions directly.&lt;/p&gt;




&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;SigNoz Self-Host Docker Install Guide: &lt;a href="https://signoz.io/docs/install/docker/" rel="noopener noreferrer"&gt;https://signoz.io/docs/install/docker/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SigNoz OpenTelemetry Python Instrumentation Guide: &lt;a href="https://signoz.io/docs/instrumentation/opentelemetry-python/" rel="noopener noreferrer"&gt;https://signoz.io/docs/instrumentation/opentelemetry-python/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SigNoz GitHub Repository: &lt;a href="https://github.com/SigNoz/signoz" rel="noopener noreferrer"&gt;https://github.com/SigNoz/signoz&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
  </channel>
</rss>
