Data Agent Voice

Talk to your governed data.

Ask out loud. Hear the answer with the definition it applied and the caveat it raised — because you cannot skim audio, and a number without its meaning is how the wrong team wins.

A voice front end over data-agent-service, built on the TEN framework. You sign in once, and every question runs as you, all the way to the source.

No one has heard an answer from this yet. Every capability below says which it is — written, run, or waiting on something — and carries what would catch it if it were false.

The problem this solves

The agent is careful, and careful is slow

A question to the service upstream takes twenty-six seconds at the median — six to eight model turns, each one grounded in the catalog. A conversation reads as broken after about one second of silence. No faster model closes a gap of thirty times.

A voice agent that waits is a voice agent nobody uses

The usual answer is to make the agent shallower. That trades away the only thing worth having: an answer you can defend. So this does not make the agent faster. It stops the conversation waiting for it.

Speech is upstream of every guard

The SQL guard catches a malformed query. It cannot catch a perfectly formed one about the wrong team, because “Billing” misheard as “Building” produces a valid question with a wrong subject. That is a new class of error, and it is checked before dispatch rather than discovered in the answer.

How it fits together

  browser ---- audio over WebSocket ----> TEN graph
                                            |
     ASR --> host --> model --> TTS --> audio back        ≤ 1 second
              |
              +-- lookup   --> the catalog                ≈ 1.5 seconds
              +-- dispatch --> ask service --> ticket      8-25 seconds,
                                  +-- bridge --> host     narrated as it goes

The host owns the turn and is the only thing that speaks. The slow agent runs where nobody is waiting, and its answer arrives as a spoken turn when it is ready.

What it does

One voice, one floor written

Everything spoken leaves by one path — model prose, a backend’s answer, a fixed phrase alike. An answer arriving mid-sentence waits for a gap rather than talking over the caller, and is not dropped, because they asked for it.

A test asserts there is exactly one send to the voice in the host.

A refusal is never narrated written

The service reports answered, abstained and refused as three distinct events. The last two cross into this repository as a kind with no text and are spoken from fixed phrases the model never sees. It cannot smooth what it is not shown.

The renderer has no words for them, and a test fails if it gains any.

Definitional questions never reach the warehouse written

“What counts as resolved?” is one catalog hop — about a second, no SQL, nothing to wait for. Tiering is not a classifier: it is which tool the model reached for.

A fast lookup declares a millisecond budget; that is what makes it fast.

A backend is a file written

This repository knows kinds of capability — a fast lookup, a dispatch, a terminal outcome — and never a capability’s name. A second service is a descriptor, and the model routes between backends by their tool descriptions in the turn it was already taking.

A test fails if any general layer starts naming one service’s vocabulary.

Speech in, transcripts out run

A session starts, all seven nodes load, and 2.7 seconds of synthesized speech goes in over the socket and comes back as final transcripts — on arm64, natively.

Seven upstream defects were found doing this, none of them by reading.

A spoken answer needs a key

Everything up to the model is proven. The host’s call fails against a stub, which answers plain JSON where the SDK streams — a stub cannot stand in for a model on a streaming path.

One API key away, and it closes four rows of the ledger at once.

Every optimization is a switch measurable

Semantic turn detection, prefix caching, host model, sentence chunking, pre-rendered acknowledgements, speculative dispatch — each one a setting, each one a delta the panel shows. A lever an audience cannot hear is a claim, not a demonstration.

The panel’s arithmetic is checked; the deltas need a call.

Multi-arch, natively run

The framework publishes arm64 in its release and not its registry, and its arm64 tooling needs a newer glibc than its own build image. Both legs build here, on native runners, and the server starts on both.

amd64 326 s, arm64 213 s, in CI on every code change.

Why the conversation cannot wait for the agent

The instinct is to speed the agent up. Every lever there is real and none of them is enough: prompt caching saves seconds, model tiering saves more, and a careful answer still takes ten. Meanwhile the levers that decide whether it feels alive are somewhere else entirely — when the turn is judged to have ended, whether the first sentence goes out before the last one exists, whether the acknowledgement was synthesized or served from disk.

So the split is not a compromise for a demo. It is the architecture: a small model owns the turn and answers what it can in under a second, and the careful one runs where nobody is waiting and narrates itself while it works.

Measured where it says run, estimated everywhere else.

The budget on this page is an estimate until the panel replaces each number with one it watched. The ledger keeps the two apart, row by row.

Honest status

end-to-end witnesses
7upstream defects filed
2architectures, both native
0spoken answers yet

No one has heard an answer from this. The architecture is decided, the framework is pinned and read from its source, both image legs build, all seven nodes load, and speech has been recognised end to end. What has never happened is a reply coming back as sound, because the model on the host’s path needs a key.

Every row of the ledger that describes a working line reads not run. That is what the ledger is for: a green test suite here proves the configuration agrees with itself, and nothing more.

Run it locally

make doctor   # toolchain, docker, and the upstream stack
make up       # the graph and a local voice
make status   # is the line usable?
make call     # open the browser client

make up needs data-agent-service running first: this reaches its gateway over the network and never runs it. A documentation change skips the image builds — why.