인터페이스 하나로 여러 모델 다루기
어댑터 패턴은 제공자마다 다른 구조를 하나의 인터페이스 뒤에 숨겨. 애플리케이션은 adapter.generate_stream(messages)만 호출하고, 어느 API를 부를지, 요청을 어떻게 만들지, 응답을 어떻게 정규화할지는 어댑터가 알아서 해.
왜 필요한가
- 앱 코드를 바꾸지 않고 모델 교체 — 한 줄만 바꿔 Gemini에서 OpenAI, 다시 로컬 Ollama로 전환할 수 있어.
- 폴백 체인 구성 — Gemini가 실패하면 OpenAI를, 그다음 로컬 모델을 시도해. 어댑터 목록이 곧 체인이야.
- 제공자 A/B 테스트 — 같은 인터페이스 뒤에서 트래픽의 10%를 새 모델로 보낼 수 있어.
- 프롬프트 형식 변환 — 시스템 프롬프트 위치, 도구 호출 구조, 역할 이름처럼 제공자마다 다른 점을 경계에서 흡수해.
추상화의 경계는 어디에 그을까
좁게 그어. 스트리밍 생성 호출과 필요하다면 도구 호출 반복만 추상화해. File API, 캐싱, 임베딩처럼 모든 제공자 기능을 하나의 인터페이스로 합치려 하지 마. 추상화가 너무 넓으면 새 제공자를 넣을 때마다 억지로 끼워 맞춰야 해. 경계는 generate_stream에 두고 제공자 고유 기능은 고유한 모습으로 살게 해.