Skip to main content

Recipe Author Persona

Role: Design, build, test, and document recipes that solve real-world problems for App Developers and end users.

Primary Goals

  • Create effective recipes that solve specific problems
  • Design clear interfaces with well-defined inputs/outputs
  • Write comprehensive tests for reliability
  • Document thoroughly for easy adoption

Typical Workflow

1

Define the Problem

# Start with a clear problem statement
# What task does this recipe automate?
# Who is the target user?
# What are the expected inputs and outputs?

# Example: Support Reply Drafter
# Problem: Support agents spend too much time drafting replies
# User: Support team members
# Input: Ticket ID, customer message
# Output: Draft reply text
2

Create Recipe Structure

# Initialize recipe directory
mkdir -p ~/.praison/templates/support-reply-drafter
cd ~/.praison/templates/support-reply-drafter
# TEMPLATE.yaml
name: support-reply-drafter
version: "1.0.0"
description: "Generate professional support reply drafts"
author: "Your Name"
license: "MIT"

tags:
  - support
  - customer-service
  - text-generation

requires:
  env:
    - OPENAI_API_KEY
  packages:
    - praisonaiagents

inputs:
  ticket_id:
    type: string
    description: "Support ticket identifier"
    required: true
  message:
    type: string
    description: "Customer message to respond to"
    required: true
  tone:
    type: string
    description: "Response tone"
    default: "professional"
    enum: ["professional", "friendly", "formal"]

outputs:
  reply:
    type: string
    description: "Generated reply draft"
  confidence:
    type: number
    description: "Confidence score (0-1)"

cli:
  command: "praisonai recipe run support-reply-drafter"
  examples:
    - 'praisonai recipe run support-reply-drafter --input ''{"ticket_id": "T-123", "message": "I need help"}'''

safety:
  dry_run_default: false
  requires_consent: false
3

Implement the Recipe

# recipe.py
from praisonaiagents import Agent, Task, PraisonAIAgents

def run(input_data: dict, config: dict = None) -> dict:
    """
    Generate a support reply draft.
    
    Args:
        input_data: Contains ticket_id, message, and optional tone
        config: Optional configuration overrides
        
    Returns:
        Dict with reply and confidence score
    """
    ticket_id = input_data.get("ticket_id")
    message = input_data.get("message")
    tone = input_data.get("tone", "professional")
    
    # Create support agent
    support_agent = Agent(
        name="Support Specialist",
        role="Customer Support Expert",
        goal=f"Draft a {tone} reply to customer inquiries",
        instructions="""
        You are an expert customer support specialist.
        - Be helpful and empathetic
        - Address the customer's concern directly
        - Provide clear next steps if applicable
        - Keep responses concise but complete
        """,
    )
    
    # Create task
    task = Task(
        name="draft_reply",
        description=f"""
        Draft a {tone} reply to this customer message:
        
        Ticket: {ticket_id}
        Message: {message}
        
        Provide a professional, helpful response.
        """,
        expected_output="A well-crafted support reply",
        agent=support_agent,
    )
    
    # Execute
    agents = PraisonAIAgents(
        agents=[support_agent],
        tasks=[task],
    )
    
    result = agents.start()
    
    return {
        "reply": result.get("task_output", ""),
        "confidence": 0.85,  # Could be calculated from model response
    }
4

Write Tests

# test_recipe.py
import pytest
from recipe import run

def test_basic_reply():
    """Test basic reply generation."""
    result = run({
        "ticket_id": "T-123",
        "message": "I can't log into my account",
    })
    
    assert "reply" in result
    assert len(result["reply"]) > 50
    assert result["confidence"] > 0.5

def test_tone_professional():
    """Test professional tone."""
    result = run({
        "ticket_id": "T-124",
        "message": "Your product is broken!",
        "tone": "professional",
    })
    
    assert "reply" in result
    # Should not contain informal language
    assert "hey" not in result["reply"].lower()

def test_tone_friendly():
    """Test friendly tone."""
    result = run({
        "ticket_id": "T-125",
        "message": "How do I reset my password?",
        "tone": "friendly",
    })
    
    assert "reply" in result

def test_missing_required_field():
    """Test error handling for missing fields."""
    with pytest.raises(ValueError):
        run({"ticket_id": "T-126"})  # Missing message

@pytest.mark.integration
def test_end_to_end():
    """Full integration test."""
    from praisonai import recipe
    
    result = recipe.run(
        "support-reply-drafter",
        input={
            "ticket_id": "T-127",
            "message": "I need a refund",
        }
    )
    
    assert result.ok
    assert result.output.get("reply")
5

Document the Recipe

