SHIFTONE

60일 뒤 사라지는 모델, 설정 한 줄 바꾸고 끝내도 될까요 — LLM 모델 교체를 배포처럼 다루는 평가 게이트

은퇴 공지와 5분의 1 가격 모델이 같은 주에 왔어요. 모델 ID 한 줄을 바꾸면 temperature 가 400 을 내고, 검색 도구 호출은 조용히 줄어요. LLM 모델 교체를 코드 배포처럼 다루는 세 겹, 게이트웨이 역할 이름 · 골든셋 평가 게이트 · 트래픽 일부 비교와 설정 한 줄 롤백을 정리했어요.

비에 젖은 철로 두 갈래가 분기점에서 갈라지는 모습을 위에서 내려다본 사진 — 한 선로에서 다른 선로로 옮겨 타는 전환점

9월 30일에 메일이 왔어요. 지금 쓰는 모델이 11월 30일에 은퇴한다고요. 다음 날엔 다른 공급자가 모델 세 종의 종료일을 공지했고, 그 전 주 DevDay 에서는 "거의 같은 성능"이라는 새 모델이 기존 가격의 5분의 1로 나왔어요. 10월 1일과 2일 이틀에만 모델 일곱 종이 더 나왔고요.

바꿔야 할 이유가 한 주에 두 개 온 거예요. 하나는 사라져서, 하나는 싸져서요. 담당자는 설정 파일에서 모델 ID 한 줄을 고치고요. 그 한 줄이 코드 배포와 같은 무게라는 걸 보여주는 게 400 에러예요.

모델 ID 한 줄은 설정 변경이 아니라 배포예요

같은 프롬프트, 같은 코드인데 모델만 바뀌면 두 종류의 일이 생겨요. 시끄럽게 깨지는 것과 조용히 달라지는 것이에요. 시끄럽게 깨지는 건 차라리 나아요. Anthropic 은 Opus 4.7 부터 temperature 를 기본값이 아닌 값으로 보내면 400 을 돌려줘요. Fable 5.1 에서는 특정 도구를 강제로 부르는 tool_choice 가 400 이고요. 요청이 실패하니 당일에 알아요.

조용히 달라지는 게 문제예요. 같은 가이드에 낮은 effort 에서는 검색 도구를 부르지 않고 기억으로 답하는 경우가 이전보다 많다고 적혀 있어요. RAG 를 붙여 둔 서비스라면 에러는 한 건도 없는데 출처 없는 답이 늘어나요. 지난 관측성 글에서 본 바로 그 모양이에요.

그래서 저희는 모델 교체를 배포처럼 다뤄요. 배포에 있는 테스트 · 카나리 · 롤백을 모델에도 세 겹으로 두는 거예요.

첫째 겹, 코드에서 모델 이름을 떼어내요

코드가 모델 ID 를 직접 들고 있으면 교체는 코드 수정이고, 롤백은 재배포예요. 그래서 코드는 역할 이름만 부르고, 실제 모델은 게이트웨이 설정이 정하게 해요. LiteLLM 라우터가 이 방식이에요. model_name 하나에 실제 배포 여러 개를 묶어요.

model_list:
  - model_name: ticket-triage                # 코드는 이 이름만 알아요
    litellm_params:
      model: anthropic/claude-sonnet-5-5           # 후보
  - model_name: ticket-triage-stable
    litellm_params:
      model: anthropic/claude-sonnet-4-5-20250929  # 현재
router_settings:
  fallbacks: [{ ticket-triage: [ticket-triage-stable] }]

달라진 건 롤백이 설정 한 줄이 됐다는 점이에요.

둘째 겹, 골든셋이 승인 도장을 찍어요

후보 모델이 역할 이름 뒤에 들어가려면 시험을 통과해야 해요. 시험지는 골든셋 글에서 만든 그 20~40문항이에요.

도구는 promptfoo 를 써요. 같은 프롬프트를 현재 모델과 후보 모델에 나란히 돌리고, 문항마다 통과 조건을 적어요. OpenAI 의 자체 Evals 플랫폼도 11월 30일에 종료돼요. 평가 도구도 바뀌니 결과는 도구 밖 파일로 남겨요.

