Approval System

The approval system provides human-in-the-loop oversight for potentially dangerous operations, ensuring safety and compliance in agent operations.

Overview

The approval system provides:

  • Risk-based categorisation of tools and operations
  • Customisable approval workflows for different risk levels
  • Human oversight for dangerous operations
  • Audit trail of approval decisions

Risk Levels

Critical Risk

Operations that could cause severe damage or security breaches

High Risk

Operations with significant potential impact

Medium Risk

Operations requiring caution

Low Risk

Operations with minimal risk

Quick Start

from praisonaiagents import Agent
from praisonaiagents.approval import require_approval, set_approval_callback

# Create a tool requiring approval
@require_approval(risk_level="high")
def delete_file(filepath: str):
    """Delete a file from the system"""
    import os
    os.remove(filepath)
    return f"Deleted {filepath}"

# Create agent with the tool
agent = Agent(
    name="File Manager",
    tools=[delete_file]
)

# Tool will request approval before execution
agent.start("Delete the file config.old")

Default Dangerous Tools

The following tools are pre-configured as dangerous:

# System-level operations
- subprocess_run
- execute_command
- run_shell_command

# Code execution
- exec
- eval
- python_repl_tool

Console Approval Interface

The default console approval interface:

🛡️ APPROVAL REQUIRED - HIGH RISK OPERATION
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Function: delete_file
Risk Level: HIGH

Arguments:
  filepath: /home/user/important.doc

Description: This operation will permanently delete the specified file.

Options:
[A]pprove  [D]eny  [M]odify arguments  [?]Help

Your choice:

Creating Approval-Required Tools

Using Decorators

from praisonaiagents.approval import require_approval

@require_approval(risk_level="critical")
def execute_sql(query: str, database: str):
    """Execute SQL query on database"""
    # Implementation
    pass

@require_approval(risk_level="medium")
def send_email(to: str, subject: str, body: str):
    """Send email to recipient"""
    # Implementation
    pass

Dynamic Approval Requirements

from praisonaiagents.approval import add_approval_requirement

# Add approval to existing function
add_approval_requirement(
    function=existing_function,
    risk_level="high"
)

# Add approval by function name
add_approval_requirement(
    function_name="dangerous_operation",
    risk_level="critical"
)

Approval Callbacks

Basic Callback Structure

def approval_callback(function_name: str, arguments: dict, risk_level: str):
    """
    Returns:
        - ApprovalDecision(approved=True/False, modified_args=dict)
        - None to use default console approval
    """
    pass

Advanced Callback Examples

from praisonaiagents.approval import ApprovalDecision
import re

class ApprovalPolicy:
    def __init__(self):
        self.policies = {
            "file_operations": {
                "allowed_paths": ["/tmp/", "/var/log/"],
                "forbidden_extensions": [".exe", ".dll", ".so"]
            },
            "network_operations": {
                "allowed_domains": ["api.company.com", "*.trusted.com"],
                "forbidden_ports": [22, 3389]
            }
        }
    
    def check_file_operation(self, function_name, arguments):
        filepath = arguments.get("filepath", "")
        
        # Check allowed paths
        if any(filepath.startswith(path) for path in self.policies["file_operations"]["allowed_paths"]):
            return ApprovalDecision(approved=True)
        
        # Check forbidden extensions
        if any(filepath.endswith(ext) for ext in self.policies["file_operations"]["forbidden_extensions"]):
            return ApprovalDecision(
                approved=False,
                reason="Forbidden file type"
            )
        
        return None  # Require manual approval
    
    def __call__(self, function_name, arguments, risk_level):
        if "file" in function_name:
            return self.check_file_operation(function_name, arguments)
        return None

# Set policy-based approval
policy = ApprovalPolicy()
set_approval_callback(policy)

Integration Patterns

With Agents

from praisonaiagents import Agent
from praisonaiagents.approval import require_approval

# Define dangerous tools
@require_approval(risk_level="high")
def modify_database(table: str, operation: str):
    # Database modification logic
    pass

@require_approval(risk_level="critical")
def system_command(command: str):
    # System command execution
    pass

# Create agent with approval-required tools
agent = Agent(
    name="System Administrator",
    instructions="You help with system administration tasks.",
    tools=[modify_database, system_command]
)

# Tools will request approval when used
response = agent.start("Delete old logs from the database")

With Custom Tools

from praisonaiagents.tools import Tool
from praisonaiagents.approval import require_approval

class FileManager(Tool):
    @require_approval(risk_level="medium")
    def delete_files(self, pattern: str):
        """Delete files matching pattern"""
        import glob
        import os
        
        files = glob.glob(pattern)
        for file in files:
            os.remove(file)
        return f"Deleted {len(files)} files"
    
    @require_approval(risk_level="high")
    def modify_permissions(self, path: str, mode: int):
        """Change file permissions"""
        import os
        os.chmod(path, mode)
        return f"Changed permissions for {path}"

Best Practices

Risk Assessment

  • Properly categorise risk levels
  • Consider worst-case scenarios
  • Document risk rationale
  • Regular risk reviews

