latest Post Click Subscribe!
المشاركات

I Built a Hindi AI Shopkeeper in 10 Days & Then Everything Broke

A story about building DukaanSaathi, failing spectacularly at agent handoffs, and learning that voice AI for India isn't about translating English app
Please wait 0 seconds...
Scroll Down and click on Go to Link for destination
Congrats! Link is Generated

A story about building DukaanSaathi, failing spectacularly at agent handoffs, and learning that voice AI for India isn't about translating English apps, it's about building for people who prefer to call, not type.

I Built a Hindi AI Shopkeeper in 10 Days & Then Everything Broke

It Started With a Phone Call

Picture this: It's a hot afternoon in Laxmi Nagar, Delhi. Ramesh Sharma is standing behind the counter of his general store, juggling three things — weighing 2 kg of atta for a customer, checking stock on his dusty notebook, and answering his phone for the 47th time today.

"Bhaiya, aaj tel ka rate kya hai?"

He quotes the price, hangs up, and the phone rings again.

"Mera order kab aayega?"

And again.

"Kal jo atta aaya tha usme keede the, refund chahiye!"

Ramesh can't afford a call center. His customers don't use apps — they're aunties and uncles from the neighborhood who call because that's what they've always done. And honestly? Calling is easier than typing in Hindi on a phone.

When Murf AI announced the 10 Days of Voice Agents — VoiceForBharat Edition challenge, I thought: what if Ramesh had an AI assistant that could pick up those calls? Not a chatbot. Not an app. A voice — a warm, Hindi-speaking voice that knows his store, remembers his customers, and handles the routine so he can focus on his business.

That's how DukaanSaathi (दुकानसाथी) was born. Your Store's AI Companion.


Day 1–2: Teaching an AI to Be a Shopkeeper

The first two days were about giving DukaanSaathi a personality. I didn't want a generic assistant — I wanted someone who felt like they belonged in Sharma General Store.

I wrote a system prompt that defined her as a warm, helpful female assistant who speaks Hindi in Devanagari script. She greets customers by name, knows the store timings (7 AM to 10 PM, closed Tuesdays), and has Ramesh's personal touch — "Ramesh bhai ka number hai 98765 43210, unse seedhe baat kar lijiye."

The magic ingredient? Murf Falcon's Anisha voice. With 55ms latency and natural Hindi pronunciation, DukaanSaathi didn't sound like a robot reading a script. She sounded like someone who actually worked at the store. The Conversation style made her responses feel casual and warm — exactly how a real store assistant would talk.

tts=murf.TTS(
    voice="Anisha",
    style="Conversation",
    text_pacing=True,  # Natural speaking pace
)

I also added safety guardrails early — she never hallucinate prices, never speaks sensitive data like PINs or OTPs aloud, and always asks permission before taking action. These aren't nice-to-haves. For a voice agent handling real customer calls, they're non-negotiable.


Day 3–4: The Agent Gets Eyes and Memory

Day 3 was about giving DukaanSaathi a proper frontend. The LiveKit starter already had a great base, but I customized it for our kirana store context — Hindi status messages, a branded welcome screen, and a real-time chat transcript so you could see what was being said.

Day 4 brought customer memory. This changed everything.

Before memory, every call started from scratch — "Aapka naam kya hai?" every single time. After adding SQLite-backed persistence, returning customers got a completely different experience:

"Namaste Gyanesh ji! Aapka phir se swagat hai. Pichli baar aapne atta aur tel liya tha. Aaj kya chahiye?"

It's a small thing, but it's the difference between talking to a stranger and talking to someone who knows you. That's what makes kirana stores special — the personal touch — and DukaanSaathi needed to have it too.


Day 5: Real Tools, Real Data

This is where DukaanSaathi stopped being a demo and started being useful.

I built a product catalog for Sharma General Store — atta, edible oil, sugar, milk, dal, spices, snacks — with live prices, stock status, and unit information. Then I created function tools that the LLM could call:

  • lookup_product"Atta ka rate?""Aaj atta 45 rupees per kg hai, stock mein hai."
  • calculate_bill"2 kg atta aur 1 litre tel ka bill banao" → Computes subtotal, delivery charges (free above ₹500), and total
  • save_customer / delete_customer — Persistent memory management

The hardest part was handling failures gracefully. What if someone asks about a product that's not in the catalog? A bad agent would hallucinate a price. DukaanSaathi says: "Yeh item abhi catalog mein nahi hai, aap Ramesh bhai se seedhe pooch sakte hain." Honest. Helpful. No made-up numbers.


Day 6: The Phone Rings... From the Other Side

Day 6 was outbound calling — DukaanSaathi could now initiate calls to customers using SIP trunking through LiveKit. Imagine: a customer's monthly atta order is due, and DukaanSaathi calls them:

"Namaste! Main DukaanSaathi bol rahi hoon, Sharma General Store ki taraf se. Aapka monthly atta restock due hai. Kya aaj order kar doon?"

I added strict consent rules — if anyone says "mujhe call mat karo", she immediately responds "Ji bilkul, main aage se call nahi karungi. Shukriya!" and stops. No nagging. No dark patterns.


Day 7: When AI Needs to Say "I Don't Know"

This was humbling. I realized DukaanSaathi couldn't handle everything. Damaged items? Payment disputes? Bulk pricing negotiations? These need a human.

So I built a two-turn escalation flow:

Turn 1: "Kya main Ramesh bhai ko aapki yeh request ticket bhej doon?"

Turn 2 (only if user says YES): Creates a ticket with a reference ID (TICK-8492), saves it to SQLite, and gives an honest timeline — "Ramesh bhai 2 se 4 ghante mein aapse baat karke solve kar denge."

The two-turn pattern was crucial. Without it, the agent would sometimes create tickets the customer didn't ask for. Always ask permission first.


Day 8: Measuring What Matters

I built a Call Analytics Dashboard in Next.js that shows:

  • Total calls, successful calls, failed calls, success rate
  • Average call duration
  • Which tools were used (product lookup, bill calculation, escalation)
  • Channel breakdown (browser vs outbound SIP)

A call is "successful" if the customer completes a product lookup, bill calculation, or any meaningful interaction. Early disconnects (under 5 seconds) are marked as failed. Simple, but it gives Ramesh real insight into how his AI assistant is performing.


Day 9: The Day Everything Broke (And Then Worked Beautifully)

Day 9 was about specialist agent handoff — the most technically challenging feature. The idea: when a customer asks about refunds or damaged products, DukaanSaathi should transfer the call to SevaSaathi (सेवासाथी) — a dedicated male Returns & Refunds specialist with a completely different voice.

What I wanted:

  1. DukaanSaathi (female voice, Anisha) announces: "Main aapko SevaSaathi ke paas transfer kar rahi hoon"
  2. Voice audibly switches to male (Samar)
  3. SevaSaathi introduces himself and handles the refund

What actually happened (Attempt 1):

I tried manually swapping the TTS inside the tool function:

self.session.tts = murf.TTS(voice="Samar", ...)

💥 AttributeError: property 'tts' of 'AgentSession' object has no setter

The tts property is read-only. Dead end.

Attempt 2: I tried the internal attribute:

self.session._tts = murf.TTS(voice="Samar", ...)

It didn't crash, but the voice changed before DukaanSaathi finished her announcement. So her transfer message — "Main aapko SevaSaathi ke paas transfer kar rahi hoon" — came out in a male voice. Confusing and wrong.

Attempt 3: I moved the voice change to on_enter() and added generate_reply():

async def on_enter(self):
    self.session._tts = murf.TTS(voice="Samar", ...)
    await self.session.generate_reply()

The handoff stopped working entirely. DukaanSaathi would announce the transfer, but SevaSaathi never took over. The user had to say "Hello?" to get any response — and even then, it was DukaanSaathi responding again.

Attempt 4: I added asyncio.create_task() with a 0.5s delay to trigger the specialist's greeting after the main agent finished speaking. This caused double-speech — both agents talking simultaneously.

