<?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: Akash Goyal</title>
    <description>The latest articles on DEV Community by Akash Goyal (@akash_goyal).</description>
    <link>https://dev.to/akash_goyal</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%2F4035678%2F8d61e23d-d268-4115-b5ca-7e0c452f4742.jpg</url>
      <title>DEV Community: Akash Goyal</title>
      <link>https://dev.to/akash_goyal</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/akash_goyal"/>
    <language>en</language>
    <item>
      <title>How I Built Warden - an authorization gateway for agentic fleets on Google Cloud</title>
      <dc:creator>Akash Goyal</dc:creator>
      <pubDate>Mon, 31 Aug 2026 19:54:23 +0000</pubDate>
      <link>https://dev.to/akash_goyal/how-i-built-warden-an-authorization-gateway-for-agentic-fleets-on-google-cloud-48lp</link>
      <guid>https://dev.to/akash_goyal/how-i-built-warden-an-authorization-gateway-for-agentic-fleets-on-google-cloud-48lp</guid>
      <description>&lt;p&gt;I built Warden for the &lt;a href="https://allthingsagentichackathon.devpost.com/?ref_feature=challenge&amp;amp;ref_medium=discover&amp;amp;_gl=1*1403lyu*_gcl_au*MTk0MjgxMzE2MC4xNzgzNzU1MDY5*_ga*MzQzMTY4NDY5LjE3NjEzMjI1ODU.*_ga_0YHJK3Y10M*czE3ODgyMDEzODgkbzUwJGcxJHQxNzg4MjAyNjQ1JGo0MSRsMCRoMA.." rel="noopener noreferrer"&gt;All Things Agentic Hackathon&lt;/a&gt;, in the Fortified Enterprise Fleet track. It's an authorization gateway that sits in front of a fleet of task agents and checks every tool call — search a flight, hold a booking, charge a card — before it executes, not after.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live app:&lt;/strong&gt; &lt;a href="https://warden-330594494974.us-central1.run.app" rel="noopener noreferrer"&gt;https://warden-330594494974.us-central1.run.app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architecture walkthrough:&lt;/strong&gt; &lt;a href="https://akashgoyal.github.io/aiml/blog/warden-architecture.html" rel="noopener noreferrer"&gt;https://akashgoyal.github.io/aiml/blog/warden-architecture.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://github.com/akashgoyal/finops-te-agents-warden" rel="noopener noreferrer"&gt;https://github.com/akashgoyal/finops-te-agents-warden&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Video:&lt;/strong&gt; &lt;a href="https://youtu.be/ebTWWi_bfEc" rel="noopener noreferrer"&gt;https://youtu.be/ebTWWi_bfEc&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  In this post
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Section&lt;/th&gt;
&lt;th&gt;Google tools covered&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Why I built this&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What I built&lt;/td&gt;
&lt;td&gt;Gemini, Gemma, Google ADK, Firestore&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Watching it run&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;From my laptop to Google Cloud&lt;/td&gt;
&lt;td&gt;Cloud Run, Google AI Studio&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Making every decision persist&lt;/td&gt;
&lt;td&gt;Firestore&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Adding Model Armor as an extra layer&lt;/td&gt;
&lt;td&gt;Vertex AI, Model Armor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Where things actually stand&lt;/td&gt;
&lt;td&gt;Vertex AI Agent Engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Setting up the Google Cloud tools I used&lt;/td&gt;
&lt;td&gt;Cloud Run, Firestore, Vertex AI, Model Armor, Vertex AI Agent Engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Try it yourself&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Why I built this
&lt;/h2&gt;

&lt;p&gt;2026 didn't wait long to hand me real examples of agents doing things nobody had actually authorized:&lt;/p&gt;

&lt;p&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%2Fk04rr4qw2u86c4yldrp4.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%2Fk04rr4qw2u86c4yldrp4.png" alt="Fox News: AI agent hacks gym system to move up waitlist" width="800" height="245"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="//www.foxnews.com/tech/ai-agent-hacks-gym-system-move-up-waitlist"&gt;Fox News: "AI agent hacks gym system to move up waitlist"&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&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%2Fpyr7w71p805ugkx7ccjw.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%2Fpyr7w71p805ugkx7ccjw.png" alt="The Hacker News: OpenAI Agent Used Exposed Credentials Across Four Services During Hugging Face Breach" width="799" height="379"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="//thehackernews.com/2026/07/openai-agent-used-exposed-credentials.html"&gt;The Hacker News: "OpenAI Agent Used Exposed Credentials Across Four Services During Hugging Face Breach"&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Reading both of these, I kept landing on the same thought: &lt;br&gt;
Neither agent was "hacked" from outside. Each one just did something technically possible that nobody had actually signed off on. That's the gap I set out to close — for a workflow every company already has a strict policy for: Travel &amp;amp; Expense.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;p&gt;I think of it as a bouncer standing between every agent in the fleet and the tools it's asking to use. Both agents doing the actual thinking here are built on Google's Agent Development Kit (ADK), and&lt;br&gt;
every attempted call goes through, in order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A &lt;strong&gt;hard-limit check&lt;/strong&gt; — no model runs for this one, on purpose. It reads the cap straight from the &lt;em&gt;agent's own scope&lt;/em&gt;, stored as data in the &lt;em&gt;same Firestore registry entry&lt;/em&gt; you'll see a screenshot of below. A payment over that cap stops the trip and waits for a human, full stop; some decisions shouldn't be a model's judgment call at all.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;cheap first pass&lt;/strong&gt;, &lt;em&gt;Gemma&lt;/em&gt;, that clears the obvious, in-scope calls for free before anything heavier gets involved.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;careful review&lt;/strong&gt; — an &lt;em&gt;ADK agent running on Gemini&lt;/em&gt; — for
anything ambiguous or out-of-scope. It reads the &lt;em&gt;fleet's actual policy document&lt;/em&gt; and returns a real decision, with a reason.&lt;/li&gt;
&lt;li&gt;If a call gets blocked, a second ADK agent, also on Gemini, decides live, whether to retry the same step through the agent that's actually allowed to do it, or abort the trip.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Every decision, allowed or blocked, gets written to Firestore as a tamper-evident log before anything happens next. So the pipeline runs on three pieces of the Google stack end to end: &lt;u&gt;Gemini&lt;/u&gt; and &lt;u&gt;Gemma&lt;/u&gt; doing the actual judgment calls, &lt;u&gt;ADK&lt;/u&gt; structuring the two agents that make them, and &lt;u&gt;Firestore&lt;/u&gt; making sure none of it gets lost.&lt;/p&gt;

&lt;p&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%2F2vosf3p5lc4k2x1rwo46.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%2F2vosf3p5lc4k2x1rwo46.png" alt="Warden's full request-flow architecture — intercept, guardrail, triage, review, orchestrator, ledger" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The full pipeline I built. I go into more depth on this — including two scenario walkthroughs and the Google Cloud stack behind each stage — on the &lt;a href="//akashgoyal.github.io/aiml/blog/warden-architecture.html"&gt;architecture page&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Watching it run
&lt;/h2&gt;

&lt;p&gt;The dashboard I built is a real console, not a mockup — I click a trip and watch my own fleet run:&lt;/p&gt;

&lt;p&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%2F46sfi7bufj1wvrje0eb9.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%2F46sfi7bufj1wvrje0eb9.png" alt="Warden's dashboard at rest, before a trip is triggered" width="800" height="454"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The live app, sitting idle before I trigger anything. Four panels: what the traveler sees, the live call trace, past transactions, and every touch of Google infrastructure as it happens.&lt;/em&gt;&lt;/p&gt;

&lt;p&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%2Fhz777o13d9kjbvfbx1aw.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%2Fhz777o13d9kjbvfbx1aw.png" alt="A completed trip where the hotel agent's payment attempt was blocked and recovered" width="800" height="454"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A full AUS → CHI trip I ran end to end. At step 6, hotel_agent tries to charge a card directly — outside its job. I block it. At step 7, a second agent decides to retry the same charge through payment_agent instead, and the trip finishes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zooming into the two moments I care about most:&lt;/strong&gt;&lt;/p&gt;

