Skip to main content

Confirmation, Profiles & Agent Architecture

This page records the theory and research behind two related engineering decisions in Booker4j:

  1. How a booking is confirmed before it is committed — and why this is configurable per customer rather than hard-wired.
  2. How configurable behavior is structured — as a small set of named interaction profiles rather than an ever-growing pile of boolean feature flags.

It exists so that future changes to these decisions are made with the reasoning, not against it. Every claim is cited inline; the full annotated bibliography lives in References.

Where this is implemented
  • Per-customer profile: interaction.profile in the customer form YAML → InteractionConfig / InteractionProfile.
  • Deterministic completion: the join-barrier hook in ParallelToolCallingManager + FormCompletionInterceptor, with idempotent finalization in FormSubmissionService and a durable submitted flag on the session.
  • Design record & step-by-step plan: docs/superpowers/plans/2026-05-30-confirmation-removal-interaction-profiles.md and .claude/completion-interaction-profiles.md.

Part 1 — Explicit vs. implicit confirmation

When a conversational agent is about to commit a transaction (book, pay, send), it can confirm in one of two ways:

  • Explicit confirmation — restate what will happen and wait for a separate "yes" before acting ("Here's your booking — shall I confirm?"). The user is put in control before the commit.
  • Implicit confirmation — assume the input was understood and fold the acknowledgement into the result itself ("Done — booked your cleaning for June 5th"). No extra turn.

The research is consistent on how to choose between them: by stakes. Explicit confirmation is recommended for high-consequence, hard-to-reverse, "high-purposiveness" actions — flights, payments, medical, anything a mistaken commit would hurt — because it reassures the user and keeps them in control (Hanakano, Voice First — 5 confirmations). Implicit confirmation is preferred for low-stakes, "low-purposiveness" tasks, where users prioritize efficiency and find an extra confirmation turn slow and "overly structured" (Springer — confirmation strategies). Crucially, implicit confirmation still happens in the live turn, with the user present — it is not a silent, after-the-fact commit.

There is also a structural hazard specific to chat (vs. GUIs): the submit/reset metaphor problem. A GUI button gives the backend an unambiguous "commit now" signal; a free-text chat has no button, so the system can lose track of when the user actually intends to commit — especially if they change topic mid-flow (Bridging UI Design and Chatbot Interactions). The lesson: don't make "commit" a thing the model has to remember to do — anchor it to deterministic state.

How Booker4j applies this

We expose the choice as a per-customer interaction profile:

ProfileConfirmation styleWhen the booking commits
verified (default)ExplicitAfter the form is complete, a deterministic summary is shown and the user must confirm; an idempotent submitBooking tool then commits.
expressImplicitThe moment the form is complete, the backend submits in the same turn and the assistant's reply is the success message.

Two design choices follow directly from the research:

  • Commit is a deterministic event, not an LLM tool-press or a background timeout. Completion is detected in code at the parallel-tool join barrier (all mandatory answered + all optional addressed) — sidestepping the submit/reset-metaphor risk. A background "auto-submit after inactivity" job was explicitly rejected: it would commit without consent and outside the live turn, which is not what implicit confirmation means.
  • The summary is deterministic, not LLM-generated — built from the form state, so values are never hallucinated.

Part 2 — Playbooks, hybrid agents, and "LLM interprets, logic decides"

The second decision is architectural: how should configurable behavior be expressed? Two industry-leading platforms converged on the same answer — a playbook / flow as the unit of behavior.

  • Google Dialogflow CX — "Playbooks." A playbook is a declarative unit of agent behavior: a goal, natural-language instructions, few-shot examples, and parameters. You define many and route between them, and you can run a hybrid agent — deterministic flows where exactness matters, generative playbooks where flexibility helps (Playbooks, Generative vs. deterministic, hybrid agents).
  • Rasa CALM — "Flows." YAML business-process definitions, with the guiding principle: "the LLM interprets what the user wants, the logic decides what happens next, and that separation keeps agents fast and predictable" (Rasa CALM). CALM also ships reusable conversation patterns for the off-happy-path cases (corrections, digressions, repair) (conversation patterns), and a process-calling model where the LLM triggers and collaborates with a stateful process (Rasa 2025 overview).

The takeaway: keep three layers cleanly separated — declarative (the what, in config), deterministic (the control, in code), generative (the conversation, in the LLM).

How Booker4j applies this

This is exactly the shape Booker4j already has and now leans into:

  • Declarative — the form definition and the interaction.profile live in customer YAML.
  • Deterministic — the form engine validates and gates; the join-barrier hook finalizes the booking. The commit never depends on an LLM coin-flip.
  • Generative — the LLM conducts the conversation (collecting answers, phrasing, handling corrections), steered per-turn by profile-appropriate context injected into the tool results rather than by scattered Java if branches.

Part 3 — Why profiles, not boolean flags (constraint decay)

A natural temptation is to add each new behavior as an independent boolean toggle (confirmation.enabled, autoSubmit.enabled, recap.enabled, …). That path is a trap, and the research names the trap.

The distinction that resolves the tension:

  • N independent booleans = 2ⁿ behavior combinations, almost none tested, read by scattered conditionals → combinatorial fragility. This is constraint decay.
  • A profile = one named, coherent, tested bundle of behavior. Far fewer valid states, each verified — composition, not accretion.

How Booker4j applies this

interaction.profile is deliberately a named profile (verified / express), not the first of ten future booleans. New behavior becomes either a new profile or an attribute inside the profile schema — validated together at config-load (fail-fast) and covered by a test per profile — so the valid-config space stays small and verifiable. This is the structural defense against constraint decay, and it mirrors what Dialogflow CX and Rasa landed on.


Summary

QuestionDecisionPrimary basis
Confirm before commit?Configurable: verified (explicit) / express (implicit)Choose-by-stakes (refs)
When does it commit?Deterministically, in-turn, at the join barrierSubmit/reset-metaphor hazard (ref)
How is config structured?Named interaction profiles, not boolean flagsConstraint decay; declarative > imperative (refs)
LayeringDeclarative (YAML) · deterministic (engine) · generative (LLM)Playbooks; Rasa CALM (refs)

The full bibliography — with source-type labels and the non-research practitioner links — is in References.