Deterministic Conversation

fact_check

Scripted Speech

Some sentences cannot be paraphrased. A recording notice, a cooling-off period, an affordability warning, a right-to-cancel statement: these are approved text, and a model rewording them slightly is a compliance failure even when the meaning survives. The pattern is to carve deterministic islands into a generative conversation, so the parts that must be exact are exact and everything around them stays fluent.

compare_arrows

Two Kinds of Utterance

A scripted node in a workflow graph emits fixed text rather than asking the model to produce it. The difference is not stylistic. It changes who owns the sentence and what can go wrong with it.

PropertyGenerated UtteranceScripted Utterance
WordingVaries per call, and varies with model versionByte-identical every time
OwnershipWhoever last edited the promptWhoever approved the catalogue entry, usually legal or compliance
ReviewSampled from transcripts after the factReviewed before release, as a text artefact
Failure modeDrifts, omits a clause, or is argued away by a persistent callerFires at the wrong moment, or does not fire at all
TranslationRe-generated per language, with no guarantee of equivalenceTranslated once and approved per locale
EvidenceYou can show what was said, not what should have beenYou can show the approved text and the version in force that day
menu_book

The Catalogue Pattern

A scripted node with its text typed into the graph solves the wording problem and creates a maintenance one, because the approved sentence now lives in a diagram rather than anywhere legal can govern it. Lifting the text into a catalogue and having a trigger select from it separates three concerns that otherwise collapse: when to speak, what to say, and who approved it.

1
TriggerOwned by the flow

A condition on the graph, evaluated the same way an edge is: an expression over variables, a tool result, or a semantic check. It decides only that something must be said, not what.

Gets you: The moment of delivery becomes testable independently of the wording.

2
Catalogue LookupOwned by compliance

The trigger names a message by identifier rather than by content. The catalogue resolves it against locale, product, jurisdiction and effective date, returning approved text and its version.

Gets you: The approved sentence lives in one governed place instead of scattered across graph nodes and prompts.

3
DeliveryOwned by the runtime

The text is spoken or rendered verbatim, bypassing generation entirely. Nothing about the model influences the output at this point.

Gets you: Prompt drift, jailbreaks and model upgrades cannot alter a regulated sentence.

4
Context InjectionOwned by the runtime

The delivered text is appended to the conversation as an assistant turn, so subsequent generations treat it as something the agent said.

Gets you: Conversational coherence without the model having authored the words.

input

Injecting It as the Assistant's Own Turn

The delivered text is written back into the context window as an assistant message rather than as a system note. From the model's next turn onward it reads as something it said, which is the entire point: the model will not repeat it, will not contradict it, and can refer back to it naturally. The same mechanism as prefilling an assistant turn, used for compliance rather than formatting.

repeat_onNo Repetition

The model can see the disclosure in its own history, so it does not deliver it again a turn later.

ruleNo Contradiction

It will not tell the caller something the injected text has already ruled out, because the constraint is in the history rather than in an instruction it might weigh.

forumNatural Callback

It can refer back to what was said, as in "as I mentioned, this call is recorded", without any special handling.

shieldUnarguable

A caller cannot talk the model out of a disclosure it never decided to make. There is no decision to attack.

This is steering at its strongest setting. Most steering asks the model to take a correction into account and hopes it wins against everything else in context. Writing into the conversation history does not ask, and the model has no representation of the difference.

receipt_long

Audit Metadata

If the model cannot tell that it did not compose the sentence, neither can anything reading the transcript later. That is fine for the model and unacceptable for the audit trail, so the injected turn needs metadata the model is never shown. Without it the pattern produces a record asserting a disclosure was made and no way to prove which one.

FieldWhy It Is NeededQuestion It Answers Later
Message identifier and versionCatalogue entries change, and the text in force on the day of the call is what mattersWhich exact wording did this caller hear
Trigger that firedSeparates a disclosure that was correctly required from one delivered by accidentWhy was this said at this point
Delivery statusQueued, started, completed and interrupted are four different outcomesDid the caller actually hear all of it
Spoken prefix on interruptionA barge-in means the audible text and the catalogue text divergeHow much of it was delivered before the caller cut in
Provenance flag on the turnThe injected turn is indistinguishable from a generated one without itWhich parts of this transcript did the model actually compose
Locale and jurisdiction resolvedThe same identifier resolves differently by marketWas the right variant selected for this caller
error_outline

Failure Modes

content_cutInterrupted but Recorded as Delivered

The caller talks over the disclosure four words in. The full approved text is already in context and in the log, so the record asserts a disclosure the caller never heard. This is the pattern's sharpest edge, because the injection that makes it work is also what makes the failure invisible.

Fix: Reconcile against playback marks, inject only the spoken prefix, and mark the turn incomplete. Treat an interrupted mandatory message as undelivered and re-deliver rather than assuming.

notifications_offSilent Non-Delivery

A trigger condition never evaluates true because a variable was not set, so the disclosure simply never happens. Nothing errors, and the call completes looking normal.

Fix: Assert on the outcome rather than the trigger. Every call in a regulated flow should be checked at completion for the set of disclosures it was required to contain.

low_priorityOrdering Not Enforced

The disclosure fires, but after the action it was supposed to precede. Card details are taken and the recording notice follows a moment later, which is a breach even though both events occurred.

Fix: Gate the action on delivery completion, not on the trigger having fired. This is an authorisation condition, and it belongs at the tool boundary.

edit_noteDownstream Paraphrase

Having seen the disclosure in its own history, the model helpfully re-explains it in simpler words later in the call. The catalogue governed the first delivery and nothing governs the second.

Fix: Instruct explicitly against restating catalogue content, and check for it in evaluation. If callers genuinely need a plain-language version, approve one and give it its own identifier.

record_voice_overRegister Mismatch

Approved text written by lawyers is dropped into a warm conversational agent, and the seam is audible. Callers notice the switch and some disengage exactly when attention matters most.

Fix: Approve the wording and the delivery together. Where the law fixes the words, tune pace and voice rather than the text.

historyCatalogue Drift Against History

An entry is updated and old transcripts are re-read against the new text, so a historic call appears to have used the wrong wording.

Fix: Version entries immutably and resolve by effective date. Never mutate an entry in place once it has been delivered to anyone.

history

Where This Sits

The prior art is the mandatory IVR prompt, which was deterministic because nothing in the system could be anything else. What is new is putting that determinism back inside a generative agent selectively, so a conversation can be fluent everywhere it is allowed to be and exact everywhere it is not.

Related: Agentic Workflows for the graph the trigger lives in, Turn-Taking and Interruption for the truncation problem that makes a barge-in over a disclosure a compliance question rather than a cosmetic one, Authorisation Policy for gating an action on a disclosure having completed, Steering for the weaker forms of the same idea, and Guardrails for checking what the model produced rather than replacing it.