Conditional Execution

Unified condition syntax for controlling task and workflow execution

Conditional execution gates tasks and workflow steps on runtime values using one when syntax that works in both AgentFlow pipelines and Task teams.

python
from praisonaiagents import Agent, Task, when

agent = Agent(name="conditional", instructions="Run tasks only when conditions match.")
agent.start("Execute the follow-up step if the score is above 0.8.")

The user defines workflows; when expressions gate tasks and AgentFlow steps on runtime variables.

Diagram

Conditional Execution

True

False

📝 User Request

🤖 Agent

when condition

✅ then_task

❌ else_task

Overview

Conditional execution allows you to control workflow branching based on variables, scores, or other runtime values. PraisonAI supports:

  • String expression conditions – Simple {{variable}} syntax for comparisons
  • Dictionary routing – Map decision values to next tasks
  • Callable conditions – Custom Python functions

Quick Start

  1. Task or AgentFlow
    Task with when (Recommended)
    from praisonaiagents import Task
    
    # Simple condition with then/else routing
    task = Task(
        name="score_check",
        description="Check if score passes threshold",
        when="{{score}} > 80",
        then_task="approve",
        else_task="reject"
    )
    
    # Evaluate the condition
    result = task.evaluate_when({"score": 90})  # True
    next_task = task.get_next_task({"score": 90})  # "approve"
    AgentFlow with when()
    from praisonaiagents import AgentFlow, Agent, when
    
    agent = Agent(name="worker", instructions="Process data")
    
    flow = AgentFlow(
        agents=[agent],
        steps=[
            when(
                condition="{{score}} >= 50",
                then_steps=["high_score_handler"],
                else_steps=["low_score_handler"]
            )
        ],
        variables={"score": 75}
    )

Condition Syntax

String Expression Conditions

Use {{variable}} placeholders with comparison operators:

OperatorExampleDescription
>{{score}} > 80Greater than
>={{score}} >= 80Greater than or equal
<{{score}} < 50Less than
<={{score}} <= 50Less than or equal
=={{status}} == approvedEqual to
!={{status}} != rejectedNot equal to
in{{word}} in {{text}}Contains (substring)
contains{{list}} contains {{item}}Contains (list)

Examples

python
# Numeric comparisons
"{{score}} > 80"
"{{count}} >= 10"
"{{price}} < 100.50"

# String comparisons
"{{status}} == approved"
"{{category}} != spam"

# Contains checks
"{{text}} contains error"
"{{tags}} in important"

# Boolean checks
"{{is_valid}}"  # True if truthy

Task Condition Parameters

when Parameter

The when parameter accepts a string expression condition:

python
from praisonaiagents import Task

task = Task(
    name="quality_check",
    description="Check content quality",
    when="{{quality_score}} >= 7",
    then_task="publish",
    else_task="revise"
)

then_task and else_task

Route to different tasks based on condition result:

python
task = Task(
    name="review",
    description="Review submission",
    when="{{approved}} == true",
    then_task="finalize",    # Run if condition is True
    else_task="request_changes"  # Run if condition is False
)

routing Parameter (Advanced)

For LLM-driven decisions, use the routing parameter (formerly condition). Pass a bare dict, or the TaskRoutingConfig dataclass for clarity:

python
from praisonaiagents import Task
from praisonaiagents.workflows.workflow_configs import TaskRoutingConfig

# Dict form (still supported)
Task(
    name="decide",
    description="Decide next action based on content",
    routing={"approved": ["publish"], "rejected": ["edit"]},
)

# Dataclass form (recommended for clarity)
Task(
    name="decide",
    description="Decide next action based on content",
    routing=TaskRoutingConfig(
        branches={"approved": ["publish"], "rejected": ["edit"]},
        next_steps=["cleanup"],
    ),
)

Precedence Ladder

The routing parameter resolves in this order (only the last two forms are supported today):

python
# Dict (map decision value → next steps)
Task(routing={"approved": ["publish"], "rejected": ["edit"]})

# Config class (full control over branches + next_steps)
Task(routing=TaskRoutingConfig(
    branches={"approved": ["publish"]},
    next_steps=["cleanup"],
))

Precedence: Bool > String > Dict > Config — the markdown engine honours the Dict and Config forms.

End-to-End Markdown Example

A branch actually being taken in a markdown workflow:

classify_and_route.md
---
name: classify_and_route
---

## step1
Classify the request.

```branch
success: [handle_success]
failure: [handle_failure]
```

## handle_failure
Escalate.

## handle_success
Reply.
bash
praisonai workflow run .praisonai/workflows/classify_and_route.md

When step1‘s output contains success, execution jumps to handle_success, skipping handle_failure.

should_run Callable

For complex logic, use a callable:

python
def check_prerequisites(context):
    return context.get("data_ready", False) and context.get("approved", False)

task = Task(
    name="process",
    description="Process data",
    should_run=check_prerequisites
)

AgentFlow Conditions

when() Function

python
from praisonaiagents import when

flow = AgentFlow(
    steps=[
        "step1",
        when(
            condition="{{result}} == success",
            then_steps=["success_handler"],
            else_steps=["error_handler"]
        ),
        "final_step"
    ]
)

Nested Conditions

python
flow = AgentFlow(
    steps=[
        when(
            condition="{{score}} >= 80",
            then_steps=[
                when(
                    condition="{{premium}} == true",
                    then_steps=["premium_path"],
                    else_steps=["standard_path"]
                )
            ],
            else_steps=["low_score_path"]
        )
    ]
)

