CrewAI Multi-Agents

Course Content

CrewAI Multi-Agents

9 sections · 53 lessons

How do you implement review, critique, or validation agents?


The review loop for an escalation replyWriterdrafts the replyGuardrails:code check,then rule checkCritic returnspass or reviseRevise — atmost two roundsStillfailing:human queueThe refund-promise rule alone cut bad drafts from 11 percent to under 1.
Cheap guardrails catch what code can see; the critic is kept for tone and judgement, and the cap stops a loop that never ends.

What you need to know

Layer 1: guardrails on the task

A guardrail is a check that runs on a task's output before it is passed on. CrewAI supports two kinds, and you can mix them in a guardrails list:

Python
from typing import Anyfrom crewai import Task, TaskOutputdef has_ticket_id(result: TaskOutput) -> tuple[bool, Any]:    if "TKT-" not in result.raw:        return (False, "Reply must quote the ticket ID, e.g. TKT-48213.")    return (True, result.raw)reply = Task(    description="Draft a reply to the customer for ticket {ticket_id}",    expected_output="A polite reply under 150 words",    agent=support_writer,    guardrails=[        has_ticket_id,                                   # code check        "The reply must not promise a refund or a date",  # LLM check    ],    guardrail_max_retries=2,   # default is 3)

A function guardrail returns (True, value) to pass (the value can be a cleaned version) or (False, message) to fail. A string guardrail is checked by an LLM. On failure, the message goes back to the agent, which tries again with that feedback. This is cheaper and more precise than a whole critic agent.

Layer 2: a critic agent

Some checks need judgement: "Is the tone right for an angry premium customer?" For those, add a reviewer:

Python
class Review(BaseModel):    verdict: str        # "pass" or "revise"    issues: list[str]   # each quotes the problem textcritic = Agent(role="Support QA Reviewer",               goal="Find policy, tone and accuracy problems. Never rewrite.",               backstory="You quote the exact sentence that is wrong.",               tools=[], allow_delegation=False, llm="openai/gpt-4.1")review = Task(description="Review the draft reply against the refund policy.",              expected_output="Verdict and specific issues",              agent=critic, context=[reply], output_pydantic=Review)

Three rules make a critic useful:

  • A different agent, ideally a different model. A model that reviews its own text tends to agree with it.
  • Structured findings. "Looks good overall" cannot be acted on; a list of quoted issues can.
  • A cap on rounds. A critic asked to find problems always finds some. Allow at most two revision rounds, then send it to a person.

The revision loop

Loops belong in a Flow, where the counter is ordinary state:

Python
@router(review_draft)def decide(self):    if self.state.verdict == "pass":        return "send"    if self.state.rounds >= 2:        return "human"    self.state.rounds += 1    return "revise"

For sign-off by a person, current Flows have @human_feedback, which pauses the flow and routes on the reviewer's answer.

A real-life example

A telecom company runs a customer-support escalation crew for complaints that reach level 2. A writer agent drafts the reply; the reply goes out only after review.

In the first week, 11% of drafts promised "refund in 3 days", which is against policy. The team added the string guardrail "must not promise a refund or a date"; those promises dropped to under 1%, at the cost of about one retry per 12 tickets. The critic agent then catches tone problems, for example a cheerful reply to a customer whose service was down for 4 days. Drafts that fail review twice go to a human agent's queue, about 3% of tickets.

Follow-up questions to expect

  • "Guardrail or critic agent — which first?" — Guardrail first. It is cheaper, deterministic for code checks, and retries with a precise message. Add a critic only for problems code cannot see.
  • "What happens when retries run out?" — The task raises an error. Catch it in the Flow and route to a fallback or a person; never let a failed draft go out silently.
  • "Is max_retries still the setting?" — It is deprecated on Task; use guardrail_max_retries.