YAML Workflows
Define and execute complex workflows using YAML configuration files
from praisonaiagents import Agent
agent = Agent(name="pipeline-runner", instructions="Execute multi-step YAML workflows.")
agent.start("Run my workflow.yaml with sequential and parallel steps.")The user authors YAML once; PraisonAI loads steps, routing, and parallel blocks at runtime.
Define complex multi-agent workflows in YAML files with support for advanced patterns like routing, parallel execution, loops, and more.
Minimum Required Fields
The absolute minimum to run a workflow:
agents:
my_agent:
role: Assistant # Required: Agent's job title
steps:
- agent: my_agent # Required: Which agent executesPractical minimum (recommended):
name: My Workflow
input: "Your input here" # The data passed INTO the workflow (accessed via {{input}})
agents:
my_agent:
role: Assistant
goal: Help with the task
instructions: "You are a helpful assistant"
steps:
- agent: my_agent
action: "Process this: {{input}}"| Field | Required | Default | Description |
|---|---|---|---|
agents | ✅ | – | At least one agent definition |
agents.*.role | ✅ | – | Agent’s job title |
steps | ✅ | – | At least one step |
steps.*.agent | ✅ | – | Agent to execute the step |
name | ❌ | “Workflow” | Workflow identifier |
input | ❌ | “” | Data passed INTO the workflow (accessed via {{input}}). See Workflow Input Resolution for CLI and programmatic precedence. |
run_on | ❌ | None | A shared remote sandbox used by every step’s shell / file tools — maps directly to the Python AgentFlow(run_on=…) kwarg. Accepts: docker, e2b, modal, daytona, flyio, tenki, local. Validated at load time. See Shared Sandbox and Placement. |
goal | ❌ | “Complete the task” | Agent’s objective |
instructions | ❌ | Generic | Agent behavior/persona |
action | ❌ | {{input}} | What the step does |
Workflow Input Resolution
The value that fills {{input}} is resolved with this precedence, highest first.
graph TB
A["Where does {{input}} come from?"] --> B{"--var input= set?"}
B -->|Yes| C["Use --var input value<br/>(even if empty)"]
B -->|No| D{"start(...) / run(input=...) called?"}
D -->|Yes| E["Use programmatic value"]
D -->|No| F{"YAML top-level input: set?"}
F -->|Yes| G["Use YAML input:<br/>(workflow.default_input)"]
F -->|No| H["{{input}} = empty string"]
classDef q fill:#6366F1,stroke:#7C90A0,color:#fff
classDef dec fill:#189AB4,stroke:#7C90A0,color:#fff
classDef ans fill:#10B981,stroke:#7C90A0,color:#fff
classDef warn fill:#F59E0B,stroke:#7C90A0,color:#fff
class A q
class B,D,F dec
class C,E,G ans
class H warn| Source | Wins over | Notes |
|---|---|---|
--var input=<value> (CLI) | Everything | An explicit empty (--var input=) is respected — it is not replaced by the YAML default. |
workflow.start(<value>) / workflow.run(input=<value>) | YAML input: | Any non-empty explicit argument wins. |
YAML top-level input: | Empty string | Stored on the parsed workflow as default_input. Used when no explicit input is supplied. |
Empty string "" | — | The final fallback. Prints a CLI warning. |
CLI input examples
# 1. Uses the YAML `input:` value
praisonai workflow run research.yaml
# 2. Overrides the YAML value
praisonai workflow run research.yaml --var input="AI trends"
# 3. Force empty (does NOT fall back to the YAML default)
praisonai workflow run research.yaml --var input=
# Other --var keys do not affect input precedence
praisonai workflow run research.yaml --var topic="AI trends"Programmatic input examples
from praisonaiagents import YAMLWorkflowParser
workflow = YAMLWorkflowParser().parse_file("research.yaml")
# Uses the YAML `input:` field if set
workflow.start() # or workflow.start("")
# Explicit input wins over the YAML default
workflow.start("AI trends")
# run() also honors the YAML default when called with no argument
workflow.run()Field Names Reference (A-I-G-S)
PraisonAI accepts both old (agents.yaml) and new (workflow.yaml) field names. Use the canonical names for new projects:
| Canonical (Recommended) | Alias (Also Works) | Purpose |
|---|---|---|
agents | roles | Define agent personas |
instructions | backstory | Agent behavior/persona |
action | description | What the step does |
steps | tasks (nested) | Define work items |
name | – | Workflow identifier |
input | topic | Data passed INTO the workflow |
A-I-G-S Mnemonic – Easy to remember:
- Agents – Who does the work
- Instructions – How they behave
- Goal – What they achieve
- Steps – What they do
# Quick Reference - Canonical Format
name: My Workflow # Workflow name
input: What to process # Data going INTO the workflow (not 'topic')
agents: # Define agents (not 'roles')
my_agent:
role: Job Title # Agent's role
goal: What to achieve # Agent's goal
instructions: How to act # Agent's behavior (not 'backstory')
steps: # Define steps (not 'tasks')
- agent: my_agent
action: "Process: {{input}}" # Step action (not 'description')What workflow validate Guarantees
praisonai workflow validate <file.yaml> validates against the schema and cross-references every agent reference. It now guarantees:
- Every workflow under
examples/yaml/workflows/passes (enforced by a regression test). - Agent-existence checks traverse nested
route:,parallel:,loop:,repeat:, andif:payloads — an undefined agent in any of them is flagged with a clear error and non-zero exit. loop: {over: <variable_name>}is treated as a variable, not an agent reference, so it is never flagged.
Sample output — pass:
$ praisonai workflow validate research.yaml
✓ research.yaml is validSample output — undefined agent:
$ praisonai workflow validate broken.yaml
✗ broken.yaml is invalid
Step 'route' references undefined agent 'reviewer'Supported Step Forms
The validator accepts two dialects:
1. Explicit typed steps — name and type set:
steps:
- name: research
type: task
agent: researcher
task: "Research {{input}}"2. Bare runtime forms — no name/type required:
steps:
- agent: researcher
action: "Research {{input}}"
- route: {approved: [publish], rejected: [edit]}
- if: {condition: "{{score}} > 80", then: [premium], else: [standard]}
- loop: {over: items, agent: worker}
- repeat: {times: 3, agent: worker}
- parallel: [research, summarize]
- include: shared/common.yamlFeature Parity
Both agents.yaml and workflow.yaml now support the same features:
| Feature | agents.yaml | workflow.yaml |
|---|---|---|
| Workflow patterns (route, parallel, loop, repeat) | ✅ | ✅ |
| All agent fields | ✅ | ✅ |
| All step/task fields | ✅ | ✅ |
| Framework support (praisonai, crewai, autogen) | ✅ | ✅ |
| Process types (sequential, hierarchical, workflow) | ✅ | ✅ |
| Planning & Reasoning | ✅ | ✅ |
Quick Start
- Run and validate a workflow
CLI # Run a YAML workflow praisonai workflow run research.yaml # Run with variables praisonai workflow run research.yaml --var topic="AI trends" # Override the top-level `input:` field (see Workflow Input Resolution) praisonai workflow run research.yaml --var input="AI trends" # Validate a workflow praisonai workflow validate research.yaml # Create from template praisonai workflow template routing --output my_workflow.yaml # Auto-generate a workflow praisonai workflow auto "Research AI trends" --pattern parallelPython from praisonaiagents import YAMLWorkflowParser, WorkflowManager # Option 1: Parse and execute parser = YAMLWorkflowParser() workflow = parser.parse_file("research.yaml") result = workflow.start("Research AI trends") # Option 2: Use WorkflowManager manager = WorkflowManager() result = manager.execute_yaml( "research.yaml", input_data="Research AI trends", variables={"topic": "Machine Learning"} )Programmatic Usage # Option 3: AgentsGenerator (auto-detects workflow YAML) from praisonai.agents_generator import AgentsGenerator # Sync path gen = AgentsGenerator(agent_file="research.yaml") result = gen.generate_crew_and_kickoff() # Async path (works the same as sync) import asyncio async def main(): gen = AgentsGenerator(agent_file="research.yaml") return await gen.agenerate_crew_and_kickoff() result = asyncio.run(main())
tool_timeout on workflow YAML
Setting an explicit tool_timeout on a process: workflow YAML fails fast — the run raises rather than silently ignoring it. Per-tool timeouts apply to process: sequential and process: hierarchical runs only; the native workflow orchestrator resolves its own tools by name and doesn’t host the per-tool timeout wrapper.
Historical: PR #3252 added warning diagnostics; PR #3963 upgraded them to fail-fast so a silent no-op can’t hide a mis-configured production YAML. The follow-up commit refined the check so the bare CLI --tool-timeout default is still ignored — only an explicit request raises.
Run on a Shared Remote Sandbox
Add run_on: at the top level and every step’s shell and file tools bind to one shared remote sandbox — no Python needed.
name: remote-sandbox-demo
run_on: docker # every step shares ONE sandbox
agents:
writer:
role: File Writer
goal: Write files into the shared workspace
instructions: "Use write_file and execute_command on the remote sandbox."
reader:
role: File Reader
goal: Read files back out of the shared workspace
instructions: "Use read_file on the same remote sandbox the writer used."
steps:
- agent: writer
action: "Use write_file to put 'hello from step 1' in /workspace/note.txt"
- agent: reader
action: "Use read_file on /workspace/note.txt and report its contents"Accepted providers: docker, e2b, modal, daytona, flyio, tenki, local. Omit run_on: for the default local execution. An unknown provider name fails at load time with the list of valid names, so a typo never reaches the first tool call:
ValueError: Unknown run_on provider 'dokcer'. Available: docker, e2b, modal, daytona, flyio, tenki, localFull behaviour, the choose-a-provider decision diagram, and Python-API equivalents.
Complete workflow.yaml Reference
# workflow.yaml - Full feature reference
name: Complete Workflow
description: Demonstrates all workflow.yaml features
framework: praisonai # workflow YAML only supports praisonai
process: workflow # sequential, hierarchical, workflow
input: "Your input data here" # Data passed into workflow (accessed via {{input}})
run_on: docker # optional — share one sandbox across every step. See Shared Sandbox.
# ============================================================================
# WORKFLOW SETTINGS
# ============================================================================
workflow:
planning: true # Enable planning mode
planning_llm: gpt-4o # LLM for planning
reasoning: true # Enable reasoning mode
verbose: true # Verbose output
router: true # Enable model routing
routing_strategy: cost-optimized # auto, cost-optimized, performance-optimized
memory_config:
provider: chroma
persist: true
# ============================================================================
# CONTEXT MANAGEMENT (Prevent Token Overflow)
# ============================================================================
context: true # Enable auto-compaction (recommended for workflows with tools)
# ============================================================================
# CUSTOM MODELS (Optional - for model routing)
# ============================================================================
models:
cheap-fast:
provider: openai
complexity: [simple] # simple, moderate, complex, very_complex
cost_per_1k: 0.0001
capabilities: [text]
context_window: 16000
balanced:
provider: openai
complexity: [moderate]
cost_per_1k: 0.001
capabilities: [text, function-calling]
context_window: 128000
premium:
provider: anthropic
complexity: [complex, very_complex]
cost_per_1k: 0.015
capabilities: [text, vision, function-calling]
context_window: 200000
supports_tools: true
supports_streaming: true
strengths: [reasoning, analysis, code-generation]
# ============================================================================
# VARIABLES
# ============================================================================
variables:
topic: AI trends
items: [ML, NLP, Vision]
# ============================================================================
# AGENTS
# ============================================================================
agents:
researcher:
name: Researcher # Display name
role: Research Analyst # Required: Agent's job title
goal: Research topics thoroughly # Agent's objective
instructions: "Provide detailed research findings" # Agent behavior/persona
# LLM Configuration
llm: gpt-4o-mini # Model to use
llm_routing: auto # Enable auto model selection
llm_models: [balanced, premium] # Models for auto-routing
function_calling_llm: gpt-4o # Model for tool calls
reflect_llm: gpt-4o # Model for self-reflection
# Rate Limiting & Timeouts
max_rpm: 10 # Wires a live RateLimiter (throttles at 10 rpm); must be > 0
max_execution_time: 300 # Timeout in seconds
# Self-Reflection
min_iterations: 1 # Minimum reflection iterations
max_iterations: 3 # Maximum reflection iterations
# System Prompt
system_template: "You are a helpful assistant"
# Tools
tools:
- tavily_search
- wikipedia_search
writer:
name: Writer
role: Content Writer
goal: Write clear content
instructions: "Write engaging content"
llm: premium # Use premium model for quality
# ============================================================================
# STEPS
# ============================================================================
steps:
# Basic step
- name: research_step
agent: researcher
action: "Research {{input}}" # Use {{input}} for workflow input
expected_output: "Comprehensive research report"
output_file: "output/research.md"
create_directory: true
# Step with context dependency
- name: writing_step
agent: writer
action: "Write article based on: {{previous_output}}"
context: # Task dependencies
- research_step
output_json: # Structured output
type: object
properties:
title: { type: string }
content: { type: string }
# Parallel step
- name: parallel_research
parallel:
- agent: researcher
action: "Research market trends"
- agent: researcher
action: "Research competitors"
# Routing step
- name: routing
route:
technical: [tech_agent]
creative: [creative_agent]
default: [researcher]
# Loop step
- agent: researcher
action: "Research {{item}}"
loop:
over: items # Variable to iterate
# Repeat step (evaluator-optimizer)
- agent: writer
action: "Write and improve"
repeat:
until: "approved"
max_iterations: 3
# ============================================================================
# CALLBACKS (Optional)
# ============================================================================
callbacks:
on_workflow_start: log_start
on_step_complete: log_step
on_workflow_complete: log_completeAgent Fields Reference
| Field | Required | Default | Description |
|---|---|---|---|
role | ✅ | – | Agent’s job title |
name | ❌ | Agent ID | Display name |
goal | ❌ | “Complete the task” | Agent’s objective |
instructions | ❌ | Generic | Agent behavior/persona (alias: backstory) |
llm | ❌ | gpt-4o-mini | Model to use |
llm_routing | ❌ | – | Enable auto model selection (auto) |
llm_models | ❌ | – | Models for auto-routing |
function_calling_llm | ❌ | Same as llm | Model for tool calls |
reflect_llm | ❌ | Same as llm | Model for self-reflection |
max_rpm | ❌ | Unlimited | Max requests per minute. A positive int wires a live RateLimiter(requests_per_minute=max_rpm) when the agent has no limiter set, so YAML max_rpm actually throttles requests. max_rpm <= 0 raises ValueError. |
max_execution_time | ❌ | 300 | Timeout in seconds |
min_iterations | ❌ | 0 | Minimum reflection iterations |
max_iterations | ❌ | 3 | Maximum reflection iterations |
system_template | ❌ | – | Custom system prompt |
tools | ❌ | [] | List of tools |
Step Fields Reference
| Field | Required | Default | Description |
|---|---|---|---|
agent | ✅* | – | Agent to execute (*not needed for parallel/route/include) |
action | ❌ | {{input}} | What the step does |
name | ❌ | Auto-generated | Step identifier |
expected_output | ❌ | – | Description of expected output |
output_file | ❌ | – | Save output to file |
create_directory | ❌ | false | Create output directory |
context | ❌ | – | List of dependent step names |
output_json | ❌ | – | JSON schema for structured output (inline schema) |
output_pydantic | ❌ | – | Pydantic model name from tools.py for structured output |
output_variable | ❌ | – | Store output in named variable for use in subsequent steps |
parallel | ❌ | – | List of parallel sub-steps |
context (on parallel/loop) | ❌ | full | Upstream-output pass-through for parallel/loop(parallel: true) steps: full (default, lossless), truncate, or summarize (parallel only). Distinct from the step-dependency context: list above. See Context pass-through to branches. |
route | ❌ | – | Routing configuration |
loop | ❌ | – | Loop configuration (over: variable) |
repeat | ❌ | – | Repeat configuration (until, max_iterations) |
include | ❌ | – | Include another recipe (recipe, input, variables) |
Models Fields Reference
| Field | Required | Default | Description |
|---|---|---|---|
provider | ✅ | – | Provider: openai, anthropic, google, openrouter |
complexity | ✅ | – | List: simple, moderate, complex, very_complex |
cost_per_1k | ✅ | – | Cost per 1,000 tokens in USD |
capabilities | ❌ | [text] | List: text, vision, function-calling, streaming |
context_window | ❌ | 128000 | Max context window in tokens |
supports_tools | ❌ | true | Supports tool/function calling |
supports_streaming | ❌ | true | Supports streaming responses |
strengths | ❌ | – | List: reasoning, code-generation, analysis, etc. |
YAML Callbacks
The callbacks: block wires workflow lifecycle functions into the Workflow engine (PR #5338). YAML cannot import Python by name, so each func_name must be registered on the parser before parsing.
- Register the callbacks
python from praisonaiagents.workflows import YAMLWorkflowParser def log_start(*args, **kwargs): print("workflow started") def log_step(*args, **kwargs): print("step complete") parser = YAMLWorkflowParser() parser.register_callback("log_start", log_start) parser.register_callback("log_step", log_step) workflow = parser.parse_file("workflow.yaml") - Reference them in YAML
yaml callbacks: on_workflow_start: log_start on_step_complete: log_step
Supported lifecycle names. Only these five names map to WorkflowHooksConfig and reach the Workflow engine:
| Lifecycle name | Fires |
|---|---|
on_workflow_start | Before the workflow runs |
on_workflow_complete | After the workflow finishes |
on_step_start | Before each step |
on_step_complete | After each step |
on_step_error | When a step raises |
Any other name resolves but is not wired to the Workflow engine.
Context Management (Token Overflow Prevention)
Enable automatic context optimization to prevent “context length exceeded” errors:
# Simple: Enable with defaults
context: true
# Or detailed configuration
context:
auto_compact: true # Automatically compact when threshold reached
compact_threshold: 0.8 # Trigger at 80% of context window
strategy: smart # smart | truncate | sliding_window | summarize | prune_tools
tool_output_max: 10000 # Max tokens per tool output (e.g., search results)What Context Management Does
When context: true is enabled:
- Auto-compaction: Automatically compresses history when approaching token limits
- Tool Output Truncation: Limits large tool results (e.g., full web page content)
- Smart Strategy: Prioritizes recent/important messages, prunes tool outputs first
- Overflow Prevention: Prevents “context_length_exceeded” errors
Best Practices
# Recommended for workflows with search/crawl tools
name: Research Workflow
context: true # ← Enable this!
agents:
researcher:
role: Researcher
tools:
- tavily_search
- crawl_url
steps:
- agent: researcher
action: "Search for {{topic}}"Context Strategies
| Strategy | Description | Best For |
|---|---|---|
smart | Adaptive strategy (default) | General use |
truncate | Remove oldest messages | Speed-critical |
sliding_window | Keep last N turns | Conversation agents |
summarize | LLM summarizes old context | Quality preservation |
prune_tools | Truncate tool outputs first | Tool-heavy workflows |
Remote execution (run_on:)
Add run_on: at the top of a workflow and every step’s shell and file tools share one remote sandbox — the file one step writes to /workspace is there for the next step to read.
name: remote-sandbox-demo
run_on: docker # every step shares one sandbox
agents:
writer:
role: File Writer
goal: Write files into the shared workspace
reader:
role: File Reader
goal: Read files back from the shared workspace
steps:
- agent: writer
action: "Write 'hello from step 1' to /workspace/note.txt"
- agent: reader
action: "Read /workspace/note.txt and report its exact contents"Omit run_on: and nothing changes — the workflow runs locally as before. A typo is caught at load time, not mid-run. See Shared Sandbox for providers, validation, and the Python AgentFlow(run_on=…) equivalent.
Workflow Patterns
Sequential (Default)
Agents execute one after another, passing context.
name: Sequential Workflow
agents:
researcher:
name: Researcher
role: Research Analyst
goal: Research topics
instructions: "Provide research findings"
writer:
name: Writer
role: Content Writer
goal: Write content
instructions: "Write based on research"
steps:
- agent: researcher
action: "Research {{topic}}"
- agent: writer
action: "Write summary based on: {{previous_output}}"Parallel
Multiple agents work concurrently.
name: Parallel Workflow
agents:
market_analyst:
name: MarketAnalyst
role: Market Researcher
goal: Research market trends
instructions: "Provide market insights"
tech_analyst:
name: TechAnalyst
role: Technology Researcher
goal: Research technology
instructions: "Provide tech insights"
aggregator:
name: Aggregator
role: Synthesizer
goal: Combine findings
instructions: "Synthesize all research"
steps:
- name: parallel_research
parallel:
- agent: market_analyst
action: "Research market trends for {{topic}}"
- agent: tech_analyst
action: "Research technology trends for {{topic}}"
context: full # optional; default "full". "truncate"/"summarize" opt in to token savings
- agent: aggregator
action: "Combine all findings into a report"The context: key maps to the Python parallel(..., context=...) parameter — full (default, lossless), truncate, or summarize. See Context pass-through to branches for the semantics.
# Parallel step — opt in to the head+tail excerpt (and warn on drop)
parallel:
- step_a
- step_b
context: truncate # optional; default "full"
on_failure: fail_fast # optional; default "partial_ok"
# Loop step — same opt-in for parallel iterations (no "summarize" on loop)
loop:
over: items
parallel: true
context: truncate # optional; default "full"
step: processorRouting
Classifier routes to specialized agents.
name: Routing Workflow
agents:
classifier:
name: Classifier
role: Request Classifier
goal: Classify requests
instructions: "Respond with ONLY: 'technical', 'creative', or 'general'"
tech_expert:
name: TechExpert
role: Technical Expert
goal: Handle technical questions
instructions: "Provide technical answers"
creative_expert:
name: CreativeExpert
role: Creative Expert
goal: Handle creative requests
instructions: "Provide creative responses"
steps:
- agent: classifier
action: "Classify: {{input}}"
- name: routing
route:
technical: [tech_expert]
creative: [creative_expert]
default: [tech_expert]Loop
Iterate over a list of items.
name: Loop Workflow
variables:
topics:
- Machine Learning
- Natural Language Processing
- Computer Vision
agents:
researcher:
name: Researcher
role: Research Analyst
goal: Research topics
instructions: "Provide brief research on each topic"
steps:
- agent: researcher
action: "Research {{item}}"
loop:
over: topics
parallel: true
context: full # optional; default "full". "truncate" opts in to token savings (parallel only)Multi-Step Loop
Execute multiple steps sequentially for each item in the loop. This is perfect for pipelines where each item needs to go through several processing stages (e.g., research → write → publish).
name: Multi-Step Loop Workflow
variables:
topics:
- Machine Learning
- Natural Language Processing
- Computer Vision
agents:
researcher:
role: Research Analyst
goal: Research topics thoroughly
instructions: "Provide comprehensive research"
tools:
- tavily_search
writer:
role: Content Writer
goal: Write engaging articles
instructions: "Write in British English, include code examples"
publisher:
role: WordPress Publisher
goal: Publish content to WordPress
instructions: "Validate content before publishing"
tools:
- create_wp_post
steps:
# Multi-step loop: research → write → publish for EACH topic
- loop:
over: topics
parallel: true # Process topics in parallel
max_workers: 4 # Limit concurrent executions
steps:
- agent: researcher
action: "Research {{item}} thoroughly"
- agent: writer
action: "Write article based on: {{previous_output}}"
- agent: publisher
action: "Publish: {{previous_output}}"
# Enable context management to prevent token overflow
context:
enabled: true
max_tool_output_tokens: 5000Multi-Step Loop Features
| Feature | Description |
|---|---|
steps: | Array of steps to execute for each item |
{{item}} | Access current loop item in first step |
{{previous_output}} | Chain outputs between steps in iteration |
parallel: true | Execute iterations concurrently |
max_workers: N | Limit parallel workers |
output_variable | Store final outputs in variable |
Structured Output
Get structured JSON responses from agents using output_json (inline schema) or output_pydantic (reference to Pydantic model in tools.py).
Inline JSON Schema (Option A)
name: Structured Output Workflow
agents:
topic_finder:
role: Topic Finder
goal: Find AI topics
instructions: "Return topics as structured JSON"
steps:
- agent: topic_finder
action: "Find 3 AI topics"
output_json:
type: array
items:
type: object
properties:
title:
type: string
url:
type: string
required:
- title
- url
output_variable: topics
# Loop over the structured output
- agent: researcher
action: "Research: {{item.title}}"
loop:
over: topicsPydantic Reference (Option B)
# agents.yaml
name: Structured Output Workflow
agents:
topic_finder:
role: Topic Finder
goal: Find AI topics
instructions: "Return topics as structured JSON"
steps:
- agent: topic_finder
action: "Find 3 AI topics"
output_pydantic: TopicList # Reference to class in tools.py
output_variable: topics# tools.py (in same directory)
from pydantic import BaseModel
from typing import List
class Topic(BaseModel):
title: str
url: str
class TopicList(BaseModel):
items: List[Topic]Repeat (Evaluator-Optimizer)
Repeat until a condition is met.
name: Repeat Workflow
agents:
writer:
name: Writer
role: Content Writer
goal: Write high-quality content
instructions: "Write and improve content"
evaluator:
name: Evaluator
role: Quality Checker
goal: Evaluate content quality
instructions: "Rate content 1-10. Say 'approved' if >= 8"
steps:
- agent: writer
action: "Write article about {{topic}}"
repeat:
until: "approved"
max_iterations: 3Include (Modular Recipes)
Include reusable recipe files in your workflow.
name: Parent Workflow
topic: "AI Code & Tools 2026"
variables:
today: "January 2026"
agents:
writer:
role: Content Writer
goal: Write content
instructions: "Write engaging content"
steps:
# Include a modular recipe - it receives parent's variables
- include: ai-topic-gatherer
# Or with explicit configuration
- include:
recipe: ai-research-pipeline
input: "{{previous_output}}"
# Continue with local steps
- agent: writer
action: "Write blog post about: {{previous_output}}"Variables
Define reusable variables for use throughout your workflow.
name: Variables Workflow
topic: "AI Development Tools" # Automatically added to variables
variables:
today: "January 2026"
max_results: 5
categories:
- "Coding Assistants"
- "Testing Tools"
- "Documentation"
agents:
researcher:
role: Research Analyst
goal: Research topics
instructions: "Research AI development tools"
steps:
# Use topic variable (from topic: field)
- agent: researcher
action: |
Today is: {{today}}
Research: {{topic}}
Find up to {{max_results}} results
# Loop over variable list
- agent: researcher
action: "Research {{item}}"
loop:
over: categoriesVariable Substitution Examples:
| Pattern | Description | Example |
|---|---|---|
{{input}} | Workflow input | "Process: {{input}}" |
{{topic}} | Topic field value | "Research {{topic}}" |
{{previous_output}} | Previous step result | "Expand on: {{previous_output}}" |
{{variable_name}} | Custom variable | "Date: {{today}}" |
{{item}} | Current loop item | "Process {{item}}" |
{{item.field}} | Field in loop item | "Title: {{item.title}}" |
Extended agents.yaml
Use workflow patterns in agents.yaml with process: workflow:
# agents.yaml with workflow patterns
framework: praisonai
process: workflow # Enables workflow mode
topic: "Research AI trends"
workflow:
planning: true
reasoning: true
verbose: true
agents: # Canonical: use 'agents' instead of 'roles'
classifier:
role: Request Classifier
instructions: # Canonical: use 'instructions' instead of 'backstory' "Classify requests into categories"
goal: Classify requests
researcher:
role: Research Analyst
instructions: # Canonical: use 'instructions' instead of 'backstory' "Expert researcher"
goal: Research topics
tools:
- tavily_search
steps:
- agent: classifier
action: "Classify: {{topic}}"
- name: routing
route:
technical: [tech_expert]
default: [researcher]
- name: parallel_research
parallel:
- agent: researcher
action: "Research market trends"
- agent: researcher
action: "Research competitors"Run with:
praisonai agents.yamlAuto-Generate Workflows
Generate workflows automatically from a topic description:
# Sequential workflow (default)
praisonai workflow auto "Research AI trends"
# Parallel workflow
praisonai workflow auto "Research AI trends" --pattern parallel
# Routing workflow
praisonai workflow auto "Build a chatbot" --pattern routing
# Specify output file
praisonai workflow auto "Research AI" --output my_workflow.yamlCLI Commands
| Command | Description |
|---|---|
praisonai workflow run <file.yaml> | Run a YAML workflow |
praisonai workflow run <file.yaml> --var key=value | Run with variables |
praisonai workflow validate <file.yaml> | Validate a workflow (checks nested route:/parallel:/loop:/repeat:/if: agent references) |
praisonai workflow template <name> | Create from template |
praisonai workflow auto "topic" | Auto-generate workflow |
praisonai workflow list | List workflows |
praisonai workflow help | Show help |
CLI Options
| Flag | Description |
|---|---|
--var key=value | Set variable for YAML workflows |
--var input=<value> | Reserved key. Overrides the YAML top-level input: field. An explicit empty --var input= is respected and does not fall back to the YAML default. See Workflow Input Resolution. |
--pattern <pattern> | Pattern for auto-generation (sequential, parallel, routing, loop) |
--output <file> | Output file for auto-generation |
--planning | Enable planning mode |
--reasoning | Enable reasoning mode |
--verbose | Enable verbose output |
--save | Save output to file |
Progress Indicators
When running workflows, you’ll see clear progress indicators:
Running YAML workflow: research.yaml
Workflow input: Research the latest AI development tools...
Workflow: Research Workflow
┏━━━━━━━━━━━┳━━━━━━━┓
┃ Property ┃ Value ┃
┡━━━━━━━━━━━╇━━━━━━━┩
│ Steps │ 3 │
│ Planning │ True │
│ Reasoning │ False │
└───────────┴───────┘
Executing workflow...
📋 Execution Plan: [plan description]
⚡ Running 2 steps in parallel...
✅ Parallel complete: 2 results
🔀 Routing to: technical
✅ AgentName: [output preview]
✅ Workflow completed successfully!When no input is resolved (no YAML input:, no --var input, no argument), the CLI prints a warning instead of the preview line:
Warning: no workflow input — {{input}} will be empty unless --var input=... is setDebug Mode
Enable debug logging to see detailed execution:
LOGLEVEL=debug praisonai workflow run research.yamlThis shows:
- Agent parameters (prompt, temperature, tools)
- Messages sent to LLM
- HTTP requests to API
- Full agent/role/goal context
Best Practices
Validate YAML before deploy
Run workflows locally with LOGLEVEL=debug to catch missing agents or tools early.
Use variable substitution for secrets
Reference env vars in YAML instead of hard-coding API keys in workflow files.
Keep workflows focused
Split large YAML files into modular recipes rather than one monolithic definition.
Version control workflow files
Treat YAML workflows like code — review changes and test in CI before production.