<?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: Gajanand V Dhayagode</title>
    <description>The latest articles on DEV Community by Gajanand V Dhayagode (@gajanand_vdhayagode_55da).</description>
    <link>https://dev.to/gajanand_vdhayagode_55da</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%2F4048190%2Fe08fb3af-9e92-4f47-b0ca-f6509f7b2c5e.jpg</url>
      <title>DEV Community: Gajanand V Dhayagode</title>
      <link>https://dev.to/gajanand_vdhayagode_55da</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gajanand_vdhayagode_55da"/>
    <language>en</language>
    <item>
      <title>We made SigNoz's LLM observability actually reachable…</title>
      <dc:creator>Gajanand V Dhayagode</dc:creator>
      <pubDate>Sun, 26 Jul 2026 17:01:23 +0000</pubDate>
      <link>https://dev.to/gajanand_vdhayagode_55da/we-made-signozs-llm-observability-actually-reachable-ldk</link>
      <guid>https://dev.to/gajanand_vdhayagode_55da/we-made-signozs-llm-observability-actually-reachable-ldk</guid>
      <description>&lt;p&gt;-and added the one signal it was missing&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Track 01 — AI &amp;amp; Agent Observability.&lt;/strong&gt; A hackathon project built from scratch, July 20–26, 2026.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There's a sentence we said to each other on day one and then kept saying all week, because it kept being true:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;An API 200 is not evidence that a feature works.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This post is the story of a project that lives entirely inside that sentence. It's about a feature SigNoz already shipped that almost nobody can reach, a real bug that makes part of it impossible, and a missing signal — &lt;em&gt;measured answer quality&lt;/em&gt; — that we built on top. And it's about how we used SigNoz not as a black box but as the thing under test.&lt;/p&gt;




&lt;h2&gt;
  
  
  The surprising starting point: SigNoz already built the machinery
&lt;/h2&gt;

&lt;p&gt;Most "add observability to LLMs" projects start from zero. Ours didn't, and that's the interesting part.&lt;/p&gt;

&lt;p&gt;SigNoz &lt;strong&gt;v0.133.0&lt;/strong&gt; already ships a complete LLM-observability product in open source: a dedicated frontend module, two custom OpenTelemetry collector processors (an LLM-pricing processor and a span-mapper processor), and CRUD APIs behind them. The machinery to ingest &lt;code&gt;gen_ai.*&lt;/code&gt; spans, price them, normalize vendor-specific shapes, and render them on a dashboard is &lt;em&gt;there&lt;/em&gt;, in the OSS repo, today.&lt;/p&gt;

&lt;p&gt;So our thesis wasn't "SigNoz is missing LLM observability." It was sharper and, we think, more useful:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;SigNoz shipped the machinery. We make the shipped feature actually reachable — and add the one signal it's missing: measured answer quality.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That framing mattered to us ethically, too. Nothing in this project reads "SigNoz is broken." It reads: they built something good, three defaults and one bug stand between a self-hoster and it, and here's the fix — upstreamed.&lt;/p&gt;




&lt;h2&gt;
  
  
  The three silent gates
&lt;/h2&gt;

&lt;p&gt;Here's what actually stands between a fresh &lt;code&gt;docker compose up&lt;/code&gt; and a working LLM-observability feature. Every one of them fails &lt;em&gt;silently&lt;/em&gt; — the backend returns success while the frontend hides the result, and nothing tells you why.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gate 1 — &lt;code&gt;enable_ai_observability&lt;/code&gt; is default-disabled
&lt;/h3&gt;

&lt;p&gt;The whole feature sits behind an experimental flag that ships off. With it off, the UI route for LLM observability &lt;strong&gt;silently redirects to &lt;code&gt;/home&lt;/code&gt;&lt;/strong&gt;. No error, no "coming soon," no toast. You click the thing and you're just... somewhere else. At the time we hit it, there were &lt;em&gt;zero&lt;/em&gt; web results for the flag name.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gate 2 — &lt;code&gt;use_dashboard_v2&lt;/code&gt; is default-disabled
&lt;/h3&gt;

