<?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: eiza</title>
    <description>The latest articles on DEV Community by eiza (@eiza_c23f621cf4f3a85f5559).</description>
    <link>https://dev.to/eiza_c23f621cf4f3a85f5559</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%2F4028481%2F801c86cc-028c-4855-942a-658a254709c6.png</url>
      <title>DEV Community: eiza</title>
      <link>https://dev.to/eiza_c23f621cf4f3a85f5559</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/eiza_c23f621cf4f3a85f5559"/>
    <language>en</language>
    <item>
      <title>Making AI agent quality a first-class observability signal with SigNoz</title>
      <dc:creator>eiza</dc:creator>
      <pubDate>Sun, 26 Jul 2026 18:56:43 +0000</pubDate>
      <link>https://dev.to/eiza_c23f621cf4f3a85f5559/making-ai-agent-quality-a-first-class-observability-signal-with-signoz-4chj</link>
      <guid>https://dev.to/eiza_c23f621cf4f3a85f5559/making-ai-agent-quality-a-first-class-observability-signal-with-signoz-4chj</guid>
      <description>&lt;h2&gt;
  
  
  Making AI agent quality a first-class observability signal with SigNoz
&lt;/h2&gt;

&lt;p&gt;An AI support agent is asked whether the store price-matches a competitor. It&lt;br&gt;
thinks for two seconds, calls a knowledge-base tool, and answers confidently:&lt;br&gt;
&lt;em&gt;"We don't offer a price-match guarantee."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That policy does not exist. Nobody wrote it. The agent invented it.&lt;/p&gt;

&lt;p&gt;Now look at what your monitoring saw: HTTP 200. Latency 1.8s, well inside the&lt;br&gt;
p95. Zero exceptions. Zero error-rate movement. Every dashboard green.&lt;/p&gt;

&lt;p&gt;This is the failure mode that makes AI systems different from the software we&lt;br&gt;
already know how to operate. &lt;strong&gt;A hallucination is a successful request.&lt;/strong&gt; It&lt;br&gt;
passes every health check we have, because every health check we have measures&lt;br&gt;
whether the machine responded — not whether the answer was true.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;AgentLens&lt;/strong&gt; for the Agents of SigNoz hackathon to close that gap: to&lt;br&gt;
make "was this answer actually grounded in something real?" a signal you can&lt;br&gt;
graph, alert on, and block a deploy on, using the same tooling you already use&lt;br&gt;
for latency and error rate.&lt;/p&gt;


&lt;h2&gt;
  
  
  The core idea: read the traces back out
&lt;/h2&gt;

&lt;p&gt;Most eval tooling wraps your agent. You import a library, wrap your calls, and&lt;br&gt;
it grades things inline. That couples evaluation to the agent's runtime, its&lt;br&gt;
language, and its deploy cycle.&lt;/p&gt;

&lt;p&gt;AgentLens does the opposite, and this is the one design decision worth stealing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The agent is instrumented with &lt;strong&gt;plain OpenTelemetry&lt;/strong&gt; — no proprietary SDK.
Every LLM call and tool call is a span, using OTel's GenAI semantic
conventions.&lt;/li&gt;
&lt;li&gt;Those spans go to &lt;strong&gt;SigNoz&lt;/strong&gt; over OTLP. Normal telemetry, nothing special.&lt;/li&gt;
&lt;li&gt;The evaluator then &lt;strong&gt;queries finished traces back out of SigNoz&lt;/strong&gt; through its
query API, rebuilds each agent run from its spans, and grades it with an LLM
judge.&lt;/li&gt;
&lt;li&gt;The scores are written &lt;strong&gt;back into SigNoz as OpenTelemetry metrics&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The evaluator never imports the agent. It never calls it. It never shares a&lt;br&gt;
process with it. It only ever reads spans.&lt;/p&gt;

&lt;p&gt;That decoupling buys three things that a wrapper cannot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It can grade an agent written in any language.&lt;/strong&gt; The interface is
OpenTelemetry, not a Python import.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It can grade runs from yesterday&lt;/strong&gt;, because the traces are already stored.
Evaluation stops being something that must happen at request time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quality becomes telemetry.&lt;/strong&gt; Once a score is an OTel metric in the same
backend as your latency, you can put it on the same dashboard, alert on it
with the same rules, and query it with the same language. No second system,
no separate quality portal to remember to check.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point is why AgentLens deliberately ships &lt;strong&gt;no custom UI&lt;/strong&gt;. The&lt;br&gt;
interface &lt;em&gt;is&lt;/em&gt; SigNoz. Building a bespoke dashboard would have argued against&lt;br&gt;
the whole premise.&lt;/p&gt;


&lt;h2&gt;
  
  
  How SigNoz is used
