<?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: Subhangi Dutta</title>
    <description>The latest articles on DEV Community by Subhangi Dutta (@subhangidutta23shipit).</description>
    <link>https://dev.to/subhangidutta23shipit</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%2F4078923%2Ffb372270-602c-4fe9-9aca-f267d86f94e3.jpg</url>
      <title>DEV Community: Subhangi Dutta</title>
      <link>https://dev.to/subhangidutta23shipit</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/subhangidutta23shipit"/>
    <language>en</language>
    <item>
      <title>The 6.2-Second Silence: Smashing a 'Silent Handoff' Bug in Real-Time Voice AI</title>
      <dc:creator>Subhangi Dutta</dc:creator>
      <pubDate>Sat, 22 Aug 2026 07:45:13 +0000</pubDate>
      <link>https://dev.to/subhangidutta23shipit/the-62-second-silence-smashing-a-silent-handoff-bug-in-real-time-voice-ai-1ihe</link>
      <guid>https://dev.to/subhangidutta23shipit/the-62-second-silence-smashing-a-silent-handoff-bug-in-real-time-voice-ai-1ihe</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/bugsmash"&gt;DEV's Summer Bug Smash: Smash Stories&lt;/a&gt; powered by &lt;a href="https://sentry.io/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;I almost missed this one. It didn't throw an exception. It didn't fail a health check. Every service reported &lt;code&gt;200 OK&lt;/code&gt;. The only evidence was a number my stress-test dashboard quietly rolled over one night: &lt;strong&gt;74% of simulated emergency callers hung up mid-transfer.&lt;/strong&gt; Nothing crashed. Nothing errored. The system just went silent at the exact moment someone needed it most.&lt;/p&gt;

&lt;p&gt;This is the story of how a bug that never once threw an error nearly sank my pilot rollout three days before launch — and what it taught me about the difference between a service being &lt;em&gt;up&lt;/em&gt; and a system actually being &lt;em&gt;alive&lt;/em&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Backstory: Building Sentinel
&lt;/h3&gt;

&lt;p&gt;During natural disasters like flash floods, emergency dispatch lines get overwhelmed within minutes. Traditional interactive voice systems (IVR) fail callers because navigating rigid keypads while trapped in rising water is impossible.&lt;/p&gt;

&lt;p&gt;To address this, I built &lt;strong&gt;Sentinel&lt;/strong&gt;—an autonomous, real-time emergency voice AI dispatcher, as a solo hackathon project. Sentinel handles concurrent inbound emergency calls, conducts immediate triage (extracting location, injuries, and flood depth), redacts personally identifiable information (PII), and dynamically delegates specialized routing through WebRTC pipelines.&lt;/p&gt;

&lt;p&gt;The underlying stack integrates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deepgram Nova-3&lt;/strong&gt; for ultra-low latency Speech-to-Text (STT) streaming.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini 1.5 Pro&lt;/strong&gt; as the core intelligence orchestrator.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LiveKit Agents Framework&lt;/strong&gt; for WebRTC audio transport and multi-agent coordination.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Murf Falcon 2&lt;/strong&gt; for hyper-realistic voice synthesis (TTS).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In an emergency pipeline, latency is not just a user experience metric—it is life-critical. A caller who hangs up assuming the line dropped doesn't call back immediately — in a flood, they go looking for another way out instead. That's the failure mode I was testing against.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Bug: When Silence Became the Failure Mode
&lt;/h3&gt;

