Ethical Guidelines and Human Oversight › The Role of Human Oversight

Michael BrenndoerferAugust 16, 20256 min read
AI Agent HandbookAll chapters
Part 13 of 15Safety and Governance - Keeping the Agent Aligned
Chapter 38Ethical Guidelines and Human Oversight › The Role of Human Oversight
Reading section 6 of 12 · The Role of Human Oversight · 6 minSection 6 of 12 · 6 min

Book contents

Section 6 of 12 · 6 min read

Ethical Guidelines and Human Oversight › The Role of Human OversightOne section · Sections in this chapter6 / 12 ↓

Even with strong ethical guidelines, your agent will encounter situations where human judgment is needed. This is where human oversight comes in.

Human oversight means having a person review, approve, or audit the agent's decisions, especially for high-stakes situations. The level of oversight should match the risk.

Levels of Human Oversight

Different situations call for different levels of human involvement:

Level 1: Human in the Loop (HITL)

The agent proposes actions but a human must approve before they're executed. This is appropriate for high-stakes decisions.

In[2]:
Code
## Using Claude Sonnet 4.5 with human-in-the-loop workflow
import os
from anthropic import Anthropic

client = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))


class HITLAgent:
    def __init__(self):
        self.pending_actions = []

    def propose_action(self, action_type, details, reasoning):
        """Agent proposes an action for human review"""
        action_id = len(self.pending_actions)

        self.pending_actions.append(
            {
                "id": action_id,
                "type": action_type,
                "details": details,
                "reasoning": reasoning,
                "status": "pending",
            }
        )

        return f"""PROPOSED ACTION #{action_id}
Type: {action_type}
Details: {details}

Reasoning: {reasoning}

This action requires your approval.
- Approve: agent.approve({action_id})
- Reject: agent.reject({action_id})
- Request changes: agent.modify({action_id}, new_details)"""

    def approve(self, action_id):
        """Human approves the action"""
        if action_id >= len(self.pending_actions):
            return "Invalid action ID"

        action = self.pending_actions[action_id]
        if action["status"] != "pending":
            return f"Action already {action['status']}"

        # Execute the action
        action["status"] = "approved"
        return f"Action #{action_id} approved and executed: {action['details']}"

    def reject(self, action_id, reason=None):
        """Human rejects the action"""
        if action_id >= len(self.pending_actions):
            return "Invalid action ID"

        action = self.pending_actions[action_id]
        action["status"] = "rejected"
        action["rejection_reason"] = reason

        return f"Action #{action_id} rejected. {reason if reason else ''}"

    def get_audit_log(self):
        """Get a log of all proposed actions and their outcomes"""
        return self.pending_actions


## Example usage
agent = HITLAgent()

## Agent proposes sending an important email
proposal = agent.propose_action(
    action_type="send_email",
    details="Send email to board@company.com with Q4 financial results",
    reasoning="User requested quarterly report distribution. This is high-stakes "
    "communication with company leadership, so requesting approval.",
)
print(proposal)

## Human reviews and approves
result = agent.approve(0)
print(result)

Level 2: Human on the Loop (HOTL)

The agent acts autonomously but a human monitors its actions and can intervene if needed. This is appropriate for medium-risk situations.

In[3]:
Code
## Using Claude Sonnet 4.5 with human-on-the-loop monitoring
class HOTLAgent:
    def __init__(self, review_threshold=0.7):
        self.action_log = []
        self.review_threshold = review_threshold

    def execute_action(self, action_type, details, confidence):
        """Execute action with optional human review based on confidence"""
        action_id = len(self.action_log)

        # Log the action
        self.action_log.append(
            {
                "id": action_id,
                "type": action_type,
                "details": details,
                "confidence": confidence,
                "flagged_for_review": confidence < self.review_threshold,
            }
        )

        if confidence < self.review_threshold:
            return f"""ACTION EXECUTED (Flagged for review)
ID: {action_id}
Type: {action_type}
Details: {details}
Confidence: {confidence:.2f}

This action was executed but flagged for review due to low confidence.
Review with: agent.review_action({action_id})"""
        else:
            return f"Action executed: {details}"

    def review_action(self, action_id):
        """Human reviews a flagged action"""
        if action_id >= len(self.action_log):
            return "Invalid action ID"

        action = self.action_log[action_id]
        return f"""REVIEW ACTION #{action_id}
Type: {action["type"]}
Details: {action["details"]}
Confidence: {action["confidence"]:.2f}

If this action was inappropriate:
- Undo: agent.undo_action({action_id})
- Adjust settings: agent.adjust_threshold()"""

    def get_flagged_actions(self):
        """Get all actions flagged for review"""
        return [a for a in self.action_log if a["flagged_for_review"]]


## Example usage
agent = HOTLAgent(review_threshold=0.7)

## High confidence action (executes without review)
result = agent.execute_action(
    "send_routine_email", "Send weekly status update to team", confidence=0.95
)
print(result)

## Low confidence action (executes but flagged)
result = agent.execute_action(
    "schedule_meeting", "Schedule meeting with new contact", confidence=0.6
)
print(result)

