<?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: William</title>
    <description>The latest articles on DEV Community by William (@innovate).</description>
    <link>https://dev.to/innovate</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%2F2732448%2F4e18d577-4f6a-462e-80d4-8ced0a5814d6.png</url>
      <title>DEV Community: William</title>
      <link>https://dev.to/innovate</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/innovate"/>
    <language>en</language>
    <item>
      <title>How Liquet became an autonomous marketplace dispute arbitrator powered by three Qwen models, and a 2,000-year-old Roman legal principle</title>
      <dc:creator>William</dc:creator>
      <pubDate>Fri, 10 Jul 2026 15:21:41 +0000</pubDate>
      <link>https://dev.to/innovate/how-liquet-became-an-autonomous-marketplace-dispute-arbitrator-powered-by-three-qwen-models-and-a-7ba</link>
      <guid>https://dev.to/innovate/how-liquet-became-an-autonomous-marketplace-dispute-arbitrator-powered-by-three-qwen-models-and-a-7ba</guid>
      <description>&lt;h2&gt;
  
  
  The Problem We Couldn't Ignore
&lt;/h2&gt;

&lt;p&gt;A buyer files a dispute: "Item not as described. The bag in the photo was leather. What arrived is plastic." The seller responds: "Item matches the listing exactly. Buyer is lying."&lt;/p&gt;

&lt;p&gt;Two conflicting narratives. One order record. One blurry product photo. Somewhere in there is the truth — but no human agent will read it carefully at 11 PM on a Friday.&lt;/p&gt;

&lt;p&gt;Online marketplaces collectively handle hundreds of millions of disputes every year. The resolution process at most platforms is, charitably, a keyword filter with a confidence score hardcoded to sound decisive. The systems that call themselves "AI-powered" are almost always rule engines that optimize for throughput over correctness. On genuinely ambiguous cases, they guess.&lt;/p&gt;

&lt;p&gt;We kept asking: what would it look like if the AI actually &lt;em&gt;reasoned&lt;/em&gt; over a dispute the way a careful adjudicator would? And — critically — what would it look like if it knew when to stop and say "I'm not sure"?&lt;/p&gt;

&lt;p&gt;That question led us to Roman law.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Latin Phrase That Named the Project
&lt;/h2&gt;

&lt;p&gt;In ancient Roman courts, when a jury had heard all the evidence and still could not decide, a juror was entitled to return a verdict of &lt;em&gt;non liquet&lt;/em&gt; — "it is not clear" — rather than force a guess. That option, the structured right to abstain, is what separates honest adjudication from fast adjudication.&lt;/p&gt;

&lt;p&gt;The moment we articulated that principle, the project had a name and a north star: &lt;strong&gt;LIQUET&lt;/strong&gt; — build an agent that resolves disputes it &lt;em&gt;can&lt;/em&gt; resolve, and says so clearly when it cannot.&lt;/p&gt;

&lt;p&gt;Every existing dispute-automation tool we examined had the same flaw: no principled abstention mechanism. The most dangerous failure mode in automated dispute resolution isn't slow resolution — it's a system that is wrong with high confidence.&lt;/p&gt;




&lt;h2&gt;
  
  
  What We Built
&lt;/h2&gt;

&lt;p&gt;Liquet is a fully autonomous dispute arbitration agent. A dispute enters the system — through email, API, or the web dashboard — and Liquet runs a complete three-stage AI pipeline, stress-tests its own verdict with a built-in adversarial pass, runs adjudication three times to verify the reasoning is stable, and then makes a binary decision:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LIQUET ✅&lt;/strong&gt; — confidence ≥ 80%, order value &amp;lt; $500, no hard contradictions, verdict stable → resolved autonomously, both parties notified, webhook fired&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NON LIQUET ⚠&lt;/strong&gt; — any condition violated → escalated to human reviewer with a complete decision brief&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The gate is entirely deterministic Python. It cannot be hallucinated past.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Three-Model Pipeline
&lt;/h2&gt;

&lt;p&gt;One of the earliest and most important decisions we made was: &lt;strong&gt;don't route everything through one model.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 1: qwen3.6-flash — Triage
&lt;/h3&gt;

&lt;p&gt;Before spending a single premium token on full investigation, every incoming dispute goes through a fast triage pass. In milliseconds, qwen3.6-flash classifies the dispute category, scores its complexity, assigns a priority, and estimates resolution time. This pre-classification routes simple cases efficiently and prevents the expensive pipeline from wasting compute on disputes that can be resolved with minimal evidence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 2: qwen3.6-plus — Visual Evidence
&lt;/h3&gt;

&lt;p&gt;Many marketplace disputes live or die on physical evidence: the product photo in the listing versus the photo the buyer uploaded. qwen3.6-plus handles every call that carries images — damage detection, label verification, packaging inspection, screenshot analysis of tracking pages. The vision model doesn't write verdicts; it writes evidence summaries that the reasoning model then weighs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 3: qwen3.7-max — Reasoning and Adjudication
&lt;/h3&gt;

&lt;p&gt;The flagship model handles the calls where extended chain-of-thought actually improves correctness: the full adjudication pass over the assembled case file, the adversarial skeptic rebuttal, the email parsing that turns a buyer's complaint email into a structured case. qwen3.7-max also powers &lt;strong&gt;ReasoningGlass&lt;/strong&gt; — a live stream of the model's extended-thinking tokens displayed in the dashboard as the adjudicator works, so reviewers can watch the agent reason in real time.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Feature That Changed Everything: The Skeptic
&lt;/h2&gt;

&lt;p&gt;Early versions of Liquet had a critical flaw we didn't notice until we started reading the verdicts carefully.&lt;/p&gt;

&lt;p&gt;The adjudicator was too confident. Given both narratives and all evidence, it reliably produced a verdict — and then the human reviewer would read it and immediately see the one thing it missed: the seller's counter-claim about return shipping that, if true, changed the outcome entirely.&lt;/p&gt;

&lt;p&gt;We added a &lt;strong&gt;Skeptic Pass&lt;/strong&gt;: after the primary verdict, a second qwen3.7-max call receives &lt;em&gt;only&lt;/em&gt; the losing party's narrative and the primary verdict. Its instruction is to generate the strongest possible case against the verdict. It has no access to the primary reasoning, only the evidence that favors the side that lost.&lt;/p&gt;

&lt;p&gt;The first version still agreed with the primary verdict most of the time — because it could see the same evidence the adjudicator saw. The fix was &lt;strong&gt;forced adversarial asymmetry&lt;/strong&gt;: give the skeptic &lt;em&gt;only&lt;/em&gt; the losing narrative, nothing else. That produced real rebuttals. On cases where the skeptic raised a hard contradiction the primary pass had underweighted, the case correctly escalated.&lt;/p&gt;