The Breakthrough (Attempt 5):

After four failures, I found the official Murf LiveKit agent handoff example. The correct pattern was completely different from what I'd been doing:

# The correct way — return a tuple, let the framework handle everything
@function_tool()
async def transfer_to_returns_specialist(self, context: RunContext, issue_description: str) -> tuple[Agent, str]:
    specialist = ReturnsSpecialist(
        chat_ctx=self.chat_ctx.copy(exclude_instructions=True),
    )
    return specialist, "जी, मैं आपको सेवासाथी के पास ट्रांसफर कर रही हूँ।"

And the specialist declares its own voice in __init__:

class ReturnsSpecialist(Agent):
    def __init__(self, ...):
        super().__init__(
            instructions=RETURNS_SPECIALIST_PROMPT,
            tts=murf.TTS(voice="Samar", style="Conversation", ...),
        )

That's it. No manual session manipulation. No hacking internal properties. No sleep delays. The LiveKit framework handles the entire transition — draining the old agent's speech, switching to the new agent, and triggering on_enter() automatically.

The lesson: When a framework provides a pattern, use it. Don't fight the abstraction.


Day 10: Writing This Post

And here we are. Ten days, one AI shopkeeper, one specialist agent, approximately 47 failed attempts at various things, and one working voice AI system.


Want to Build Your Own? Here's How

What You Need

Component Service Purpose
TTS Murf Falcon Converts text to natural Hindi speech (55ms latency)
STT Deepgram Converts speech to text
LLM Google Gemini Reasoning, tool calling, response generation
Transport LiveKit Real-time audio streaming via WebRTC

Quick Start

# Clone
git clone https://github.com/gyanesh-maurya/murf-livekit-starter.git
cd murf-livekit-starter

# Backend
cd backend
uv sync
uv run python src/agent.py download-files
uv run python src/agent.py dev

# Frontend (new terminal)
cd frontend
pnpm install
pnpm dev

Create .env.local in both backend/ and frontend/ with your API keys:

LIVEKIT_URL=wss://your-project.livekit.cloud
LIVEKIT_API_KEY=your_key
LIVEKIT_API_SECRET=your_secret
MURF_API_KEY=your_murf_key
DEEPGRAM_API_KEY=your_deepgram_key
GOOGLE_API_KEY=your_google_key

⚠️ Never commit .env.local to git. It's already in .gitignore.

Open http://localhost:3000, click Start Talking, and say "Namaste!"


What's Next for DukaanSaathi

If I had another 10 days, I'd add:

  • WhatsApp voice notes — Most kirana customers use WhatsApp, not browser calls
  • Real inventory sync — Connect to actual POS systems instead of a static catalog
  • Multi-language — Gujarati, Tamil, Bengali for wider Bharat coverage
  • Call summarization — Auto-generate daily reports for Ramesh

The Bigger Picture

India has 12 million+ kirana stores. They handle ₹12 lakh crore in annual sales. And most of them run on phone calls and personal relationships.

Voice AI isn't going to replace those relationships — but it can handle the routine so store owners like Ramesh can focus on the personal. That's not disruption. That's assistance.

And with tools like Murf Falcon making Hindi TTS sound genuinely natural at 55ms latency, we're closer to that future than I thought possible 10 days ago.


🔗 Source Code: github.com/gyanesh-maurya/murf-livekit-starter

Built during the 10 Days of Voice Agents — VoiceForBharat Edition challenge by Murf AI. Powered by the fastest TTS API on the market — Murf Falcon.

About the Author

Passionate coder dedicated to crafting innovative solutions.

إرسال تعليق

Please do not post any spam links
Cookie Consent
We serve cookies on this site to analyze traffic, remember your preferences, and optimize your experience.
Oops!
It seems there is something wrong with your internet connection. Please connect to the internet and start browsing again.
AdBlock Detected!
We have detected that you are using adblocking plugin in your browser.
The revenue we earn by the advertisements is used to manage this website, we request you to whitelist our website in your adblocking plugin.
Site is Blocked
Sorry! This site is not available in your country.