# **ADR-004** - 제목: **LLM Gateway의 비동기 추론 구조 개선: OCR 및 추론 파이프라인 요청 처리 방식 전환** - 날짜: **2025-06-23** - 상태: 채택됨 (Proposed) - 작성자: **\[김용연 연구원]** --- ## 1. 컨텍스트 (Context) 기존 LLM Gateway에서는 `/ocr`, `/extract/*`, `/general/*` 요청 처리 시 다음과 같은 문제가 발생하였다: - Gateway가 OCR 결과를 **최대 85초간 polling**하여 기다린 뒤, 후속 작업(LLM 추론 또는 최종 결과 응답)을 수행 - 이 구조는 Gateway가 **해당 요청을 점유**한 상태로 유지되므로, 동시에 여러 사용자의 요청을 처리하기 어려워짐 - 클라이언트는 장시간 대기해야 하며, 요청 실패 시 다시 처음부터 재시도해야 하는 구조였음 --- ## 2. 결정 (Decision) LLM Gateway는 **모든 OCR 및 LLM 요청을 비동기 백그라운드 처리 방식**으로 전환하여, Gateway가 요청을 즉시 반환하고 작업은 `asyncio.create_task()`를 통해 처리되도록 구조를 개선하였다. ### 📌 asyncio.create_task()를 사용한 이유: - `asyncio.create_task()`는 FastAPI와 잘 통합되는 **간단한 백그라운드 실행 방식**으로, - Gateway의 요청 핸들러를 차단하지 않으면서 - 요청 수신 즉시 응답을 반환하고 - 이후 파이프라인 작업을 **비동기 이벤트 루프에서 독립적으로 처리**할 수 있게 한다. - FastAPI의 `BackgroundTasks`는 요청 스코프에 종속되어 **작업 진행 중 연결이 끊기면 실행되지 않을 수 있는 단점**이 있음. - `create_task()`를 사용하면 상태 추적, 결과 저장 등 복합 처리가 필요한 **OCR + LLM 파이프라인**에도 유연하게 대응 가능. ### 📌 주요 변경 사항: - 클라이언트 요청 시 `request_id`와 `progress_url`만 응답 - 전체 파이프라인(OCR → LLM → 후처리)은 백그라운드에서 실행 - 최종 결과는 Redis에 저장되며, 클라이언트는 /progress/{request_id}를 통해 상태 및 결과를 직접 조회 --- ## ✅ `/ocr`에서 요청 ### 변경 전 - Gateway가 OCR API에 요청한 후, **최대 85초간 polling** - 완료 여부에 따라 최종 응답을 직접 반환 ### 변경 후 - Gateway는 OCR API에 요청만 전송하고 다른 작업 요청 수행 - 클라이언트에는 다음 정보만 반환: - `request_id`: OCR 작업 식별자 - `status`: 작업 접수 - `message`: 사용자 안내 문구 - `status_check_url`: `/ocr/progress/{request_id}` - 클라이언트는 해당 URL로 결과 조회 --- ## ✅ `/extract/*`, `/general/*`에서 요청 ### 변경 전 - Gateway가 내부에서: 1. OCR API 요청 → 결과 polling 2. 결과 수신 후 LLM API 요청 3. 최종 결과 응답 ### 변경 후 - 전체 파이프라인(OCR → LLM → 후처리)을 `asyncio.create_task()`로 **백그라운드 비동기 실행** - Gateway는 즉시 아래 정보 응답: - `message`: 작업이 백그라운드에서 실행 중 - `request_id`: 상태 추적용 - `status_check_url`: `/extract/progress/{request_id}` - 최종 결과는 Redis에 저장되고, `/extract/progress/{result_id}`로 확인 가능 ``` Client │ ├── POST /extract/outer (input + prompt) │ ↓ │ Gateway → OCR → LLM → Result │ ↓ └── return: request_id + progress URL 백그라운드 작업 ├── OCR API 호출 ├── 추출 텍스트 Redis 저장 ├── LLM API 호출 └── 최종 결과 Redis 저장 ``` --- ## 3. 고려된 대안 - **FastAPI BackgroundTasks** 사용: → 요청 스코프와 연결되어 있어 상태 추적 및 작업 흐름 관리에 제한적 - **Celery를 통한 전체 파이프라인 비동기화** → 복잡성 증가, 구현 부담이 커서 1차 개선으로는 적절하지 않음 - **기존 polling 방식 유지 + 타임아웃 설정 개선** → 구조적 병목 문제 해결 불가 --- ## 4. 결과 - Gateway가 작업을 점유하지 않고 **완전한 비동기 구조** 확보 - 클라이언트 응답 속도 향상 및 사용자 경험 개선 - Redis 기반 상태 추적 및 결과 조회 API 완비 - 확장성 및 고부하 처리 능력 증가 ---