&lt;p&gt;A system with a built-in mechanism for arguing against itself is harder to fool than one that always confirms its own reasoning.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stability Scoring: Running It Three Times on Purpose
&lt;/h2&gt;

&lt;p&gt;Here's a non-obvious thing we discovered: &lt;strong&gt;a model that gives the same answer every time is not the same as a model that gives the right answer.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We ran the adjudicator on the same case file with temperature set to zero — perfectly deterministic — and got identical outputs across three runs. But when we introduced slight temperature variation and shuffled the order of evidence items in the system prompt between runs, something interesting happened: about 15% of cases produced different outcomes across the three passes.&lt;/p&gt;

&lt;p&gt;Those cases were almost always the ones where a human reviewer, reading the brief, would say "I can see why someone could go either way here." The variance was real signal about genuine ambiguity — not noise.&lt;/p&gt;

&lt;p&gt;So we kept it. &lt;strong&gt;Stability scoring&lt;/strong&gt; runs three adjudication passes with slightly varied conditions, measures the fraction that agree, and applies a stability penalty to the effective confidence when they don't. A verdict that holds across all three runs earns its confidence score. One that splits is penalised. One that produces three different resolutions cannot pass the LIQUET gate regardless of what the nominal confidence number says.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;effective_confidence = raw_confidence × stability_score&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Ghost Cases: Grounding the Agent in History
&lt;/h2&gt;

&lt;p&gt;One of the subtler features — and one of the most impactful — is &lt;strong&gt;ghost case injection&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Before the primary adjudication pass, Liquet queries the historical case database for disputes with matching category, overlapping order value bracket, and the same set of evidence types present. Up to five matching historical cases, with their final resolution and outcome, are injected into the adjudicator's system prompt as precedent examples.&lt;/p&gt;

&lt;p&gt;The practical effect is that the model reasons over what the platform has actually decided before, not just its general prior about disputes. On categories where the platform has established a strong track record — "never arrived" cases with confirmed non-delivery scan carry a 92% buyer-favor rate in the database — the adjudicator grounds its reasoning in that pattern rather than starting from scratch.&lt;/p&gt;

&lt;p&gt;When no close historical matches exist, the case adjudicates on first principles and Liquet says so explicitly in the brief.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing the Loop: Email-to-Resolution with No Human Touch
&lt;/h2&gt;

&lt;p&gt;The full closed loop that we're most proud of:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Buyer sends a complaint email&lt;/li&gt;
&lt;li&gt;The IMAP poller detects it, passes it to qwen3.7-max for parsing&lt;/li&gt;
&lt;li&gt;The parser extracts buyer narrative, order ID, dispute category, and any attached photos&lt;/li&gt;
&lt;li&gt;A new case is automatically submitted and queued for investigation&lt;/li&gt;
&lt;li&gt;The orchestrator runs the full pipeline&lt;/li&gt;
&lt;li&gt;On LIQUET: both buyer and seller receive a verdict email; a signed webhook fires to the external system&lt;/li&gt;
&lt;li&gt;On NON LIQUET: the human reviewer receives a decision brief and a HMAC-SHA256 signed one-click approval link&lt;/li&gt;
&lt;li&gt;The reviewer clicks approve or override — no separate login, no UI navigation required&lt;/li&gt;
&lt;li&gt;The case closes with a full immutable audit trail&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;End-to-end, zero human involvement on LIQUET cases. For the reviewer on NON LIQUET cases, the cognitive load is minimized: every piece of information needed to decide is pre-surfaced on one screen, with confidence breakdown, stability scores, and the exact reason the case abstained.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Working with Qwen Cloud Taught Us
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Model tier selection matters more than always reaching for the biggest model.&lt;/strong&gt; The naive approach — route everything through qwen3.7-max — would have made the system 4–5× more expensive and no more accurate on the simple cases that make up the majority of volume. qwen3.6-flash at triage costs a fraction of a qwen3.7-max call and makes decisions that are good enough to route correctly. Using the right model for the right task is an architectural decision, not an afterthought.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extended thinking is genuinely valuable, but only for the right calls.&lt;/strong&gt; qwen3.7-max's extended chain-of-thought pass improved our adjudication quality measurably on complex cases with conflicting evidence. But for the skeptic pass — where we want a focused adversarial rebuttal, not an essay — it added latency without benefit. Knowing when to turn it on versus off took experimentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The QwenCloud API's reliability under concurrent load held up throughout.&lt;/strong&gt; Running three stability passes concurrently, with a vision call happening in parallel, under our semaphore-limited concurrency of 3 simultaneous investigations, we didn't see unexpected rate limit failures once we respected the concurrency model. The structured output support for JSON schema enforcement made our Pydantic integration clean.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Architecture: Why It's Built to Last
&lt;/h2&gt;

&lt;p&gt;Liquet's core pattern — fast triage → deep evidence analysis → adversarial stress test → deterministic gate → human escalation with brief — is not specific to marketplace disputes. The same structure applies to insurance claim routing, vendor invoice disputes, content moderation appeals, and any domain where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inputs are ambiguous and partially contradictory human narratives&lt;/li&gt;
&lt;li&gt;Some cases can be resolved confidently and some genuinely cannot&lt;/li&gt;
&lt;li&gt;Wrong confident answers are more dangerous than honest abstentions&lt;/li&gt;
&lt;li&gt;A human reviewer needs to be fast and correct on the cases that escalate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The domain models and policy document change. The orchestration, gate logic, and audit trail do not. We designed Liquet to be a reference implementation of a pattern, not a one-off tool.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;The gap between "useful demo" and "production system" in dispute resolution is: real EHR integration, multi-currency jurisdiction-aware policy selection, and longitudinal seller risk scoring. We know exactly where the seams are. The architecture is already async SQLAlchemy on top of SQLite — switching to PostgreSQL for real concurrent load is a &lt;code&gt;DATABASE_URL&lt;/code&gt; change. The repository layer is already abstracted.&lt;/p&gt;

