Multi-Flow Applications¶
Time: 20 minutes
Prerequisites: Tutorials 1-4
Example: multi_flow_example.qtype.yaml
What You'll Learn¶
In this tutorial, you'll learn how to:
- Create applications with multiple independent flows
- Understand variable scoping within flows
- Choose which flow to execute at runtime
- Design modular, reusable flows
Why Multiple Flows?¶
So far, we've built applications with a single flow. But real-world applications often need multiple distinct workflows:
- Different use cases (e.g., "create customer" vs "update customer")
- Different entry points for different user roles
- Reusable sub-workflows for common operations
- A/B testing different approaches
Key concept: Each flow is independent with its own variables, inputs, and outputs. Variables in one flow cannot access variables in another flow - this isolation prevents naming conflicts and makes flows easier to reason about.
The Multi-Flow Example¶
Our example application has three independent flows for customer data processing:
clean_names- Cleans and standardizes customer namesvalidate_names- Validates that names are legitimate person namesgenerate_profile- Generates complete customer profiles
Let's examine the structure:
id: multi_flow_example
description: Multi-flow application demonstrating multiple independent flows
models:
- type: Model
id: gpt4o-mini
provider: openai
model_id: gpt-4o-mini
flows:
- type: Flow
id: clean_names
# ... flow definition ...
- type: Flow
id: validate_names
# ... flow definition ...
- type: Flow
id: generate_profile
# ... flow definition ...
Variable Scoping¶
Each flow defines its own variables that are completely isolated from other flows:
flows:
- type: Flow
id: clean_names
variables:
- id: raw_name # Only exists in clean_names flow
type: text
- id: clean_name # Only exists in clean_names flow
type: text
- type: Flow
id: validate_names
variables:
- id: name_to_validate # Only exists in validate_names flow
type: text
- id: validation_result # Only exists in validate_names flow
type: text
Important: Even though both flows work with names, they use different variable names. The clean_name variable in clean_names is completely separate from name_to_validate in validate_names. This prevents accidental data leakage and makes each flow self-contained.
Flow Inputs and Outputs¶
Each flow declares what inputs it requires and what outputs it produces:
- type: Flow
id: clean_names
inputs:
- raw_name # Required input - must be provided when running this flow
outputs:
- clean_name # Output - available in results after flow completes
This contract makes flows easy to understand and test in isolation.
The Clean Names Flow¶
Let's examine the clean_names flow in detail:
- type: Flow
id: clean_names
description: Clean and standardize customer names
variables:
- id: raw_name
type: text
- id: clean_prompt
type: text
- id: clean_name
type: text
inputs:
- raw_name
outputs:
- clean_name
steps:
# Step 1: Create prompt to clean the name
- id: create_clean_prompt
type: PromptTemplate
template: "Clean this name by trimming whitespace and converting to title case: {{raw_name}}. Return ONLY the cleaned name, nothing else."
inputs:
- raw_name
outputs:
- clean_prompt
# Step 2: Call LLM to clean the name
- id: clean_step
type: LLMInference
model: gpt4o-mini
inputs:
- clean_prompt
outputs:
- clean_name
Pattern: Notice the PromptTemplate → LLMInference pattern. This is the standard way to work with LLMs in QType:
- Use
PromptTemplateto construct the prompt from variables - Pass the prompt to
LLMInferenceto get the result
Running Specific Flows¶
When you have multiple flows, you specify which one to run with the -f flag:
# Run the clean_names flow
uv run qtype run -f clean_names \
-i '{"raw_name":" john doe "}' \
examples/multi_flow_example.qtype.yaml
Output:
# Run the validate_names flow
uv run qtype run -f validate_names \
-i '{"name_to_validate":"John Doe"}' \
examples/multi_flow_example.qtype.yaml
Output:
# Run the generate_profile flow
uv run qtype run -f generate_profile \
-i '{"customer_name":"John Doe"}' \
examples/multi_flow_example.qtype.yaml
Output:
customer_profile: Customer Profile for John Doe
Account Number: 12345678
Member Since: January 2020
Status: Gold
...
Design Principles¶
When designing multi-flow applications:
1. Keep flows focused: Each flow should do one thing well. Our example has separate flows for cleaning, validation, and profile generation rather than one monolithic flow.
2. Make flows reusable: Design flows that can be used in multiple contexts. The clean_names flow could be used anywhere you need name standardization.
3. Use clear variable names: Since variables are scoped to their flow, use descriptive names that make sense within that flow's context.
4. Define explicit contracts: Always specify inputs and outputs clearly so flows can be understood and tested independently.
5. Consider composition: While this example shows independent flows, real applications might combine flows (using InvokeFlow step) to build complex workflows from simple building blocks.
Testing Flows Independently¶
One major benefit of multi-flow applications is testability. You can validate and test each flow in isolation:
# Validate the entire application
uv run qtype validate examples/multi_flow_example.qtype.yaml
# Test just the clean_names flow
uv run qtype run -f clean_names -i '{"raw_name":" JANE DOE "}' examples/multi_flow_example.qtype.yaml
# Test just the validate_names flow
uv run qtype run -f validate_names -i '{"name_to_validate":"X Æ A-12"}' examples/multi_flow_example.qtype.yaml
This makes debugging easier - if something goes wrong, you can quickly isolate which flow is causing the issue.
Visualizing Multi-Flow Applications¶
You can generate a mermaid diagram showing all flows:
This creates a visual representation of your application's structure, showing all flows and their steps:
flowchart TD
subgraph APP ["📱 Application: multi_flow_example"]
direction TB
subgraph FLOW_0 ["🔄 Flow: clean_names
Clean and standardize customer names"]
direction TB
FLOW_0_START@{shape: circle, label: "▶️ Start"}
FLOW_0_S0@{shape: doc, label: "📄 Template: create_clean_prompt"}
FLOW_0_S1@{shape: rounded, label: "✨ clean_step"}
FLOW_0_START -->|raw_name: text| FLOW_0_S0
FLOW_0_S0 -->|clean_prompt: text| FLOW_0_S1
end
subgraph FLOW_1 ["🔄 Flow: validate_names
Validate that a name is a valid person name"]
direction TB
FLOW_1_START@{shape: circle, label: "▶️ Start"}
FLOW_1_S0@{shape: doc, label: "📄 Template: create_validate_prompt"}
FLOW_1_S1@{shape: rounded, label: "✨ validate_step"}
FLOW_1_START -->|name_to_validate: text| FLOW_1_S0
FLOW_1_S0 -->|validate_prompt: text| FLOW_1_S1
end
subgraph FLOW_2 ["🔄 Flow: generate_profile
Generate a customer profile with account details"]
direction TB
FLOW_2_START@{shape: circle, label: "▶️ Start"}
FLOW_2_S0@{shape: doc, label: "📄 Template: create_profile_prompt"}
FLOW_2_S1@{shape: rounded, label: "✨ generate_step"}
FLOW_2_START -->|customer_name: text| FLOW_2_S0
FLOW_2_S0 -->|profile_prompt: text| FLOW_2_S1
end
subgraph RESOURCES ["🔧 Shared Resources"]
direction LR
MODEL_GPT4O_MINI@{shape: rounded, label: "✨ gpt4o-mini (openai)" }
end
end
FLOW_0_S1 -.->|uses| MODEL_GPT4O_MINI
FLOW_1_S1 -.->|uses| MODEL_GPT4O_MINI
FLOW_2_S1 -.->|uses| MODEL_GPT4O_MINI
%% Styling
classDef appBox fill:none,stroke:#495057,stroke-width:3px
classDef flowBox fill:#e1f5fe,stroke:#0277bd,stroke-width:2px
classDef llmNode fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
classDef modelNode fill:#e8f5e8,stroke:#2e7d32,stroke-width:2px
classDef authNode fill:#fff3e0,stroke:#ef6c00,stroke-width:2px
classDef telemetryNode fill:#fce4ec,stroke:#c2185b,stroke-width:2px
classDef resourceBox fill:#f5f5f5,stroke:#616161,stroke-width:1px
class APP appBox
class FLOW_0 flowBox
class RESOURCES resourceBox
class TELEMETRY telemetryNode
The diagram shows:
- Three independent flows, each with its own variables and steps
- The
PromptTemplate→LLMInferencepattern in each flow - A shared model resource (
gpt4o-mini) used by all flows
What You've Learned¶
You now understand how to:
✅ Structure applications with multiple flows
✅ Scope variables within flows
✅ Run specific flows with the -f flag
✅ Design modular, testable workflows
Next Steps¶
Reference the complete example:
multi_flow_example.qtype.yaml- Full working example
Learn more: