요약 — 오픈 웨이트를 다운로드하는 것은 포터블해 보이지만, 실제 배포 가능한 단위는 5가지 요소로 구성된 튜플입니다: 가중치(weights), 양자화 레시피(quantization recipe), 런타임 커널(runtime kernel), 채팅 템플릿(chat template), 그리고 토크나이저 설정(tokenizer config)입니다. 대부분의 로컬 AI 설정은 첫 번째 요소만 고정하기 때문에, '같은 모델'이라도 다른 장치에서 다르게 출력되는 것입니다. 이 전체 튜플을 버전 관리된 아티팩트(versioned artifact)로 취급하지 않으면, 로컬 AI 스택이 조용히 틀어지게 됩니다.
모두가 오픈 웨이트 모델을 클로즈드 API의 포터블 대안으로 취급합니다. 파일을 다운로드하고 런타임(runtime)을 연결하면 영원히 자신만의 추론 스택(inference stack)을 갖게 된다고 생각합니다. 하지만 이 이야기는 절반만 사실입니다. 가중치 자체는 포터블하지만, 실제로 출력을 생성하는 것—즉, 그 가중치를 둘러싼 전체 런타임 계약(full runtime contract)—은 그렇지 않으며, 거의 아무도 이를 하나의 단위로 버전 관리하지 않습니다.
이는 팀들이 자체 모델을 운영한 지 6개월 만에 직면하는 불편한 발견입니다: 두 엔지니어가 두 대의 장치에서 "같은 모델"을 로드했지만 미묘하게 다른 완성(completion) 결과를 얻었습니다. 가중치가 변해서가 아니라, 가중치를 둘러싼 모든 것이 변했기 때문입니다.
아티팩트는 파일이 아닌 튜플이다 (The artifact is a tuple, not a file)
배포된 로컬 모델은 단일한 것이 아닙니다. 추론 시간(inference time)에 결합되는 다섯 가지 요소입니다:
-
가중치 자체 (The weights themselves)
-
여기에 적용된 양자화 레시피 (quantization recipe) (형식, 보정 데이터, 그룹 크기)
-
순전파(forward pass)를 실행하는 런타임 커널 (runtime kernel)
-
대화를 토큰 시퀀스로 변환하는 채팅 템플릿 (chat template)
-
토크나이저와 그 특수 토큰 설정
대부분의 팀은 이 중 오직 하나만 버전 관리합니다—보통 모델 이름과 양자화 접미사(quantization suffix)로 가중치입니다. 나머지 네 가지는 런타임 기본 설정이 놓아주는 곳에 존재하며, 그 주간에 설치된 서비스 도구(serving tool)의 어떤 버전을 통해 조용히 상속받게 됩니다. 아무도 Jinja 템플릿 개정판을 기록하지 않습니다. 토크나이저의 턴 종료 토큰 매핑(end-of-turn token mapping)을 고정하지도 않습니다. 그들의 추론 엔진(inference engine) 출시 버전 간에 커널 구현(kernel implementations) 차이를 비교 분석(diffs)하지도 않습니다.
오픈 웨이트(Open weights)는 모델 계층에서의 라이선싱 및 포터빌리티 문제를 해결했습니다. 하지만 스택(stack) 계층의 문제는 전혀 해결하지 못했으며, 실제 정확성(correctness)이 존재하는 곳은 바로 이 스택 계층입니다.
채팅 템플릿은 숨겨진 API 계약이다 (Chat templates are the hidden API contract)
채팅 템플릿은 이 튜플(tuple)에서 가장 눈에 띄지 않지만 가장 중요한 부분입니다. 이는 역할 태그가 지정된 메시지 목록을 모델이 보는 실제 토큰 문자열로 변환하는 함수입니다. 역할 태그 주변의 공백을 변경하거나, 시스템 프롬프트(system-prompt) 래퍼를 제거하거나, 어떤 끝 시퀀스(end-of-sequence) 토큰이 턴 경계를 알리는지 업데이트하면 모델의 동작이 바뀝니다. 이는 모델이 더 성능이 떨어진 것이 아니라, 모델을 튜닝할 때와 비교하여 입력 분포(input distribution)가 변경되었기 때문입니다.
이는 내부 API에 브레이킹 체인지(breaking change)를 배포하는 것과 기능적으로 동일하지만, 이를 잡아주는 계약 테스트(contract test)가 없다는 차이가 있습니다. 런타임 프로젝트들은 실제 버그를 수정하기 위해서일 수도 있고, 때로는 모델 발행사로부터의 상위 변경 사항을 추적하기 위해서일 수도 있지만, 릴리스 사이에 기본 템플릿을 업데이트합니다. 배포 시 런타임에서 템플릿을 고정(pinning)하지 않고 "latest"를 가져온다면, 하나의 종속성(dependency)만 업데이트해도 모델과는 전혀 관련 없는 이유로 평가 점수(eval scores)가 변하는 것을 지켜볼 수 있습니다.
이 문제로 인해 어려움을 겪은 팀들은 결국 채팅 템플릿을 직렬화 스키마(serialization schema)를 다루는 방식처럼 취급하기 시작합니다. 즉, 버전 관리 시스템에 커밋하고, 코드 리뷰에서 차이점(diffed)을 확인하며, 예상되는 토큰 수준의 출력과 함께 고정된 대화 세트를 대상으로 테스트하는 것입니다. 이것은 과도한 조치가 아닙니다. 이는 모델이 무엇을 읽는지 직접적으로 결정하는 구성 요소에게 필요한 최소한의 기준입니다.
양자화(Quantization)와 커널은 두 번째로 분리된 종속성이다 (a second, separate lock-in)
선택하는 양자화(quantization) 형식 — 그리고 더 중요하게는 그것을 실행하는 커널(kernel)이 바로 로컬 AI가 벗어나려 했던 정확한 벤더 종속성(vendor lock-in)을 재창조해내는 지점입니다. 한 런타임(runtime)의 커널 구현을 위해 양자화된 모델은 다른 런타임에서 단순히 느리게 실행되는 것 이상으로, 측정 가능한 차이가 있는 출력을 생성할 수 있습니다. 이는 서로 다른 커널들이 행렬 연산 과정에서 다른 반올림(rounding), 융합(fusion), 그리고 수치 정밀도(numerical-precision) 트레이드오프를 만들기 때문입니다. "GGUF"나 "AWQ"는 JPEG처럼 보편적인 형식이 아닙니다. 그것은 특정 실행 엔진이 해당 형식을 해석하는 방식과의 계약과 같습니다.
이는 로컬 AI 런타임을 변경하는 것이 HTTP 클라이언트를 교체하는 것과는 다르다는 것을 의미합니다. 데이터베이스 엔진을 교체하는 것에 가깝습니다. 데이터 형식 자체는 명목상 호환될 수 있지만, 실제 계산된 결과, 성능 특성, 그리고 실패 모드(failure modes)는 런타임에 따라 달라집니다. 초기에 편의를 위해 특정 런타임을 선택한 팀들은 종종 모든 것을 재양자화하고 재검증하지 않고서는 떠날 수 없다는 사실을 발견하게 되는데, 이는 애초에 종속성을 피하기 위해 "호스팅(hosted)" 대신 "오픈(open)"을 선택했을 때 아무도 예상하지 못한 종속성 비용입니다.
실제로 재현성(Reproducibility)이 요구하는 것
만약 프로덕션 환경에서 오픈 웨이트 모델을 운영하고 있다면, 버전 관리해야 할 단위는 체크포인트(checkpoint)가 아니라 튜플(tuple)입니다. 구체적으로 이는 몇 가지 번지르르하지 않은 관행들을 의미합니다:
-
런타임 버전과 그 커널 빌드(kernel build)만 고정하는 것이 아니라, 모델 파일 해시(model file hash)까지 고정해야 합니다.
-
채팅 템플릿(chat template)은 런타임에 번들된 기본값(default)을 신뢰하기보다 자체 저장소(repository)에 포함하고, 모든 의존성 업데이트(dependency bump) 시마다 차이점 분석(diff)을 수행해야 합니다.
-
예상 출력 결과가 있는 작고 고정된 골든 프롬프트 세트(golden set of prompts)를 유지하고 (비결정론적 샘플링의 경우 예상 출력 분포), 가중치, 양자화(quantization), 런타임, 템플릿 또는 토크나이저 등 튜플(tuple)의 어떤 부분이 변경될 때마다 회귀 테스트(regression test)로 실행해야 합니다.
-
양자화를 인프라스트럭처 변화가 아닌 모델 변화로 취급하고, 파인튜닝(fine-tune) 후와 동일한 방식으로 평가 스위트(evaluation suite)를 재실행해야 합니다.
-
사용하는 레지스트리(registry)에서 모든 모델 버전에 대해 전체 튜플을 기록하여, '현재 트래픽에 어떤 모델이 서비스되고 있는가'라는 질문에 절반만 답하는 것이 아니라 실제로 답변할 수 있도록 해야 합니다.
이는 대부분의 팀들이 파일 복사처럼 간단해야 한다고 느끼는 것에 비해 더 많은 프로세스를 요구합니다. 하지만 이러한 복잡성은 항상 존재해 왔습니다. 폐쇄형 API 제공업체들은 안정적인 엔드포인트(endpoint) 뒤에서 이 모든 것을 조용히 흡수하고 처리해주었기 때문입니다. 오픈 웨이트 모델은 그 능력과 책임 모두를 하나의 패키지로 사용자에게 넘겨줍니다. 둘 중 하나만 가질 수는 없습니다.
'그냥 자체 호스팅하는 것'의 실제 비용
이 모든 것이 오픈 웨이트 모델이나 로컬 추론(local inference)에 반대한다는 주장은 아닙니다. 이는 가중치 파일 자체가 전체 이야기인 것처럼 취급하는 것에 대한 비판입니다. 로컬 AI를 홍보할 때의 핵심은 제어력과 재현성(reproducibility)입니다. 사용자는 API 이름 뒤에서 모델을 조용히 교체하는 제공업체의 지배를 받지 않기 때문입니다. 하지만 이러한 이점을 누리려면 실제로 그 통제력을 행사해야 하며, 이는 전체 튜플을 소유하고 테스트해야 함을 의미합니다. 즉, 튜플의 네 오 분의 일이 이번 주에 런타임이 기본으로 제공하는 것에 의존하도록 내버려 두는 것이 아니라 말입니다.
문제를 겪는 팀들은 출처가 불분명하거나 라이선스가 의심스러운(sketchy) 모델을 구동하는 팀들이 아니다. 오히려 잘 알려진 오픈 모델을 올바르게 구동하다가, 전혀 관련 없는 버그 수정(bug fix) 때문에 추론 서버(inference server)를 업그레이드했는데, 그 과정에서 채팅 템플릿(chat template)이 자신들 아래로 이동했다는 사실을 눈치채지 못한 팀들이다. 오픈 웨이트(open weights)가 모델을 포터블하게 만들었을 뿐이다. 그것들은 당신의 스택(stack)을 모델로 만들어주지 않았다. 그 부분은 여전히 당신의 몫이다.