<?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: Aryan patil</title>
    <description>The latest articles on DEV Community by Aryan patil (@aryan_patil).</description>
    <link>https://dev.to/aryan_patil</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%2F4079092%2F083bb8ac-d43f-4672-aa4f-a761de79da9f.jpg</url>
      <title>DEV Community: Aryan patil</title>
      <link>https://dev.to/aryan_patil</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aryan_patil"/>
    <language>en</language>
    <item>
      <title>From Rakshika to Doctor Gurleen: Building a Voice AI That Hands You Off, Not On Hold</title>
      <dc:creator>Aryan patil</dc:creator>
      <pubDate>Sat, 15 Aug 2026 15:36:44 +0000</pubDate>
      <link>https://dev.to/aryan_patil/from-rakshika-to-doctor-gurleen-building-a-voice-ai-that-hands-you-off-not-on-hold-1f8p</link>
      <guid>https://dev.to/aryan_patil/from-rakshika-to-doctor-gurleen-building-a-voice-ai-that-hands-you-off-not-on-hold-1f8p</guid>
      <description>&lt;h1&gt;
  
  
  Rakshika: An AI Voice Agent That Doesn't Put You on Hold During a Disaster
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Built for 10 Days of Voice Agents — VoiceForBharat Edition&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;The Problem, and Who It's For&lt;/li&gt;
&lt;li&gt;What Rakshika Actually Does&lt;/li&gt;
&lt;li&gt;How the System Works&lt;/li&gt;
&lt;li&gt;Indian Voice, Hindi/Hinglish, and Safety Guardrails&lt;/li&gt;
&lt;li&gt;Bringing In a Specialist: Doctor Gurleen&lt;/li&gt;
&lt;li&gt;Memory, Escalation, Outbound Calls, and the Dashboard&lt;/li&gt;
&lt;li&gt;The 10-Day Build Journey&lt;/li&gt;
&lt;li&gt;Troubleshooting Notes for Fellow Builders&lt;/li&gt;
&lt;li&gt;Build It Yourself&lt;/li&gt;
&lt;li&gt;What's Next&lt;/li&gt;
&lt;li&gt;Links&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. The Problem, and Who It's For
&lt;/h2&gt;

&lt;p&gt;During a disaster — a flood, a cyclone, an earthquake — the phone lines that are supposed to help people are exactly the ones that get overwhelmed first. Everyone in the affected area is trying to reach the same emergency number at the same time, in a language and tone that doesn't always match a rushed, overworked human operator on the other end.&lt;/p&gt;

&lt;p&gt;Two things tend to go wrong at once: &lt;strong&gt;capacity&lt;/strong&gt; (too many callers, not enough responders) and &lt;strong&gt;language&lt;/strong&gt; (panicked callers naturally code-mix Hindi and English mid-sentence — they don't switch into formal, textbook language just because they're on a phone call).&lt;/p&gt;

&lt;p&gt;Rakshika is built for exactly this moment. It's for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Callers who are more comfortable speaking Hindi or Hinglish than typing into an app&lt;/li&gt;
&lt;li&gt;People in semi-urban and rural areas where code-mixed speech is simply how people talk, not a fallback&lt;/li&gt;
&lt;li&gt;Anyone calling under stress, who cannot navigate a menu tree ("press 1 for shelter, press 2 for medical...") while something urgent is happening&lt;/li&gt;
&lt;li&gt;Human rescue coordinators, who need callers pre-triaged so they can focus their limited time on cases that genuinely need a human&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rakshika isn't meant to replace a real emergency helpline or a real doctor. It's meant to be the layer in front of them — always available, speaks the way the caller speaks, and hands over a clean, already-understood conversation the moment a human or a specialist needs to take over.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.1 Why Voice, Specifically
&lt;/h3&gt;

