2026-09-05 The first real calls have been placed: five ended normally, six failed. Here is what broke.

Assistants

Everything an assistant is made of.

A transcriber, a model, a voice, some tools, some knowledge, a schema for what you want back, and a way to prove it still works next week.

§01 Workbench

Latency, cost and quality belong on the card you are choosing from.

Picking a model is a three-way trade-off, and most interfaces hide two of the three behind a comparison page nobody opens. In the assistant workbench each option carries its latency, its cost per minute and a quality index on the card itself, and the card is the control: clicking it opens the picker.

Above the cards sits the stacked latency bar for the current configuration, in the seven stage colours, and the estimated cost per minute for the whole stack. The two numbers you trade against each other are read together, because seeing one without the other invites half a decision.

Presets are ranking rules over the provider registry rather than three hard-coded model names, so a deprecated model degrades to the next best option instead of breaking the preset. Once any stage is hand-edited the assistant stops matching a preset and the interface says so, rather than highlighting a preset that no longer describes the configuration.

Three ways a number is written
1 240 ms
Measured. Plain, full weight, no prefix. This one came from a real observation of this system.
~690 ms
Estimated. Prefixed with a tilde, dimmed, and carrying a tooltip that says it is a vendor-published typical figure rather than something CallAgent measured.
price unknown
No published price in the registry. The relative band is shown and no figure is invented, because a number a customer budgets against has to be one we can stand behind.

This is a rule in the component, not an editorial preference: an estimate that cannot be mistaken for a measurement at a glance.[1]

§02 Tools

Tools, and MCP servers taken seriously.

A tool is a JSON schema, a destination and a version. The model sees the schema, the runtime validates the call against it, and every attempt is written to the call ledger — including the ones that were refused.

MCP servers are not bolted on as a generic escape hatch. Every tool a server exposes is discovered, stored with its schema and offered to the model individually.

Discovery, namespacing and the permission gate are all in the control plane, so the gateway never has to re-discover a server in the middle of a call.[2]

How tools and MCP servers are handled.
server tools Your own HTTP endpoints, versioned and published like an assistant. A tool version in flight keeps working when you edit the draft.
schemas JSON Schema in, JSON out, validated on both sides. The console shows the schema the model was actually given, not the one you last saved.
MCP discovery Connect a server and its tool list, schemas and server info are fetched and stored. Discovery is refreshed on a schedule and can be forced from the console.
MCP exposure Each remote tool becomes its own function in the model tool list, namespaced per server so two servers can both expose a tool called search without colliding. The model addresses them individually rather than through one generic call-the-server tool.
MCP permissions Per-tool allow and deny, with a risk level per tool. A denied tool is not merely hidden from the model; a call to it is refused and recorded.
MCP freshness A scheduled command refreshes discovery hourly, so a server that adds or removes a tool does not silently drift from what the model believes it has.
refusals A refused tool call is a first-class event with a reason, not a dropped request. You can see which policy refused it and when.
§03 Knowledge
How the knowledge base works.
store Postgres with pgvector. One database, one backup, one tenant scope.
ingestion Documents are extracted, chunked and embedded through a queued pipeline, so a large upload does not block the request that started it.
retrieval A hybrid retriever combining vector similarity with Postgres full-text search, because a customer asking for an exact part number is not doing semantic search.
embeddings Behind a provider interface, with an OpenAI implementation shipped. Swapping it does not touch the retriever.
in the call Retrieval during a call writes knowledge.queried, and a retrieval that found nothing useful writes knowledge.insufficient — so you can see the calls where the assistant was answering without support.
knowledge-only mode An assistant can be restricted to what the knowledge base actually returned, rather than allowed to fill the gap from the model weights.

Retrieval that lives in the same database as everything else.

No separate vector service to keep in sync with your tenant boundary. Documents, chunks and embeddings are rows in the same Postgres database as the calls that cite them, under the same organization scope.

§04 Shape

Getting data out, and handing over.

A call is only useful if something downstream can read it. Two mechanisms cover most of that: a schema the model fills during the call, and a squad that moves the conversation to a different assistant without dropping the line.

Structured outputs, squads and handoff.
structured outputs Define the fields you want back as a schema, attach it to an assistant, and the model fills it during the call. Versioned records, validated on write, readable from the API and from a webhook.
squads Several assistants on one call, each with its own prompt, model and tools. Routing between them is a resolver with an explicit decision, not a prompt instruction and a hope.
handoff context How much of the conversation the next assistant receives is a setting, because sometimes the whole transcript is the point and sometimes it is exactly what you must not forward.
§05 Proof

Knowing it still works after you change the prompt.

Voice agents regress quietly. A word added to a prompt makes the model chatty, the turn goes over budget, and nothing fails — the calls just get slower and worse until somebody listens to one.

So the tools for noticing are part of the product rather than a thing you build afterwards.

Evaluation, monitoring and analytics objects.
eval suites Cases with assertions, run against an assistant and scored. Each run keeps its per-case results so a regression points at the case that broke.
simulation suites Scripted conversations run against an assistant, for the behaviour you cannot assert on a single response.
scorecards Metrics evaluated over real calls — did it collect the field, did it stay inside the budget, did it hand over when it should have.
monitors Standing conditions evaluated on a schedule, so a drift in latency or in failure rate raises something rather than waiting to be noticed.
issues What a monitor raises. A record with a state, not an email nobody reads.
boards Analytics over rolled-up data rather than over raw events: percentiles computed in SQL, hourly and daily rollups, funnels and histograms.
§06 Publishing

A draft you can break, and a published version you cannot.

Assistants are versioned. You edit a draft, you publish it, and calls in flight keep the configuration they started with. A published version is a snapshot, so rolling back is choosing an old one rather than remembering what you changed.

Publishing is also where validation happens, in front of the person who made the choice. The example on the right is real: an assistant was once published with a batch-only transcriber as its live transcriber, the gateway rejected the whole call configuration three layers away, and the caller heard a dead line with nothing on any screen saying why.

Refused at publish

A transcriber that only works on a finished recording cannot be the live one. The publish is blocked with the reason and with where the option does belong.

Sonix only transcribes a finished recording, so it cannot be the live transcriber. Choose a streaming transcriber here and configure Sonix under Artifacts → post-call transcription.

The message is quoted from the validator, not written for this page.[3]

Notes

  1. 1 Estimate convention: resources/views/components/ca/provider-card.blade.php and the workbench header at resources/views/filament/resources/assistants/workbench-header.blade.php.
  2. 2 MCP flattening and namespacing: app/Services/Calls/AssistantSnapshotWire.php and apps/voice-gateway/src/tools/ToolExecutor.ts. Permission gate: app/Services/Mcp/McpPermissionGate.php.
  3. 3 The publish check and its message: app/Services/Assistants/AssistantValidator.php.