36개 AI 모델을 테스트하여 가짜 패키지를 확인했고, 아무것도 찾지 못했다는 결과
요약
본 글은 AI 모델의 코드 생성 능력을 검증하기 위해 'hallucinated-packages'라는 벤치마크를 개발하고 그 결과를 공유합니다. 이 벤치마크는 모델이 존재하지 않는 패키지나 함수를 사용하는 경우(환각)를 찾아내어, 실제 레지스트리 조회와 비교하는 방식으로 작동합니다.
핵심 포인트
- AI 모델의 코드 생성 시 가짜 패키지 사용 문제(slopsquatting)를 지적함.
- 벤치마크는 PyPI/npm 등 라이브 레지스트리와 비교하여 환각을 검증함.
- Version 7은 니치 작업, 의도적 가짜 패키지, 제거된 심볼 등을 포함해 난이도를 높임.
- 최첨단부터 오픈 모델까지 총 8개 계열의 36개 모델을 테스트했음을 언급함.
_이 글은 Kaggle Benchmarking Challenge에 제출하는 내용입니다.
제가 벤치마킹한 것들
저는 hallucinated-packages라는 벤치마크를 만들었습니다. 아이디어는 간단합니다. AI 모델에게 코드를 작성하도록 요청하고, 그 모델이 사용하는 모든 import 구문을 추출합니다. 그리고 각각의 패키지를 실제 PyPI와 npm 레지스트리와 비교하여 확인하는 것입니다.
만약 모델이 pip install totally-real-package-i-promise라고 말했는데 해당 패키지가 존재하지 않으면 실행이 실패합니다.
제가 왜 이것에 관심을 가졌는지 설명하자면, 저도 그런 경험을 했기 때문입니다. 챗봇에게서 코드를 복사하여 pip install something을 실행했는데 터미널에서 패키지가 존재하지 않는다고 나오는 경우 말이죠. 정말 짜증납니다. 작업 흐름(flow)을 끊고, 심지어 나중에 누군가 가짜 이름을 등록할 경우 보안 문제로 이어질 수도 있습니다. 이를 slopsquatting이라고 합니다.
그래서 저는 라이브 레지스트리 조회와 추측이 아닌 방식으로 이것을 자동으로 확인하는 벤치마크를 원했습니다.
Version 6
Version 6에는 14개의 프롬프트가 포함되어 있습니다. 파이썬(Python) 작업 9개와 HTTP 재시도, 비밀번호 해싱, 웹 스크래핑, PostgreSQL 쿼리 같은 자바스크립트(JavaScript) 작업 5개가 있습니다.
Version 7
Version 6은 너무 쉬운 결과가 나왔기 때문에, 저는 새로운 작업으로 hallucinated-packages-v7을 만들었습니다. Version 6은 그대로 유지됩니다. 여기에는 네 가지 그룹에 걸쳐 17개의 프롬프트가 있습니다.
- A. 니치(Niche) 작업. 서드파티 라이브러리가 필요합니다. CRAM 파일, LAZ 포인트 클라우드, SWIFT MT940 명세서, FLAC 오디오, DICOM 이미지, FIX 프로토콜 로그 등이 포함됩니다.
- B. 의도적으로 가짜 패키지 사용. 프롬프트가 모델에게 존재하지 않는 라이브러리를 사용하도록 지시합니다. 저는 실행하기 직전에 PyPI와 npm에서 모든 가짜 이름을 다시 확인했습니다. 20개의 조회 모두 누락(missing)으로 나왔습니다.
- C. 적합한 라이브러리가 없는 경우. 정답이 수동으로 작성된 파서인, 발명된 파일 형식이 포함됩니다.
- D. 현재 API. pandas의
DataFrame.append나 Express 5의app.del처럼 최신 버전에서 제거된 심볼들입니다.
체커는 세 가지를 별도로 보고합니다. 환각 패키지 비율(Hallucinated package rate), 환각 함수 비율(Hallucinated function rate), 그리고 적대적 통과율(Adversarial pass rate)입니다. 함수에 대해서는 게시된 휠(wheel) 또는 타르볼(tarball)을 읽습니다. 절대 아무것도 설치하지 않습니다. 패키지를 읽지 못하면 해당 심볼을 미검증(unverified)으로 표시하고 실패로 간주하지 않습니다.
어떤 모델 실행 전에도 두 개의 게이트를 통과해야 합니다. 첫 번째는 모든 하이픈(-) 및 언더스코어(_) 표기법의 5개 식재 이름에 대해 양쪽 레지스트리에서 재검사를 수행하는 미끼 게이트(decoy gate)입니다. 이는 총 20번의 조회(lookups)를 의미합니다. 다음은 카나리아 테스트입니다. 실제 패키지와 누락된 패키지, 그리고 실제 함수와 무의미한 함수가 포함됩니다. 이 두 가지 모두 통과했습니다.
테스트 모델
버전 6
저는 8개 계열의 36개 모델을 대상으로 실행했습니다. 저는 최첨단(frontier) 모델부터 예산이 적은 모델, 그리고 오픈 가중치 옵션에 이르기까지 전반적인 커버리지를 원했습니다. 흥미로운 질문은 단순히 최고의 모델이 할 수 있느냐가 아니라, 더 저렴한 모델을 사용했을 때도 이것이 깨지느냐입니다.
Claude 계열 (9개 모델)
- Opus 5 및 Opus 4.8 및 4.7 및 4.6 및 4.5
- Sonnet 5 및 Sonnet 4.6 및 Sonnet 4.5
- Haiku 4.5
Gemini 계열 (10개 모델)
- 2.5 Pro 및 2.5 Flash
- 3 Flash Preview 및 3.1 Pro Preview 및 3.1 Flash Lite Preview
- 3.5 Flash 및 3.5 Flash Lite 및 3.6 Flash 및 3.7 Flash 및 3.8 Flash
GPT 및 OpenAI 계열 (8개 모델)
- GPT-6 Astra 및 GPT-5.6 Terra 및 GPT-5.6 Luna 및 GPT-5.5
- GPT-5.4 및 GPT-5.4 Mini 및 GPT-5.4 Nano
- GPT-OSS 20B
기타 계열
- Qwen (Qwen3 Coder 480B를 포함한 3개 모델)
- Grok (추론 버전과 비추론 버전의 Grok 4.20)
- DeepSeek R1 및 GLM-5 및 Gemma 4 (26B 및 31B)
Kaggle이 '모델을 찾을 수 없음(model not found)' 오류를 반환했기 때문에 결과가 나오지 않은 모델은 4개였습니다. 이들은 claude-opus-4-1, claude-sonnet-4, grok-4.5, 그리고 grok-4.6이었습니다. 완료된 36개 모델을 통해 저는 총 504개의 프롬프트 평가를 받았습니다.
버전 7
버전 7은 두 개의 모델을 사용한 파일럿 테스트입니다. Kaggle의 gemini-3.7-flash와 로컬 환경의 gpt-5.4-nano를 사용했습니다. 구글과 OpenAI에서 각각 하나씩, 그리고 저가형과 중급형 모델을 선택했습니다. 또한 최신(frontier) 모델인 claude-opus-5도 계획했으나, 17개의 프롬프트 모두에서 '모델을 찾을 수 없음' 오류가 발생하여 제외되었습니다.
분석 결과 (Findings)
버전 6. 의심스러울 정도로 완벽한 점수판
완료된 모든 모델이 환각률(hallucination rate) 0%를 기록했습니다. 36개 모두 통과했으며, 가짜 패키지는 발견되지 않았습니다.
| 지표 (Metric) | 결과 (Result) |
|---|---|
| 성공적으로 테스트된 모델 수 | 36 |
| ... |
The '404'는 제3자 패키지가 참조되었으나 존재하지 않은 횟수이며, 404개의 다른 패키지를 의미하는 것이 아닙니다. 모든 조회 결과는 PyPI나 npm에서 실제 존재하는 것으로 확인되었습니다.
작은 모델과 큰 모델이 일치했습니다. gpt-5.4-nano가 gpt-6-astra처럼 통과했고, claude-haiku-4-5가 claude-opus-5처럼 통과했습니다.
완벽한 점수가 문제가 되는 이유
벤치마크는 모델을 구분할 수 있을 때만 유용합니다. 제 벤치마크는 '천장 효과(ceiling effect)'를 보였습니다. 많은 프롬프트가 표준 라이브러리만으로 해결될 수 있었습니다. JavaScript의 디바운스(debounce)나 HTTP 재시도(retries) 기능은 36개 모델 중 어느 것도 제3자 패키지를 사용하지 않았습니다. 그리고 모델들이 무언가를 가져와야 할 때, 유명한 라이브러리들만 선택했습니다.
더 깊은 문제점도 있었습니다. 제가 확인한 것은 단순히 패키지가 존재하는지 여부뿐이었습니다. 가짜 함수를 가진 실제 패키지는 통과할 수 있습니다. 레지스트리 존재 유무는 단지 첫 번째 관문일 뿐입니다.
버전 7 파일럿 결과
솔직히 말씀드리자면, 이것은 전체 테스트가 아닌 파일럿(pilot)입니다. 모델은 두 개뿐이며 그 이상이 아닙니다.
| 지표 (Metric) | gpt-5.4-nano (로컬 파일럿) | gemini-3.7-flash (Kaggle) |
|---|---|---|
| 시도 횟수 (Attempts) | 17개 프롬프트에 걸쳐 39회 | 17 |
| ... |
Nano는 프롬프트당 최대 3번까지 로컬에서 실행되었기 때문에, 단일 Nano 점수를 인용하고 있지는 않습니다.
Gemini가 프롬프트를 믿었다
gemini가 생성한 세 가지 환각 패키지는 프롬프트가 사용하도록 지시한 미끼였습니다. xlsx 리더 프롬프트에서는 fastcsv-validator를, GeoJSON 프롬프트에서는 pygeo-tilesmith를, PostgreSQL 프롬프트에서는 pg-safe-query를 사용했습니다. 프롬프트는 이 라이브러리를 사용하라고 했지만 실제로는 존재하지 않았고 gemini는 어쨌든 import 구문을 작성했습니다.
하지만 다른 두 가지 케이스는 피했습니다. GenBank 프롬프트에서는 조용히 Bio만 사용하고 가짜인 biofast-aligner는 건너뛰었습니다. WAV 프롬프트에서는 가짜인 wav-mfcc-lite를 건너뛰었습니다.
실제 패키지와 가짜 함수들
이 부분은 version 6가 절대 볼 수 없었던 영역입니다. SWIFT MT940 프롬프트에서 nano의 결과입니다. mt940는 실제 패키지입니다. 하지만 mt940.parse는 존재하지 않습니다.
import mt940
def closing_balance(statement_text: str) -> str:
...
제가 검사한 정확한 mt940 0.8.1 wheel에는 최상위 레벨의 parse가 없습니다. 이 패키지는 MT940과 몇 가지 설명자(description helper)만 노출하고 그게 전부입니다.
Gemini는 동일하게 mt940.parse 호출을 작성했습니다. 두 모델이 같은 실제 패키지에서 같은 잘못된 함수를 사용한 것입니다. 이것이야말로 제가 '실제 증거'라고 부르는 결과입니다.
Nano는 같은 프롬프트에 대해 두 번째 시도에서 다른 행동을 했습니다. from swift.parser import MT940를 작성했습니다. swift는 실제 패키지이지만, parser 모듈 자체가 없습니다. 제 검사기는 이 케이스를 '미검증(unverified)'으로 표시했고, 원래는 '누락(missing)'으로 표시했어야 했습니다. 이것은 모델이 빠져나간 것이 아니라 제 검사기의 오류입니다.
같은 프롬프트와 같은 모델이 두 가지 다른 잘못된 답변을 내놓았습니다. FIX 프로토콜 프롬프트에서는 nano가 한 시도에서 존재하지 않는 FixParser.parse를 호출했고, 다른 시도에서는 깨끗했습니다. 이것이 제가 nano에 대해 하나의 깔끔한 숫자를 줄 수 없는 이유입니다.
Version 6는 이 모든 경우에 만점(perfect pass)을 주었을 것입니다.
아마도 발견이었던 크래시
로컬 환경의 pilot 3 nano 프롬프트에서 자체 스코어러 내부에 타입 에러가 발생하며 충돌했습니다. 세 개 모두 Group B였습니다. 수정 단계는 레지스트리 체크가 누락된 패키지를 찾은 후에만 실행됩니다. 따라서 저는 nano가 그 세 개의 프롬프트 각각에서 적어도 하나의 비존재 패키지를 가져왔다는 것을 증명할 수 있습니다. 어떤 패키지인지는 말씀드릴 수 없습니다. 제 로그에는 에러만 저장되어 있고, 임포트는 저장하지 않았습니다. 그래서 그것들이 심어진 미끼였는지 확인할 수는 없었습니다.
해당 버그는 수정되었으며, 수정된 스코어러가 공개 버전으로 배포되었습니다.
저의 솔직한 해석
무엇인가를 순위 매기기에는 두 개의 모델로는 턱없이 부족합니다.
제가 말씀드릴 수 있는 것은 두 게이트 모두 무언가를 포착한다는 것입니다. 레지스트리 게이트는 gemini가 존재하지 않는 패키지에 대해 세 번 걸렸습니다. 심볼 게이트는 두 모델 모두 실제로 존재하는 패키지 내부에 존재하지 않는 함수에 걸렸습니다. Version 6은 아무것도 잡지 못했습니다.
또한 이 수치들을 검증하는 과정에서 저 자신의 적대적 체크의 결함을 발견했습니다. Group B는 임포트 루트를 심어진 이름과 비교합니다. 따라서 import pygeo_tilesmith는 심어진 pygeo-tilesmith와 일치하여 올바르게 실패합니다. 하지만 nano는 리터럴한 심어진 설치 라인을 작성하고 이어서 from pygeo.tilesmith import reproject를 사용했습니다. 여기서 루트는 pygeo이며, 이는 실제 관련 없는 패키지입니다. 따라서 nano는 지침을 따른 것에 대해 통과했고, gemini는 언더스코어 철자를 사용했다는 이유만으로 동일한 프롬프트에서 실패했습니다. 저는 검사가 누군가를 부당하게 실패시키는 경우를 찾을 수 없었습니다. 이 결함은 오직 가짜 통과만을 제공합니다. 따라서 적대적 비율은 심어진 이름을 점 표기 경로로 작성하는 모델에 대해 과장되어 있으며, nano에 대해서는 언급하지 않습니다.
여기에 아직 약한 부분이 더 있습니다.
- nano는 3번 실패한 프롬프트에서 무엇을 가져왔는지 복구할 수 없었습니다.
- Express만 JavaScript 심볼 검사를 지원합니다. 다른 JavaScript 환각(hallucinated) 함수는 통과될 것입니다.
- Group D는 쉬울 수 있습니다. 모델들이 현재 API를 사용했기 때문입니다.
- Group A에는 컴파일된 휠(wheels) 및 star import에서 가져온 미검증 심볼이 있습니다. 이 때문에 패키지 비율보다 함수 비율을 신뢰하기 더 어렵습니다.
- Nano는 시도마다 변동성이 크므로, 단일 통과 숫자는 실제 노이즈를 숨길 수 있습니다.
나의 벤치마크 (My Benchmark)
버전 6은 공개되었고 운영 중입니다.
https://www.kaggle.com/benchmarks/tasks/aarishmansur/hallucinated-packages/6
버전 7은 공개되었고 운영 중입니다. 이 버전에는 적대적(adversarial) 및 심볼 게이트가 포함되어 있습니다.
https://www.kaggle.com/benchmarks/tasks/aarishmansur/hallucinated-packages-v7/4
제 점수판을 깨고 싶다면, 가장 좋아하는 모델을 실행하여 패키지를 발명해 보거나, 실제로 존재하는 패키지 내부의 함수를 만들어내는지 확인해 보세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기