&lt;p&gt;A chat app would have been simpler to build and debug. But during an actual emergency, people's hands are full, their attention is scattered, and typing — in any language, under stress — is a much higher-friction ask than speaking. Voice is the interface that demands the least from the person using it, which is exactly why it fits a crisis tool.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. What Rakshika Actually Does
&lt;/h2&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%2Frmkso1yx6d5j9ntomz6n.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%2Frmkso1yx6d5j9ntomz6n.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Rakshika's landing screen — deliberately calm and uncluttered. It's upfront about scope: "For life-threatening emergencies, contact local emergency services directly. Rakshika provides general guidance only."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;On a live call, Rakshika can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hold a natural, code-mixed Hindi/English conversation, in Devanagari script, using short and calm sentences&lt;/li&gt;
&lt;li&gt;Look up &lt;strong&gt;live weather, active disaster alerts, and recent earthquake data&lt;/strong&gt; for the caller's district through the &lt;code&gt;get_emergency_status&lt;/code&gt; tool, so her guidance is grounded in real data rather than a guess&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remember returning callers&lt;/strong&gt; — name, location, household size, mobility needs — through a SQLite-backed memory system, so a caller who reconnects doesn't have to explain everything again&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Escalate to a human rescue team&lt;/strong&gt;, but only after explicitly asking the caller's permission first&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Place outbound calls&lt;/strong&gt; through a SIP/Linphone integration, so the system can proactively reach people instead of only waiting for them to call in&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hand off medical questions to Doctor Gurleen&lt;/strong&gt;, a dedicated specialist agent, mid-call — without the caller repeating a single word of what they already told Rakshika&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a condensed sense of what an actual call sounds like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caller:&lt;/strong&gt; "Rakshika, mere area mein bahot tez baarish ho rahi hai, kya koi alert hai?"&lt;br&gt;
&lt;strong&gt;Rakshika:&lt;/strong&gt; "मैं अभी आपके इलाके की जानकारी देख रही हूँ. कृपया अपना शहर या ज़िला बताएं."&lt;br&gt;
&lt;strong&gt;Caller:&lt;/strong&gt; "Nashik"&lt;br&gt;
&lt;strong&gt;Rakshika:&lt;/strong&gt; "नासिक में अभी कोई गंभीर चेतावनी सक्रिय नहीं है, लेकिन बारिश तेज़ है. कृपया निचले इलाकों से दूर रहें और मुझे बताएं अगर पानी बढ़ने लगे."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The design principle behind all of it: a caller in an emergency should never have to wait, never have to navigate a menu, and never have to explain their situation twice.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. How the System Works
&lt;/h2&gt;

&lt;p&gt;Rakshika runs on the &lt;strong&gt;LiveKit Agents SDK&lt;/strong&gt;, which handles the real-time audio transport so the agent code itself can focus on reasoning, tool calls, and conversation logic rather than the plumbing of getting audio in and out reliably.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Audio flows left to right through the STT → LLM → TTS pipeline. The LLM decides both tool calls and specialist handoffs; a parallel data channel keeps the dashboard and voice orb in sync in real time.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 Voice Activity Detection — Silero VAD
&lt;/h3&gt;

&lt;p&gt;Turn-taking is one of the most underrated problems in voice AI. If the agent doesn't know precisely when the caller has stopped speaking, it either interrupts them mid-sentence or leaves an awkward pause — both especially jarring when the caller is already stressed. Silero VAD flags speech-start and speech-end boundaries with low latency, which is what makes the pipeline feel like a phone call rather than a walkie-talkie exchange.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 Speech-to-Text — Deepgram Nova-3
&lt;/h3&gt;

&lt;p&gt;Set to Hindi, and critical for handling code-mixed speech correctly — a sentence like &lt;em&gt;"mere ghar mein paani bhar gaya hai, please help jaldi"&lt;/em&gt; gets transcribed as spoken, without forcing it into a single language.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.3 The LLM Layer — Google Gemini 3.5 Flash-Lite
&lt;/h3&gt;