&lt;p&gt;The next version of Liquet doesn't need a new system. It needs a real data feed, a real policy library, and time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="http://liquet.43.98.167.71.sslip.io" rel="noopener noreferrer"&gt;http://liquet.43.98.167.71.sslip.io&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Source code:&lt;/strong&gt; &lt;a href="https://github.com/nnam-droid12/Liquet" rel="noopener noreferrer"&gt;https://github.com/nnam-droid12/Liquet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Deployed on Alibaba Cloud ECS, Singapore. QwenCloud API at &lt;code&gt;dashscope-intl.aliyuncs.com/compatible-mode/v1&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Liquet knows when it's clear — and admits when it isn't.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>alibabacloud</category>
      <category>qwencloud</category>
      <category>python</category>
    </item>
    <item>
      <title>Building HELPIT, a memory layer for robotic dental surgery on Qwen Cloud — and the bugs that taught me more than the features did.</title>
      <dc:creator>William</dc:creator>
      <pubDate>Fri, 03 Jul 2026 14:34:01 +0000</pubDate>
      <link>https://dev.to/innovate/giving-robotic-dental-surgery-a-memory-building-helpit-on-qwen-cloud-jg2</link>
      <guid>https://dev.to/innovate/giving-robotic-dental-surgery-a-memory-building-helpit-on-qwen-cloud-jg2</guid>
      <description>&lt;h2&gt;
  
  
  The problem that started this
&lt;/h2&gt;

&lt;p&gt;Picture the second right before a robotic arm lowers a drill bit toward a patient's jaw. It has sub-millimeter precision. It has a plan, loaded and ready. What it doesn't have is any idea that this same patient's last implant failed on the other side of their mouth three years ago.&lt;/p&gt;

&lt;p&gt;That's not hypothetical. Robotic dental implant surgery isn't science fiction anymore — implant-placement robots are already in clinical use, drilling and seating implants with precision no human hand can match. But precision was never the missing piece. Memory was.&lt;/p&gt;

&lt;p&gt;Every one of these systems starts every procedure the exact same way: cold. The robot has no idea this patient's bone density scan looked different six months ago than it does today. It has no idea the last patient with this exact bone-density class and this exact implant brand had a 30% higher rejection rate the moment torque crossed 45 Ncm. That knowledge lives in one dentist's head, in a paper chart nobody cross-references mid-procedure, or nowhere at all — a robot with world-class precision and the situational awareness of a stranger.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;HELPIT&lt;/strong&gt; — a memory layer that sits between the dentist, the patient record, and the robot, and refuses to let a procedure start without the full picture: this patient's own history, what's worked across thousands of similar patients, and a hard gate that can say &lt;em&gt;pause&lt;/em&gt; or &lt;em&gt;stop&lt;/em&gt; before anything happens.&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%2Fraw.githubusercontent.com%2Fnnam-droid12%2FHelpit%2Fmain%2Fdocs%2Fhelpit-architecture-diagram-technical.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%2Fraw.githubusercontent.com%2Fnnam-droid12%2FHelpit%2Fmain%2Fdocs%2Fhelpit-architecture-diagram-technical.png" alt="HELPIT architecture — frontend, backend, memory layer, and Qwen Cloud" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What HELPIT actually does
&lt;/h2&gt;

&lt;p&gt;Before any robotic procedure begins, HELPIT runs a memory-and-safety pipeline and hands the dentist a cited, evidence-backed brief — never a robot's own unsupervised call:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Retrieve memory&lt;/strong&gt; — episodic history for &lt;em&gt;this&lt;/em&gt; patient (prior procedures, complications, healing trajectory) and semantic memory (population-level outcome patterns for this bone-density class), pulled in parallel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evaluate the gate&lt;/strong&gt; — a deterministic &lt;strong&gt;PROCEED / PAUSE / ESCALATE&lt;/strong&gt; decision. One hard contraindication (a material allergy, an anticoagulant flag, a scan contradiction) always outranks an otherwise-good composite score. This part is plain Python, not a model call — more on why below, because it's the decision I'd defend hardest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approve &amp;amp; monitor&lt;/strong&gt; — the dentist reviews a cited brief and approves. Every step is tracked live against plan, with automatic pause on deviation — and, mid-procedure, a live co-pilot that checks whether anything like this deviation has happened before and speaks back what actually worked last time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consolidate outcomes&lt;/strong&gt; — post-procedure results get written back into memory automatically, so the next patient with similar anatomy benefits from what was just learned.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why the gate itself isn't an LLM call
&lt;/h2&gt;

&lt;p&gt;This is the design decision I'd defend hardest, so let me actually defend it.&lt;/p&gt;

&lt;p&gt;It would have been trivial to have Qwen just... decide. Feed it the patient's history and the population data, ask for PROCEED, PAUSE, or ESCALATE, done. It would have demoed fine. It also would have been the kind of decision that quietly changes on a re-roll, or that a model gets subtly wrong in a way nobody notices until it matters. A safety-critical branch that can silently drift isn't something I was willing to ship, hackathon or not.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;gate_evaluator.py&lt;/code&gt; decides nothing with a model. It's deterministic Python, full stop:&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;has_allergy_or_systemic_flag&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;has_anatomy_contradiction&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;risk_score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gate_risk_escalate_threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;gate_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GateResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ESCALATE&lt;/span&gt;
&lt;span class="nf"&gt;elif &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;memory_completeness&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gate_memory_completeness_threshold&lt;/span&gt;
    &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;anatomy_match_score&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gate_anatomy_match_threshold&lt;/span&gt;
    &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;risk_score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gate_risk_pause_threshold&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;gate_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GateResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PAUSE&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;gate_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GateResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PROCEED&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Qwen's job is everything &lt;em&gt;around&lt;/em&gt; that decision — writing the narrative that explains it, citing the specific records it's grounded in, generating the anatomy-drift summary a dentist actually reads. The decision itself is reproducible, testable, and immune to a bad generation. I have an entire pytest file whose only job is proving a hard contraindication can never be masked by an otherwise-good composite score, no matter what any model outputs on any given run.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Qwen Cloud actually gets used, model by model
&lt;/h2&gt;

&lt;p&gt;I didn't want one model doing everything by default. A hackathon build where every call just goes to "whichever model is handy" would have been both slower and dishonest about what each call actually needs. So the routing here is deliberate, not default:&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;Used for&lt;/th&gt;
&lt;th&gt;Why this one&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;qwen3.7-max&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The surgery-ready brief&lt;/td&gt;
&lt;td&gt;The single highest-stakes generation call in the pipeline — worth the deepest reasoning model available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;qwen3.6-flash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The anatomy-drift narrative, and the live co-pilot's mid-procedure advisory&lt;/td&gt;
&lt;td&gt;Short, grounded output that doesn't need the flagship model — and for the co-pilot, latency &lt;em&gt;is&lt;/em&gt; the feature: a surgeon mid-procedure needs one sentence in under two seconds, not a paragraph in ten&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;qwen-vl-plus&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;X-ray/CBCT analysis, post-op video-frame review&lt;/td&gt;
&lt;td&gt;The only calls that actually carry images&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;text-embedding-v3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cross-patient case recall and deviation-event recall&lt;/td&gt;
&lt;td&gt;Ranks by cosine similarity against every other patient's history — a practice-wide semantic index, not a per-patient linear scan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wan2.6-t2v&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;AI-generated, narrated procedure preview video&lt;/td&gt;
&lt;td&gt;Not a chat-completion model at all — goes through DashScope's native async task API (submit, poll by &lt;code&gt;task_id&lt;/code&gt;) instead of the OpenAI-compatible surface everything else uses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cosyvoice-v3-plus&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Spoken co-pilot advisories and video-analysis Q&amp;amp;A&lt;/td&gt;
&lt;td&gt;The one model in this stack that talks back — over a WebSocket, not a REST call&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The anatomy narrative and the surgery brief run &lt;strong&gt;concurrently&lt;/strong&gt; via &lt;code&gt;asyncio.gather&lt;/code&gt;, since neither depends on the other's output — that alone keeps end-to-end gate evaluation to about 9 seconds instead of stacking two model calls sequentially.&lt;/p&gt;