&lt;p&gt;The seeded LLM dashboards are v2 dashboards. Import one over the API with this flag off and it succeeds — &lt;code&gt;200 OK&lt;/code&gt; — and then renders as an empty &lt;strong&gt;"Welcome to your new dashboard"&lt;/strong&gt; shell. The data is flowing. The panels are defined. The screen is blank.&lt;/p&gt;

&lt;p&gt;This is where the day-one sentence was born. Both gates let the backend succeed while the frontend hides the result. We got bitten by &lt;em&gt;both, in one afternoon&lt;/em&gt;, before we understood the pattern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix for gates 1 and 2 is to ship both flags together — and it has to be both:&lt;/strong&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="nv"&gt;SIGNOZ_FLAGGER_CONFIG_BOOLEAN_ENABLE__AI__OBSERVABILITY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="nv"&gt;SIGNOZ_FLAGGER_CONFIG_BOOLEAN_USE__DASHBOARD__V2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting only one still gives you an empty shell — the exact failure we're fixing. So any "dashboards-as-code" deliverable that sets one flag is quietly reproducing the bug.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gate 3 — the registries ship empty
&lt;/h3&gt;

&lt;p&gt;Two registries back the feature — &lt;code&gt;llm_pricing_rules&lt;/code&gt; (model prices) and &lt;code&gt;span_mapper_groups&lt;/code&gt; (vendor→spec mapping presets). &lt;strong&gt;Both ship with zero rows.&lt;/strong&gt; No seeded prices, no mapping presets. And there's a nasty second-order trap here: SigNoz's own CrewAI docs tell users to instrument with OpenInference — which emits &lt;em&gt;zero&lt;/em&gt; &lt;code&gt;gen_ai.*&lt;/code&gt; attributes — so a user who follows the docs faithfully lands on a seeded dashboard that shows nothing, and has no way to know the mapping layer is what's missing.&lt;/p&gt;




&lt;h2&gt;
  
  
  The one real defect: cost is impossible regardless of configuration
&lt;/h2&gt;

&lt;p&gt;The three gates are configuration. This one is a bug.&lt;/p&gt;

&lt;p&gt;SigNoz's LLM-pricing processor and span-mapper processor are generated into the collector config correctly — we verified the rendered YAML. But neither of them is ever &lt;strong&gt;wired into a pipeline&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GenerateCollectorConfigWithLLMPricingProcessor&lt;/code&gt; and &lt;code&gt;GenerateCollectorConfigWithSpanMapperProcessor&lt;/code&gt; both write only &lt;code&gt;collectorConf["processors"]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Neither touches &lt;code&gt;service.pipelines.traces.processors&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In OpenTelemetry Collector terms, that's &lt;em&gt;dead config&lt;/em&gt;: a processor that exists but is in no pipeline never runs. So even with the flag on and a correctly-rendered &lt;code&gt;gpt-4o&lt;/code&gt; pricing rule, the effective traces pipeline was still &lt;code&gt;[signozspanmetrics/delta, batch]&lt;/code&gt;, and ClickHouse showed &lt;strong&gt;no cost attributes&lt;/strong&gt; — not &lt;code&gt;_signoz.gen_ai.total_cost&lt;/code&gt;, not &lt;code&gt;_signoz.gen_ai.cost_input&lt;/code&gt; / &lt;code&gt;cost_output&lt;/code&gt; (those are the real emitted names; there is no &lt;code&gt;gen_ai.usage.cost&lt;/code&gt; output attribute).&lt;/p&gt;

