<?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: CosmosTechy</title>
    <description>The latest articles on DEV Community by CosmosTechy (@cosmostechy).</description>
    <link>https://dev.to/cosmostechy</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%2F4109303%2F873d6381-48af-46d8-b41c-95c9d971ed48.png</url>
      <title>DEV Community: CosmosTechy</title>
      <link>https://dev.to/cosmostechy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cosmostechy"/>
    <language>en</language>
    <item>
      <title>How I Built Maya: A Real-Time Voice AI Clinic Receptionist</title>
      <dc:creator>CosmosTechy</dc:creator>
      <pubDate>Fri, 04 Sep 2026 08:12:17 +0000</pubDate>
      <link>https://dev.to/cosmostechy/how-i-built-maya-a-real-time-voice-ai-clinic-receptionist-45m</link>
      <guid>https://dev.to/cosmostechy/how-i-built-maya-a-real-time-voice-ai-clinic-receptionist-45m</guid>
      <description>&lt;p&gt;Most AI demos you see online are either simple text chatbots or basic wrappers around an API.&lt;/p&gt;

&lt;p&gt;I wanted to build something that felt like a real product solving a real problem. So I built Maya—a real-time voice AI receptionist for a fictional clinic in Bengaluru called SwasthyaCare Clinic.&lt;/p&gt;

&lt;p&gt;Instead of typing into a chat box, you just talk to your microphone like you're on a real phone call. Maya answers your questions, checks doctor schedules, books appointments, reschedules or cancels existing visits, and gives clinic&lt;br&gt;
  information—all in real-time with sub-second voice latency.&lt;/p&gt;

&lt;p&gt;Here is a breakdown of how the architecture works, the real issues I faced during development (rate limits, background noise, Docker container paths), and how I solved them.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. What Maya Does
&lt;/h2&gt;

&lt;p&gt;When someone calls a clinic, they don't want to navigate a robotic IVR menu ("Press 1 for appointments..."). They want to talk to a human receptionist.&lt;br&gt;
  Maya handles that conversational flow:&lt;/p&gt;

&lt;p&gt;• Checks live doctor availability: Understands dates and doctor specialties (General Physician vs. Dentist).&lt;br&gt;
  • Gathers details naturally: Asks for missing information (name, 10-digit mobile number, preferred slot) across multiple turns.&lt;br&gt;
  • Explicit confirmation before booking: Summarizes the appointment details and only commits the booking once the user says "Yes".&lt;br&gt;
  • Enforces business rules: Only books up to 14 days in advance, checks for slot collisions, and enforces a strict 2-hour cancellation/rescheduling policy.&lt;br&gt;
  • Medical triage guardrails: If someone mentions severe symptoms like acute chest pain or breathing issues, Maya immediately instructs them to dial 112 or visit an emergency room rather than booking a routine outpatient slot.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The Architecture &amp;amp; Tech Stack
&lt;/h2&gt;

&lt;p&gt;To make voice feel natural, latency has to be as close to human conversational speed as possible. A typical turn looks like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User speaks into Browser Mic
       ↓ (WebRTC Audio Stream)
LiveKit Cloud (ap-south / Mumbai)
       ↓
Silero VAD (Voice Activity Detection on-device)
       ↓
Groq Whisper (Speech-to-Text)
       ↓
Groq LLM (Reasoning + Function/Tool Calling)
       ↓
SQLite Database (Appointment State Engine)
       ↓
Cartesia TTS (Streaming Indian English Voice)
       ↓ (WebRTC Audio)
Browser Speakers (User hears response)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  The Stack Breakdown:
&lt;/h4&gt;

