<?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: Karthikeya Atthavelli</title>
    <description>The latest articles on DEV Community by Karthikeya Atthavelli (@karthikeya_atthavelli_a64).</description>
    <link>https://dev.to/karthikeya_atthavelli_a64</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%2F4047354%2Fdbfbd87d-6deb-42d2-bf59-17a461afd2a5.png</url>
      <title>DEV Community: Karthikeya Atthavelli</title>
      <link>https://dev.to/karthikeya_atthavelli_a64</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/karthikeya_atthavelli_a64"/>
    <language>en</language>
    <item>
      <title>ur rag can return 200 and still be cooked — how we built Goose on SigNoz</title>
      <dc:creator>Karthikeya Atthavelli</dc:creator>
      <pubDate>Sat, 25 Jul 2026 22:47:44 +0000</pubDate>
      <link>https://dev.to/karthikeya_atthavelli_a64/ur-rag-can-return-200-and-still-be-cooked-how-we-built-goose-on-signoz-1k2g</link>
      <guid>https://dev.to/karthikeya_atthavelli_a64/ur-rag-can-return-200-and-still-be-cooked-how-we-built-goose-on-signoz-1k2g</guid>
      <description>&lt;p&gt;ok so real talk. your latency graph looks clean. error rate is chilling. every &lt;code&gt;/chat&lt;/code&gt; is HTTP 200.&lt;/p&gt;

&lt;p&gt;and the answers are just… wrong. like confidently wrong. mid.&lt;/p&gt;

&lt;p&gt;that’s the thing that lowkey haunted us after &lt;strong&gt;HackRx RAG 6.0&lt;/strong&gt;. like ~&lt;strong&gt;1000 teams&lt;/strong&gt;, we somehow landed &lt;strong&gt;top 30&lt;/strong&gt;, we were deep in evals / retrieval / “is this grounded tho”, and the whole time our “monitoring” was basically vibes + batch scripts. RED metrics (rate, errors, duration) stay green while the model yoinks the wrong KB. for a startup shipping RAG that gap is actually lethal cuz users bounce before your SLO even flinches.&lt;/p&gt;

&lt;p&gt;so for Agents of SigNoz we built &lt;strong&gt;Goose&lt;/strong&gt; — catch silent AI quality fails, put them in SigNoz, alert on the cliff, then an MCP agent writes an RCA and can auto-fix the break. this post is what we actually did / hit, not a docs remix. you’ll see the signals, the gotchas, and a real e2e run with numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  the failure mode nobody puts on the dashboard
&lt;/h2&gt;

&lt;p&gt;normal API: wrong = 5xx or timeout. rag chatbot: wrong = success arc.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;retriever returns &lt;em&gt;something&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;llm writes a smooth answer&lt;/li&gt;
&lt;li&gt;handler returns 200&lt;/li&gt;
&lt;li&gt;span status: OK (fr)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;but asked category ≠ queried category. user asks a custom fact, pipeline silently pulls billing docs. or tool output is empty/invalid and the model just freestyles from “general knowledge”.&lt;/p&gt;

&lt;p&gt;on hackrx that silent miss is the diff between “cool demo” and “i would never ship this”. we needed signals for &lt;strong&gt;is this answer actually grounded / routed right&lt;/strong&gt; — not just &lt;strong&gt;is the process alive&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  what Goose even is
&lt;/h2&gt;

&lt;p&gt;short version:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;demo rag emits otel quality junk every turn&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;signoz&lt;/strong&gt; eats traces/metrics/logs and shows the cliff&lt;/li&gt;
&lt;li&gt;alert when &lt;code&gt;quality.score&lt;/code&gt; tanks&lt;/li&gt;
&lt;li&gt;agent hits &lt;strong&gt;signoz mcp&lt;/strong&gt;, pulls evidence, writes markdown rca&lt;/li&gt;
&lt;li&gt;optional auto-remediate (&lt;code&gt;POST /admin/fix-retriever&lt;/code&gt; for our injected break)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;track is &lt;strong&gt;AI &amp;amp; Agent Observability&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;demo flow we used: run ~100 custom-fact questions healthy (answers deliberately weird — “we live on &lt;strong&gt;Mars&lt;/strong&gt;” — so you can tell its grounded in our chroma store not wikipedia). then break retrieval. same questions. still 200s. scores go brrr into the floor. answers flip. signoz shows the cliff. agent investigates + can fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  how we actually used SigNoz
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. otlp everything into one place
&lt;/h3&gt;

