1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
35 small wins to finish your pathNext lesson →
Conditional tasks that run only sometimes
A ConditionalTask runs only when a function you write approves the previous task's output, and otherwise the crew skips it and finishes with what it has.
Last updated: 28 Sep, 2026 · CrewAI 1.15
The desk should email a customer about a missing order and say nothing extra when the order was found. The clerk's output decides which happens.
The ConditionalTask class
A ConditionalTask takes a condition: a function handed the previous task's output that returns True to run or False to skip.
def order_missing(output):
return "not an order we have" in output.raw # True runs the task, False skips it
apology = ConditionalTask(..., condition=order_missing)The condition and the task
The clerk and the look task are the desk's usual pair. The apology only runs when the clerk reports a missing order.
from crewai.tasks.conditional_task import ConditionalTask
def order_missing(output):
return "not an order we have" in output.raw
apology = ConditionalTask(
description="Tell the customer we cannot find the order in: {question}",
expected_output="A short, friendly email.",
agent=writer, condition=order_missing,
)
crew = Crew(agents=[clerk, writer], tasks=[look, apology], verbose=False)
Project files used on this pageThis lesson builds on a project from earlier lessons. The code below imports these files. Click a file to see its code, or follow the link to the lesson that wrote it. To run the code yourself, keep them in the same folder.
View the code here
tools.py
from crewai.tools import tool
ORDERS = {"A17": "shipped on 3 March", "C40": "waiting for stock"}
@tool
def lookup_order(order_id: str) -> str:
"""Look up an order's shipping status by its id, such as A17."""
status = ORDERS.get(order_id)
return f"{order_id} {status}." if status else f"{order_id} is not an order we have."
shop_llm.py
import json
import os
import re
from crewai import BaseLLM
os.environ["OTEL_SDK_DISABLED"] = "true"
os.environ["CREWAI_DISABLE_TELEMETRY"] = "true"
os.environ["CREWAI_TRACING_ENABLED"] = "false"
os.environ["CREWAI_DISABLE_VERSION_CHECK"] = "true"
class ShopLLM(BaseLLM):
script: list = []
def supports_function_calling(self):
return True
def call(self, messages, tools=None, **kwargs):
if isinstance(messages, str):
messages = [{"role": "user", "content": messages}]
if self.script:
return self.script.pop(0)
names = [t["function"]["name"] for t in tools or []]
return self.decide(messages, names)
def decide(self, messages, tools):
last = messages[-1]
if last["role"] == "tool":
return last["content"]
text = last["content"]
orders = re.findall(r"\b[A-Z]\d+\b", text)
wanted = "refund_order" if "refund" in text.lower() else "lookup_order"
matches = [name for name in tools if name.endswith(wanted)]
if orders and matches:
args = json.dumps({"order_id": orders[0]})
return [{"id": f"call_{orders[0]}", "type": "function",
"function": {"name": matches[0], "arguments": args}}]
if "working with:" in text:
context = text.split("working with:")[1].strip().split("\n\n")[0]
return f"Dear customer, {context}"
if orders:
return f"I have no way to look up {orders[0]} yet."
return "Hello. Which order is this about?"
One condition, two tickets
Run a found order and a missing one through the same crew.
import os
os.environ["OTEL_SDK_DISABLED"] = "true"
os.environ["CREWAI_DISABLE_TELEMETRY"] = "true"
from crewai import Agent, Crew, Task
from shop_llm import ShopLLM
from tools import lookup_order
clerk = Agent(role="Order clerk", goal="Find the status of customers' orders",
backstory="You can look up any order in the shop's system.",
llm=ShopLLM(model="shop"), tools=[lookup_order])
writer = Agent(role="Reply writer", goal="Write replies to customers",
backstory="You write short, friendly emails.",
llm=ShopLLM(model="shop"))
look = Task(description="Find the order in this message: {question}",
expected_output="The order's status.", agent=clerk)
from crewai.tasks.conditional_task import ConditionalTask
def order_missing(output):
return "not an order we have" in output.raw
apology = ConditionalTask(
description="Tell the customer we cannot find the order in: {question}",
expected_output="A short, friendly email.",
agent=writer, condition=order_missing,
)
crew = Crew(agents=[clerk, writer], tasks=[look, apology], verbose=False)
for question in ["Where is my order A17?", "Where is my order B22?"]:
result = crew.kickoff(inputs={"question": question})
print([output.raw for output in result.tasks_output])Output
['A17 shipped on 3 March.', ''] ['B22 is not an order we have.', 'Dear customer, B22 is not an order we have.']
What each ticket produced
- For A17 the condition was False, the apology was skipped, and its output is an empty string.
- For B22 the condition held, so the writer ran with the clerk's finding as context.
- A skipped task produces empty output, and the crew finishes with the last output it has.
Conditional vs plain task
| Plain Task | ConditionalTask | |
|---|---|---|
| When it runs | Always, in list order | Only when its condition is True |
| Skipped output | Not applicable | An empty string |
| Decides on | Nothing | The previous task's output |
When to skip a task
- A step that only makes sense sometimes: apologise only when the order is missing.
- Branching a pipeline on a value a Python function can read from the last output.
Watch out. A
ConditionalTask reads the task immediately before it, so it cannot come first in the list, and its condition sees output.raw, the text, not a typed object.Related
- Previous: Guardrails: checking a task's answer
- Next: Delegation between agents
- Reference: Conditional tasks
Try it yourself
- Make the condition also catch messages with no order id at all.
- Print
result.rawfor both questions. - Put the conditional task first in the list and read the error.
You understood something today that you didn't yesterday.