<?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: Syed Kabeer Ali</title>
    <description>The latest articles on DEV Community by Syed Kabeer Ali (@syed_kabeerali_65ed7d04d).</description>
    <link>https://dev.to/syed_kabeerali_65ed7d04d</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%2F3961215%2F2477a176-9f97-4fd0-82aa-05486c26ac62.png</url>
      <title>DEV Community: Syed Kabeer Ali</title>
      <link>https://dev.to/syed_kabeerali_65ed7d04d</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/syed_kabeerali_65ed7d04d"/>
    <language>en</language>
    <item>
      <title>How I Debugged an AI Incident Response Pipeline Using OpenTelemetry and SigNoz (And the 5 Mistakes That Nearly Broke My Hackathon Project)</title>
      <dc:creator>Syed Kabeer Ali</dc:creator>
      <pubDate>Sat, 18 Jul 2026 11:46:36 +0000</pubDate>
      <link>https://dev.to/syed_kabeerali_65ed7d04d/how-i-debugged-an-ai-incident-response-pipeline-using-opentelemetry-and-signoz-and-the-5-mistakes-58h6</link>
      <guid>https://dev.to/syed_kabeerali_65ed7d04d/how-i-debugged-an-ai-incident-response-pipeline-using-opentelemetry-and-signoz-and-the-5-mistakes-58h6</guid>
      <description>&lt;p&gt;Three days before the hackathon submission deadline, my local React dashboard looked absolutely flawless.&lt;/p&gt;

&lt;p&gt;I would trigger a simulated brute-force attack, the UI would instantly flash a red alert, the background Python engine would spin up a Google Gemini instance to analyze the threat vector, calculate a 94% confidence score, and cleanly trigger a simulated firewall tool to drop the offending IP address.&lt;/p&gt;

&lt;p&gt;From the outside, it was a software developer's dream. The frontend updated perfectly in real-time. But when I opened my self-hosted SigNoz instance to verify the backend data, the Trace Explorer was a complete ghost town.&lt;/p&gt;

&lt;p&gt;That was the exact moment I realized a foundational truth of modern software engineering: building an autonomous AI agent is only half the problem—making its internal reasoning transparent, observable, and auditable is the actual challenge.&lt;/p&gt;

&lt;p&gt;This is the raw engineering diary of how I built VigilTrace—an autonomous AI Incident Commander—and the exact telemetry roadblocks, Windows-specific traps, and OpenTelemetry architectural bugs I had to fight through to make it truly observable.&lt;/p&gt;

&lt;p&gt;What I Was Building: VigilTrace&lt;br&gt;
The design philosophy behind VigilTrace is simple: If you can't observe your AI agents, you don't own them.&lt;/p&gt;

&lt;p&gt;Instead of creating a black-box AI script that blindly triggers a shell command when a log drops, I architected an interactive pipeline that treats AI analysis as a multi-stage, regulated security investigation. The workflow operates as follows:&lt;/p&gt;

&lt;p&gt;Plaintext&lt;br&gt;
Server Simulator ──&amp;gt; Access Logs ──&amp;gt; Incident Commander (Python)&lt;br&gt;
                                                │&lt;br&gt;
  SigNoz UI &amp;lt;── OTLP Collector &amp;lt;── OpenTelemetry SDK &amp;lt;── Gemini AI&lt;br&gt;
                                                │&lt;br&gt;
                         [Blocklist.json] &amp;lt;── Firewall Simulator&lt;br&gt;
Every single link in this chain—from the raw token usage inside the Gemini LLM step to the file I/O latency inside the FirewallSimulator—had to emit structured telemetry packets over gRPC to a local OpenTelemetry Collector monitored via SigNoz.&lt;/p&gt;

&lt;p&gt;Mistake #1: The Phantom Metrics (Why create_gauge Left Me in the Dark)&lt;br&gt;
My first major roadblock was system metrics. I wanted to capture real-time resource spikes (cpu_usage and memory_usage) on the machine while the AI engine was chewing through dense security logs.&lt;/p&gt;

&lt;p&gt;I spun up a background metrics daemon in Python and wrote what I assumed was standard OpenTelemetry code:&lt;/p&gt;

&lt;p&gt;Python&lt;/p&gt;

&lt;h1&gt;
  
  
  THE BUGGY CODE THAT PRODUCED ZERO DATA IN SIGNOZ
&lt;/h1&gt;

&lt;p&gt;from opentelemetry import metrics&lt;/p&gt;

&lt;p&gt;meter = metrics.get_meter("system-monitor")&lt;br&gt;
cpu_gauge = meter.create_gauge(&lt;br&gt;
    name="system.cpu.utilization",&lt;br&gt;
    description="Tracks CPU load metrics"&lt;br&gt;
)&lt;/p&gt;

