<?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: Praveen Rajak</title>
    <description>The latest articles on DEV Community by Praveen Rajak (@praveen_rajak).</description>
    <link>https://dev.to/praveen_rajak</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%2F4078466%2F6583140e-4b87-47b6-b70b-cca8b5f6c886.png</url>
      <title>DEV Community: Praveen Rajak</title>
      <link>https://dev.to/praveen_rajak</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/praveen_rajak"/>
    <language>en</language>
    <item>
      <title>I built a voice agent that explains money to people who were never taught it</title>
      <dc:creator>Praveen Rajak</dc:creator>
      <pubDate>Sat, 15 Aug 2026 04:45:21 +0000</pubDate>
      <link>https://dev.to/praveen_rajak/i-built-a-voice-agent-that-explains-money-to-people-who-were-never-taught-it-5037</link>
      <guid>https://dev.to/praveen_rajak/i-built-a-voice-agent-that-explains-money-to-people-who-were-never-taught-it-5037</guid>
      <description>&lt;h2&gt;
  
  
  I built a voice agent that explains money to people who were never taught it — here's how
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;My 10 Days of Voice Agents — VoiceForBharat Edition, and how you can build one too.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem, and who it's for
&lt;/h2&gt;

&lt;p&gt;A huge number of people in India are using a bank account, UPI, and government schemes for the first time in their lives. The information they need exists — but it lives in English PDFs, dense scheme portals, and bank jargon. The people who most need it are often the least comfortable reading it, and they're the exact people fraudsters target with fake "OTP" and "KYC" calls.&lt;/p&gt;

&lt;p&gt;Text-first apps quietly exclude these users. A &lt;strong&gt;voice&lt;/strong&gt; helpline doesn't. You call, you talk in the words you actually use — including mixing a little Hindi into your English — and someone patient explains one idea at a time. No app to install, no form to read, no typing.&lt;/p&gt;

&lt;p&gt;So for the &lt;strong&gt;Financial Services track&lt;/strong&gt;, I built &lt;strong&gt;Dhan Saathi&lt;/strong&gt; ("wealth companion") — a warm, voice-first money guide for everyday people in India. It explains banking basics, saving, UPI safety, and government schemes in plain words, and it is very careful about the one thing that matters most in finance: &lt;strong&gt;it never asks for your OTP or PIN, and it never pretends to be your bank.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Dhan Saathi actually does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Explains how a savings account, UPI, or a scheme works — one short idea at a time, out loud.&lt;/li&gt;
&lt;li&gt;Warns about common frauds and gives the real escalation path (call your bank, cyber helpline 1930).&lt;/li&gt;
&lt;li&gt;Remembers you between calls (with your permission) so you don't repeat yourself.&lt;/li&gt;
&lt;li&gt;Hands you to a &lt;strong&gt;scheme specialist&lt;/strong&gt; when you ask about government schemes.&lt;/li&gt;
&lt;li&gt;Can &lt;strong&gt;call you back&lt;/strong&gt; for a reminder, and can &lt;strong&gt;raise a request for a real human&lt;/strong&gt; when something is beyond a helpline.&lt;/li&gt;
&lt;li&gt;Logs every call to an &lt;strong&gt;analytics dashboard&lt;/strong&gt; so I can see what's working.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of it in an Indian English voice, powered by &lt;strong&gt;Murf Falcon 2&lt;/strong&gt; — which mattered more than I expected (more on latency below).&lt;/p&gt;




&lt;h2&gt;
  
  
  How the system works
&lt;/h2&gt;

&lt;p&gt;A voice agent is really four moving parts wired together in real time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🎙️ You speak
   │  audio
   ▼
Deepgram STT (nova-3, "multi")   ← ears: speech → text, Hindi + English
   │  text
   ▼
Gemini (LLM)                     ← brain: decides what to say / which tool to call
   │  response text
   ▼
Murf Falcon 2 TTS (en-IN-anisha) ← voice: text → natural Indian-English speech
   │  audio
   ▼
LiveKit (real-time transport)    ← the pipe carrying audio both ways
   │
   ▼
