By Sagar Shankaran, Founder of CallSphere
Learn how to implement slot filling patterns in conversational AI agents that collect required information through natural, multi-turn dialog instead of rigid form-like interactions.
Key takeaways
Slot filling is a dialog management pattern where an AI agent identifies pieces of information (slots) it needs to complete a task and collects them through natural conversation. Rather than presenting users with a rigid form, the agent extracts values from free-form utterances and asks follow-up questions only for missing pieces.
Consider a restaurant booking agent. It needs a date, time, party size, and optionally a seating preference. A user might say "Book a table for four this Friday" — providing party size and date in a single utterance. The agent should extract both and only ask about the missing time slot.
Start by modeling each slot with its constraints, extraction logic, and confirmation behavior.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
flowchart LR
INPUT(["User intent"])
PARSE["Parse plus<br/>classify"]
PLAN["Plan and tool<br/>selection"]
AGENT["Agent loop<br/>LLM plus tools"]
GUARD{"Guardrails<br/>and policy"}
EXEC["Execute and<br/>verify result"]
OBS[("Trace and metrics")]
OUT(["Outcome plus<br/>next action"])
INPUT --> PARSE --> PLAN --> AGENT --> GUARD
GUARD -->|Pass| EXEC --> OUT
GUARD -->|Fail| AGENT
AGENT --> OBS
style AGENT fill:#4f46e5,stroke:#4338ca,color:#fff
style GUARD fill:#f59e0b,stroke:#d97706,color:#1f2937
style OBS fill:#ede9fe,stroke:#7c3aed,color:#1e1b4b
style OUT fill:#059669,stroke:#047857,color:#fff
from dataclasses import dataclass, field
from typing import Any, Callable, Optional
import re
from datetime import datetime, timedelta
@dataclass
class Slot:
name: str
prompt: str
required: bool = True
value: Any = None
confirmed: bool = False
validator: Optional[Callable] = None
extractor: Optional[Callable] = None
def is_filled(self) -> bool:
return self.value is not None
def validate(self) -> bool:
if self.validator and self.value is not None:
return self.validator(self.value)
return True
def extract_party_size(text: str) -> Optional[int]:
patterns = [
r"for (d+)",
r"(d+) people",
r"(d+) guests",
r"party of (d+)",
]
for pattern in patterns:
match = re.search(pattern, text, re.IGNORECASE)
if match:
return int(match.group(1))
return None
def extract_date(text: str) -> Optional[str]:
today = datetime.now()
text_lower = text.lower()
if "today" in text_lower:
return today.strftime("%Y-%m-%d")
if "tomorrow" in text_lower:
return (today + timedelta(days=1)).strftime("%Y-%m-%d")
days = ["monday","tuesday","wednesday","thursday","friday","saturday","sunday"]
for i, day in enumerate(days):
if day in text_lower:
current_day = today.weekday()
diff = (i - current_day) % 7
if diff == 0:
diff = 7
target = today + timedelta(days=diff)
return target.strftime("%Y-%m-%d")
return None
Each slot carries its own extraction function so the agent can pull values from any user utterance, not just direct answers to prompts.
The engine iterates over unfilled slots, attempts extraction from each user message, and only prompts for slots that remain empty.
class SlotFillingEngine:
def __init__(self, slots: list[Slot]):
self.slots = {s.name: s for s in slots}
self.conversation_history: list[dict] = []
def extract_all(self, user_message: str):
"""Try to fill every empty slot from the user message."""
for slot in self.slots.values():
if not slot.is_filled() and slot.extractor:
value = slot.extractor(user_message)
if value is not None:
slot.value = value
def get_next_unfilled(self) -> Optional[Slot]:
for slot in self.slots.values():
if slot.required and not slot.is_filled():
return slot
return None
def all_required_filled(self) -> bool:
return all(
s.is_filled() for s in self.slots.values() if s.required
)
def process_message(self, user_message: str) -> str:
self.conversation_history.append(
{"role": "user", "content": user_message}
)
self.extract_all(user_message)
# Validate filled slots
for slot in self.slots.values():
if slot.is_filled() and not slot.validate():
name = slot.name
slot.value = None
response = f"The {name} you provided is not valid. {slot.prompt}"
self.conversation_history.append(
{"role": "assistant", "content": response}
)
return response
if self.all_required_filled():
return self._build_confirmation()
next_slot = self.get_next_unfilled()
if next_slot:
self.conversation_history.append(
{"role": "assistant", "content": next_slot.prompt}
)
return next_slot.prompt
return self._build_confirmation()
def _build_confirmation(self) -> str:
filled = {
name: slot.value
for name, slot in self.slots.items()
if slot.is_filled()
}
details = ", ".join(f"{k}: {v}" for k, v in filled.items())
return f"I have everything. Confirming: {details}. Shall I proceed?"
engine = SlotFillingEngine([
Slot("date", "What date would you like?", extractor=extract_date),
Slot("party_size", "How many guests?", extractor=extract_party_size),
Slot("time", "What time works for you?", extractor=lambda t:
re.search(r"(d{1,2}(?::d{2})?s*(?:am|pm))", t, re.I)
and re.search(r"(d{1,2}(?::d{2})?s*(?:am|pm))", t, re.I).group(1)
),
])
print(engine.process_message("Table for 4 this Friday"))
# Extracts date=Friday, party_size=4, asks for time
print(engine.process_message("7pm"))
# Extracts time=7pm, confirms all slots
The key insight is that extraction runs against every unfilled slot on every message, so users can volunteer information in any order and the agent adapts.
Form filling presents fields in a fixed order and expects one answer per turn. Slot filling extracts multiple values from free-form text, handles any ordering, and only asks about genuinely missing information. This makes conversations feel natural rather than scripted.
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
The engine should detect override intent — phrases like "actually make it 6 people" — and update the corresponding slot value. Add an override extraction pass that checks already-filled slots for new values when the user expresses correction intent.
When an extractor returns multiple possible values, present them as disambiguation options rather than guessing. For example, if "next week" could mean any day, ask "Which day next week?" to narrow down the value before filling the slot.
#SlotFilling #ConversationalAI #DialogManagement #NLU #Python #AgenticAI #LearnAI #AIEngineering
Prototyping voice AI for a real business? If you would rather skip the telephony plumbing, CallSphere's AI phone answering service runs production-grade voice agents that answer, book, and take payments 24/7 — live in 24 hours with a free 7-day pilot.

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
A founder's guide to page chat: web page chat box options, best live chat for small business, and how CallSphere ships an embed in 5 minutes.
A founder's guide to building a chatbot for answering questions on your website: RAG, voice, and how CallSphere ships one in 3-5 days.
Good messaging apps in 2026 ranked by a founder running 6 AI voice agents. Signal, iMessage, WhatsApp, Telegram, and where AI fits.
Group chat apps in 2026 ranked by a founder running a 14-tool AI platform. Slack, Discord, Teams, Telegram, and where AI voice chat fits.
Create a chat bot in 2026 means LLM-backed agents, not decision trees. Here is the working guide: platforms, build steps, and what actually matters.
Best chat software in 2026: a founder running 6 AI agents ranks website chat tools, live chat, and AI chat platforms. Real prices, real picks.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco