Pick a depth. Each prompt opens in your AI pre-loaded with the lesson. Click a row to preview the prompt.
A voice agent is a real-time system, and real-time systems that aren't modeled explicitly as state machines tend to grow invisible bugs — a stray flag here, a race condition there — that only show up under real network jitter, not in your quiet local test. Naming the states (idle, listening, transcribing, thinking, speaking, interrupted) and the legal transitions between them turns 'why did the agent say something after the user already started talking' from a mystery into a one-line diff: you allowed a transition you shouldn't have. This is the same discipline that makes protocol implementations (TCP, TLS handshakes) reliable, applied to conversation instead of packets. Build this explicitly now, in code you can read in thirty seconds, and every later module's turn-taking and interruption work has somewhere solid to attach.
This extends the earlier sketch into a small class with guarded transitions — illegal transitions raise instead of silently doing nothing, which is exactly the property that catches bugs early.
from enum import Enum, auto
class State(Enum):
IDLE = auto()
LISTENING = auto()
THINKING = auto()
SPEAKING = auto()
TRANSITIONS = {
(State.IDLE, "call_started"): State.LISTENING,
(State.LISTENING, "endpoint_detected"): State.THINKING,
(State.THINKING, "reply_ready"): State.SPEAKING,
(State.SPEAKING, "reply_finished"): State.LISTENING,
(State.SPEAKING, "user_barge_in"): State.LISTENING,
(State.LISTENING, "call_ended"): State.IDLE,
}
class VoiceCallFSM:
def __init__(self):
self.state = State.IDLE
def fire(self, event: str) -> State:
key = (self.state, event)
if key not in TRANSITIONS:
raise ValueError(f"illegal transition: {event} from {self.state.name}")
self.state = TRANSITIONS[key]
return self.state
fsm = VoiceCallFSM()
for event in ["call_started", "endpoint_detected", "reply_ready", "reply_finished"]:
print(f"{event:<20} -> {fsm.fire(event).name}")
try:
fsm.fire("reply_ready") # illegal: we're LISTENING, not THINKING
except ValueError as e:
print(f"caught: {e}")python3 main.py