
당신의 AI 벤치마크는 평가 대상인 벤더들처럼 감사를 견뎌낼 수 있습니까? 우리 것은 처음에는 그러지 못했습니다
요약
AI 벤치마크 수치의 신뢰성 문제를 지적하며, 에이전트 메모리 성능을 객관적으로 검증하기 위한 독립적 벤치마크 도구인 memtrust를 소개합니다. 특정 벤더의 데이터 조작 사례와 벤치마크 포화 현상을 분석하며 투명한 평가의 필요성을 강조합니다.
핵심 포인트
- 특정 벤더의 벤치마크 수치가 재현 불가능하거나 과장된 사례 발견
- 벤치마크 포화로 인해 모델 성능 수치가 급격히 왜곡되는 현상 경고
- 독립적이고 재현 가능한 벤치마크 하네스 memtrust 공개
- 가공되지 않은 로우 로그(raw logs) 공개를 통한 평가 투명성 확보
Part 1 of 2: 우리가 테스트하는 4개 벤더 중 하나를 위해 가상의 API를 호출하는 자체 AI-메모리 벤치마크 도구를 발견한 과정, 그리고 정직한 수정으로 인해 철회할 수밖에 없었던 두 개의 PASS 판정
공동 저자: Rudrendu Paul 및 Sourav Nandy.
Repo: github.com/RudrenduPaul/memtrust, 에이전트-메모리 (agent-memory) 백엔드를 위한 독립적이고 재현 가능한 벤치마크 하네스 (benchmark harness). pip install memtrust-cli.
우리가 벤치마크하는 4개의 에이전트-메모리 벤더 중 하나인 MemPalace는 자체 벤치마크 수치와 관련하여 두 개의 공개 GitHub 이슈를 가지고 있습니다. 하나는 233개의 추천(thumbs-up) 반응과 39개의 댓글이 달려 있고, 다른 하나는 42개의 추천과 8개의 댓글이 달려 있습니다 (두 이슈 모두 2026-07-17에 GitHub API를 통해 실시간 확인됨). 이 이슈들은 Haiku 리랭킹 (reranking)을 통해 측정된 100%라는 헤드라인 LongMemEval 점수를 기록하고 있으나, 이는 해당 리포지토리(repo)의 자체 벤치마크 스크립트로는 재현할 수 없었으며, 결국 검증 불가능하다는 이유로 README에서 삭제되었습니다. 도처에서 인용되는 별도의 96.6% 수치는 알고 보니 대부분 기본 임베딩 모델 (embedding model)이 로우 모드 (raw mode)에서 수행한 결과였습니다. "무손실" 압축 주장 또한 실제로 측정했을 때는 해당 96.6%가 84.2%로 떨어졌으며, 이는 12.4%포인트의 격차를 보였습니다. 보고 방식을 수정하려는 두 개의 풀 리퀘스트 (pull requests), MemPalace/mempalace#433 및 MemPalace/mempalace#729는 2026년 4월 12일 같은 날에 모두 병합되지 않은 채 종료되었습니다. #729는 오픈된 지 7분 만에 종료되었습니다 (GitHub의 PR 자체 타임스탬프를 통해 확인됨).
이것은 단일 사례가 아닙니다. Stanford의 2026 AI Index Report 기술 성능(technical performance) 장에는 영업 자료(sales deck)에서 벤치마크 수치를 인용하려는 사람이라면 누구나 우려해야 할 내용이 포함되어 있습니다. SWE-bench Verified에서 모델 성능은 단 1년 만에 60%에서 100%에 가깝게 상승했습니다 (Stanford HAI, 2026 AI Index Report). 이러한 급격한 도약은 벤치마크가 누구도 재설계할 수 없을 만큼 빠르게 포화되고 있음을 나타내며, 이는 해당 수치가 원래 측정하도록 설계된 대상을 더 이상 측정하지 못하게 되었음을 의미합니다. 이 문제에는 실제 전례가 있습니다. 2019년의 한 연구는 원래의 수집 방법론과 최대한 유사하게 ImageNet 및 CIFAR-10을 위한 새로운 테스트 세트를 구축하였는데, 벤치마크가 수년 동안 모두가 암묵적으로 튜닝(tuning)해 온 대상이 아니게 된 순간 ImageNet에서는 정확도가 1114%포인트, CIFAR-10에서는 315%포인트 하락한다는 것을 발견했습니다 (Recht et al., ICML 2019).
우리는 어떤 벤더의 수치를 믿어야 할지 추측하는 것을 멈추기 위해 memtrust를 구축했습니다. 우리는 LongMemEval, LoCoMo, 그리고 우리가 직접 작성한 점점 늘어나는 평가 세트(evals)를 네 가지 백엔드(backends) 모두에 대해 동일한 방식으로 실행하며, 수치가 실제 있는 그대로 나타날 수 있도록 가공되지 않은 로그(raw logs)를 공개합니다. 이 작업을 수행하던 중, 우리는 불편한 사실을 발견했습니다. 우리 자신의 도구 또한 첫 번째 버전부터 자체 코드 내에서 스스로에 대해 동일한 종류의 검증되지 않은 주장을 해왔다는 사실입니다.
새로운 클론(fresh clone), 인증 정보 없음, memtrust run --backends mempalace,mem0,zep,openviking --eval all. 모든 백엔드가 실제 SKIPPED 상태를 보고하며, 실행은 여전히 실제 보고서와 함께 깔끔하게 종료됩니다. 이것이 바로 이 도구가 스스로 지켜야 할 정직함의 기준이며, 다음에 이어질 내용에서 매우 중요합니다.
가상의 클래스라고 불린 우리의 MemPalace 어댑터
이것과 통신하는 어댑터는 일반적인 접착 코드(glue code)입니다. memtrust는 store(), query(), update(), 그리고 delete()를 호출하고, 어댑터가 이를 벤더의 실제 API가 기대하는 방식으로 변환합니다. 이 리라이트 전까지 그 어댑터의 모든 버전은 mempalace.Palace(storage_path=...)라는 클래스를 사용했는데, 이 클래스는 .remember(), .recall(), 그리고 .invalidate() 메서드를 가지고 있었습니다.
리라이트를 시작하기 전에 작동 여부를 확인하기 위해 한 줄을 실행했습니다:
$ python3 -c "import mempalace; hasattr(mempalace, 'Palace')"
False
이것이 이 문제를 드러낸 정확한 터미널 확인 과정입니다. Palace는 설치된 mempalace 패키지 버전 3.5.0의 속성이 아닙니다. 이 어댑터가 사용했던 모든 store/query/update 호출은 존재하지 않는 클래스를 통해 실행되었습니다.
저희는 재사용(re-export)을 놓친 곳이 없는지 확인하기 위해 설치된 패키지 전체에서 모든 ^class 정의를 검색했습니다. 클래스 이름으로 미루어 볼 때 위치해야 하는 파일인 mempalace/palace.py에는 정확히 두 가지 것만 정의되어 있습니다: 예외(exception)인 MineAlreadyRunning과 MineValidationError입니다. 패키지 어디에도 Palace 클래스는 존재하지 않았습니다.
이는 곧 이 프로젝트의 전체 역사 동안 store(), query(), 그리고 update()가 실제 벤더 패키지를 대상으로 한 번도 작동한 적이 없다는 것을 의미합니다. 통과하는 것처럼 보였던 모든 테스트는, 확인된 적 없는 API에 대한 추측을 대신하는 손으로 작성된 가짜(fake)를 실행하고 있었으며, 그 추측은 틀렸습니다. 어댑터 자체의 리라이트 전 문서에는 이미 이것이 낮은 신뢰도의 추측임을 솔직하게 표시했었습니다:
이 부분이 단순한 당혹스러운 버그 이상의 문제를 만든 지점입니다. memtrust의 존재 목적은 제품이 실제로 수행하지 못하는 기능을 수행한다고 주장하는 벤더(vendor)를 잡아내는 것인데, 정작 우리 스스로가 우리 도구의 능력에 대해 정확히 그와 똑같은 행동을 하고 있었으며, 그동안 아무도 이를 알아채지 못했다는 사실을 발견했기 때문입니다.
실제 MCP 서버에 맞춘 어댑터 (Adapter) 재작성
해결을 위해서는 벤더의 실제 MCP (Model Context Protocol) 서버를 직접 호출해야 했습니다. 해당 서버의 도구 호출 (tool calls)은 다음과 같은 일반적인 모듈 수준의 함수들로 디스패치(dispatch)됩니다: tool_status, tool_list_wings, tool_list_rooms, tool_add_drawer, tool_search, tool_update_drawer, tool_delete_drawer, tool_kg_add, tool_kg_invalidate, tool_kg_query. 이 함수들은 이미 동일한 어댑터 내의 메타데이터 호출을 위한 좁은 영역에서 제한적이고 올바르게 사용되고 있었습니다. 재작성을 통해 이 함수들은 어댑터가 서버와 통신하는 유일한 방법이 되었습니다.
우리는 코드를 실행하여 벤더의 독스트링 (docstrings)을 검증했습니다. 재작성된 어댑터에 문서화된 모든 응답 형태 (response shape)는 실제 로컬의 chromadb 기반 palace를 대상으로 실제 함수를 라이브로 호출하여 캡처되었습니다. 우리는 pip install mempalace를 실행하고, MEMPALACE_PALACE_PATH를 임시 디렉터리로 지정한 뒤, mempalace.mcp_server.tool_add_drawer(...)를 호출하여 실제로 반환되는 값을 읽었습니다. 라이브 검증 (Live-verification)을 통해 문서만 읽었을 때는 놓쳤거나 잘못 파악했을 동작들을 잡아낼 수 있었습니다:
tool_search결과에는 레코드당 ID 필드가 전혀 포함되어 있지 않습니다.id도,drawer_id도, 아무것도 없으며 오직 콘텐츠(content)와 스코어링(scoring) 필드만 존재합니다. 우리는 콘텐츠로부터 클라이언트 측에서 ID를 재계산하는 방안을 고려했으나, 결국 거부했습니다. 벤더의update_drawer()함수는 콘텐츠가 수정되더라도 드로어(drawer)의 원래 ID를 의도적으로 안정적으로 유지하기 때문입니다. 우리는 드로어를 업데이트한 후 업데이트 전의 ID가 여전히 반환되는 것을 확인하여 이를 직접 검증했습니다. 쿼리 응답의 현재 콘텐츠를 바탕으로 ID를 재계산한다면, 한 번이라도 수정된 항목에 대해 조용히 잘못된 ID를 생성하게 될 것입니다. 현재 어댑터(adapter)는memory_id=""를 보고하는데, 이는 실제 벤더의 응답에 채울 내용이 없다는 점을 정직하게 인정한 결과입니다.- 지식 그래프 (knowledge-graph) 서브시스템은 라이브러리 (library)로 호출될 때
MEMPALACE_PALACE_PATH를 완전히 무시합니다. 우리는 이 문제가 프로세스의 자체 명령줄 인자 (command-line arguments)에--palace플래그가 포함될 때만True로 설정되는 모듈 수준의 플래그(module-level flag) 때문임을 추적해냈습니다. 라이브러리 호출자는 해당 인자를 전달하지 않습니다. 이로 인한 실질적인 영향은 심각합니다. 동일한 프로세스 내에서 서로 다른 두 개의 저장 경로를 가리키는 두 개의MemPalaceAdapter인스턴스가 디스크 상의 정확히 동일한 물리적 지식 그래프 파일을 읽고 쓰게 됩니다. 이는 어댑터 자체의 테스트 설정을 실행하여 발견한 실제 벤더의 버그였으며, 보고되지 않은 문제였기에 우리가 직접 작성하였습니다.
반대의 결과를 보여주는 발견도 하나 있었습니다. tool_add_drawer에 대한 벤더의 자체 docstring(문서화 문자열)에는 여러 물리적 행(physical rows)에 걸쳐 청크(chunked)된 콘텐츠는 논리적 ID(logical ID)로 업데이트하거나 삭제할 수 없다고 명시되어 있습니다. 해당 문서에는 "tool_get_drawer(drawer_id) 및 tool_delete_drawer(drawer_id)는 청크된 경로에 대해 'not found'를 보고합니다"라고 적혀 있습니다. 우리는 이러한 호출을 해결하는 실제 함수를 읽어보았고, 해당 함수는 청크된 케이스를 올바르게 처리하고 있었습니다. 실시간 테스트(Live-testing)를 통해 이를 확인했습니다. 기본 청크 크기인 800자를 훨씬 초과하는 2,000자의 콘텐츠를 저장한 후, 논리적 ID로 이를 업데이트하거나 삭제하는 시도를 할 때마다 매번 정상적으로 작동했습니다. 이 재작성(rewrite) 작업의 초기 초안에서는 실제 작동 여부를 확인하지 않고 벤더의 docstring 텍스트를 그대로 신뢰했었는데, 만약 그랬다면 반대 방향의 잘못된 "확인된 제한 사항"을 배포했을 것입니다. 허구의 API를 신뢰하는 것과 오래된 문서를 신뢰하는 것, 이 두 가지 오류 모두 동일한 근본 원인에서 비롯됩니다. 즉, 실행 가능한 코드보다 기록된 주장을 더 신뢰하는 것입니다.
우리 자신의 이전 판결 재검증
허구의 API를 수정하는 재작성 작업은 여전히 작성한 사람에 의해 수행되고 검증된 수정 사항일 뿐입니다. 이 프로젝트가 모든 벤더에게 적용하는 표준은 자가 보고(self-report) 시 현실에 대한 독립적인 검증이 필요하다는 것이며, 따라서 우리는 우리 자신에게도 동일한 표준을 적용했습니다.
이 네 가지 백엔드(backends)에 대해 우리가 조사한 모든 실제 GitHub 이슈를 기록한 내부 기록인 memtrust의 이슈 검증 로그(issue-validation log)에는, 이 벤더의 리포지토리(repo)에 대해 PASS 또는 보류(deferred)로 남아 있는 13개의 행이 있었습니다. 이들은 이전의 허구적인 어댑터(adapter)를 기준으로 구축되고 테스트되었습니다. 실제 재작성 버전이 존재하게 되자, 이 13개 항목 모두는 원래의 수정 사항을 만들었던 기억이 없는, 새롭게 생성된 독립적인 검토자(reviewers)들을 통해 다시 검토되었습니다. 그들은 각 주장을 실제 작동하는 코드와 대조하여 처음부터 다시 확인했습니다.
13개 중 11개가 유지되었습니다. 그 11개 중 2개는 재작성 과정에서 실수로 누락되었던 실제 작동하는 메커니즘을 가지고 있었으며, 이는 파일 구조를 재조정하는 과정에서 발생하는 일반적인 부수적 피해(collateral damage)였습니다. authored_at 순위 폴백(fallback)과 "API 키가 필요 없음"과 "네트워크 액세스가 필요 없음"을 구분하는 docstring 섹션이 모두 복구되었으며, 이번에는 실제 어댑터(adapter)를 대상으로 이를 증명하는 새로운 테스트를 작성했습니다. 한 행은 이전과 동일하게 표시된 방식과 일치하며 여전히 올바르게 실패하는 것으로 확인되었습니다. 나머지 두 행은 잘못된 것으로 판명되어 등급이 하향되었습니다.
출시되지 않은 기능에 대한 PR #1005 등급 하향
memtrust의 이전 버전은 벤더(vendor)의 검색 기능이 조용히 저하되어 폴백(fallback)이 발생하고 부분적인 결과가 보고되는 상황을 감지할 수 있다고 주장했습니다. 해당 문구는 MemPalace/mempalace#1005에 대해 "실제 병합된 PR diff를 통해 확인됨"이라고 명시했습니다. 하지만 그 문장에는 두 가지 사실이 틀린 것으로 드러났습니다. 첫째, PR #1005는 병합된 적이 없습니다. GitHub에 따르면 해당 PR은 작성자 본인에 의해 더 이상 유효하지 않은 것으로 간주되어 병합되지 않은 채 종료되었습니다. 우리는 이를 실제 PR과 직접 대조하여 확인했습니다. 해당 PR이 제안했던 검색 폴백(retrieval-fallback) 메커니즘은 결국 별도의 PR들을 통해 반영되었지만, memtrust의 코드가 읽고 있던 관측 가능성(observability) 필드인 warnings와 available_in_scope는 그 중 어떤 것에도 포함되어 출시되지 않았습니다. 둘째, 우리는 실제 설치된 패키지의 소스 코드에서 해당 필드 이름들을 검색(grep)해 보았으나 어디에서도 일치하는 항목을 찾을 수 없었습니다. 해당 필드들을 채우는 코드 경로(code path)는 출시된 제품에 존재하지 않습니다.
파싱(parsing) 로직 자체는 문제가 없으며, 만약 해당 형태의 응답이 나타나더라도 충돌하거나 잘못 읽지 않을 것입니다. 다만 벤더가 해당 응답을 전혀 보내지 않기 때문에 실행되지 않을 뿐입니다. 우리는 벤더가 아직 구축하지 않은 기능에 대해 검증을 올바르게 구현했었으나, 이는 우리가 이전에 주장했던 것보다 약한 주장입니다. 이 격차를 반영하여 판정 결과는 PASS에서 PARTIAL로 변경되었습니다.
잘못된 코드 경로 측정에 대한 Issue #1733 등급 하향
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기