&lt;p&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%2F0n7koang4q06badynehc.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%2F0n7koang4q06badynehc.png" alt="The BLOCK decision on the hotel agent's payment attempt, with the reviewer's rationale" width="800" height="418"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Gemini's actual reasoning: hotel_agent is scoped to search and hold a room, not to charge anything.&lt;/em&gt;&lt;/p&gt;

&lt;p&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%2Fcxxgix04axa9mmtub9tv.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%2Fcxxgix04axa9mmtub9tv.png" alt="The orchestrator's live recovery decision, routing the charge through the correct agent" width="799" height="354"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;This isn't a hand-coded fallback I wrote — it's a second model call, at runtime, deciding this specific retry is safe because payment_agent is the one agent actually authorized for it.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  From my laptop to Google Cloud
&lt;/h2&gt;

&lt;p&gt;While I was building this, my day-to-day iteration ran against small open models on my own laptop, through Ollama — that let me test changes instantly without a cloud project in the loop at every step. That's a development convenience, not what's actually deployed, and I want to be upfront about the distinction.&lt;/p&gt;

&lt;p&gt;What's actually live right now is a different, real thing: Cloud Run running the same code, calling Gemini through Google AI Studio, backed by Firestore. No stub mode, no mocked responses — I wanted anyone checking this out to be looking at the real thing.&lt;/p&gt;

&lt;p&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%2Fl2rpkh9jtgxqjhlrx1sc.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%2Fl2rpkh9jtgxqjhlrx1sc.png" alt="The deployed Cloud Run revision, showing MODEL_BACKEND=gemini and other real deploy config" width="800" height="454"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;I pulled this straight off the deployed revision — &lt;code&gt;MODEL_BACKEND=gemini&lt;/code&gt;, 1 CPU, 1GiB, port 8080.&lt;/em&gt;&lt;/p&gt;

&lt;p&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%2F6cd6xoor5lkus97uyy62.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%2F6cd6xoor5lkus97uyy62.png" alt="Cloud Run request metrics, instance count dropping to zero between demo runs" width="799" height="453"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Container instance count returns to zero between runs — real serverless scaling on Cloud Run, not something I'm just claiming.&lt;/em&gt;&lt;/p&gt;

&lt;p&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%2Fm5gmxd3gd6qy4yuozrzp.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%2Fm5gmxd3gd6qy4yuozrzp.png" alt="Real Cloud Run request logs from an actual trip run" width="800" height="453"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Real request logs from a trip I actually ran — not a local server standing in for the deployment.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Making every decision persist
&lt;/h2&gt;

&lt;p&gt;I wanted every agent's declared scope, and every decision Warden makes, written somewhere durable — so restarting the service, or Cloud Run scaling down, never loses the record. I used Firestore for that.&lt;/p&gt;

&lt;p&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%2F9ix0yuxjavvqs4435pb9.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%2F9ix0yuxjavvqs4435pb9.png" alt="Firestore document showing hotel_agent's declared scope: hotel.search and hotel.hold only" width="799" height="585"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The registry entry that actually made the block possible — hotel_agent's allowed tools, stored as data I wrote once, not hardcoded logic buried in the reviewer.&lt;/em&gt;&lt;/p&gt;

&lt;p&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%2Fnb2ixr8bqwxvyt9lkqoj.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%2Fnb2ixr8bqwxvyt9lkqoj.png" alt="Firestore document showing a fully persisted transaction record with the block decision and reviewer rationale" width="800" height="908"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;One trip I ran, fully persisted — every step, its decision, the reviewing model, and the signed token, exactly as the dashboard read them back.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Adding Model Armor as an extra layer
&lt;/h2&gt;

&lt;p&gt;&lt;a href="//docs.cloud.google.com/model-armor/overview"&gt;Model Armor&lt;/a&gt; is Google Cloud's own screening layer — it inspects prompts and responses for injection attempts, jailbreaks, and sensitive data before and after they reach a model. I wired it into my reviewer's calls, and I wanted to actually see it work, not just configure it and move on. These next three are from a run I did locally against the Vertex AI backend — my live deployment above intentionally runs a different path, which I explain right after:&lt;/p&gt;

&lt;p&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%2F0c2wyc40fwhd3qg53f9b.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%2F0c2wyc40fwhd3qg53f9b.png" alt="Model Armor badge on the blocked hotel agent call, showing the real template name" width="800" height="356"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The &lt;code&gt;warden-prompt-response&lt;/code&gt; badge here is read live off the actual review result I got back — not a label I added for the screenshot.&lt;/em&gt;&lt;/p&gt;

&lt;p&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%2Feoo9pkccyk5o2f234f4s.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%2Feoo9pkccyk5o2f234f4s.png" alt="Model Armor badge also present on the orchestrator's retry decision" width="800" height="303"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Same screening showed up on the recovery decision right after.&lt;/em&gt;&lt;/p&gt;

&lt;p&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%2Fuw5a0bpwqcggglu45l9o.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%2Fuw5a0bpwqcggglu45l9o.png" alt="Real Vertex AI request logs from the reviewer, confirming genuine Vertex traffic" width="800" height="451"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;I confirmed it in Cloud Logging too — a real call to gemini-2.5-flash through Vertex AI, not something simulated.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So why isn't this my deployed default? Model Armor only attaches when calls go through Vertex AI directly — and this project's Vertex AI model access currently tops out at Gemini 2.5 Flash, not the newer Gemini generation I wanted running as my main deployment. So I kept Model Armor as a working, opt-in layer I can turn on and demonstrate, rather than swapping it into the always-on default.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where things actually stand
&lt;/h2&gt;

&lt;p&gt;Here's where things actually stand, plainly — I'd rather be upfront about it than oversell it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The audit trail, agent registry, and transaction history are real Firestore data I wrote to — not held in memory, not reset on restart.&lt;/li&gt;
&lt;li&gt;The reviewer and the recovery decision are real Google ADK agents I built, reasoning over an actual policy document — not if/else logic dressed up as AI.&lt;/li&gt;
&lt;li&gt;What's live is genuinely running on Cloud Run, on Gemini — nothing shown above is stubbed.&lt;/li&gt;
&lt;li&gt;Model Armor screening is something I demonstrated and verified working, and I've kept it additive rather than folding it into the main deployment, for the reason above.&lt;/li&gt;
&lt;li&gt;I also gave my reviewer a real per-agent cryptographic identity — and it came with persistent memory as a bonus — on a separate Vertex AI Agent Engine deployment. That's a different, more involved setup, so I kept it additive rather than wiring it into the main live path.&lt;/li&gt;
&lt;li&gt;One gap I'll be upfront about: full trace-level observability, the kind OpenTelemetry tooling expects, is something I've partially confirmed, not fully — I'd rather say that plainly than gloss over it.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Setting up the Google Cloud tools I used
&lt;/h2&gt;

