My voice classifier was 93% confident. It was completely wrong.

My voice classifier was 93% confident. It was completely wrong.

Martin Shein · · 5 min read

I ran my own voice skill against a real brand. It came back 93% confident, and it was completely wrong. Here's the breakdown.

The brand was ONYX Radiance, a direct-to-consumer skincare client of mine. match-archetype.py — the classifier inside @di-atomic/voice-builder — read their copy and returned trait-terse x icp-b2b-saas at 0.93 confidence. ONYX sells face serum to consumers. That is about as far from B2B SaaS as a brand gets.

The classifier wasn't broken. It was measuring one thing well and calling that the answer. ONYX writes short sentences. B2B SaaS writes short sentences. The sentence lengths matched, so the archetype matched, and the confidence score climbed because the single signal it looked at was strong.

That is the failure mode I care about most. A classifier that says "I don't know" costs you a minute. A classifier that says 0.93 and hands you the wrong voice costs you a quarter of off-brand copy, written fluently, in a register your customers don't recognize.

Why "just describe the tone" fails

Here's how most agents handle brand voice today. Feed the model three samples. Ask it to describe the tone. Get back a paragraph.

"Professional yet approachable. Clear and confident. Customer-focused."

Every brand gets that paragraph. I know, because I shipped it. Version 0.1.0 of voice-builder did exactly this, and I rated it in my own notes as "not useful at all." It passed its tests. It produced a voice.md file. The file was generic, so everything downstream — copy-engine, post-writer, outbound-engine — inherited generic.

The problem isn't the model's writing ability. It's that describing a voice and specifying one are different jobs. A description is prose you nod at. A specification is a set of numbers a later skill can be held to.

The reframe: install and tune, don't re-derive

So I stopped asking the model to invent a voice on every call.

Version 0.3.x ships a registry of 15 starter voices: 4 trait variants (terse, warm, authoritative, playful — modeled on Stripe, Mailchimp, IBM, Duolingo), 4 ICP archetypes (B2B SaaS, agency, e-commerce, regulated chemicals), and 7 language profiles (EN, DE, HE, RU, RO, FR, ES). A starting voice is trait x ICP x language.

Three chalk sliders labelled trait, ICP and language feeding one gold-outlined voice document, captioned install, then tune

The job changes from "invent a voice" to "find the closest one and tune it." The thresholds are explicit:

  • Confidence 0.70 or higher — install and tune.

  • 0.50 to 0.69 — install the primary, graft the secondary.

  • Below 0.50 — build custom, and say so out loud.

A score falling through a chalk funnel into three chutes: 0.70+ install, 0.50-0.69 graft in gold, under 0.50 build

The second rule is: measure, don't eyeball. Reading ease, grade level, sentence rhythm, hype density, second-person density. Numbers, before any adjective gets written down.

What the fix actually was

Back to ONYX. The real-agent test caught the 0.93, which is the entire reason that test exists — the unit tests were green.

The fix was to measure who the sentence is pointed at, not just how long it is. Direct-address density. ONYX runs 94 second-person words per 1,000. B2B SaaS copy sits far below that, because it talks about things: teams, organizations, workflows, stakeholders. Direct-to-consumer copy talks to you.

Two identical chalk speech bubbles: the left one points a gold arrow at the viewer, labelled talks TO you, 94 per 1,000; the right points away at a cluster of figures, labelled talks ABOUT them

I added that signal to analyze-voice.py and weighted it in match-archetype.py. ONYX re-scored as icp-ecommerce x trait-warm at 1.0.

The same test run surfaced two more real bugs. A rhythm score below 0.70 was a hard failure, when the underlying research says it should warn, not block. And the Russian register check missed inflected ты-forms — твоя, твоей — so it couldn't reliably tell вы from ты, which is the single most consequential decision in Russian brand voice. Both were fixed in the skill, not worked around in the prompt.

The voice this post is written in

This is the part I find hardest to argue with. The voice profile behind this post was produced by the skill the post is about. docs/di-atomic-brand/voice.md opens with:

Registry match: icp-agency (confidence 0.93, install-and-tune)

Measured on my real corpus: Flesch reading ease 59.9, grade 7.8, second-person density 53.9 per 1,000 words, hype-density 0.0 per 1,000.

Those numbers aren't decoration. When copy-engine drafts a Di-Atomic landing page, it is held to them. The profile also carries an anti-pattern floor: generic SaaS jargon copy measures Flesch 14.5, university-level, and hard-fails the analyzer. If a draft reads like that, it's off-brand by measurement, not by opinion.

Notice that 0.93 appears twice in this post. Once where the match was right, once where it was wrong. That's the whole lesson: confidence is not correctness. You need a second, independent signal to tell those two cases apart.

What to ask your own setup

If you have an agent writing in your brand's voice, ask it two questions.

What are the numbers? If the answer is adjectives, you have a description, not a specification. Nothing downstream can be held to "approachable."

What did it install? If the answer is "derived from your samples" on every single call, you're getting a fresh guess each time, and fresh guesses average out to generic.

One more thing, for anyone selling regulated products. When the brand matches icp-regulated-chemicals, trait-playful is refused outright — a hard exclusion, not a suggestion. That's BPR Article 72, a legal rule rather than a style preference. Copy produced in that voice also has to clear a compliance lint before it counts as final.

voice-builder is a foundation skill. copy-engine, post-writer, outbound-engine, and section-designer all read what it writes. Get it wrong and every one of them is confidently, fluently, measurably off-brand.

Live on the marketplace as @di-atomic/voice-builder. Trigger it with "build my brand voice".