## Human reviews flagged actions periodically
flagged = agent.get_flagged_actions()
print(f"\n{len(flagged)} actions flagged for review")
Out[3]:
Console
Action executed: Send weekly status update to team
ACTION EXECUTED (Flagged for review)
ID: 1
Type: schedule_meeting
Details: Schedule meeting with new contact
Confidence: 0.60

This action was executed but flagged for review due to low confidence.
Review with: agent.review_action(1)

1 actions flagged for review

Level 3: Human Out of the Loop (HOOTL)

The agent acts fully autonomously, but all actions are logged for later audit. This is appropriate for low-risk, routine tasks.

In[4]:
Code
## Using Claude Sonnet 4.5 with audit logging
class HOOTLAgent:
    def __init__(self):
        self.audit_log = []

    def execute_action(self, action_type, details):
        """Execute action autonomously with audit logging"""
        import datetime

        action_id = len(self.audit_log)
        timestamp = datetime.datetime.now().isoformat()

        # Log the action
        self.audit_log.append(
            {
                "id": action_id,
                "timestamp": timestamp,
                "type": action_type,
                "details": details,
            }
        )

        # Execute without human involvement
        return f"Action executed: {details}"

    def get_audit_log(self, action_type=None, start_date=None):
        """Retrieve audit log for review"""
        log = self.audit_log

        if action_type:
            log = [a for a in log if a["type"] == action_type]

        if start_date:
            log = [a for a in log if a["timestamp"] >= start_date]

        return log

    def generate_audit_report(self):
        """Generate a summary report of agent actions"""
        from collections import Counter

        action_counts = Counter(a["type"] for a in self.audit_log)

        report = "AUDIT REPORT\n"
        report += f"Total actions: {len(self.audit_log)}\n\n"
        report += "Actions by type:\n"
        for action_type, count in action_counts.most_common():
            report += f"  {action_type}: {count}\n"

        return report


## Example usage
agent = HOOTLAgent()

## Agent acts autonomously
agent.execute_action("send_routine_email", "Daily standup reminder")
agent.execute_action("update_calendar", "Added team lunch event")
agent.execute_action("send_routine_email", "Weekly newsletter")

## Human reviews audit log periodically
print(agent.generate_audit_report())
Out[4]:
Console
AUDIT REPORT
Total actions: 3

Actions by type:
  send_routine_email: 2
  update_calendar: 1

Choosing the Right Level of Oversight

How do you decide which level of oversight to use? Consider these factors:

Stakes: How much harm could result from a mistake?

  • High stakes (financial transactions, legal documents) → Human in the loop
  • Medium stakes (important emails, scheduling) → Human on the loop
  • Low stakes (routine reminders, simple queries) → Human out of the loop

Reversibility: Can the action be easily undone?

  • Irreversible (sending emails, deleting data) → Higher oversight
  • Reversible (creating drafts, setting reminders) → Lower oversight

Frequency: How often does this action occur?

  • Rare, unusual actions → Higher oversight
  • Routine, frequent actions → Lower oversight

User preference: How much control does the user want?

  • Some users prefer more autonomy, others want more control
  • Make oversight levels configurable

Here's a framework for categorizing actions:

In[5]:
Code
## Using Claude Sonnet 4.5 with risk-based oversight
from enum import Enum


class OversightLevel(Enum):
    HITL = "human_in_loop"  # Requires approval
    HOTL = "human_on_loop"  # Monitored, can intervene
    HOOTL = "human_out_of_loop"  # Audited after the fact


class ActionClassifier:
    def __init__(self):
        # Define oversight requirements for different action types
        self.oversight_rules = {
            "send_email": {
                "external": OversightLevel.HITL,  # Emails to external contacts
                "internal": OversightLevel.HOTL,  # Emails to team
                "automated": OversightLevel.HOOTL,  # Routine notifications
            },
            "modify_data": {
                "delete": OversightLevel.HITL,  # Deletions require approval
                "update": OversightLevel.HOTL,  # Updates are monitored
                "create": OversightLevel.HOOTL,  # Creating new items is low-risk
            },
            "schedule": {
                "cancel": OversightLevel.HITL,  # Canceling requires approval
                "create": OversightLevel.HOTL,  # Creating is monitored
                "remind": OversightLevel.HOOTL,  # Reminders are low-risk
            },
        }

    def get_oversight_level(self, action_type, subtype):
        """Determine required oversight level for an action"""
        if action_type in self.oversight_rules:
            rules = self.oversight_rules[action_type]
            return rules.get(subtype, OversightLevel.HOTL)  # Default to HOTL
        return OversightLevel.HOTL  # Default for unknown actions


## Example usage
classifier = ActionClassifier()

## Check oversight requirements
print(classifier.get_oversight_level("send_email", "external"))  # HITL
print(classifier.get_oversight_level("schedule", "remind"))  # HOOTL
print(classifier.get_oversight_level("modify_data", "delete"))  # HITL
Out[5]:
Console
OversightLevel.HITL
OversightLevel.HOOTL
OversightLevel.HITL