Chapter 4 · Part 2
Mouth: text to speech
Every desktop operating system comes with a text-to-speech voice built in. It won't win awards, but it's free, works offline and starts instantly, which makes it ideal for building the loop. (Chapter 6 covers swapping in a more natural voice.)
One function, three platforms
Each OS has its own tool, so speak() picks the right one:
import platform
import subprocess
def speak(text):
system = platform.system()
if system == "Darwin": # macOS
subprocess.run(["say", text])
elif system == "Linux":
subprocess.run(["espeak-ng", text])
else: # Windows
import pyttsx3
engine = pyttsx3.init()
engine.say(text)
engine.runAndWait()- macOS has the
saycommand. Trysay helloin a terminal. - Linux uses
espeak-ng, which you installed in chapter 1. It sounds robotic, but it's reliable. - Windows uses
pyttsx3, a Python wrapper around the built-in Windows voices.
subprocess.run waits until the speech has finished before it returns, so the agent won't start
listening again while it's still talking. We'll rely on that.
pyttsx3 claims to work on every platform, and it's tempting to use it for all three. But when
we tested it on macOS, it spoke the first reply and then went silent: every later
runAndWait() produced empty audio. The say command doesn't have that problem, so macOS gets
say.
Close the loop
You now have all three parts. The main loop is five steps: listen, check for a goodbye, think, speak, repeat.
if __name__ == "__main__":
print("Voice agent ready. Say 'goodbye' to quit.")
while True:
heard = listen()
if not heard:
continue
print(f"You: {heard}")
if "goodbye" in heard.lower():
speak("Goodbye!")
break
answer = think(heard)
print(f"Claude: {answer}")
speak(answer)Run it and have a conversation:
Voice agent ready. Say 'goodbye' to quit.
Press Enter, then speak...
Listening... press Enter when you're done.
You: What's the tallest mountain in Japan?
Claude: That's Mount Fuji, at about three thousand seven hundred seventy-six meters.
Press Enter, then speak...
Listening... press Enter when you're done.
You: When did it last erupt?
Claude: Mount Fuji last erupted in seventeen oh seven, in what's called the Hoei eruption.Notice the second question: "it" works because the history carries the first turn. You've built a voice agent. Ears, brain and mouth, in under a hundred lines of Python.
But listen to the pause
Use it for a minute and one thing stands out. After you press Enter there's a long silence before it speaks, and the silence grows with longer answers. That's because the code waits for Claude's entire reply before saying a single word.
A person doesn't work like that. They start talking once they know how the sentence begins. Next chapter, so will your agent.