prompts: [file://prompts/ticket-triage.txt]
providers:
  - anthropic:messages:claude-sonnet-4-5-20250929   # 현재
  - anthropic:messages:claude-sonnet-5-5            # 후보
defaultTest:
  assert:
    - type: is-json                                 # 형식은 전부 통과해야 해요
    - type: javascript
      value: "['billing','bug','howto'].includes(JSON.parse(output).category)"
tests: file://golden/ticket-triage.csv              # 40문항

달라진 건 "느낌상 괜찮다"가 "40문항 중 몇 개가 형식을 깼는지"로 바뀐 거예요. 통과 기준은 두 줄이에요. 형식 조건은 전부 통과할 것, 정확도는 현재 모델보다 낮지 않을 것.

모델 교체 = 배포 — 코드는 안 바뀌고 설정과 시험만 움직여요 계기은퇴 공지 (60일~)새 모델 · 가격 1/5 게이트웨이 설정역할 이름 뒤에 후보 등록코드는 역할 이름만 알아요 골든셋 평가형식 조건 전부 통과정확도 ≥ 현재 모델 소량 트래픽 비교형식 오류율 · 도구 호출 횟수출력 길이 · 비용 나란히 전환역할 이름 → 후보 모델 시험 탈락 롤백 = 설정 한 줄 어느 단계에서 실패하든 역할 이름을 현재 모델로 되돌려요. 재배포가 아니라 설정 되돌리기예요. 게이트웨이 설정 파일 자체도 골든셋을 통과해야 배포되는 대상에 넣어요.

셋째 겹, 전부 바꾸지 않고 일부만 먼저 보내요

골든셋을 통과해도 실제 요청은 골든셋보다 넓어요. 그래서 게이트웨이에서 트래픽 일부만 후보로 보내고, 형식 오류율 · 도구 호출 횟수 · 출력 길이 · 비용을 현재 모델과 나란히 봐요. 출력 길이는 꼭 봐야 해요. 단가가 5분의 1이어도 답이 두 배 길어지면 기대만큼 안 싸지거든요. 토큰 비용 글에서 본 것처럼 비용은 단가가 아니라 길이에서 와요.

AS-ISTO-BE
무엇코드에 모델 ID, 교체는 코드 수정코드엔 역할 이름, 교체는 게이트웨이 설정
검증몇 번 돌려 보고 느낌으로골든셋 자동 평가 + 트래픽 일부 비교
문제은퇴 공지가 오면 영향 범위부터 찾아야 해요역할 이름별 모델이 한 파일에 있어 바로 보여요
롤백재배포설정 한 줄

바로 믿으면 안 되는 세 가지

하나, 골든셋이 작으면 통과가 의미 없어요. 40문항으로는 형식 깨짐은 잡아도 드문 오답은 못 잡아요. 트래픽 일부 단계를 빼지 않는 이유예요.

둘, 은퇴 일정은 공급자마다, 플랫폼마다 달라요. Anthropic 은 최소 60일, OpenAI 는 정식 모델 기준 최소 6개월 전에 공지해요. 같은 모델이라도 Bedrock 이나 Vertex 는 일정이 따로라, 게이트웨이 설정에 은퇴일을 같이 적어 둬요.

셋, 게이트웨이도 하나의 의존이에요. 라우터 라이브러리가 버전을 올리면 설정 문법이 바뀔 수 있어요. 그래서 설정 파일도 골든셋을 통과해야 배포돼요.

한 줄로 끝내도 되냐고요

네, 한 줄로 끝나게 만드는 게 목표예요. 다만 그 한 줄 앞에 골든셋이, 뒤에 트래픽 일부와 롤백이 있을 때요. 모델이 한 달에 몇 번씩 바뀌는 동안 저희가 고정하려는 건 모델이 아니라 바꾸는 절차예요. 완성된 절차는 아니고, 은퇴 공지가 올 때마다 한 줄씩 늘어요.

자주 묻는 질문

Q. 모델 하나만 쓰는데도 게이트웨이가 필요한가요? A. 역할 이름과 실제 모델을 분리하는 게 핵심이라, 설정 파일 하나와 얇은 래퍼로도 돼요.

Q. 골든셋의 정답은 누가 만드나요? A. 그 업무를 아는 담당자예요. 운영 중 틀렸던 질문을 모아 두면 자연스럽게 커져요.

Q. 새 모델이 더 낫다는데 굳이 현재 모델과 비교해야 하나요? A. 공개 벤치마크가 아니라 우리 프롬프트와 출력 형식에서 나은지가 문제예요. 거꾸로 가는 문항이 나올 수 있어서 비교해요.

Sources