가상 스레드(Virtual Threads) 차단 중단하기: Spring AI를 활용한 비동기식 Human-in-the-Loop AI 에이전트
요약
Spring AI를 사용하여 인간의 승인이 필요한 Human-in-the-Loop(HITL) AI 에이전트를 구축할 때, 가상 스레드 차단을 방지하는 비동기 아키텍처 설계 방법을 다룹니다. 상태를 메모리가 아닌 Redis 등에 직렬화하여 저장함으로써 시스템 확장성을 확보하는 Stateless 에이전트 구현 패턴을 제안합니다.
핵심 포인트
- 인간의 승인 대기 시 스레드를 차단하지 말고 비동기 이벤트 방식으로 처리할 것
- 에이전트의 ReAct 루프 상태를 Redis 등에 직렬화하여 저장할 것
- Spring AI의 커스텀 ChatMemory를 활용해 상태를 동적으로 복원할 것
가상 스레드(Virtual Threads) 차단 중단하기: Spring AI를 활용한 비동기식 Human-in-the-Loop AI 에이전트 구축
2026년에는 자율적인 AI 에이전트가 인간의 감독 없이 고위험 기업용 도구를 실행하도록 두는 것이 운영상의 리스크가 되겠지만, 관리자의 Slack 승인을 기다리며 플랫폼 스레드(Platform Threads)나 심지어 Project Loom의 가상 스레드(Virtual Threads)를 몇 시간 동안 차단(Blocking)하는 것은 명백한 아키텍처 설계 오류입니다. 우리는 동기식 실행 루프(Synchronous execution loops)에서, 인간 참여형(Human-in-the-loop, HITL) 중단이 발생하는 동안 LLM의 추론 상태가 직렬화(Serialized)되고 영속화(Persisted)되는 상태 비저장(Stateless), 이벤트 기반 에이전트 하이드레이션(Agent hydration) 방식으로 전환해야 합니다.
대부분의 개발자가 실수하는 이유
-
가상 스레드(Virtual Threads) 남용: 가상 스레드(
VirtualThreadExecutor)가 대기 문제를 해결할 것이라고 생각하지만, 그렇지 않습니다. 4시간 동안의 인간의 커피 휴식 시간을 위해 리소스를 열어두는 것은 시스템 확장성(Scalability)을 파괴하고 커넥션 풀(Connection pools)을 망가뜨립니다. -
메모리 내 상태 저장(State-in-Memory) 안티패턴: 활성 ReAct 루프 상태(활성
ChatMemory또는 에이전트 컨텍스트와 같은)를 로컬 힙(Heap) 메모리에 저장하여 -
명시적 중단 예외 (Explicit Interrupt Exceptions): 고위험 도구 (High-risk tool)가 트리거될 때, 직렬화된
stateId와 도구 실행 메타데이터를 포함하는 특화된AgentSuspensionException을 발생시킵니다. -
상태 복원 (State Hydration): 특정 메시지 인덱스에서의 스냅샷 (Snapshotting)을 지원하는, Redis 기반의 커스텀
ChatMemory구현체와 함께 Spring AI의ChatClient를 사용합니다. -
비동기 재개 (Asynchronous Resumption): 인간의 결정을 수락하고, 이를
ToolResponseMessage로서 직렬화된 히스토리에 병합하며, ReAct 루프의 다음 단계를 트리거하는 상태가 없는 (Stateless) REST 엔드포인트/api/v1/agent/resume를 노출합니다.
Show Me The Code
@PostMapping("/agent/resume")
public ResponseEntity<String> resumeAgent(@RequestBody ApprovalResponse approval) {
// 1. Redis에서 직렬화된 채팅 히스토리 (ReAct 상태)를 검색
...
Key Takeaways
- 인간을 위해 차단하지 마세요 (Never block on humans): 인간의 승인을 장기적인 동기식 I/O 작업이 아닌, 비동기적이고 이벤트 중심적인 입력으로 취급하십시오.
- 프롬프트 히스토리를 직렬화하세요 (Serialize the prompt history): 도구 호출 사이에 에이전트가 완전히 상태가 없는 (Stateless) 상태를 유지할 수 있도록, 정확한 LLM 프롬프트/응답 상태를 Redis 또는 Postgres에 저장하십시오.
- Spring AI의 모듈성을 활용하세요 (Leverage Spring AI's modularity): 커스텀
ChatMemory어댑터를 사용하여 필요에 따라 컨텍스트 윈도우 (Context window)를 동적으로 복원 (Hydrate) 및 제거 (Dehydrate) 하십시오.
주의 사항: 이러한 패턴이 실제 인터뷰 문제에 적용된 사례를 보고 싶다면, javalld.com에서 트레이스(Trace)가 포함된 전체 머신 코딩 솔루션을 확인할 수 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기