Long Lists Over Voice

8 min read

V25
Playbook · Voice & Realtime Agents

Twelve results and one ear: the list is a monologue the caller cannot replay.

Your retrieval works, your transcription is clean, and the call still dies at the moment the agent starts reading the fourth of eight options — because speech has no scrollback and a caller holds about four chunks of anything. The repair is not a shorter list or a better summary: a tool that returns a ranked result set has handed the agent something it cannot speak, so the tool has to return an answer or a question instead, and "too many matches" has to be an outcome rather than a payload.

Step 1Do the arithmetic before you write the prompt.

Natural conversational speech runs around 150 words a minute — roughly 2.5 words a second. An option a caller can actually tell apart needs a name and at least one attribute (a time, a price, a branch, a date), which is eight to twelve words once you add the connective tissue. Six options is therefore fifty to seventy words, or twenty to thirty seconds of uninterrupted agent speech, in a medium where the 800 milliseconds you are fighting for already reads as a pause.

Then the harder number. Cowan's 2001 review in Behavioral and Brain Sciences put the focus of attention at about four chunks for normal adults — a deliberate counterpoint to Miller's seven, contested since, and a laboratory figure rather than a phone-call figure. Treat it as the right order of magnitude rather than a constant and the arithmetic still lands in the same place: one spoken option is two or three chunks, so the caller can hold the option they are hearing and perhaps the one before it. Everything earlier in your list is gone, and it was gone before you stopped speaking.

# what the caller retains from a six-item spoken list
items 1-3   overwritten
item  4     a fragment, maybe the price
item  5     held
item  6     held, and now the default answer

# the recency effect is not a preference signal.
# your conversion data will read it as one.

That last line is where the bug hides. A caller who picks the last option they heard looks identical in your analytics to a caller who chose it. Enumeration failures do not surface as errors — they surface as a suspiciously strong preference for whatever your ranker happened to put at the end.

Step 2The defect is in the tool signature, not the prompt.

The standard fix is an instruction: never read more than three options. It does not hold, and the reason is worth understanding because it is the same reason in every stack. The tool returned ten rows. The model is now holding ten rows and an instruction to speak three. Nothing in the result says which three, so the model picks — by position, usually, which means your ranker's tie-breaking has quietly become your product's recommendation engine, uninstrumented and unreviewed. You did not shorten the list. You moved the truncation into the least observable component you own.

A voice tool that can return a result set is mis-specified. The caller cannot consume a set, so something must collapse it, and that something should be the tool — where you can log the decision, test it, and change it without a prompt deploy. This is the voice-specific case of the general argument in shaping tool results: the result shape should match what the consumer can use, not what your query happened to produce.

# mis-specified: the caller cannot hear this, so the model improvises
find_appointments(...) -> { results: [ {...} x10 ] }

# speakable: three outcomes, each one a sentence, each one loggable
find_appointments(...) -> one of
  { kind: "answer",       slot: {...}, alternatives_held: 9 }
  { kind: "discriminate", axis: "part_of_day",
                            split: { morning: 4, afternoon: 6 } }
  { kind: "too_many",     count: 61, best_axis: "branch" }

The third outcome is the one teams leave out and the one that pays. A query matching sixty-one rows is not a list problem, it is a signal that the agent asked the wrong question, and the honest response is a question back — not sixty-one rows trimmed to three by whatever comes first alphabetically.

Step 3Ask the discriminating question instead of reading the set.

When the tool returns discriminate, the agent's turn is one sentence containing one axis. The axis to pick is the one that splits the candidate set most evenly, because that is the question that removes the most options per second of speech — the same logic a binary search uses, applied to a channel billed in seconds.

  • Compute the split, do not guess it. Six morning and four afternoon slots is a good question. Nine morning and one afternoon is a bad one: you spent a turn to eliminate a tenth of the set. Have the tool return the counts and pick the axis from them.
  • One axis per turn. "Morning or afternoon, and which branch?" is two questions, and the caller will answer one — usually the second, and you will not know which one they meant.
  • Name the two ends, never "or other". An open third option re-opens the set you just halved.
  • Carry the count, quietly. "I have ten of those — morning or afternoon?" tells the caller why they are being asked and buys you the next question for free. It does not tell them all ten.

