Human-in-the-Loop 패턴
자율도 스펙트럼
섹션 제목: “자율도 스펙트럼”에이전트 시스템은 완전히 수동인 상태에서 완전히 자율적인 상태까지 연속적인 스펙트럼 위에 존재합니다.
완전 수동 완전 자율 │ │ ▼ ▼[매 단계 승인] → [위험 단계만 승인] → [요약 후 승인] → [자율 실행]
Human-heavy Agent-heavy 안전 ↑ 속도 ↓ 안전 ↓ 속도 ↑어느 지점이 적합한지는 작업의 가역성, 실패 비용, 에이전트 신뢰도에 따라 결정됩니다.
사람 개입이 필요한 5가지 상황
섹션 제목: “사람 개입이 필요한 5가지 상황”1. 비가역적 작업 전 (Irreversible Actions)
섹션 제목: “1. 비가역적 작업 전 (Irreversible Actions)”삭제, 배포, 금전 전송처럼 되돌릴 수 없는 작업 직전에는 반드시 사람의 확인이 필요합니다.
에이전트: "프로덕션 데이터베이스에서 user_id=1234 레코드를 삭제하려 합니다" [이 작업은 되돌릴 수 없습니다]사람: [확인 / 취소]2. 신뢰도 임계값 이하 (Low Confidence)
섹션 제목: “2. 신뢰도 임계값 이하 (Low Confidence)”에이전트가 자신의 결정에 불확실함을 감지했을 때 스스로 사람에게 도움을 요청합니다.
if agent.confidence_score < 0.7: result = await request_human_clarification( question=agent.ambiguity_description, options=agent.candidate_actions, )3. 예상치 못한 상황 (Unexpected State)
섹션 제목: “3. 예상치 못한 상황 (Unexpected State)”에이전트가 실행 중 예상과 다른 상황을 발견했을 때입니다.
에이전트: "config.yml 파일을 수정하려 했는데, 파일이 이미 수정된 상태입니다 (최근 변경: 5분 전). 계속 덮어쓸까요?"4. 반복 실패 (Repeated Failures)
섹션 제목: “4. 반복 실패 (Repeated Failures)”동일한 작업을 N번 이상 실패했을 때 자동 에스컬레이션합니다.
MAX_RETRIES = 3
if attempt_count >= MAX_RETRIES: await escalate_to_human( context=current_task, failure_log=recent_errors, message=f"{MAX_RETRIES}번 시도 후 실패했습니다. 도움이 필요합니다." )5. 정책 경계 (Policy Boundary)
섹션 제목: “5. 정책 경계 (Policy Boundary)”에이전트가 명확한 정책 경계에 도달했을 때입니다.
에이전트: "이 작업은 외부 API 키가 필요하지만 현재 컨텍스트에서 제공받지 못했습니다. API 키를 제공하거나 작업을 취소해 주세요."승인 워크플로우 구현
섹션 제목: “승인 워크플로우 구현”interface HumanReviewRequest { taskId: string; agentAction: AgentAction; reason: string; urgency: 'low' | 'medium' | 'high'; timeout: number; // ms}
async function requestHumanReview(req: HumanReviewRequest): Promise<Decision> { // 슬랙/이메일 등으로 알림 발송 await notify(req);
// 타임아웃 내 응답 대기 const decision = await waitForDecision(req.taskId, req.timeout);
if (!decision) { // 타임아웃 시 기본 정책 적용 return getDefaultDecision(req.urgency); }
return decision;}
function getDefaultDecision(urgency: string): Decision { // 긴급도에 따라 타임아웃 기본값 결정 // high urgency → 거부 (안전 우선) // low urgency → 승인 (편의 우선) return urgency === 'high' ? { approved: false } : { approved: true };}에스컬레이션 정책
섹션 제목: “에스컬레이션 정책”에스컬레이션은 승인 레벨을 높이는 방향(더 많은 사람 개입)과 낮추는 방향(더 많은 자율)으로 모두 작동합니다.
에스컬레이션 트리거
섹션 제목: “에스컬레이션 트리거”| 트리거 | 방향 | 예시 |
|---|---|---|
| 연속 N회 실패 | 승격 (더 많은 개입) | 3번 실패 → Manual 전환 |
| 연속 M회 성공 | 강등 (더 많은 자율) | 10번 성공 → Auto 허용 |
| 특정 경로 접근 | 즉시 승격 | /prod/ 접근 → 항상 Manual |
| 신뢰도 점수 저하 | 즉시 승격 | confidence < 0.5 → 즉시 중단 |
| 비정상 속도 | 즉시 승격 | 1분에 50개 이상 도구 호출 |
에스컬레이션 구현
섹션 제목: “에스컬레이션 구현”class EscalationPolicy: def __init__(self): self.consecutive_failures = 0 self.consecutive_successes = 0 self.current_level = ApprovalLevel.SEMI_AUTO
def on_success(self): self.consecutive_failures = 0 self.consecutive_successes += 1
if self.consecutive_successes >= 10: self.current_level = ApprovalLevel.AUTO self.consecutive_successes = 0
def on_failure(self): self.consecutive_successes = 0 self.consecutive_failures += 1
if self.consecutive_failures >= 3: self.current_level = ApprovalLevel.MANUAL self.consecutive_failures = 0
def on_sensitive_path(self, path: str): if any(path.startswith(p) for p in SENSITIVE_PATHS): self.current_level = ApprovalLevel.MANUAL비동기 Human-in-the-Loop
섹션 제목: “비동기 Human-in-the-Loop”사람의 응답을 기다리는 동안 에이전트가 차단되지 않도록 비동기 패턴을 사용할 수 있습니다.
에이전트 → 승인 요청 발송 → 상태: WAITING_APPROVAL (다른 독립 작업 계속 진행)
사람 → 승인/거부 응답
에이전트 → 응답 수신 → 원래 작업 재개 또는 취소이 패턴은 LangGraph의 interrupt() 메커니즘이나 Temporal 같은 워크플로우 엔진으로 구현합니다.
Human-in-the-Loop는 단순히 “사람이 승인하는 것”이 아닙니다. 비가역 작업, 낮은 신뢰도, 예상치 못한 상황, 반복 실패, 정책 경계라는 5가지 트리거를 기반으로 동적으로 자율도를 조정하는 지능형 에스컬레이션 시스템입니다. 에이전트가 스스로 멈추고 질문하도록 설계하는 것이 가장 중요한 원칙입니다.