&lt;h1&gt;
  
  
  Polling loop trying to push values directly
&lt;/h1&gt;

&lt;p&gt;while True:&lt;br&gt;
    cpu_gauge.set(get_current_cpu_percent())&lt;br&gt;
    time.sleep(5)&lt;br&gt;
The script executed without crashing. No console errors, no warnings. Yet, when I checked the Metrics Explorer inside SigNoz, the dropdown menu for system.cpu.utilization simply did not exist.&lt;/p&gt;

&lt;p&gt;The Fix&lt;br&gt;
After diving into the official OpenTelemetry documentation, I realized a crucial architectural detail I had missed: metrics in OTel are primarily driven by a pull-based or asynchronous callback architecture to avoid blocking runtime threads. Synchronous gauges are often mishandled or dropped depending on how the OTLP exporter state is initialized.&lt;/p&gt;

&lt;p&gt;I had to completely refactor the metrics engine to use an Observable Gauge, passing a dedicated callback function that the SDK invokes automatically on its own collection cycle:&lt;/p&gt;

&lt;p&gt;Python&lt;/p&gt;

&lt;h1&gt;
  
  
  THE WORKING FIX: Migrating to an Asynchronous Observable Gauge
&lt;/h1&gt;

&lt;p&gt;from opentelemetry import metrics&lt;/p&gt;

&lt;p&gt;meter = metrics.get_meter("system-monitor")&lt;/p&gt;

&lt;p&gt;def get_cpu_callback(options):&lt;br&gt;
    # The SDK calls this function natively on every collection interval&lt;br&gt;
    return [metrics.Observation(get_current_cpu_percent())]&lt;/p&gt;

&lt;p&gt;meter.create_observable_gauge(&lt;br&gt;
    name="system.cpu.utilization",&lt;br&gt;
    callbacks=[get_cpu_callback],&lt;br&gt;
    description="Tracks real-time system CPU load percentage"&lt;br&gt;
)&lt;br&gt;
As soon as I spun up the script with the callback logic, the OTLP exporter began generating beautiful time-series line charts directly within the SigNoz metrics dashboard. Lesson learned: don't push data when the OpenTelemetry SDK wants to pull it.&lt;/p&gt;

&lt;p&gt;Mistake #2: The Broken Waterfall (Flat Spans vs. Nested Contexts)&lt;br&gt;
When I finally got my first trace batches to stream into the SigNoz Trace Explorer, the structural layout looked entirely wrong. Instead of a single, coherent incident timeline showing a clean hierarchy, SigNoz rendered ten completely disconnected, flat timelines:&lt;/p&gt;

&lt;p&gt;Plaintext&lt;br&gt;
❌ WHAT I SAW ORIGINALLY:&lt;br&gt;
Investigation (Trace ID: a1b2...)&lt;br&gt;
Recommendation (Trace ID: c3d4...)&lt;br&gt;
Firewall Execution (Trace ID: e5f6...)&lt;br&gt;
Because every stage was being drawn as a root span, I couldn't track the lineage of an incident. If the execution failed at the firewall stage, I had no clean visual way to trace it back to the specific LLM confidence calculation that triggered it.&lt;/p&gt;

&lt;p&gt;The Fix&lt;br&gt;
The bug was a classic context propagation error. In Python, if you use a standard async loop or call distinct functions across different files, the active OpenTelemetry tracing context can easily drop out if you don't explicitly pass it or set it inside the current execution context thread.&lt;/p&gt;

&lt;p&gt;I refactored the parent loop inside incident_commander.py to leverage the Python context manager, forcing child tasks to explicitly yield inside the parent scope:&lt;/p&gt;

&lt;p&gt;Python&lt;/p&gt;

&lt;h1&gt;
  
  
  THE FIX: Ensuring tight nesting of distributed trace spans
&lt;/h1&gt;

&lt;p&gt;from opentelemetry import trace&lt;/p&gt;

&lt;p&gt;tracer = trace.get_tracer(&lt;strong&gt;name&lt;/strong&gt;)&lt;/p&gt;