🔊 You hear the reply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;STT (ears):&lt;/strong&gt; Deepgram Nova-3 in &lt;code&gt;"multi"&lt;/code&gt; mode, so it transcribes code-mixed Hindi/English, not just English.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM (brain):&lt;/strong&gt; Google Gemini. This is where the personality, guardrails, and tool-calling live.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TTS (voice):&lt;/strong&gt; Murf Falcon 2, voice &lt;code&gt;en-IN-anisha&lt;/code&gt;, "Conversation" style.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transport:&lt;/strong&gt; LiveKit Agents carries audio in both directions and handles turn detection, so the agent knows when you've stopped talking. The same transport works for a browser mic &lt;strong&gt;and&lt;/strong&gt; a real phone call over a SIP trunk.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The whole backend is a single long-lived Python process. The frontend is a Next.js app. Neither calls the other directly — they both connect to the same LiveKit room, and LiveKit is the meeting point.&lt;/p&gt;




&lt;h2&gt;
  
  
  The features that tell the story
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. A personality with hard safety rails
&lt;/h3&gt;

&lt;p&gt;The single most important file is the system prompt. I structured it as &lt;strong&gt;IDENTITY / OBJECTIVES / KNOWLEDGE / LANGUAGE / GUARDRAILS / STYLE&lt;/strong&gt; so the agent has a clear job and clear limits. The guardrails are non-negotiable and read like a finance-helpline code of conduct:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Never ask for or accept an OTP, PIN, CVV, or account number — and stop the caller if &lt;em&gt;they&lt;/em&gt; start to share one.&lt;/li&gt;
&lt;li&gt;Never promise a loan or scheme will be approved.&lt;/li&gt;
&lt;li&gt;Never quote an interest rate or scheme figure from memory.&lt;/li&gt;
&lt;li&gt;Never move money or claim to access an account.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agent even opens the call by &lt;em&gt;promising&lt;/em&gt; it will never ask for your OTP — turning a guardrail into a trust signal.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Indian voice + code-mixed language, written in its own script
&lt;/h3&gt;

&lt;p&gt;Beyond the Indian-English Falcon voice, the prompt lets the agent mix a little Hindi back when the caller does, and switch fully if they do. One rule that took real work: &lt;strong&gt;every language must be written in its own native script&lt;/strong&gt; — Hindi in Devanagari (नमस्ते), never romanized "namaste" — because Falcon pronounces native script correctly but mangles romanized Hindi.&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%2F2vuhozga56f8i4fr6o11.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%2F2vuhozga56f8i4fr6o11.png" alt="Analytics Dashboard" width="800" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Memory for returning callers
&lt;/h3&gt;

&lt;p&gt;A tiny SQLite store remembers a caller by a slug of their name. On the &lt;em&gt;next&lt;/em&gt; call, the agent greets them and picks up where they left off ("last time you asked about Jan Dhan — how did that go?"). Two things make it safe and useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Permission first.&lt;/strong&gt; It only saves after explicitly asking, and only saves plain facts (a scheme they asked about, an age band) — never a sentence, never a number.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A hard backstop.&lt;/strong&gt; Even if the model slips, a sanitizer drops any value containing a long digit sequence before it can ever be written:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;_LONG_DIGITS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;\d{6,}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# looks like an account / card / OTP number
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_sanitize_facts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;facts&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="o"&gt;-&amp;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;clean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;facts&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;_LONG_DIGITS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
            &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Refusing to store sensitive-looking fact: %r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="n"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;clean&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. A specialist handoff
&lt;/h3&gt;