Create a README.md with:
  • Title and description - What the recipe does
  • Quick start - Minimal example to get started
  • Inputs table - All parameters with types and descriptions
  • Outputs table - What the recipe returns
  • Examples - Multiple usage scenarios
  • Requirements - Dependencies and environment variables
  • Troubleshooting - Common issues and solutions
Example README structure:
# Recipe Name

Description of what the recipe does.

## Quick Start
praisonai recipe run my-recipe --input '{"key": "value"}'

## Inputs
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| field1 | string | Yes | Description |

## Outputs
| Field | Type | Description |
|-------|------|-------------|
| output1 | string | Description |

## Examples
[Code examples for different use cases]

## Requirements
- OPENAI_API_KEY environment variable
- praisonaiagents package

## Troubleshooting
Common issues and solutions

Key Concerns

Interface Design

# Good: Clear, typed inputs with defaults
inputs:
  query:
    type: string
    required: true
    description: "Search query"
  max_results:
    type: integer
    default: 10
    minimum: 1
    maximum: 100
    description: "Maximum results to return"

# Bad: Vague, untyped inputs
inputs:
  data:
    description: "Input data"

Error Handling

def run(input_data: dict, config: dict = None) -> dict:
    """Recipe with proper error handling."""
    
    # Validate required inputs
    if not input_data.get("query"):
        raise ValueError("query is required")
    
    try:
        # Main logic
        result = process(input_data)
        return {"output": result, "ok": True}
        
    except RateLimitError:
        return {"error": "Rate limit exceeded, try again later", "ok": False}
    except InvalidInputError as e:
        return {"error": f"Invalid input: {e}", "ok": False}
    except Exception as e:
        # Log for debugging but don't expose internals
        logger.error(f"Recipe failed: {e}")
        return {"error": "An unexpected error occurred", "ok": False}

Testing Strategy

# Unit tests - fast, isolated
def test_input_validation():
    with pytest.raises(ValueError):
        run({})

# Integration tests - with real APIs
@pytest.mark.integration
def test_with_real_api():
    result = run({"query": "test"})
    assert result["ok"]

# Snapshot tests - for output consistency
def test_output_format(snapshot):
    result = run({"query": "hello"})
    assert result == snapshot

Best Practices

Recipe Naming

✅ Good names:
- support-reply-drafter
- code-review-assistant
- document-summarizer

❌ Bad names:
- my-recipe
- test123
- agent

Version Management

# Use semantic versioning
version: "1.2.3"
# 1 = major (breaking changes)
# 2 = minor (new features)
# 3 = patch (bug fixes)

Dependency Management

requires:
  packages:
    - praisonaiagents>=0.5.0  # Specify minimum versions
  env:
    - OPENAI_API_KEY  # Required
  optional_env:
    - LANGCHAIN_API_KEY  # Optional enhancement

Troubleshooting

Check recipe location:
# Recipes are discovered from:
ls ~/.praison/templates/
ls ~/.config/praison/templates/
ls ./.praison/templates/
  • Ensure API keys are set in CI secrets
  • Use mocks for unit tests
  • Mark integration tests appropriately
  • Add temperature control to prompts
  • Implement output validation
  • Add retry logic for edge cases

Handoff Checklist

Before releasing a recipe:
  • TEMPLATE.yaml is complete and valid
  • All inputs have descriptions and types
  • All outputs are documented
  • README.md with examples
  • Unit tests pass
  • Integration tests pass
  • Tested with all integration models (SDK, CLI, HTTP)
  • Error messages are user-friendly
  • Dependencies are specified with versions
  • License is specified

Next Steps


SDK & Recipe Author Reference (Copy/Paste for AI)

This section is designed to be copied wholesale and given to an AI assistant. It contains everything needed to build a complete, valid PraisonAI recipe from scratch.

1. Recipe Discovery

Recipes are discovered from these directories in order of precedence (first match wins):
PriorityPathDescription
1~/.praison/templates/User-local recipes
2~/.config/praison/templates/XDG config location
3./.praison/templates/Project-local recipes
4/path/to/Agent-Recipes/agent_recipes/templates/Built-in recipes
Source: praisonai/cli/features/recipes.pyRECIPE_PATHS, find_recipe_paths()

2. Recipe Directory Structure

my-recipe/
├── TEMPLATE.yaml      # Required: Recipe metadata and configuration
├── README.md          # Required: Documentation
├── recipe.py          # Required: Main entrypoint with run() function
├── test_recipe.py     # Recommended: Tests
└── requirements.txt   # Optional: Additional dependencies

3. TEMPLATE.yaml Complete Schema

