AMD 미니 PC에서 실행된 27B 모델이 오퍼레이터의 버그를 수정했습니다. 그 후, 모델이 선을 넘었습니다.
요약
AMD 소비자용 하드웨어에서 실행되는 27B 모델(Qwopus)이 LLMKube 오퍼레이터의 하드코딩된 타임아웃 버그를 스스로 발견하고 수정했습니다. 모델은 컴파일과 검증을 거쳐 정확한 수정안을 제시했으며, 이 과정은 로컬 AMD 머신에서 엔드 투 엔드로 수행되었습니다.
핵심 포인트
- AMD Strix Halo 기반 로컬 환경에서 27B 모델로 에이전트 코딩 수행
- 하드코딩된 60초 타임아웃 버그를 모델이 직접 감지 및 수정
- 모델이 수정 과정에서 관련 없는 테스트 코드를 생성하는 현상 발견
- Vulkan 및 소비자용 GPU를 활용한 셀프 호스팅 LLM 추론 사례
원문은 llmkube.com/blog/operator-fixed-its-own-bug-on-amd에 게시되었습니다. dev.to 독자들을 위해 교차 게시합니다.
(LLMKube는 NVIDIA, Apple Silicon, AMD 전반에 걸쳐 셀프 호스팅 LLM 추론을 지원하는 오픈 소스 Apache-2.0 Kubernetes 오퍼레이터 (operator)입니다. Foreman은 이 오퍼레이터의 에이전트형 하네스 (agentic harness)입니다.)
지난번 Foreman은 스스로를 위한 기능을 구축했습니다. 이번에는 그 후속편이며, 더 멋진 이야기입니다. 이번에는 제가 방금 배포한 버그를 모델이 직접 수정했고, 수정을 수행한 모델은 제 책상 위에 있는 소비자용 AMD 박스에서 실행되었으며, 전체 과정에서 가장 유용했던 순간은 모델이 틀렸을 때였습니다.
요약 (TL;DR)
- Claude Code를 제가 직접 운영하는 로컬 모델 중 하나에 연결하는 과정에서 오퍼레이터의 실제 버그가 발견되었습니다: 설정값과 관계없이 모든 요청을 조용히 제한해 버리는 하드코딩된 60초 타임아웃 (timeout) 문제였습니다.
- 저는 이를 Foreman에게 맡겼습니다. **Vulkan 위에서 소비자용 AMD Strix Halo 머신에 구동되는 27B 밀집 코더 (dense coder, Qwen3.6 증류 모델인 Qwopus)**를 사용했습니다. 데이터 센터도, NVIDIA도, 클라우드 GPU도 없었습니다.
- 모델은 정확한 수정안을 생성했고, 게이트 (gate)를 통해 컴파일, 검증, 린트 (lint) 및 통과를 확인했습니다. 또한 모델은 관련 없는 코드에 대한 테스트 두 개를 조용히 작성했고, 게이트 역시 이를 통과시켰습니다. 저는 리뷰 과정에서 이를 발견하고 삭제했습니다. 이 간극이 바로 핵심입니다.
- 엔드 투 엔드 (end-to-end) 검증 완료: 정확히 60초 만에 종료되던 요청이 이제 128초 만에 완료됩니다.
0.8.16버전에 반영되었습니다. - 루프의 모든 단계는 제가 소유한 하드웨어에서 실행되었습니다.
1. 제가 직접 실행하는 모델 기반의 코딩 에이전트
이곳의 장비들은 의도적으로 혼합되어 있습니다: NVIDIA 박스, 몇 대의 Apple Silicon 머신, 그리고 128GB의 통합 메모리 (unified memory)를 갖춘 AMD Strix Halo 머신입니다. LLMKube의 목적은 하나의 사양으로 이 모든 장비에서 모델을 서비스하는 것입니다. 따라서 명확한 실험은 에이전트형 코딩 (agentic coding)을 위해 프론티어 모델 (frontier model)을 대여하는 것을 중단하고, 게이트웨이 (gateway)를 통해 제 하드웨어에 있는 제 모델 중 하나로 Claude Code를 향하게 하는 것이었습니다.
짧은 작업들은 잘 작동했습니다. 그러다 진짜 작업을 하나 주었는데, 약 12분 동안 연산을 수행한 후 불투명한 API 에러와 함께 죽어버렸습니다. 모델의 잘못이 아닙니다. 제 잘못입니다. 에이전트(agent)가 오퍼레이터(operator)의 버그를 아주 정면으로 마주친 것입니다.
2. 그것이 드러낸 버그
ModelRouter는 Envoy AI Gateway로 컴파일되는데, 재시도 정책(retry policy)을 생성할 때 시도당 타임아웃(per-attempt timeout)을 다음과 같이 하드코딩했습니다:
"perRetry": map[string]interface{}{
"timeout": "60s", // <- 하드코딩됨
},
Envoy는 라우트 수준의 요청 타임아웃(route-level request timeout)에 더해 이 시도당 타임아웃을 적용합니다. 따라서 긴 생성(generation)을 위해 라우트가 30분으로 설정되어 있었음에도 불구하고, 모든 요청은 조용히 60초로 제한되었고, 결과로 발생한 504 에러는 재시도 목록에 없었기 때문에 재시도 대신 즉시 실패했습니다. 짧은 턴(turn)들은 1분 이내에 완료되어 정상적으로 보였습니다. 프리필(prefill)이 1분을 넘어간 첫 번째 턴이 절벽 아래로 떨어졌습니다.
깔끔하고, 당혹스러우며, 전적으로 자업자득인 상황입니다. 바로 이런 상황을 위해 Foreman이 존재하는 것입니다.
3. 버그를 플릿(fleet)에 넘기기
Foreman은 LLMKube에 내장된 에이전트형 하네스(agentic harness)입니다. 문제를 지정하면, 코더 모델(coder model)이 정해진 규칙(rails) 안의 샌드박스(sandbox)에서 수정 작업을 수행하고, 검증 게이트(verification gate)가 결과가 유효한지 확인한 후 반영됩니다. 여기서 주목할 만한 선택은 하드웨어입니다.
코더는 Qwopus3.6-27B-Coder였습니다. 이는 Claude-Opus의 트레이스(traces)로 학습된 27B 밀집(dense) Qwen3.6 증류(distill) 모델이며, 4비트 양자화(4-bit quantized)되어 일반 소비자용 AMD Strix Halo 머신(gfx1151, Vulkan)에서 llama.cpp의 TurboQuant 포크를 통해 서빙되었습니다. H100도 아니고, NVIDIA 카드조차 아닙니다. 미니 PC급 박스입니다.
저는 #817로 버그를 등록하고, 작업을 해당 파일 하나와 그 테스트로 범위를 제한한 뒤 배포했습니다. 코더(coder)는 해당 함수를 찾아 올바른 수정을 수행했습니다. 즉, 라우터(router)의 규칙과 백엔드(backend) 전체에 걸쳐 실제로 설정된 가장 큰 타임아웃으로부터 시도당 타임아웃을 도출하는 작은 헬퍼(helper) 함수를 만들고, 명명된 상수(named-constant)를 폴백(fallback)으로 사용하도록 했습니다. 모델은 테스트를 작성하고 GO를 제출했습니다. 게이트(gate)는 gofmt, go vet, go build, Linux 타겟 golangci-lint, 그리고 클린 룸(clean room) 환경에서의 전체 테스트 스위트(suite)를 실행했습니다. 모두 통과(All green)되었습니다.
4. 모델이 선을 넘은 지점, 그리고 게이트가 잡아내지 못한 것
여기가 바로 "AI가 내 버그를 고쳤다"라고 올라오는 게시물들이 생략하는 부분입니다.
수정 사항은 정확했고, 자체적인 두 개의 테스트도 훌륭했습니다. 하나는 정책이 설정된 30분 타임아웃을 사용하는지 확인했고, 다른 하나는 60초 폴백(fallback)을 확인했습니다. 하지만 모델은 변경 사항이 전혀 닿지 않은, 완전히 무관한 함수인 기본 경로 컴파일(default-route compilation)에 대한 두 개의 테스트를 추가로 작성해 두었습니다. 이 테스트들은 컴파일되었고, 통과되었습니다. 게이트는 아무런 의견을 내지 않았습니다. 왜냐하면 테스트가 통과(green)되었기 때문이며, 게이트가 측정하는 것은 오직 통과 여부뿐이기 때문입니다.
범위(Scope)는 컴파일러나 린터(linter)가 판단할 수 있는 영역이 아닙니다. 그것은 인간의 판단 영역이며, 리뷰 과정에서 제가 직접 판단했습니다. 저는 변경 사항이 정확히 수정 사항과 그에 따른 테스트만을 포함하도록 무관한 두 개의 테스트를 제거하고, 깨끗한 버전을 푸시(push)했습니다.
이것이 제가 더 큰 모델을 기다리는 대신 하네스(harness)를 구축하는 이유입니다. 데스크톱에서 돌아가는 27B 모델은 Claude가 아니며, 요령은 그것이 Claude인 척하는 것이 아닙니다. 이 모델이 확실하게 해낼 수 있는 일은, 검증된 최저선(floor)을 보장하는 게이트 안에서 올바르고, 컴파일 가능하며, 테스트된 변경 사항을 생성하는 것입니다. 모델이 할 수 없는 일은 범위와 의도(intent)를 판단하는 것입니다. 하네스는 당신에게 검증된 최저선을 무료로 제공하며, 판단은 당신이 가져오는 것입니다. 모델이 아니라 하네스를 신뢰하십시오. 이러한 분업이야말로 이 정도 크기의 모델을 단순한 눈속임이 아닌 진정으로 유용한 도구로 만드는 핵심입니다.
5. 클러스터에서의 증명
테스트 통과가 곧 작동하는 시스템을 의미하지는 않기에, 저는 믿기 전에 검증을 거쳤습니다.
저는 이곳의 모든 빌드 방식과 동일하게 패치된 오퍼레이터 (operator)를 구축했습니다: 클러스터 내부 (in-cluster)에서, 제 노드 위의 kaniko 작업 (job)을 통해, 수정 브랜치 (fix branch)로부터 빌드했습니다. 이를 배포하고, 오퍼레이터가 게이트웨이 정책 (gateway policy)을 다시 생성하는 것을 지켜보았으며, 시도당 타임아웃 (per-attempt timeout)이 실시간으로 60s에서 30m0s로 변경된 것을 확인했습니다. 그 후, 이전에는 1분 지점에서 실패했던 요청을 실행했습니다:
HTTP 200 in 128.1s
수정 전에는 정확히 60초에 504 오류가 발생했습니다. 수정 후에는 2분 동안 깔끔하게 생성되었습니다. 이것이 녹색 체크 표시와 실제로 수정된 버그 사이의 차이이며, 매번 추가로 소요되는 10분의 가치가 충분합니다. 0.8.16 버전에 포함되었습니다.
6. 실행 환경
이 루프의 모든 구성 요소가 어디에 있었는지 살펴보십시오. 코더 (coder): 제 책상 위의 소비자용 AMD 박스. 빌드 (build): 제 노드 위의 작업 (job). 배포 및 검증 (deploy and verification): 저의 클러스터. 원래 코딩 세션 뒤의 모델: 같은 집에 있는 Apple Silicon 머신. 그 어떤 것도 클라우드 GPU, 대여된 데이터센터 플릿 (datacenter fleet), 또는 타인의 클러스터 위에 위치한 컨트롤 플레인 (control plane)을 사용하지 않았습니다.
이것은 각주가 아니라 논지 (thesis)입니다. 셀프 호스팅 추론 (self-hosted inference) 주변의 대부분의 도구들은 여전히 "셀프 호스팅"이 대규모로 운영되는 데이터센터 가속기 랙을 의미한다고 가정합니다. LLMKube는 그 반대를 가정합니다. 즉, 구축할 가치가 있는 최전선은 추론을 수행하고, 이제 그 위에서 에이전트적 작업 (agentic work)을 수행하는 것을, 여러분이 이미 소유하고 있는 이기종 하드웨어 (heterogeneous hardware) 전반에서 잘 작동하게 만드는 것입니다. Apple Silicon, 소비자용 AMD, 몇 개의 NVIDIA 카드, 원격지의 엣지 박스 (edge box). 하나의 오퍼레이터, 하나의 모델 스펙, 여러분이 제어하는 하드웨어.
미니 PC에서 실행된 27B 모델이 이를 서비스하는 오퍼레이터의 실제 버그를 수정하고, 클러스터에서 검증되었다는 사실은 그것에 대한 가장 작고 구체적인 증거입니다. 오늘 날짜로 이것은 또한 실제 이야기입니다.
실행하기
- Quickstart: 몇 분 안에 여러분의 GPU, Apple Silicon, 또는 AMD 박스에서 모델을 서비스하세요.
- 수정 사항: #817, PR #818.
- 이 모든 것의 비용 계산: InferCost.
제가 시도해보지 않은 하드웨어에서 실행해 보신다면, 그 결과를 알려주세요. star는 더 많은 사람들이 이 프로젝트를 찾는 데 큰 도움이 됩니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기