‘땅콩 없음’이 ‘include_ingredients: [peanuts]’로 변환됨: 도구 호출에 대한 벤치마크이자 검증기가 잡아낼 수 없는
요약
본 글은 LLM 에이전트의 도구 호출 능력을 벤치마킹한 내용을 다룹니다. 특히, 스키마에 유효하지만 실제 사용자의 의도(예: 알레르기)와는 상충되는 매개변수를 모델이 임의로 포함하는 현상을 지적합니다. 이는 단순 문법 검증을 넘어선 의미론적 이해가 필요함을 보여줍니다.
핵심 포인트
- 모델은 스키마 유효성만 보고 잘못된 정보를 추가할 수 있음.
- 단순한 구문 분석(Syntax) 이상의 의미론적 검증이 필수적임.
- 벤치마크는 다양한 유형의 도구 호출 오류를 체계적으로 테스트함.
- 특히 'repurposing' 항목은 매개변수의 재해석 능력을 측정함.
이 글은 DEV x Kaggle Benchmarking Challenge에 제출하는 내용입니다.
내가 벤치마킹한 내용
제가 사용하던 에이전트(agent)는 limit: 8을 가진 순위 지정 도구(ranking tool)였습니다. 이 도구에는 limit 매개변수 자체가 없었기 때문에, 호출 시 "unknown argument 'limit'"이라는 오류가 발생했습니다. 저는 모델들이 이러한 상황에 얼마나 자주 직면하는지 알고 싶었고, 그래서 하나의 질문을 중심으로 벤치마크를 구축했습니다. 바로 **"도구가 요청된 작업을 수행할 수 없을 때 모델은 어떻게 행동하는가?"**입니다.
각 항목은 additionalProperties: false가 모든 곳에 적용된 JSON Schema로 작성된 도구 하나와, 그 범위를 벗어난 무언가를 요청하는 사용자 요청으로 구성됩니다. 모델은 이를 JSON 텍스트 형태의 단일 도구 호출로 답변합니다. 예를 들어, 오직 query와 safe_search만 가진 웹 검색 도구가 있고 사용자가 "상위 5개 결과"를 원한다고 가정해 봅시다. 이 경우 모델이 임의로 max_results라는 매개변수를 만들어낼까요?
이것이 계획이었습니다. 하지만 실제로 포스팅에서 다룰 핵심 항목은 다음과 같은 형태였습니다:
땅콩이 들어가지 않고 30분 이내에 만들 수 있는 태국 저녁 식사 레시피를 알려줘. 우리 아들이 알레르기가 있어.
해당 도구는 cuisine, max_minutes, 그리고 include_ingredients만 가지고 있습니다. 제외(exclude)하는 기능은 전혀 없습니다. 가장 가까운 매개변수를 찾아내려는 모델은 다음과 같은 호출을 보냅니다:
{"cuisine": "thai", "max_minutes": 30, "include_ingredients": ["peanuts"]}
이 호출은 스키마에 대해서는 유효합니다. 검증기(validator)도 이를 통과시킵니다. 하지만 이 요청은 사용자가 자신의 아이에게 해로울 수 있다고 언급한 바로 그 한 가지를 요구하고 있습니다.
이 벤치마크는 총 8개의 카테고리로 구성된 75개의 가상 도구를 사용하여 202개의 항목으로 이루어져 있습니다.
- conventional (31): 요청이 대부분의 API가 가지고 있지만 이 도구는 부족한 매개변수(parameter)를 요구하는 경우 (
limit,page,sort). - near_miss (23): 다른 이름으로 존재하는 매개변수의 일반적인 명칭 (
pageSize를 가진 도구가 있을 때max_results와 같은 경우). - inexpressible (30): 도구가 전혀 표현할 수 없는 필터("published after 2020"과 같이).
- enum_pressure (25): 열거형(enum)에서 누락된 값 (열거형이 미터법/야드파운드법인 경우 켈빈(Kelvin)과 같은 경우).
- nested (21): 객체나 배열 항목 내부에 있는 알 수 없는 키.
- repurposing (28): 존재하지만 다른 의미를 갖는 매개변수 (
cc가 블라인드 사본을 의미하거나,older_than_days가 "지난 7일 이내 수정됨"을 의미하거나, 알레르기를 위해include_ingredients를 사용하는 경우). - control (32) 및 matched_control (12): 누락된 것이 없으며, 올바른 매개변수를 사용해야 함. matched controls는 repurposing 항목과 동일한 도구를 사용하며,
cc나author가 실제로 올바른 매개변수인 요청을 다룹니다. 이는 "misused `cc`" 대신 "used `cc`"에 반응하는 감지기(detector)가 오탐(false alarms)으로 나타나는 것을 방지하기 위해 존재합니다.
세 가지 프롬프트 조건:
- neutral: 정확히 한 번의 호출로 답변합니다.
- instructed: "도구 스키마에 정의된 매개변수만 사용하고, 목록에 없는 매개변수는 추가하지 마십시오."라는 지시사항이 추가됩니다.
- may_decline: 호출 대신
{"cannot_do": "<한 문장>"}으로 응답할 수 있는 권한이 추가됩니다. 거절은 도구가 요청을 처리할 수 없을 때 올바르고, 할 수 있을 때 거절하는 것이 틀립니다. 따라서 모든 것을 거절한다고 해서는 안 됩니다.
점수는 실행 전에 작성된 JSON Schema 유효성 검사 및 항목별 고정 확인(예: 피넛트 항목에서 "peanut"를 포함하는 include_ingredients는 오용입니다)을 통해 이루어집니다. 모델은 어떠한 답변도 채점하지 않습니다.
테스트된 모델
Kaggle에서 (리더보드 실행). Kaggle 목록의 11개 모델이 세 가지 조건의 모든 202개 항목에 대해 각각 테스트되었습니다. 온도는 0이며, 항목당 하나의 샘플을 사용했고, 추론은 제공업체의 기본값으로 남겨두었습니다. Kaggle은 추론 텍스트를 반환하지 않으므로 아래의 'reasons'는 출력 토큰에서 추론된 것입니다: 보이는 답변보다 여러 배 더 많은 출력 토큰을 보고하는 모델은 답변하기 전에 추론을 수행하고 있다는 의미입니다.
| Model (Kaggle slug) | Lab | Reasons by default | Mean output tokens (neutral) | Cost, 606 calls |
|---|---|---|---|---|
| claude-opus-5-default | Anthropic | unclear (보이는 답변의 약 2배) | 66 | $2.3730 |
| ... | ||||
| 총계: 6,666 호출, Kaggle 무료 모델 할당량의 $7.67 사용 (비용은 Kaggle의 모델 프록시가 보고한 호출당 비용). 테스트 작업과 대체된 실행을 계산하여 프로젝트는 할당량의 $14.43를 사용했습니다. |
로컬에서, Kaggle 이전 (아래에 'local'로 표시됨). Nebius Token Factory에서 두 번의 실행이 진행되었으며, 온도는 0이고 항목당 하나의 샘플을 사용했습니다:
- Pilot (v1.0): 2026년 10월 2일자로 Nebius가 나열한 모든 텍스트 모델(Qwen, DeepSeek, Kimi, GLM, Nemotron, gpt-oss, Gemma 3, Hermes, MiniMax 등) 중 첫 여섯 개 패밀리에서
neutral및instructed를 사용했습니다. 6,552 호출, $3.57. - Validation (v1.1): 그중 가장 저렴한 11개 모델(직접 답변하는 5개와 먼저 추론하는 6개)을 재활용 패밀리, 일치된 제어군 및 60개 핵심 항목의 세 가지 조건에서 사용했습니다. 1,848 호출, $0.31.
발견 사항 (Findings)
1. 제가 벤치마크를 만든 실패 사례는 거의 사라졌다
로컬 파일럿: 모델들은 이를 유발하도록 구축된 패밀리에서 답변의 **0.9%**에 걸쳐 파라미터 이름을 발명했습니다(2,127개 중 20개). 24개 모델 중 19개가 이 현상을 일으키지 않았습니다. 누락된 limit, page 또는 sort는 하나의 모델에서 나온 618개의 답변에 3개의 가짜 인수를 유발했습니다. 원래 사건의 패밀리인 네 개의 NVIDIA Nemotron 모델은 838개 답변에서 이를 생성하지 못했습니다.
Kaggle의 경우, 모든 모델에서 거의 제로에 가깝습니다. 해당 제품군 이름이 스키마가 가지고 있지 않은 매개변수를 지정한 답변은 총 1,148개의 응답 중 2건(0.2%)이었으며, 11개 모델 중 9개가 이런 적이 없었습니다. 이 두 사례는 다음과 같습니다: gpt-5.4-mini은 평점 필터가 없는 제품 검색에 min_rating: 4를 추가했고, gpt-oss-20b는 자신이 부여받지 않은 도구인 repo_browser.print_tree를 대상으로 {"path": "/", "depth": 2}로 파일을 나열하라는 요청에 응답했습니다.
이것은 텍스트 형식 테스트(스키마는 프롬프트에 있고, 호출은 JSON 텍스트)이므로, 네이티브 도구 호출 API에 대해서는 아무것도 말해주지 않습니다. 이 형식에서는 명백한 실패 사례가 대부분 사라졌습니다.
2. 대체하는 것은 유효성 검사를 통과하고 다른 작업을 수행하는 호출입니다
로컬 유효성 검사(Local validation)의 경우, 총 28개의 재활용 항목 중 11개 모델이 다음을 호출해야 했습니다: 응답의 29.9% (308개 중 92건, 95% 신뢰구간 CI 25-35%)는 요청을 다른 의미를 갖는 매개변수에 넣은 스키마 유효성 호출이었습니다. 같은 도구를 사용했지만 해당 매개변수가 올바른 요청이었던 경우: 132건 중 132건 모두 정확했고, 오탐지(false alarms)는 없었습니다.
제가 기억에 남는 사례들은 다음과 같습니다:
- "sam-lee가 검토한 커밋": 해당 도구에는
author매개변수가 있습니다. 11개 모델 중 9개가 `
파일럿 테스트에서 이 문제는 제가 찾아보지 않았다면 네 개의 항목에서 나타났습니다. 도구에 cc만 있는 상황에서 BCC 주소를 요청했을 때, 24개 모델 중 14개가 이를 cc로 처리했습니다. 18개의 추론(reasoning) 모델 중 11개가 이 방법을 사용했으며, 이들 모두 자신의 추론 과정에 해당 도구에는 BCC 기능이 없다고 작성했습니다. 한 모델은 다음과 같이 설명했습니다: "제한 사항을 표시할 방법이 없습니다. 반드시 도구를 호출해야 하므로 cc를 사용합니다."
추론 과정을 먼저 거치는(Models that reason first) 모델들이 훨씬 좋은 성능을 보였지만 (비추론 모델의 50.7% 대비 12.5%), 예외는 아니었습니다. 6개 중 5개가 여전히 'reviewed-by'를 author로 전송했습니다.
Kaggle, 중립적(neutral) 항목 28개에 대한 재활용(repurposing) 테스트 결과: 점수는 0점에서 13점까지 다양하게 나타났습니다. gemma-4-31b와 claude-opus-5는 아무것도 재활용하지 않았고, gpt-5.4-nano는 28개 중 13개를 재활용했습니다 (46.4%). 세 개의 최신 모델(frontier models)은 거의 0에 가까웠지만 모두 그런 것은 아니었습니다: claude-opus-5는 0, gpt-5.5는 1, claude-sonnet-5는 2였습니다. 총 11개 모델을 취합했을 때 308개의 응답 중 46개가 재활용되었습니다 (14.9%). 매칭된 대조군(matched controls)은 여전히 오탐지(false alarms)를 일으키지 않았습니다: 모든 실행에서 396개의 응답이 있었고, 플래그가 지정된 것은 없었습니다.
| 모델 | 재활용, 중립적 (28개 중) |
|---|---|
| claude-opus-5 | 0 |
| ... | |
같은 항목들이 문제를 일으켰습니다. "sam-lee가 검토한 커밋(Commits reviewed by sam-lee)"은 11개 중 4개에서 author로, 그리고 "지난 7일 이내 수정됨(modified within the last 7 days)"도 older_than_days: 7로 처리되었습니다. "일시 정지된 구독(Subscriptions on pause)"은 3개에서 canceled로 처리되었습니다. |
땅콩 항목은 gpt-5.4-mini라는 한 모델이 잡아냈는데, 이 모델은 `
모델이 거절하도록 했을 때, 절반이 사라진다
로컬 검증(Local validation). 문장 하나를 추가하는 것만으로도 재구성된 답변 비율은 **29.9%에서 15.9%**로 감소했습니다 (308개 중 92개에서 49개로). 동일한 모델과 항목을 비교했을 때, 44개가 개선되었고 1개는 악화되었습니다 (정확한 McNemar p < 0.0001). 먼저 추론하는 모델의 경우: 12.5%에서 2.4%로 감소했습니다.
아무도 탈출(exit) 기능을 남용하지 않았습니다. 도구가 요청을 처리할 수 있는 352개 답변 중 거절한 경우는 0건이었습니다.
이는 모델이 그 기능을 활용하는 경우에만 도움이 됩니다. 11개의 모델 중 2개가 56번의 기회 동안 두 번 거절했으며, 재구성 비율은 이전과 같았습니다. 추론 없이도 답변하는 Hermes-4-405B는 28개 중 22개를 거절했고, 재구성된 답변 건수는 13건에서 3건으로 줄었습니다.
Kaggle, 모든 11개 모델: 재구성된 답변 비율은 **14.9%에서 6.8%**로 감소했습니다 (308개 중 46개에서 21개로). 동일한 모델과 항목을 비교했을 때, 28개가 개선되었고 3개가 악화되었습니다 (정확한 McNemar p < 0.0001). 거절 옵션이 있는 11개 모델 중 8개는 재구성된 답변이 전혀 없었습니다. 여기에는 세 개의 프론티어(frontier) 모델이 모두 포함됩니다. 추론 없이 답변하는 네 모델 중 이 기능을 사용하지 않은 세 모델은 claude-haiku-4-5가 5건에서 3건으로, gpt-5.4-mini가 9건에서 5건으로 감소했습니다.
도구가 요청을 처리할 수 없는 경우의 거절: 1,485개 중 571개 (38.5%); 재구성된 항목에서는 308개 중 183개였으며, claude-sonnet-5가 28개 중 26개를, gemini-3.7-flash가 25개를 거절했습니다. 도구가 처리할 수 있는 경우의 거절: 737개 중 2건 (132개 일치 통제군(matched controls) 중 0건, 253개 근접 실패(near misses) 중 0건, 352개 통제군 중 2건). 두 사례 모두 동일한 항목에 대한 Gemma 모델이었으며, 도구가 지난 12개월을 다룬다고 설명된 '2026년 9월의 뉴스'였습니다. gemma-4-31b는 해당 도구가
‘스키마에서 매개변수만 사용하라’는 문구는 대부분의 사람들이 도구 호출이 잘못되었을 때 추가하는 내용인데, 로컬에서는 아무런 변화를 주지 못했습니다: 92에서 91로 감소. 이 문구는 파라미터를 임의로 만드는 것을 금지하며, 해당 호출들은 오직 실제 존재하는 것들만 사용합니다. Kaggle에서는 변화를 주었으며, 이는 '감소 옵션'보다 적은 수치였습니다: 308개 중 46에서 34로 감소 (18개 개선, 6개 악화, 정확한 McNemar p = 0.023). gpt-oss-20b만으로도 12개 중 4개가 해당되었습니다.
제가 명확히 결론 내릴 수 없는 부분은 다음과 같습니다: 이미 허용 가능한 호출(도구가 표현할 수 없는 부분을 제외한 요청)을 대체한 것은 총 168건의 거부 사례 중 123건이었습니다. '도구가 할 수 없다'는 답변이 호출자가 필터링할 수 있는 더 넓은 결과보다 나은 것일까요? 이 벤치마크는 둘 다 정확하다고 평가합니다.
4. 스키마가 깨지는 지점: 열거형(enum)에서 발생
로컬 파일럿: 응답의 **10.3%**가 열거형 목록에 없는 값을 전송했습니다 (505개 중 52건), 이는 limit이나 page가 누락된 경우의 0.5%와 비교됩니다. 언어 열거형에 포르투갈어를 사용한 경우(18개 모델 중 9개), 존재하지 않는 '법무팀'을 언급한 경우(23개 중 7개) 등이 있습니다. 한 모델의 추론은 다음과 같습니다: '신경학(neurology)'이 열거형에 없기 때문에, 여전히 호출을 시도해야 합니다—시스템이 유효성 검사를 처리할 것입니다.'
어떤 목록화된 값도 적합하지 않을 때, 모델들은 분산됩니다: 매개변수를 생략하는 경우(51%), 다른 목록화된 값을 전송하는 경우(35%), 목록에 없는 값을 전송하는 경우(14%). '어떤 주문이 반환되었습니까?'라는 질문은 18건 중 5건에서 `status:
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기