# ═══════════════════════════════════════════════════════════════════════════
# TEMPLATE.yaml - Complete Schema Reference
# ═══════════════════════════════════════════════════════════════════════════

# ─────────────────────────────────────────────────────────────────────────────
# METADATA (Required)
# ─────────────────────────────────────────────────────────────────────────────
name: my-recipe-name              # Required: Unique identifier (kebab-case)
version: "1.0.0"                  # Required: Semantic version string
description: "Short description"  # Required: One-line description
author: "Your Name"               # Required: Author name or handle
license: "MIT"                    # Required: SPDX license identifier

# ─────────────────────────────────────────────────────────────────────────────
# TAGS (Required)
# ─────────────────────────────────────────────────────────────────────────────
tags:                             # Required: At least one tag
  - video                         # Category tags for discovery
  - audio
  - transcription

# ─────────────────────────────────────────────────────────────────────────────
# REQUIREMENTS (Required)
# ─────────────────────────────────────────────────────────────────────────────
requires:
  # Environment variables (checked at runtime)
  env:                            # List of required env vars
    - OPENAI_API_KEY
    - ANTHROPIC_API_KEY
  
  # Optional environment variables (not blocking)
  optional_env:                   # List of optional env vars
    - LANGCHAIN_API_KEY
  
  # Python packages (checked via import)
  packages:                       # List of pip packages
    - praisonaiagents
    - openai>=1.0.0
    - pandas
  
  # External system tools (checked via shutil.which)
  external:                       # List of CLI tools
    - ffmpeg
    - tesseract
    - pandoc
  
  # Recipe tools from praisonai-tools
  tools:                          # List of tool module names
    - llm_tool
    - vision_tool
    - whisper_tool
    - media_tool
    - doc_tool
    - image_tool
    - data_tool
    - chart_tool
    - email_tool

# ─────────────────────────────────────────────────────────────────────────────
# INPUTS (Required)
# ─────────────────────────────────────────────────────────────────────────────
inputs:
  input_field_name:               # Field name (snake_case)
    type: string                  # Type: string, integer, number, boolean, array, object
    description: "Field desc"     # Human-readable description
    required: true                # Whether field is required (default: false)
    default: "default_value"      # Default value if not provided
    enum:                         # Allowed values (optional)
      - option1
      - option2
    minimum: 1                    # For integer/number: minimum value
    maximum: 100                  # For integer/number: maximum value
    pattern: "^[a-z]+$"           # For string: regex pattern

# ─────────────────────────────────────────────────────────────────────────────
# OUTPUTS (Required)
# ─────────────────────────────────────────────────────────────────────────────
outputs:
  output_field_name:              # Field name (snake_case)
    type: string                  # Type: string, integer, number, boolean, array, object
    description: "Field desc"     # Human-readable description

# ─────────────────────────────────────────────────────────────────────────────
# CLI CONFIGURATION (Required)
# ─────────────────────────────────────────────────────────────────────────────
cli:
  command: "praison recipes run my-recipe-name"  # Full command
  examples:                       # List of example invocations
    - 'praison recipes run my-recipe-name --input ''{"field": "value"}'''
    - 'praison recipes run my-recipe-name input.json --output ./out/'
    - 'praison recipes run my-recipe-name --dry-run'

# ─────────────────────────────────────────────────────────────────────────────
# SAFETY CONFIGURATION (Required)
# ─────────────────────────────────────────────────────────────────────────────
safety:
  dry_run_default: false          # If true, --write flag required to execute
  requires_consent: false         # If true, --consent flag required
  consent_message: ""             # Message shown when consent required
  legal_disclaimer: ""            # Legal text shown before execution
  overwrites_files: true          # Whether recipe may overwrite files
  network_access: true            # Whether recipe accesses network
  pii_handling: false             # Whether recipe handles PII data

4. Runtime Contract

Required Entrypoint

Every recipe must have a recipe.py with this signature:
def run(input_data: dict, config: dict = None) -> dict:
    """
    Execute the recipe.
    
    Args:
        input_data: Dictionary matching the inputs schema from TEMPLATE.yaml
        config: Optional configuration overrides
        
    Returns:
        Dictionary matching the outputs schema from TEMPLATE.yaml
        Must include 'ok' boolean for success/failure indication
    """
    pass

Standard Output Contract

# Success response
{
    "ok": True,
    "run_id": "run_abc123",           # Unique run identifier
    "recipe": "my-recipe-name",       # Recipe name
    "output": {                       # Tool-specific output
        "field1": "value1",
        "field2": 123
    },
    "artifacts": [                    # Generated files
        {"path": "output/file.txt", "type": "text", "size_bytes": 1024}
    ],
    "warnings": [],                   # Non-fatal warnings
    "error": None
}

