AboutExpertiseProjectsJourneyBlogContact
Discuss
AboutExpertiseProjectsJourneyBlogContactDiscuss

Yao David Logan

Software Engineer fullstack specialized in SaaS, business automation and scalable web/mobile platforms.

NavigationExpertiseProjectsJourneyBlogContact
LinksGitHubLinkedInEmail
© 2026 Yao David Logan. All rights reserved.
Autonomous LLM agents on the phone — Vapi + SIP + GPT-4 field notes
Back to blog

Tech

Xin@

Autonomous LLM agents on the phone — Vapi + SIP + GPT-4 field notes

YDLYao David Logan17 min readSeptember 18, 2026

CategoryTech

Read17 min read

PublishedSeptember 18, 2026

Views0

Shares0

Comments0

Contents
  1. 011. The context
  2. 022. Why it's tempting and tricky
  3. 033. Target architecture
  4. 044. Pattern 1 — Stream everywhere
  5. 055. Pattern 2 — The system prompt as a checklist
  6. 066. Pattern 3 — Barge-in detection
  7. 077. Pattern 4 — Function calling for real-world actions
  8. 088. Pattern 5 — Compliance from "hello"
  9. 099. Pattern 6 — Human handover
  10. 1010. Metrics and quality
  11. 1111. Pitfalls to avoid
  12. 1212. Economics
  13. 1313. Closing

Stream everywhere, function calling for truth, barge-in, compliant opt-out, human handover: the six patterns that turn an LLM voice agent into a production tool.

Wiring an LLM agent to a phone line via Vapi + SIP (IllyVoIP, Vonage) is technically appealing. In production, it's a minefield. Field notes.

1. The context

On an agency mission, the client wanted to automate outbound qualification calls (B2B, appointment booking, follow-up). Specs: natural-feeling voice agent, CRM integration, human operators taking over on demand, legal compliance (consent, opt-out).

Final stack: Vapi for voice orchestration + SIP (IllyVoIP backup, Vonage primary) for PSTN connectivity, OpenAI GPT-4 for conversational logic, N8N for the CRM pipeline, PostgreSQL for logs.

The goal of this article: share what worked, what blew up in flight, and how I'd restart today.

2. Why it's tempting and tricky

The LLM voice agent ticks every hype box: automation, generative AI, scale, marketing ROI. But under the hood, it's the integration of five asynchronous systems talking in real time:

  • LLM (OpenAI/Anthropic): 800-2000 ms latency, sometimes more
  • TTS (Vapi/ElevenLabs): 200-500 ms latency per sentence
  • STT (Deepgram/Whisper): 100-300 ms streaming latency
  • SIP (IllyVoIP/Vonage): 50-150 ms per RTP packet
  • CRM (HubSpot/Pipedrive): 200-1000 ms

At 3 seconds cumulative latency, the agent feels "slow and lost". At 1.2 seconds, "robot but OK". Under 800 ms, "almost human". Latency optimization is the project, not a detail.

3. Target architecture

Plain Text
1[User] ← PSTN/SIP → [IllyVoIP / Vonage]
2 │
3 ▼
4 [Vapi]
5 ╱ │ ╲
6 [Deepgram] │ [ElevenLabs]
7 STT │ TTS
8 ▼
9 [Agent API]
10 Node.js
11 │
12 ┌──────────┼──────────┐
13 ▼ ▼ ▼
14 [OpenAI] [Postgres] [N8N]
15 reasoning logs CRM

Vapi orchestrates the audio loop (STT → agent → TTS) with bidirectional streaming. Agent API (my code) receives transcribed text, calls OpenAI, returns the response to speak. Postgres traces everything. N8N integrates with the CRM.

4. Pattern 1 — Stream everywhere

Absolute rule: never wait for a step to finish before starting the next.

