
Projects
BizKit AI 서버 설계 기록
FastAPI, Redis Streams, Worker, vLLM, 임베딩 서비스를 조합해 긴 AI 작업을 안정적으로 처리하도록 설계한 과정을 정리했습니다.
설계 목표
BizKit AI 서버는 팀 서비스에서 AI 기능을 담당하는 별도 서버입니다. 주요 기능은 GitHub 활동 기반 HEX 분석, 직무 기반 자기소개 생성, 명함 OCR입니다.
이 서버의 설계 목표는 단순히 LLM을 호출하는 것이 아니었습니다. 처리 시간이 긴 작업을 안정적으로 실행하고, 외부 API 장애를 흡수하며, 프론트엔드가 사용할 수 있는 예측 가능한 결과를 반환하는 것이 핵심이었습니다.
- 긴 작업을 동기 요청에서 분리한다.
- 작업 상태를 조회할 수 있게 한다.
- LLM 출력은 JSON 스키마로 제한한다.
- 규칙 기반 계산과 LLM 생성을 분리한다.
- 임베딩과 벡터 검색은 별도 서비스로 분리한다.
- 외부 API 실패가 전체 서버 장애로 번지지 않게 한다.
전체 구조
AI 서버는 API 서버와 Worker를 분리한 구조로 설계했습니다. FastAPI 서버는 요청을 받고 작업을 등록하는 역할을 맡고, Worker는 Redis Streams에서 작업을 가져와 실제 AI 처리를 수행합니다.
Client
-> FastAPI API Server
-> Redis Streams
-> AI Worker
-> External APIs
-> LLM or OCR
-> Task Result Store
-> Client Polling
사용자는 작업 생성 API를 호출해 task_id를 받고, 이후 작업 상태와 결과를 조회합니다. 이 방식은 LLM과 OCR처럼 처리 시간이 길고 실패 가능성이 높은 기능을 일반 API 요청과 분리하는 데 적합했습니다.
API 책임
FastAPI 서버는 무거운 AI 처리를 직접 수행하지 않습니다. API 계층의 책임은 요청 검증, 작업 생성, 상태 조회, 결과 반환으로 제한했습니다.
- 요청 본문 검증
- 작업 유형 판별
- Redis Streams 작업 등록
task_id반환- 작업 상태 조회
- 작업 결과 조회
대표적인 흐름은 다음과 같습니다.
POST /ai/tasks
-> request validation
-> create task_id
-> append job to Redis Streams
-> return task_id
GET /ai/tasks/{task_id}
-> return status
GET /ai/tasks/{task_id}/result
-> return result when completed
이렇게 나누면 API 서버는 짧고 예측 가능한 요청만 처리하고, 긴 작업은 Worker가 담당하게 됩니다.
Worker 책임
Worker는 Redis Streams에서 작업을 읽고, 작업 유형에 따라 필요한 처리 흐름을 실행합니다.
- HEX 분석 Worker
- 자기소개 생성 Worker
- 명함 OCR Worker
- 작업 실패 처리
- 결과 저장
- 상태 업데이트
작업 처리 중 외부 API 호출이나 LLM 호출이 실패할 수 있기 때문에, Worker는 실패 상태를 명확히 남겨야 합니다. 실패한 작업이 단순히 응답 없는 상태로 남으면 클라이언트가 원인을 알 수 없습니다.
Redis Streams를 선택한 이유
AI 작업은 일반적인 HTTP 요청보다 오래 걸립니다. Redis Streams를 사용하면 작업을 큐에 쌓고 Worker가 순차적으로 소비할 수 있습니다.
선택 이유는 다음과 같습니다.
- API 요청과 작업 실행을 분리할 수 있음
- 작업량이 늘어났을 때 Worker 확장 가능
- Consumer Group을 통해 작업 소비 상태 관리 가능
- Redis 기반이라 팀 프로젝트 규모에서 도입 비용이 낮음
복잡한 메시지 브로커를 도입하기보다, 프로젝트 규모에 맞는 단순한 작업 큐가 필요했습니다. Redis Streams는 그 균형이 맞는 선택이었습니다.
HEX 분석 설계
HEX 분석은 GitHub 활동 데이터를 바탕으로 개발자의 협업 성향을 계산하는 기능입니다. 분석 축은 협업, 소통, 기술, 문서화, 신뢰성, 선호도입니다.
설계에서 중요한 점은 LLM이 점수를 마음대로 만들지 않게 하는 것이었습니다. 그래서 점수의 1차 계산은 GitHub 데이터 기반 규칙으로 처리하고, LLM은 해석과 요약에 사용했습니다.
GitHub username
-> GitHub API data collection
-> metric extraction
-> rule-based score calculation
-> optional LLM summary
-> JSON result
이 구조에서는 관측 가능한 데이터가 점수의 근거가 됩니다. LLM은 계산 결과를 설명하는 보조 역할을 맡기 때문에 결과가 과하게 생성되는 문제를 줄일 수 있습니다.
자기소개 생성 설계
자기소개 생성은 회사, 부서, 직무, 프로젝트 정보를 입력받아 3문장 자기소개를 만드는 기능입니다. 이 기능은 LLM 호출 전에 입력값을 검증하고, 필요한 컨텍스트를 보강하는 과정이 중요했습니다.
Input
-> job relevance check
-> company or department search
-> prompt context assembly
-> vLLM generation
-> JSON parsing
-> validation
-> semantic cache save
직무 관련성 판단은 임베딩 서비스와 벡터 검색을 사용했습니다. 개발과 관련 없는 직무라면 LLM을 호출하기 전에 차단했습니다.
회사와 부서 정보는 검색 API로 보강했습니다. 검색 결과가 충분하지 않은 경우에는 search_confidence를 낮게 두어 결과 신뢰도를 표시할 수 있게 했습니다.
유사한 자기소개 요청은 시맨틱 캐시에 저장했습니다. 같은 의미의 요청이 반복될 때 매번 LLM을 호출하지 않도록 하여 응답 시간과 비용을 줄이는 것이 목적이었습니다.
임베딩 서비스 분리
메인 AI 서버가 임베딩 모델과 Milvus 연결을 직접 관리하면 책임이 커집니다. 그래서 임베딩과 벡터 검색은 별도 FastAPI 서비스로 분리했습니다.
AI Server
-> Embedding Service
-> BGE-m3-ko
-> Milvus
이 분리의 장점은 다음과 같습니다.
- 메인 AI 서버가 임베딩 모델 구현을 몰라도 됨
- 모델 교체 시 API 계약만 유지하면 됨
- Milvus 컬렉션 관리 책임이 한곳에 모임
- 직무 필터링과 시맨틱 캐시가 같은 검색 인터페이스를 사용할 수 있음
임베딩 서비스는 단일 텍스트 임베딩, 배치 임베딩, 컬렉션 생성, 데이터 삽입, 텍스트 검색, 벡터 검색 API를 제공합니다.
LLM 출력 제어
LLM은 자연어 생성에는 강하지만, 서비스 데이터로 바로 사용하기에는 불안정합니다. 그래서 모든 주요 기능에서 출력 스키마를 명확히 제한했습니다.
- JSON 형식 강제
- 필수 필드 명시
- 점수 범위 제한
- 근거 없는 보정 금지
- 파싱 실패 시 재시도 또는 실패 처리
특히 HEX 분석에서는 점수 보정 범위를 제한했습니다. 자기소개 생성에서는 문장 수와 문장 목적을 제한했습니다. OCR에서는 연락처 필드별 후처리를 통해 형식이 맞지 않는 값을 걸러냈습니다.
오류 처리 방향
AI 서버의 오류는 단순히 500 응답으로 끝나면 안 됩니다. 사용자는 작업을 제출한 뒤 상태를 조회하기 때문에, 실패도 작업 상태로 남아야 합니다.
오류는 크게 세 가지로 나누어 다뤘습니다.
- 입력 오류: 요청 검증 단계에서 즉시 실패
- 외부 의존성 오류: Worker에서 실패 상태 저장
- 출력 검증 오류: 파싱 실패 또는 스키마 불일치로 실패 처리
이렇게 나누면 클라이언트는 작업이 아직 처리 중인지, 실패했는지, 결과가 준비되었는지 구분할 수 있습니다.
설계하면서 고려한 트레이드오프
비동기 작업 구조는 단순 동기 API보다 구현량이 많습니다. 작업 ID, 상태 저장, 결과 조회, Worker 실행, 실패 처리를 모두 만들어야 합니다. 하지만 AI 기능처럼 처리 시간이 길고 실패 가능성이 높은 기능에서는 이 복잡도가 필요했습니다.
임베딩 서비스를 분리한 것도 운영 컴포넌트가 늘어나는 선택이었습니다. 대신 메인 AI 서버의 책임을 줄이고, 임베딩 모델과 벡터 DB를 독립적으로 바꿀 수 있는 장점이 있었습니다.
LLM 출력을 강하게 제한하면 모델의 자유도는 줄어듭니다. 하지만 서비스에서는 자유로운 답변보다 안정적인 구조가 더 중요했습니다.
개선하고 싶은 부분
다음 단계에서는 작업 상태 모델과 평가 체계를 더 보강하고 싶습니다.
- 작업 상태 전이 문서화
- 실패 코드 세분화
- 프롬프트 변경 전후 품질 비교용 테스트 세트 구축
- Worker 재시도 정책 추가
- LLM 응답 파싱 실패율 모니터링
- 기능별 입력 샘플과 기대 출력 관리
AI 서버는 한 번 동작한다고 끝나는 서버가 아닙니다. 프롬프트, 모델, 외부 API, 사용자 입력이 계속 바뀌기 때문에 관찰 가능성과 평가 체계를 함께 갖춰야 장기적으로 운영할 수 있습니다.
정리
BizKit AI 서버 설계의 핵심은 긴 AI 작업을 일반 API 요청에서 분리하고, LLM을 예측 가능한 서비스 데이터 생산자로 제한하는 것이었습니다.
FastAPI는 작업 접수와 조회를 담당하고, Redis Streams와 Worker는 긴 작업 실행을 담당합니다. 임베딩 서비스는 벡터 검색 책임을 분리하고, LLM 출력은 JSON 스키마와 후처리로 제한합니다.
이 구조 덕분에 AI 기능을 단순한 모델 호출이 아니라, 상태와 실패를 가진 백엔드 기능으로 다룰 수 있었습니다.