&lt;p&gt;Here's what I actually configured, tool by tool, in case you want to run this yourself. My repo's README has the exact commands — this is the map of what each piece is for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cloud Run&lt;/strong&gt; — hosts the gateway itself.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I enabled the Cloud Run API from the &lt;a href="https://console.cloud.google.com/apis/library" rel="noopener noreferrer"&gt;API Library&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;I deployed with &lt;code&gt;--min-instances=0&lt;/code&gt;, so it scales to zero when idle
and spins back up on the next request.&lt;/li&gt;
&lt;li&gt;The model backend, project ID, and secret key are set as deploy-time
environment variables, not baked into the image — you can see them
for yourself on the &lt;a href="https://console.cloud.google.com/run" rel="noopener noreferrer"&gt;Cloud Run console&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Firestore&lt;/strong&gt; — the agent registry and the audit/transaction ledger.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I created a &lt;strong&gt;Native mode&lt;/strong&gt; database from the &lt;a href="https://console.cloud.google.com/firestore" rel="noopener noreferrer"&gt;Firestore console&lt;/a&gt; — Datastore mode won't work here.&lt;/li&gt;
&lt;li&gt;I ran a seed script once to write each agent's declared scope in as
data — nothing about scopes is hardcoded in my reviewer's code.&lt;/li&gt;
&lt;li&gt;I leave the project ID blank in local dev, and everything falls back
to in-memory automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Vertex AI + Model Armor&lt;/strong&gt; — the opt-in screening layer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I enabled the Vertex AI and Model Armor APIs from the &lt;a href="https://console.cloud.google.com/apis/library" rel="noopener noreferrer"&gt;API Library&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;I created a Model Armor template — prompt-injection/jailbreak
filters, sensitive-data checks — with one
&lt;code&gt;gcloud model-armor templates create&lt;/code&gt; call, done once.&lt;/li&gt;
&lt;li&gt;I pointed my reviewer at Vertex AI instead of the Gemini Developer
API, and passed that template's name in on each call — that's the
entire integration surface.&lt;/li&gt;
&lt;li&gt;Worth checking your own project's Vertex AI model catalog before
assuming it matches Google AI Studio's — mine didn't, which is
exactly why I kept this path opt-in.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Vertex AI Agent Engine&lt;/strong&gt; &lt;em&gt;(optional)&lt;/em&gt; — a real per-agent identity.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;From the &lt;a href="https://console.cloud.google.com/vertex-ai" rel="noopener noreferrer"&gt;Vertex AI console&lt;/a&gt;, or through the SDK, I deployed
the same reviewer agent as a managed Agent Engine resource.&lt;/li&gt;
&lt;li&gt;Google provisioned a dedicated IAM service account for that specific
deployed agent automatically — I didn't configure that by hand.&lt;/li&gt;
&lt;li&gt;It also came with a persistent Memory Bank for cross-session
context, with no extra setup from me.&lt;/li&gt;
&lt;li&gt;Unlike Cloud Run, this one stays provisioned rather than scaling to
zero — I clean it up once I'm done demonstrating it.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try it yourself
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live app:&lt;/strong&gt; &lt;a href="https://warden-330594494974.us-central1.run.app" rel="noopener noreferrer"&gt;https://warden-330594494974.us-central1.run.app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architecture walkthrough:&lt;/strong&gt; &lt;a href="https://akashgoyal.github.io/aiml/blog/warden-architecture.html" rel="noopener noreferrer"&gt;https://akashgoyal.github.io/aiml/blog/warden-architecture.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://github.com/akashgoyal/finops-te-agents-warden" rel="noopener noreferrer"&gt;https://github.com/akashgoyal/finops-te-agents-warden&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>googlecloud</category>
      <category>ai</category>
      <category>agents</category>
      <category>security</category>
    </item>
    <item>
      <title>I Gave My AI Coding Agent Amnesia, Then Watched Sentry Diagnose It</title>
      <dc:creator>Akash Goyal</dc:creator>
      <pubDate>Sun, 23 Aug 2026 18:24:59 +0000</pubDate>
      <link>https://dev.to/akash_goyal/i-gave-my-ai-coding-agent-amnesia-then-watched-sentry-diagnose-it-1g12</link>
      <guid>https://dev.to/akash_goyal/i-gave-my-ai-coding-agent-amnesia-then-watched-sentry-diagnose-it-1g12</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/bugsmash"&gt;DEV's Summer Bug Smash: Clear the Lineup&lt;/a&gt; powered by &lt;a href="https://sentry.io/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;I built &lt;code&gt;sentry-agent-harness&lt;/code&gt; that instruments the &lt;strong&gt;cline&lt;/strong&gt; AI coding agents with &lt;strong&gt;Sentry AI Monitoring&lt;/strong&gt;. &lt;br&gt;
