Writing Flows
A flow in CALM is a structured sequence of steps describing only the business logic needed to complete a specific user goal—like blocking a credit card, changing an address, or adding a payee. By separating the business logic from the rest of the conversation, you get:
- Clarity: Each flow focuses on a single job or outcome (e.g., “Block Credit Card”).
- Reusability: Flow logic can be called from other flows or triggered on its own.
- Maintainability: You can refine or change the flow logic without affecting the entire conversation design.
There are two types of flows in CALM: flows that you write to express your business logic and patterns or syste flows that come out of the box with CALM. Before we move on to explain how flows work, a word on these conversation patterns:
Conversation Patterns (System Flows)
In addition to the flows you write for your domain-specific tasks, Rasa provides patterns—pre-defined, reusable flows that handle “meta” conversational situations or repairs. For instance, if a user cancels a flow midway or wants to clarify a previously collected piece of information, a pattern steps in to handle this detour. These patterns work like templates: they can be triggered whenever relevant, so your assistant can handle common conversational patterns consistently.
Read more about customizing patterns under Customizing Pattenrs
How Do Flows Work?
Triggering Flows
CALM uses an LLM “command generator” prompt that contains the conversation history, the relevant flows, slots, and conversation patterns. Essentially, CALM leverages the LLM to parse the user’s request into structured commands, referencing all pertinent context—including conversation history, current state, and flow definitions. This approach ensures that when the user’s goal matches the description of a given flow, that flow will be triggered. Flows can also be:
- Started by a direct NLU trigger (e.g., when a recognized intent maps to a flow).
- Linked or called from inside another flow (for subflows or follow-up tasks).
Dialogue Stack
When a flow (or pattern) is activated, it’s placed on top of a dialogue stack (like stacking plates). The topmost flow is always active. Once that flow finishes or is canceled, the system returns to the next flow on the stack. This structure ensures that your assistant’s logic remains organized, even when users interrupt or pivot to new tasks.
How to Write Flows
Writing a flow in CALM involves capturing the essential steps to fulfill a user request without hardcoding every possible conversation path. You define flows as YAML in your flows.yml
(or multiple YAML files), focusing on the business logic:
-
Give the flow an ID and a clear description
flows.ymlflows:
block_card:
description: Block a user's credit card when requested
steps: []- The
description
is critical for the LLM to understand when to pick this flow.
- The
-
Add the steps
Each step specifies what your assistant should do:
-
Collect user information:
flows.ymlflows:
block_card:
description: Block a user's credit card when requested
steps:
- collect: card_number
description: “The 16-digit card number to block” -
Take an action (e.g., a custom action or a response):
flows.ymlflows:
block_card:
description: Block a user's credit card when requested
steps:
# ...
- action: action_block_card_in_backend -
Set or reset slots:
flows.ymlflows:
block_card:
description: Block a user's credit card when requested
steps:
# ...
- set_slots:
- card_number: null -
Call or link other flows for subflows or follow-ups:
flows.ymlflows:
block_card:
description: Block a user's credit card when requested
steps:
# ...
- call: authenticate_user_flowflows.ymlflows:
block_card:
description: Block a user's credit card when requested
steps:
# ...
- link: collect_feedback
More information on different step types can be found on the reference page.
-
-
Include branching if needed
You can add simple conditional logic (like checking if a slot is filled or if a user is already authenticated):
flows.ymlflows:
block_card:
description: Block a user's credit card when requested
steps:
# ...
- collect: user_authenticated
next:
- if: not slots.user_authenticated
then:
- action: utter_ask_for_login
- link: authenticate_user_flow -
Leverage conversation patterns
You don’t need to write custom branching logic for every possible user detour. Flows represent the business logic your assistant is supposed to drive throughout conversation. Instead, rely on patterns (built-in flows) to accommodate user detours.
Flows in CALM can do more than these basics—such as advanced branching, slot validation, or subflow calls. For a deeper look at each property, step type, or YAML configuration, check out the Flows reference.
Importance of Clear Descriptions
Each flow has a description that briefly explains what the flow accomplishes. The LLM reads these descriptions to decide which flow to start. A concise, specific description reduces errors in flow selection. For example:
Good: “Block a user’s credit card if they suspect fraud or want to freeze it”
Less useful: “Card blocking request”
Key Takeaways
- Flows define business logic: They are not full conversation scripts but the critical steps your organization wants to guarantee.
- LLMs + flows: The LLM remains flexible in interpreting user input and context, while flows make sure the assistant sticks to rules and processes.
- Write clear and detailed descriptions: They help the LLM reliably select the right flow at the right time.
- Use patterns for conversation repair: Don’t clutter your flow with every possible detour. Let patterns handle cancellations, clarifications, or other unexpected conversation turns.
With flows, you maintain control over complex processes while giving the LLM room to shine in adapting to user input. Start by mapping out the tasks your assistant must support, split them into distinct flows, and keep descriptions tight. That’s all you need to harness the best of both worlds: rigid business logic and flexible, human-like conversations.