# Error response
{
    "ok": False,
    "run_id": "run_abc123",
    "recipe": "my-recipe-name",
    "output": None,
    "artifacts": [],
    "warnings": [],
    "error": {
        "code": "MISSING_DEPENDENCY",
        "message": "ffmpeg not found",
        "details": "Install with: brew install ffmpeg",
        "retriable": False
    }
}

5. SDK Reference for Recipe Authors

Core Imports from praisonaiagents

# Agent - The primary agent class
from praisonaiagents import Agent

Agent(
    name="Agent Name",              # Required: Display name
    role="Role Description",        # Agent's role
    goal="What agent aims to do",   # Agent's goal
    instructions="...",             # System instructions
    llm="gpt-4o-mini",              # LLM model to use
    tools=[],                       # List of tools
    verbose=True,                   # Enable verbose output
    self_reflect=False,             # Enable self-reflection
    max_reflect=3,                  # Max reflection iterations
    min_reflect=1,                  # Min reflection iterations
)

# Task - Define work for agents
from praisonaiagents import Task

Task(
    name="task_name",               # Task identifier
    description="What to do",       # Task description
    expected_output="Expected",     # What output should look like
    agent=agent,                    # Agent to execute task
    tools=[],                       # Task-specific tools
    context=[],                     # Context from other tasks
    async_execution=False,          # Run asynchronously
    output_file="output.txt",       # Save output to file
    output_json=MyModel,            # Pydantic model for output
)

# PraisonAIAgents - Orchestrator
from praisonaiagents import PraisonAIAgents

agents = PraisonAIAgents(
    agents=[agent1, agent2],        # List of agents
    tasks=[task1, task2],           # List of tasks
    process="sequential",           # "sequential" or "hierarchical"
    verbose=True,                   # Enable verbose output
)
result = agents.start()             # Execute all tasks

# Tool decorator
from praisonaiagents import tool

@tool
def my_tool(param: str) -> str:
    """Tool description for LLM."""
    return f"Result: {param}"

Recipe Tools from praisonai-tools

# Install: pip install praisonai-tools

# LLM Tool - Unified LLM interface
from praisonai_tools.recipe_tools import LLMTool, llm_complete

llm = LLMTool(provider="openai", model="gpt-4o-mini")
response = llm.complete("prompt", system="system prompt", max_tokens=1000)
# response.content, response.model, response.usage

# Vision Tool - Image analysis
from praisonai_tools.recipe_tools import VisionTool, vision_caption

vision = VisionTool(provider="openai")
result = vision.analyze("image.jpg", prompt="Describe this")
# result.description, result.tags

# Chart Tool - Visualization
from praisonai_tools.recipe_tools import ChartTool, chart_bar

chart = ChartTool()
result = chart.bar({"A": 10, "B": 20}, title="Chart", output="chart.png")
# result.path

# Media Tool - Audio/Video
from praisonai_tools.recipe_tools import MediaTool, media_probe

media = MediaTool()
info = media.probe("video.mp4")
# info.duration, info.format, info.streams

# Whisper Tool - Transcription
from praisonai_tools.recipe_tools import WhisperTool, whisper_transcribe

whisper = WhisperTool()
transcript = whisper.transcribe("audio.mp3", language="en")
# transcript.text, transcript.segments

# Doc Tool - Document processing
from praisonai_tools.recipe_tools import DocTool, doc_extract_text

doc = DocTool()
text = doc.extract_text("document.pdf")

# Email Tool - Email parsing
from praisonai_tools.recipe_tools import EmailTool, email_parse

email = EmailTool()
parsed = email.parse("message.eml")
# parsed.sender, parsed.subject, parsed.body

# Data Tool - Data processing
from praisonai_tools.recipe_tools import DataTool, data_profile

data = DataTool()
profile = data.profile("data.csv")
# profile.columns, profile.row_count

# Check dependencies
deps = llm.check_dependencies()
# {"openai": True, "api_key": True}

6. CLI Reference

All Commands

# List all recipes
praison recipes list
praison recipes list --json
praison recipes list --group

# Show recipe details
praison recipes info <recipe-name>
praison recipes info <recipe-name> --json

# Check dependencies
praison recipes doctor <recipe-name>

# Run a recipe
praison recipes run <recipe-name> <input>
praison recipes run <recipe-name> --input '{"key": "value"}'
praison recipes run <recipe-name> --input-file input.json
praison recipes run <recipe-name> --output ./output/
praison recipes run <recipe-name> --dry-run
praison recipes run <recipe-name> --write          # For dry-run-default recipes
praison recipes run <recipe-name> --consent        # For consent-required recipes
praison recipes run <recipe-name> --force          # Ignore missing deps
praison recipes run <recipe-name> --skip-checks    # Skip all checks
praison recipes run <recipe-name> --json           # JSON output