I took a fork of &lt;a href="https://github.com/cline/cline" rel="noopener noreferrer"&gt;Cline&lt;/a&gt; and instrumented it entirely through its own public hook system (no patches to Cline's internals). &lt;br&gt;
It's delievered with the four toggleable agent failure modes in the &lt;a href="https://github.com/akashgoyal/sentry-agent-harness" rel="noopener noreferrer"&gt;github repo&lt;/a&gt; : &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;silent tool-output truncation &lt;/li&gt;
&lt;li&gt;an infinite tool-call retry loop&lt;/li&gt;
&lt;li&gt;an MCP schema mismatch, and &lt;/li&gt;
&lt;li&gt;unbounded context growth&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To actually exercise it, I didn't hand Cline a synthetic prompt — I gave it a real support ticket:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A customer (&lt;a href="mailto:grace@example.com"&gt;grace@example.com&lt;/a&gt;) says her July invoice — she thinks the ID is &lt;code&gt;INV-1034&lt;/code&gt; — is higher than expected. Look into the database and the billing app, figure out what's wrong, and explain the root cause.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Behind that ticket is a tiny billing service (a &lt;code&gt;users&lt;/code&gt;/&lt;code&gt;orders&lt;/code&gt;/&lt;code&gt;invoices&lt;/code&gt; SQLite database over MCP, a &lt;code&gt;billing_engine.py&lt;/code&gt; with the real calculation logic, a &lt;code&gt;PRICING.md&lt;/code&gt; with the &lt;em&gt;documented&lt;/em&gt; pricing contract, and an &lt;code&gt;audit.log&lt;/code&gt; of deploy events) carrying a genuine, seeded bug: a deploy on &lt;code&gt;2026-07-01&lt;/code&gt; silently switched whether tax is computed before or after a discount, so invoice &lt;code&gt;INV-1043&lt;/code&gt; came out &lt;strong&gt;$195.00&lt;/strong&gt; instead of the correct &lt;strong&gt;$192.50&lt;/strong&gt;. The ticket's invoice ID even has a typo, so step one is already a dead end.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Everything ran against &lt;strong&gt;&lt;code&gt;llama3.1:8b&lt;/code&gt; through Ollama — entirely local, no API key, no hosted inference bill.&lt;/strong&gt; That matters twice over: anyone can reproduce this for free, and a small local model is noisier than a frontier hosted one, which is honestly closer to real production agent traffic than a cherry-picked demo would be — exactly the kind of variance you need observability for, not the case where everything already works.&lt;/p&gt;

&lt;p&gt;Worth separating out clearly from the four scenarios below: some of that noise was just the model being an 8B model, nothing to do with any bug I injected. It occasionally hallucinated a plausible-looking tool call as plain text instead of issuing a real one, and routinely sent integer arguments as quoted strings (&lt;code&gt;"limit": "5"&lt;/code&gt; instead of &lt;code&gt;5&lt;/code&gt;) — usually harmless, since the MCP server coerced it, but a good reminder that "the agent looks like it's working" and "the agent is reliably doing what you asked" aren't the same claim. None of the four failures in this post are that — each one below reproduces identically regardless of model size, because each one is an infrastructure/hook-level bug, not a reasoning failure. A bigger model wouldn't have avoided any of them: silent truncation cuts data &lt;em&gt;before&lt;/em&gt; it reaches the model, and the other three are hook-level overrides that happen regardless of what the model does.&lt;/p&gt;

&lt;p&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%2Fszotmescb1jyosczktu4.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%2Fszotmescb1jyosczktu4.png" alt="The trace waterfall for a clean run — agent.run → agent.turn → tool spans, token counts visible." width="800" height="428"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&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%2Fuaf0v8p4q6aknpc821ir.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%2Fuaf0v8p4q6aknpc821ir.png" alt="" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Bug Fix or Performance Improvement
&lt;/h2&gt;

&lt;p&gt;There were two layers of bugs here, and honestly the second one ended up being the more interesting story.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 1 — the bug I seeded on purpose.&lt;/strong&gt; The billing discrepancy above, investigated live by Cline while I watched Sentry. Four distinct failure modes surfaced during that investigation (full detail in "My Improvements" below): a silently truncated log file that made the agent confidently cite the wrong deploy, a retry loop that burned 3x the tokens on a typo'd invoice ID, and a schema-mismatch that produced a real, cascading &lt;code&gt;ValidationError&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 2 — the bugs I found by accident.&lt;/strong&gt; Building this wasn't "write the instrumentation once, watch it work." Actually running it against a live Sentry project surfaced &lt;strong&gt;five real bugs in my own instrumentation code&lt;/strong&gt;, none of which &lt;code&gt;tsc&lt;/code&gt; or a unit test would have caught, all of which I only noticed because I went looking in the Sentry UI and found something missing or wrong:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Events dropped at process exit.&lt;/strong&gt; My first version called &lt;code&gt;Sentry.flush()&lt;/code&gt; fire-and-forget. Cline's CLI calls &lt;code&gt;process.exit()&lt;/code&gt; moments after a run finishes, which routinely won the race against the network flush — exceptions were being generated and silently never delivered.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The error flag that's never true.&lt;/strong&gt; FastMCP catches its own schema validation errors and returns them as normal-looking text content, not a protocol-level error — so gating my capture logic on &lt;code&gt;result.isError&lt;/code&gt; meant it never fired for the exact scenario it existed to catch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seer caught a bug in my bug report.&lt;/strong&gt; My exception message did &lt;code&gt;String(result.output)&lt;/code&gt; on a response object, coercing it to the literal text &lt;code&gt;"[object Object]"&lt;/code&gt;. I asked Seer for the root cause of my own captured exception, and it correctly diagnosed &lt;em&gt;my&lt;/em&gt; bug instead of the scenario I'd built — exact line number, reproduction steps, all of it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The fix that silently didn't apply.&lt;/strong&gt; My first version of silent truncation returned a modified tool result from an &lt;code&gt;afterTool&lt;/code&gt; hook. The hook fired, computed the right truncated value, returned it — and the &lt;em&gt;next&lt;/em&gt; model call still saw the full, untruncated content anyway.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Breadcrumbs need a body to travel in.&lt;/strong&gt; Sentry breadcrumbs only ship attached to an event. Three of my four scenarios are deliberately silent (no exception), so their breadcrumbs were being recorded locally and then never sent anywhere — invisible until I noticed nothing showed up where I expected an issue.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All five are fixed in the code linked below.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Git Commit for cline instrumentation : &lt;a href="https://github.com/akashgoyal/sentry-agent-harness/commit/68d590af3ac41fab1bd4a57a05ebf6fa5aadb7ec" rel="noopener noreferrer"&gt;https://github.com/akashgoyal/sentry-agent-harness/commit/68d590af3ac41fab1bd4a57a05ebf6fa5aadb7ec&lt;/a&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;All four scenarios are just environment variables, off by default — clone the repo and flip whichever one you want to see for yourself, no code changes needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;CLINE_SENTRY_SILENT_TRUNCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1     &lt;span class="c"&gt;# cuts tool output over 2,000 chars before the model sees it&lt;/span&gt;
&lt;span class="nv"&gt;CLINE_SENTRY_INFINITE_TOOL_LOOP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1    &lt;span class="c"&gt;# forces a retry instead of stopping on an empty result&lt;/span&gt;
&lt;span class="nv"&gt;CLINE_SENTRY_MCP_SCHEMA_MISMATCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1   &lt;span class="c"&gt;# corrupts an outgoing MCP argument's type&lt;/span&gt;
&lt;span class="nv"&gt;CLINE_SENTRY_CONTEXT_INFLATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1     &lt;span class="c"&gt;# skips message-history trimming&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  My Improvements
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;No patches to Cline's internals.&lt;/strong&gt; &lt;code&gt;AgentRuntime&lt;/code&gt; — the actual agent loop — already exposes a public hook system (&lt;code&gt;beforeModel&lt;/code&gt;, &lt;code&gt;beforeTool&lt;/code&gt;, &lt;code&gt;afterTool&lt;/code&gt;, &lt;code&gt;onEvent&lt;/code&gt;) plus a subscribable event stream. The whole integration is one new module split into two files by responsibility: a &lt;code&gt;telemetry.ts&lt;/code&gt; that turns Cline's own events into a Sentry span tree and knows nothing about the scenarios below, and a &lt;code&gt;scenarios.ts&lt;/code&gt; that injects the four failure modes purely through the same public hooks. Three call sites, merged in via &lt;code&gt;mergeAgentHooks&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failure #1 — the confidently wrong agent.&lt;/strong&gt; The &lt;code&gt;audit.log&lt;/code&gt; file is 4,258 characters. Buried at character &lt;strong&gt;2,311&lt;/strong&gt; — well past a naive truncation point — is the one line that actually explains the bug: a &lt;code&gt;billing-engine version=2.0.0&lt;/code&gt; deploy whose changelog admits the tax-calculation change outright. I added a middleware bug on purpose: silently cut any tool output over 2,000 characters before it reaches the model, no exception raised. With it active, the agent confidently blamed a real but &lt;em&gt;unrelated&lt;/em&gt; &lt;code&gt;api-gateway&lt;/code&gt; release from three weeks earlier — sha and all — because that's what was still inside its 2,000-char window. Nothing crashed. A green checkmark looks identical whether the agent got lucky or got it right.&lt;/p&gt;

&lt;p&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%2Fhigq5z1wlnh1touznz39.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%2Fhigq5z1wlnh1touznz39.png" alt="The " width="799" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failure #2 — the loop that won't take no for an answer.&lt;/strong&gt; Normally an empty query result is where the agent should say "I don't have that" and stop. I added a hook that, on an empty result, injects a "try a different query" nudge instead of letting the run end. Baseline: &lt;strong&gt;2 iterations&lt;/strong&gt;. With the nudge active, runs stretched to &lt;strong&gt;3–6 iterations&lt;/strong&gt; depending on how many reformulations the model attempted, and prompt token usage peaked at &lt;strong&gt;116,901&lt;/strong&gt; in one run — over &lt;strong&gt;3x&lt;/strong&gt; the baseline's ~36,000 for the exact same question.&lt;/p&gt;

&lt;p&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%2Fjs76h5jp6cnv7pfb9j73.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%2Fjs76h5jp6cnv7pfb9j73.png" alt="The bloated trace next to the baseline trace" width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failure #3 — the one with a real stack trace.&lt;/strong&gt; I corrupted an outgoing MCP tool argument's type right before it left the client — a &lt;code&gt;limit&lt;/code&gt; parameter that should be an integer went out as the string &lt;code&gt;"not-a-valid-type"&lt;/code&gt;. What happened next wasn't scripted: the tool rejected it with a genuine schema &lt;code&gt;ValidationError&lt;/code&gt; from the server's own pydantic validation. The agent retried the &lt;em&gt;exact same broken call&lt;/em&gt; — nothing about the corruption changes between attempts — until Cline's own "max consecutive mistakes" safety net aborted the run. A cascading failure, not a single bad call.&lt;/p&gt;

&lt;p&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%2Fanqu473hbn1ti85lyomd.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%2Fanqu473hbn1ti85lyomd.png" alt="Corrupted-argument Trace" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&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%2F3u7llzg1wg0dh99yb94m.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%2F3u7llzg1wg0dh99yb94m.png" alt="The ValidationError, with the corrupted-argument breadcrumb" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why hooks instead of a fork-and-edit.&lt;/strong&gt; Every failure mode above is injected through &lt;code&gt;beforeModel&lt;/code&gt;/&lt;code&gt;beforeTool&lt;/code&gt;/&lt;code&gt;onEvent&lt;/code&gt; — the same seams Cline itself uses for its own hook-event dispatch, tool approval, and context compaction. That meant zero merge conflicts with Cline's own logic (confirmed by testing that its native hook-event output kept working unchanged throughout), and it's the reason the one genuinely subtle bug (#4 in the list above) was fixable by moving logic between two &lt;em&gt;equally legitimate&lt;/em&gt; hooks rather than needing a deeper architectural change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Use of Sentry
&lt;/h2&gt;

&lt;p&gt;This project leans on several distinct Sentry AI Monitoring capabilities, each mapped to a specific problem in agent debugging that a plain log file handles badly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Distributed Tracing / Agent Tracing&lt;/strong&gt; — &lt;code&gt;AgentRuntime&lt;/code&gt;'s event stream (&lt;code&gt;run-started&lt;/code&gt;, &lt;code&gt;turn-started&lt;/code&gt;, &lt;code&gt;tool-started&lt;/code&gt;, ...) is turned into a real nested span tree: &lt;code&gt;agent.run&lt;/code&gt; → &lt;code&gt;agent.turn &amp;lt;n&amp;gt;&lt;/code&gt; → &lt;code&gt;agent.llm_call&lt;/code&gt; / &lt;code&gt;tool.&amp;lt;name&amp;gt;&lt;/code&gt;, with prompt/completion token counts attached to each LLM span via &lt;code&gt;gen_ai.usage.*&lt;/code&gt; attributes. Because the span &lt;code&gt;op&lt;/code&gt; values follow Sentry's &lt;code&gt;gen_ai.*&lt;/code&gt; semantic conventions (&lt;code&gt;gen_ai.invoke_agent&lt;/code&gt;, &lt;code&gt;gen_ai.chat&lt;/code&gt;, &lt;code&gt;gen_ai.execute_tool&lt;/code&gt;), Sentry's own UI recognized this as agent monitoring automatically — a dedicated &lt;strong&gt;Agent Activity&lt;/strong&gt; view came for free, not something I had to build a custom dashboard to get.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Monitoring&lt;/strong&gt; — the schema-mismatch scenario produces a real &lt;code&gt;Sentry.captureException&lt;/code&gt; with full breadcrumb context (which argument got corrupted, on which tool, right before the server's validation error). The two other "silent" scenarios (truncation, infinite loop) don't throw by design, so I added explicit &lt;code&gt;Sentry.captureMessage(..., "warning")&lt;/code&gt; calls at the exact moment each one fires — turning invisible failures into findable issues, with the issue's event count doubling as "how many times did this actually happen in one run."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seer&lt;/strong&gt; — asked to explain the schema-mismatch issue, and its root cause analysis was accurate down to the specific line and the specific reason (&lt;code&gt;String()&lt;/code&gt; coercing an object to &lt;code&gt;"[object Object]"&lt;/code&gt;) — which is how I found and fixed bug #3 in the list above. Seer diagnosed a real bug in my own code, not the synthetic scenario I thought I was showing it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Breadcrumbs&lt;/strong&gt; — every &lt;code&gt;AgentRuntimeEvent&lt;/code&gt; (run/turn/tool lifecycle, model requests) leaves a breadcrumb, plus a scenario-specific one at each failure point (&lt;code&gt;scenario.silent_truncation&lt;/code&gt;, &lt;code&gt;scenario.infinite_tool_loop&lt;/code&gt;, &lt;code&gt;scenario.mcp_schema_mismatch&lt;/code&gt;). These are what actually let me (and Seer) reconstruct &lt;em&gt;why&lt;/em&gt; a run went wrong, not just &lt;em&gt;that&lt;/em&gt; it did.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Seer's RCA on schema-mismatch issue : &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%2F8fyh2tr78lhyh8c6dfxi.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%2F8fyh2tr78lhyh8c6dfxi.png" alt="Seer's root cause analysis on the schema-mismatch issue" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Seer's RCA for Infinite tool calls issue : &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%2Fd56z5edfybqs496xbda3.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%2Fd56z5edfybqs496xbda3.png" alt=" " width="800" height="813"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
    </item>
    <item>
      <title>Why I Built PaperFlakes (And How Zerops Saved the Stack)</title>
      <dc:creator>Akash Goyal</dc:creator>
      <pubDate>Sun, 09 Aug 2026 18:22:38 +0000</pubDate>
      <link>https://dev.to/akash_goyal/why-i-built-paperflakes-and-how-zerops-saved-the-stack-2ki7</link>
      <guid>https://dev.to/akash_goyal/why-i-built-paperflakes-and-how-zerops-saved-the-stack-2ki7</guid>
      <description>&lt;p&gt;Late one night while preparing slides for an upcoming talk, I ran three dense research papers through a standard OCR tool.&lt;/p&gt;

&lt;p&gt;The OCR worked, but left me with 40 pages of raw, unformatted text. The real problem wasn't extracting the text; it was finding the insight buried inside it.&lt;/p&gt;

&lt;p&gt;I wanted something that could turn dense PDFs into small, visual insights - &lt;strong&gt;bite-sized cards ready for slides, developer notes, or Twitter&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That was the spark for &lt;strong&gt;PaperFlakes&lt;/strong&gt;.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;PaperFlakes takes PDFs - pick from a curated list of classic ML papers or upload your own - OCRs them, and turns the extracted text into shareable "sticky note" insight cards across four categories, which you can filter, download as images, or share straight to X /Twitter.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Features for Users
&lt;/h2&gt;

&lt;p&gt;I set out to build a platform that turns dense academic reading into a fast, visual experience:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Batch Processing:&lt;/strong&gt; Drop up to 25 multi-page PDFs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-Time Progress:&lt;/strong&gt; Watch live updates as the engine parses document queues, tracking statuses, page counts, and extracted word tallies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Did You Know?" Fact Cards:&lt;/strong&gt; Color-coded visual cards highlighting key facts, formulas, and takeaways.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instant Export &amp;amp; Sharing:&lt;/strong&gt; Download cards or share them straight to social media.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/5ra5lgwRpeQ"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  How to use it
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open the &lt;a href="https://app-2c11-8000.prg1.zerops.app/" rel="noopener noreferrer"&gt;live app&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Check one or more papers under "Try it on papers" and &lt;strong&gt;click Process batch&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Watch the Processing status table update as each document is OCR'd.&lt;/li&gt;
&lt;li&gt;Insight cards appear in the Insights panel as soon as each category finishes — &lt;strong&gt;no need to wait for the whole batch&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Use the &lt;em&gt;category chips and paper dropdown&lt;/em&gt; above the panel to filter what's shown.&lt;/li&gt;
&lt;li&gt;On any card: &lt;strong&gt;Save downloads it as a PNG&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;On any card: 𝕏 Share opens a pre-filled tweet. &lt;/li&gt;
&lt;li&gt;Download all (top right) exports every currently-visible card as one combined image. &lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;strong&gt;The UI view:&lt;/strong&gt;&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%2F5gv5890v04jmu46xbbd8.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%2F5gv5890v04jmu46xbbd8.png" alt="The product view" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The social media share view:&lt;/strong&gt; &lt;a href="https://x.com/akashgoyal95/status/2086510334790303979?s=20" rel="noopener noreferrer"&gt;Link to generated tweet&lt;/a&gt;&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%2Fl6pj5ryjpiotkago4yub.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%2Fl6pj5ryjpiotkago4yub.png" alt="Share card on X" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Product Walkthrough :
&lt;/h2&gt;

&lt;p&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%2Foo9chd9755cp0y4i4v6z.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%2Foo9chd9755cp0y4i4v6z.png" alt="How it works" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The application follows a simple &lt;strong&gt;three-stage flow&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Upload &amp;amp; Queue (left)&lt;/strong&gt; - Users select multiple PDFs or upload their own documents and start a batch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process &amp;amp; Track (center)&lt;/strong&gt; - Each document is processed asynchronously, with its status, page count, and processing details visible in real time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generate Insights (right)&lt;/strong&gt; - As soon as a document finishes processing, AI-generated facts and insights appear as shareable &lt;strong&gt;sticky-note cards&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The Engineering &amp;amp; Zerops ZCP
&lt;/h2&gt;

&lt;p&gt;Behind the UI, the backend handles the pipeline:&lt;br&gt;
&lt;strong&gt;PDF ingestion → metadata storage → chunking &amp;amp; OCR → AI-powered insight extraction → result storage → fact-card generation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This keeps the user experience simple while the heavy document processing happens asynchronously in the backend.&lt;/p&gt;

&lt;p&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%2Ftr8b6ucksdpced1m8pal.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%2Ftr8b6ucksdpced1m8pal.png" alt="Zerops hosting flow" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&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%2F5rczewr48le5qdq9n9hf.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%2F5rczewr48le5qdq9n9hf.png" alt="Project architecture" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The zerops.yaml is &lt;a href="https://github.com/akashgoyal/paperflakes-zerops/blob/main/zerops.yaml" rel="noopener noreferrer"&gt;shared here&lt;/a&gt; .&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When a user submits a batch, the Express API registers the job inside &lt;strong&gt;PostgreSQL with a queued status&lt;/strong&gt; and returns an instant job token to the &lt;strong&gt;React UI&lt;/strong&gt;. As workers process pages through &lt;strong&gt;Gemma (Together AI) asynchronously&lt;/strong&gt;, the database updates in real time, and the UI streams progress smoothly without dropping connections.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  This whole project is heavily reliant on Zerops infra &amp;amp; built using the ZCP.
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Runtime service&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Managed PostgreSQL&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Public HTTPS&lt;/strong&gt; — zerops.app subdomain after deployment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secrets&lt;/strong&gt; — TOGETHER_API_KEY is stored as a Zerops service secret.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Health checks&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Git-based deployment&lt;/strong&gt; — deploys the latest commit to Zerops using zcli.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ZCP with Claude Code&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Running your own copy
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The repository is self-contained:&lt;/strong&gt; zerops.yaml defines the deployment configuration, while migrate.py initializes the database schema idempotently on each deploy.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create a Zerops project&lt;/strong&gt; with a Python runtime service and a PostgreSQL service named db.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a Together AI API key&lt;/strong&gt; and add it as the TOGETHER_API_KEY secret on the runtime service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configure ZEROPS_TOKEN and ZEROPS_SERVICE_ID&lt;/strong&gt; as GitHub repository secrets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Push to main&lt;/strong&gt;. The included GitHub Actions workflow builds and deploys the application automatically.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can also deploy manually using zcli push or the Zerops MCP tools.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;You can see the zerops-services in action &amp;amp; associated cost with them (left-bottom)&lt;/strong&gt;&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%2Fqhr4syhnca39997w2uml.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%2Fqhr4syhnca39997w2uml.png" alt=" " width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;By offloading setup headaches to Zerops, I was able to go from an initial draft idea to a production-ready, multi-service application in a single hackathon build window.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demo Video :&lt;/strong&gt; &lt;a href="https://youtu.be/5ra5lgwRpeQ" rel="noopener noreferrer"&gt;https://youtu.be/5ra5lgwRpeQ&lt;/a&gt; &lt;br&gt;
&lt;strong&gt;Github :&lt;/strong&gt; &lt;a href="https://github.com/akashgoyal/paperflakes-zerops" rel="noopener noreferrer"&gt;https://github.com/akashgoyal/paperflakes-zerops&lt;/a&gt; &lt;br&gt;
&lt;strong&gt;Live App :&lt;/strong&gt; &lt;a href="https://app-2c11-8000.prg1.zerops.app/" rel="noopener noreferrer"&gt;https://app-2c11-8000.prg1.zerops.app/&lt;/a&gt; &lt;br&gt;
&lt;strong&gt;Built For :&lt;/strong&gt; &lt;a href="https://www.wemakedevs.org/hackathons/zerops" rel="noopener noreferrer"&gt;https://www.wemakedevs.org/hackathons/zerops&lt;/a&gt; &lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Written by :&lt;/strong&gt; Akash Goyal&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Sentinel MCP Signoz (Agents of Signoz)</title>
      <dc:creator>Akash Goyal</dc:creator>
      <pubDate>Sun, 26 Jul 2026 17:40:36 +0000</pubDate>
      <link>https://dev.to/akash_goyal/sentinel-mcp-signoz-25nf</link>
      <guid>https://dev.to/akash_goyal/sentinel-mcp-signoz-25nf</guid>
      <description>&lt;p&gt;I participated in 'Agents of Signoz' hacakthon with Ashish Agrawal.&lt;br&gt;
We built an impactful project using the Signoz ecosystem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Signoz Alert based - Agentic system - with Unified Observability Dashboard
&lt;/h3&gt;

&lt;h2&gt;
  
  
  Project features :
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Agentic application with SRE Agent, Data Quality Agent.&lt;/li&gt;
&lt;li&gt;SREAgent would trigger on Alert (from log/trace/metric) via webhook.&lt;/li&gt;
&lt;li&gt;Data Quality Agent would run at certain durations once the Data ingestion is complete.&lt;/li&gt;
&lt;li&gt;Each Agents make use MCP tools from signoz-mcp-server &amp;amp; tools&lt;/li&gt;
&lt;li&gt;Signoz Dashboard customized for unified experience - &lt;strong&gt;for Agent, App, mcp-tools &amp;amp; APM metrics&lt;/strong&gt; - using Query Builder&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Hackathon :&lt;/strong&gt; &lt;a href="https://www.wemakedevs.org/hackathons/signoz" rel="noopener noreferrer"&gt;https://www.wemakedevs.org/hackathons/signoz&lt;/a&gt; &lt;br&gt;
&lt;strong&gt;Github :&lt;/strong&gt; &lt;a href="https://github.com/akashgoyal/sentinel-mcp-signoz" rel="noopener noreferrer"&gt;https://github.com/akashgoyal/sentinel-mcp-signoz&lt;/a&gt; &lt;br&gt;
&lt;strong&gt;Hackathon Project PPT :&lt;/strong&gt; &lt;a href="https://docs.google.com/presentation/d/1ECSsDfM-7LE-z0lPU4y3F6BGCXiJrOl_uem43iVTJC8/edit?slide=id.g3f5b372df0a_0_749#slide=id.g3f5b372df0a_0_749" rel="noopener noreferrer"&gt;LINK&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Youtube Video :&lt;/strong&gt; &lt;a href="https://youtu.be/OIFl7tt6IVY" rel="noopener noreferrer"&gt;Demo Link&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&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%2F9ly7ab6hr5zmm2d8pmdo.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%2F9ly7ab6hr5zmm2d8pmdo.png" alt="Tech Stack" width="800" height="605"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;This blog will hand hold you through the project - in understanding it's whereabout, reasoning behind features, and finally practicals.&lt;br&gt;
Kepe reading to know below : &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;About the Project&lt;/li&gt;
&lt;li&gt;Tech Stack &amp;amp; architecture&lt;/li&gt;
&lt;li&gt;How to setup on your local &amp;amp; run demo &lt;/li&gt;
&lt;li&gt;Learning &amp;amp; Growth&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Create User application - which will use OTel
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Langgraph based application with [Ingestion → Analyser → Extractor → Refiner] Nodes. All using qwen2.5-coder:1.5b model from ollama.&lt;/li&gt;
&lt;li&gt;Application Flow chart : &lt;/li&gt;
&lt;/ol&gt;

&lt;p&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%2Feu7y5tdnw78zyejoek33.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%2Feu7y5tdnw78zyejoek33.png" alt="sentinel-mcp-signoz project overview" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&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%2F6akycbx8rpm3v5yenyy2.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%2F6akycbx8rpm3v5yenyy2.png" alt="MCP agents in project" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Create SRE Agent &amp;amp; Data Quality Agent - using signoz-mcp-server
&lt;/h2&gt;

&lt;h3&gt;
  
  
  SRE Agent :
&lt;/h3&gt;

&lt;p&gt;SRE Agent (powered by gemini-3.6-flash) takes over without human intervention. Features : &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Performs RCA on application &amp;amp; setup errors &lt;/li&gt;
&lt;li&gt;Triggered on Signoz-Alert for RCA.&lt;/li&gt;
&lt;li&gt;Analyzes the application traces, logs, metrics&lt;/li&gt;
&lt;li&gt;Identify the Root Cause of the problem &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Design Diagram : &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%2Fzcvfocj5r5qbz40fmumh.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%2Fzcvfocj5r5qbz40fmumh.png" alt="SRE Agent" width="799" height="517"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Data Quality Agent :
&lt;/h3&gt;

&lt;p&gt;Data Quality Agent (powered by openai-4o) takes over without human intervention. Features : &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Triggered on Signoz-Alert when ingestion process completes.&lt;/li&gt;
&lt;li&gt;Analyzes the application traces and logs - from Signoz Storage.&lt;/li&gt;
&lt;li&gt;Generates an analysis focused on Compliance Governance.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Design Diagram : &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%2Fkkg77rf0u968ar3tz7mx.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%2Fkkg77rf0u968ar3tz7mx.png" alt="Data Quality Agent" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Setup Signoz Dashboard :
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Unified Observability Dashboard
&lt;/h3&gt;

&lt;p&gt;Single monitoring panel for all project components. Features : &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Metrics related to &amp;gt;10 signoz-mcp-server tool calls . &lt;/li&gt;
&lt;li&gt;Langgraph based user app metrics (custom metrics)&lt;/li&gt;
&lt;li&gt;SRE Agent related metrics&lt;/li&gt;
&lt;li&gt;APM Metrics&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Tools Used : &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Default Signoz Dashboard Templates.&lt;/li&gt;
&lt;li&gt;Query Builder - Drag &amp;amp; Drop, query builder.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&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%2Fqjy8sxirktm5d8lv6hmt.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%2Fqjy8sxirktm5d8lv6hmt.png" alt="Unified Observability Dashboard" width="800" height="791"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  PRACTICALS
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Setup on your machine locally :
&lt;/h3&gt;

&lt;p&gt;Refer the github repo shared in first section of this blog.&lt;/p&gt;

&lt;h4&gt;
  
  
  Infra Setup
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Setup Signoz via foundry. Enable MCP. Follow this guide.&lt;/li&gt;
&lt;li&gt;Create user-account in Docker&lt;/li&gt;
&lt;li&gt;Create a service-account&lt;/li&gt;
&lt;li&gt;Create Signoz-API-Key in service account&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  User + Agents Application Setup
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;An agentic app using instrumented packages which could generate telemetry data &lt;/li&gt;
&lt;li&gt;Enable Traces, Logs, metrics in app&lt;/li&gt;
&lt;li&gt;Exporters configured to Signoz OTel Collector&lt;/li&gt;
&lt;li&gt;Scripts to generate data - for both Success, Failure scenarios&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Signoz Viewers
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Check Trace Viewers &lt;/li&gt;
&lt;li&gt;Check Log Viewers &lt;/li&gt;
&lt;li&gt;Check Metric Viewers&lt;/li&gt;
&lt;li&gt;Create Metric dashboard and use Signoz Query Builder to adjust metric widgets queries, with service-name, metric name or trace-field name&lt;/li&gt;
&lt;li&gt;Setup a webhook notification channel&lt;/li&gt;
&lt;li&gt;Configure Alerts (using query builder)&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Execution Outputs :
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;User Application Run&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%2F7e3lw01swr0noy0cql01.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%2F7e3lw01swr0noy0cql01.png" alt="User Application Run" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Errors generated in Signoz&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%2Fb93yk415w0ucrrbxvj14.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%2Fb93yk415w0ucrrbxvj14.png" alt="Errors generated in Signoz" width="799" height="449"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Alert triggered in Signoz&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%2Fkncag78vllynapn6ckxn.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%2Fkncag78vllynapn6ckxn.png" alt="Alert triggered in Signoz" width="799" height="449"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Webhook server start &amp;amp; request received&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%2Fapohdw5g8pug7lp8lgxm.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%2Fapohdw5g8pug7lp8lgxm.png" alt="Webhook server logs" width="799" height="448"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SRE Agent in Action - MCP tool call (fetch logs)&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%2Fbxzlwiq8isb3b93tljxa.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%2Fbxzlwiq8isb3b93tljxa.png" alt="SREAgent-log-fetch" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SRE Agent in Action - MCP tool call (fetch traces) &amp;amp; OUTPUT &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%2F2xekgn4vewdgbutxa3di.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%2F2xekgn4vewdgbutxa3di.png" alt="SREAgent-trace-fetch" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unified Observability Dashboard for - APM + User App + Agent App + MCP Tools metrics&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%2F7qhpclf9p9k7skc1qm2r.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%2F7qhpclf9p9k7skc1qm2r.png" alt="unified-observability-dashboard" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Impact of Project
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Built an AI-powered observability solution leveraging the Signoz ecosystem.&lt;/li&gt;
&lt;li&gt;Minutes instead of hours for initial incident triage using AI agents.&lt;/li&gt;
&lt;li&gt;Single-click investigation across Signoz Logs, Metrics, Traces, and Query Builder.&lt;/li&gt;
&lt;li&gt;Automated root cause analysis and data quality validation using Signoz MCP Tools.&lt;/li&gt;
&lt;li&gt;Showcased the potential to significantly reduce manual debugging effort.&lt;/li&gt;
&lt;li&gt;Dashboard view in one place has customer - Easy visuals for Business stakeholders&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Learning &amp;amp; Growth
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Gained hands-on experience with Signoz, OpenTelemetry, and Signoz MCP.&lt;/li&gt;
&lt;li&gt;Learned to integrate Logs, Metrics, Traces, Query Builder, Dashboards, and Alerts into AI workflows.&lt;/li&gt;
&lt;li&gt;Strengthened skills in AI agents, observability, and event-driven system design.&lt;/li&gt;
&lt;li&gt;Improved rapid prototyping and solution building in a hackathon environment.&lt;/li&gt;
&lt;li&gt;MCP tools eased the Agentic Development &lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;Github Repo : &lt;a href="https://github.com/akashgoyal/sentinel-mcp-signoz" rel="noopener noreferrer"&gt;https://github.com/akashgoyal/sentinel-mcp-signoz&lt;/a&gt; &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User Application &lt;/li&gt;
&lt;li&gt;MCP based Agents &lt;/li&gt;
&lt;li&gt;Programmatic Webhook Server &lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Disclaimer :
&lt;/h2&gt;

&lt;p&gt;AI-tools were used in : &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Writing Code &lt;/li&gt;
&lt;li&gt;Debugging issues &lt;/li&gt;
&lt;li&gt;Refining system design diagrams &lt;/li&gt;
&lt;li&gt;Getting clarity on doubtful concepts
AI Tools - Gemini, CoPilot, AntiGravity&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Presenting with Thanks
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://www.linkedin.com/in/akashgoyal7/" rel="noopener noreferrer"&gt;Akash Goyal&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.linkedin.com/in/ashish-agrawal-6b889915/" rel="noopener noreferrer"&gt;Ashish Agrawal&lt;/a&gt; &lt;/li&gt;
&lt;/ol&gt;

</description>
    </item>
    <item>
      <title>Custom Metrics Dashboard for GenAI apps with SigNoz and OpenTelemetry</title>
      <dc:creator>Akash Goyal</dc:creator>
      <pubDate>Sat, 18 Jul 2026 22:55:40 +0000</pubDate>
      <link>https://dev.to/akash_goyal/custom-metrics-dashboard-for-genai-apps-with-signoz-and-opentelemetry-1lg</link>
      <guid>https://dev.to/akash_goyal/custom-metrics-dashboard-for-genai-apps-with-signoz-and-opentelemetry-1lg</guid>
      <description>&lt;p&gt;SigNoz supports lot of features and has a lot of documentations around those features. It has a full section dedicated to LLM observability across different service providers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But, this blog is a bit different. Instead of relying on out-of-the-box vendor presets, it shows you how to create a completely custom dashboard tailored for your specific GenAI/agentic application as per your needs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Just tracking generic infrastructure metrics like CPU and memory utilization isn't enough. To understand how your AI features are truly performing, you need specialize application-level telemetry.&lt;/p&gt;

&lt;p&gt;All the code discussed in this post is available in the &lt;a href="https://www.google.com/search?q=https://github.com/akashgoyal/custom-metrics-dashboard-signoz" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Signoz supports both Trace Data and Metrics Data - for setting metric widget queries. This feature is really helpful in scaling the existing instrumented codebases &amp;amp; has uniqueness.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part 1: Setting Up SigNoz Locally (Self-Hosted Docker)
&lt;/h2&gt;

&lt;p&gt;Referred &lt;a href="https://signoz.io/docs/install/docker/" rel="noopener noreferrer"&gt;Signoz Doc Section&lt;/a&gt; for setting this up.&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="c"&gt;### Step 2: Create `casting.yaml`&lt;/span&gt;
vi casting.yaml  &lt;span class="c"&gt;#add below&lt;/span&gt;
&lt;span class="s2"&gt;"""
apiVersion: v1alpha1
kind: Installation
metadata:
  name: signoz
spec:
  deployment:
    flavor: compose
    mode: docker
"""&lt;/span&gt;

&lt;span class="c"&gt;### Step 3: Deploy SigNoz - spin up your local SigNoz infrastructure:&lt;/span&gt;
foundryctl cast &lt;span class="nt"&gt;-f&lt;/span&gt; casting.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Part 2: Application Architecture &amp;amp; Quick Start
&lt;/h2&gt;

&lt;p&gt;I designed a minimal llm-inference based workflow application, with two language models, to work on this topic.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Application Architecture Block
&lt;/h3&gt;

&lt;p&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%2Fx89wnv5972bazvh7g1sy.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%2Fx89wnv5972bazvh7g1sy.png" alt="Demo Application Design" width="800" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explanation of the diagram:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Traffic Flow:&lt;/strong&gt; The LLM Client actively invokes endpoints on the LLM Server to run inference tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Telemetry Collection:&lt;/strong&gt; Both the client and the server generate performance data during these interactions. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Export:&lt;/strong&gt; The gathered telemetry is continuously pushed out via a Metrics periodic export routine utilizing OpenTelemetry standard components (OTLP Span Exporter and OTLP Metric Exporter).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage and Visualization:&lt;/strong&gt; The exported data flows directly into SigNoz, where it is permanently stored in a ClickHouse DB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observability:&lt;/strong&gt; Finally, developers can monitor the application using the integrated Trace Viewer and Dashboards.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2. Getting Started with App
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Clone the &lt;a href="https://github.com/akashgoyal/custom-metrics-dashboard-signoz" rel="noopener noreferrer"&gt;repo from github&lt;/a&gt;&lt;/strong&gt;. Then, follow these steps to initialize the environment and launch your components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Setup local virtual environment&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 2. Ensure metric computation logic is added.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;CODE - &lt;a href="https://github.com/akashgoyal/custom-metrics-dashboard-signoz/blob/main/metric_record.py" rel="noopener noreferrer"&gt;custom metrics&lt;/a&gt; relevant to the application.&lt;/li&gt;
&lt;li&gt;CODE - Setting &lt;a href="https://github.com/akashgoyal/custom-metrics-dashboard-signoz/blob/main/llm_server.py#L78-L101" rel="noopener noreferrer"&gt;span attrs &amp;amp; record metrics&lt;/a&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%2Fas5o0c5cffvox0ffmif8.png" alt="Recording metrics &amp;amp; setting Span attributes" width="800" height="328"&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 3. Spin up the LLM Server container &lt;/span&gt;
&lt;span class="c"&gt;# Create your docker-compose.yaml file first, then run:&lt;/span&gt;
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt; llm-server 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&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%2Fozvtywtlhx8mx0r1awi6.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%2Fozvtywtlhx8mx0r1awi6.png" alt="llm-server is up" width="800" height="387"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 4. Configure local environment and run client loops sequentially&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;httpx opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp 
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:4317"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_RESOURCE_ATTRIBUTES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"service.name=llm-client-service"&lt;/span&gt;
python llm_client.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Part 3: Visualising the Traces
&lt;/h2&gt;

&lt;h3&gt;
  
  
  A look at the traces :
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Success Client requests - Trace View:&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%2Fb43yhvygz62ca8iei5f7.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%2Fb43yhvygz62ca8iei5f7.png" alt="Success Client Requests Trace" width="799" height="336"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the infra capacity is limited, after some requests, time-out errors might start surfacing. (Nothing to worry, just restart the llm-server from docker app). Related traces examples below:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Requests getting Timed Out one after other. (Server was hung)&lt;/em&gt;&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%2Fflhlz2eilmglgf8159jh.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%2Fflhlz2eilmglgf8159jh.png" alt="Requests getting Timed Out" width="798" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Errored Request Trace (server was not up by that time)&lt;/em&gt;&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%2Fnqravl2b8j1925tzxz1h.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%2Fnqravl2b8j1925tzxz1h.png" alt="Trace of Errored Request" width="800" height="269"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 4: Custom Metrics Dashboard in SigNoz
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Creating the Custom Dashboard - Below are &lt;u&gt;the steps I followed in order&lt;/u&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Implemented the &lt;a href="https://github.com/akashgoyal/custom-metrics-dashboard-signoz/blob/main/metric_record.py" rel="noopener noreferrer"&gt;custom metrics&lt;/a&gt; relevant to the application.&lt;/li&gt;
&lt;li&gt;Added relevant attributes in &lt;a href="https://github.com/akashgoyal/custom-metrics-dashboard-signoz/blob/main/llm_server.py#L78-L101" rel="noopener noreferrer"&gt;spans &amp;amp; record metrics&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Referred one of the template dashboards in &lt;a href="https://signoz.io/docs/dashboards/dashboard-templates/overview/" rel="noopener noreferrer"&gt;Signoz Dashboard Page&lt;/a&gt;. Took the &lt;u&gt;&lt;a href="https://signoz.io/docs/dashboards/dashboard-templates/agno-dashboard/" rel="noopener noreferrer"&gt;Agno Dashboard&lt;/a&gt; as Base&lt;/u&gt; to build upon.&lt;/li&gt;
&lt;li&gt;Took the code for 'Error rate' metric widget. Created new metric widgets, in sync with the &lt;em&gt;metric_record.py&lt;/em&gt; logic.&lt;/li&gt;
&lt;li&gt;Modified the queries of existing widgets in Base Json.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;For any further edits&lt;/strong&gt; &lt;em&gt;(metric-name, widget-type, etc.)&lt;/em&gt;, I used the &lt;strong&gt;&lt;a href="https://signoz.io/docs/userguide/query-builder-v5/" rel="noopener noreferrer"&gt;SigNoz's native Query Builder&lt;/a&gt;&lt;/strong&gt;. &lt;/p&gt;

&lt;p&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%2Fb71jgx8kolloivq6xeia.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%2Fb71jgx8kolloivq6xeia.png" alt="Update Metric Widget in DB Builder" width="800" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The dashboard JSON used for this experiment is shared in &lt;a href="https://github.com/akashgoyal/custom-metrics-dashboard-signoz/blob/main/custom-metrics-dashboard.json" rel="noopener noreferrer"&gt;the repo file&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it appears in SigNoz:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Signoz Default Metrics Section :&lt;/strong&gt;&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%2Fs1b4bvze8kov69y69enj.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%2Fs1b4bvze8kov69y69enj.png" alt="Signoz Default metrics section view" width="800" height="676"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your Custom Metrics dashboard - data rendered using both span params &amp;amp; metrics :&lt;/strong&gt;&lt;/p&gt;

&lt;p&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%2Fs6xgwz0blzcmz04tdc27.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%2Fs6xgwz0blzcmz04tdc27.png" alt="Custom Metrics dashboard" width="799" height="439"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion &amp;amp; Impact: An insight into custom dashboards with Signoz
&lt;/h2&gt;

&lt;p&gt;These are my findings on how &lt;strong&gt;Signoz supports custom metrics dashboards&lt;/strong&gt;, and what changes are needed or good to have while working on such requirements. &lt;/p&gt;

&lt;p&gt;Signoz supports both Trace Data and Metrics Data - for setting metric widget queries. This feature is really helpful in scaling the existing instrumented codebases &amp;amp; has uniqueness.&lt;/p&gt;

&lt;p&gt;I hope this content would help readers enhance their knowledge.&lt;/p&gt;

&lt;p&gt;Ready to get started? Clone the experimental code from &lt;a href="https://github.com/akashgoyal/custom-metrics-dashboard-signoz/tree/main" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt; and spin it up in your local developer environment!&lt;/p&gt;

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