Skip to content
SDB
Responsible AI

Chapter 07 · intermediate · 28 min

Human-Centricity & Empathy

Human-in-the-loop patterns, meaningful human control, accessible AI design, and ethical value alignment

Subhendu Datta BhowmikAI Tutorials

What Is Human-Centric AI?

Human-centric AI puts people — their needs, rights, and values — at the center of AI system design. It goes beyond legal compliance to ensure AI genuinely serves and empowers people rather than marginalizing or automating away human agency.

The Automation Spectrum

AI decisions span a spectrum from fully human to fully automated:

FULLY HUMAN ←────────────────────────────────→ FULLY AUTOMATED
    │                                                    │
    │  Human        AI-Assisted    AI-Augmented    Auto-    Auto-
    │  Decision     Decision       Decision         Flagged  Decision
    │  Only         (AI Recommends) (AI Decides,    Decision  (No
    │               Human Acts)     Human Approves) Review)   Human)
    │
    ↓ Appropriate escalation based on risk, uncertainty, context

Where to place AI on this spectrum? Factors to consider:

  • Stakes: Life-critical decisions require human oversight; low-stakes can be automated
  • Uncertainty: High model uncertainty triggers human review
  • Reversibility: Irreversible decisions (firing, arrest) demand human accountability
  • Regulatory: GDPR Art.22 mandates human review option for significant automated decisions
  • Context sensitivity: Nuanced situations (grief, mental health) require human empathy

ISO 42001 — Human Oversight Requirements

ISO 42001 A.9.4 (Responsible Use) requires:

  • Appropriate human oversight mechanisms commensurate with the risk level
  • Users must be able to understand the basis of AI system outputs
  • Systems must support human ability to intervene, override, or correct AI decisions
  • Excessive reliance on AI without appropriate human judgment must be prevented

Human-in-the-Loop Design Patterns

Pattern 1: Confidence-Gated Automation

The system auto-decides when confident, escalates when uncertain:

def make_decision(model, input_data, auto_threshold=0.95, review_threshold=0.70):
    pred, confidence = model.predict_with_confidence(input_data)

    if confidence >= auto_threshold:
        return Decision(type="AUTO", prediction=pred,
                        confidence=confidence, requires_review=False)
    elif confidence >= review_threshold:
        return Decision(type="RECOMMENDED", prediction=pred,
                        confidence=confidence, requires_review=True,
                        reviewer_notes=f"AI suggests {pred}, please verify")
    else:
        return Decision(type="HUMAN_ONLY", prediction=None,
                        confidence=confidence, requires_review=True,
                        reviewer_notes="Low confidence — human judgment required")

Use cases: medical image triage, content moderation, document classification, loan underwriting

Pattern 2: Review Queue with SLA

High-volume systems use an asynchronous review queue:

  1. AI processes all items; high-confidence auto-decides; uncertain items → queue
  2. Human reviewers work through the queue with SLA (e.g., urgent: 4h, standard: 24h)
  3. Reviewer decisions feed back into model retraining (active learning)
  4. Metrics: queue depth, time-to-review, override rate (% of AI decisions reversed)

Override rate is a critical signal: if humans override 30%+ of AI decisions, the model needs retraining.

Pattern 3: Active Learning Loop

Humans are most valuable when labeling the examples the model is most uncertain about:

Pool of unlabeled data
  ↓
Model predicts with uncertainty scores
  ↓
Select top-k most uncertain samples → Human labeler
  ↓
Add labeled samples to training set
  ↓
Retrain model → repeat

This achieves higher accuracy with fewer human labels than random sampling — reducing labeling cost by 30–70%.

Pattern 4: Contestability and Appeals

Every AI decision affecting a person must have a contestability mechanism:

  1. Explanation: why did the AI decide this way? (SHAP, counterfactuals)
  2. Appeal: clear path to request human review
  3. Redress: ability to correct wrong decisions
  4. Feedback: outcome of the appeal recorded and used to improve the system

This is required by GDPR Art.22 and EU AI Act Art.14 for high-risk systems.

Pattern 5: Meaningful Consent Workflow

Before using AI to process user data, obtain informed consent:

  • Specific: consent for each distinct processing purpose (not blanket)
  • Informed: plain-language explanation of what the AI does and the implications
  • Revocable: users can withdraw consent at any time
  • Granular: users can consent to some uses but not others
  • Non-coercive: denying consent cannot result in denial of core service