&lt;h3&gt;
  
  
  The bug that taught me the most about Qwen3.x specifically
&lt;/h3&gt;

&lt;p&gt;When I switched the brief generator to &lt;code&gt;qwen3.7-max&lt;/code&gt; — to use the strongest reasoning model for the highest-stakes call — gate evaluation quietly went from ~9 seconds to &lt;strong&gt;~50 seconds&lt;/strong&gt;. Same prompt. Same output quality. Five times slower, for nothing.&lt;/p&gt;

&lt;p&gt;The cause: the Qwen3.x family defaults to an extended chain-of-thought "thinking" pass before the actual answer. Genuinely useful for open-ended reasoning. Completely wasted here, because the &lt;em&gt;decision&lt;/em&gt; is already deterministic Python by the time the model is called — there's nothing left for extended thinking to improve when the model's only job is writing narrative around an answer that's already been computed.&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_client&lt;/span&gt;&lt;span class="p"&gt;.&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;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model_reasoning&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;extra_body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;enable_thinking&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One flag. Verified with direct before/after timing against the live API: straight back to ~9 seconds, identical eval-harness accuracy — 100% gate accuracy, 0% false-PROCEED rate, both before and after. I would not have caught this by trusting that "point at the flagship model" meant the job was done. I caught it by actually timing real calls against a real API and refusing to believe a 5x slowdown was just "how it is."&lt;/p&gt;

&lt;h2&gt;
  
  
  Making MCP actually real, not just decorative
&lt;/h2&gt;

&lt;p&gt;HELPIT runs on 7 FastMCP tool servers — patient memory, imaging, procedure tracking, a semantic knowledge base, risk scoring, dentist preferences, outcome tracking. The honest first version of this only ever called them as plain Python functions, in-process. &lt;code&gt;@mcp.tool()&lt;/code&gt; was doing nothing but organizing code into folders with an official-looking decorator on top. Functionally fine. Not actually "using MCP" in any way a skeptical reader should accept.&lt;/p&gt;

&lt;p&gt;So two of the servers now also carry a real standalone entrypoint:&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;mcp&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;transport&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stdio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And I wrote a client script that proves it isn't decorative — a genuine MCP &lt;code&gt;initialize()&lt;/code&gt; handshake, &lt;code&gt;list_tools()&lt;/code&gt;, &lt;code&gt;call_tool()&lt;/code&gt;, over the actual protocol, against the &lt;em&gt;same&lt;/em&gt; live SQLite database the running app uses:&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="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call_server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StdioServerParameters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;executable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-m&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;cwd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;REPO_ROOT&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;stdio_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;as &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;ClientSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running it against &lt;code&gt;patient_memory_server&lt;/code&gt; returns the same five seeded patients the web app shows. A standalone MCP client and the FastAPI app, reading identical memory through two completely different transports. That's the difference between "we used MCP" as a slide bullet and "we used MCP" as something you can actually go run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real anatomy, and the decimation algorithm nobody asked for
&lt;/h2&gt;

&lt;p&gt;I wanted the 3D tooth and implant previews to be actual anatomy, not procedural boxes-and-spheres wearing a tooth costume. Three separate attempts at hand-built geometry all read as "blocky toy" or, memorably, "light bulb" — a fully round, radially-symmetric shape structurally can't represent a tooth's flat mesial, distal, buccal, and lingual walls. No amount of clever code fixes that. Only a real scan does.&lt;/p&gt;

&lt;p&gt;So I sourced one — a full maxillary arch, public domain, from NIH 3D. &lt;strong&gt;951,316 vertices at 34MB&lt;/strong&gt;, straight off a real scan. Beautiful. Also completely unusable: a browser tab auto-rotating that mesh next to several &lt;em&gt;other&lt;/em&gt; live 3D canvases ground to a crawl. Three.js's standard edge-collapse simplifier was still churning after twenty minutes and counting.&lt;/p&gt;

&lt;p&gt;So I wrote a decimator instead — the simplest one that could possibly work. Snap every vertex to a 3D grid cell sized as a fraction of the mesh's bounding-box diagonal. Average every vertex that lands in the same cell into one point. Remap every triangle's indices through that old-to-new mapping. Drop anything that degenerated into a zero-area sliver in the process.&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;cell_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;diagonal&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.0026&lt;/span&gt;
&lt;span class="n"&gt;cell&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;bbox_min&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;cell_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;axis&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;# accumulate all vertices sharing a cell, average, remap triangle indices
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;951,316 vertices became 128,720 (13.5%). 1.9 million triangles became 264,000. 34MB became 13.2MB. Twenty-plus minutes became a few seconds. It is not a clever algorithm — it's arguably the crudest spatial hash you could write — and that's exactly the point. The textbook-correct simplifier was strictly better geometry and strictly useless for what I actually needed, which was "finishes before I lose interest in waiting for it."&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it stands right now
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;100% gate accuracy, 0% false-PROCEED rate&lt;/strong&gt; on a labeled eval harness — the single most dangerous failure mode a system like this can have (a false PROCEED on a patient who should PAUSE or ESCALATE) is checked explicitly, and hard-fails the eval run the moment it's ever non-zero.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;42 pytest tests&lt;/strong&gt;, gating every deploy in CI — including a permanent regression test for a genuinely nasty SQLAlchemy bug: mutating a JSON column's cached list in place and reassigning the &lt;em&gt;same&lt;/em&gt; object reference is a silent no-op for the ORM's dirty-tracking. The API response looked completely correct. The database write just never happened, and nothing said otherwise until a second, independent read proved it.&lt;/li&gt;
&lt;li&gt;A live memory co-pilot that speaks up the instant a step deviates mid-procedure, grounded in what actually happened the last time something similar occurred — and honest enough to say "nothing similar on file" instead of fabricating confidence when it has nothing to go on.&lt;/li&gt;
&lt;li&gt;Deployed on Alibaba Cloud ECS, redeployed automatically on every push to &lt;code&gt;main&lt;/code&gt;, gated by that same test suite.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Real EHR integration instead of synthetic patient data.&lt;/li&gt;
&lt;li&gt;A second reference implementation of the same episodic + semantic + gate + live-co-pilot pattern in a non-dental domain, to make the "this architecture generalizes" claim concrete instead of just argued.&lt;/li&gt;
&lt;li&gt;Feeding the live co-pilot a real practice's historical deviation/outcome records on day one, instead of starting its memory from zero.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're working with Qwen Cloud's model family and have run into the &lt;code&gt;enable_thinking&lt;/code&gt; latency trap yourself, or have opinions on where the PROCEED/PAUSE/ESCALATE line should actually sit for a safety-critical gate — I'd genuinely like to hear about it in the comments.&lt;/p&gt;