&lt;p&gt;Gemini sits at the center of the pipeline doing three jobs: understanding intent, deciding when to call a tool (weather/alerts lookup, escalation, specialist handoff) via function calling so the model never has to hallucinate live data, and generating the spoken response in natural, code-mixed Hindi following the persona and safety guardrails baked into the system prompt. Flash-Lite was chosen specifically for low latency — in a voice pipeline, every extra hundred milliseconds of "thinking time" is audible as dead air.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.4 Text-to-Speech — Murf Falcon
&lt;/h3&gt;

&lt;p&gt;Murf Falcon's response times are low enough that Rakshika's replies start playing almost immediately after Gemini finishes generating them, instead of a noticeable "processing" gap. She speaks in the &lt;em&gt;Namrita&lt;/em&gt; voice.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.5 The Frontend — Next.js 15
&lt;/h3&gt;

&lt;p&gt;A custom animated SVG voice orb reflects the live call state — idle, listening, or speaking — via CSS-variable-driven colors, with a smooth transition instead of a hard cut. The same LiveKit room carries a data channel alongside the audio, which the backend uses to push live events (escalations, analytics, which agent is currently active) straight to the frontend with no polling.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.6 Storage — SQLite
&lt;/h3&gt;

&lt;p&gt;A local database handles two jobs: &lt;strong&gt;caller memory&lt;/strong&gt; (name, location, and prior-call context, so returning callers are recognised instantly) and &lt;strong&gt;escalation/outcome tracking&lt;/strong&gt; (every escalation and every call's success/failure flag, which is what powers the dashboard in Section 6).&lt;/p&gt;

&lt;h3&gt;
  
  
  3.7 A Simplified Session Setup
&lt;/h3&gt;

&lt;p&gt;A trimmed-down look at how the agent session ties the STT/LLM/TTS/VAD stack together:&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;AgentSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;livekit.plugins&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;deepgram&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;google&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;murf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;silero&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;vad&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;silero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;VAD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&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;hi&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;Namrita&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="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;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Assistant&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;SYSTEM_PROMPT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_identity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user_identity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&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;room&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;room&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;Everything downstream — tool calls, escalation creation, the specialist handoff — is layered on top of this session as &lt;code&gt;@function_tool&lt;/code&gt;-decorated methods on the agent class.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Indian Voice, Hindi/Hinglish, and Safety Guardrails
&lt;/h2&gt;

&lt;p&gt;Getting the language right wasn't a small detail bolted onto an English-first design — it was a first-class requirement from Day 1.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1 The Voice
&lt;/h3&gt;

&lt;p&gt;Rakshika speaks in Murf Falcon's &lt;em&gt;Namrita&lt;/em&gt; voice, chosen specifically because Falcon's latency is low enough that a reply doesn't feel like it's being "processed" before it plays. In a distress call, that pause is the fastest way to make the line feel untrustworthy.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 Code-Mixing, Not Translation
&lt;/h3&gt;

&lt;p&gt;Callers say things like &lt;em&gt;"mere ghar mein paani bhar gaya hai, please help jaldi"&lt;/em&gt; — mixing Hindi and English naturally. Rakshika is instructed to understand and respond to this exactly as spoken, replying primarily in Hindi (Devanagari script) while letting common English words (hospital, ambulance, locality names) stay in English, the way people actually say them.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.3 Why Script Matters, Not Just Language
&lt;/h3&gt;

&lt;p&gt;The system prompt requires Rakshika to output responses in native Devanagari, not romanized Hindi ("Ghabraiye mat, main aapki madad ke liye yahaan hoon"). Romanization forces the TTS engine to guess at pronunciation instead of reading native-script text with correct phonetics — native Devanagari produces noticeably better pronunciation and intonation.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.4 Prompt Engineering for Natural Speech
&lt;/h3&gt;

&lt;p&gt;A few rules that made a real difference in how natural Rakshika sounds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;An explicit punctuation rule.&lt;/strong&gt; The prompt requires English periods, commas, and question marks instead of the Hindi poorna viram (।) at the end of sentences — required for the streaming TTS to pace her speech correctly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Short sentences.&lt;/strong&gt; Long, clause-heavy sentences are harder to parse by ear, especially under stress.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confirm before acting.&lt;/strong&gt; Before escalating or handing off, Rakshika states what she's about to do first, so the caller isn't caught off guard by a sudden change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guardrails on scope.&lt;/strong&gt; Rakshika always confirms the caller's safety status before secondary requests, and always asks explicit permission before escalating. Doctor Gurleen's guardrails are stricter still — she never names a specific diagnosis or recommends a specific medicine, and defaults immediately to "get in-person help" for anything serious or ambiguous.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. Bringing In a Specialist: Doctor Gurleen
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 Why One Agent Wasn't Enough
&lt;/h3&gt;

&lt;p&gt;By Day 8, Rakshika's single system prompt was carrying a lot — general safety triage, live disaster data, memory management, escalation. Asking it to also carry deep, careful medical guidance on top of all that risked making every one of those responsibilities shallower. Medical guidance specifically needed much stricter, narrower safety rules than general disaster triage — rules that didn't sit naturally in the same prompt as "check the weather" and "log a caller's address."&lt;/p&gt;

&lt;p&gt;So Day 9 split the system into two agents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rakshika&lt;/strong&gt; — the primary agent. General safety, live alerts, memory, escalation. Red UI theme.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Doctor Gurleen&lt;/strong&gt; — a focused specialist, brought in only for injury/medical-symptom questions, with stricter safety rules than Rakshika carries. Blue UI theme.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5.2 How the Handoff Actually Works
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@function_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;description&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;Hand off the call to the Doctor Specialist. Use this ONLY when the &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;caller describes an injury, medical symptom, health emergency, or &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;explicitly asks for medical/first-aid advice. Do NOT use this for &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shelter questions, weather/disaster status, or general escalation.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;transfer_to_doctor_specialist&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;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RunContext&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;context&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;मैं आपको हमारी मेडिकल स्पेशलिस्ट डॉक्टर से जोड़ती हूँ, जो आपकी मदद करेंगी.&lt;/span&gt;&lt;span class="sh"&gt;"&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;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;DoctorSpecialist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_identity&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;user_identity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&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;ctx&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Transferring caller to doctor specialist&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things happen the instant a handoff triggers: the caller hears the transfer line spoken naturally, and the full conversation context carries over automatically to Doctor Gurleen — she never asks the caller to repeat themselves. If she later determines the question is non-medical, &lt;code&gt;handoff_back_to_rakshika&lt;/code&gt; sends the caller back the same way, with a short role-context note so Rakshika also picks the thread back up correctly.&lt;/p&gt;

&lt;p&gt;Here's what the handoff actually sounds like, mid-call:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caller:&lt;/strong&gt; "Mujhe chot lag gayi hai, khoon beh raha hai haath se"&lt;br&gt;
&lt;strong&gt;Rakshika:&lt;/strong&gt; "मैं आपको हमारी मेडिकल स्पेशलिस्ट डॉक्टर से जोड़ती हूँ, जो आपकी मदद करेंगी. कृपया लाइन पर बनी रहें."&lt;br&gt;
&lt;em&gt;(orb: red → connecting → blue)&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Doctor Gurleen:&lt;/strong&gt; "नमस्ते, मैं डॉक्टर गुरलीन हूँ और मैंने आपका हाल सुन लिया है, आप मुझे यह बताएं कि अभी खून बहना बंद हुआ या नहीं?"&lt;/p&gt;
&lt;/blockquote&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%2Fy909jrwsyeshgngaqohb.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%2Fy909jrwsyeshgngaqohb.png" alt=" " width="800" height="450"&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%2Fk1o6lbbb2svf3dxcb5v3.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%2Fk1o6lbbb2svf3dxcb5v3.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Left: Rakshika mid-call, orb glowing red. Right: seconds later, the handoff complete — orb now blue, label switched to "DOCTOR GURLEEN," state pill now reading "LISTENING TO YOU."&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5.3 Making the Handoff Feel Instant, Not Jarring
&lt;/h3&gt;

&lt;p&gt;The handoff logic itself — a &lt;code&gt;@function_tool&lt;/code&gt; returning a new specialist &lt;code&gt;Agent&lt;/code&gt; instance — is a documented LiveKit pattern and wasn't the hard part. The hard part was &lt;strong&gt;timing&lt;/strong&gt;. An early version fired the orb-color change the moment the handoff tool was called, which meant the orb turned blue &lt;em&gt;while Rakshika was still mid-sentence&lt;/em&gt; saying "let me connect you." It looked like a glitch, not a clean transfer.&lt;/p&gt;

&lt;p&gt;The fix was to sequence the events deliberately: the frontend shows a &lt;strong&gt;"Connecting..."&lt;/strong&gt; state while Rakshika finishes speaking her transfer line, and the orb only commits to blue once Doctor Gurleen's own introduction actually begins — timed with a short delay against when the transfer speech finishes, rather than switching the instant the backend function returns.&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;broadcast_active_agent&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;agent_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="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;agent_id: &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rakshika&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; | &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;doctor&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; — pushes the orb state over the data channel.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&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;agent_switch&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;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;agent_id&lt;/span&gt;&lt;span class="p"&gt;}}).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&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="n"&gt;room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;local_participant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reliable&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;h3&gt;
  
  
  5.4 What I'd Do Differently
&lt;/h3&gt;

&lt;p&gt;A fixed short delay works, but it's a blunt instrument. A cleaner long-term fix would be listening for a "TTS playback complete" event from the pipeline itself and triggering the orb swap off that, instead of a hand-tuned timer.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Memory, Escalation, Outbound Calls, and the Dashboard
&lt;/h2&gt;

&lt;h3&gt;
  
  
  6.1 Memory
&lt;/h3&gt;

&lt;p&gt;A SQLite database (&lt;code&gt;db.py&lt;/code&gt;) stores caller details — name, location, household size, mobility needs — but only after Rakshika explicitly asks permission to save them. On a caller's next connection, the agent looks up their identity and injects what's known into Rakshika's system prompt before she even speaks, so returning callers aren't starting from zero mid-disaster.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.2 Human Escalation and the Dashboard
&lt;/h3&gt;

&lt;p&gt;When a case is serious, Rakshika asks permission, then calls &lt;code&gt;create_escalation&lt;/code&gt;, which logs the case. A live Next.js admin dashboard reads this and updates instantly, giving a human coordinator a structured case file — who needs help, what happened, and what Rakshika already checked and confirmed — instead of a raw transcript to sift through.&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%2Fkw2yi9jszbabxc0s38wy.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%2Fkw2yi9jszbabxc0s38wy.png" alt=" " width="800" height="554"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The live escalation feed. Each card shows a reference ID and urgency level, who needs help, what happened, and a note on what Rakshika already verified before escalating — e.g. "Verified caller location as College Road, Nashik and noted a major building fire due to short circuit with people trapped." A human responder acknowledges the request directly from here.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The caller-facing side has its own short status flow, so the person on the call knows help is actually being requested — not just told "someone will call you back":&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%2Fxlzz81i94nw8qpv4accy.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%2Fxlzz81i94nw8qpv4accy.png" alt=" " width="673" height="664"&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%2Fcj8i7iz9rl40t0r5ibcu.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%2Fcj8i7iz9rl40t0r5ibcu.png" alt=" " width="726" height="772"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Left: the moment an escalation triggers, the caller sees a live "Calling Rescue Team" card with the case reference and urgency. Right: once the team is alerted, it flips to "Request Dispatched" — a small piece of UI, but it closes the loop for a caller who has no way of knowing otherwise whether their emergency was actually acted on.&lt;/em&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="nd"&gt;@function_tool&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;create_escalation&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;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RunContext&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="n"&gt;situation&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;urgency&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;ref_id&lt;/span&gt; &lt;span class="o"&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;REQ-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9999&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_escalation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ref_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ref_id&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="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;situation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;situation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urgency&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;urgency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&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;escalation&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;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ref_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ref_id&lt;/span&gt;&lt;span class="p"&gt;}}).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&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;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;local_participant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reliable&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="k"&gt;return&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;Escalation &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ref_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; created and sent to the response team.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6.3 Outbound Calling
&lt;/h3&gt;