&lt;p&gt;• Audio Transport: LiveKit (WebRTC). Handles real-time, low-latency audio streaming between the browser and backend worker.&lt;br&gt;
  • VAD (Voice Activity Detection): Silero VAD. Runs locally inside the worker to detect when the user starts and stops speaking.&lt;br&gt;
  • STT (Speech-to-Text): Groq Whisper (whisper-large-v3-turbo). Transcribes speech into text in ~100–200ms.&lt;br&gt;
  • LLM Reasoning &amp;amp; Tool Calling: Groq (openai/gpt-oss-120b). Fast reasoning and function calling.&lt;br&gt;
  • TTS (Text-to-Speech): Cartesia (sonic-turbo). Uses the "Priya" voice profile—a natural, clear Indian English tone suited for a Bengaluru clinic.&lt;br&gt;
  • State &amp;amp; Database: SQLite + Python. Manages appointments, availability checks, and audit trails.&lt;br&gt;
  • Frontend: React + Vite with @livekit/components-react and Tailwind CSS.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. The Real-World Engineering Problems I Hit
&lt;/h2&gt;

&lt;p&gt;Building the basic happy path is easy; making real-time voice work reliably is where the real learning happened. Here are 4 specific problems I ran into:&lt;/p&gt;
&lt;h4&gt;
  
  
  Bug 1: The 429 Rate Limit Trap on Groq
&lt;/h4&gt;

&lt;p&gt;When I first ran voice tests, everything would work for 2 or 3 turns, and then suddenly crash with an HTTP 429 Too Many Requests (Rate Limit Exceeded).&lt;/p&gt;

&lt;p&gt;Why it happened:&lt;br&gt;
  On Groq's free tier, there is an 8,000 Tokens Per Minute (TPM) limit. My initial system prompt combined with the JSON schemas for 5 function tools was taking ~1,900 tokens per single LLM call. If the user spoke 4 times in a minute,&lt;br&gt;
  that was 1,900 * 4 = 7,600+ tokens, immediately blowing through the 8,000 token limit.&lt;/p&gt;

&lt;p&gt;How I fixed it:&lt;br&gt;
  I completely compressed the system prompt and tool docstrings. I removed repetitive instructions, used dense bullet points, and kept the tool parameters minimal while preserving all clinical guardrails. This brought the request size&lt;br&gt;
  down from ~1,900 tokens to ~500 tokens (a 73% reduction). Suddenly, I could have 15+ turns a minute without hitting rate limits.&lt;/p&gt;
&lt;h4&gt;
  
  
  Bug 2: Ambient Background Noise Eating API Quota
&lt;/h4&gt;

&lt;p&gt;While testing with my laptop mic, I noticed the agent would randomly trigger and start speaking even when I hadn't said anything.&lt;/p&gt;

&lt;p&gt;Why it happened:&lt;br&gt;
  Default VAD sensitivity was picking up subtle background sounds—fan noise, keyboard typing, and breathing. Each micro-sound triggered LiveKit's turn detector, which immediately dispatched an STT call, an LLM call, and a TTS synthesis&lt;br&gt;
  call. This was burning API quota and interrupting the conversation.&lt;/p&gt;

&lt;p&gt;How I fixed it:&lt;br&gt;
  I tuned the Silero VAD parameters and turn handling in LiveKit:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vad_instance = silero.VAD.load(
    min_speech_duration=0.25,   # Ignore clicks and breath sounds under 250ms
    min_silence_duration=0.65,  # Wait for a clean pause before marking end-of-turn
    prefix_padding_duration=0.3,
    activation_threshold=0.6,   # Require clearer vocal energy over ambient noise
)

