
Projects
BizKit AI 서버 개발 회고: 점수 보정과 중첩 재시도
LLM 점수 보정의 근거 부족을 인정한 과정과 두 계층의 재시도가 GPU 비용 사고로 이어진 경험을 중심으로 AI 서버 개발을 돌아본다.
프로젝트와 맡은 역할
BizKit은 팀 CARO가 개발한 디지털 명함 서비스이다. 나는 AI 서버의 설계와 구현, VLM OCR을 제외한 모델 비교, GPU 모델 서버 배포를 맡았다. FastAPI 서버 배포는 클라우드 팀원이 담당했다. 팀 서비스는 최종 사용자에게 제공했다.
이 글은 기능 목록보다 구현 중 바뀐 판단을 다룬다. 특히 LLM 점수 보정의 근거를 설명하기 어려웠던 일과, 실패 대응으로 넣은 재시도가 불필요한 GPU 작업을 늘린 사고를 돌아본다. 전체 구성은 프로젝트 소개와 아키텍처에 정리했다.
긴 작업을 상태가 있는 작업으로 분리
GitHub API, 검색 API, 모델 추론을 하나의 HTTP 응답 안에서 기다리는 대신 접수와 실행을 나눴다. FastAPI가 작업을 등록하고 Worker가 실행하며, 클라이언트는 task_id로 상태와 결과를 조회하는 구조이다.
이렇게 분리하면 요청이 접수됐는지와 실제 처리가 끝났는지를 구분할 수 있다. 하지만 큐가 있다는 이유만으로 실패와 비용까지 안전하게 처리되는 것은 아니었다.
HEX의 방향을 바꾼 이유
HEX는 GitHub 활동을 규칙에 따라 점수로 환산해 6각 차트로 보여주는 기능이다. 처음에는 그 점수를 LLM이 보정하는 방향도 계획했다. 보정 범위를 제한하거나 JSON 형식을 맞추는 것과, 보정한 점수가 타당하다는 근거를 갖는 것은 다른 문제였다.
나는 LLM 보정값에 정확한 근거를 제시하기 어렵다는 점을 인정했다. 점수는 정형화할 수 있는 지표로만 제공하고, LLM은 서비스에서 동료가 남긴 리뷰를 분석하는 방향으로 바꿨다. GitHub 활동을 점수로 환산하더라도 개발자의 협업 능력 전체를 평가할 수 있는 것은 아니었다.
AI 서버 배포와 제품 연동은 별개였다. 2월 2일에는 리뷰 분석 검증과 점수 보정 로직 분리를 목표로 적었다. 3월 3일에는 AI 서버 배포 현황으로 6각차트&리뷰를 보고했지만, 3월 16일에도 리뷰 기능 연결은 목표로 남아 있었다.
팀은 최종적으로 리뷰 기능을 제외했고, 리뷰 분석은 최종 사용자에게 제공하지 않았다.
재시도가 작업과 비용을 늘린 사고
운영 중에는 두 계층의 재시도가 겹쳐 GPU 비용을 소모하는 사고를 겪었다. 당시 상황은 다음과 같았다.
- 클라우드 팀원이 부하 테스트를 진행했다.
- 서버리스 GPU 할당 과정에서 문제가 발생했다.
- 두 계층에 각각 구현된 “실패하면 재시도한다”는 정책이 중첩되면서 작업이 누적됐다.
- GPU가 할당되자 누적된 작업이 계속 실행됐고, 충전 잔액을 모두 소모했다.
- 이후 프론트엔드와 백엔드에서도 테스트 요청을 보내 대기 작업이 더 쌓였다.
GPU 할당 문제는 계기였다. 불필요한 실행을 키운 원인은 각 계층의 재시도를 전체 호출 경로에서 함께 보지 못한 데 있었다. 한 계층에서 다시 요청하는 동안 다른 계층도 재시도하면, 하나의 실패에 대응하는 작업 수가 늘어날 수 있다.
작업 누적과 재시도 설정
남겨둔 과금 화면에는 여러 시간대에 걸친 GPU 사용이 보인다. 다른 캡처에는 1003 Queued, 0/0 Running, 0 Idle이 표시돼 있다. 이는 실행 중인 워커 없이 작업이 대기하던 상태이다.
1,003건은 대기 건수이지 비용을 발생시킨 실행 건수가 아니다. 잔액을 소모한 뒤의 추가 테스트 요청도 누적됐으므로, 이 숫자로 손실액이나 재시도 배수를 계산할 수 없다.
재시도를 줄인 설정 변경은 AsyncOpenAI의 내부 재시도를 max_retries=0으로 끄고, 애플리케이션의 generate_json() 인자 기본값을 2에서 1로 낮추는 내용이다.
사고 이후의 설계 기준
사고 이후에는 각 계층의 재시도 설정을 따로 보기보다 전체 요청 경로에서 다음을 확인해야 한다고 생각하게 됐다.
- 애플리케이션과 SDK 중 어느 계층이 재시도를 담당하는지 확인한다.
- 호출자가 실패로 판단한 요청도 외부 서버에서는 여전히 실행 중일 수 있음을 고려한다.
- 전체 시도 횟수와 작업 생존 시간, 이미 불필요해진 작업의 취소·만료를 함께 검토한다.
- 부하 테스트에서는 서비스 부하와 유료 모델 호출의 비용 범위를 함께 정한다.
vLLM 배포 문제
또 다른 문제는 RunPod 배포 중 발생한 라이브러리 호환성 오류였다. Qwen 계열 토크나이저에서 all_special_tokens_extended 속성을 찾지 못했고, 버전 조합을 맞춘 뒤에도 OpenAI 호환 서버 계층의 V0/V1 API 충돌이 이어졌다.
당시 대응은 엔진 계층을 V1 방식에 맞추는 것이었다. AsyncLLMEngine에서 AsyncLLM으로 전환하고, AsyncEngineArgs의 임포트 경로와 모델 설정 접근을 수정했다. 제거된 get_model_config() 대신 model_config 속성을 사용하는 변경도 포함됐다.
토크나이저 오류를 고치는 데서 끝나지 않고, 엔진과 서버 계층이 같은 API 세대를 사용하도록 맞춰야 했다. 위 변경은 당시 대응한 내용이며, 현재 환경에 그대로 적용할 수 있는 설치 절차는 아니다.
결과와 남은 한계
AI 서버를 구현하고 팀 서비스에 연결했지만, 모든 초기 계획을 유지하지는 않았다. HEX의 LLM 보정은 근거 부족을 인정하고 방향을 바꿨고, 리뷰 기능은 제품에서 제외됐다. 재시도는 장애를 줄이기 위한 코드였지만 다른 계층의 재시도와 겹쳐 비용 사고를 키웠다.
앞으로 같은 작업을 한다면 모델 품질뿐 아니라 요청이 실패한 뒤 남는 작업과 비용까지 확인하려 한다.
