코드를 변경하지 않고 5개의 LLM Provider 간 전환하는 방법
요약
LLM Provider가 다운될 때마다 코드를 수정하고 재배포해야 하는 문제를 해결하는 방법을 제시합니다. 여러 LLM API의 상이한 구조를 통합하여, 마치 하나의 표준화된 API처럼 사용할 수 있는 단일 엔드포인트를 구축하는 것이 핵심입니다.
핵심 포인트
- LLM Provider별로 다른 API 형식 때문에 전환 시 코드를 수정해야 하는 문제가 발생함.
- 자체 추상화 레이어는 API 업데이트에 취약하여 유지보수 비용이 높음.
- OpenAI 형식과 호환되는 단일 통합 API 엔드포인트를 사용하는 것이 가장 효율적임.
지난 화요일, 오후 2시에 프로덕션 이슈를 디버깅하던 중 LLM provider로부터 503 에러가 발생했습니다. 또다시요.
그것은 그 주에 세 번째였습니다. 제가 수동으로 백업 provider로 전환하고, 환경 변수를 업데이트하며, 재배포하는 동안 앱이 15분 동안 다운되었습니다. 모든 것이 끝날 무렵, 저는 고객 세 명을 놓쳤습니다.
저는 생각했습니다: 더 나은 방법이 있어야만 해.
문제점 (The Problem)
대부분의 LLM provider는 자체적인 API 형식을 가지고 있습니다. OpenAI는 한 구조를 사용하고, Anthropic은 또 다른 것을 사용하며, Google도 또 다른 것을 가집니다. 만약 provider를 전환해야 한다면(예: 하나가 다운될 때), 전체 요청 로직을 다시 작성해야 합니다.
제가 말하는 것은 다음과 같습니다:
# OpenAI format
response = openai.ChatCompletion.create(
model="gpt-4",
...
서로 다른 SDK, 서로 다른 요청 구조, 서로 다른 응답 형식입니다. 여러 provider를 지원하려면 각각에 대한 어댑터 레이어(adapter layers)를 작성해야 합니다.
처음에 시도한 방법 (What I Tried First)
저는 자체적인 추상화 레이어(abstraction layer)를 구축하는 것으로 시작했습니다:
class LLMProvider:
def __init__(self, provider_name):
self.provider = provider_name
...
이 방법은 작동했지만, 취약했습니다. provider가 API를 업데이트할 때마다 저는 어댑터를 업데이트해야 했습니다. 저는 기능을 구축하는 것보다 어댑터를 유지 관리하는 데 더 많은 시간을 쓰고 있었습니다.
통합 API 접근 방식 (The Unified API Approach)
그러다가 다른 접근 방식을 발견했습니다: OpenAI 형식과 호환되지만, 내부적으로는 여러 provider로 라우팅되는 단일 API 엔드포인트를 사용하는 것입니다.
코드는 다음과 같습니다:
import os
import requests
...
```}{
그게 전부입니다. 어댑터 레이어도 필요 없고, SDK를 전환할 필요도 없습니다. 모델 이름만 변경하면 됩니다.
[](https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flromqcwqlk6egd0nij1t.jpeg)
## 오류 처리 (API는 실패하기 때문에)
여기서부터가 까다롭습니다. API는 실패합니다. 아주 자주요.
지난 목요일에 이 접근 방식을 테스트하다가 429 에러(rate limit exceeded)를 받았습니다. 제 첫 생각은 즉시 재시도하는 것이었습니다. 나쁜 생각이었죠. 또 다른 429를 받았고, 그다음에도 또 받았습니다. 저는 재시도 루프에 빠졌습니다.
실제로 효과가 있었던 것은 다음과 같습니다:
import time
def chat_with_retry(model, message, max_retries=3):
...
핵심은 **지수 백오프 (exponential backoff)**입니다. 즉시 재시도하지 마세요. 1초 기다린 다음, 2초, 그리고 4초를 기다리세요. 이렇게 하면 API가 복구할 시간을 벌어줍니다.
[](https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fegs994yqvsyimnbmyc5o.jpeg)
## 스트리밍 응답 (Streaming Responses)
실시간 출력(예: ChatGPT)을 원한다면, 스트리밍이 필요합니다. 제가 구현한 방식은 다음과 같습니다:
def chat_stream(model, message):
response = requests.post(
-
통합 API가 시간을 절약해 줍니다. 저는 직접 어댑터 레이어를 구축하는 데 3일을 보냈습니다. 통합 API를 사용했다면 단 3시간 만에 끝낼 수 있었을 겁니다.
-
오류 처리가 매우 중요합니다. 단순히 즉시 재시도하지 마세요. 지수 백오프(exponential backoff)를 사용하세요. 오류를 기록하고, 속도 제한(rate limits)을 모니터링하세요.
-
스트리밍은 그만한 가치가 있습니다. 사용자들은 10초 동안 전체 응답을 기다리는 것보다 토큰이 실시간으로 나타나는 것을 선호합니다.
-
모델 전환은 사소해야 합니다. 제공업체를 전환하기 위해 코드를 50줄이나 변경해야 한다면, 당신의 추상화가 너무 복잡하다는 뜻입니다.
전체 공개(Full Disclosure)
저는 현재 RollTok이라는 프로젝트를 진행하고 있습니다. 이곳은 여러 LLM 제공업체를 위한 통합 API 게이트웨이입니다. 위 코드 예제들은 이 프로젝트를 구축하면서 제가 배운 내용을 바탕으로 작성되었습니다. 직접 사용해보고 싶다면, rolltok.com에서 API 키를 받을 수 있습니다.
하지만 솔직히 말해서, 이 접근 방식은 어떤 통합 API와도 작동합니다. 특정 서비스 자체보다는 패턴이 더 중요합니다.
다음 계획(What's Next)
저는 다음과 같은 주제에 대해 글을 쓸 계획입니다:
- 여러 제공업체에 걸쳐 토큰 사용량을 추적하는 방법
- 요청/응답 로깅을 구현하는 방법
- 더 나은 성능을 위해 연결 풀링(connection pooling)을 설정하는 방법
질문이나 제안 사항이 있다면 댓글로 알려주세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기