&lt;/h2&gt;

&lt;p&gt;SigNoz is not a log sink here. It is load-bearing in three distinct roles:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. As the trace store the agent writes to.&lt;/strong&gt; The agent emits spans named&lt;br&gt;
&lt;code&gt;invoke_agent support-triage&lt;/code&gt;, with child spans for each LLM call and tool call,&lt;br&gt;
carrying GenAI conventions plus a few custom attributes — most importantly&lt;br&gt;
&lt;code&gt;agentlens.variant&lt;/code&gt;, which tags which version of the agent produced the run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. As the evaluator's data source.&lt;/strong&gt; This is the unusual part. The evaluator&lt;br&gt;
issues queries against SigNoz's query API to find agent runs in a time window,&lt;br&gt;
pulls their spans, and reconstructs what the agent actually did — which tools it&lt;br&gt;
called, in what order, what it finally said. SigNoz is the input to evaluation,&lt;br&gt;
not just the output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. As the destination for the scores.&lt;/strong&gt; Grades come back as metrics —&lt;br&gt;
&lt;code&gt;agentlens.eval.score.groundedness&lt;/code&gt;, an overall score, token counts — plus eval&lt;br&gt;
spans named &lt;code&gt;evaluate_run support-triage&lt;/code&gt; carrying&lt;br&gt;
&lt;code&gt;agentlens.eval.failure_mode&lt;/code&gt; and the judge's reasoning. So when a score drops,&lt;br&gt;
you can click into the exact evaluation that produced it and read &lt;em&gt;why&lt;/em&gt;, in the&lt;br&gt;
judge's own words, next to the trace of the run being judged.&lt;/p&gt;

&lt;p&gt;Two panels tell the whole story:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Groundedness by variant&lt;/strong&gt; — avg of &lt;code&gt;agentlens.eval.score.groundedness&lt;/code&gt;,
grouped by &lt;code&gt;agentlens.variant&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent latency p95 by variant&lt;/strong&gt; — p95 span duration, same grouping&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Deployed side by side, the top panel falls off a cliff while the bottom panel&lt;br&gt;
stays perfectly flat. That contrast is the entire argument for the project.&lt;/p&gt;


&lt;h2&gt;
  
  
  The experiment
&lt;/h2&gt;

&lt;p&gt;I ran the same golden task suite against two variants of the same agent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;baseline&lt;/code&gt;&lt;/strong&gt; — full grounding rules in the system prompt: cite what you looked&lt;br&gt;
up, say you don't know when the knowledge base doesn't cover it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;regressed&lt;/code&gt;&lt;/strong&gt; — one plausible-sounding prompt change, the kind a real team&lt;br&gt;
ships on a Tuesday to improve customer satisfaction scores: &lt;em&gt;"be confident,&lt;br&gt;
always give the customer a concrete answer."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;No syntax error. No failing test. No new dependency. No latency change. It just&lt;br&gt;
quietly makes the agent invent policies rather than admit ignorance.&lt;/p&gt;

&lt;p&gt;The suite includes deliberate traps — an order ID that doesn't exist, a policy&lt;br&gt;
question the knowledge base genuinely doesn't answer, an international shipping&lt;br&gt;
question that requires combining two lookups. The correct behaviour on a trap is&lt;br&gt;
often to decline to answer.&lt;/p&gt;
&lt;h3&gt;
  
  
  Baseline results
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Found 6 agent run(s) in SigNoz.
  PASS  T07-international-shipping baseline   overall=0.92 ground=0.90 tokens=2185
  PASS  T07-international-shipping baseline   overall=0.90 ground=1.00 tokens=2426
  PASS  T06-undocumented-policy    baseline   overall=0.86 ground=1.00 tokens=2398 [incomplete_answer]
  PASS  T06-undocumented-policy    baseline   overall=0.93 ground=0.95 tokens=2350
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Six runs, zero errors, groundedness between 0.90 and 1.00. Note the&lt;br&gt;
&lt;code&gt;incomplete_answer&lt;/code&gt; flag on a run that still passed — the judge records &lt;em&gt;how&lt;/em&gt;&lt;br&gt;
an answer was weak even when it clears the bar, which is exactly the granularity&lt;br&gt;
you want when a score moves and you need to know what changed.&lt;/p&gt;
&lt;h3&gt;
  
  
  Regressed results
&lt;/h3&gt;

&lt;p&gt;The same suite, the same model, the same tools — only the system prompt changed.&lt;br&gt;
The judge flags the failing runs with a &lt;code&gt;hallucinated_policy&lt;/code&gt; failure mode: the&lt;br&gt;
agent stated a company policy that appears nowhere in the retrieved evidence.&lt;br&gt;
Groundedness drops below the gate's floor while latency and error rate do not&lt;br&gt;
move at all, which is precisely the blind spot this project exists to close.&lt;/p&gt;