User Experience

  • Clear approval prompts
  • Provide context for decisions
  • Allow argument modification
  • Minimise approval fatigue

Security

  • Never auto-approve critical operations
  • Validate modified arguments
  • Log all approval decisions
  • Implement approval timeouts

Implementation

  • Test approval workflows
  • Handle approval denials gracefully
  • Provide fallback options
  • Document approval requirements

Complete Example

from praisonaiagents import Agent, Task, PraisonAIAgents
from praisonaiagents.approval import require_approval, set_approval_callback, ApprovalDecision
import logging

# Configure logging
logging.basicConfig(level=logging.INFO)

# Define approval-required tools
@require_approval(risk_level="low")
def check_disk_space(path: str = "/"):
    import shutil
    stats = shutil.disk_usage(path)
    return f"Free space: {stats.free / (1024**3):.2f} GB"

@require_approval(risk_level="medium")
def clean_temp_files(directory: str = "/tmp"):
    import os
    import glob
    
    files = glob.glob(f"{directory}/*")
    size = sum(os.path.getsize(f) for f in files if os.path.isfile(f))
    
    for file in files:
        if os.path.isfile(file):
            os.remove(file)
    
    return f"Cleaned {len(files)} files ({size / 1024**2:.2f} MB)"

@require_approval(risk_level="high")
def restart_service(service_name: str):
    import subprocess
    
    result = subprocess.run(
        ["systemctl", "restart", service_name],
        capture_output=True,
        text=True
    )
    
    if result.returncode == 0:
        return f"Successfully restarted {service_name}"
    else:
        return f"Failed to restart {service_name}: {result.stderr}"

# Custom approval logic
def smart_approval(function_name, arguments, risk_level):
    # Auto-approve disk checks
    if function_name == "check_disk_space":
        return ApprovalDecision(approved=True)
    
    # Approve temp cleaning with restrictions
    if function_name == "clean_temp_files":
        directory = arguments.get("directory", "")
        if directory.startswith("/tmp"):
            logging.info(f"Auto-approved temp cleaning: {directory}")
            return ApprovalDecision(approved=True)
        else:
            logging.warning(f"Manual approval required for: {directory}")
            return None
    
    # Always require manual approval for service restarts
    if function_name == "restart_service":
        service = arguments.get("service_name", "")
        if service in ["nginx", "apache2"]:
            # These are considered safer
            return None  # Still require manual approval
        else:
            # Deny potentially dangerous services
            return ApprovalDecision(
                approved=False,
                reason=f"Service {service} is not in allowed list"
            )
    
    return None

# Set approval callback
set_approval_callback(smart_approval)

# Create system admin agent
admin_agent = Agent(
    name="System Administrator",
    instructions="""You are a system administrator that helps with:
    - Monitoring disk space
    - Cleaning temporary files
    - Managing services
    Always explain what you're doing and why.""",
    tools=[check_disk_space, clean_temp_files, restart_service]
)

# Create maintenance tasks
tasks = [
    Task(
        description="Check disk space on the root partition",
        agent=admin_agent,
        expected_output="Disk space report"
    ),
    Task(
        description="Clean temporary files if disk space is low",
        agent=admin_agent,
        expected_output="Cleaning report or explanation"
    ),
    Task(
        description="Restart the web server if needed",
        agent=admin_agent,
        expected_output="Service status report"
    )
]

# Run with approval system
agents = PraisonAIAgents(
    agents=[admin_agent],
    tasks=tasks,
    verbose=True
)

# Execute (will prompt for approvals as needed)
result = agents.start()

Next Steps

======= title: “Human Approval System” description: “Implement human-in-the-loop approval for dangerous operations” icon: “shield-check”

Human Approval System

The approval module provides a comprehensive human-in-the-loop approval system for dangerous tool operations, ensuring safety and control over high-risk agent actions.

Overview

The approval system allows you to:

  • Require human approval before executing dangerous operations
  • Categorize tools by risk level (critical, high, medium, low)
  • Implement custom approval logic and policies
  • Modify tool arguments before execution
  • Track approved operations to avoid duplicate prompts

Quick Start

from praisonaiagents import Agent
from praisonaiagents.tools import ShellTool

# Tools automatically require approval based on risk
agent = Agent(
    name="System Admin",
    tools=[ShellTool()]  # Shell commands are high-risk
)

# When agent tries to execute a command
agent.chat("Delete all temporary files in /tmp")
# User will be prompted for approval before execution

Risk Levels

Critical Risk

Operations that could cause severe damage:

  • execute_command - Arbitrary command execution
  • execute_code - Code execution
  • kill_process - Process termination

High Risk

Operations that modify system state:

  • write_file - File creation/modification
  • delete_file - File deletion
  • move_file - File relocation
  • copy_file - File duplication
  • execute_query - Database modifications

Medium Risk

Operations with moderate impact:

  • evaluate - Expression evaluation
  • crawl - Web crawling
  • scrape_page - Web scraping

Low Risk

Operations with minimal impact (example category)

Using the Approval Decorator

Basic Usage

from praisonaiagents.agents.approval import require_approval

@require_approval("high")
def delete_user_data(user_id: str):
    """Delete all data for a user - requires approval"""
    # Dangerous operation
    pass

