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:
- AI processes all items; high-confidence auto-decides; uncertain items → queue
- Human reviewers work through the queue with SLA (e.g., urgent: 4h, standard: 24h)
- Reviewer decisions feed back into model retraining (active learning)
- 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:
- Explanation: why did the AI decide this way? (SHAP, counterfactuals)
- Appeal: clear path to request human review
- Redress: ability to correct wrong decisions
- 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
# ─── 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):
| Disability | AI Design Considerations |
|---|---|
| Visual impairment | Screen reader compatible outputs; text alternatives for AI-generated images; high-contrast interfaces |
| Motor impairment | Voice control interface; AI assistance for typing; timeout extensions |
| Cognitive impairment | Plain language explanations of AI decisions; simplified interfaces; consistent patterns |
| Hearing impairment | Captions for audio AI output; visual alternatives for voice interfaces |
| Neurodiversity | Customizable 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.