&lt;h2&gt;
  
  
  Blocking CI on quality
&lt;/h2&gt;

&lt;p&gt;Because the scores are just telemetry, a CI job can query them and fail the&lt;br&gt;
build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run gate &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--variant&lt;/span&gt; regressed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The gate reads evaluation results out of SigNoz for a variant and time window,&lt;br&gt;
and enforces thresholds: minimum overall score, minimum groundedness, maximum&lt;br&gt;
tokens per run. If groundedness has fallen below the floor, it exits non-zero&lt;br&gt;
and the deploy stops.&lt;/p&gt;

&lt;p&gt;That's the payoff. A hallucination is a 200 OK — latency and error rate can&lt;br&gt;
never catch it. This can.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tech stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Agent + evaluator:&lt;/strong&gt; Node.js / TypeScript, run with &lt;code&gt;tsx&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Telemetry:&lt;/strong&gt; OpenTelemetry SDK — OTLP HTTP exporters for both traces and
metrics, GenAI semantic conventions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM:&lt;/strong&gt; Groq (&lt;code&gt;openai/gpt-oss-20b&lt;/code&gt;) over the OpenAI-compatible protocol, for
both the agent under test and the judge. Provider is one env var; Gemini,
OpenAI, Anthropic, OpenRouter and Ollama all work unchanged.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observability:&lt;/strong&gt; self-hosted SigNoz, ClickHouse-backed, deployed with
Foundry&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI:&lt;/strong&gt; GitHub Actions job running the quality gate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The judge and the agent being the same model is deliberate for reproducibility&lt;br&gt;
on a free tier, and the judge is given the retrieved evidence rather than asked&lt;br&gt;
to recall facts — it grades whether the answer is &lt;em&gt;supported&lt;/em&gt;, not whether it is&lt;br&gt;
&lt;em&gt;true in general&lt;/em&gt;.&lt;/p&gt;




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

&lt;p&gt;&lt;strong&gt;SigNoz's install path changed under me mid-hackathon.&lt;/strong&gt; The &lt;code&gt;docker-compose&lt;/code&gt;&lt;br&gt;
manifests and &lt;code&gt;deploy/install.sh&lt;/code&gt; are deprecated; self-hosting is now&lt;br&gt;
declarative through &lt;code&gt;foundryctl cast&lt;/code&gt; against a &lt;code&gt;casting.yaml&lt;/code&gt;. My setup script&lt;br&gt;
was hunting for compose files that no longer exist. Pinning your assumptions&lt;br&gt;
about someone else's deploy tooling is a bug waiting to happen — and generated&lt;br&gt;
lock files belong in the repo, so a reviewer can reproduce your exact stack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Settings → API Keys" no longer exists&lt;/strong&gt; in current builds; service accounts&lt;br&gt;
replaced it. A read-only viewer role is enough for an evaluator that only&lt;br&gt;
queries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ingestion lag is real and you must design for it.&lt;/strong&gt; The evaluator reads what&lt;br&gt;
the agent just wrote, and ClickHouse needs a beat. Every read path takes a&lt;br&gt;
lookback window, and the fix for an empty result is patience, not a retry storm.&lt;br&gt;
It's an honest constraint of the read-it-back-out architecture, and worth&lt;br&gt;
stating plainly rather than hiding behind a sleep.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Instrument the judge too.&lt;/strong&gt; The evaluation itself is a traced span with its&lt;br&gt;
own token counts. The cost of measuring quality is visible in the same place as&lt;br&gt;
the quality.&lt;/p&gt;




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

&lt;p&gt;Repo: &lt;a href="https://github.com/eiza763/agentlens" rel="noopener noreferrer"&gt;https://github.com/eiza763/agentlens&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;casting.yaml&lt;/code&gt; and &lt;code&gt;casting.yaml.lock&lt;/code&gt; are committed, so &lt;code&gt;foundryctl cast&lt;/code&gt;&lt;br&gt;
reproduces the exact SigNoz deployment I used. &lt;code&gt;npm run selftest&lt;/code&gt; runs 47 checks&lt;br&gt;
with no credentials and no network if you just want to confirm the logic. There&lt;br&gt;
is a Codespaces devcontainer that brings up SigNoz for you, because not everyone&lt;br&gt;
has Docker and admin rights on the machine in front of them.&lt;/p&gt;

&lt;p&gt;Quality is not a vibe you check manually before a release. It's a signal. Put it&lt;br&gt;
on the dashboard next to latency, and let CI block on it.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>llm</category>
      <category>monitoring</category>
    </item>
  </channel>
</rss>