</description>
      <category>qwencloud</category>
      <category>ai</category>
      <category>alibabacloud</category>
      <category>python</category>
    </item>
    <item>
      <title>Building Choragi: How We Orchestrated a 6-Agent Concert Planning System with Gemini Live and Google Cloud</title>
      <dc:creator>William</dc:creator>
      <pubDate>Fri, 13 Mar 2026 11:35:52 +0000</pubDate>
      <link>https://dev.to/innovate/building-choragi-how-we-orchestrated-a-6-agent-concert-planning-system-with-gemini-live-and-google-4jg7</link>
      <guid>https://dev.to/innovate/building-choragi-how-we-orchestrated-a-6-agent-concert-planning-system-with-gemini-live-and-google-4jg7</guid>
      <description>&lt;p&gt;*We created this piece of content for the purposes of entering the Gemini Live Agent Challenge. &lt;/p&gt;

&lt;h1&gt;
  
  
  GeminiLiveAgentChallenge*
&lt;/h1&gt;

&lt;p&gt;Music has a universal power to heal and bring communities together. But behind every magical live concert is a grueling logistical nightmare. Independent artists and event planners spend weeks scouting venues, making stressful phone calls to negotiate rates, designing promotional materials, and manually wrestling with ad campaigns. &lt;/p&gt;

&lt;p&gt;We thought: &lt;em&gt;What if we could build an engine that takes away the logistical burden, turning a month of planning into a 5-minute automated sequence?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Enter &lt;strong&gt;Choragi&lt;/strong&gt;—an autonomous, multi-agent event orchestration system triggered entirely by a natural voice conversation. In this post, I’ll break down how we built this system using Java Spring Boot, Gemini Live API, Google Cloud Run, and the absolute cutting edge of Google's generative AI models.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture: A Serverless Symphony
&lt;/h2&gt;

&lt;p&gt;Choragi is not a single monolith; it is a highly specialized microservices architecture. We built 6 independent Spring Boot applications and deployed them entirely on &lt;strong&gt;Google Cloud Run&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;By leveraging Cloud Run, we achieved a scalable, serverless backend where each agent scales independently based on its workload. We secured the entire internal network using Google Cloud's &lt;strong&gt;Application Default Credentials (ADC)&lt;/strong&gt;, entirely eliminating the need for hardcoded service account keys in our codebase.&lt;/p&gt;

&lt;p&gt;Here is the breakdown of the fleet:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;UI Dashboard (&lt;code&gt;ui-client&lt;/code&gt;)&lt;/strong&gt;: The real-time WebSockets command center.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Venue Scout (&lt;code&gt;venue-finder&lt;/code&gt;)&lt;/strong&gt;: Discovers potential concert spaces.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Live Negotiator (&lt;code&gt;live-negotiator&lt;/code&gt;)&lt;/strong&gt;: Telephony agent connecting Twilio to Gemini.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Creative Director (&lt;code&gt;creative-director&lt;/code&gt;)&lt;/strong&gt;: Generates 8K posters and cinematic video trailers.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Site Builder (&lt;code&gt;site-builder&lt;/code&gt;)&lt;/strong&gt;: Autonomously deploys a live promotional website.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Digital Promoter (&lt;code&gt;digital-promoter&lt;/code&gt;)&lt;/strong&gt;: Navigates Google Ads to launch campaigns.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Brains: Integrating Google AI Models
&lt;/h2&gt;

&lt;p&gt;To make this pipeline truly autonomous, we had to fuse multiple modalities of AI. &lt;/p&gt;

&lt;h3&gt;
  
  
  1. Real-Time Telephony with Gemini 2.5 Flash Native Audio
&lt;/h3&gt;

&lt;p&gt;The most ambitious part of Choragi was the &lt;code&gt;live-negotiator&lt;/code&gt;. We wanted the AI to actually call a venue owner over the phone and negotiate a booking space.&lt;/p&gt;

&lt;p&gt;We bridged a Twilio WebSocket stream directly to the &lt;strong&gt;Gemini BidiGenerateContent API&lt;/strong&gt;. Because Twilio streams audio in 8kHz MuLaw format and Gemini strictly requires 16kHz PCM, we had to build an on-the-fly byte transcoder in Java. We utilized the &lt;code&gt;["AUDIO"]&lt;/code&gt; response modality to ensure the model spoke naturally, and we implemented a custom Voice Activity Detection (VAD) algorithm using RMS thresholding to prevent noisy phone lines from interrupting the AI.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Multi-Modal Creative Generation
&lt;/h3&gt;

&lt;p&gt;Once the venue is secured, the &lt;code&gt;creative-director&lt;/code&gt; agent takes over to generate promotional assets, directly uploading the results to a public &lt;strong&gt;Google Cloud Storage&lt;/strong&gt; bucket.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tour Posters:&lt;/strong&gt; We utilized &lt;strong&gt;Gemini 2.5 Flash Image&lt;/strong&gt; to generate highly realistic, professional concert posters that strictly adhered to text prompts for the artist's name and location.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cinematic Video Trailers:&lt;/strong&gt; We integrated &lt;strong&gt;Vertex AI Veo 3.0 Fast&lt;/strong&gt; via its Long-Running Operations REST API to generate photorealistic concert stage visuals. &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Robotic Web Automation with Gemini Vision
&lt;/h3&gt;

&lt;p&gt;For the &lt;code&gt;digital-promoter&lt;/code&gt;, we didn't want to use standard APIs. We wanted the agent to navigate the web like a human. Using Microsoft Playwright in a headless Chromium container, the agent literally "looks" at the Google Ads dashboard using &lt;strong&gt;Gemini 2.5 Flash&lt;/strong&gt;. The model analyzes the screenshot of the DOM and outputs precise visual coordinates and text commands (e.g., &lt;code&gt;CLICK_TEXT: Page views&lt;/code&gt;, &lt;code&gt;FILL_FIELD: businessName&lt;/code&gt;) to autonomously launch the campaign.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hardest Technical Challenge: Vertex AI REST Routing
&lt;/h2&gt;

