WhatsApp AI Agent: Multilingual Lead Qualification with Zero Dependencies
An open-source WhatsApp business agent that chats in English, Hindi, and Hinglish, qualifies leads (name → need → budget → timeline), answers FAQs, and books appointments — with a free-LLM mode and a fully scripted fallback. Standard library only: no pip installs.
TL;DR: This is a working WhatsApp agent in four Python files and one HTML file. agent.py (~38KB) is the brain: per-sender sessions, trilingual language detection that mirrors how the customer writes, regex-based extraction of name/need/budget/timeline, date and time parsing, FAQ detection, and a scripted conversation state machine — with an optional free-LLM layer (Groq, Gemini, Mistral, OpenRouter, or local Ollama) that upgrades reply quality while extraction stays rule-based for reliability. webhook.py is a stdlib HTTP server wiring it to Meta's WhatsApp Cloud API, Twilio, a JSON chat API, and a demo chat UI with a live lead panel. With no API keys at all, the whole thing runs locally in rule-based mode.
The problem: WhatsApp is where the customers are, but the tooling isn't
WhatsApp has over 3 billion users, and in markets like India it's not a messaging app — it's the business channel. Customers ask about prices, check timings, and expect replies at 11 PM. The standard answers are all bad for a small business: hire someone to watch the phone, buy an expensive chatbot platform, or let messages sit unanswered until morning.
This repo is the fourth option: a self-hosted agent you can read in an afternoon. No platform lock-in, no per-message SaaS fee, no black box. It handles the repetitive 80% — greeting, qualifying, FAQs, booking — in the customer's own language, and it's explicit about what it doesn't do (it never claims to bypass Meta's approval process; the README says anyone telling you otherwise is selling something).
How it works: a brain, a provider, a server, a UI
| File | Role | Key detail |
|---|---|---|
agent.py | Conversation engine | Per-sender sessions; language detection; lead/booking extraction; scripted state machine with LLM override |
llm_provider.py | Free-LLM client | OpenAI-compatible, stdlib-only (urllib); Groq / Gemini / Mistral / OpenRouter / Ollama |
webhook.py | HTTP server | Meta verification + inbound, Twilio TwiML, JSON chat API, serves the demo UI |
demo.html | Demo chat UI | Single file; live lead panel fills as you talk |
Language detection that mirrors the customer
detect_language() classifies each message as English, Hindi (Devanagari), or Hinglish — and the agent replies in the detected language. The implementation is two checks: a Devanagari Unicode-range regex ([\u0900-\u097F]) for Hindi script, and a lexicon of 90+ Hindi words written in Roman script (namaste, chahiye, kal, baje, haan, kitna…) for Hinglish. Two or more lexicon hits — or one hit in a short message — classifies as Hinglish. It's heuristic, transparent, and debuggable, which beats a black-box classifier for this job.
Lead extraction: regexes that understand Indian commerce
The extraction layer is where the repo shows its market. extract_name() handles "my name is…", "mera naam…", "main…", and bare names, with a stop-word guard and cleanup of trailing verbs (hoon/hai). extract_budget() parses the ways Indians actually state money: "2 lakh", "20-30 hazar", "₹20000", "Rs 50k", "$500", and bare 4–7 digit numbers — normalizing to formatted rupee amounts. extract_timeline() maps urgent/asap/jaldi/turant to "urgent" and week-relative phrases to buckets. parse_date() and parse_time() resolve kal (tomorrow), aaj (today), parso, and times like "shaam 5 baje" against the calendar.
Try the scripted demo flow from the README and watch the lead panel fill live: Namaste → Mera naam Rahul hai → Mujhe website chahiye → Budget 20000 tak hai → Next week tak chahiye → Haan → kal → shaam 5 baje → haan.
The session model: memory without a database
Each sender gets a session dictionary holding their detected language, the four lead fields (name, need, budget, timeline), the in-progress booking, the current conversation stage, and the message history — capped at the last 20 turns to bound memory. Language is re-detected on every message, but only overwrites the session default when the message isn't English or it's the first turn, which keeps a mid-conversation English clarification from flipping a Hindi speaker's session. The stage machine walks greeting → qualification → booking → confirmation, but the extractors also run opportunistically: if a user volunteers their budget in the first message, it's captured without being asked again. That opportunistic extraction is what makes the conversation feel human rather than form-like — the agent listens first and asks only for what's missing.
Two reply modes, one extraction pipeline
handle_message() runs the scripted state machine first, then asks the LLM for a better reply — only if one is configured and reachable:
reply, llm_used = self._scripted(s, text)
if not llm_used:
maybe = self._llm_reply(s, text)
if maybe:
reply, llm_used = maybe, True
This ordering is the architecture's best decision. The scripted flow guarantees a correct, on-rails reply; the LLM is an upgrade path, not a dependency. And lead extraction runs identically in both modes — the numbers in your lead panel never depend on model output. llm_provider.chat() returns None on any failure instead of raising, so a dead API key degrades to scripted mode silently rather than crashing the conversation. Session history is capped at 20 turns per sender to bound memory.
The server: Meta, Twilio, and a demo API
webhook.py is a stdlib ThreadingHTTPServer with five routes: GET /webhook answers Meta's verification handshake (mode + verify token + challenge echo); POST /webhook parses Meta's nested entry → changes → messages payload and replies through the Graph API (v19.0) — or logs a [DRY-RUN] line when no WhatsApp credentials are set; POST /twilio accepts form-encoded Twilio inbound and returns XML-escaped TwiML; POST /api/chat exposes the agent as JSON (reply, lead, booking, language, stage, llm); GET /api/status reports LLM mode and session count. GET / serves the demo chat UI.
Run it yourself
python3 webhook.py
# open http://localhost:8000/ — chat in the UI, watch the lead panel fill
No pip installs. To upgrade replies with a free LLM (optional):
# Groq — free tier, no credit card: https://console.groq.com
export LLM_PROVIDER=groq
export LLM_MODEL=openai/gpt-oss-120b
export LLM_API_KEY=your_key
python3 webhook.py
Alternatives with free tiers: gemini (AI Studio), mistral, openrouter (:free models), ollama (local, no key). Environment variables control branding (BUSINESS_NAME/AGENT_NAME, defaulting to VisionQuantech/Arjun), the Meta verify token, and WhatsApp credentials. Sanity checks: python3 -m py_compile agent.py webhook.py llm_provider.py, plus the README's curl recipes for the Meta handshake and the chat API.
Honest status and limits
Status: working local demo; integration-ready. Every advertised feature runs — the README's claim is "no stubs, no placeholders," and the code backs it: the agent, both webhook integrations, the LLM client, and the demo UI are all implemented. Without credentials it runs fully in dry-run/scripted mode, which is genuinely useful for testing conversation flows.
- Real WhatsApp needs Meta's approval. Test numbers work in the sandbox immediately; production needs a WhatsApp Business Account, an approved display name, and Meta review — plus per-conversation pricing beyond the free tier (~1,000 conversations/month). The README documents this instead of hand-waving it.
- Rule-based extraction has edges. Regexes handle the common cases well and fail loudly-visible rather than subtly, but unusual phrasings will miss — the LLM mode helps with reply quality, not extraction.
- Sessions are in-memory. Restart the server and conversation state is gone. A production deployment wants a persistent store.
- Change the verify token. The default (
visionquantech123) is in the repo; the README flags it.
Roadmap
- Persistent sessions (SQLite) so restarts don't lose conversations.
- Calendar integration — write bookings to Google Calendar instead of just summarizing them.
- Human handoff — explicit escalation path with conversation summary to a staff member.
- Template messaging support for Meta's 24-hour window rules.
- Analytics — qualification funnel, drop-off stages, booking conversion per channel.
- Voice notes — speech-to-text for WhatsApp audio messages.
Frequently asked questions
It chats with customers in English, Hindi, or Hinglish; qualifies leads by collecting name, need, budget, and timeline; answers FAQs (price, hours, location, services); and books appointments with a confirmation summary — over Meta's WhatsApp Cloud API, Twilio, or a demo web UI.
No. Without any key it runs fully in rule-based scripted mode. With a free-tier key (Groq, Gemini, Mistral, OpenRouter, or local Ollama), replies become LLM-generated while lead extraction stays rule-based for reliability.
Meta's sandbox test number works immediately. Messaging your own business number in production requires a WhatsApp Business Account, an approved display name, and Meta's review — the README walks through it honestly.
Yes — the entire app is Python standard library (
http.server, urllib, re, json). No pip install step exists.The bottom line
This is the rare "AI agent" repo you can actually read: ~38KB of transparent conversation logic, a provider client that fails safe, and a server that speaks both Meta and Twilio — with honest docs about what Meta requires and what the free tiers cover. If your customers live on WhatsApp, this is a credible starting point you can run tonight and extend tomorrow.
Try it: github.com/Shivay00001/whatsapp-ai-app — built by Shivam Kumar, VisionQuantech. Want a WhatsApp agent built and deployed for your business? [email protected]