&lt;p&gt;Using &lt;code&gt;linphone_outbound.py&lt;/code&gt;, the backend can dial a real phone number over a SIP trunk and bridge that audio into a LiveKit room — the same agent logic that handles inbound calls handles outbound ones. When an outbound call connects, Rakshika doesn't wait for the person to speak first; she immediately opens with a distress-appropriate greeting.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.4 Call Analytics
&lt;/h3&gt;

&lt;p&gt;Every call gets a success/failure flag based on whether the caller dropped early or got real help, calculated the instant a &lt;code&gt;participant_disconnected&lt;/code&gt; event fires — zero delay, no batch job.&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%2Fc2829mqzbcb9onc69iry.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%2Fc2829mqzbcb9onc69iry.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The analytics panel at the top of the dashboard: total calls, successful outcomes, failed/dropped calls, and an overall success-rate figure, calculated live as calls come in.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  7. The 10-Day Build Journey
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Days 1–3 — Getting a voice pipeline working at all.&lt;/strong&gt; &lt;code&gt;agent.py&lt;/code&gt; set up around the LiveKit voice pipeline, Deepgram for Hindi STT, Murf Falcon (Namrita) for TTS, and the first version of Rakshika's persona — calm, short sentences, Hindi/Hinglish.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day 4 — Memory.&lt;/strong&gt; &lt;code&gt;db.py&lt;/code&gt; and SQLite came in, along with &lt;code&gt;save_caller_info&lt;/code&gt; and &lt;code&gt;delete_caller_info&lt;/code&gt;, so Rakshika could ask permission to remember a caller and recall them automatically on reconnection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day 5 — Human escalation and the admin dashboard.&lt;/strong&gt; &lt;code&gt;create_escalation&lt;/code&gt; was added, with an explicit rule that Rakshika must ask permission first. The dashboard started reading live escalation data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day 6 — Outbound calling.&lt;/strong&gt; Linphone/SIP integration let the backend dial real numbers and bridge audio into the room, plus logic to detect outbound rooms and open with a mandatory greeting instead of waiting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day 7 — Dashboard polish.&lt;/strong&gt; UI refinements to the escalation cards and the live caller-facing "Calling Rescue Team → Request Dispatched" status flow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day 8 — Call analytics.&lt;/strong&gt; Success/failure tracking, tied to the &lt;code&gt;participant_disconnected&lt;/code&gt; event for instant sync, plus the analytics panel on the dashboard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day 9 — The multi-agent handoff.&lt;/strong&gt; The hardest day of the build — splitting Rakshika into two agents and getting the Rakshika ↔ Doctor Gurleen handoff to feel seamless (see Section 5.3).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day 10 — This post.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Troubleshooting Notes for Fellow Builders
&lt;/h2&gt;

