<?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: Bereket Legesse</title>
    <description>The latest articles on DEV Community by Bereket Legesse (@bek-dev-22).</description>
    <link>https://dev.to/bek-dev-22</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%2F4048204%2F982b6fb0-5cbe-4174-a8d6-971610554830.png</url>
      <title>DEV Community: Bereket Legesse</title>
      <link>https://dev.to/bek-dev-22</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bek-dev-22"/>
    <language>en</language>
    <item>
      <title>Taraol: making a distributed AI-agent mesh observable — and self-defending — with SigNoz</title>
      <dc:creator>Bereket Legesse</dc:creator>
      <pubDate>Sun, 26 Jul 2026 18:03:35 +0000</pubDate>
      <link>https://dev.to/bek-dev-22/how-we-caught-and-contained-a-runaway-ai-agent-loop-with-signoz-1548</link>
      <guid>https://dev.to/bek-dev-22/how-we-caught-and-contained-a-runaway-ai-agent-loop-with-signoz-1548</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Built during the &lt;strong&gt;Agents of SigNoz&lt;/strong&gt; hackathon. Source + reproducible Foundry config:&lt;br&gt;
&lt;a href="https://github.com/MTC-123/Taraol" rel="noopener noreferrer"&gt;github.com/MTC-123/Taraol&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We ran a five-agent research pipeline — &lt;code&gt;planner → researcher → writer → critic → router&lt;/code&gt; — each&lt;br&gt;
agent in its own container, talking over HTTP. From the outside it looked healthy: every service&lt;br&gt;
was up, every request returned &lt;code&gt;200&lt;/code&gt;, every model call produced valid text.&lt;/p&gt;

&lt;p&gt;But on one run, the &lt;strong&gt;writer and critic were caught in a loop across process boundaries&lt;/strong&gt; — the&lt;br&gt;
writer produced a draft, the critic sent it back, the writer regenerated, forever. Each hop was a&lt;br&gt;
real Gemini call in a &lt;em&gt;different&lt;/em&gt; container. And here's the problem that makes distributed agents&lt;br&gt;
genuinely hard:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;No single service could see the loop.&lt;/strong&gt; The planner had already returned. The writer only saw&lt;br&gt;
"another request." The critic only saw "another draft." The loop existed &lt;strong&gt;only in the&lt;br&gt;
telemetry&lt;/strong&gt; — in the relationships &lt;em&gt;between&lt;/em&gt; the services.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is exactly where SigNoz comes in. We built &lt;strong&gt;&lt;a href="https://github.com/MTC-123/Taraol" rel="noopener noreferrer"&gt;Taraol&lt;/a&gt;&lt;/strong&gt; to&lt;br&gt;
instrument agent systems with OpenTelemetry, compare workflow variants, and — crucially — &lt;strong&gt;detect&lt;br&gt;
and contain failures like this from the telemetry itself&lt;/strong&gt;. SigNoz is the backend: traces, the&lt;br&gt;
Service Map, logs, dashboards, and alerts. We built no custom UI.&lt;/p&gt;

&lt;p&gt;This post walks the full loop we built: &lt;strong&gt;instrument → experiment → distributed trace → detect →&lt;br&gt;
alert → enforce → audit.&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Why distributed agent observability is hard
&lt;/h2&gt;

&lt;p&gt;A single LLM call is easy. A &lt;em&gt;mesh&lt;/em&gt; of agents owned by different services is not. Traditional&lt;br&gt;
monitoring answers infrastructure questions ("is the writer up?"); LLM evaluation answers quality&lt;br&gt;
questions ("is the answer good?"). Neither answers the operational ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which agent hop is making the workflow slow or expensive?&lt;/li&gt;
&lt;li&gt;Are two services stuck calling each other in a loop?&lt;/li&gt;
&lt;li&gt;Is a prompt injection propagating from one agent to the next?&lt;/li&gt;
&lt;li&gt;Can the system react &lt;strong&gt;automatically&lt;/strong&gt; before the loop becomes an outage?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Taraol answers these on open standards — &lt;strong&gt;W3C trace context&lt;/strong&gt; and the OpenTelemetry &lt;code&gt;gen_ai&lt;/code&gt;&lt;br&gt;
semantic conventions — so it works with any Python agent and any OTel backend.&lt;/p&gt;


