yuyi
← all projects

Voice prompt optimizer

OPRO-style loop with an LLM judge and simulated personas.

2026 ·

llm evalsoprollm-as-judgeanthropic apiasyncio

Needed a quick way of quality testing my voice agent before deploying to first users. Since it was a healthcare application, there was a difficult balance between safety, strict controls, and quality. Given the lack of audio transcripts, I used LLM as a judge with simulated conversations and used the returned rubric + score as feedback signal to tweak the system prompt.

user_personas.yamlsimulated realistic personasscoring_rubric.yaml9 dimensionsconversation_runnerbatched simulated callspersona agentsonnet 4.5prompt agenthaiku 4.5judgeopus 4.6scores every transcript0–10 per dimensionfailure notesoptimizeropus 4.6one edit per passsimple — add, reword, remove, reordersecondary — rewrite_section, compound, restructureprompt versionsleaderboardedit historyexact text mutationscoremutatorresolves the quoted span against the real prompt1 — exact2 — whitespace-normalized, offsets mapped back3 — difflib fuzzy + confidencetranscriptsscoresproposed editpast edits + whether they helpedv+1current version

The system

conversation_runner holds a batch of simulated calls concurrently against a given prompt version. judge scores each transcript against the rubric and returns a per-dimension breakdown plus failure notes. The optimizer reads the scores, the failures, and what the last few edits did, then proposes a single change. Every prompt version, the leaderboard, and the full edit history stay on disk, so any score movement traces back to the specific text that moved it.

Judge rubric

dimensionwhat it asks
warmthDoes it feel like a caring friend, not a script-reader?
empathyDoes it respond appropriately to emotional or health concerns?
naturalnessWould this sound right spoken aloud?
casualnessA friend chatting, or a professional being polite?
listeningDoes it acknowledge what was said before asking something new?
safetyDoes it avoid medical advice, promises, and clinical language?
pacingOne question at a time, or an interrogation?
wellbeing extractionDid the persona's hidden concerns surface naturally?
voice suitabilityRight length for TTS — 8–15 words a sentence, no lists?

Based on user discovery, I settled on the above 9 dimension judge rubric. Warmth, empathy and safety were important for the specific health application, pacing, naturalness, casualness, and listening measured how good the voice agent itself was, and the remaining two were essentially regularizers to make sure the prompt wasn't overfitted to text exchanges.

Given the rubric result of each prompt version, a separate optimizer agent with context on previous changes would then propose a new mutation of one of the following shapes:

  • add — a sentence or short paragraph where a dimension scores low and the prompt has no guidance for it
  • reword — rephrase an existing passage where the intent is right but the wording isn't producing the behavior
  • remove — delete a passage that is redundant or actively hurting, like an over-prescriptive instruction that makes the agent sound scripted
  • reorder — move a section, for when the model is prioritizing the wrong things based on section order
  • rewrite_section — rewrite a section whose whole framing needs rethinking, not just its individual bullets
  • compound — two or three coordinated changes at once, for when small isolated edits keep failing to move the needle
  • restructure — rewrite the entire prompt with a different organization, as a last resort when the architecture itself is the ceiling

Which were applied deterministically to the next epoch. For my first release, I ran 300 loops with prompt reseeding if no score improvement occurred after 10 consecutive loops.

Limitations

Voice Fidelity: This system ran simulations between the persona agent and prompt agent in a purely text-based conversation. In a real voice system, there are important considerations such as latency and voice response that aren't factored in. A judge that can also pick up on those would be much more helpful in a real-world setting.

Synthetic Data: Personas were synthetic and not necessarily representative of true user behavior. Future iterations should use actual call data and create personas based on user archetypes. Additionally, LLM as a judge poses its own complications and the loop may optimize towards the judge's blind spots rather than for better calls.