&lt;p&gt;A few lessons, condensed for anyone building something similar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;If your multi-agent handoff cuts off audio mid-sentence&lt;/strong&gt;, check whether your agent swap is happening before the TTS has actually finished speaking. Sequencing a "connecting" state and delaying the swap until the handoff line finishes is often the fix.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If your STT is mangling code-mixed speech&lt;/strong&gt;, check whether the model you're using actually supports multilingual/code-mixed transcription — many STT APIs default to a single-language mode that silently degrades on mixed speech instead of erroring out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If your TTS pronunciation sounds "off" for Hindi&lt;/strong&gt;, check whether you're feeding it romanized text. Native Devanagari script almost always produces noticeably better pronunciation than a transliterated string.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you're using an AI coding agent for part of the build&lt;/strong&gt;, always have it verify against the &lt;em&gt;current&lt;/em&gt; state of your files before applying a change — a suggested patch based on a stale assumption of what a file contains will confidently point you at code that no longer exists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test with real, messy speech early.&lt;/strong&gt; Clean, scripted test sentences make almost any voice pipeline look good. The bugs that matter only show up with genuine code-mixing, interruptions, and background noise.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  9. Build It Yourself
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.10+&lt;/li&gt;
&lt;li&gt;Node.js (for the frontend)&lt;/li&gt;
&lt;li&gt;API keys for LiveKit, Murf AI, Deepgram, and Google Gemini&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 1 — Clone the repo
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="o"&gt;[&lt;/span&gt;ADD YOUR PUBLIC GITHUB REPO LINK HERE]
&lt;span class="nb"&gt;cd &lt;/span&gt;rakshika
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2 — Set environment variables
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;.env.local&lt;/code&gt; file in both &lt;code&gt;backend/&lt;/code&gt; and &lt;code&gt;frontend/&lt;/code&gt;. &lt;strong&gt;Never commit this file.&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;# backend/.env.local
LIVEKIT_URL=wss://&amp;lt;your-project&amp;gt;.livekit.cloud
LIVEKIT_API_KEY=&amp;lt;your_livekit_key&amp;gt;
LIVEKIT_API_SECRET=&amp;lt;your_livekit_secret&amp;gt;
MURF_API_KEY=&amp;lt;your_murf_api_key&amp;gt;
DEEPGRAM_API_KEY=&amp;lt;your_deepgram_key&amp;gt;
GOOGLE_API_KEY=&amp;lt;your_google_gemini_key&amp;gt;
SIP_USERNAME=&amp;lt;your_sip_username&amp;gt;
SIP_PASSWORD=&amp;lt;your_sip_password&amp;gt;
SIP_DOMAIN=&amp;lt;your_sip_domain&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# frontend/.env.local
LIVEKIT_URL=wss://&amp;lt;your-project&amp;gt;.livekit.cloud
LIVEKIT_API_KEY=&amp;lt;your_livekit_key&amp;gt;
LIVEKIT_API_SECRET=&amp;lt;your_livekit_secret&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3 — Run the backend
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;backend
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
python src/agent.py dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4 — Run the frontend
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;frontend
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5 — Talk to Rakshika
&lt;/h3&gt;