&lt;p&gt;def run_incident_workflow(incident_data):&lt;br&gt;
    # Establish the overarching parent context span&lt;br&gt;
    with tracer.start_as_current_span("AI-Incident-Investigation") as parent_span:&lt;br&gt;
        parent_span.set_attribute("incident.id", incident_data["id"])&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    # Explicitly passing structural execution tokens down the pipeline
    analyze_threat_vector(incident_data)
    execute_mitigation_workflow(incident_data)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;def execute_mitigation_workflow(incident_data):&lt;br&gt;
    # This automatically detects and nests itself under the active parent span&lt;br&gt;
    with tracer.start_as_current_span("FirewallSimulator.block_ip") as child_span:&lt;br&gt;
        child_span.set_attribute("tool.action", "drop_packet")&lt;br&gt;
        # Simulating file I/O blocklist updates&lt;br&gt;
        write_to_blocklist(incident_data["attacker_ip"])&lt;br&gt;
This tiny adjustments paid off instantly. The next time I fired up the app, SigNoz rendered a gorgeous nested flamegraph waterfall:&lt;/p&gt;

&lt;p&gt;Plaintext&lt;br&gt;
✅ THE CORRECT HIERARCHY IN SIGNOZ:&lt;br&gt;
AI-Incident-Investigation [Trace ID: 7fa98c...]&lt;br&gt;
├── Analyze_Threat_Vector (LLM Ingestion)&lt;br&gt;
└── Execute_Mitigation_Workflow&lt;br&gt;
    └── FirewallSimulator.block_ip&lt;br&gt;
Mistake #3: The Windows Path Trap (spawn python3 ENOENT)&lt;br&gt;
Because I develop across different operating environments, I cloned my Next.js/Node.js telemetry aggregator backend onto my secondary Windows workstation to test stability.&lt;/p&gt;

&lt;p&gt;I hit npm run dev, clicked the execution button on the frontend interface, and my Node runtime exploded with this raw error block:&lt;/p&gt;

&lt;p&gt;Plaintext&lt;br&gt;
events.js:377&lt;br&gt;
      throw er; // Unhandled 'error' event&lt;br&gt;
      ^&lt;br&gt;
Error: spawn python3 ENOENT&lt;br&gt;
    at Process.ChildProcess._handle.onread (internal/child_process.js:245:12)&lt;br&gt;
    at onErrorNT (internal/child_process.js:482:16)&lt;br&gt;
The issue was painfully obvious yet highly disruptive: my Node core layer was hardcoded to invoke the background telemetry workers via a direct shell invocation to python3. While Linux and macOS environments default to python3, standard Windows installations map the runtime binary path explicitly to python.&lt;/p&gt;

&lt;p&gt;The Fix&lt;br&gt;
I refactored the application initialization handler to evaluate the host operating system dynamically via process.platform before spinning up the background telemetry workers:&lt;/p&gt;

&lt;p&gt;TypeScript&lt;br&gt;
import { spawn } from 'child_process';&lt;br&gt;
import os from 'os';&lt;/p&gt;

&lt;p&gt;// Dynamically bridge OS path execution discrepancies&lt;br&gt;
const pythonExecutable = process.platform === 'win32' ? 'python' : 'python3';&lt;/p&gt;

&lt;p&gt;const runTelemetryWorker = () =&amp;gt; {&lt;br&gt;
  const workerProcess = spawn(pythonExecutable, ['telemetry_daemon.py'], {&lt;br&gt;
    env: { ...process.env, PYTHONUNBUFFERED: '1' }&lt;br&gt;
  });&lt;/p&gt;

&lt;p&gt;workerProcess.stderr.on('data', (data) =&amp;gt; {&lt;br&gt;
    console.error(&lt;code&gt;[WORKER ERROR]: ${data.toString()}&lt;/code&gt;);&lt;br&gt;
  });&lt;br&gt;
};&lt;br&gt;
This brought cross-platform parity to the project, allowing the Node orchestrator to coordinate cleanly with the Python OpenTelemetry SDK regardless of the host OS architecture.&lt;/p&gt;

&lt;p&gt;Mistake #4: The React UI Illusion (The Dashboard That Lied)&lt;br&gt;
The biggest psychological trap during development was the fidelity of my own React application UI.&lt;/p&gt;

&lt;p&gt;Because I was using standard React state objects (useState) to pass event records locally between my components, the user interface looked like an absolute engineering marvel. Spans lit up sequentially, data tables populated flawlessly, and progress bars moved perfectly.&lt;/p&gt;

&lt;p&gt;But my frontend state was completely insulated from reality. When I purposefully pulled down my Docker instances of SigNoz, my React app still looked like it was working perfectly. The frontend was happily lying to me, masking the fact that the underlying OTLP connection was completely severed and exporting absolutely zero enterprise telemetry data.&lt;/p&gt;

&lt;p&gt;The Lesson Learned&lt;br&gt;
A healthy user interface does not mean a healthy telemetry pipeline.&lt;/p&gt;

