Trawling

해커톤 심사 도구에서 버그 두 개를 찾았다 — 그러나 둘 다 내가 탈락한 이유가 아니었다

Africa Deep Tech Challenge에 온디바이스 RAG 코딩 어시스턴트 StacksNG를 제출했다가 예선에서 탈락한 개발자가, 심사 도구에서 실제 버그 두 개를 찾아내고 점수를 재계산해 4.6배 차이를 확인했습니다. 그러나 진짜 탈락 사유는 루브릭 어디에도 없던 originality gate — base model을 손대지 않았다는 점이었습니다.

본문

저자는 Nigeria fintech API 네 곳(Paystack, Flutterwave, Monnify, Termii)의 공식 문서를 근거로 답변하는 오프라인 코딩 어시스턴트 StacksNG를 만들어 Africa Deep Tech Challenge 2026에 출품했습니다. 메모리가 아닌 검색에 기반한 RAG 구조로, 답변 전에 실제 문서 구절을 가져와 출처를 인용하고, 스크랩한 780개 청크를 llama.cpp로 전부 온디바이스에서 실행합니다. 클라우드도 API 키도 없고, 결제 코드에 존재하지 않는 엔드포인트가 끼어들 여지가 없습니다. 하지만 준결승(semifinal)에 진입하지 못했고, 피드백도 없었습니다. 그래서 저자는 직접 원인을 파헤치기 시작합니다.

공식을 거스른 7B 선택과 비어 있던 accuracy 배열

StacksNG에는 qwen2.5-coder-7b를 stock 상태로 Q4_K_M 양자화해 탑재했습니다. 대회의 8GB 메모리 제한에 들어가는 가장 큰 모델이며, 이는 실수가 아니라 채점 공식을 의식한 의도적 베팅이었습니다. 저자는 ablation을 보고서에 직접 적었습니다. 같은 파이프라인의 1.5B 버전이 속도와 메모리 효율 항목에서 공식상 약 35점을 더 얻습니다. 그럼에도 저자는 이를 거절했는데, 등록한 테스트 프롬프트 둘 중 하나에서 1.5B가 'import flutterwave'로 시작하는 존재하지 않는 라이브러리 코드를 생성했기 때문입니다. 같은 검색 컨텍스트에서 7B는 실제 문서화된 엔드포인트에 대한 진짜 requests 호출을 작성했습니다. 정확성을 공식보다 택한 결정이었지만, 훗날 이것이 방어해야 할 대상이 아니었다는 것이 드러납니다.

저자는 공개된 준결승 진출작 20개 전부를 git 히스토리째 클론해 기술 보고서를 모두 읽었습니다. SME-Ledger, Jamii Afya, TaxSabi, CodeFellow, ARIS, Homa 등 전원이 LoRA, QLoRA, distillation, 또는 merge로 base model을 어떻게든 수정했습니다. StacksNG는 유일한 stock-model 아키텍처였고, 저자는 이 패턴을 '흥미롭다'고만 분류하고 넘어갔습니다. 이것이 첫 번째 실수였습니다.

자신의 submission.json에는 '"accuracy": []' — 낮은 점수가 아니라 아예 빈 배열이 들어 있었습니다. git log상 이 파일은 한 번 커밋된 뒤 마감까지 손대지 않았습니다. 이를 생성한 adtc-profiler 실행이 accuracy gate를 건너뛰었거나 당시 lm_eval이 설치되지 않았던 것인데, 어느 쪽이든 실패는 조용했습니다. Sacc는 채점 공식의 50%를 차지하므로, 저자는 의도하지 않게 점수의 절반을 0점으로 제출한 셈입니다.

확인된 두 개의 버그