Multi-agent Workflows (PraisonAIAgents)

when/then_task/else_task also routes multi-agent workflows built with PraisonAIAgents.

python
from praisonaiagents import Agent, Task, PraisonAIAgents

reviewer  = Agent(name="reviewer",  instructions="Score the draft 0-100 as JSON: {\"score\": <int>}")
publisher = Agent(name="publisher", instructions="Publish the approved draft")
writer    = Agent(name="writer",    instructions="Revise the draft")

review  = Task(name="review",  description="Score the draft", agent=reviewer)
publish = Task(name="publish", description="Publish it",       agent=publisher)
revise  = Task(name="revise",  description="Revise the draft", agent=writer)

gate = Task(
    name="gate",
    description="Route based on score",
    agent=reviewer,
    when="{{score}} > 80",
    then_task="publish",
    else_task="revise",
)

PraisonAIAgents(
    agents=[reviewer, publisher, writer],
    tasks=[review, gate, publish, revise],
).start()

should_run under PraisonAIAgents

A should_run gate composes directly with PraisonAIAgents(...).start() — a falsy return skips the task.

python
from praisonaiagents import Agent, Task, PraisonAIAgents

reviewer  = Agent(name="reviewer",  instructions="Score the draft 0-100 as JSON: {\"score\": <int>}")
publisher = Agent(name="publisher", instructions="Publish the approved draft")

review = Task(name="review", description="Score the draft", agent=reviewer, output_variable="score")

publish = Task(
    name="publish",
    description="Publish it",
    agent=publisher,
    context=[review],
    should_run=lambda ctx: ctx.variables.get("score", 0) > 80,
)

PraisonAIAgents(
    agents=[reviewer, publisher],
    tasks=[review, publish],
).start()

An async def gate is awaited natively on the async path and resolved to completion on the sync path.

python
async def check(ctx):
    return ctx.variables.get("score", 0) > 80

publish = Task(name="publish", description="Publish it", agent=publisher, should_run=check)
Diagram
RevisePublishConditionProcessReviewerRevisePublishConditionProcessRevieweralt[score > 80][score <= 80]Result {score: 90}Build routing context (result.to_dict() + previous_output)evaluate_when(“{{score}} > 80”)then_taskRoute to publishelse_taskRoute to revise

Flow Diagram

Diagram

Condition Evaluation

True

False

Task with when condition

Evaluate Expression

then_task

else_task

How It Works

Diagram
ConditionAgentUserConditionAgentUseralt[condition is True][condition is False]Submit workflow with when conditionsEvaluate “{{score}} > 80”Route to then_taskRoute to else_taskWorkflow result

Best Practices

Use simple conditions

Keep conditions readable and simple. Complex logic should go in should_run callables.

python
# ✅ Good - Simple and clear
when="{{score}} > 80"

# ❌ Avoid - Too complex
when="{{score}} > 80 and {{status}} == approved and {{count}} < 10"
Provide both then_task and else_task

Specify both branches when you want an explicit fork:

python
# ✅ Both branches defined - explicit fork
Task(
    when="{{approved}}",
    then_task="proceed",
    else_task="wait"
)

# ✅ One-branch routing (early-exit) - intentional shape
# When the condition is false, the branch resolves to None and the
# workflow ends cleanly on this path instead of picking an unrelated task.
Task(
    when="{{approved}}",
    then_task="proceed"
)
Use routing for LLM decisions

When the LLM needs to make a decision, use routing with task_type="decision":

python
Task(
    name="classifier",
    description="Classify the input",
    task_type="decision",
    routing={
        "positive": ["positive_handler"],
        "negative": ["negative_handler"],
        "neutral": ["neutral_handler"]
    }
)
Keep condition keys lowercase and whitespace-free

The router normalises the model’s output with .lower().strip() before the lookup, so any key that carries uppercase letters, surrounding whitespace, or punctuation falls through to the silent exit branch.

python
# ✅ Do — lowercase, no whitespace, no punctuation
Task(
    name="judge",
    description="Reply with exactly one word: valid or invalid.",
    task_type="decision",
    routing={"valid": [], "invalid": ["rewrite"]},
)

# ❌ Don't — capitalised / padded / punctuated keys never match
Task(
    name="judge",
    description="Judge the draft.",
    task_type="decision",
    routing={"Valid": [], "Invalid.": ["rewrite"], " retry ": ["rewrite"]},
)

Migration Guide

From condition to routing

python
# Old syntax (still works)
Task(condition={"yes": ["next"], "no": ["stop"]})

# New syntax (recommended)
Task(routing={"yes": ["next"], "no": ["stop"]})

Adding when to existing Tasks

python
# Before - Using should_run callable
Task(
    should_run=lambda ctx: ctx.get("score", 0) > 80
)

# After - Using when expression (simpler)
Task(
    when="{{score}} > 80"
)

API Reference

Task Parameters

ParameterTypeDescription
whenstrString expression condition
then_taskstrTask name to run if condition is True
else_taskstrTask name to run if condition is False
routingDict[str, List[str]] \TaskRoutingConfigMap decision values to task names; TaskRoutingConfig unpacks to branch_condition / next_steps
should_runCallableCustom condition function

Task Methods

MethodReturnsDescription
evaluate_when(context)boolEvaluate the when condition
get_next_task(context)strGet next task based on condition

Related