&lt;p&gt;When building hackathon projects, you quickly find the bleeding edge of new APIs. When integrating Veo 3.0 Fast, we bypassed the standard SDK and hit the raw Vertex AI &lt;code&gt;predictLongRunning&lt;/code&gt; REST endpoints. &lt;/p&gt;

&lt;p&gt;We successfully authenticated and triggered the video generation, receiving an Operation ID (a UUID) to poll for the video's completion. However, we discovered a routing quirk: polling the standard &lt;code&gt;v1&lt;/code&gt; API with a UUID resulted in a &lt;code&gt;400 Bad Request: The Operation ID must be a Long&lt;/code&gt;. The &lt;code&gt;v1&lt;/code&gt; endpoint was strictly expecting numeric legacy IDs!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt; We engineered a resilient fallback mechanism. We stripped the publisher paths from the operation name and routed the polling request to the experimental &lt;code&gt;v1beta1&lt;/code&gt; endpoint. Furthermore, we wrapped the polling loop in a "God Mode" safety net—if the Google routing API threw an exception, our Spring Boot service caught the &lt;code&gt;HttpStatusCodeException&lt;/code&gt;, gracefully waited 40 seconds for the backend Veo rendering to finish, and successfully returned the Cloud Storage URL anyway. The architecture held strong.&lt;/p&gt;

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

&lt;p&gt;Building Choragi pushed us to the limits of real-time streaming, asynchronous microservices, and multi-modal AI orchestration. By combining the infrastructure of Google Cloud with the intelligence of Gemini and Vertex AI, we successfully transformed the logistical chaos of event planning into an elegant, autonomous engine. &lt;/p&gt;

&lt;p&gt;Artists should spend their time creating music that heals, not navigating ad campaigns and making cold calls. With AI orchestration, we can finally let them get back to the music.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Check out our full repository and project submission for the #GeminiLiveAgentChallenge!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>geminiliveagentchallenge</category>
      <category>googlecloud</category>
      <category>ai</category>
      <category>java</category>
    </item>
    <item>
      <title>From First Issue to Merged PR: My Journey Contributing to Spring AI</title>
      <dc:creator>William</dc:creator>
      <pubDate>Sun, 19 Oct 2025 14:42:48 +0000</pubDate>
      <link>https://dev.to/innovate/from-first-issue-to-merged-pr-my-journey-contributing-to-spring-ai-i2d</link>
      <guid>https://dev.to/innovate/from-first-issue-to-merged-pr-my-journey-contributing-to-spring-ai-i2d</guid>
      <description>&lt;h2&gt;
  
  
  INTRODUCTION
&lt;/h2&gt;

&lt;p&gt;For a long time, I admired open-source contributors from afar, people who helped build the libraries, frameworks, and tools that power the software we use daily. This October, during Hacktoberfest 2025, I decided to finally take the leap and make my first real contribution to open source.&lt;/p&gt;

&lt;p&gt;That experience, contributing to Spring AI, a project under the Spring Framework turned out to be both challenging and rewarding. In this post, I’ll share what I did, what I learned, and why I think everyone should try contributing at least once.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Started
&lt;/h2&gt;

&lt;p&gt;I joined Hacktoberfest this year with a simple goal: to understand what contributing to open source really feels like. After exploring a few repositories, I noticed Spring AI, an application framework for AI engineering built by the Spring team.&lt;/p&gt;

&lt;p&gt;As someone interested in backend development, Java, and AI integrations, this felt like the perfect project to dive into. I scanned through open issues, read contribution guidelines, and picked an issue related to fixing Azure OpenAI auto-configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Contribution
&lt;/h2&gt;

&lt;p&gt;The task was small but technical, updating a Spring Boot auto-configuration class to include the correct dependency for ToolCallingAutoConfiguration.&lt;/p&gt;

&lt;p&gt;I forked the repository, cloned it locally, and followed the contribution setup. Along the way, I ran into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Checkstyle formatting errors I didn’t cause but had to understand.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Build failures that taught me how Maven and Spring’s formatting rules work.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A review cycle with one of the project maintainers, who helped me refine the PR.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After I made the requested updates and pushed the final changes, the maintainer rebased, squashed, and merged my code into the project. Seeing my work appear in a widely used open-source framework was an incredible moment.&lt;/p&gt;

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

&lt;p&gt;This single PR taught me a lot more than I expected:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Read contribution guides carefully, every project has its own workflow and formatting rules.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Code reviews are learning opportunities, maintainers often suggest small but important details that improve your engineering mindset.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Even small contributions matter, fixing a line of configuration or documentation still improves the project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Communication is key. Be polite, thank reviewers, and show that you’re open to feedback.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s okay if your PR isn’t merged directly, sometimes maintainers rebase or squash your commits for consistency; what matters is that your work becomes part of the codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hacktoberfest Twist
&lt;/h2&gt;

&lt;p&gt;Although my pull request was part of Hacktoberfest, it wasn’t counted automatically because the maintainer rebased and merged it manually, a common workflow for large enterprise projects.&lt;/p&gt;

&lt;p&gt;Still, the real reward wasn’t the t-shirt. It was seeing my name in the Spring AI commit history and knowing that I contributed to one of the world’s most recognized Java frameworks.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next
&lt;/h2&gt;

&lt;p&gt;This first experience gave me confidence to keep contributing, not just during Hacktoberfest, but throughout the year. My goal now is to explore more open-source projects, particularly those involving:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Java and Spring Boot&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AI integrations&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cloud and backend architecture&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you’re thinking about contributing but haven’t yet, start small. Fix a typo, improve documentation, or pick a “good first issue.” The open-source community is more welcoming than it looks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Contributing to open source is one of the best ways to grow as a developer. It pushes you beyond tutorials into real-world collaboration, code quality, and communication.&lt;/p&gt;

&lt;p&gt;I’m grateful to the Spring AI maintainers, especially @ilayaperumalg&lt;br&gt;
, for their guidance and patience during my first PR review.&lt;/p&gt;

&lt;p&gt;This experience reminded me that even the smallest contributions make a lasting impact, not only on the project but on your growth as a developer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pull Request:&lt;/strong&gt; &lt;a href="https://github.com/spring-projects/spring-ai/pull/4636" rel="noopener noreferrer"&gt;Fix Azure OpenAI Auto-Configuration – spring-projects/spring-ai#4636&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project:&lt;/strong&gt; &lt;a href="https://github.com/spring-projects/spring-ai" rel="noopener noreferrer"&gt;Spring AI GitHub Repository&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Author:&lt;/strong&gt; &lt;a href="https://github.com/nnam-droid12" rel="noopener noreferrer"&gt;William Nnamani&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  hacktoberfest
&lt;/h1&gt;

