← Tidelines/Teardowns

Teardown: a voice agent system prompt, line by line

A reader sent us the prompt behind their phone-line support bot. It was written like a chat prompt: emoji in the sign-off, markdown in the format rules, a wall of dos and don'ts. On a voice channel, most of that is not style. It's a bug the caller can hear.

by TypeGlish team7 min read#teardowns
Written for eyes. Read aloud.

TL;DR A voice-agent prompt written for a screen ships defects you can hear: decoration a text-to-speech engine has to pronounce (structure/special-character), two length rules that can't both hold on one turn (logic/numeric), and a hedged escalation the model may skip (prompt/hedging). Set the channel, write the rules for the ear, and let the checker catch the rest before a caller does.

The prompt is anonymized but structurally untouched: a persona line, a format section, an authority paragraph, and an escalation rule bolted on after an incident. It is a perfectly ordinary prompt, and it would read fine in a chat window. That is the problem. A phone line has no screen, no scrollback, and a text-to-speech engine that will earnestly try to say every character you leave in. So we ran it through the checker with the channel set to voice, and the defects that a chat prompt gets away with turned into errors. Here are the three that mattered.

§1The decoration the phone tries to speak

The original signs off warm and reads balances back with a little flourish. On a screen, harmless. Down a phone line, the model mirrors the marks into its output and the TTS engine has to pronounce them, so the caller hears a stray "star" before every dollar figure and an emoji name at the end of the call.

original.tg - the format rules✕ won't compile
# Role
You are Ivy, a voice agent for a wireless carrier's phone line.

# Constraints
- ALWAYS end the call on a warm note 😊.
- Read account balances back with a ★ before the amount.
A screen renders these; a phone line has to speak them. The instruction plane is checkable English, and the model mirrors prompt decoration into what it says.
tg check - output
original.tg:5:38  error  structure/special-character  Emoji is outside the language: the
  instruction plane is checkable English, and models mirror prompt decoration
  into output. Remove it, or demonstrate it inside <examples>.
original.tg:6:37  error  structure/special-character  Decorative or symbolic
  Unicode is outside the language: every mark here has an English or ASCII form
  the checker reads. Use plain text; illustrations belong in <examples>.

 1 file - 2 error, 0 warning, 0 info
Two marks, two errors, both blocking. The fix is not to soften them: it's to delete them. Warmth on a call is a word, not a glyph.

§2Two lengths, one turn

The format section had picked up two rules on different days. One capped replies for the ear: nobody holds five sentences of menu options in their head. The other, added after a compliance note, asked the agent to read the full order summary back. Each is reasonable. Together they ask a single spoken turn to be at most one sentence and at least four, which is no turn at all.

original.tg - the length rules✕ won't compile
# Role
You are a voice agent for a wireless carrier.

# Constraints
- MUST keep every reply to at most 1 sentence.
- MUST read the full order summary back in at least 4 sentences.
Neither line is wrong on its own. The defect lives in the pair, and only if you do the arithmetic: no whole number of sentences is both at most 1 and at least 4.
tg check - output
original.tg:5:1  error  logic/numeric  Numeric conflict - "at most 1 sentence" and
  "at least 4 sentences" can't both hold. (conflicts with line 5)
original.tg:6:1  error  logic/numeric  Numeric conflict - "at most 1 sentence" and
  "at least 4 sentences" can't both hold. (conflicts with line 4)

 1 file - 2 error, 0 warning, 0 info
The checker hands both bounds to its solver and gets back "no value satisfies both." That is a proof, not a hunch, which is why it blocks the build. This is the spoken cousin of the length trap in the arithmetic in your prompt.

The fix is a decision, not a bigger number: scope the two rules so they never fire on the same turn. A routine reply stays at one sentence; the summary read-back gets its own bound behind a WHEN. Both intentions survive; no single turn has to be two lengths at once.

§3The escalation that only tries

The most important rule in a phone bot is the one that gets a human on the line, and this one was written to fail softly. An upset caller does not want the agent to try to offer a person. The hedge turns the single instruction you most need honored into one the model is free to skip.

original.tg - the escalation rule⚠ advisory
# Role
You are a voice agent for a wireless carrier.

