本文へ移動
Zomer Gregorio

Zomer Gregorio

ソフトウェアエンジニア

履歴書
言語
← ブログ

Human-in-the-Loop AI: Architecture Patterns for Safe Automation

· AI Architecture · Workflow Engines · TypeScript · Distributed Systems

Learn how to architect resilient human-in-the-loop workflows for LLMs using confidence thresholds, state machines, and pessimistic locking.

更新情報を受け取る

新しい記事を公開したときに短いお知らせを送ります。メールまたはブラウザの購読情報は通知配信のためだけに保存され、いつでも解除できます。アカウントや追跡用プロフィールは不要です。

通知を開始する前に確認メールでの承認が必要です。

Introduction to Human-in-the-Loop AI

Integrating Large Language Models and automated agents into production systems often exposes a fundamental tension: systems require high throughput and autonomy, yet probabilistic models inevitably hallucinate, drift, or fail on edge cases. Pure automation risks catastrophic failures, while total human oversight removes the economic benefit of automation. The solution lies in engineering robust Human-in-the-Loop (HITL) architectural patterns that intercept low-confidence decisions and route them to human operators without bottlenecking the entire pipeline.

Building an effective HITL system requires treating the human as an asynchronous worker node in a distributed state machine. This demands careful consideration of state serialization, idempotency, timeout handling, and race conditions. This article explores concrete design patterns for implementing safe automation workflows using confidence scoring, persistent state machines, and pessimistic locking.

Core Architecture: The State Machine Approach

Traditional request-response architectures fail for HITL workflows because human review cycles can take minutes, hours, or days. Synchronous HTTP connections will time out, requiring an asynchronous architecture backed by a persistent state machine. Every interaction with an AI model must be modeled as a distinct state transition.

import { StateMachine } from 'workflow-engine';
 
export enum WorkflowState {
  PENDING_AI = 'PENDING_AI',
  EVALUATING_CONFIDENCE = 'EVALUATING_CONFIDENCE',
  PENDING_HUMAN_REVIEW = 'PENDING_HUMAN_REVIEW',
  APPROVED = 'APPROVED',
  REJECTED = 'REJECTED',
  EXECUTION_FAILED = 'EXECUTION_FAILED'
}
 
export interface WorkflowContext {
  taskId: string;
  payload: Record<string, unknown>;
  aiOutput?: string;
  confidenceScore?: number;
  reviewerId?: string;
}

The workflow engine must persist state to a durable store (such as PostgreSQL or a dedicated workflow orchestrator like Temporal) before and after interacting with the AI model. If the application crashes while waiting for a webhook from the human review interface, the state machine must safely recover upon restart.

Confidence Scoring and Routing Strategies

To prevent operator fatigue, the system must filter out high-confidence outputs and only escalate ambiguous decisions. Confidence scoring can be derived via multiple signals: internal model logprobs, self-consistency checks (sampling multiple generations), or secondary deterministic validation layers.

Determining the Threshold

Setting a static confidence threshold is rarely sufficient. The threshold should be dynamic, depending on the severity and cost of failure for the specific action.

function evaluateRouting(score: number, actionType: 'read' | 'write' | 'financial'): WorkflowState {
  const thresholds = {
    read: 0.70,
    write: 0.85,
    financial: 0.98
  };
 
  const requiredThreshold = thresholds[actionType];
  return score >= requiredThreshold ? WorkflowState.APPROVED : WorkflowState.PENDING_HUMAN_REVIEW;
}

When an execution falls below the threshold, the task transitions to PENDING_HUMAN_REVIEW, serializing the exact prompt, the model output, the raw scoring telemetry, and a diff or visualization of the proposed change.

Handling Race Conditions and Concurrency

In high-volume systems, multiple human reviewers might access the same pending review queue, or an automated timeout might trigger while a human is actively submitting a decision. To prevent split-brain issues and duplicate executions, database-level locking is mandatory.

-- Pessimistic locking for task assignment to prevent double-reviews
BEGIN TRANSACTION;
 
SELECT task_id, status 
FROM workflow_tasks 
WHERE task_id = 'task_123' AND status = 'PENDING_HUMAN_REVIEW'
FOR UPDATE;
 
UPDATE workflow_tasks 
SET status = 'IN_REVIEW', assigned_to = 'user_456', updated_at = NOW()
WHERE task_id = 'task_123';
 
COMMIT;

If the transaction fails to acquire the row lock because another worker claimed the task, the UI must gracefully inform the secondary reviewer and fetch the next available item in the queue.

Timeouts, Deadlines, and Escalation Paths

Human queues inevitably experience backlogs. An architectural safeguard must define deterministic behaviors for tasks that exceed maximum review windows. Depending on the business domain, a timed-out review can default to:

  1. Fail-Safe Rejection: The action is canceled, preserving system safety at the cost of availability.
  2. Escalation Routing: The task is reassigned to a secondary manager or senior review group.
  3. Fallback to Heuristics: The system falls back to rigid, deterministic rule-based validation if available.

Implementing this requires scheduled background workers that query for expired tasks based on a TTL stored alongside the state context.

async function processTimeouts(db: DatabaseClient): Promise<void> {
  const expiredTasks = await db.query(
    `SELECT task_id FROM workflow_tasks 
     WHERE status = 'PENDING_HUMAN_REVIEW' AND expires_at < NOW()`
  );
 
  for (const task of expiredTasks) {
    await stateMachine.transition(task.task_id, 'TIMEOUT_EXCEEDED');
  }
}

Audit Logging and Observability

Compliance and debugging in HITL systems require end-to-end traceability. Every state mutation must record an immutable audit log containing the exact model version, prompt parameters, confidence score vector, user ID of the human reviewer, timestamp, and the final modified payload.

Without comprehensive telemetry, debugging non-deterministic regressions becomes impossible. Engineers need the ability to replay exact sequences—including the precise human overrides—in staging environments to validate prompt updates or fine-tuning runs.

Summary

Human-in-the-loop AI is fundamentally a distributed systems problem disguised as an AI problem. By decoupling execution via persistent state machines, implementing risk-based dynamic confidence thresholds, and enforcing strict concurrency controls, engineering teams can build automation pipelines that scale safely without sacrificing correctness.