Inspiration

Altur's agents make phone calls. Increasingly, so does everyone else, and when two autonomous systems end up on the same line, every assumption behind the conversation breaks. The same capability that lets a company automate outbound calls lets an attacker automate social engineering at a scale no fraud team can staff against.

What it does

Given a two-channel 8 kHz recording of a Mexican-Spanish phone call: is the caller a person?

POST /detect takes the base64 WAV and returns {"is_synthetic": bool, "confidence": float}. Behind it, a voice activity pass reconstructs who spoke when, six independent families of evidence score the caller, and a fusion layer combines them into a calibrated probability.

Balanced accuracy 1.000 on validation (71/71), median latency 1.0 s.

How we built it

We went in with a hypothesis we were confident about, and we realized we were wrong about it within the first hour. This turned out to be an important part of discovery for the project.

The idea was echo leakage, a human sits in a room, so the agent's voice bleeds from their earpiece back into their mic. A synthetic caller has no room. We tested it before building anything and measured a correlation peak of 0.007 at random lags, identical for humans and machines. Both call legs arrive already separated at the carrier.

What replaced it was the real discovery, the strongest family isn't acoustic at all. It never listens to the voice, it measures when the caller speaks. Humans hesitate, with a reaction floor near 250 ms and a wide spread above it. A speech-to-text -> LLM -> text-to-speech stack has a tighter distribution, because it's a machine doing the same work every time. Timing alone scores 0.986 AUC.

So instead of "does this voice sound synthesised?", we ask ourselves "does the caller behave like a mind?"

Challenges we ran into

AUC is not accuracy. Our worst held-out-engine fold scored 0.9982 AUC and we read that as robustness. AUC measures ranking; the judges score a threshold. Measured properly, our worst fold was 0.9550 balanced accuracy, and validation couldn't reveal it, because it separates so cleanly that every threshold in a 0.99-wide band scores 71/71. We picked the threshold on unseen-engine folds instead, cutting worst-case error 35%.

An optimization we were sure about did nothing. Moving detection off the event loop: 1.78 -> 1.50 req/s. We retracted the claim.

Accomplishments that we're proud of

71/71 on validation. A 4.4× speedup we could prove was free, we collapsed ~3,400 VAD inference calls per conversation into one, and verified bit-identical output on all 71 calls. Choosing not to build something. We analysed a GPU port, found Amdahl capped it at ~33% against a 29× headroom we already had, and shipped CPU.

What we learned

Test your best idea first, and cheaply. Killing the echo hypothesis in hour one cost an afternoon. A perfect score isn't evidence of a good model, instead it's evidence your evaluation ran out of resolution. Knowing which metric you're scored on. We optimized AUC and nearly shipped a threshold 35% worse on the metric that counted.

What's next for Vionis

Segment-level scoring. Our most honest weakness: a spliced call, real human audio at the open, synthetic after, defeats any fixed-prefix strategy. The fix is aggregating over segments with a rule sensitive to any synthetic region.

Three tiers, flash, medium, turbo and max, each with its own fitted threshold, because reusing one gives correct ranking at the wrong operating point.

Built With

Share this project:

Updates

Submission history