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

All Integration Models

Understand how recipes are consumed

Use Cases

Learn from existing patterns

Decision Guide

Help users choose the right approach

App Developer Persona

Understand your users

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 ~/.praisonai/templates/support-reply-drafter
cd ~/.praisonai/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, AgentTeam

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 = AgentTeam(
        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 ~/.praisonai/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~/.praisonai/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
                       # Enable verbose output
    reflection=False,             # Enable self-reflection
    max_iterations=3,                  # Max reflection iterations
    min_iterations=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
)

# Agents - Orchestrator
from praisonaiagents import AgentTeam

agents = AgentTeam(
    agents=[agent1, agent2],        # List of agents
    tasks=[task1, task2],           # List of tasks
    process="sequential",           # "sequential" or "hierarchical"
                       # 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@example.com>"
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 ~/.praisonai/templates/
  3. Or publish to a recipe registry (future feature)

9. Troubleshooting Matrix

IssueCauseSolution
Recipe not discoveredWrong locationMove to ~/.praisonai/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, Agents
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