&lt;p&gt;I fundamentally altered my testing validation loop. I decoupled my reliance on the client-side state. I wrote a dedicated integration command to hit the backend directly:&lt;/p&gt;

&lt;p&gt;Bash&lt;/p&gt;

&lt;h1&gt;
  
  
  Explicitly force clean dependency instantiation and trigger an isolated log event
&lt;/h1&gt;

&lt;p&gt;npm install&lt;br&gt;
curl -X POST &lt;a href="http://localhost:3000/api/simulate-attack" rel="noopener noreferrer"&gt;http://localhost:3000/api/simulate-attack&lt;/a&gt; -d '{"type": "brute_force"}'&lt;br&gt;
From that point forward, my source of truth wasn't my shiny React UI—it was the SigNoz Services and Traces explorer panes. If a trace didn't resolve inside SigNoz, the feature was treated as broken, regardless of how beautiful the CSS animation looked on the screen.&lt;/p&gt;

&lt;p&gt;The Ultimate Payoff: Total Transparency into AI&lt;br&gt;
Once these technical integration battles were won, the true power of an OpenTelemetry-native platform like SigNoz clicked for me.&lt;/p&gt;

&lt;p&gt;By injecting custom metadata attributes directly into the span context blocks during the Gemini LLM investigation phase, I transformed standard distributed traces into comprehensive, auditable incident records. When an alert triggers, an engineer doesn't have to guess why the AI took a specific action. They can open up SigNoz, click on the active span, and immediately inspect the exact logical reasoning parameters:&lt;/p&gt;

&lt;p&gt;incident.threat_type: Credential Stuffing&lt;/p&gt;

&lt;p&gt;agent.confidence_score: 96%&lt;/p&gt;

&lt;p&gt;agent.evidence_summary: 850 failed logins from Tor Exit Node IP detected inside a 120-second window.&lt;/p&gt;

&lt;p&gt;llm.tokens_used: 1452&lt;/p&gt;

&lt;p&gt;Plaintext&lt;br&gt;
[Insert Screenshot Here: SigNoz Trace Detail side-panel clearly rendering custom metadata attributes mapping out the agent reasoning chains and exact token costs]&lt;br&gt;
What I Learned from the Terminal&lt;br&gt;
Before this hackathon, I viewed observability as a utility you wrap around server infrastructure to check if your hard drives are full or your APIs are returning HTTP 500s.&lt;/p&gt;

&lt;p&gt;Building VigilTrace changed my perspective entirely. As we move into an era dominated by autonomous AI agents, LLM tool-calling, and complex multi-step prompt chains, observability is no longer just an infrastructure requirement—it is the core framework for AI safety and explainability.&lt;/p&gt;

&lt;p&gt;By incorporating OpenTelemetry and SigNoz from line one, I didn't just build a security system that works. I built a system that can explicitly explain why it works, when it fails, and how much it costs down to the millisecond.&lt;/p&gt;

&lt;p&gt;Technology Stack Utilized&lt;br&gt;
Frontend/Orchestration: React, TypeScript, Vite, Node.js, Express, Framer Motion, Recharts&lt;/p&gt;

&lt;p&gt;AI &amp;amp; Telemetry Core: Python, Google Gemini API, OpenTelemetry SDK, OTLP/gRPC Exporters&lt;/p&gt;

&lt;p&gt;Observability Backend: SigNoz (Self-Hosted via Docker / ClickHouse architecture)&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx1bxyvi1lymv03gjepg3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx1bxyvi1lymv03gjepg3.png" alt=" " width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>cybersecurity</category>
      <category>signoz</category>
      <category>hackathon</category>
    </item>
    <item>
      <title>How to Build a Custom AI Security Agent with Coral &amp; Gemini (My First Hackathon Build!)</title>
      <dc:creator>Syed Kabeer Ali</dc:creator>
      <pubDate>Sun, 31 May 2026 13:15:17 +0000</pubDate>
      <link>https://dev.to/syed_kabeerali_65ed7d04d/how-to-build-a-custom-ai-security-agent-with-coral-gemini-my-first-hackathon-build-c27</link>
      <guid>https://dev.to/syed_kabeerali_65ed7d04d/how-to-build-a-custom-ai-security-agent-with-coral-gemini-my-first-hackathon-build-c27</guid>
      <description>&lt;p&gt;Hey everyone! I just submitted my Track 2 project for the Pirates of the Coral-bean hackathon. I built The Coral Lookout, an autonomous AI agent that scans developer blogs (like Dev.to) to flag malicious npm packages and crypto scams.&lt;/p&gt;

&lt;p&gt;Tbh, building this solo was a massive learning curve. I couldn't find a lot of tutorials on hooking up custom unmapped APIs to Coral, so I figured I’d write down exactly how I did it in case anyone else gets stuck on the same things I did.&lt;/p&gt;