&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   planner ─HTTP▶ researcher ─HTTP▶ writer ─HTTP▶ critic ─HTTP▶ router
      │              │                │             │             │
      └──────── @agent / @chat / @tool  +  W3C traceparent on every hop ───────┘
                                   │
                       OpenTelemetry / OTLP (gen_ai semconv)
                                   │
                                 SigNoz
                 ┌─────────────────┴──────────────────┐
           Traces · Service Map · Logs         Query engine
           Dashboards · Alerts                        │
                                                       ▼
                                       Watcher ──▶ Controller
                                  (reads telemetry,   (pause agent /
                                   emits signals)      break edge / audit)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Every layer is standard OpenTelemetry. Taraol adds the AI-specific spans, the experiment layer,&lt;br&gt;
and the detection/enforcement services; SigNoz provides tracing, topology, storage, dashboards,&lt;br&gt;
and alerting.&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%2F86pqq46d7zxsl5shyrwy.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%2F86pqq46d7zxsl5shyrwy.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📸 &lt;strong&gt;Figure 1 (hero) — the Service Map.&lt;/strong&gt; &lt;em&gt;(Repo: &lt;code&gt;docs/service-map.png&lt;/code&gt;.)&lt;/em&gt; The five agent&lt;br&gt;
services, drawn automatically by SigNoz from propagated trace context.&lt;br&gt;
&lt;strong&gt;Caption:&lt;/strong&gt; &lt;em&gt;"SigNoz reconstructs the agent topology from traces alone — no separate topology&lt;br&gt;
database, no hand-drawn diagram."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Instrumentation: two decorators, and traceparent on every hop
&lt;/h2&gt;

&lt;p&gt;Each agent runs as its own FastAPI service. The LLM and tool steps use Taraol's decorators; the&lt;br&gt;
per-request root uses a context manager (it needs the conversation id from the incoming payload):&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;taraol&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;instrument&lt;/span&gt;

&lt;span class="n"&gt;kit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;instrument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;service_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# one-line OpenTelemetry setup per service
&lt;/span&gt;
&lt;span class="nd"&gt;@chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MODEL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                     &lt;span class="c1"&gt;# tokens + cost read off the returned response
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MODEL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@tool&lt;/span&gt;                            &lt;span class="c1"&gt;# the web search step
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search_sources&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hits_to_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;web_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The distributed part is standard trace propagation. Taraol's A2A client injects &lt;code&gt;traceparent&lt;/code&gt;&lt;br&gt;
(and baggage) into every outgoing agent-to-agent request, and the receiving service extracts it —&lt;br&gt;
so a request that starts in the planner and ends in the router is &lt;strong&gt;one trace&lt;/strong&gt; in SigNoz. That is&lt;br&gt;
what lets SigNoz draw the Service Map in Figure 1.&lt;/p&gt;

&lt;p&gt;Content capture is &lt;strong&gt;off by default&lt;/strong&gt;; we enabled it for the demo so we could read each hop's&lt;br&gt;
prompt and completion under standard &lt;code&gt;gen_ai.*&lt;/code&gt; keys.&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%2Fbmgmy2ce21psn8ffy31e.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%2Fbmgmy2ce21psn8ffy31e.png" alt=" " width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📸 &lt;strong&gt;Figure 2 — a distributed trace.&lt;/strong&gt; &lt;em&gt;(Repo: &lt;code&gt;docs/mesh-trace.png&lt;/code&gt;.)&lt;/em&gt; The flame graph +&lt;br&gt;
waterfall across all five services, with a &lt;code&gt;chat&lt;/code&gt; span selected so the right panel shows&lt;br&gt;
&lt;code&gt;gen_ai.input.messages&lt;/code&gt;, &lt;code&gt;gen_ai.usage.output_tokens&lt;/code&gt;, and &lt;code&gt;agentmesh.cost.direct_usd&lt;/code&gt;.&lt;br&gt;
&lt;strong&gt;Caption:&lt;/strong&gt; &lt;em&gt;"One trace spanning five containers. Each hop carries the previous agent's output&lt;br&gt;
as this agent's input — genuine A→B→C data flow, with tokens and cost per hop."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  AgentLab: comparing a converging vs a runaway workflow
&lt;/h2&gt;

