<?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: Gaurav Bomra</title>
    <description>The latest articles on DEV Community by Gaurav Bomra (@gauravstack).</description>
    <link>https://dev.to/gauravstack</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%2F4120435%2F1c67ed64-c25d-43da-ab82-44b97bfa8b5c.png</url>
      <title>DEV Community: Gaurav Bomra</title>
      <link>https://dev.to/gauravstack</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gauravstack"/>
    <language>en</language>
    <item>
      <title>How I Built (and Broke) a Production Multi-Agent AI System</title>
      <dc:creator>Gaurav Bomra</dc:creator>
      <pubDate>Mon, 21 Sep 2026 10:02:50 +0000</pubDate>
      <link>https://dev.to/gauravstack/how-i-built-and-broke-a-production-multi-agent-ai-system-4b2i</link>
      <guid>https://dev.to/gauravstack/how-i-built-and-broke-a-production-multi-agent-ai-system-4b2i</guid>
      <description>&lt;h1&gt;
  
  
  How I Built (and Broke) a Production Multi-Agent AI System
&lt;/h1&gt;

&lt;p&gt;Most agent demos work great — right up until you run them past the happy path. This is the story of building a real multi-agent orchestration system, and three bugs that taught me more than the initial build did.&lt;/p&gt;

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

&lt;p&gt;A production-style agent system with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;supervisor agent&lt;/strong&gt; that decomposes complex tasks and delegates to specialist agents (LangGraph state machine)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistent memory&lt;/strong&gt; — short-term working state in Redis, long-term semantic memory in ChromaDB&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human-in-the-loop approval&lt;/strong&gt; for sensitive actions, with a full review queue and UI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full observability&lt;/strong&gt; — OpenTelemetry tracing on every agent call and tool invocation, cost tracking, and a replay engine&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The stack: Python/FastAPI, LangGraph, PostgreSQL, Redis, ChromaDB, Celery, OpenTelemetry, React.&lt;/p&gt;

&lt;p&gt;The architecture is the easy part to explain. The bugs are the part worth writing about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug #1: The 5,000-row runaway loop
&lt;/h2&gt;

&lt;p&gt;Early on, I noticed one test task had generated over 5,000 duplicate rows in the &lt;code&gt;review_requests&lt;/code&gt; table. A single task. One escalation. Five thousand rows.&lt;/p&gt;

&lt;p&gt;The root cause: the human-in-the-loop escalation logic had no protection against a task being re-evaluated multiple times in quick succession — each re-evaluation created a new pending review request instead of checking if one already existed. Under certain retry/timing conditions, this spiraled.&lt;/p&gt;

&lt;p&gt;The fix had two layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An application-level guard — check for an existing pending review before creating a new one&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;database-level partial unique index&lt;/strong&gt; — &lt;code&gt;UNIQUE (task_id, subtask_id) WHERE status = 'pending'&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The app-level check alone wasn't enough — there was still a race condition window between the check and the insert. The database constraint is what actually closed the gap. Lesson: if a bug can be prevented at the database layer, prevent it there. Application logic is not atomic by default.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug #2: The failure that never failed
&lt;/h2&gt;

&lt;p&gt;While building the observability layer, I had a checkpointer initialization path with a fallback: if the primary checkpointer setup failed, the code would fall back to a sentinel value and continue running — silently, without persistence.&lt;/p&gt;

&lt;p&gt;This is worse than a crash. A crash tells you something is wrong. A silent fallback lets the whole system appear to work while quietly not doing the one thing (state persistence) the rest of the architecture depended on.&lt;/p&gt;

&lt;p&gt;The fix: removed the fallback entirely. If checkpointer init fails now, it raises a hard &lt;code&gt;RuntimeError&lt;/code&gt; at startup, with a clear log line confirming success when it &lt;em&gt;does&lt;/em&gt; work. No degraded-but-invisible states allowed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug #3: Two identifiers that looked the same but weren't
&lt;/h2&gt;

&lt;p&gt;The replay feature let you re-run a past task with a modified input, comparing the old and new outcomes side by side. It worked — until I actually verified &lt;em&gt;what&lt;/em&gt; it was comparing.&lt;/p&gt;