&lt;p&gt;The good news: the codebase already does this right somewhere. The &lt;strong&gt;logs&lt;/strong&gt; feature wires its generated processor straight into the pipeline. So the fix is small, it follows in-repo prior art, and it's the kind of change a maintainer can verify in two minutes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;enable &lt;code&gt;enable_ai_observability&lt;/code&gt; → add a pricing rule → observe the effective traces pipeline is still &lt;code&gt;[signozspanmetrics/delta, batch]&lt;/code&gt; → cost attributes never appear.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We filed it upstream as &lt;strong&gt;&lt;a href="https://github.com/SigNoz/signoz/pull/12268" rel="noopener noreferrer"&gt;SigNoz/signoz#12268&lt;/a&gt;&lt;/strong&gt; — a small, focused change that cites the prior-art file and ships a clean reproduction. A teammate's fix, not a hackathon drive-by.&lt;/p&gt;




&lt;h2&gt;
  
  
  The part we're proudest of: measuring whether the answers are any &lt;em&gt;good&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;Everything above makes the shipped feature reachable. But there's a signal SigNoz's LLM product genuinely does not have, and it's the one that matters most for agents in production: &lt;strong&gt;is the model's answer any good, and did the last release make it worse?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cost, latency, and token counts tell you the model &lt;em&gt;ran&lt;/em&gt;. They say nothing about whether it was &lt;em&gt;right&lt;/em&gt;. So we built an eval layer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Emitting a signal SigNoz has never seen
&lt;/h3&gt;

&lt;p&gt;OpenTelemetry specs an evaluation signal — &lt;code&gt;gen_ai.evaluation.result&lt;/code&gt; (name, score, label, explanation). It has &lt;strong&gt;zero references in the SigNoz codebase&lt;/strong&gt;. We emit it: an LLM-as-judge scores an answer, and the score travels into SigNoz over OTLP as spec-shaped telemetry.&lt;/p&gt;

&lt;p&gt;Crucially, &lt;em&gt;how&lt;/em&gt; you carry it decides whether it's usable. We learned this the hard way and built a probe (&lt;code&gt;probe_eval_carriers.py&lt;/code&gt;) that emits the same score three ways side by side to prove which one survives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Span events&lt;/strong&gt; are stored in ClickHouse as &lt;code&gt;Array(String)&lt;/code&gt; — you &lt;strong&gt;cannot filter or aggregate inside them.&lt;/strong&gt; An eval you can't &lt;code&gt;GROUP BY&lt;/code&gt; is not auditable.&lt;/li&gt;
&lt;li&gt;A spec-compliant &lt;strong&gt;log record&lt;/strong&gt; and a dedicated &lt;strong&gt;evaluation span&lt;/strong&gt; both land as queryable attributes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So we carry every score on the two that SigNoz can actually aggregate, never on a span event.&lt;/p&gt;

&lt;h3&gt;
  
  
  The judge earns its place by benchmark, not reputation
&lt;/h3&gt;

&lt;p&gt;The judge is the load-bearing piece: every downstream claim is only as trustworthy as the score it produces. So we picked it by measuring it against our actual task, and we picked it to run &lt;strong&gt;locally on open weights&lt;/strong&gt; — &lt;code&gt;gpt-oss:20b&lt;/code&gt; on Ollama, free and self-hostable.&lt;/p&gt;

&lt;p&gt;That last choice is architecture, not thrift. This entire project exists to &lt;em&gt;remove&lt;/em&gt; a gate between a self-hoster and a feature. A judge that needed a paid API key would reintroduce exactly that kind of gate — now you need a billing account before you can evaluate anything. A free, self-hostable judge works for the same person we're unblocking.&lt;/p&gt;