# Explain execution plan
praison recipes explain <recipe-name>

# Initialize recipe locally
praison recipes init <recipe-name>
praison recipes init <recipe-name> --output ./my-copy/
praison recipes init <recipe-name> --force
praison recipes init <recipe-name> --dry-run

CLI Flags Reference

FlagCommandsDescription
--jsonlist, info, runOutput as JSON
--grouplistGroup recipes by category
--inputrunJSON input string
--input-filerunPath to JSON input file
--output, -orun, initOutput directory
--dry-runrun, initShow what would be done
--writerunExecute dry-run-default recipes
--consentrunAcknowledge consent requirements
--forcerun, initForce execution/overwrite
--skip-checksrunSkip dependency checks

7. Testing Patterns

# test_recipe.py
import pytest
from recipe import run

# Unit test - fast, no API calls
def test_input_validation():
    """Test that missing required fields raise errors."""
    with pytest.raises(ValueError):
        run({})

def test_output_structure():
    """Test output has required fields."""
    result = run({"input": "test"})
    assert "ok" in result
    assert "output" in result

# Integration test - with real APIs
@pytest.mark.integration
def test_with_real_api():
    """Test with actual API calls."""
    result = run({"input": "real data"})
    assert result["ok"] is True

# Mark slow tests
@pytest.mark.slow
def test_large_file_processing():
    """Test with large files."""
    pass

# Run tests
# pytest test_recipe.py                    # Unit tests only
# pytest test_recipe.py -m integration     # Integration tests
# pytest test_recipe.py -m "not slow"      # Skip slow tests

8. Distribution & Packaging

# TEMPLATE.yaml metadata for distribution
name: my-recipe
version: "1.0.0"
author: "Your Name <[email protected]>"
license: "MIT"                    # SPDX identifier
repository: "https://github.com/user/repo"
homepage: "https://example.com"
keywords:
  - ai
  - automation
To share a recipe:
  1. Create a Git repository with the recipe directory structure
  2. Users clone/copy to ~/.praison/templates/
  3. Or publish to a recipe registry (future feature)

9. Troubleshooting Matrix

IssueCauseSolution
Recipe not discoveredWrong locationMove to ~/.praison/templates/
Recipe not discoveredMissing TEMPLATE.yamlCreate valid TEMPLATE.yaml
Missing dependencyPackage not installedpip install <package>
Missing dependencyExternal tool missingInstall system tool (ffmpeg, tesseract)
Env var missingNot setexport VAR_NAME=value
Tool import failurepraisonai-tools not installedpip install praisonai-tools
Runtime errorInvalid inputCheck input against schema
Dry-run modedry_run_default=trueUse --write flag
Consent requiredrequires_consent=trueUse --consent flag

10. AI Recipe Author Prompt

Use this prompt to instruct an AI to create a valid recipe:
You are creating a PraisonAI recipe. Follow these rules exactly:

1. Create a directory with the recipe name (kebab-case)
2. Create TEMPLATE.yaml with ALL required fields:
   - name, version, description, author, license
   - tags (at least one)
   - requires (env, packages, tools as needed)
   - inputs (with type, description, required for each)
   - outputs (with type, description for each)
   - cli (command and examples)
   - safety (dry_run_default, requires_consent, overwrites_files)

3. Create recipe.py with:
   - def run(input_data: dict, config: dict = None) -> dict
   - Validate all required inputs
   - Return dict with 'ok', 'output', 'artifacts', 'warnings', 'error'
   - Handle errors gracefully

4. Create README.md with:
   - Title and description
   - Quick start example
   - Inputs table
   - Outputs table
   - Requirements
   - Troubleshooting

5. Create test_recipe.py with:
   - Unit tests for input validation
   - Integration tests marked with @pytest.mark.integration

6. Use praisonaiagents for Agent, Task, PraisonAIAgents
7. Use praisonai_tools.recipe_tools for LLMTool, VisionTool, etc.
8. Always check dependencies before using tools
9. Use semantic versioning for version field
10. Include proper error handling and logging

Source Files Reference:
  • CLI: praisonai/cli/features/recipes.py
  • Tools: praisonai_tools/recipe_tools/__init__.py
  • Core SDK: praisonaiagents/__init__.py
  • Templates: Agent-Recipes/agent_recipes/templates/*/TEMPLATE.yaml