</description>
      <category>beginners</category>
      <category>opensource</category>
      <category>ai</category>
      <category>hacktoberfest</category>
    </item>
    <item>
      <title>Event-Driven Microservices with Apache Kafka And Spring Boot: A Practical Guide</title>
      <dc:creator>William</dc:creator>
      <pubDate>Fri, 07 Feb 2025 18:39:37 +0000</pubDate>
      <link>https://dev.to/innovate/event-driven-microservices-with-apache-kafka-and-spring-boot-a-practical-guide-2k68</link>
      <guid>https://dev.to/innovate/event-driven-microservices-with-apache-kafka-and-spring-boot-a-practical-guide-2k68</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In modern microservices architecture, event-driven systems play a crucial role in enabling scalable, decoupled, and efficient communication between services. Apache Kafka has emerged as a leading distributed event-streaming platform, widely used for real-time data processing.&lt;/p&gt;

&lt;p&gt;In this article, we will explore how to build an event-driven microservices system using &lt;strong&gt;Java Spring Boot&lt;/strong&gt; and &lt;strong&gt;Apache Kafka&lt;/strong&gt;, with a practical example of decoupling &lt;strong&gt;order processing from inventory management&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Event-Driven Microservices?
&lt;/h2&gt;

&lt;p&gt;Traditional REST-based communication introduces tight coupling between services, making them harder to scale and maintain. Event-driven microservices solve this problem by allowing services to communicate asynchronously using events.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of Event-Driven Architecture:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Decoupling&lt;/strong&gt;: Services interact via events, reducing direct dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;: Independent services can scale as needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resilience&lt;/strong&gt;: Failures in one service do not immediately impact others.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility&lt;/strong&gt;: Easily extend systems by adding new consumers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting Up Apache Kafka
&lt;/h2&gt;