&lt;p&gt;rag app ships otlp into signoz. each chat turn ain’t just a request span — we slap quality attrs + metrics on it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;quality.score&lt;/code&gt; — the main cliff metric&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;quality.entity_match&lt;/code&gt; — asked category match retrieved?&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tool.output_valid&lt;/code&gt; — was retriever/tool output even usable&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gen_ai.*&lt;/code&gt; + zone attrs (&lt;code&gt;asked&lt;/code&gt; / &lt;code&gt;queried&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;structured quality logs for the same mess&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;silent fails are cross-signal by nature. trace can look fine (OK) while the metrics panel shows &lt;code&gt;quality.score&lt;/code&gt; falling like 1.0 → 0.2. in signoz we open the dashboard cliff then jump a span that still says OK but &lt;code&gt;tool.output_valid=false&lt;/code&gt;. that combo is the whole thesis.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;drop your dashboard screenshot here — quality.score healthy then cliff&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. dashboard for the cliff, not vanity charts
&lt;/h3&gt;

&lt;p&gt;we didn’t want another “cpu go up” board. ours is basically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;latest / avg &lt;code&gt;quality.score&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;entity_match / misroute vibes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tool.output_valid&lt;/code&gt; rate&lt;/li&gt;
&lt;li&gt;traffic still healthy while quality dies (thats the joke)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;if volume stays up and quality dies, you know its not “the box is down”, its “the answers got dumb”.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;screenshot: healthy avg ~1.0 vs broken ~0.2&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3. alert = answers got dumb, not cpu spicy
&lt;/h3&gt;

&lt;p&gt;alert: &lt;code&gt;quality.score&lt;/code&gt; &lt;strong&gt;below 0.6&lt;/strong&gt;. channel = webhook into our agent on &lt;code&gt;:8020/webhook/alert&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;this is the bit that made it feel real. signoz isn’t just a museum of charts. its the pager for quality. same muscle memory as a classic outage page, except the outage is semantic.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;screenshot: alert rule / fired alert&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4. mcp agent as the on-call that reads telemetry
&lt;/h3&gt;

&lt;p&gt;alert fires → goose doesn’t invent lore. it talks to &lt;strong&gt;signoz mcp&lt;/strong&gt;, grabs recent quality evidence, dumps a markdown rca (what tanked, misroute hints like asked vs queried zones, what to do).&lt;/p&gt;

&lt;p&gt;for the hackathon remediator we call &lt;code&gt;POST /admin/fix-retriever&lt;/code&gt; when the report / alert value smells like our injected misroute. irl you’d swap that for reindex / model rollback / feature flag whatever — pattern stays: &lt;strong&gt;signoz evidence → agent decides → action&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;also being honest: auto-fix does &lt;strong&gt;not&lt;/strong&gt; magically fix “we changed embeddings and everything drifted”. our remediator is for the routing break we demo. don’t overclaim that part, judges can smell it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;screenshot: agent report markdown&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  e2e we actually ran (not vibes)
&lt;/h2&gt;

&lt;p&gt;healthy traffic → break → webhook → fix. autoheal &lt;strong&gt;off&lt;/strong&gt; on purpose so the agent had to be the one that healed it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;healthy&lt;/strong&gt; (~5 min, 25 q) — avg score &lt;strong&gt;1.0&lt;/strong&gt;, retriever not broken&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;broken&lt;/strong&gt; (5 q) — scores &lt;strong&gt;0.2&lt;/strong&gt;, &lt;code&gt;tool.output_valid=false&lt;/code&gt;, answers drift to earth/general knowledge etc&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;agent&lt;/strong&gt; — webhook → mcp investigate → &lt;code&gt;POST /admin/fix-retriever&lt;/code&gt; &lt;strong&gt;ok&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;verify&lt;/strong&gt; — mars answer back, score &lt;strong&gt;1.0&lt;/strong&gt;, retriever healthy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;this is literally the loop we wished we had during hackrx crunch. not only “did the batch score drop in a csv” but “did prod quality just cliff and can something investigate without me clicking around for 40 mins”.&lt;/p&gt;

&lt;h2&gt;
  
  
  instrumentation (the part you can copy)
&lt;/h2&gt;

&lt;p&gt;every turn we roughly do this then export otlp:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quality.score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quality_score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quality.entity_match&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;asked&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;queried&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool.output_valid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool_output_valid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quality.asked_zone&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;asked&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quality.queried_zone&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;queried&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;quality_score_gauge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quality_score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;point &lt;code&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/code&gt; at signoz otlp.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;gotcha we ate so you don’t have to:&lt;/strong&gt; if you start the rag &lt;em&gt;before&lt;/em&gt; signoz is up, otel can basically no-op / disable and then your dashboards look haunted (empty) even tho the app “works”. restart rag after signoz is healthy. we wasted time on this. not proud.&lt;/p&gt;

&lt;p&gt;another dumb one: &lt;code&gt;localhost&lt;/code&gt; vs &lt;code&gt;127.0.0.1&lt;/code&gt; on mac — ipv6 being spicy — our e2e died with connection refused until we pinned 127.0.0.1. tiny, but its the kind of detail you only get from actually running the loop at 3am.&lt;/p&gt;

&lt;h2&gt;
  
  
  what worked / what didn’t
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;worked&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;quality metrics next to traces in one ui (signoz) = way less context switch brain death&lt;/li&gt;
&lt;li&gt;alerting on &lt;code&gt;quality.score&lt;/code&gt; feels correct. cpu alerts would’ve slept through this incident&lt;/li&gt;
&lt;li&gt;mcp for rca is actually useful when the evidence is already in signoz, not in a random spreadsheet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;didn’t / don’t lie about it&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;in-app autoheal can steal the demo if you forget to turn it off (we did, then fixed)&lt;/li&gt;
&lt;li&gt;one remediator ≠ every root cause. embedding change ≠ misroute break&lt;/li&gt;
&lt;li&gt;starting services in the wrong order will gaslight you into thinking instrumentation is broken&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;what i’d tell hackrx-me: instrument semantic quality on day 1. batch evals are cute. production still needs a cliff on a graph.&lt;/p&gt;

&lt;h2&gt;
  
  
  wrap
&lt;/h2&gt;

&lt;p&gt;goose exists cuz top 30 at hackrx still left a hole — strong rag, blind when it fails quiet. for this hackathon we closed it: quality signals in, dashboards + alerts in signoz, mcp agent out with rca (+ optional fix). we’re using the same mindset for our startup rag stuff too, because “http 200 but wrong” is not a hypothetical, its the default failure mode.&lt;/p&gt;

&lt;p&gt;one question for your stack: &lt;strong&gt;when the answer is wrong but http is 200, what breaks on the graph?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;if the answer is “nothing”… yeah. that’s the bug.&lt;/p&gt;

</description>
      <category>rag</category>
      <category>signoz</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
