Building Production AI Voice & RAG Intake Agents with Vapi, Retell, pgvector & n8n
Over the past two years I've built production AI systems for healthcare providers, real estate agencies, and service businesses — including Ellie, a 24/7 intake assistant for Achievement Behavior Services (ABS) in the US, and Riley, an AI voice scheduling agent. Here is what separates a fragile prompt demo from a production agent that books real appointments and syncs cleanly to a company's CRM.
Full-Stack Product Engineer · Full-Stack Engineer @ Callture
Key architectural takeaways (TL;DR)
- Static 20-field intake forms lose high-intent families and buyers after business hours; conversational AI agents capture and qualify leads 24/7 while writing structured records straight to the CRM.
- For voice agents (built with Vapi or Retell), keeping total turn-around latency under 700ms requires streaming STT/TTS and decoupling heavy CRM/calendar webhooks into async tool calls.
- For chat and intake agents (like Ellie for Achievement Behavior Services), hybrid `pgvector` retrieval + strict JSON schema extraction prevents hallucinated policy answers and guarantees clean CRM fields.
1. Why Conversational Intake Outperforms Static Forms
When parents seeking pediatric therapy at Achievement Behavior Services landed on the site at 10 PM, they previously faced a long multi-step form asking for insurance carrier details, child diagnosis status, and location preferences. Drop-off was high, and staff spent hours every morning chasing incomplete submissions.
We replaced the static friction with Ellie — an AI intake assistant that answers parent questions empathetically using verified clinic documentation (RAG) while naturally collecting the required intake fields one turn at a time. The moment a conversation reaches qualification, an n8n workflow writes the structured lead into Monday.com / CRM and triggers instant follow-up.
2. Grounding Answers with Hybrid pgvector Retrieval (Zero Hallucinated Policies)
In healthcare, real estate, or B2B sales, an AI assistant must never guess insurance coverage, pricing tiers, or service states. Pure vector similarity search occasionally misses exact acronyms or zip codes, while keyword search misses paraphrased questions.
Combining PostgreSQL `pgvector` cosine similarity (`<=>`) with full-text search (`tsvector`) inside a single SQL query gives the LLM the exact policy chunks in under 15ms — without introducing a separate vector database to manage.
SELECT
id,
title,
content,
1 - (embedding <=> $1::vector) AS semantic_score,
ts_rank_cd(search_tsv, plainto_tsquery('english', $2)) AS keyword_score
FROM knowledge_chunks
WHERE
tenant_id = $3
AND (1 - (embedding <=> $1::vector) > 0.72 OR search_tsv @@ plainto_tsquery('english', $2))
ORDER BY
(1 - (embedding <=> $1::vector)) * 0.75 +
ts_rank_cd(search_tsv, plainto_tsquery('english', $2)) * 0.25 DESC
LIMIT 5;3. Designing Sub-700ms AI Voice Agents with Vapi & Retell
On a phone call, any pause longer than ~1 second makes the caller say 'Hello? Are you still there?'. When building Riley (our 24/7 voice appointment scheduling agent), we optimized every stage of the voice pipeline:
| Pipeline Stage | Target Budget | Production Optimization |
|---|---|---|
| Endpointing & STT (Deepgram Nova-2) | 120ms – 180ms | Streaming WebSocket audio + tuned silence threshold |
| LLM First Token (GPT-4o-mini / Claude) | 220ms – 300ms | Compact system prompt + pre-fetched caller context |
| Tool Execution (Calendar / CRM Lookup) | < 250ms | Edge webhook + cached slot availability window |
| Streaming TTS Voice Synthesis | 150ms – 200ms | Chunked sentence streaming before full completion |
4. Deterministic Tool Calling & Idempotent CRM Sync via n8n
An AI agent is only useful if the data lands reliably in the team's system of record (HubSpot, GoHighLevel, Monday.com, Salesforce, or Odoo). Never let an LLM format raw API payloads directly without validation.
Instead, define strict JSON tool schemas (`check_availability`, `book_appointment`, `upsert_qualified_lead`), validate the arguments with Zod on your backend or n8n webhook, and upsert contacts idempotently keyed by normalized phone number or email.
- Always return a conversational filler ('Let me check the schedule for Tuesday afternoon…') before running external calendar lookups so the line never goes silent.
- Log every transcript, extracted JSON payload, and CRM webhook response code so operations teams can audit every call.
- Configure explicit human-handoff triggers (e.g., billing disputes, urgent clinical questions, or caller frustration) that transfer the live call or alert staff on Slack/WhatsApp.