&lt;p&gt;Open &lt;code&gt;http://localhost:3000&lt;/code&gt;, connect your microphone, and start talking — in Hindi, English, or a natural mix of both.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Quick Sanity-Check Flow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Say a simple greeting and confirm you get a spoken Hindi reply.&lt;/li&gt;
&lt;li&gt;Ask about the weather/situation in a city to confirm &lt;code&gt;get_emergency_status&lt;/code&gt; fires.&lt;/li&gt;
&lt;li&gt;Describe an injury to confirm the handoff to Doctor Gurleen triggers, and the orb turns blue.&lt;/li&gt;
&lt;li&gt;Say something indicating a serious emergency to confirm the escalation flow generates a reference ID and shows up on the dashboard.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If all four steps work, your local setup is wired up correctly.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;More specialist agents beyond Doctor Gurleen — a shelter-logistics specialist would let Rakshika stay even more focused&lt;/li&gt;
&lt;li&gt;Expand beyond Hindi to other Indian languages&lt;/li&gt;
&lt;li&gt;Move the handoff timing from a fixed delay to an event-driven "TTS playback complete" signal, for a more robust sync than a hand-tuned timer&lt;/li&gt;
&lt;li&gt;Load-test the outbound calling flow for genuinely high call volume during an active disaster&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  11. Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub repository:&lt;/strong&gt; &lt;code&gt;https://github.com/aryxett/MurfAI&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Built with &lt;strong&gt;Murf Falcon&lt;/strong&gt; — the fastest TTS API I used in this build — for &lt;strong&gt;10 Days of Voice Agents — VoiceForBharat Edition&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>voiceforbharat</category>
      <category>murfai</category>
      <category>vibecoding</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