&lt;p&gt;The frontend was sending an OpenTelemetry &lt;code&gt;span_id&lt;/code&gt; to the replay endpoint, where a LangGraph &lt;code&gt;checkpoint_id&lt;/code&gt; was expected. These are two unrelated identifier systems that happen to look like similar opaque strings. The backend's &lt;code&gt;load_checkpoint()&lt;/code&gt; function had its own fallback (see a pattern here?) that would silently reconstruct approximate state from trace data instead of loading the real checkpoint when the ID didn't resolve.&lt;/p&gt;

&lt;p&gt;So replay was "working" — it just wasn't replaying from the real checkpoint most of the time.&lt;/p&gt;

&lt;p&gt;Fix: recorded the actual &lt;code&gt;checkpoint_id&lt;/code&gt; as a span attribute at trace time, and had the frontend send &lt;em&gt;that&lt;/em&gt; instead of the span's own id. I also removed the silent fallback in &lt;code&gt;load_checkpoint()&lt;/code&gt; — now it raises an explicit error if the checkpoint can't be found, rather than quietly approximating. I verified the fix by checking for a &lt;code&gt;[PATH: NATIVE_LANGGRAPH_CHECKPOINT]&lt;/code&gt; log line confirming the real path was actually being used.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern across all three
&lt;/h2&gt;

&lt;p&gt;Every one of these bugs shares something: &lt;strong&gt;a system that appeared to work while silently doing the wrong thing.&lt;/strong&gt; Not crashes — those are easy to catch. Silent degradation is the dangerous failure mode, because your test suite can pass, your demo can look perfect, and the bug only surfaces under conditions you didn't think to simulate.&lt;/p&gt;

&lt;p&gt;If I had to generalize this into a rule for building agent systems: &lt;strong&gt;wherever your code has a fallback "just in case," ask whether that fallback should actually be a hard failure instead.&lt;/strong&gt; A fallback that masks a real problem is worse than no fallback at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this ended up
&lt;/h2&gt;

&lt;p&gt;I packaged the system (with all three fixes already in place) into a starter kit for developers building similar agent products, since most of the pain in this space isn't the "agent" logic — it's this orchestration/memory/observability layer that everyone ends up rebuilding. If you're curious: &lt;a href="https://whop.com/gauravxd/multi-agent-orchestration-kit/" rel="noopener noreferrer"&gt;https://whop.com/gauravxd/multi-agent-orchestration-kit/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But even if you never look at the kit, I hope the bugs are useful on their own. If you're building agent systems and want to compare notes on failure modes you've hit, I'd genuinely like to hear about them.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>langgraph</category>
      <category>python</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How I Built (and Broke, and Fixed) a Production Multi-Agent AI Orchestration System</title>
      <dc:creator>Gaurav Bomra</dc:creator>
      <pubDate>Fri, 11 Sep 2026 07:31:17 +0000</pubDate>
      <link>https://dev.to/gauravstack/how-i-built-and-broke-and-fixed-a-production-multi-agent-ai-orchestration-system-264g</link>
      <guid>https://dev.to/gauravstack/how-i-built-and-broke-and-fixed-a-production-multi-agent-ai-orchestration-system-264g</guid>
      <description>&lt;p&gt;Why I built it — the gap between toy agent demos and production-ready systems&lt;br&gt;
Architecture overview (LangGraph, memory, HITL, observability) — with the diagram&lt;br&gt;
Deep-dive: the 5,000-row runaway loop bug — root cause, fix, the DB-level guard&lt;br&gt;
Deep-dive: the silent checkpointer fallback — why silent failure is worse than a crash&lt;br&gt;
Deep-dive: the replay identifier mismatch bug&lt;br&gt;
What I'd do differently / lessons for anyone building agent systems&lt;br&gt;
Closing: packaged it as a starter kit, link + PH link&lt;/p&gt;

&lt;p&gt;(This one's worth writing properly rather than templating — it's your best long-term SEO/credibility asset. Do this in the days after launch, not necessarily on launch day itself.)&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
      <category>python</category>
    </item>
  </channel>
</rss>