&lt;p&gt;We benchmarked five candidates. The results were not what "bigger is better" would predict:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;model&lt;/th&gt;
&lt;th&gt;valid JSON&lt;/th&gt;
&lt;th&gt;grounded&lt;/th&gt;
&lt;th&gt;fabricated&lt;/th&gt;
&lt;th&gt;separation&lt;/th&gt;
&lt;th&gt;jitter (temp 0)&lt;/th&gt;
&lt;th&gt;p50 latency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;gpt-oss:20b&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3/3&lt;/td&gt;
&lt;td&gt;1.000&lt;/td&gt;
&lt;td&gt;0.000&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+1.000&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.000&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.16s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gpt-oss:120b&lt;/td&gt;
&lt;td&gt;3/3&lt;/td&gt;
&lt;td&gt;1.000&lt;/td&gt;
&lt;td&gt;0.133&lt;/td&gt;
&lt;td&gt;+0.867&lt;/td&gt;
&lt;td&gt;0.250&lt;/td&gt;
&lt;td&gt;4.66s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;nemotron-3-nano:30b&lt;/td&gt;
&lt;td&gt;flaky&lt;/td&gt;
&lt;td&gt;1.000&lt;/td&gt;
&lt;td&gt;0.270&lt;/td&gt;
&lt;td&gt;+0.730&lt;/td&gt;
&lt;td&gt;0.180&lt;/td&gt;
&lt;td&gt;5.38s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;deepseek-v4-flash&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;HTTP 403 (not in tier)&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gemma4:31b&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;returned non-JSON&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The bigger model was the noisier one.&lt;/strong&gt; On a repeated fabricated input at temperature 0, &lt;code&gt;gpt-oss:120b&lt;/code&gt; returned 0.25 / 0.00 / 0.15 — a 0.25 spread — while &lt;code&gt;gpt-oss:20b&lt;/code&gt; returned 0.00 every single time. A quality-regression alert lives or dies on that stability: a judge that wobbles ±0.25 makes "quality dropped 12%" indistinguishable from measurement noise, and can name the &lt;em&gt;wrong&lt;/em&gt; release as the culprit. We chose the model our alert can actually trust, and it happened to be the smaller, faster, free one.&lt;/p&gt;

&lt;p&gt;Every score also carries the two attributes that make it reproducible after the fact:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;gen_ai.evaluation.judge.model&lt;/code&gt; — which model scored it&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;judge.prompt.version&lt;/code&gt; — which rubric version scored it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So "which judge and which rubric produced this score?" is a &lt;code&gt;GROUP BY&lt;/code&gt;, not a shrug.&lt;/p&gt;

&lt;h3&gt;
  
  
  The alert that names the release that regressed
&lt;/h3&gt;

&lt;p&gt;The payoff is a threshold alert: &lt;code&gt;avg(gen_ai.evaluation.score.value)&lt;/code&gt; below &lt;code&gt;0.7&lt;/code&gt;, over a 30-minute window, &lt;strong&gt;grouped by &lt;code&gt;service.version&lt;/code&gt;&lt;/strong&gt;. When a release's answer quality slides under the floor, the alert fires &lt;em&gt;and names the release&lt;/em&gt;. No anomaly detection — that's enterprise-gated in SigNoz — just a threshold on a signal we made aggregatable.&lt;/p&gt;

&lt;p&gt;We measured the full loop end to end against a live instance, not estimated it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;emit at &lt;code&gt;16:21:43Z&lt;/code&gt; → alert transitioned to &lt;strong&gt;firing&lt;/strong&gt; at &lt;code&gt;16:25:40Z&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;span-to-alert = 238s (~4 min)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And here's the part that matters for anyone building on this: that time is almost entirely SigNoz's own machinery — the 120s &lt;code&gt;eval_delay&lt;/code&gt; plus the 1-minute evaluation interval. The judge adds only ~2.5s per call, and because spans are timestamped when the run &lt;em&gt;starts&lt;/em&gt;, even that overlaps the &lt;code&gt;eval_delay&lt;/code&gt; rather than stacking on it. &lt;strong&gt;The eval layer adds essentially nothing to detection time; SigNoz's evaluation interval is the floor.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For the demo we use a deliberately loud drop — measured mean &lt;strong&gt;0.958 → 0.167&lt;/strong&gt; — so the mechanism is legible in one glance. That's a demo choice, not a limitation: the alert fires on the same 0.7 floor whether the slide is a cliff or a subtle crossing.&lt;/p&gt;