&lt;p&gt;One healthy run doesn't prove robustness. &lt;strong&gt;AgentLab&lt;/strong&gt; fires the &lt;em&gt;same&lt;/em&gt; mesh workload as tagged&lt;br&gt;
variants and compares them on operational telemetry. Here, one stack, two variants — a&lt;br&gt;
&lt;code&gt;baseline&lt;/code&gt; that converges and a &lt;code&gt;runaway&lt;/code&gt; that storms the writer↔critic loop — under a single&lt;br&gt;
run id, no redeploy (the loop mode rides the request payload):&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;taraol&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Experiment&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Experiment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;converge-vs-runaway&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Fraol&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;baseline&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loop_mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;off&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;# converges in a few hops
&lt;/span&gt;    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;runaway&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="n"&gt;loop_mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;storm&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# writer ↔ critic loop
&lt;/span&gt;    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fire&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;                               &lt;span class="c1"&gt;# fire(variant) POSTs to the planner
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every span — in every service — carries &lt;code&gt;experiment.id&lt;/code&gt; / &lt;code&gt;experiment.variant&lt;/code&gt; /&lt;br&gt;
&lt;code&gt;experiment.run_id&lt;/code&gt;, because the tags propagate across the A2A hops too. We compare them in a&lt;br&gt;
SigNoz dashboard (shipped with Taraol as JSON, built with the &lt;strong&gt;Query Builder&lt;/strong&gt;) and via a CLI&lt;br&gt;
that reads the same telemetry back. One real run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;variant     cost$   tokens  agents   avg ms    loops  breakers  fails  health
baseline   0.0079    2799       5    50340        0         0      0    89.9
runaway    0.0406   14342       5   130017        1         1      0    59.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The runaway variant cost &lt;strong&gt;~5× the tokens&lt;/strong&gt; and ran &lt;strong&gt;~2.5× longer&lt;/strong&gt; — and the &lt;code&gt;loops&lt;/code&gt; and&lt;br&gt;
&lt;code&gt;breakers&lt;/code&gt; columns are the important part: they were populated by detection running &lt;strong&gt;from&lt;br&gt;
telemetry&lt;/strong&gt;, not by any single agent noticing.&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%2Fb2bxlfm1kt4ebh81jotu.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%2Fb2bxlfm1kt4ebh81jotu.png" alt=" " width="799" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📸 &lt;strong&gt;Figure 3 — the AgentLab dashboard.&lt;/strong&gt; &lt;em&gt;(Repo: &lt;code&gt;docs/agentlab-dashboard.png&lt;/code&gt; shows the&lt;br&gt;
concept; for the mesh, capture the dashboard on the &lt;code&gt;converge-vs-runaway&lt;/code&gt; run.)&lt;/em&gt; Panels split by&lt;br&gt;
&lt;code&gt;experiment.variant&lt;/code&gt;: direct LLM cost, output tokens, P95 latency, loop count, breaker trips.&lt;br&gt;
&lt;strong&gt;Caption:&lt;/strong&gt; &lt;em&gt;"Same workload, two designs, compared on real operational telemetry — SigNoz is&lt;br&gt;
the dashboard."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The dashboard tells us &lt;em&gt;which&lt;/em&gt; variant is abnormal. The trace tells us &lt;em&gt;why&lt;/em&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  The distributed trace: the loop nobody could see
&lt;/h2&gt;

&lt;p&gt;Opening &lt;strong&gt;Trace Explorer&lt;/strong&gt; and filtering to the runaway run, the longest trace showed one pair of&lt;br&gt;
operations repeating across services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;writer → critic
writer → critic
writer → critic
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That single pattern explained all three dashboard symptoms — latency, tokens, and cost all rose&lt;br&gt;
because every revision was another cross-service model call. This was our key lesson:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A &lt;strong&gt;dashboard&lt;/strong&gt; tells you &lt;em&gt;which run is abnormal&lt;/em&gt;. A &lt;strong&gt;distributed trace&lt;/strong&gt; tells you &lt;em&gt;why&lt;/em&gt; — and&lt;br&gt;
in a mesh, the "why" lives in the edges between services, which is precisely what SigNoz makes&lt;br&gt;
visible.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  From detection to enforcement — the part a single agent can't do
&lt;/h2&gt;