Two or three of these questions resolve a sixty-item set, and each one costs about two seconds of speech against the twenty-five seconds enumeration would have cost. The counter-argument you will hear is that interrogation feels worse than a list. It does not: a list that the caller cannot hold feels like being read a menu by someone who is not listening. See clarifying questions for when the same move is wrong — it is wrong when you could have inferred the answer, and a candidate count is not an inference.

Step 4When you must speak options, lead with the discriminator.

Sometimes three really is the answer — three flights, three appointment windows, three plans. Then the ordering inside each utterance decides whether the caller can use it. Put the attribute that distinguishes the options first and the shared noun last, because the caller is deciding whether to keep listening during the first two words.

bad  "There's a Dr Okonkwo appointment on Tuesday at 9:40,
      a Dr Okonkwo appointment on Tuesday at 2:15, and..."
      # the first six words of each are identical; by the time the
      # distinguishing part arrives the caller has stopped parsing

good "Tuesday morning, Tuesday afternoon, or Thursday morning.
      All with Dr Okonkwo."
      # the discriminator is the whole utterance; the shared part
      # is said once, at the end, where it costs nothing
  • Factor out everything shared. If all three options share the clinician, the branch and the price, say those once. The list is only the axis that varies.
  • Three maximum, and mean it. If the fourth matters, it belongs behind a discriminate turn, not behind an "and also".
  • Prosody is load-bearing. Three options with no pause between them is one long noun phrase. Insert real breaks — most TTS engines will give you a sentence-final pause if you give them sentence-final punctuation, which is cheaper and more reliable than a markup tag.
  • Expect the barge-in and want it. A caller who interrupts on option two has chosen option two. Your turn manager has to attribute that interruption to the option that was actually audible when it happened — which is the one it had finished speaking, not the one the model had generated.

Step 5Never speak an index.

"Press 2" worked for thirty years because DTMF is a separate channel with a tiny alphabet and no ambiguity. "Say two" is not the same move, and it fails in three ways that compound.

  • The index refers to a list that may no longer exist. If anything re-queries — a retry, a barge-in that rewinds, a tool call that re-ranks — position two now means a different row. There is no stable identifier in "the second one", and you will not detect the mismatch, because both are valid selections.
  • Small numbers are acoustically poor. "Two" against "to" and "too" is a language-model problem your recognizer solves by context, and a bare numeral has no context. The failure modes in alphanumerics over voice apply here in miniature.
  • It asks the caller to maintain your data structure. They now have to remember a mapping as well as the options, which is the chunk budget you were trying to protect.

Make the handle the content: "Tuesday morning or Thursday?" — the caller answers with the thing, you resolve the thing against the candidate set you are still holding, and ambiguity becomes a discriminate turn rather than a silent mis-selection. Keep DTMF for digits, as replacing an IVR argues, and keep it for digits only.

One carve-out worth making deliberately: a caller using a relay service, a screen reader, or a speech-to-text keyboard may genuinely prefer a numbered list, because their channel does have scrollback. Make the enumerate-and-index path available on request rather than deleting it, and keep it out of the default. Accessible voice agents has the rest of that argument.

Step 6Instrument the enumeration, because nothing else will.

None of this appears in a transcript eval, which is the usual reason it ships broken. The agent's utterance is fluent, the options were accurate, the caller selected one, and every text-level metric is green. Four cheap measurements catch it.

  • Candidate-count distribution at the tool boundary. Log the pre-collapse count on every call. The tail is your bug list: if the 90th percentile is eleven, your agent is improvising selections eleven rows deep several times an hour.
  • Longest uninterrupted agent utterance, in seconds. One number, trivially computable from synthesis timings, and the single best proxy for this whole failure class. Alert on it. Anything over about eight seconds on a conversational turn deserves a look.
  • Barge-in rate during enumeration versus elsewhere. A spike while the agent is listing is not impatience; it is the caller stopping a monologue they cannot use.
  • Position bias in selections. Compare the distribution of chosen positions against what your ranker intended. A strong last-position or first-position skew that your ranking does not predict is the recency effect from Step 1, showing up as revenue.

Add one line to your golden set in evaluating voice agents: a query that legitimately matches dozens of rows. Score the turn on whether the agent asked a question, not on whether its list was accurate. Most agents fail this case on the first run, and almost nobody has the case.

Do this first: find your highest-traffic voice tool, log the row count before any truncation for one day, and plot it. Then change that tool's return type so a result set is no longer expressible — one answer, one discriminating question with counts, or a too-many outcome. The prompt change you were about to make is a workaround for a signature you are allowed to edit.