&lt;p&gt;Before implementing our example, make sure you have &lt;strong&gt;Apache Kafka&lt;/strong&gt; installed and running.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Download and Extract Kafka:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Head over to this website [Download Apache Kafka](https://kafka.apache.org/downloads)
unzip to your favorite location
for this article i created a folder called software in local c on windows and rename kafka_2.13-3.0.0 to just kafka
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start Zookeeper and Kafka Broker:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    FOR ZOOKEEPER

   * To Start Zookeeper
   * open command prompt or terminal and CD into C:\softwares\kafka\bin\windows&amp;gt;
  * Then run this,  zookeeper-server-start.bat ..\..\config\zookeeper.properties 

FOR KAFKA BROKER 
  * To start kafka broker
  * open another command prompt or terminal and CD into C:\softwares\kafka\bin\windows&amp;gt;
  * Then run this, kafka-server-start.bat ..\..\config\server.properties
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Experiment with Kafka
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;create a Topic - With another cmd instance open with same path you cd into to run both kafka broker and zookper while your kafka broker and zookeeper is still open, then run this command to create a kafka topic, &lt;code&gt;kafka-topic.bat --bootstrap-server --create --topic order-topic --partitions 3 --replication-factor 1&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt; list Topic - run this command to list a kafka topic, 
&lt;code&gt;kafka-topic.bat --bootstrap-server localhost:9092 --list&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;describe a Topic - run this command to describe a kafka topic,
&lt;code&gt;kafka-topic.bat --bootstrap-server localhost:9092 --describe --topic order-topic&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;publish message from producer to consumer - Open another instance of the command prompt and then run this to publish message from producer to consumer
&lt;code&gt;kafka-console-producer.bat --broker-list localhost:9092 --topic order-topic&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;subscribe/consume message from producer - Open another instance of the command prompt and then run this to subscribe to message from the producer
&lt;code&gt;kafka-console-consumer.bat --bootstrap-server localhost:9092 --topic order-topic --from-beginning&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementing the Practical Example
&lt;/h2&gt;

&lt;p&gt;Let's implement a scenario where an &lt;strong&gt;Order Service&lt;/strong&gt; processes orders, and an &lt;strong&gt;Inventory Service&lt;/strong&gt; listens to order events to update stock levels.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Spring Boot project
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Head over to &lt;a href="https://start.spring.io/" rel="noopener noreferrer"&gt;Spring Starter template&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Create a Spring Boot Kafka Producer (Order Service)&lt;/li&gt;
&lt;li&gt;add the dependency spring web and kafka
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-boot-starter-web&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.kafka&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-kafka&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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.amazonaws.com%2Fuploads%2Farticles%2Fozqwxrhobpp6wwpjmnwb.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.amazonaws.com%2Fuploads%2Farticles%2Fozqwxrhobpp6wwpjmnwb.PNG" alt="Image description" width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create a Kafka Producer:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

@Service
public class OrderProducer {
    private final KafkaTemplate&amp;lt;String, String&amp;gt; kafkaTemplate;

    public OrderProducer(KafkaTemplate&amp;lt;String, String&amp;gt; kafkaTemplate) {
        this.kafkaTemplate = kafkaTemplate;
    }

    public void sendOrderEvent(String message) {
        kafkaTemplate.send("order-topic", message);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expose an API to trigger order processing:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/orders")
public class OrderController {
    private final OrderProducer orderProducer;

    public OrderController(OrderProducer orderProducer) {
        this.orderProducer = orderProducer;
    }

    @PostMapping("/create")
    public String createOrder(@RequestBody String orderDetails) {
        orderProducer.sendOrderEvent(orderDetails);
        return "Order event sent!";
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Create a Spring Boot Kafka Consumer (Inventory Service)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Create a Kafka Consumer:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Service;

@Service
public class InventoryConsumer {
    @KafkaListener(topics = "order-topic", groupId = "inventory-group")
    public void listenOrderEvents(ConsumerRecord&amp;lt;String, String&amp;gt; record) {
        System.out.println("Received Order Event: " + record.value());
        updateInventory(record.value());
    }

    private void updateInventory(String orderDetails) {
        // Simulate inventory update logic
        System.out.println("Inventory updated for order: " + orderDetails);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running the Application
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start Kafka and Zookeeper&lt;/strong&gt; (if not already running).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run the Order Service&lt;/strong&gt;: Start the producer microservice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run the Inventory Service&lt;/strong&gt;: Start the consumer microservice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trigger an Order Event&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8080/orders/create &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'"{"orderId": "123", "productId": "456", "quantity": "2"}"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check the Consumer Logs&lt;/strong&gt;: You should see the order event being processed and the inventory updated.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Event-driven microservices with Apache Kafka provide a powerful way to build decoupled, scalable systems. In this guide, we demonstrated how to implement a simple &lt;strong&gt;order processing system&lt;/strong&gt; using &lt;strong&gt;Kafka producers and consumers&lt;/strong&gt; in &lt;strong&gt;Spring Boot&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;By adopting event-driven design, you can create robust, loosely coupled microservices that scale efficiently while ensuring smooth communication between components.&lt;/p&gt;

&lt;p&gt;Please if you find the article interesting and valuable drop a like and comment. See you in the next one, keep building!&lt;br&gt;
Follow me on socials &lt;a href="https://www.linkedin.com/in/williamnnamani" rel="noopener noreferrer"&gt;my Linkedin Handle&lt;/a&gt;&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>java</category>
      <category>kafka</category>
      <category>eventdriven</category>
    </item>
    <item>
      <title>How to Design a Scalable Microservices Architecture: Lessons from Real-World Systems</title>
      <dc:creator>William</dc:creator>
      <pubDate>Fri, 07 Feb 2025 12:42:36 +0000</pubDate>
      <link>https://dev.to/innovate/how-to-design-a-scalable-microservices-architecture-lessons-from-real-world-systems-25p6</link>
      <guid>https://dev.to/innovate/how-to-design-a-scalable-microservices-architecture-lessons-from-real-world-systems-25p6</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Microservices architecture has become the poster child of modern software development. It promises scalability, flexibility, and the dream of independent deployability. But let’s be real—building a microservices-based system isn’t all sunshine and rainbows. It comes with its own set of challenges, especially when working with Java and Spring Boot.&lt;/p&gt;

&lt;p&gt;In this article, we’ll dive into real-world lessons learned from implementing microservices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 1: Microservices Doesn’t Mean Micro-Problems
&lt;/h2&gt;

&lt;p&gt;Many teams start with microservices thinking, &lt;em&gt;"Let’s break this monolith into smaller, manageable pieces."&lt;/em&gt; Sounds great, right? Until you realize you now have 20+ services talking to each other like a whole community fetching from a stagnant stream of water.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution: Proper Service Boundaries
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;Domain-Driven Design (DDD)&lt;/strong&gt; to define proper service boundaries.&lt;/li&gt;
&lt;li&gt;Avoid creating microservices that are &lt;em&gt;too&lt;/em&gt; micro—sometimes a monolith is just a misunderstood hero.&lt;/li&gt;
&lt;li&gt;Ensure each service has a clear, independent responsibility.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lesson 2: Distributed Transactions Are a Nightmare
&lt;/h2&gt;

&lt;p&gt;In monolithic applications, transactions are simple—you start one, do some operations, commit or rollback. But in microservices? Welcome to the &lt;em&gt;Saga&lt;/em&gt; pattern and compensating transactions, where a failed step means you have to undo everything.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution: Handling Transactions Properly
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use the &lt;strong&gt;Saga pattern&lt;/strong&gt; for long-running transactions.&lt;/li&gt;
&lt;li&gt;Implement &lt;strong&gt;event-driven architectures&lt;/strong&gt; with tools like Kafka or RabbitMQ.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotency is your best friend&lt;/strong&gt;—ensure your services can handle duplicate requests gracefully.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lesson 3: Latency is the Silent Killer
&lt;/h2&gt;

&lt;p&gt;Breaking down a monolith into microservices means your once-simple function call is now a network request. And we all know how reliable networks are. Suddenly, your ultra-fast system is moving at the speed of a sloth on a Monday morning. &lt;/p&gt;

&lt;h3&gt;
  
  
  Solution: Performance Optimization
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;circuit breakers&lt;/strong&gt; (Resilience4j, Hystrix) to prevent cascading failures.&lt;/li&gt;
&lt;li&gt;Implement &lt;strong&gt;caching&lt;/strong&gt; (Redis, EhCache) to avoid unnecessary calls.&lt;/li&gt;
&lt;li&gt;Monitor API latencies and optimize slow endpoints.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lesson 4: Versioning is Inevitable
&lt;/h2&gt;

&lt;p&gt;Deploying updates in a microservices world can be like trying to replace a car tire while speeding down the highway. One breaking change, and everything falls apart.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution: Backward-Compatible APIs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;semantic versioning&lt;/strong&gt; (e.g., v1, v2 endpoints).&lt;/li&gt;
&lt;li&gt;Follow &lt;strong&gt;API-first design&lt;/strong&gt; with OpenAPI and Swagger.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lesson 5: Logging and Monitoring Save Lives
&lt;/h2&gt;

&lt;p&gt;Debugging a microservices system without proper logging is like trying to solve a crime without evidence. You &lt;em&gt;think&lt;/em&gt; you know what’s happening, but reality is a different story.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution: Observability Tools
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Centralize logs using &lt;strong&gt;ELK Stack (Elasticsearch, Logstash, Kibana)&lt;/strong&gt; or &lt;strong&gt;Grafana Loki&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Implement &lt;strong&gt;distributed tracing&lt;/strong&gt; with &lt;strong&gt;Jaeger&lt;/strong&gt; or &lt;strong&gt;Zipkin&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Monitor metrics with &lt;strong&gt;Prometheus&lt;/strong&gt; and &lt;strong&gt;Grafana&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lesson 6: Security is Not Optional
&lt;/h2&gt;

&lt;p&gt;Exposing multiple microservices to the world without security is like leaving your house with the doors open and a sign that says, &lt;em&gt;"Come on in, free valuables inside!"&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution: Secure Your Services
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;OAuth 2.0 and JWT&lt;/strong&gt; for authentication.&lt;/li&gt;
&lt;li&gt;Implement &lt;strong&gt;API gateways&lt;/strong&gt; (Spring Cloud Gateway) for centralized security.&lt;/li&gt;
&lt;li&gt;Keep dependencies up to date to avoid vulnerabilities.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Microservices architecture, when done right, can bring immense benefits. But it requires careful planning, solid design principles, and the right set of tools. Java and Spring Boot provide a robust ecosystem to build scalable and resilient microservices, but they also come with challenges that need to be addressed.&lt;/p&gt;

&lt;p&gt;So, before you jump headfirst into microservices, ask yourself: &lt;em&gt;"Do I really need this, or can my monolith still do the job?"&lt;/em&gt; Because sometimes, the best microservice decision is not using microservices at all!&lt;/p&gt;

&lt;p&gt;Please like this post and comment if you find it valuable and interesting, till next time keep building.&lt;br&gt;
You can connect with me on socials: &lt;a href="https://www.linkedin.com/in/williamnnamani" rel="noopener noreferrer"&gt;My Linkedin Handle&lt;/a&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>microservices</category>
      <category>database</category>
    </item>
  </channel>
</rss>