&lt;p&gt;Because the loop spans services, no in-process breaker can see it. So Taraol runs a &lt;strong&gt;Watcher&lt;/strong&gt;: a&lt;br&gt;
separate service that queries recent SigNoz telemetry, groups agent-to-agent spans by trace and&lt;br&gt;
edge, looks for repeated traversal with no progress, and emits a structured &lt;code&gt;loop_detected&lt;/code&gt; &lt;strong&gt;log&lt;br&gt;
record&lt;/strong&gt; — trace-correlated, and tagged with the experiment variant that caused it. On the run&lt;br&gt;
above it emitted both &lt;code&gt;loop_detected&lt;/code&gt; and &lt;code&gt;edge_unhealthy&lt;/code&gt; for the &lt;code&gt;writer → critic&lt;/code&gt; edge.&lt;/p&gt;

&lt;p&gt;The Watcher does &lt;strong&gt;not&lt;/strong&gt; pause anything itself. We kept the operational policy in SigNoz:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;loop_detected log
      └─▶ SigNoz log alert
              └─▶ Controller webhook
                      └─▶ pause agent  /  break edge
                              └─▶ agent_paused / edge_broken  ──▶ back to SigNoz (audit)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why the separation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;strong&gt;Watcher&lt;/strong&gt; decides the telemetry is &lt;em&gt;suspicious&lt;/em&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SigNoz&lt;/strong&gt; decides whether that should trigger a response (a configurable alert rule);&lt;/li&gt;
&lt;li&gt;the &lt;strong&gt;Controller&lt;/strong&gt; performs the action (pause the agent, or trip the per-edge breaker so the bad
hop short-circuits while the rest of the mesh keeps running);&lt;/li&gt;
&lt;li&gt;the resulting &lt;code&gt;agent_paused&lt;/code&gt; / &lt;code&gt;edge_broken&lt;/code&gt; event is exported &lt;strong&gt;back&lt;/strong&gt; to SigNoz, so the
intervention is itself observable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing happens silently. An operator gets a visible query, an alert rule, a notification path,&lt;br&gt;
and a full audit trail.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📸 &lt;strong&gt;Figure 4 — Logs Explorer&lt;/strong&gt; filtered to &lt;code&gt;body = 'loop_detected'&lt;/code&gt;, showing the signal with&lt;br&gt;
its &lt;code&gt;experiment_variant&lt;/code&gt; and the &lt;code&gt;writer → critic&lt;/code&gt; edge. &lt;strong&gt;Caption:&lt;/strong&gt; &lt;em&gt;"Detection lives in the&lt;br&gt;
telemetry, not the agent code — the loop no single service could see, caught and made&lt;br&gt;
searchable next to the traces that caused it."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;📸 &lt;strong&gt;Figure 5 (optional) — the audit event.&lt;/strong&gt; Logs filtered to &lt;code&gt;body = 'agent_paused'&lt;/code&gt; or&lt;br&gt;
&lt;code&gt;'edge_broken'&lt;/code&gt;, showing the Controller's action correlated back to the trace.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  How SigNoz is deployed: reproducible with Foundry
&lt;/h2&gt;

&lt;p&gt;A judge — or any new user — has to reproduce the exact backend. Taraol ships its SigNoz stack as a&lt;br&gt;
&lt;strong&gt;Foundry&lt;/strong&gt; deployment, not a hand-written Compose file:&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="s"&gt;casting.yaml            what you want (SigNoz version, compose flavor)&lt;/span&gt;
      &lt;span class="s"&gt;│  foundryctl cast&lt;/span&gt;
      &lt;span class="s"&gt;▼&lt;/span&gt;
&lt;span class="s"&gt;casting.yaml.lock       every resolved decision pinned (all components + configs)&lt;/span&gt;
      &lt;span class="s"&gt;▼&lt;/span&gt;
&lt;span class="na"&gt;pours/deployment/       the rendered stack&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;compose + ClickHouse / keeper / ingester configs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both files are committed, so the environment reproduces bit-for-bit:&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;# reproduce the exact backend from the committed spec&lt;/span&gt;
foundryctl cast &lt;span class="nt"&gt;-f&lt;/span&gt; demos/research_mesh/signoz/casting.yaml