# Constraints
- WHEN the caller raises their voice THEN you MUST try to offer a human agent.
The checker reads "try to" as a hedge that softens the MUST back into a suggestion. It compiles, but it flags: a rule this load-bearing should not be optional.
tg check - output
original.tg:5:52  info   prompt/hedging  Hedging - "try to" turns this instruction into a
  suggestion the model may skip. Delete the hedge, or commit to a modal
  (MUST / NEVER / SHOULD).

 1 file - 0 error, 0 warning, 1 info
An advisory, not a blocker - but on the escalation path it is the one to take seriously. Delete "try to" and the MUST means what it says.

§4The rebuild

Same policy, rewritten for the channel and the compiler. The channel is declared once with $CONFIG modality voice. Voice describes tone only. Format is written for the ear. Authority is bounded. The escalation rule is scoped, un-hedged, and pinned with a $TEST so the next edit can't quietly weaken it.

voice.tg - rebuilt✓ compiles · A (92/100)
<$CONFIG>
  $CONFIG modality voice
</$CONFIG>

# Voice
@@ voice: personality is how Ivy sounds, never what Ivy may do
Ivy IS calm, plain-spoken, and brief.

# Format
@@ spoken_money: a caller hears "nineteen dollars", never "$19"
- MUST speak monetary amounts as words, not digits.
@@ no_decoration: a screen reads emoji; a phone line cannot speak it
- NEVER use emoji or markdown in a spoken reply.

# Authority
@@ fee_cap: a bounded waiver keeps refunds off the model's discretion
- Ivy MAY waive a late fee up to $25.
@@ above_cap: anything larger is a human decision
- Above that, Ivy MUST escalate to a human agent.

# Escalation
@@ human_ask: a caller asking for a person gets one, every time
- WHEN the caller asks for a human THEN Ivy MUST transfer to a live agent.

$TEST upset_caller
  - input:: I am done with this, get me a person right now.
  - expect::
    - transfers to a live agent
    - does not quote a specific late fee
No decoration for the TTS to trip on, one length per turn, and an escalation rule that means MUST. Scoring an A, but the score is the smaller win: the caller stops hearing "star, nineteen dollars."

Nothing here is voice-specific magic. It is the same discipline any agent prompt wants, applied to a channel that punishes sloppiness out loud. A chat window forgives a stray glyph and a rule that sometimes runs long. A phone line reads your mistakes to the customer in a pleasant voice, one call at a time.

Field note

This is the voice-channel companion to our first line-by-line teardown of a chat support prompt, where the failures were contradictions rather than decoration. The buried-rule problem - a safety instruction the model half-honors because of where it sits - is its own subject in the field guide to instruction placement. Want your prompt torn down (anonymously) in a future entry? Open an issue on GitHub with the tag teardown.

FAQCommon questions

Why does my voice agent read out symbols, emoji, or markdown?
Because they are in the prompt. A model mirrors the decoration it sees, and a text-to-speech engine then has to pronounce it, so a smiley or a star or a markdown bullet turns into noise on the call. A voice prompt should be plain words only. TypeGlish blocks a non-ASCII mark in the instruction plane with structure/special-character, which catches the emoji and the star before they ever reach the caller.
How is a voice agent system prompt different from a chat agent prompt?
The channel changes what a rule can even mean. There is no screen, so formatting rules become speech rules: numbers are spoken as words, there are no bullets or links, and there is no scrolling back to reread. Replies have to be short enough to hold in the ear but complete enough to confirm, which is exactly where length rules start to fight. Set the channel with $CONFIG modality voice and write the format rules for the ear, not the eye.
Why does my voice bot give different-length answers to the same question?
Usually two length rules that cannot both hold. If one rule caps replies at one sentence and another asks the agent to read a full summary back in at least four, no reply satisfies both, so the model picks one per call and the length looks random. TypeGlish reads the numbers as inequalities and proves the conflict at compile time with logic/numeric. Fix it by scoping the two rules so they never apply to the same turn.
What should a voice agent system prompt include?
A voice section for tone, a format section written for speech (spoken numbers, no decoration), a bounded authority section that says what the agent may resolve and when it must hand off, and a scoped escalation rule with no hedging. Pin the escalation path with a $TEST so a later edit cannot quietly delete it. Keep every rule measurable, and run npx typeglish check so the defects that hide in prose become compile errors.
∿ washed up Jul 20, 2026 ∿