Groq, OpenRouter, Ollama, Gemini 사이를 자동으로 전환하는 공급자 불가지론적 (Provider-agnostic) AI
요약
LangChain과 FastAPI를 사용하여 특정 LLM 공급자에 의존하지 않는 공급자 불가지론적(Provider-agnostic) 라우터를 구축하는 방법을 소개합니다. Groq, Gemini, Ollama 등 다양한 모델 간의 자동 전환과 장애 조치(Failover)를 통해 시스템의 유연성과 안정성을 높이는 아키텍처를 다룹니다.
핵심 포인트
- 특정 LLM 공급자에 대한 강한 결합을 방지하여 유지보수성 향상
- 장애 발생 시 자동으로 다른 공급자로 전환하는 Failover 기능 구현
- BaseLLMProvider 추상화를 통해 새로운 모델 추가를 용이하게 설계
- 애플리케이션 로직 수정 없이 최적의 모델 및 비용 선택 가능
🚀 LangChain & FastAPI를 활용한 프로덕션 준비 완료된 Multi-LLM 라우터 구축하기
단 하나의 서비스도 변경하지 않고 Groq, OpenRouter, Ollama, Gemini 사이를 자동으로 전환하도록 CVForbes를 어떻게 설계했는지 소개합니다.
Client
│
▼
...
서론 (Introduction)
**CVForbes**를 구축하면서, 저는 단일 LLM 공급자(Provider)에 의존하는 것이 장기적으로 좋은 전략이 아니라는 것을 빠르게 깨달았습니다.
만약 Groq가 다운된다면 어떻게 될까요?
만약 다른 공급자가 더 저렴하거나 빨라진다면 어떻게 될까요?
만약 애플리케이션의 모든 서비스를 건드리지 않고 새로운 모델을 테스트하고 싶다면 어떻게 해야 할까요?
비즈니스 로직을 특정 공급자에 강하게 결합(Tightly coupling)하는 대신, 저는 애플리케이션과 LLM 사이에 위치하는 공급자 불가지론적 (Provider-agnostic) 라우팅 계층을 설계했습니다.
이제 이력서 맞춤화, 파싱, 자기소개서 생성 및 향후 추가될 모든 모듈을 포함한 모든 AI 기능은 단일 라우터와 통신합니다. 라우터는 어떤 공급자가 요청을 처리해야 할지 결정하고, 필요할 때 자동 장애 조치 (Failover)를 수행하며, 나머지 애플리케이션이 백그라운드에서 무슨 일이 일어나고 있는지 전혀 알지 못하도록 유지합니다.
이 글에서는 해당 시스템의 이면에 있는 아키텍처와 이를 확장 가능하게 만든 설계 결정에 대해 설명합니다.
문제점 (The Problem)
많은 AI 프로젝트가 다음과 같이 시작됩니다:
llm = ChatGroq(...)
response = llm.invoke(prompt)
이 방식은 작동합니다.
하지만 작동하지 않게 될 때까지뿐입니다.
애플리케이션이 성장하면, 공급자를 변경한다는 것은 여러 서비스를 수정하고, 임포트(Import)를 업데이트하며, 코드베이스 전반에 걸쳐 공급자별 로직을 도입해야 함을 의미합니다.
저는 이를 완전히 피하고 싶었습니다.
해결책 (The Solution)
서비스가 LLM 공급자와 직접 통신하도록 허용하는 대신, 모든 요청은 단일 라우터를 통과합니다.
Client
│
▼
...
모든 서비스는 단순히 **"LLM"**을 요청할 뿐입니다.
라우터가 어떤 것을 사용할지 결정합니다.
왜 라우터인가? (Why a Router?)
라우터의 임무는 단 하나입니다:
- 사용 가능한 최적의 공급자 (Provider) 선택.
- 장애 조치 (Failover) 처리.
- LangChain 모델 반환.
그게 전부입니다.
그 외의 모든 것은 제자리에 머뭅니다.
이를 통해 애플리케이션을 깔끔하게 유지하고 공급자들을 상호 교체 가능하게 만듭니다.
공급자 추상화 (Provider Abstraction)
각 공급자는 동일한 인터페이스를 따릅니다.
class BaseLLMProvider:
def get_llm(self):
...
그것이 Groq, Gemini, Ollama, 또는 OpenRouter인지 여부는 중요하지 않습니다.
라우터는 단순히 다음을 호출합니다:
provider.get_llm()
나중에 새로운 공급자를 추가하는 것이 믿을 수 없을 정도로 간단해집니다.
요청 생명주기 (Request Lifecycle)
모든 AI 요청은 동일한 여정을 따릅니다.
Incoming Request
│
...
무언가 빠진 것이 보이나요?
애플리케이션 어디에서도 Groq나 Gemini를 직접 참조하지 않습니다.
이는 의도된 것입니다.
자동 장애 조치 (Automatic Failover)
저의 가장 큰 목표 중 하나는 회복 탄력성 (Resilience)이었습니다.
공급자를 사용할 수 없게 되었을 때 즉시 실패하는 대신, 라우터는 단순히 다음 공급자를 시도합니다.
Groq
│
❌
...
사용자는 공급자가 실패했다는 사실을 알지 못합니다.
그리고 솔직히, 사용자가 알 필요도 없습니다.
상태 추적 (Health Tracking)
매 요청마다 실패한 동일한 공급자를 다시 시도하는 것은 시간을 낭비합니다.
대신, 라우터는 가벼운 상태 (Health) 정보를 유지합니다.
Groq ✅
OpenRouter ✅
Ollama ❌
...
공급자가 반복적으로 실패하면, 다시 정상 상태가 될 때까지 일시적으로 건너뜁니다.
이는 장애 발생 시 불필요한 지연을 줄여줍니다.
의존성 주입 (Dependency Injection)
또 다른 설계 선택은 FastAPI의 의존성 주입 (Dependency Injection)을 사용하는 것이었습니다.
모든 엔드포인트 내부에서 공급자를 생성하는 대신, 공유된 라우터 인스턴스를 필요한 곳마다 주입합니다.
Endpoint
│
Depends()
...
이를 통해 엔드포인트가 인프라가 아닌 비즈니스 로직에 집중할 수 있게 합니다.
왜 이 아키텍처인가? (Why This Architecture?)
이 접근 방식은 CVForbes에 몇 가지 이점을 제공했습니다:
- ✅ 공급자 불가지론적 (Provider-agnostic) 비즈니스 로직
- ✅ 자동 장애 조치 (Automatic failover)
- ✅ 용이한 공급자 교체
- ✅ 책임의 깔끔한 분리 (Clean separation of responsibilities)
- ✅ 단순한 확장성 (Simple scalability)
- ✅ 더 쉬운 테스트 및 유지보수
아마도 제가 가장 좋아하는 부분은 다른 공급자를 추가할 때 애플리케이션의 나머지 부분에 거의 변경 사항이 필요하지 않다는 점입니다.
이 아키텍처는 더 복잡해지지 않으면서도 확장됩니다.
교훈 (Lessons Learned)
AI 애플리케이션을 구축하는 것은 단순히 최고의 모델을 선택하는 것만이 아닙니다.
모델, 공급자 또는 API가 필연적으로 변경될 때에도 계속 작동하는 시스템을 설계하는 것에 관한 것입니다.
초기에 추상화 (Abstraction)에 투자한 작은 노력이 제 애플리케이션 전체가 단일 벤더 (Vendor)에 종속되는 것을 막아주었습니다.
돌이켜보면, 그 결정은 아마도 CVForbes에서 가장 가치 있는 아키텍처적 선택 중 하나였을 것입니다.
다음 단계는? (What's Next?)
라우터는 이미 자동 장애 조치 (Automatic failover)를 포함한 여러 공급자를 지원하지만, 향후 개선할 여지는 충분합니다:
- 지연 시간 인지 라우팅 (Latency-aware routing)
- 비용 인지 공급자 선택 (Cost-aware provider selection)
- 서킷 브레이커 (Circuit breakers)
- 메트릭 및 모니터링 (Metrics & monitoring)
- 동적 공급자 구성 (Dynamic provider configuration)
- 작업별 라우팅 (예: 파싱 (Parsing) vs 생성 (Generation)을 위한 서로 다른 모델 사용)
흥미로운 점은 이 아키텍처가 재설계 없이도 이미 이러한 아이디어들을 지원한다는 것입니다.
마치며 (Final Thoughts)
사람들이 AI 아키텍처를 생각할 때, 종종 어떤 모델을 사용할지에 집중하곤 합니다.
저는 더 나은 질문은 다음과 같다고 생각합니다:
당신의 애플리케이션은 내일 얼마나 쉽게 모델을 전환할 수 있는가?
벤더가 아닌 추상화 (Abstractions)를 중심으로 설계함으로써 CVForbes를 훨씬 더 유지보수하기 쉽고, 탄력적이며, 확장 가능하게 만들었습니다. 공급자는 바뀔 수 있고, API는 진화할 수 있으며, 새로운 모델은 계속 등장하겠지만, 애플리케이션의 나머지 부분은 그 사실을 알 필요가 없을 것입니다.
그것이 바로 제가 목표로 하는 아키텍처입니다. 단일 AI 공급자가 아닌 소프트웨어 엔지니어링 원칙을 중심으로 구축된 아키텍처 말입니다.
⭐ 이 내용이 도움이 되었다면...
라우터(router), 공급자(providers), 상태 관리(health management), 그리고 FastAPI 통합을 포함한 전체 구현체는 함께 제공되는 GitHub repository에서 확인할 수 있습니다.
자유롭게 살펴보시고, 개선 사항을 제안하거나 여러분만의 AI 애플리케이션에 이 아키텍처를 맞춤형으로 적용해 보세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기