&lt;span class="c"&gt;# or let Taraol boot the bundled deployment in one command&lt;/span&gt;
taraol signoz up      &lt;span class="c"&gt;# → SigNoz UI on :8080, OTLP on :4317&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The mesh's &lt;code&gt;compose.yaml&lt;/code&gt; includes this Foundry-rendered SigNoz, so the demo's backend &lt;em&gt;is&lt;/em&gt; the&lt;br&gt;
Foundry deploy. The same output is also bundled inside the pip package, so &lt;code&gt;pip install taraol &amp;amp;&amp;amp;&lt;br&gt;
taraol signoz up&lt;/code&gt; gives any user a Foundry-provisioned SigNoz with no manual Docker steps. To use&lt;br&gt;
SigNoz Cloud instead, point &lt;code&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/code&gt; at your ingest endpoint — no code&lt;br&gt;
changes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📸 &lt;strong&gt;Figure 6 (optional) — the running stack / Dashboards list&lt;/strong&gt; with the imported&lt;br&gt;
&lt;em&gt;"AgentLab — Experiment Comparison"&lt;/em&gt; dashboard. &lt;strong&gt;Caption:&lt;/strong&gt; &lt;em&gt;"The whole backend comes up with&lt;br&gt;
&lt;code&gt;foundryctl cast&lt;/code&gt; or &lt;code&gt;taraol signoz up&lt;/code&gt;."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  What worked, and what we'd change
&lt;/h2&gt;

&lt;p&gt;Held up well:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Experiment attributes across process boundaries.&lt;/strong&gt; Because &lt;code&gt;experiment.id&lt;/code&gt; / &lt;code&gt;variant&lt;/code&gt; /
&lt;code&gt;run_id&lt;/code&gt; propagate over the A2A hops, every service's spans stay comparable — no mixed results.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standard trace propagation was enough.&lt;/strong&gt; W3C context let SigNoz reconstruct the whole
cross-service workflow and derive the Service Map — no custom tracing backend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alerts as a policy boundary.&lt;/strong&gt; Detection and enforcement stayed separate, visible, and
configurable.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Honest limitations (a hackathon system, not a finished safety platform):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pause/breaker state is in memory, so a restart clears it;&lt;/li&gt;
&lt;li&gt;the injection detector is regex-based — a signal, not a complete security layer;&lt;/li&gt;
&lt;li&gt;SigNoz notification-channel setup is still partly manual;&lt;/li&gt;
&lt;li&gt;content capture needs stricter redaction before production;&lt;/li&gt;
&lt;li&gt;the Watcher → alert → Controller path needs stronger end-to-end testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We also ship a smaller single-process demo (a four-agent trip planner) for the quickstart path,&lt;br&gt;
but the mesh is where the distributed story — cross-process traces, the Service Map, and&lt;br&gt;
telemetry-driven loop detection — actually lands.&lt;/p&gt;


&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The useful result wasn't another AI dashboard. It was a &lt;strong&gt;closed, observable path&lt;/strong&gt; for a&lt;br&gt;
distributed agent system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;instrument → experiment → distributed trace → detect → alert → enforce → audit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dashboard showed &lt;em&gt;which&lt;/em&gt; variant was unhealthy. The distributed trace showed &lt;em&gt;why&lt;/em&gt; — a&lt;br&gt;
writer↔critic loop across containers. The Watcher caught it from telemetry that no single service&lt;br&gt;
could see, and the SigNoz alert path turned that detection into a bounded, audited response.&lt;br&gt;
Every step — spans, experiments, detection signals, and enforcement actions — lives in SigNoz.&lt;/p&gt;

&lt;p&gt;Source and the reproducible Foundry configuration are in the&lt;br&gt;
&lt;strong&gt;&lt;a href="https://github.com/MTC-123/Taraol" rel="noopener noreferrer"&gt;Taraol repository&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Appendix: run it yourself
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"taraol[all]"&lt;/span&gt;

&lt;span class="c"&gt;# reproducible SigNoz backend (Foundry)&lt;/span&gt;
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; demos/research_mesh/compose.yaml up &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--build&lt;/span&gt;

&lt;span class="c"&gt;# fire the converging-vs-runaway experiment across the five services&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;research-mesh-planner-1 python &lt;span class="nt"&gt;-m&lt;/span&gt; research_mesh.experiment

&lt;span class="c"&gt;# then open SigNoz on :8080 → Service Map, Traces, the AgentLab dashboard, and&lt;/span&gt;
&lt;span class="c"&gt;# Logs (body = 'loop_detected')&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>opentelemetry</category>
      <category>ai</category>
      <category>observability</category>
      <category>signoz</category>
    </item>
  </channel>
</rss>