&lt;p&gt;I was three days from a planned pilot rollout with a regional disaster-response team, running concurrency load tests to simulate a flash-flood surge — dozens of overlapping calls hitting Sentinel at once. That's when the abandonment number showed up: &lt;strong&gt;over 74% of simulated emergency callers hung up within the first 10 seconds of a transfer.&lt;/strong&gt;&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%2Fy0k9xrkv8krbjx8puwda.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%2Fy0k9xrkv8krbjx8puwda.png" alt="Sentinel Call History Dashboard showing failed call metrics" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Fig 1: Initial telemetry capturing 24 failed calls out of 32 during the flood-surge load test—the majority logging: "Caller hung up without responding to Sentinel's opening statement."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When an inbound caller needed specialized shelter triage, the root &lt;code&gt;TriageAgent&lt;/code&gt; recognized the intent and executed a delegation handoff to the &lt;code&gt;ShelterSpecialistAgent&lt;/code&gt;:&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="c1"&gt;# The delegation trigger inside Sentinel's core triage loop
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;detected_intent&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shelter_dispatch&lt;/span&gt;&lt;span class="sh"&gt;"&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;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handoff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;to_agent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ShelterSpecialistAgent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;transfer_context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;caller_data&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;The handoff itself succeeded in the background, but the audio channel went completely dead.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;6.2 seconds&lt;/strong&gt;, absolute silence hung on the line. The caller—assuming the call had dropped in the middle of a disaster—hung up. Only after the caller had already disconnected did Sentinel's logs register a late, unprompted audio dispatch.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Investigation &amp;amp; Root Cause
&lt;/h3&gt;

&lt;p&gt;Debugging asynchronous audio streams across WebRTC connections is notoriously tricky — nothing was throwing, so standard error monitoring was useless. I turned to Sentry's trace spans across the STT → LLM → TTS pipeline, and every service completed cleanly and fast. The delegation &lt;code&gt;handoff()&lt;/code&gt; call itself completed in milliseconds. The gap wasn't inside any span at all — it was in the six seconds of &lt;em&gt;nothing between&lt;/em&gt; them, a silence no span boundary was built to measure.&lt;/p&gt;

&lt;p&gt;That reframed the question I was even asking. I'd been debugging as if the only thing that mattered was &lt;em&gt;did the request succeed?&lt;/em&gt; But for a voice system, the question that actually matters is &lt;em&gt;did the caller hear something when they expected to?&lt;/em&gt; Every span said yes. The caller would have said no.&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%2F54pugbqd7oglkzk673qd.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%2F54pugbqd7oglkzk673qd.png" alt="Sequence diagram showing turn-taking deadlock between Caller, TriageAgent, and ShelterSpecialistAgent" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Fig 2: Sequence diagram illustrating the unprompted turn-taking standoff across agent lifecycles.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I isolated the root cause to an &lt;strong&gt;unprompted turn-taking standoff&lt;/strong&gt; in the agent delegation lifecycle:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;State Transfer Without Audio Activation:&lt;/strong&gt; The &lt;code&gt;TriageAgent&lt;/code&gt; completed its turn, transferred memory context, and relinquished session control to &lt;code&gt;ShelterSpecialistAgent&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Turn-Taking Deadlock:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;LiveKit's agent loop operates reactively: an agent processes input &lt;em&gt;only after detecting a new VAD (Voice Activity Detection) turn&lt;/em&gt; from the user.&lt;/li&gt;
&lt;li&gt;The user, having just spoken their emergency request, was waiting for the assistant to acknowledge the transfer.&lt;/li&gt;
&lt;li&gt;The newly mounted &lt;code&gt;ShelterSpecialistAgent&lt;/code&gt; was passively waiting for the user to speak first before generating an LLM response.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The 6-Second Timeout:&lt;/strong&gt; The system only recovered after a 6-second inactivity timeout triggered an automatic fallback prompt—far too late for a high-stress emergency call.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My first instinct was to shorten the 6-second inactivity timeout — but that just meant the fallback prompt fired faster into an already-lost call. The real problem wasn't timeout tuning; it was that the agent had no reason to speak until the timeout forced it to.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Fix &amp;amp; Implementation
&lt;/h3&gt;

&lt;p&gt;To break the standoff, the incoming agent needed to claim conversational initiative immediately upon entering the session lifecycle, rather than waiting for an inbound audio frame.&lt;/p&gt;

