Skip to main content

Thinking in Flows

If you have built LLM chatbots before, SuperDialog requires one shift in thinking:
Traditional botSuperDialog
Write a long system promptDesign a node graph
LLM decides everythingDeveloper defines topology; LLM fills gaps
No structure - LLM can go anywhereExplicit states - LLM can only traverse defined edges

The Core Model

A flow is a directed graph of nodes connected by edges.
  • Node - one step in the conversation (collect a name, confirm a booking, handle an objection)
  • Edge - a condition that moves the conversation to the next node
  • Criteria - what data must be collected before an edge can fire

Start With the Topology

Before writing any prompts, map your conversation:
  1. What are the distinct steps in this conversation?
  2. What data needs to be collected at each step?
  3. What conditions cause the conversation to move forward?
  4. What can go wrong at each step? (user refuses, unclear answer)
For an appointment booking bot:
Appointment-style SuperDialog flow graph showing greeting, detail collection, availability checks, confirmation, completion, missing information fallback, and retry paths.

Then Generate It

superdialog flow generate "Book appointments. Collect patient name, doctor, date, time. Confirm before saving." --output booking.json
superdialog flow lint booking.json    # always lint after generate
superdialog flow draw booking.json    # visualise the graph

Test It - No Infrastructure Needed

superdialog chat booking.json
Full interactive REPL against your flow. No Unpod account, no phone number, no voice setup required.
> I want to book an appointment
Certainly! May I have your full name?
> John Smith
Thank you, John. Which doctor would you like to see?
[42ms]
Iterate on booking.json, re-run chat - the full loop takes seconds.

Understanding Criteria and Edges

A node has two separate concerns: Criteria - what this node needs to collect:
"completion_criteria": [
  {"key": "name", "description": "Patient full name", "required": true},
  {"key": "date", "description": "Appointment date (e.g. 'May 25', 'next Friday')", "required": true}
]
Edges - where to go next and what to extract:
"edges": [
  {
    "condition": "Patient provided both name and appointment date",
    "target_node_id": "collect_time",
    "input_schema": {
      "type": "object",
      "properties": {
        "name": {"type": "string", "description": "Full name"},
        "date": {"type": "string", "description": "Appointment date"}
      }
    }
  },
  {
    "condition": "Patient did not provide all required information",
    "target_node_id": "collect_details",
    "is_fallback": true
  }
]
Criteria and edges are evaluated independently by the LLM at runtime. You can write criteria that say one thing and edges that say another - the system does not automatically check they are consistent. Always run flow lint to catch the most common mismatches.
Be explicit in descriptions. "User gave a date" is vague. "Appointment date (e.g. 'May 25', 'next Friday', '2026-05-25')" tells the LLM exactly what counts.

Two LLM Calls Per Turn

By default, each user turn makes two LLM calls:
  1. Criteria judge - evaluates which edge fires and extracts slot data
  2. Response generator - generates what the agent says next
For voice, this adds 400-800 ms latency before TTS starts. To collapse to one call (requires a function-calling model like GPT-4o or Claude Haiku):
DialogMachine(flow=flow, llm="openai/gpt-4o-mini", adapter="toolcall")
Or test it in the CLI:
superdialog chat booking.json --adapter toolcall

Next Steps

Quickstart

Bootstrap your first flow in 5 minutes

Architecture

Component breakdown and data flow

Tools

Connect your backend APIs as flow tools

Sessions

Multi-user and persistent sessions