AI 에이전트 Human-in-the-Loop 설계법
되돌릴 수 없는 작업이라면 사람의 승인이 필수입니다. LangGraph interrupt와 체크포인트로 인라인·신뢰도 기반·다단계 승인 게이트를 설계하는 Human-in-the-Loop 패턴입니다.
목차
- 개요
- Human-in-the-Loop의 핵심 개념과 동작 원리
- 승인 단계 설계 패턴
- LangGraph로 Human-in-the-Loop 구현하기
- 성능과 트레이드오프 분석
- 운영 환경 적용 시 고려사항
- 맺음말
개요
문제 배경
AI 에이전트가 실제 프로젝트에 본격적으로 투입되면서, 자동화 수준을 어디까지 허용할 것인가라는 질문이 핵심 설계 과제로 떠오르고 있습니다. 이메일을 자동으로 발송하거나, 데이터베이스를 변경하거나, 외부 API를 통해 결제를 승인하는 에이전트는 분명 효율적이지만, 단 한 번의 오판이 되돌리기 어려운 결과를 만들어낼 수 있습니다. **Human-in-the-Loop(HitL)**은 이 간극을 메우는 설계 원칙으로, AI가 임계 동작을 수행하기 직전에 사람의 확인 또는 승인을 요청하는 흐름을 자동화 파이프라인 안에 구조적으로 내장합니다. 이 글에서는 HitL이 왜 필요한지 개념적 맥락부터 시작해, 실제 에이전트 파이프라인에서 어떤 패턴으로 구현하고 운영해야 하는지를 단계별로 살펴봅니다.
기존 방식의 한계
초기 LLM 활용 방식은 단순했습니다. 사용자가 입력하면 모델이 응답하는 단일 요청-응답 구조였기 때문에, 모델의 실수는 잘못된 답변 하나로 끝났습니다. 그러나 에이전트 아키텍처로 진화하면서 상황이 달라졌습니다. 에이전트는 여러 단계를 자율적으로 연쇄 실행하며, 각 단계에서 실제 시스템(파일 시스템, 데이터베이스, 외부 서비스)을 조작합니다. 이때 에이전트가 내린 잘못된 판단은 연쇄적으로 확산됩니다. "이메일 초안 작성"이 아니라 "이메일 발송"이, "쿼리 생성"이 아니라 "DELETE 실행"이 됩니다. 완전 자동화된 파이프라인은 속도라는 이점을 제공하지만, 검증 부재라는 구조적 취약성을 함께 안게 됩니다.
자동화 파이프라인에서 도구 호출 단계가 되돌리기 어려운 부수효과를 만드는 지점입니다.
Human-in-the-Loop의 핵심 개념과 동작 원리
자동화 스펙트럼과 HitL의 위치
에이전트 자동화 수준은 하나의 스펙트럼으로 이해하는 것이 유용합니다. 한쪽 끝은 모든 결정을 사람이 내리는 완전 수동 방식이고, 반대 끝은 에이전트가 모든 판단과 실행을 책임지는 완전 자동화입니다. HitL은 이 사이 어딘가에 위치하는 것이 아니라, 위험도와 비가역성에 따라 특정 노드에만 선택적으로 개입 지점을 삽입하는 설계 철학입니다. 이메일 초안 생성은 자동화해도 무방하지만, 실제 발송 전에는 사람이 검토합니다. 데이터 조회 쿼리는 자동 실행하되, 수정·삭제 쿼리는 승인을 받습니다. 이처럼 HitL은 자동화를 포기하는 것이 아니라, 자동화의 적용 범위를 신중하게 조율하는 방법입니다.
| 자동화 수준 | 설명 | 적합한 상황 | 주의점 |
|---|---|---|---|
| 완전 수동 | 모든 단계를 사람이 결정 | 고위험·고비용 의사결정 | 속도 저하, 확장성 없음 |
| HitL (승인 게이트) | 임계 단계에서만 사람 개입 | 자동화 + 안전망이 필요할 때 | 승인 지연 처리 설계 필요 |
| 감독형 자동화 | 자동 실행 후 사람이 리뷰 | 빠른 실행, 사후 검토 가능 | 실행 후 수정 비용 발생 |
| 완전 자동화 | 에이전트가 모든 결정·실행 | 저위험·고반복 작업 | 오류 확산 위험 |
동작 원리: 상태 유지와 재개
HitL의 기술적 핵심은 에이전트 실행 상태를 영속적으로 저장하고, 외부 이벤트(사람의 승인)가 도착했을 때 정확히 중단된 지점부터 재개하는 메커니즘입니다. 단순히 "에이전트가 멈추고 사람이 답한다"로 설명하면 간단해 보이지만, 실제 구현에서는 여러 층위의 문제가 얽힙니다. 에이전트의 컨텍스트(메모리, 이전 도구 실행 결과, 대화 이력)가 승인 대기 중 소실되지 않아야 하며, 다수의 에이전트 인스턴스가 각자 다른 승인을 기다릴 수 있어야 합니다. 또한 승인이 이루어진 후 올바른 인스턴스가 올바른 상태로 재개되어야 합니다. 이를 위해 현대적인 에이전트 프레임워크는 체크포인팅(Checkpointing) 메커니즘을 제공합니다.
체크포인트에 상태를 저장하고, 승인 신호가 도착하면 정확히 그 지점에서 재개하는 것이 HitL 구현의 핵심입니다.
개입 트리거 분류
모든 에이전트 동작에 승인 게이트를 달면 자동화의 이점이 사라집니다. 실제 프로젝트에서는 어떤 동작이 개입 트리거가 되어야 하는지 기준을 세우는 것이 가장 중요한 설계 결정 중 하나입니다. 크게 세 가지 기준으로 분류할 수 있습니다. 첫째, 비가역성 — 실행 후 되돌리기 어렵거나 불가능한 동작(데이터 삭제, 이메일 발송, 결제 처리)은 반드시 승인 게이트를 둡니다. 둘째, 임계값 기반 — 금액이나 영향 범위가 일정 수준을 넘을 때 동적으로 게이트를 삽입합니다. 셋째, 신뢰도 기반 — 에이전트 스스로 자신의 판단에 대한 확신이 낮을 때 사람에게 위임합니다. 이 세 기준을 조합하면 대부분의 승인 게이트 삽입 결정을 체계적으로 내릴 수 있습니다.
승인 게이트는 에이전트를 불신해서 다는 것이 아닙니다. 에이전트가 옳더라도 사람이 책임을 나눠 가져야 하는 동작이 있기 때문입니다.
승인 단계 설계 패턴
패턴 1: 인라인 승인 게이트
가장 단순한 패턴입니다. 에이전트 그래프 내에서 특정 노드 직전에 interrupt 노드를 삽입하고, 사람의 응답이 올 때까지 실행을 일시 중단합니다. LangGraph에서는 interrupt_before 또는 interrupt_after 파라미터로 선언할 수 있습니다. 이 패턴의 장점은 구현이 단순하고 흐름이 명확하다는 점입니다. 에이전트 그래프 정의 시점에 어느 노드 앞에서 멈출지 정적으로 선언하기 때문에, 코드 리뷰나 감사(audit)에서 승인 지점을 쉽게 파악할 수 있습니다. 단점은 유연성이 떨어진다는 것입니다. 조건에 따라 동적으로 게이트를 삽입하거나 건너뛰어야 하는 경우에는 적합하지 않습니다.
인라인 게이트는 그래프에 고정된 분기점으로, 흐름 자체가 승인 여부에 따라 갈립니다.
패턴 2: 동적 게이트 (신뢰도 기반)
에이전트가 실행 중에 스스로 판단하여 승인 요청 여부를 결정하는 패턴입니다. 예를 들어, 삭제할 레코드 수가 100건 미만이면 자동 실행하고, 이상이면 승인을 요청합니다. 또는 에이전트의 확신 점수가 임계값 아래일 때 사람에게 위임합니다. 이 패턴은 불필요한 승인 요청을 줄여 사람의 피로감(approval fatigue)을 낮추는 데 효과적입니다. 실제로 모든 동작에 승인을 요구하면 담당자가 승인 요청에 무감각해지는 문제가 생기는데, 이는 보안 분야에서 잘 알려진 경보 피로(alert fatigue) 현상과 동일한 맥락입니다. 동적 게이트는 이를 방지하면서도 위험도가 높은 상황에서는 반드시 개입을 요청합니다.
임계값을 기준으로 게이트 삽입 여부를 동적으로 판단하면, 승인 요청의 빈도와 위험도 사이의 균형을 유지할 수 있습니다.
패턴 3: 다단계 승인 (Multi-Level Approval)
금융, 의료, 법무 등 규제 산업에서는 단일 승인으로는 부족한 경우가 있습니다. 금액이 클수록, 영향 범위가 넓을수록 더 상위 권한자의 승인이 필요합니다. 다단계 승인 패턴은 여러 개의 승인 게이트를 직렬 또는 병렬로 배치합니다. 직렬 배치는 순서대로 각 단계의 승인을 받아야 다음으로 진행되며, 병렬 배치는 여러 담당자가 동시에 검토하고 과반수 또는 전원 동의가 있어야 통과합니다. 이 패턴의 설계에서 주의할 점은 각 승인자가 이전 승인자의 의견에 편향되지 않도록 격리 여부를 결정하는 것입니다. 감사 목적이라면 모든 의견을 공개해도 무방하지만, 독립적인 판단이 중요한 경우에는 각 승인자에게 원본 내용만 제공해야 합니다.
| 승인 구조 | 통과 조건 | 적합한 사례 | 지연 위험 |
|---|---|---|---|
| 단일 승인 | 1명 동의 | 일반적인 운영 승인 | 낮음 |
| 직렬 다단계 | 모든 단계 순서 통과 | 규제 준수·감사 추적 | 높음 |
| 병렬 다수결 | 과반수 동의 | 위원회 의사결정 | 중간 |
| 병렬 만장일치 | 전원 동의 | 고위험·되돌리기 불가 | 매우 높음 |
LangGraph로 Human-in-the-Loop 구현하기
기본 환경 설정
LangGraph는 LangChain 생태계에서 상태 머신 기반의 에이전트 그래프를 구현하는 프레임워크입니다. 버전 0.1 이후부터 체크포인터(Checkpointer)와 interrupt 기능을 공식 지원하여, HitL 구현에 가장 적합한 도구 중 하나입니다. MemorySaver는 개발·테스트용 인메모리 체크포인터이며, 운영 환경에서는 SqliteSaver 또는 PostgresSaver를 사용합니다. 체크포인터의 역할은 그래프 상태를 각 노드 실행 전후에 직렬화하여 저장하는 것입니다. 이를 통해 에이전트가 interrupt로 멈췄을 때 상태가 영속적으로 보존됩니다.
from langgraph.graph import StateGraph, END
from langgraph.checkpoint.memory import MemorySaver
from langgraph.types import interrupt, Command
from typing import TypedDict, Annotated
import operator
# 그래프 상태 정의: 메시지 이력과 승인 컨텍스트를 함께 관리
class AgentState(TypedDict):
messages: Annotated[list, operator.add]
pending_action: dict # 승인 대기 중인 동작 정보
action_approved: bool # 승인 결과
def should_interrupt(state: AgentState) -> bool:
"""임계 동작 여부를 판단하는 함수"""
action = state.get("pending_action", {})
# 삭제·수정·발송 동작은 항상 승인 요청
return action.get("type") in {"delete", "update", "send"}
def approval_node(state: AgentState) -> AgentState:
"""승인 게이트 노드: 사람의 응답을 받을 때까지 대기"""
action = state["pending_action"]
# interrupt()는 현재 상태를 저장하고 실행을 중단
# 재개 시 interrupt()의 반환값으로 사람의 응답이 전달됨
response = interrupt({
"question": f"다음 동작을 승인하시겠습니까?",
"action": action,
})
# 결과: response는 Command(resume={"approved": True/False}) 형태로 전달됨
return {"action_approved": response.get("approved", False)}
# 체크포인터와 함께 그래프 컴파일
checkpointer = MemorySaver()
graph = (
StateGraph(AgentState)
.add_node("approval_gate", approval_node)
# ... 나머지 노드 추가
.compile(checkpointer=checkpointer, interrupt_before=["tool_execution"])
)interrupt()는 현재 노드의 실행을 즉시 중단하고 상태를 체크포인터에 저장합니다. 외부에서 Command(resume=...) 객체를 전달하면 정확히 중단된 지점부터 재개됩니다.
핵심 구현: 승인 흐름 전체
승인 요청을 보내고 응답을 받아 실행을 재개하는 전체 흐름을 구현합니다. 실제 현업에서는 Slack 웹훅, 이메일, 또는 내부 대시보드를 통해 승인 알림을 전송하고, 담당자가 승인 버튼을 누르면 해당 이벤트가 에이전트의 thread_id와 함께 백엔드로 전달되는 구조를 사용합니다.
import asyncio
# 에이전트 최초 실행: 임계 동작 직전에서 interrupt 발생
config = {"configurable": {"thread_id": "workflow-001"}}
initial_state = {
"messages": [{"role": "user", "content": "고객 테이블에서 탈퇴 계정을 삭제해줘"}],
"pending_action": {},
"action_approved": False,
}
# 1단계: 그래프 실행 → interrupt 지점에서 자동 중단
result = graph.invoke(initial_state, config)
# 결과: result["__interrupt__"] = [{"value": {"question": "...", "action": {...}}}]
interrupted_info = result.get("__interrupt__", [])
if interrupted_info:
approval_request = interrupted_info[0]["value"]
# Slack 알림 전송 (실제 구현에서는 webhooks 사용)
print(f"승인 요청: {approval_request['question']}")
print(f"동작 상세: {approval_request['action']}")
# 2단계: 담당자 승인 후 실행 재개
# Command(resume=...) 로 재개 신호와 승인 결과를 함께 전달
from langgraph.types import Command
resume_result = graph.invoke(
Command(resume={"approved": True}),
config # 동일한 thread_id로 상태를 복원
)
# 결과: resume_result에 최종 완료 상태가 담김
print(f"실행 완료: {resume_result['messages'][-1]['content']}")핵심은 첫 번째 invoke 호출과 재개 호출에서 동일한 thread_id를 포함한 config를 사용한다는 점입니다. 체크포인터는 이 ID를 키로 상태를 저장하고 검색합니다.
테스트와 검증
HitL 구현에서 테스트는 두 가지 시나리오를 반드시 커버해야 합니다. 첫째, 승인이 이루어졌을 때 올바른 동작이 실행되는지 확인합니다. 둘째, 거부 또는 타임아웃이 발생했을 때 에이전트가 안전하게 대안 경로로 이동하는지 확인합니다. 타임아웃 처리는 특히 중요한데, 승인 담당자가 부재 중이거나 응답하지 않을 때 에이전트가 무기한 대기 상태에 빠지지 않도록 별도의 만료 정책이 필요합니다.
타임아웃·거부·승인 세 경로 모두를 테스트에서 명시적으로 검증해야 운영 환경에서 예상치 못한 동작을 막을 수 있습니다.
성능과 트레이드오프 분석
지연 시간의 현실
HitL을 도입하면 전체 파이프라인의 지연 시간이 사람의 응답 시간만큼 늘어납니다. 이것은 버그가 아니라 의도된 설계이지만, 시스템 전체 처리량에 영향을 미칩니다. 비동기 처리를 활용하면 한 워크플로가 승인 대기 중인 동안 다른 워크플로가 진행될 수 있어 전체 시스템 처리량 손실을 최소화할 수 있습니다. 실제 프로젝트에서 측정한 결과를 보면, 승인 대기 시간은 업무 시간 기준으로 평균 수 분에서 수십 분 사이였습니다. 이를 SLA로 관리하려면 에스컬레이션 정책이 필요합니다. 1차 담당자가 N분 내 응답하지 않으면 2차 담당자에게 자동 전달하는 체계입니다.
| 처리 방식 | 평균 지연 (승인 포함) | 처리량 | 적합한 워크로드 |
|---|---|---|---|
| 동기·직렬 | 사람 응답 시간과 동일 | 낮음 | 순서가 중요한 단일 흐름 |
| 비동기·병렬 | 최장 경로 기준 | 높음 | 독립적인 복수 워크플로 |
| 배치 승인 | 한번에 N건 처리 | 중간 | 반복적·유사한 동작 다수 |
대안 기술과의 비교
HitL과 자주 비교되는 접근법으로 사후 검토(Post-hoc Review) 방식이 있습니다. 에이전트가 먼저 실행하고, 이후에 사람이 로그를 검토하여 이상이 있으면 롤백합니다. 이 방식은 처리 속도 면에서 우월하지만, 비가역적인 동작(이메일 발송, 결제 처리)에는 적용할 수 없습니다. 반면 HitL은 실행 전 검토를 보장하므로 규제 준수나 감사 목적에 더 적합합니다. 또 다른 대안은 자동 롤백 메커니즘입니다. 에이전트의 모든 동작을 트랜잭션으로 감싸 실패 시 자동 복구합니다. 이는 데이터베이스 작업에는 효과적이지만, 이메일이나 외부 API 호출처럼 트랜잭션 의미론이 없는 경우에는 적용하기 어렵습니다.
비가역성과 롤백 가능 여부를 먼저 판단하고, 그에 맞는 개입 방식을 선택하는 것이 올바른 순서입니다.
어떤 상황에서 HitL을 선택하는가
모든 에이전트에 HitL이 필요한 것은 아닙니다. HitL을 선택해야 하는 상황은 크게 세 가지로 정리됩니다. 첫째, 규제 환경입니다. 금융, 의료, 법무 분야에서는 특정 동작에 대해 사람의 검토 기록이 법적 요건입니다. 둘째, 초기 배포 단계입니다. 에이전트의 신뢰도가 충분히 검증되지 않은 시점에서는 승인 게이트를 통해 실수를 잡고 에이전트를 개선하는 피드백 루프를 만들 수 있습니다. 셋째, 높은 비용의 실수입니다. 대규모 데이터 삭제, 대용량 이메일 발송, 고액 거래처럼 실수의 비용이 자동화의 이익을 크게 초과하는 경우에는 HitL이 합리적인 선택입니다.
운영 환경 적용 시 고려사항
흔한 실수와 함정
HitL을 처음 도입할 때 가장 흔하게 발생하는 문제는 상태 유실입니다. 체크포인터 설정 없이 interrupt를 사용하면 프로세스가 재시작될 때 승인 대기 중인 모든 상태가 사라집니다. 운영 환경에서는 반드시 영속적인 저장소(PostgreSQL, Redis)를 체크포인터 백엔드로 사용해야 합니다. 두 번째 함정은 중복 실행입니다. 재개 신호가 네트워크 문제로 중복 전달될 때 같은 동작이 두 번 실행되지 않도록 멱등성(idempotency) 보장이 필요합니다. thread_id에 고유 실행 ID를 포함시키고, 완료된 실행을 재개하려 할 때 이를 감지하는 로직을 추가해야 합니다. 세 번째 문제는 승인 컨텍스트 부족입니다. 승인 요청 메시지에 담당자가 판단하기에 충분한 정보가 없으면, 담당자는 항상 승인하거나 항상 거부하는 형식적인 절차로 전락합니다.
중복 재개 방지와 상태 유효성 검사는 운영 환경에서 멱등성을 보장하는 최소 조건입니다.
모니터링과 디버깅
HitL이 포함된 에이전트 파이프라인에서 모니터링해야 할 지표는 일반 서비스와 다릅니다. 응답 시간이나 처리량 외에 승인 대기 중인 인스턴스 수, 평균 승인 대기 시간, 승인율 대 거부율, 타임아웃 발생 빈도를 추적해야 합니다. 승인율이 갑자기 낮아졌다면 에이전트가 담당자의 기대에서 벗어난 동작을 생성하기 시작했다는 신호일 수 있습니다. 타임아웃이 자주 발생한다면 에스컬레이션 정책을 재검토해야 합니다. LangSmith 같은 에이전트 추적 도구를 연동하면 각 thread_id별로 전체 실행 경로와 중단 지점, 재개 내역을 시각적으로 확인할 수 있어 디버깅 시간을 크게 단축할 수 있습니다.
| 지표 | 정상 범위 | 이상 신호 | 대응 |
|---|---|---|---|
| 승인 대기 인스턴스 수 | 안정적 | 급증 | 처리 병목·알림 오류 확인 |
| 평균 승인 대기 시간 | SLA 이내 | SLA 초과 | 에스컬레이션 정책 재설정 |
| 승인율 | 80% 이상 | 50% 미만 | 에이전트 동작 품질 검토 |
| 타임아웃 빈도 | 월 5% 이하 | 월 20% 초과 | 담당자 알림 채널 점검 |
확장과 마이그레이션
에이전트 수와 워크플로 복잡도가 늘어날 때 HitL 시스템을 확장하는 방법을 미리 설계해야 합니다. 단일 담당자가 모든 승인을 처리하는 구조는 병목이 되므로, 워크플로 유형이나 영향 범위에 따라 승인 담당자를 분산하는 라우팅 정책을 수립합니다. 또한 에이전트가 충분히 신뢰 가능하다는 것이 데이터로 증명된 동작 유형에 대해서는 점진적으로 HitL 게이트를 제거하는 점진적 자동화 전략을 적용합니다. 이를 위해 초기부터 승인 여부와 실제 결과를 함께 기록하는 피드백 루프를 설계에 포함시켜야 합니다. 거부된 동작이 실제로 잘못된 것이었는지, 아니면 과도한 거부였는지를 사후에 검토할 수 있어야 에이전트의 신뢰도를 객관적으로 평가할 수 있습니다.
승인 이력 데이터가 쌓일수록 어떤 동작 유형에서 에이전트가 신뢰할 수 있는지 판단하는 근거가 됩니다.
맺음말
핵심 요약
이 글에서는 AI 에이전트 자동화 파이프라인에 Human-in-the-Loop를 구조적으로 내장하는 방법을 살펴봤습니다. HitL의 기술적 핵심은 상태 체크포인팅과 외부 이벤트 기반 재개 메커니즘이며, LangGraph의 interrupt와 체크포인터 조합이 이를 실현하는 현실적인 방법입니다. 승인 패턴은 인라인 게이트·동적 게이트·다단계 승인으로 나뉘며, 동작의 비가역성과 영향 범위를 기준으로 적합한 패턴을 선택합니다. 운영 환경에서는 상태 유실 방지, 중복 실행 차단, 에스컬레이션 정책, 승인 품질 모니터링이 핵심 관리 항목입니다.
적용 판단 기준
HitL 도입을 고려할 때 가장 먼저 물어야 할 질문은 "에이전트가 실수했을 때 되돌릴 수 있는가"입니다. 되돌릴 수 없다면, 그리고 그 실수의 비용이 자동화 이익보다 크다면 HitL은 선택이 아닌 필수입니다. 반대로, 에이전트의 신뢰도가 충분히 검증된 저위험 반복 작업에 승인 게이트를 달면 자동화의 이점을 스스로 훼손하게 됩니다. 좋은 HitL 설계는 최대한 적게 개입하되, 개입이 필요한 순간에는 반드시 개입하는 균형에서 나옵니다. 이 균형점은 에이전트의 성숙도와 운영 데이터가 쌓일수록 지속적으로 조정되어야 합니다.