&lt;p&gt;I implemented proactive lifecycle synthesis using the &lt;code&gt;on_enter()&lt;/code&gt; hook to immediately kick off speech synthesis with context-aware prompt injection:&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;livekit.agents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;VoiceAgent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AgentContext&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ShelterSpecialistAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VoiceAgent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&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;def&lt;/span&gt; &lt;span class="nf"&gt;on_enter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AgentContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        Fires immediately upon agent delegation handoff.
        Forces proactive speech synthesis to eliminate
        the unprompted turn standoff.
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;caller_name&lt;/span&gt;&lt;span class="sh"&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;there&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sector&lt;/span&gt;&lt;span class="sh"&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;your area&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Proactively construct greeting from incoming handoff context
&lt;/span&gt;        &lt;span class="n"&gt;proactive_greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;I have your details, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;I am actively tracking available rescue &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shelters in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sector&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. How can I direct you?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Immediately push synthesis to the WebRTC audio track
&lt;/span&gt;        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;proactive_greeting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;allow_interruptions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&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;By binding the initial greeting synthesis directly to &lt;code&gt;on_enter()&lt;/code&gt;, the agent pipeline starts streaming audio packets through Murf Falcon 2 the exact millisecond the WebRTC track ownership flips.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Real-World Impact
&lt;/h3&gt;

&lt;p&gt;Deploying this fix completely resolved the dead-air deadlock and transformed system performance under production load:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before Fix (Deadlock State)&lt;/th&gt;
&lt;th&gt;After Fix (&lt;code&gt;on_enter&lt;/code&gt; Lifecycle)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agent Handoff Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~6,240 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&amp;lt; 318 ms&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Call Abandonment Rate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;74.2%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&amp;lt; 2.8%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;First-Turn TTS Synthesis&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;On Inactivity Fallback (~6s)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Instant (&amp;lt; 200ms TTFB)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Caller Triage Completion&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;25.8% Success Rate&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;94.6% Resolution Rate&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsv1l7sq82woonbmdhcbq.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%2Fsv1l7sq82woonbmdhcbq.png" alt="Sentinel Live Emergency Dispatcher Dashboard with resolved calls" width="800" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Fig 3: The same dispatcher dashboard, one deploy later — resolved calls climbing steadily across active flood zones instead of stacking up as failures.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Lessons Learned for Voice AI Systems
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Asynchronous Multi-Agent Systems Cannot Be Passive:&lt;/strong&gt; Voice-to-voice agent handoffs require explicit turn management. In Sentinel's case, &lt;code&gt;ShelterSpecialistAgent&lt;/code&gt; had no code path that fired on entry — only a path that reacted to input — so there was never a moment where it was actually going to speak first. Never assume an incoming agent should wait for user input; give it an explicit reason to act the instant it takes control.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Telemetry Must Measure Perceived Silence:&lt;/strong&gt; Standard error-rate monitoring won't catch deadlocks where every service returns &lt;code&gt;200 OK&lt;/code&gt;. What finally exposed the gap wasn't a failed span — it was the &lt;em&gt;absence&lt;/em&gt; of one. For voice systems, that means tracking things error monitoring normally ignores: time-to-first-byte on speech synthesis, time between agent handoff and first audio, total duration of dead air, and caller abandonment specifically during transitions — the signals that catch silence no individual service will ever report as a failure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Building real-time voice agents is as much about managing silence and state transitions as it is about low latency. What I'd actually built was two separate handoffs stacked on top of each other: a &lt;strong&gt;state&lt;/strong&gt; handoff, where session data and memory context move between agents, and a &lt;strong&gt;conversational&lt;/strong&gt; handoff, where ownership of the next spoken turn moves between agents. I had engineered the first one carefully. I had assumed the second one would just happen. By shifting from passive turn-taking to proactive lifecycle hooks, Sentinel achieved sub-second, reliable emergency dispatch handoffs when every second matters most. That 74% abandonment rate is now under 3%, and the handoff that used to take 6.2 seconds now takes under a third of a second.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;In mission-critical voice AI, eliminating dead air isn't just an optimization—it ensures the system responds the instant someone needs help.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
      <category>ai</category>
      <category>python</category>
    </item>
    <item>
      <title>When the Water Rises: Building Sentinel AI Disaster Response in 10 Days</title>
      <dc:creator>Subhangi Dutta</dc:creator>
      <pubDate>Sat, 15 Aug 2026 12:21:54 +0000</pubDate>
      <link>https://dev.to/subhangidutta23shipit/navigating-floods-without-data-building-sentinel-voice-agent-in-10-days-j7c</link>
      <guid>https://dev.to/subhangidutta23shipit/navigating-floods-without-data-building-sentinel-voice-agent-in-10-days-j7c</guid>
      <description>&lt;p&gt;It was during the peak monsoon season when I read a distress report from a family stranded on their rooftop. Power was flickering, rain was hammering against the walls, and mobile data was down to a crawling 2G connection. They had a phone with 14% battery, but navigating complex portals or downloading 20-page government advisory PDFs was impossible.&lt;/p&gt;