session = AgentSession(
    turn_handling={
        "endpointing": {"min_delay": 0.6, "max_delay": 3.0},
        "preemptive_generation": {"enabled": False},  # Only generate audio when speech is complete
        "interruption": {"enabled": True, "min_duration": 0.5},
    }
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Disabling preemptive generation and requiring at least 250ms of vocal energy eliminated the false triggers completely.&lt;/p&gt;

&lt;h4&gt;
  
  
  Bug 3: Finding the Right Voice (US Accent vs. Indian Accent)
&lt;/h4&gt;

&lt;p&gt;Initially, I used Groq's built-in TTS. While it worked, it only had US and Arabic voice profiles, and the free-tier rate limits were strict for voice generation.&lt;/p&gt;

&lt;p&gt;For a clinic located in HSR Layout, Bengaluru, a North American voice felt out of place. I integrated Cartesia's sonic-turbo model with their Priya voice (an Indian English female voice profile).&lt;/p&gt;

&lt;p&gt;The difference was night and day:&lt;/p&gt;

&lt;p&gt;• Latency dropped below 100ms.&lt;br&gt;
  • The cadence and pronunciation of Indian names sounded authentic.&lt;/p&gt;

&lt;h4&gt;
  
  
  Bug 4: Deploying Voice AI is Not Like Deploying a Chatbot
&lt;/h4&gt;

&lt;p&gt;When I tried deploying the Python backend, I quickly realized you can't just throw a voice agent onto serverless platforms like Vercel or AWS Lambda.&lt;/p&gt;

&lt;p&gt;A text chatbot handles a quick HTTP request and terminates in 1 second. A voice agent, on the other hand, is a persistent WebRTC worker daemon. It maintains an active bidirectional audio socket to LiveKit Cloud 24/7.&lt;/p&gt;

&lt;p&gt;The Solution:&lt;/p&gt;

&lt;p&gt;• React Frontend: Deployed on Vercel with a serverless token endpoint (/api/token) that generates short-lived LiveKit JWT access tokens without exposing LIVEKIT_API_SECRET to the browser.&lt;br&gt;
  • Python Agent Worker: Containerized via Docker and deployed to LiveKit Cloud Agent Hosting in the ap-south (Mumbai) region for ultra-low ping.&lt;/p&gt;

&lt;p&gt;When deploying the Docker container, I ran into a ModuleNotFoundError: No module named 'agent.prompts'. The container entrypoint was running python agent/agent.py start, which put /app/agent into Python's sys.path instead of the root&lt;br&gt;
  /app. I fixed this by adding ENV PYTHONPATH="/app" in the Dockerfile and adding a defensive path resolution in Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Key Takeaways from Building This
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Latency is king in voice: A 1.5-second pause in a text chat is fine. In a phone call, a 1.5-second pause feels awkward and broken. Streaming audio chunks and optimizing model TTFT (Time-to-First-Token) is critical.&lt;/li&gt;
&lt;li&gt;Prompt token economy matters: In voice agents, every single spoken phrase sends the entire context back to the LLM. Keeping prompts concise directly prevents rate limit crashes and saves money.&lt;/li&gt;
&lt;li&gt;Noise suppression isn't optional: Real users don't sit in soundproof recording studios. Tuning VAD thresholds is just as important as choosing the LLM.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  5. What's Next &amp;amp; Looking for Feedback
&lt;/h2&gt;

&lt;p&gt;This project gave me a massive appreciation for what it takes to build reliable real-time AI systems.&lt;/p&gt;

&lt;p&gt;I'd love to connect with other engineers and builders working in Voice AI, WebRTC, and LLM tool calling:&lt;/p&gt;

&lt;p&gt;• How are you handling ambient noise and interruption handling in your voice agents?&lt;br&gt;
  • What TTS providers have you found best for regional accents?&lt;/p&gt;

&lt;p&gt;Check out the code and feel free to share your thoughts or suggestions!&lt;/p&gt;

&lt;p&gt;🔗 GitHub Repo: &lt;a href="https://github.com/CosmosTechy/maya-ai-voice-receptionist" rel="noopener noreferrer"&gt;https://github.com/CosmosTechy/maya-ai-voice-receptionist&lt;/a&gt;&lt;br&gt;
  🌐 Live Demo: &lt;a href="https://maya-ai-voice-receptionist.vercel.app/" rel="noopener noreferrer"&gt;https://maya-ai-voice-receptionist.vercel.app/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>agents</category>
      <category>buildinpublic</category>
    </item>
  </channel>
</rss>
