Skip to content
Learn Agentic AI10 min read0 views

Slot Filling in Conversational AI: Collecting Required Information Through Natural Dialog

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.

What Is Slot Filling?

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.

Defining Slots with Validation

Start by modeling each slot with its constraints, extraction logic, and confirmation behavior.

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 Slot Filling Engine

The engine iterates over unfilled slots, attempts extraction from each user message, and only prompts for slots that remain empty.

See AI Voice Agents Handle Real Calls

Book a free demo or calculate how much you can save with AI voice automation.

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?"

Multi-Turn Collection in Action

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.

FAQ

How does slot filling differ from form filling?

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.

What happens when the user changes a previously filled slot?

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.

How do you handle ambiguous slot values?

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

Share this article
C

CallSphere Team

Expert insights on AI voice agents and customer communication automation.

Try CallSphere AI Voice Agents

See how AI voice agents work for your industry. Live demo available -- no signup required.