TypeScript
1// src/agent/handleTurn.ts
2export async function handleTurn(userText: string, context: TurnContext) {
3 const stream = await openai.chat.completions.create({
4 model: "gpt-4o",
5 messages: [...context.history, { role: "user", content: userText }],
6 stream: true,
7 temperature: 0.4,
8 });
9 
10 let buffer = "";
11 for await (const chunk of stream) {
12 const delta = chunk.choices[0]?.delta?.content ?? "";
13 buffer += delta;
14 
15 const sentenceEnd = buffer.search(/[.!?]\s/);
16 if (sentenceEnd > 0) {
17 const sentence = buffer.slice(0, sentenceEnd + 1);
18 buffer = buffer.slice(sentenceEnd + 2);
19 await vapi.speak(sentence);
20 }
21 }
22 
23 if (buffer.trim()) await vapi.speak(buffer);
24}

Measured benefit: perceived latency divided by 3. Instead of waiting 2 seconds of generation then 1 second of TTS, the user hears the first sentence after 700 ms.

5. Pattern 2 — The system prompt as a checklist

A voice agent isn't a text chatbot. Three specific requirements:

  1. Short sentences — no bulleted lists (impossible to say), no long paragraphs
  2. One question per turn — otherwise the user forgets
  3. Explicit confirmation — the user doesn't see text, you must echo critical info
Plain Text
1You are Sarah, a sales assistant at Acme Corp.
2 
3Mandatory rules:
4- Short sentences (max 15 words)
5- One question per turn, never more
6- Echo aloud names, emails, dates and numbers for confirmation
7- If you didn't catch something, say "I didn't catch that, could you repeat?"
8- Never invent information
9 
10Call objective:
11- Qualify the prospect (budget, need, deadline)
12- Propose an appointment within next week
13- Capture email and best slot
14 
15If the prospect refuses: thank politely and offer a definitive opt-out.

Rule order matters. GPT-4 and Claude apply constraints stated first more reliably.

6. Pattern 3 — Barge-in detection

The user will interrupt the agent. Normal and healthy. The agent must stop immediately, not keep talking 3 seconds over the user.

Vapi exposes a barge-in event when STT detects the user starts speaking. Agent side:

TypeScript
1vapi.on("barge-in", () => {
2 vapi.stopSpeaking();
3 currentTurn?.abort();
4 vapi.resumeListening();
5});

Without this, the agent feels "deaf" and the experience becomes frustrating. It's the detail that moves "weird robot" to "acceptable agent".

7. Pattern 4 — Function calling for real-world actions

The agent must NEVER invent a phone number, an email, an availability. Anything touching reality goes through structured function calling.

TypeScript
1const tools = [
2 {
3 type: "function",
4 function: {
5 name: "checkAvailability",
6 description: "Check available slots in the calendar",
7 parameters: {
8 type: "object",
9 properties: {
10 startDate: { type: "string", format: "date" },
11 endDate: { type: "string", format: "date" },
12 durationMin: { type: "number" },
13 },
14 required: ["startDate", "endDate", "durationMin"],
15 },
16 },
17 },
18 {
19 type: "function",
20 function: {
21 name: "bookAppointment",
22 description: "Book an appointment (call only after user confirmation)",
23 parameters: {
24 type: "object",
25 properties: {
26 email: { type: "string", format: "email" },
27 name: { type: "string" },
28 slotIso: { type: "string", format: "date-time" },
29 },
30 required: ["email", "name", "slotIso"],
31 },
32 },
33 },
34];

When GPT-4 decides to call checkAvailability, my code actually queries the sales rep's Google Calendar, returns slots, GPT-4 phrases the answer. No hallucination possible.

Important: bookAppointment callable only after explicit confirmation ("I confirm your appointment Tuesday 2 PM, correct?" → "yes"). The system prompt enforces this discipline.

8. Pattern 5 — Compliance from "hello"

Three legal requirements in France/EU for automated outbound calls:

  1. Information: "This call may be recorded and processed by an automated system"
  2. Bloctel/GDPR consent: verify the number isn't on do-not-call list before calling
  3. Explicit opt-out: on "don't call me again", opt-out is instant and logged