&lt;p&gt;All they could do was make a direct voice call.&lt;/p&gt;

&lt;p&gt;When water is rising inside your living room, you cannot type or navigate dropdowns. You need an immediate, natural voice that provides real-time flood advisories, locates relief shelter beds, and dispatches rescue teams without delay.&lt;/p&gt;

&lt;p&gt;That became the driving mission behind &lt;strong&gt;Sentinel&lt;/strong&gt; — an autonomous, real-time Voice AI emergency dispatcher built over 10 days during the &lt;strong&gt;#VoiceForBharat&lt;/strong&gt; challenge.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Problem I Wanted to Solve
&lt;/h2&gt;

&lt;p&gt;Emergency management during Indian monsoons faces three critical bottlenecks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Information Fragmentation:&lt;/strong&gt; Emergency guidelines, live rainfall alerts, and shelter capacities exist across disconnected departments. Callers need instant answers (e.g., &lt;em&gt;"Where is the nearest shelter in Patna with medical support?"&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Friction of Touch Interfaces:&lt;/strong&gt; Wet screens, low digital literacy, and high adrenaline make text UIs fail. Voice is the most accessible lifeline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context Drop During Escalation:&lt;/strong&gt; Traditional hotlines often disconnect or force victims to repeat their distress details to multiple operators.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  2. System Architecture &amp;amp; Data Flow
&lt;/h2&gt;

&lt;p&gt;To ensure sub-second response times, Sentinel connects audio streaming, AI reasoning, and low-latency speech synthesis into a unified WebRTC pipeline powered by &lt;a href="https://livekit.io" rel="noopener noreferrer"&gt;LiveKit&lt;/a&gt;, &lt;a href="https://deepgram.com" rel="noopener noreferrer"&gt;Deepgram Nova-3&lt;/a&gt;, &lt;a href="https://ai.google.dev" rel="noopener noreferrer"&gt;Google Gemini&lt;/a&gt;, &lt;a href="https://murf.ai" rel="noopener noreferrer"&gt;Murf AI&lt;/a&gt;, and &lt;a href="https://open-meteo.com" rel="noopener noreferrer"&gt;Open-Meteo&lt;/a&gt;:&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%2F7g6nv0pktem14aep6ccn.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%2F7g6nv0pktem14aep6ccn.png" alt="Disaster Response Sentinel System Architecture" width="800" height="411"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; Caller Voice (Browser WebRTC / Inbound SIP)
            │
            ▼
 ┌─────────────────────────────────────────────────────────┐
 │               LiveKit WebRTC Transport                  │
 │      (Audio Ingestion &amp;amp; Silero VAD Turn Detection)       │
 └──────────────────────────┬──────────────────────────────┘
                            │
                            ▼
 ┌─────────────────────────────────────────────────────────┐
 │               Deepgram Nova-3 (STT)                     │
 │          (Continuous Multilingual Streaming)           │
 └──────────────────────────┬──────────────────────────────┘
                            │ Real-time Transcript
                            ▼
 ┌─────────────────────────────────────────────────────────┐
 │          Google Gemini 3.5 Flash-Lite (LLM)             │
 │                                                         │
 │  • Emergency Triage &amp;amp; Safety Guardrails                 │
 │  • SQLite Caller Memory &amp;amp; State Tracking                │
 │  • Open-Meteo Weather Telemetry Tools                   │
 │  • Dynamic Specialist Agent Handoff                     │
 └──────────────────────────┬──────────────────────────────┘
                            │ Streaming Text Tokens
                            ▼
 ┌─────────────────────────────────────────────────────────┐
 │               Murf Falcon 2 (TTS)                       │
 │  (Ultra-Fast Indian Voice Stream: Anisha/hi-IN)         │
 └──────────────────────────┬──────────────────────────────┘
                            │
                            ▼
 Direct Audio Stream back to Caller (&amp;lt;500ms Time-to-First-Byte)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. The Sentinel Voice Interface
&lt;/h2&gt;

&lt;p&gt;Sentinel is designed with a lightweight, accessible interface featuring real-time Indian helpline integrations (112, NDRF 1078, Medical 108) and live ticker alerts for flood and drought monitoring across India.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sentinel in ready state with live disaster telemetry feeds:&lt;/em&gt;&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%2Fs1xskgs2qmw80lk9elbb.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%2Fs1xskgs2qmw80lk9elbb.png" alt=" " width="800" height="413"&gt;&lt;/a&gt;&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%2Frvld958sy1iissw3je6o.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%2Frvld958sy1iissw3je6o.png" alt=" " width="800" height="413"&gt;&lt;/a&gt;&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="c1"&gt;# Low-Latency Voice Pipeline Configuration
&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AgentSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;stt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;deepgram&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;STT&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nova-3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;multi&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;google&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LLM&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemini-3.5-flash-lite&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;tts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;murf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TTS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;voice&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Anisha&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;locale&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hi-IN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Conversation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tokenize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;basic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SentenceTokenizer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min_sentence_len&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;text_pacing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;turn_detection&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;MultilingualModel&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;vad&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;userdata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vad&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;preemptive_generation&lt;/span&gt;&lt;span class="o"&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;h2&gt;
  
  
  4. Real-Time Telemetry &amp;amp; Dispatch Dashboards
&lt;/h2&gt;

&lt;p&gt;To make Sentinel production-ready for disaster management cells, I built two specialized real-time monitoring consoles:&lt;/p&gt;

&lt;h3&gt;
  
  
  A. Call Analytics &amp;amp; Success Engine
&lt;/h3&gt;

&lt;p&gt;The Call Metrics Dashboard tracks total inbound WebRTC and SIP telephony calls, analyzing whether each interaction successfully delivered verified telemetry or required human intervention.&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%2Fugre3ycfldi3nvvuf0jp.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%2Fugre3ycfldi3nvvuf0jp.png" alt=" " width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Outcome Verification:&lt;/strong&gt; Automatically parses call logs and session duration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Success Criteria Standard:&lt;/strong&gt; Flags calls as successful only when verified relief shelter coordinates are provided or an escalation ticket is generated.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  B. Live Emergency Dispatcher &amp;amp; PII Protection
&lt;/h3&gt;

&lt;p&gt;When callers report being trapped or injured, Sentinel flags the situation as &lt;code&gt;EMERGENCY&lt;/code&gt; urgency and generates a structured ticket.&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%2Fnu5zq1svyftfed16hshu.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%2Fnu5zq1svyftfed16hshu.png" alt=" " width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Automated PII Redaction:&lt;/strong&gt; Phone numbers, sensitive identification, and private contact info are masked automatically (&lt;code&gt;[REDACTED_PHONE]&lt;/code&gt;) to protect caller privacy while giving rescue units necessary triage notes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Status Lifecycle:&lt;/strong&gt; Live sync tracks requests across &lt;code&gt;Open&lt;/code&gt;, &lt;code&gt;In Progress&lt;/code&gt;, and &lt;code&gt;Resolved&lt;/code&gt; states with callback dispatch verification.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. Specialist Handoffs: Shelter Information Agent
&lt;/h2&gt;

&lt;p&gt;When a caller specifically requests shelter availability or medical resources, Sentinel delegates the session to a Shelter Information Specialist using dynamic agent handoffs:&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;class&lt;/span&gt; &lt;span class="nc"&gt;ShelterInformationSpecialist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;instructions&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="n"&gt;call_context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;location&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="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;on_enter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;shelter_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;disaster_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compute_nearest_shelters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, I am the Shelter Information Specialist. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;I have live capacity for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;shelter_data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Would you like details on pet policies, medical care, or check-in rules?&lt;/span&gt;&lt;span class="sh"&gt;"&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;self&lt;/span&gt;&lt;span class="p"&gt;.&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;say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;greeting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;allow_interruptions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. Challenges &amp;amp; Solutions: The "Silent Handoff"
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Problem:&lt;/strong&gt; When delegating from Sentinel Command to the Shelter Specialist, the previous turn concluded and the audio stream went silent, leaving the user waiting without feedback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Solution:&lt;/strong&gt; Implemented the &lt;code&gt;on_enter()&lt;/code&gt; lifecycle method in &lt;a href="https://docs.livekit.io/agents" rel="noopener noreferrer"&gt;LiveKit Agents&lt;/a&gt; to trigger an immediate audio synthesis turn via &lt;code&gt;session.say()&lt;/code&gt; using Murf Falcon the instant the handoff occurs, eliminating dead air.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  7. How to Run Sentinel
&lt;/h2&gt;

&lt;p&gt;You need &lt;code&gt;Python 3.10&lt;/code&gt; through &lt;code&gt;3.13&lt;/code&gt;, &lt;code&gt;Node.js 18+&lt;/code&gt;, and &lt;code&gt;pnpm 9&lt;/code&gt;. Clone the &lt;a href="https://www.google.com/url?sa=E&amp;amp;source=gmail&amp;amp;q=https://github.com/subhangidutta23-ship-it/murf-livekit-starter.git" rel="noopener noreferrer"&gt;public repository&lt;/a&gt; then create local environment files:&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="nb"&gt;cp &lt;/span&gt;backend/.env.example backend/.env.local
&lt;span class="nb"&gt;cp &lt;/span&gt;frontend/.env.example frontend/.env.local

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add these required backend values to &lt;code&gt;backend/.env.local&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LIVEKIT_URL=your_livekit_url
LIVEKIT_API_KEY=your_livekit_api_key
LIVEKIT_API_SECRET=your_livekit_api_secret
MURF_API_KEY=your_murf_api_key
GOOGLE_API_KEY=your_gemini_api_key
DEEPGRAM_API_KEY=your_deepgram_api_key

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Start the Services
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Clone the repository&lt;/span&gt;
git clone https://github.com/subhangidutta23-ship-it/sentinel-voice-ai.git
&lt;span class="nb"&gt;cd &lt;/span&gt;sentinel-voice-ai

&lt;span class="c"&gt;# 2. Start the Backend Worker&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;backend
.&lt;span class="se"&gt;\.&lt;/span&gt;venv&lt;span class="se"&gt;\S&lt;/span&gt;cripts&lt;span class="se"&gt;\p&lt;/span&gt;ython.exe &lt;span class="nt"&gt;-m&lt;/span&gt; src.agent dev

&lt;span class="c"&gt;# 3. Start the Frontend Console&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ../frontend
pnpm &lt;span class="nb"&gt;install
&lt;/span&gt;pnpm dev

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:3000&lt;/code&gt; in your browser to begin an emergency call session.&lt;/p&gt;




&lt;p&gt;Thank you to &lt;a href="https://murf.ai" rel="noopener noreferrer"&gt;Murf AI&lt;/a&gt; and the VoiceForBharat organizers for this 10-day challenge!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>voiceai</category>
      <category>python</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
