122 lines
4.3 KiB
Markdown
122 lines
4.3 KiB
Markdown
# **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 완비
|
|
- 확장성 및 고부하 처리 능력 증가
|
|
|
|
---
|