&lt;p&gt;Here is how to build your own custom AI agent from scratch!&lt;/p&gt;

&lt;p&gt;The Stack&lt;br&gt;
Data Pipeline: Coral CLI&lt;/p&gt;

&lt;p&gt;LLM Engine: Gemini 2.5 Flash&lt;/p&gt;

&lt;p&gt;Frontend UI: Python &amp;amp; Streamlit&lt;/p&gt;

&lt;p&gt;Deployment: Docker + Railway&lt;/p&gt;

&lt;p&gt;Step 1: Mapping the API with Coral (The Hard Part)&lt;br&gt;
So, my initial plan was to just use Coral to query Dev.to articles. But I quickly realized that Dev.to isn't natively supported in Coral out of the box.&lt;/p&gt;

&lt;p&gt;Instead of writing a wierd python scraping script, I learned you can just build a custom YAML source connector. You basically tell Coral how to read the JSON from the API.&lt;/p&gt;

&lt;p&gt;Here is what my devto_guardian_connector.yaml looked like:&lt;/p&gt;

&lt;p&gt;YAML&lt;br&gt;
name: devto_agent&lt;br&gt;
type: rest&lt;br&gt;
base_url: "&lt;a href="https://dev.to"&gt;https://dev.to&lt;/a&gt;"&lt;br&gt;
tables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;name: articles
description: Latest articles pulled from Dev.to API
request:
  method: GET
  path: /api/articles
Pro tip: Make sure your indentation is perfect here. I spent like 45 minutes wondering why my SQL queries were failing only to realize I had an extra space in my YMAL file. 😅&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Step 2: Querying the Data&lt;br&gt;
Once the connector is linked, you can literally just use standard SQL to fetch internet data. In my app, I just ran a simple query to pull the titles and descriptions of the latest posts.&lt;/p&gt;

&lt;p&gt;Python&lt;/p&gt;

&lt;h1&gt;
  
  
  just standard sql, no scraping required!
&lt;/h1&gt;

&lt;p&gt;query = "SELECT title, description, url FROM devto_agent.articles LIMIT 15"&lt;br&gt;
Step 3: Adding the Brains (Gemini 2.5 Flash)&lt;br&gt;
Now that we have the raw text, we need to figure out if it's safe or if it's a scam. I used the Gemini API for this because its super fast.&lt;/p&gt;

&lt;p&gt;I passed the title and description into Gemini and asked it to act like a cybersecurity expert (or in my case, a Pirate Oracle).&lt;br&gt;
Wait, actually before you do this—make sure you load your API keys using dotenv in python, otherwise your app will crash instantly when you try to run it.&lt;/p&gt;

&lt;p&gt;I wrote a prompt telling Gemini to look for high-risk signals, like links asking you to run suspicious npm install commands or random crypto airdrops. It returns a "confidence score" and a short explanation of why it flagged it.&lt;/p&gt;

&lt;p&gt;Step 4: The Streamlit UI&lt;br&gt;
I didn't want to build a boring corporate dashboard. I wanted this to feel like a real digital first mate!&lt;/p&gt;

&lt;p&gt;I used Streamlit to build the UI. Streamlit is great, but styling it can be a pain. If you want to build a custom dark theme like I did, you have to inject CSS directly into the app using unsafe_allow_html=True.&lt;/p&gt;

&lt;p&gt;Python&lt;br&gt;
st.markdown("""&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.threat-box { border-left: 5px solid red; background-color: #1a1a1a; }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;""", unsafe_allow_html=True)&lt;br&gt;
This let me create a really cool chronological timeline. When the agent finds a safe article, it logs it quietly. When it spots a threat, it flashes a red alert box and quarantines the link.&lt;/p&gt;

&lt;p&gt;Step 5: Shipping it&lt;br&gt;
Finally, I wrote a quick Dockerfile and pushed the whole thing to GitHub. I connected my repo to Railway, and it automatically built and deployed the app. (If your Railway build fails on the first try, just double check that your Streamlit port is set to 0.0.0.0 in your start script).&lt;/p&gt;

&lt;p&gt;Conclusion&lt;br&gt;
Building this agent definetly pushed me to my limits, but bridging an unstructured API to a SQL database and feeding it into an LLM is a superpower.&lt;/p&gt;

&lt;p&gt;You can check out my full code here: &lt;a href="https://github.com/MaskedMan-code/devto-guardian-agent" rel="noopener noreferrer"&gt;https://github.com/MaskedMan-code/devto-guardian-agent&lt;/a&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>security</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