이를 바로잡으려고 lm-eval-harness를 설치해 로컬 llama-server를 통해 자기 모델을 평가하자 200개 요청 전부가 'Invalid logprobs data'로 실패했습니다. 소스를 직접 읽어 확인한 버그는 두 개입니다.

  • 첫째, adtc_profiler의 accuracy.py가 lm_eval을 호출할 때 base_url=local을 넘깁니다. URL이 아닙니다. lm-eval의 GGUF 백엔드는 http://host:port 형태를 요구하고 {base_url}/v1/completions에 POST하므로, 이 줄은 그대로 실행하는 누구에게나 실패합니다.
  • 둘째, lm-eval-harness의 GGUF 백엔드는 echo=true로 echoed prompt 전체의 logprobs가 돌아오는 옛 OpenAI completions 동작을 기대하지만, 현재 llama-server는 새로 생성된 토큰의 logprobs만 반환합니다. harness가 아직 따라가지 못한 상태입니다.

저자는 둘 다 로컬에서 패치했습니다. 깨진 echo 동작을 신뢰하는 대신, grammar rule로 모델이 정답 텍스트 그대로를 생성하도록 강제하고 그 생성물에서 confidence score를 직접 읽었습니다. 대회의 기본 벤치마크인 표준 일반지식 객관식 테스트를 돌린 결과 arc_easy acc_norm = 0.74라는 실수치를 얻었습니다. fine-tune된 준결승 진출작 한 개보다 앞서고 두 개보다 뒤지는 경쟁력 있는 수치였고, 자기 공식에 대입하면 Stotal이 제출치 10.23에서 47.23로 — 아키텍처 변경 없이 숫자 하나가 빠진 것만으로 4.6배 스윙이 생깁니다.

진짜 탈락 사유: 루브릭에 없던 originality gate

그것으로 끝인 줄 알았지만, 탈락 통지에는 다른 문구가 있었습니다. 'Originality Score (0–10): 3', 'Model Origin: Stock model, used as-is: Qwen2.5-Coder-7B-Instruct-GGUF'. Round 1에는 어떤 기술 채점보다 먼저 돌아가는 originality gate가 있었고, RAG 아키텍처도 인용 근거화도 780청크 코퍼스도 언급되지 않았습니다. 문제는 애초에 Sacc가 아니라 모델을 손대지 않았다는 점이었습니다. base_url 버그, echo/logprobs 불일치, 0.74라는 수치까지 일주일간의 디버깅은 전부 실재하고 재현 가능했지만 완전히 잘못된 레이어를 고치고 있었던 것입니다.

저자는 이미 데이터 속에 답이 있었다고 씁니다. 다른 18팀이 전부 fine-tune했다는 사실을 읽고도 '채점 공식상의 트레이드오프'로 분류했지, 자신이 모르는 게이트를 모두가 통과했다는 신호로 읽지 못했습니다. 검사 가능한 부분 — 공식, profiler 소스, 툴링 — 만 보고 있었고, 그 위에서 돌아가며 어떤 코드나 규칙에도 문서화되지 않은 리뷰가 결정적이었습니다.

교훈

저자의 결론은 이렇습니다. 'stock model을 근거화하는 RAG 레이어는 방어 가능한 제품 결정이지만, originality 리뷰어에게는 당신이 무언가 만들었다는 증거가 아니다. 둘은 다른 기준이고, 하나를 통과해도 다른 하나에 대해 아무것도 말해주지 않는다.' 채점 공식이 공개된 해커톤에 나간다면 공식을 읽고 그에 맞춰 최적화하되, 공식에 없는 것도 물어보라고 조언합니다. 심사 과정에는 루브릭이나 툴링에 나타나지 않는 게이트가 있을 수 있고, 존재한다고 가정하고 그래도 살아남는 것을 제출하는 수밖에 없습니다. 자기 코퍼스에 대한 가벼운 LoRA 한 번이면 이 게이트는 통과했을 것이며, 검색·인용·프로덕션 버그 수정 같은 애플리케이션 레이어의 좋은 작업은 모델이 자기 것이라는 증거가 될 수 없었다고 덧붙입니다.

저자는 진출한 18팀에 '내가 건너뛴 부분을 해냈다'며 행운을 빌고, 이번 사후분석에서 찾은 툴링 버그 두 개를 adtc-profiler#4와 lm-evaluation-harness#4158로 업스트림에 보고했습니다.

원문: dev.to / 번역·요약: Trawling

참고 자료

오류 제보