Implement validation and feedback for tasks using guardrails and decision-based workflows
Validate task output with guardrails — failed checks retry automatically with feedback about what to fix.
from praisonaiagents import Agent, Task, AgentTeamdef validate_word_count(output): words = len(str(output.raw).split()) if words >= 100: return True, output return False, f"Article has {words} words, expected at least 100"writer = Agent(name="Writer", instructions="Write clear articles")task = Task( description="Write a 500-word article about AI", agent=writer, guardrails=validate_word_count, max_retries=3,)AgentTeam(agents=[writer], tasks=[task]).start()
The user asks for output that must meet a guardrail; failed validation sends feedback into the next retry until the check passes or retries are exhausted.
The simplest way to add validation is using guardrails:
from praisonaiagents import AgentFlow, Task, WorkflowContext, StepResultfrom typing import Tuple, Any# Define validation functiondef validate_word_count(result: StepResult) -> Tuple[bool, str]: word_count = len(result.output.split()) if word_count >= 100: return (True, None) else: return (False, f"Article has {word_count} words, expected at least 100")# Step handlerdef write_article(ctx: WorkflowContext) -> StepResult: feedback = ctx.variables.get("validation_feedback", "") # In real usage, call an agent here return StepResult(output="AI is transforming industries..." * 20)# Workflow with guardrail validationworkflow = AgentFlow( steps=[ Task( name="write_article", handler=write_article, guardrails=validate_word_count, # Validation function max_retries=3 # Will retry up to 3 times if validation fails ) ])# Run the workflowresult = workflow.start("Write a 500-word article about AI")print(result["output"])
For complex validation that requires understanding:
writer = Agent( name="Writer", role="Content creator", goal="Write high-quality content", llm="gpt-4" # Required for LLM guardrails)task = Task( description="Write a technical blog post about quantum computing", expected_output="Technical blog post", agent=writer, guardrails="Validate that the blog post: 1) Is technically accurate, 2) Contains at least 3 code examples, 3) Has proper introduction and conclusion sections", max_retries=2)
from praisonaiagents import Agent, Task, AgentTeamimport json# Validation function for JSON datadef validate_json_schema(task_output) -> Tuple[bool, Any]: try: data = json.loads(task_output.raw) # Check required fields required_fields = ["name", "email", "age"] missing_fields = [f for f in required_fields if f not in data] if missing_fields: return False, f"Missing required fields: {', '.join(missing_fields)}" # Validate data types if not isinstance(data["age"], int) or data["age"] < 0: return False, "Age must be a positive integer" if "@" not in data["email"]: return False, "Invalid email format" return True, task_output except json.JSONDecodeError: return False, "Output is not valid JSON"# Create data processor agentprocessor = Agent( name="Data Processor", role="JSON data generator", goal="Generate valid user data in JSON format")# Task with validationgenerate_task = Task( description="Generate user data for John Doe, age 30, email john@example.com", expected_output="Valid JSON with name, email, and age fields", agent=processor, guardrails=validate_json_schema, max_retries=3)# Run pipelinepipeline = AgentTeam( agents=[processor], tasks=[generate_task])result = pipeline.start()
When validation fails, agents receive both typed outcomes and legacy feedback:Typed Outcome (Recommended):
# Access typed validation outcometask.validation_outcome = AgentRunOutcome( status="invalid_output", # Typed status for exhaustive matching error="Article word count is 450, expected 500", elapsed_s=1.2, agent_name="quality_checker", context={ "failed_criteria": ["word_count"], "suggestions": "Add 50 more words to meet requirement" })# Use typed status and helper methodsif task.validation_outcome.is_retryable(): schedule_retry(task.validation_outcome.error)
Legacy Dict Format (Backward Compatibility):
# Example of legacy validation feedback structure (still available)task.validation_feedback = { "validation_response": "retry", "validation_details": { "reason": "Article word count is 450, expected 500", "suggestions": "Add 50 more words to meet requirement", "failed_criteria": ["word_count"] }, "rejected_output": "The original article content...", "validator_task": "validate_article", "validated_task": "write_article", "retry_count": 1}# Available from typed outcome's to_dict() methodtyped_dict = task.validation_outcome.to_dict()
Define specific, measurable criteria and return actionable feedback strings when validation fails. Include examples of valid output in task descriptions.
Efficient validation
Use function guardrails for simple checks (length, format, required fields). Reserve LLM guardrails (string prompts) for subjective quality checks.
Set reasonable retry limits
Use max_retries=3 as a default. Increase only when feedback is precise enough for the agent to self-correct.
Fail fast on structural errors
Validate JSON schema, required sections, or word counts before expensive downstream tasks run.