HITL Decision System, Review Queue, and Active Learningpython
# ─── 1. Confidence-Gated HITL Decision System ────────────────
from dataclasses import dataclass, field
from typing import Optional, List
from datetime import datetime
from enum import Enum
import numpy as np

class DecisionType(Enum):
    AUTO_APPROVED = "auto_approved"
    AUTO_REJECTED = "auto_rejected"
    HUMAN_REVIEW = "human_review"
    HUMAN_REQUIRED = "human_required"

@dataclass
class Decision:
    item_id: str
    decision_type: DecisionType
    ai_prediction: Optional[int]
    ai_confidence: float
    human_decision: Optional[int] = None
    human_reviewer: Optional[str] = None
    review_timestamp: Optional[str] = None
    explanation: Optional[str] = None
    appeals: List[str] = field(default_factory=list)

    def human_overrode_ai(self) -> bool:
        return (self.human_decision is not None and
                self.ai_prediction is not None and
                self.human_decision != self.ai_prediction)

class HITLDecisionSystem:
    def __init__(self,
                 auto_approve_threshold: float = 0.92,
                 auto_reject_threshold: float = 0.92,
                 review_threshold: float = 0.70):
        self.auto_approve_threshold = auto_approve_threshold
        self.auto_reject_threshold = auto_reject_threshold
        self.review_threshold = review_threshold
        self.decisions: List[Decision] = []
        self.review_queue: List[Decision] = []

    def process(self, item_id: str, ai_pred: int, ai_confidence: float,
                explanation: str = "") -> Decision:
        if ai_pred == 1 and ai_confidence >= self.auto_approve_threshold:
            d = Decision(item_id=item_id, decision_type=DecisionType.AUTO_APPROVED,
                         ai_prediction=1, ai_confidence=ai_confidence,
                         explanation=explanation)
        elif ai_pred == 0 and ai_confidence >= self.auto_reject_threshold:
            d = Decision(item_id=item_id, decision_type=DecisionType.AUTO_REJECTED,
                         ai_prediction=0, ai_confidence=ai_confidence,
                         explanation=explanation)
        elif ai_confidence >= self.review_threshold:
            d = Decision(item_id=item_id, decision_type=DecisionType.HUMAN_REVIEW,
                         ai_prediction=ai_pred, ai_confidence=ai_confidence,
                         explanation=explanation)
            self.review_queue.append(d)
        else:
            d = Decision(item_id=item_id, decision_type=DecisionType.HUMAN_REQUIRED,
                         ai_prediction=None, ai_confidence=ai_confidence,
                         explanation="Low confidence — AI recommendation withheld to prevent anchoring bias")
            self.review_queue.append(d)

        self.decisions.append(d)
        return d

    def human_review(self, item_id: str, reviewer: str, decision: int, notes: str = ""):
        for d in self.review_queue:
            if d.item_id == item_id:
                d.human_decision = decision
                d.human_reviewer = reviewer
                d.review_timestamp = datetime.utcnow().isoformat() + "Z"
                self.review_queue.remove(d)
                return d
        raise ValueError(f"Item {item_id} not found in review queue")

    def governance_report(self):
        total = len(self.decisions)
        auto = sum(1 for d in self.decisions
                   if d.decision_type in (DecisionType.AUTO_APPROVED, DecisionType.AUTO_REJECTED))
        reviewed = sum(1 for d in self.decisions if d.human_decision is not None)
        overrides = sum(1 for d in self.decisions if d.human_overrode_ai())

        print(f"\nHITL Governance Report")
        print(f"{'─'*40}")
        print(f"  Total decisions:       {total}")
        print(f"  Automated decisions:   {auto} ({100*auto//max(total,1)}%)")
        print(f"  Queued for review:     {len(self.review_queue)}")
        print(f"  Reviewed by humans:    {reviewed}")
        print(f"  Human override rate:   {100*overrides//max(reviewed,1)}%")
        if overrides > 0.2 * reviewed:
            print(f"  ⚠ Override rate > 20% — model retraining recommended")

# Simulate loan application processing
system = HITLDecisionSystem(auto_approve_threshold=0.90, auto_reject_threshold=0.90)