&lt;h2&gt;
  
  
  How we deployed it — and made it reproducible
&lt;/h2&gt;

&lt;p&gt;We ran SigNoz self-hosted in WSL2 (Ubuntu 24.04) on native Docker Engine — &lt;em&gt;not&lt;/em&gt; Docker Desktop, whose ClickHouse Keeper is known to segfault. Deployment is via &lt;strong&gt;SigNoz Foundry&lt;/strong&gt; (&lt;code&gt;foundryctl&lt;/code&gt;), their "one config, one command" tool: you declare the whole deployment in a &lt;code&gt;casting.yaml&lt;/code&gt;, &lt;code&gt;forge&lt;/code&gt; expands it into platform manifests plus a checksum-pinned &lt;code&gt;casting.yaml.lock&lt;/code&gt;, and &lt;code&gt;cast&lt;/code&gt; deploys it.&lt;/p&gt;

&lt;p&gt;Because the judges of this hackathon may re-run Foundry to reproduce our deployment, we didn't hand-edit generated files — we put the fix &lt;em&gt;in the source of truth&lt;/em&gt;. Our &lt;code&gt;casting.yaml&lt;/code&gt; sets both gates as first-class env overrides on the SigNoz service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deployment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;flavor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;compose&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker&lt;/span&gt;
  &lt;span class="na"&gt;signoz&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v0.133.0&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;signoz/signoz:v0.133.0&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;SIGNOZ_FLAGGER_CONFIG_BOOLEAN_ENABLE__AI__OBSERVABILITY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
        &lt;span class="na"&gt;SIGNOZ_FLAGGER_CONFIG_BOOLEAN_USE__DASHBOARD__V2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We verified this is the right way to do it: &lt;code&gt;forge&lt;/code&gt; &lt;em&gt;merges&lt;/em&gt; those overrides into the defaults (it doesn't wipe the DB/store env), the flags render into the generated &lt;code&gt;compose.yaml&lt;/code&gt;, and — critically — &lt;strong&gt;forge is deterministic&lt;/strong&gt;: re-running it produces a byte-identical lock. So a judge who clones the repo and runs &lt;code&gt;foundryctl forge &amp;amp;&amp;amp; foundryctl cast&lt;/code&gt; gets the &lt;em&gt;feature-reachable&lt;/em&gt; deployment, not the default empty shell. The casting file is part of the argument, not a checkbox.&lt;/p&gt;




&lt;h2&gt;
  
  
  What we'd tell the next person
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Treat a 200 as the beginning of verification, not the end.&lt;/strong&gt; Both gates that cost us the most time were backends succeeding while frontends hid the result. Visually confirm every dashboard; never trust the POST.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pick your LLM judge by measuring it, not by its size or name.&lt;/strong&gt; The stability of the judge is the stability of your alert. Bigger lost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose carriers your backend can aggregate.&lt;/strong&gt; A beautiful eval signal stuffed into a span event is invisible to &lt;code&gt;GROUP BY&lt;/code&gt;. Know your storage model before you emit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Put the fix in the source of truth, not the generated output&lt;/strong&gt; — so reproduction reproduces the &lt;em&gt;fixed&lt;/em&gt; thing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SigNoz built a genuinely good LLM-observability product. We made it reachable, filed the bug that was blocking cost, and added the measured-quality signal it was missing — all in OSS, all reproducible from one &lt;code&gt;casting.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Repo (with &lt;code&gt;casting.yaml&lt;/code&gt; + &lt;code&gt;casting.yaml.lock&lt;/code&gt; for reproduction): &lt;a href="https://github.com/gajanand27-05/SigNoz-conform" rel="noopener noreferrer"&gt;https://github.com/gajanand27-05/SigNoz-conform&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>observability</category>
      <category>opentelemetry</category>
      <category>llm</category>
      <category>hackathon</category>
    </item>
  </channel>
</rss>
