Task guardrails

CrewAI · Practical · 12 min

Guardrails catch bad outputs inside the run, before they reach users or the next task.

A guardrail checks a task's output before the task counts as done. If the check fails, the feedback goes back to the agent, which tries again, up to guardrail_max_retries times (default 3). If it still fails, the task raises an error.

Function guardrails are plain Python. The function receives the TaskOutput and returns a pair (a tuple):

  • (True, value) when the output is acceptable. The value becomes the task's result, so you can also clean it up here.
  • (False, "message") when it is not. Write the message as an instruction, because the agent reads it.
def under_200_words(output):
    n = len(output.raw.split())
    return (True, output.raw) if n <= 200 else (False, f"Too long ({n} words); cut to 200.")

Text guardrails are written in plain language, for example guardrail="Every claim must cite a source URL.". CrewAI asks the agent's LLM to judge the output against that rule. They are flexible but cost an extra model call and can be wrong, so prefer functions for anything you can check in code.

guardrails=[...] takes a list mixing both kinds. They run in order, each receiving the previous result. If guardrails is set, guardrail is ignored.

Good uses: length and format limits, required sections, banned words, checking that numbers add up, and verifying a structured output's values. Guardrails are one layer of safety, not all of it: still limit tools and permissions.

Example

from crewai import Agent, Crew, Task
from crewai.tasks.task_output import TaskOutput

def has_three_bullets(output: TaskOutput):
    bullets = [l for l in output.raw.splitlines() if l.strip().startswith("-")]
    if len(bullets) == 3:
        return (True, output.raw.strip())
    return (False, f"Found {len(bullets)} bullet lines; return exactly 3 lines starting with '- '.")

writer = Agent(role="Summarizer", goal="Summarize articles",
               backstory="You write tight bullet summaries.")
task = Task(
    description="Summarize this article: {text}",
    expected_output="Exactly 3 bullets starting with '- '.",
    agent=writer,
    guardrails=[has_three_bullets, "No bullet may contain opinions or speculation."],
    guardrail_max_retries=2,
)
result = Crew(agents=[writer], tasks=[task]).kickoff(inputs={"text": "..."})
print(result.raw)

Remember

  • Guardrails return (True, value) to accept or (False, feedback) to retry
  • Function guardrails are cheap and exact; text guardrails use an LLM judge
  • guardrail_max_retries (default 3) bounds the retries

Watch out for

  • Returning a bare boolean from a function guardrail instead of the (bool, value) tuple.

Check yourself

  1. A function guardrail returns (False, 'Add a conclusion section.'). What happens next?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.crewai.com/en/concepts/tasks