모델 아래의 스택: oMLX, llama.cpp, Hermes, 그리고 왜 이렇게 많은가
요약
코딩 성능을 결정하는 요소는 모델뿐만 아니라 가중치 형식, 서빙 엔진, 프롬프트 등 전체 스택에 달려 있습니다. Retort 프레임워크를 통해 다양한 레이어의 구성 요소와 그 상호작용을 분석하고 지도를 제공합니다.
핵심 포인트
- 코딩 결과는 모델, 가중치, 서빙 엔진 등 전체 스택의 조합으로 결정됨
- 단일 레이어(예: 컨텍스트 엔진)의 변경만으로도 성능 수치가 크게 변함
- safetensors, GGUF, MLX 등 가중치 형식은 서빙 엔진과 밀접하게 연결됨
- Retort는 하네스 자체를 측정 가능한 변수로 만드는 메타하네스 개념 도입
Retort는 코딩 스택(coding stacks)을 비교하기 위한 프레임워크로, Claude의 버전별 결과와 64GB M5Pro 환경에서의 다양한 언어에 대한 로컬 테스트 결과를 제공합니다. 이 프레임워크는 Claude와 함께 개발되었으며, 저는 테스트 하네스(harness)와 스택의 모든 구성 요소 때문에 혼란을 느끼고 있었습니다. 그래서 Claude에게 현재 어떤 것들이 있는지, 그리고 그것들이 어떻게 테스트되고 있는지 설명해 달라고 요청했습니다. 그 결과는 다음과 같습니다:
대부분의 벤치마크는 "어떤 _모델(model)_이 가장 좋은가?"라는 질문에 답합니다. Retort는 그것이 잘못된 단위라고 주장합니다. 코딩 결과는 전체 **스택(stack)**에 의해 생성되며, 모델은 그중 하나의 레이어(layer)일 뿐입니다:
언어(language) × 모델(model) × 가중치 형식(weights-format) × 서빙 엔진(serving engine) × 에이전트/하네스(agent/harness) × 컨텍스트 엔진(context engine) × 샘플링(sampling) × 프롬프트(prompt)
어느 한 레이어만 변경해도 수치는 변합니다. TypeScript의 경우, 동일한 모델과 동일한 가중치를 사용했음에도 불구하고 컨텍스트 엔진(context-engine) 노브(knob) 하나를 조절하는 것만으로 로컬 80B 환경에서 실패에서 1.00으로 결과가 바뀌었습니다. 따라서 결과가 의미를 갖기 전에, 여러분은 이 레이어들의 지도가 필요합니다. 이 포스트가 바로 그 지도입니다. 각 구성 요소가 무엇인지, 어디에서 왔는지, 무엇이 무엇과 경쟁하는지, 어떻게 쌓이는지, 그리고 왜 이 생태계(zoo)가 이토록 북적이는지를 다룹니다. 그런 다음 Retort의 메타하네스(metaharness) — 하네스 _자체_를 측정 가능한 변수로 만드는 Retort의 부분 — 에 대해 설명합니다.
다른 블로그에서 oMLX, llama.cpp, Hermes, GGUF, omp, lcm, 또는 OpenRouter가 지나가는 것을 보고 그것들이 정확히 무엇인지 모른 채 고개를 끄덕인 적이 있다면, 여기서부터 시작하세요.
스택, 위에서 아래로
이 내용을 아래에서 위로 읽으십시오. 각 레이어는 그 아래의 레이어 위에 놓여 있습니다:
┌──────────────────────────────────────────────────────────────┐
│ 프롬프트(PROMPT) / 방법론(METHODOLOGY) "테스트를 먼저 작성하라" · BDD · 간결함 │ ← 질문하는 방식
├──────────────────────────────────────────────────────────────┤
...
"어떤 모델이 가장 좋은가"라는 질문은 중간의 한 띠(band)에 대해서만 묻는 것입니다. Retort는 열(column) 전체를 측정합니다. 이 포스트의 나머지 부분에서는 혼란스러운 이름들이 존재하는 레이어들 — 가중치(weights), 서빙(serving), 에이전트(agent), 컨텍스트(context) — 를 살펴본 후, 에이전트(agent) 레이어를 탐색(sweep) 가능한 요소로 만드는 메타하네스에 대해 알아봅니다.
레이어 1: 가중치와 그 형식 — safetensors, GGUF, MLX
모델은 거대한 숫자 더미(가중치)입니다. 이 숫자들이 디스크에 어떻게 저장되는지는 놀라울 정도로 파편화된 원인인데, 파일 형식이 읽는 엔진과 연결되어 있기 때문입니다.
- safetensors — 모델이 일반적으로 게시되는 중립적이고 프레임워크에 구애받지 않는 형식입니다 (오래되고 안전하지 않은 pickle 형식을 대체했습니다). 아래의 두 로컬 형식 모두 보통 safetensors로부터 변환됩니다.
- GGUF — llama.cpp에서 사용되는 형식입니다. 가중치, 토크나이저, 메타데이터를 하나의 독립적인 파일에 묶어 놓은 것으로, 선택된 양자화(quantization) 수준(예:
Q4_K_M≈ 4비트)을 갖습니다. CPU와 비-CUDA GPU에서 잘 실행되도록 설계되었습니다. - MLX — Apple의 형식으로, Apple의 MLX 프레임워크를 위해 제작되었습니다. MLX는 Apple Silicon용 배열/ML 라이브러리이며, Mac의 통합 메모리(unified memory) (CPU와 GPU가 동일한 RAM을 공유)를 활용합니다. 이것이 64GB Mac이 그렇지 않으면 데이터센터 GPU가 필요했을 42GB 모델을 담을 수 있는 이유입니다.
**양자화(Quantization)**는 여기서 또 다른 축입니다. 같은 모델이라도 4비트로 배포된 것은 16비트의 4분의 1 크기이며, 약간의 정확도 손실과 함께 훨씬 빠르게 실행됩니다. Qwen3-Coder-Next-4bit (≈42 GB)를 볼 때, 이는
- llama.cpp — Georgi Gerganov가 C/C++로 작성한 기초적인 오픈 소스 (open-source) 로컬 엔진입니다. GGUF 형식을 읽고, 거의 모든 환경(CPU, CUDA, 그리고 Apple의 Metal GPU API)에서 실행되며, 나머지 생태계가 수렴하게 된 형식을 정의했습니다. 대부분의 로컬 툴링은 llama.cpp이거나 이를 감싼 래퍼 (wrapper)입니다. 서버 바이너리는
llama-server이며, Retort의llamacpp백엔드는--jinja옵션을 사용하여 모델 자체의 채팅/도구 템플릿이 적용되도록 구동합니다. - Ollama — llama.cpp를 기반으로 구축된 친숙한 데몬 (daemon) + 모델 레지스트리 (model registry)입니다 (
ollama run qwen). 로컬 모델을 단 한 줄의 명령어로 쉽게 실행할 수 있게 만들었으며, 대부분의 사람들이 모델을 처음 실행하는 방식입니다. Retort의 초기 로컬 시도는 이를 사용했으나 (아래 참조) 이후 다른 방식으로 전환했습니다. - oMLX — Apple Silicon에서 MLX 형식 모델을 위한 로컬 서버로,
127.0.0.1:8080에서 OpenAI 호환 방식으로 작동합니다. 이는 llama.cpp에 대응하는 MLX 세계의 대응물입니다. 역할은 동일하지만 (가중치(weights) → HTTP를 통한 토큰(tokens)), 형식은 다르며 Mac에 최적화된 Metal/MLX 네이티브 경로를 사용합니다. Retort는 각 실험 경계에서 이를 재시작하며provenance.json에 실질적인 설정을 기록합니다. - cloud APIs — Opus, Gemini, GPT 등을 위한 것으로, 서빙 엔진을 전혀 실행하지 않습니다. 제공업체가 엔진을 실행하며 사용자는 토큰을 대여합니다. OpenAI 형태의 동일한 프로토콜을 사용하므로, 상위 레이어는 토큰이 노트북에서 왔는지 데이터 센터에서 왔는지 신경 쓰지 않습니다.
무엇이 무엇과 경쟁하는가: llama.cpp (GGUF)와 oMLX (MLX)는 두 가지 진지한 로컬 (local) 엔진이며, 주로 포맷과 하드웨어 계보에 따라 나뉩니다. GGUF/llama.cpp는 크로스 플랫폼 (cross-platform)의 기본값이며, MLX/oMLX는 Apple Silicon 네이티브 (native) 경로입니다. Ollama는 성능이 아닌 편의성 (ease) 측면에서 경쟁합니다 (그 내부에는 _llama.cpp_가 들어있습니다). Retort는 현재 serving.backend: omlx | llamacpp 스위치를 통해 두 가지 로컬 엔진을 모두 지원하는데, 이는 어느 하나가 지배적이지 않기 때문입니다. 어떤 모델은 MLX로만 배포되고, 어떤 모델은 GGUF로만 배포되며, 일부 새로운 아키텍처는 한 엔진에 다른 엔진보다 몇 달 먼저 출시되기도 합니다. "어떤 엔진이 이 아키텍처를 지원하는가?"라는 격차는 유망한 모델을 테스트할 수 없는 반복적인 이유가 되는데, 이는 모델의 문제가 아니라 서빙 레이어 (serving-layer)의 문제입니다.
레이어 3: 에이전트 / 하네스 (agent / harness) — claude-code, Hermes, gemini, opencode, omp
서빙되는 모델은 단순히 메시지에 답할 뿐입니다. 소프트웨어를 구축하려면 파일을 읽고, 수정을 작성하고, 테스트를 실행하고, 실패를 읽고, 다시 시도하는 루프, 즉 에이전트 루프 (agentic loop) (종종 "ReAct": 추론(reason) → 행동(act) → 관찰(observe), 반복)가 필요합니다. 그 루프가 바로 **에이전트 / 하네스 (agent / harness)**이며, 이는 실제로 교체 가능한 레이어입니다. 동일한 모델이라도 하네스에 따라 다르게 동작하는데, 이는 하네스마다 도구 (tools)를 제시하는 방식, 재시도하는 시점, 대화를 관리하는 방식이 다르기 때문입니다.
Retort가 지원하는 하네스들:
-
claude-code— Anthropic의 Claude CLI입니다. Retort의 기본 하네스이며, 모든 실행을 채점하는 공정한 spec-gate judge 역할도 겸합니다 (따라서 독립적인 모델이 비(非)Claude 모델까지 모두 공정하게 점수를 매깁니다). -
gemini— Google의 Gemini CLI를 사용하며,gemini-*모델에 사용됩니다. 에이전트는 보통 모델 ID를 따릅니다: Claude ID를 나열하면 claude-code가 나오고, Gemini ID를 나열하면 Gemini CLI가 나옵니다. -
Hermes— Retort가 로컬 모델(oMLX를 통해)을 구동하는 데 사용하는 하네스입니다. 이것이 주요 로컬 경로입니다: Hermes는 ReAct 루프를 실행하고 컨텍스트를 관리하며 (그의 컨텍스트 엔진은 아래의lcm임), oMLX가 토큰을 제공합니다. 참고: 여기서 -
**컨텍스트 엔진 (
lcm)**은 _모델이 실제로 무엇을 보는지_를 결정합니다. 에이전트 기반 코딩 세션은 모델의 컨텍스트 창 (context window)을 빠르게 초과하므로, 엔진은 공간을 확보하기 위해 오래된 턴들을 **압축 (compacts)**합니다. Hermes의 엔진은lcm이며, 핵심 조절 노브 (knob)는context_threshold로, 이는 압축을 수행하는 컨텍스트 창의 비율을 의미합니다. 기본값인0.35에서는 약 92K 토큰에서 압축이 일어나며, 80B 모델은 간헐적으로 _정지 (stalls)_합니다. 이를0.9(
현재 Retort에서 사용 중인 주요 스택: 로컬 무료 경로는
64GB Mac에서 context_threshold 0.9 설정의 lcm을 사용하는 Hermes 기반, oMLX로 서빙되는 Qwen3-Coder-Next 80B, MLX 4-bit입니다 — Python/Go/TypeScript 언어 지원 수준은 1.00이며 비용은 $0입니다.
클라우드 경로는 로컬 스택이 아직 해결하지 못하는 언어 및 고난도 작업을 위해 claude-code를 통한 Claude Opus 4.8을 사용합니다. 프로토콜은 동일하지만, 사용하는 컬럼(columns)이 다릅니다.
메타하네스 (metaharness): 하네스(harness) 자체를 변수로 만들기
위의 모든 내용은 하네스를 실행당 고정된 선택지로 취급합니다. 하지만 하네스는 _전략 (strategies)_의 집합이며, 이러한 전략들은 모델만큼이나 결과에 영향을 미칠 가능성이 큽니다. retort_metaharness 레이어(문서화된 실험적 사이드 브랜치)는 **에이전트 오케스트레이션 하네스 (agentic-orchestration harness)를 스윕 (sweep) 가능한 일급 요소 (first-class factor)**로 만들어, 통계적 분석을 통해 _성능 향상의 어느 정도가 하네스 때문인지, 아니면 순수 모델 때문인지_를 말할 수 있게 합니다.
이는 Retort를 재발명하는 것이 아닙니다. 동일한 디자인 생성기 (design generator), ANOVA (분산 분석), 그리고 Pareto 엔진을 조합하되, 일반적인 언어/모델 위에 세 가지 새로운 요소를 추가한 것입니다.
harness_config — 오케스트레이션 전략 (핵심 요소)
| 레벨 | 기능 |
|---|---|
| base-ReAct | 단순한 단일 에이전트의 추론(reason)→행동(act)→관찰(observe) 루프. 제어 방식: 별도의 트릭 없음. |
| ... |
scaffold — 각 시도에 둘러싸인 추론 구조
none · plan-and-solve (먼저 계획한 후 실행) · reflexion (시도, 자기 비판, 재시도).
model — 저가형에서 프런티어(frontier) 모델까지 아우르는 순수 모델
deepseek-v4-pro · glm-5.2 · opus-4.8 · gpt-5.2. 이들은 OpenRouter(하나의 요청 형식을 여러 제공업체로 라우팅하는 통합 API)를 통해 접근하며, 분석 시 모델 효과와 하네스 효과를 _분리_할 수 있도록 저가형부터 프런티어 모델까지 폭넓게 선택되었습니다.
실제로 테스트하게 될 내용
부분 요인 설계 (fractional-factorial design)는 (한 번에 하나씩 변경하는 대신) 이 모든 요소들을 한꺼번에 교차시키기 때문에, 분산 분석 (ANOVA)을 통해 지표의 분산을 **모델 vs 하네스 (harness) vs 스캐폴드 (scaffold) vs 언어 및 이들의 상호작용 (interactions)**으로 귀속시킬 수 있으며, 어떤 효과가 혼재 (confounded)되지 않고 깔끔하게 추정되었는지 보고할 수 있습니다. 구체적으로, 이는 모델 전용 그리드 (model-only grid)로는 구조적으로 답할 수 없는 질문들에 답을 제공합니다:
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기