TypeScript
1const OPT_OUT_PHRASES = [
2 "don't call me again",
3 "stop contacting me",
4 "remove me",
5 "gdpr",
6 "opt out",
7];
8 
9export function detectOptOut(userText: string): boolean {
10 const normalized = userText.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
11 return OPT_OUT_PHRASES.some((phrase) => normalized.includes(phrase));
12}
13 
14if (detectOptOut(userText)) {
15 await vapi.speak("Understood, I'm noting your request. You won't receive calls from us anymore. Have a good day.");
16 await OptOut.create({ phone: context.phone, reason: "user_request", at: new Date() });
17 await vapi.endCall();
18 return;
19}

Opt-out is non-negotiable. No "are you sure?", no "can I offer something else?". Close, log, respect.

9. Pattern 6 — Human handover

No LLM agent replaces a human for sensitive cases. Detect when to transfer:

  • Explicit request ("I want to speak to a human")
  • Stalling conversation (3 misunderstandings in a row)
  • Out-of-scope topic (complaint, dispute, emergency)
  • Detected negative emotion (sentiment analysis on transcribed text)
TypeScript
1async function transferToHuman(reason: string, context: TurnContext) {
2 await vapi.speak("I'll connect you with a counselor, please hold.");
3 await Transfer.create({ callId: context.callId, reason, at: new Date() });
4 await vapi.transferCall({ destination: HUMAN_QUEUE_NUMBER });
5}

The agent must know "I can't do this" without shame, and hand off fast.

10. Metrics and quality

Trace each call on three axes:

MetricDefinitionAcceptable threshold
p95 latency per turnUser-stops-speaking → agent-starts< 1200 ms
Barge-in rate% calls with ≥1 interruptStable around 20-30%
Opt-out rate% calls ending in opt-out< 5%
Handover rate% calls transferred to human< 15%
Completion rate% calls reaching objective> 30%

If latency drifts, experience explodes in two days. Monitor continuously.

11. Pitfalls to avoid

PitfallSymptomFix
TTS without streamingUnbearable perceived latencySentence-by-sentence streaming
No barge-inAgent talks over userbarge-in listener + stopSpeaking
Date/email hallucinationWrong appointment booked, fake emailFunction calling for all real facts
Fuzzy opt-outLegal riskLexical detection + log + end call
No handoverGuaranteed frustrationMulti-signal detection + transfer
Over-long system promptModel hallucinates moreMax 800 tokens, ordered rules
No do-not-call checkLegal riskSystematic check before each call

12. Economics

Measured cost per call (B2B qualification, 3 min average):

  • Vapi (orchestration): ~€0.15
  • IllyVoIP/Vonage (SIP): ~€0.02
  • OpenAI GPT-4o: ~€0.05
  • ElevenLabs TTS: ~€0.10
  • Deepgram STT: ~€0.02
  • Total: ~€0.34 per call

Vs a human sales rep: ~€3 to €5 per call (loaded salary + overhead). Real savings, but assume substantial call volume — under 200 calls/day, human stays better on relationship quality.

13. Closing

The LLM voice agent isn't a gadget. It's a professional tool when you respect three principles: stream everywhere, function calling for truth, legal compliance by default. Without these three, it's a demo that doesn't hold in production.

Nor is the agent a universal replacement for human sales. It excels at massive standardized qualification. It fails on nuance, negotiation and emotion. Hybrid human + agent stays the best economic calculation.

If starting: Vapi + GPT-4o + Deepgram + ElevenLabs is the stack that worked best for me in 2026. But count 6 to 10 weeks to ship a truly production-ready agent, not the weekend sprint sold on LinkedIn.

Comments

Reader reactions

No comment yet

No spam — email is only used to verify your identity.

·

Be the first to share your reaction.

←PreviousClean code and technical debt — nine months teaching fullstackRetour d'experience · 15 min readNextNext.js 14 App Router in production — lessons after 6 months on NexusRetour d'experience · 16 min read→
Next conversation

Turn this reading into a product decision.

If this topic feels close to a real product problem, I can help on diagnosis, architecture, backend, interface and automations that make a platform usable in production.

Format
Full-time, freelance, long mission
Focus
SaaS, API, back-office, automation
Discuss the topicDownload CV

Newsletter

Get the next technical notes.

A short selection on SaaS, backend architecture, business automation and product quality. No noise, only applicable ideas.