&lt;p&gt;The main guide deliberately does &lt;strong&gt;not&lt;/strong&gt; answer government-scheme detail. The moment you ask about a scheme, it hands you to &lt;strong&gt;Yojana Mitra&lt;/strong&gt;, a second agent with a narrower, sharper job and its own &lt;code&gt;check_scheme_eligibility&lt;/code&gt; tool. In LiveKit Agents, a handoff is beautifully simple — a tool just returns another &lt;code&gt;Agent&lt;/code&gt;, and the whole conversation carries over so you never repeat yourself:&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="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;transfer_to_scheme_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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Hand the caller to Yojana Mitra, the scheme specialist.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;SchemeSpecialist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;chat_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;chat_ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# the entire conversation carries over
&lt;/span&gt;        &lt;span class="n"&gt;call_id&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;_call_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;# so success still attributes to this call
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the scheme question is done, the specialist hands the call &lt;strong&gt;back&lt;/strong&gt; — and the main guide picks up mid-conversation instead of re-greeting from scratch.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Outbound calls, human escalation, and a dashboard
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Outbound:&lt;/strong&gt; a dispatcher can make Dhan Saathi &lt;em&gt;call you&lt;/em&gt; — over a real SIP trunk — to deliver a reminder, with opt-out handling ("don't call again") baked in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human escalation:&lt;/strong&gt; for fraud or a dispute that a helpline can't settle, the agent asks permission and raises a structured request for a real person, then reads back a reference id.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics:&lt;/strong&gt; every call is recorded with an outcome — &lt;code&gt;success&lt;/code&gt; (an eligibility check or a human handoff happened), &lt;code&gt;failed&lt;/code&gt;, or &lt;code&gt;no_answer/busy/declined&lt;/code&gt; for outbound — and served on a small live dashboard so I can see success rate by channel.&lt;/li&gt;
&lt;/ul&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%2Fz4py8z5c1qr1cqiuv1vz.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%2Fz4py8z5c1qr1cqiuv1vz.png" alt="Help desk logs" width="800" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The parts that were genuinely hard
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Falcon was fast — my &lt;em&gt;own config&lt;/em&gt; was what added lag
&lt;/h3&gt;

&lt;p&gt;My first instinct was to "help" the TTS by setting a minimum sentence length and text pacing so speech would sound more deliberate. It did the opposite: it chopped the audio into fragments and &lt;em&gt;added&lt;/em&gt; latency. The fix was to delete my cleverness and trust Falcon's default streaming tokenizer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;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;en-IN-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;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="c1"&gt;# no min_sentence_len, no text_pacing — those fragment the audio and add lag
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; with a TTS this fast, the pipeline is not your bottleneck — your own "optimizations" often are. Measure before you tune.&lt;/p&gt;

&lt;h3&gt;
  
  
  The model kept romanizing Hindi
&lt;/h3&gt;

&lt;p&gt;Early on, when a caller spoke Hindi, the LLM would reply with "namaste" instead of "नमस्ते" — and Falcon pronounced the romanized version poorly. Coaxing it in the general instructions wasn't enough. I had to add an explicit, separate &lt;strong&gt;LANGUAGE &amp;amp; SCRIPT&lt;/strong&gt; rule stating every language must be written in its native script, with an example and an anti-example. That specificity is what made it stick.&lt;/p&gt;

&lt;h3&gt;
  
  
  Making memory &lt;em&gt;trustworthy&lt;/em&gt;, not just functional
&lt;/h3&gt;

&lt;p&gt;Getting the agent to save something was easy. Getting it to save the &lt;em&gt;right&lt;/em&gt; thing was not. It would try to store whole sentences ("caller asked to save Jan Dhan") as a fact value. I fixed this in two layers: a tightened prompt that says "pass only the real fact, never a sentence or an instruction," &lt;strong&gt;and&lt;/strong&gt; the digit-sanitizer backstop above. In finance, "the prompt usually behaves" is not good enough — you need a mechanical guarantee for the sensitive cases.&lt;/p&gt;




&lt;h2&gt;
  
  
  Build your own — a practical starting point
&lt;/h2&gt;