applications = [
    ("APP-001", 1, 0.97, "Strong income, clean credit history, low DTI"),
    ("APP-002", 0, 0.95, "Multiple missed payments, high debt-to-income ratio"),
    ("APP-003", 1, 0.82, "Good income but short credit history"),
    ("APP-004", 0, 0.61, "Mixed signals: good income but recent job change"),
    ("APP-005", 1, 0.55, "New-to-credit, insufficient signals for automated decision"),
    ("APP-006", 0, 0.91, "Significant derogatory marks on credit report"),
]

print("Processing loan applications:")
for app_id, pred, conf, explanation in applications:
    d = system.process(app_id, pred, conf, explanation)
    status = d.decision_type.value.replace("_", " ").upper()
    print(f"  {app_id}: [{status}] conf={conf:.2f} {'→ AI: ' + str(pred) if pred is not None else '→ needs human'}")

# Simulate human reviews
system.human_review("APP-003", "jane.smith@bank.com", 1, "Approved — first-time buyer with strong employment")
system.human_review("APP-004", "jane.smith@bank.com", 0, "Rejected — job change within 3 months is a disqualifier per policy")
system.human_review("APP-005", "john.doe@bank.com", 1, "Approved — student loan will be paid off next month, reviewed bank statements")

system.governance_report()

# ─── 2. Active Learning Loop ──────────────────────────────────
from sklearn.datasets import make_classification
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
import numpy as np

X_all, y_all = make_classification(n_samples=3000, n_features=20, random_state=42)
X_labeled, X_pool = X_all[:100], X_all[100:]
y_labeled, y_pool = y_all[:100], y_all[100:]
X_test, y_test = X_all[2500:], y_all[2500:]

print("\nActive Learning vs Random Sampling:")
print(f"{'Round':<8} {'Labeled':<10} {'AL Accuracy':<15} {'Random Accuracy'}")
print("─" * 50)

al_X_labeled, al_y_labeled = X_labeled.copy(), y_labeled.copy()
rand_X_labeled, rand_y_labeled = X_labeled.copy(), y_labeled.copy()
remaining_pool_mask = np.ones(len(X_pool), dtype=bool)

for round_num in range(5):
    # Active Learning: query most uncertain samples
    al_model = RandomForestClassifier(n_estimators=50, random_state=42)
    al_model.fit(al_X_labeled, al_y_labeled)

    probs = al_model.predict_proba(X_pool[remaining_pool_mask])
    uncertainty = 1 - probs.max(axis=1)
    top_k_idx = np.argsort(uncertainty)[-20:]  # 20 most uncertain

    pool_indices = np.where(remaining_pool_mask)[0]
    query_indices = pool_indices[top_k_idx]
    al_X_labeled = np.vstack([al_X_labeled, X_pool[query_indices]])
    al_y_labeled = np.concatenate([al_y_labeled, y_pool[query_indices]])
    remaining_pool_mask[query_indices] = False

    al_acc = accuracy_score(y_test, al_model.predict(X_test))

    # Random sampling baseline
    rand_model = RandomForestClassifier(n_estimators=50, random_state=42)
    rand_model.fit(rand_X_labeled, rand_y_labeled)
    rand_idx = np.random.choice(len(X_pool), 20, replace=False)
    rand_X_labeled = np.vstack([rand_X_labeled, X_pool[rand_idx]])
    rand_y_labeled = np.concatenate([rand_y_labeled, y_pool[rand_idx]])
    rand_acc = accuracy_score(y_test, rand_model.predict(X_test))

    print(f"  {round_num+1:<6}  {len(al_X_labeled):<10} {al_acc:.4f}{'*' if al_acc>rand_acc else ' ':2}        {rand_acc:.4f}")

print("  (* AL beats random sampling with same number of labels)")

# ─── 3. Contestability API ───────────────────────────────────
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI(title="AI Decision Contestability API")

class AppealRequest(BaseModel):
    decision_id: str
    applicant_id: str
    reason: str
    additional_evidence: str = ""

class AppealResponse(BaseModel):
    appeal_id: str
    status: str
    assigned_reviewer: str
    expected_response_days: int
    explanation_provided: str
    next_steps: str