Async Functions

@require_approval("critical")
async def shutdown_server():
    """Shutdown the server - requires approval"""
    await perform_shutdown()

Custom Approval Callbacks

Console Approval (Default)

The default callback shows a formatted prompt in the console:

from praisonaiagents.agents.approval import console_approval_callback

# This is used by default, but you can customize it
def my_console_approval(tool_name, risk_level, args):
    # Custom console formatting
    response = input(f"Allow {tool_name}? (y/n): ")
    return ApprovalDecision(
        approved=response.lower() == 'y',
        reason="User input"
    )

Automated Policies

from praisonaiagents.agents.approval import ApprovalDecision

def policy_based_approval(tool_name, risk_level, args):
    # Auto-approve low risk operations
    if risk_level == "low":
        return ApprovalDecision(approved=True, reason="Auto-approved: low risk")
    
    # Block certain patterns
    if "rm -rf /" in str(args):
        return ApprovalDecision(approved=False, reason="Dangerous command pattern")
    
    # Modify arguments
    if tool_name == "write_file" and args.get("path", "").startswith("/etc"):
        return ApprovalDecision(
            approved=True,
            modified_args={**args, "path": f"/tmp{args['path']}"},
            reason="Redirected to safe location"
        )
    
    # Delegate to human for others
    return console_approval_callback(tool_name, risk_level, args)

GUI Integration

import tkinter as tk
from tkinter import messagebox

def gui_approval_callback(tool_name, risk_level, args):
    root = tk.Tk()
    root.withdraw()
    
    message = f"Approve {tool_name} ({risk_level} risk)?\nArgs: {args}"
    approved = messagebox.askyesno("Approval Required", message)
    
    root.destroy()
    
    return ApprovalDecision(
        approved=approved,
        reason="GUI approval"
    )

Managing Approval Requirements

Adding Requirements

from praisonaiagents.agents.approval import add_approval_requirement

# Add custom tool to approval list
add_approval_requirement("custom_dangerous_tool", "high")

# Add with specific module
add_approval_requirement("api_delete", "critical", module_name="my_api_tools")

Removing Requirements

from praisonaiagents.agents.approval import remove_approval_requirement

# Remove approval requirement
remove_approval_requirement("read_file")  # Reading files no longer requires approval

Checking Requirements

from praisonaiagents.agents.approval import is_approval_required, get_risk_level

if is_approval_required("delete_file"):
    risk = get_risk_level("delete_file")
    print(f"delete_file requires approval (risk: {risk})")

Integration with Agents

Setting Custom Callbacks

from praisonaiagents import register_approval_callback

# Register globally
register_approval_callback(policy_based_approval)

# Now all agents will use this callback
agent = Agent(
    name="Admin Bot",
    tools=[ShellTool(), FileTools()]
)

Context Preservation

The approval system tracks approved operations within the same context:

# First call - requires approval
agent.chat("Run ls -la")  # Prompts for approval

# Within same context - already approved
agent.chat("Run ls -la again")  # No prompt, uses previous approval

Complete Example

from praisonaiagents import Agent, register_approval_callback
from praisonaiagents.tools import ShellTool, FileTools
from praisonaiagents.agents.approval import ApprovalDecision
import logging

# Set up logging
logging.basicConfig(level=logging.INFO)

# Custom approval callback with logging
def logged_approval(tool_name, risk_level, args):
    logging.info(f"Approval requested: {tool_name} ({risk_level})")
    
    # Auto-approve read operations
    if tool_name.startswith("read_"):
        return ApprovalDecision(approved=True, reason="Read operations allowed")
    
    # Require human approval for others
    print(f"\n🛡️ Approval Required")
    print(f"Tool: {tool_name}")
    print(f"Risk: {risk_level}")
    print(f"Arguments: {args}")
    
    response = input("Approve? (y/n/m to modify): ").lower()
    
    if response == 'y':
        return ApprovalDecision(approved=True, reason="User approved")
    elif response == 'm':
        # Allow argument modification
        new_args = {}
        for key, value in args.items():
            new_val = input(f"{key} [{value}]: ") or value
            new_args[key] = new_val
        return ApprovalDecision(
            approved=True,
            modified_args=new_args,
            reason="User approved with modifications"
        )
    else:
        return ApprovalDecision(approved=False, reason="User denied")

# Register the callback
register_approval_callback(logged_approval)

# Create agent with dangerous tools
agent = Agent(
    name="System Administrator",
    role="Server management",
    goal="Maintain system health",
    tools=[ShellTool(), FileTools()]
)

# Operations will require approval
response = agent.chat("Check disk usage and clean up if needed")

Best Practices

  1. Risk Assessment - Carefully categorize tools by actual risk level
  2. User Experience - Provide clear information about what will be executed
  3. Logging - Always log approval decisions for audit trails
  4. Timeout Handling - Consider adding timeouts for approval prompts
  5. Batch Operations - Group related approvals to reduce prompt fatigue
  6. Emergency Override - Implement emergency approval bypass for critical situations
  7. Testing - Test approval flows thoroughly before production deployment