&lt;p&gt;You genuinely can stand up a talking agent in an afternoon. Here's the shape.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The four components&lt;/strong&gt; you need: &lt;strong&gt;STT&lt;/strong&gt; (speech→text), an &lt;strong&gt;LLM&lt;/strong&gt; (the brain), &lt;strong&gt;TTS&lt;/strong&gt; (text→speech), and &lt;strong&gt;real-time transport&lt;/strong&gt; (LiveKit) to move audio both ways with turn detection. Start from the &lt;a href="https://github.com/murf-ai/murf-livekit-starter" rel="noopener noreferrer"&gt;Murf LiveKit Starter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Get the code and install:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/praveenraj027/murf-livekit-starter
&lt;span class="nb"&gt;cd &lt;/span&gt;murf-livekit-starter
&lt;span class="nb"&gt;cd &lt;/span&gt;backend  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; uv &lt;span class="nb"&gt;sync&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; uv run python src/agent.py download-files
&lt;span class="nb"&gt;cd&lt;/span&gt; ../frontend &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pnpm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Add your API keys — safely.&lt;/strong&gt; Create &lt;code&gt;.env.local&lt;/code&gt; in &lt;em&gt;both&lt;/em&gt; &lt;code&gt;backend/&lt;/code&gt; and &lt;code&gt;frontend/&lt;/code&gt; (copy from each &lt;code&gt;.env.example&lt;/code&gt;). You need &lt;code&gt;LIVEKIT_URL&lt;/code&gt;, &lt;code&gt;LIVEKIT_API_KEY&lt;/code&gt;, &lt;code&gt;LIVEKIT_API_SECRET&lt;/code&gt;, &lt;code&gt;MURF_API_KEY&lt;/code&gt;, &lt;code&gt;DEEPGRAM_API_KEY&lt;/code&gt;, and a &lt;code&gt;GOOGLE_API_KEY&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Never commit keys.&lt;/strong&gt; &lt;code&gt;.env.local&lt;/code&gt; / &lt;code&gt;.env.*&lt;/code&gt; are gitignored (with &lt;code&gt;!.env.example&lt;/code&gt; as the one exception). Keys go in the deployment platform's secret store, never in code, never in a screenshot. Before you push, check your diff.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;3. Run it:&lt;/strong&gt;&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="c"&gt;# Terminal 1 — backend agent&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;backend &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; uv run python src/agent.py dev
&lt;span class="c"&gt;# Terminal 2 — frontend&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;frontend &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pnpm dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Connect and test:&lt;/strong&gt; open &lt;code&gt;http://localhost:3000&lt;/code&gt;, click &lt;strong&gt;Start talking&lt;/strong&gt;, allow the mic, and talk. Want to test with no UI at all? &lt;code&gt;uv run python src/agent.py console&lt;/code&gt; gives you a terminal-only conversation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Make it yours:&lt;/strong&gt; everything the agent &lt;em&gt;is&lt;/em&gt; lives in &lt;code&gt;SYSTEM_PROMPT&lt;/code&gt; in &lt;code&gt;backend/src/agent.py&lt;/code&gt;. Change that string and you change the whole agent — support bot, tutor, receptionist. Change the &lt;code&gt;voice&lt;/code&gt; in &lt;code&gt;murf.TTS(...)&lt;/code&gt; to pick from Murf's voice library. Add a capability by writing a method with the &lt;code&gt;@function_tool&lt;/code&gt; decorator.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd improve next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real scheme data.&lt;/strong&gt; Eligibility currently comes from a curated local dataset; wiring it to an official, dated source would make the numbers authoritative.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Voice authentication for memory.&lt;/strong&gt; Today, memory is keyed on a spoken name. For anything more sensitive, that's too weak — I'd want a proper caller identity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latency metrics on the dashboard.&lt;/strong&gt; I track call outcomes; I'd add time-to-first-audio and turn latency so I can prove the "it feels instant" claim with numbers.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The code and the details
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/praveenraj027/murf-livekit-starter" rel="noopener noreferrer"&gt;https://github.com/praveenraj027/murf-livekit-starter&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Murf Falcon:&lt;/strong&gt; the fastest production TTS — ~55ms model latency, ~130ms time-to-first-audio — which is what makes a voice helpline feel like a conversation instead of a walkie-talkie.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're thinking about who your app &lt;em&gt;doesn't&lt;/em&gt; reach today, voice is often the answer — and it's more approachable to build than you'd think. Pick a real person, give your agent a clear job and hard limits, and start talking to it.&lt;/p&gt;

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

</description>
      <category>ai</category>
      <category>database</category>
      <category>voice</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