@app.post("/decisions/{decision_id}/appeal", response_model=AppealResponse)
async def submit_appeal(decision_id: str, request: AppealRequest):
    """GDPR Art.22 contestability endpoint — required for all significant automated decisions."""
    return AppealResponse(
        appeal_id=f"APL-{decision_id}-001",
        status="RECEIVED",
        assigned_reviewer="appeals.team@bank.com",
        expected_response_days=10,
        explanation_provided="The automated decision considered: credit score (primary factor), debt-to-income ratio, and payment history. SHAP analysis available at /decisions/{decision_id}/explanation.",
        next_steps="A human reviewer will assess your appeal within 10 business days. You may submit additional supporting documents to the address above.",
    )

@app.get("/decisions/{decision_id}/explanation")
async def get_explanation(decision_id: str):
    """Return SHAP-based explanation for the decision."""
    return {
        "decision_id": decision_id,
        "outcome": "rejected",
        "top_factors": [
            {"factor": "payment_history", "direction": "negative", "impact": "high",
             "detail": "2 missed payments in the past 24 months reduced approval likelihood"},
            {"factor": "debt_to_income_ratio", "direction": "negative", "impact": "medium",
             "detail": "DTI of 47% exceeds preferred threshold of 43%"},
            {"factor": "credit_score", "direction": "neutral", "impact": "low",
             "detail": "Credit score of 680 is within acceptable range"},
        ],
        "what_would_change_decision": [
            "Reducing monthly debt obligations by €300 would bring DTI below threshold",
            "12 months of clean payment history would significantly improve outcome",
        ],
        "human_review_available": True,
        "appeal_url": f"/decisions/{decision_id}/appeal",
    }

Inclusive and Accessible AI Design

Digital Accessibility

AI systems must be usable by people with disabilities — this is both an ethical imperative and legal requirement (ADA, EN 301 549, WCAG 2.2):

DisabilityAI Design Considerations
Visual impairmentScreen reader compatible outputs; text alternatives for AI-generated images; high-contrast interfaces
Motor impairmentVoice control interface; AI assistance for typing; timeout extensions
Cognitive impairmentPlain language explanations of AI decisions; simplified interfaces; consistent patterns
Hearing impairmentCaptions for audio AI output; visual alternatives for voice interfaces
NeurodiversityCustomizable interfaces; predictable behavior; multiple modalities

Cultural Sensitivity

AI systems trained predominantly on Western, English-language data often fail for:

  • Non-Western names (NER failure), dates (different formats), currencies
  • Low-resource languages (MT, ASR quality drops dramatically)
  • Cultural context for sentiment (humor, sarcasm, honorifics)
  • Different social norms around concepts like privacy, authority, gender

Mitigation: collect representative data across cultures; test with diverse user groups; localize NLP models; involve cultural consultants in design.

Empathetic AI Interactions

For AI systems that interact directly with users in sensitive contexts (mental health, grief support, medical):

  • Avoid cold, transactional language — use warm, empathetic framing
  • Never create false intimacy — be transparent that the user is interacting with AI
  • Always provide human escalation — especially for mental health crises (crisis hotlines, human support)
  • Recognize distress signals — route users expressing suicidal ideation or severe distress to human support immediately
  • Respect cultural norms — different cultures have different expectations around AI emotional expression

Knowledge check

A content moderation AI reviews 100,000 posts per day and automatically removes 85% without human review. The human override rate (% of AI removals later reinstated by appeal) is 28%. What action is most warranted?

Summary

  • Human centricity means designing AI systems where humans retain meaningful agency, oversight, and the ability to contest, override, and correct AI decisions
  • The automation spectrum ranges from human-only to fully automated — risk, reversibility, stakes, and uncertainty determine the right position for each decision type
  • HITL patterns: confidence-gated automation, review queues with SLA, active learning loops, and mandatory contestability mechanisms (required by GDPR Art.22 and EU AI Act)
  • Human override rate is a critical governance KPI — rates above 20% signal model failure requiring retraining
  • Active learning lets human labels guide model improvement to the most uncertain examples — reducing labeling cost 30–70%
  • Accessible AI design must account for disability, language, culture, and neurodiversity — not just average-user assumptions
  • Empathetic AI in sensitive contexts (mental health, grief) must always provide human escalation paths and avoid false intimacy

Next: Sustainability — measuring and reducing the environmental impact of AI systems.

Responsible AI