Available for work

Based in NCR South, Philippines

← Go Back

Voice AI Assistant

A browser voice agent that answers portfolio questions out loud and books real Google Meet intro calls — only after the calendar API confirms a free slot — shipped as a one-line script embed.

Visit live site →

Client
Deviitor — portfolio product
Service
Deepgram · Cloudflare Workers
Year
2026
Role
Sole developer — agent, booking pipeline & embed
Timeline
Shipped · live & embedded on this site
Voice AI Assistant on desktop
voice.dilhara.deviitor.com
Voice AI Assistant on mobile
Voice Continuous browser sessions
Calendar Hard freeBusy validation
Meet Bookings with chat link cards
1 script Embeddable on any host

A portfolio can sell with copy and case studies, but the conversion that actually closes work is a conversation. Forms and static booking pages lose people between “interested” and “scheduled.” I wanted visitors on this site to talk to an assistant, ask about services and projects, and leave with a real Meet on the calendar — without inventing availability.

The product is a continuous voice session in the browser: microphone to Deepgram’s Voice Agent WebSocket, markdown-driven knowledge for Q&A, and Google Calendar as the source of truth for slots. Sessions are capped at five minutes. Confirmations are spoken only after the booking API returns success.

It runs as its own Cloudflare Worker at voice.dilhara.deviitor.com and embeds back into this Astro portfolio via a cache-busted script tag — the floating “Ask from AI Assistant” pill on every page is the same product visitors can try live.

Goals & Success Criteria

“Done and good” meant four things: visitors can ask about services and projects by voice without a form; offered times come only from live freeBusy data, never from the model’s imagination; a booking is spoken as confirmed only after Google Calendar creates the event with Meet; and the whole UI drops onto this portfolio with a single script tag that matches the host theme.

Approach & Key Decisions

Markdown as the agent CMS. Behavior and knowledge live in content/*.md and compile into Deepgram agent settings — so prompt and FAQ changes ship without rewriting Worker code. The trade-off: a hard character budget on the compiled prompt, which forces ruthless editing instead of dumping every page of the site into context.

Strict calendar truthfulness over fluent improvisation. The model can call get_available_slots and create_booking, but it may only offer API-returned times and may only say “you’re booked” after create_booking returns ok: true. freeBusy is re-checked before insert so a race doesn’t double-book. That made the agent feel slightly more cautious; inventing a free slot would have been worse.

Client-owned tools for UI side effects. create_booking and end_call run in the browser so Meet and calendar links land as chat cards (never spoken as raw URLs) and hangup can drain TTS before disconnect. Deepgram cloud tools still handle slot lookup. The trade-off: more client logic, but booking cards that actually appear.

Script embed over iframe. An iframe would isolate styles and force a boxed demo. The IIFE embed injects a floating rail, remaps the portfolio’s CSS variables, and boots DeviitorVoice.init({ layout: 'floating', source: 'portfolio' }) — one line for any host site.

Challenges

Voice agents love to hallucinate logistics. Without hard tool gates, the model will invent a Thursday at 3 PM and apologize later. The fix was prompt discipline plus API truth: digit-friendly times, never speak IANA zones or URLs, and return slot_taken when the calendar disagrees.

Embedding across origins without a fragile module import. The portfolio cannot load a cross-origin ES module without CORS gymnastics, so the voice client is bundled into embed.js as an IIFE with cache-busting versions — one request, no import map.

Results & Impact

The agent is live at voice.dilhara.deviitor.com and embedded on dilhara.deviitor.com. Visitors get continuous voice Q&A, real Meet bookings with calendar links in chat, D1 session logging, and a public Appointment-schedule fallback when slots cannot be fetched. Try the floating assistant on this page — that is the product.

What I’d Do Differently

I would add a guided “book a call” shortcut earlier in the session UX. Open-ended voice is powerful, but first-time visitors sometimes need a visible path to booking before they discover they can just say it.