에이전트를 3배 더 작은 모델로 교체하고 실제로 무엇이 변했는지 비교 분석했습니다
요약
에이전트의 모델을 llama3.2:3b에서 1b로 교체했을 때 발생하는 성능 변화를 분석한 실험 결과입니다. 단순히 답변의 느낌(vibe check)이 아닌, 실제 도구 호출(tool calling)의 정확성을 기준으로 모델 간의 차이를 검증했습니다.
핵심 포인트
- 작은 모델로 교체 시 답변은 그럴듯해도 도구 호출 인자가 스키마 자체로 들어가는 오류 발생
- 에이전트 성능 측정 시 단순 텍스트 답변이 아닌 실제 동작(tool calls) 검증이 필수적임
- llama3.2:1b 모델은 중복 결제 시나리오 등에서 도구 인자를 잘못 생성하는 경향을 보임
제가 아는 모든 이들이 이 대화를 나눕니다. 에이전트는 잘 작동합니다. 청구서가 도착합니다. 누군가 더 작은 모델로도 이 작업을 수행할 수 있는지 묻습니다. 그래서 모델 문자열을 교체하고, 몇 번의 채팅을 실행해 보고, 답변이 괜찮아 보이면 그대로 배포합니다.
문제는 "답변이 괜찮아 보인다"는 것이 잘못된 계층을 측정한다는 점입니다. 에이전트의 작업은 대부분 도구 호출 (tool calls)입니다. 에이전트는 cancel_subscription을 호출하지 않으면서도 사용자에게 구독이 취소되었다고 말할 수 있습니다. 단어들은 당신이 가진 모든 느낌 체크 (vibe check)를 통과할 것입니다. 하지만 동작은 망가져 있습니다.
저는 누구나 무료로 재현할 수 있는 설정으로, 이러한 현상이 기록되는 것을 보고 싶었습니다. 클라우드도, API 키도 필요 없습니다. 노트북 한 대면 충분합니다.
설정 (The setup)
가상의 SaaS를 위한 작은 고객 지원 에이전트입니다. 네 가지 시나리오:
- cancel-and-refund: 내 구독을 취소하고 마지막 결제 금액을 환불해 줘
- billing-question: 내가 얼마를 내고 있는지, 언제 갱신되는지 알려줘
- duplicate-charge: 두 번 결제되었으니 중복 결제 건을 환불해 줘
- cancel-only: 즉시 취소하고 다시는 결제하지 마
실제 스키마를 가진 네 가지 실제 도구: lookup_account, get_billing_history, cancel_subscription, issue_refund. 도구 결과는 스텁 (stubbed) 처리되었지만, 도구 호출 (tool calling)은 그렇지 않습니다. 시스템 프롬프트 (system prompt)는 도구 호출이 성공하지 않는 한 어떤 동작이 일어났다고 주장하지 말라고 에이전트에게 지시합니다.
기준점 (baseline)은 ollama의 OpenAI 호환 엔드포인트를 통해 llama3.2:3b에서 실행됩니다. 그런 다음 문자열 하나, llama3.2:3b를 llama3.2:1b로 교체하고 동일한 시나리오를 다시 실행합니다. 그것이 변화의 전부입니다. 가장 순수한 형태의 저가형 모델 전환입니다.
작은 로컬 모델들은 Temperature 0.2에서도 비결정론적 (nondeterministic)이기 때문에, 각 시나리오는 모델당 3번씩 실행됩니다. whatbroke는 #1 #2 #3 접미사를 읽고 모든 기준점 샘플을 모든 교체 후 샘플과 비교하므로, 각 발견 사항은 일화가 아닌 비율로 제공됩니다. 3b 모델이 자체 샘플 사이에서 이미 갈팡질팡했던 것은 교체 이전의 동작이므로 노이즈로 분류됩니다.
차이점 분석 결과 (What the diff found)
전체 요약: 오류(breaking) 0건, 변경(changed) 3건, 정보(info) 12건.
저는 취소 요청이 누락되는 공포스러운 이야기를 예상했습니다. 거의 그런 글을 쓸 뻔했지만, 도구가 저를 막아주었습니다. 이에 대해서는 잠시 후에 더 자세히 다루겠습니다. 노이즈 필터(noise filter)를 통과해 살아남은 결과는 더 기이했습니다.
중복 결제(duplicate-charge) 시나리오에서 제가 가장 중요하게 생각하는 발견 사항은 다음과 같습니다:
! lookup_account가 서로 다른 인자(args)로 호출됨 (email, type, required, properties) (9개 실행 쌍 중 6개)
- {"email":"maya.iyer@example.com"}
+ {"type":"object","required":["email"],"properties":{"email":{"description":"customer email","type":"string"}}}
이 차이점(diff) 줄을 다시 읽어보십시오. 9개의 실행 쌍 중 6개에서, 1b 모델은 고객의 이메일이 아니라 도구 자체의 JSON 스키마(JSON schema)를 인자로 사용하여 도구를 호출했습니다. 파라미터 정의(parameter definition)가 파라미터로서 그대로 되돌아온 것입니다. 그 뒤에 이어진 답변 텍스트는 환불을 처리한다는 내용의 차분하고 유능한 고객 서비스 산문이었습니다. 대화 기록(transcript) 어디에도 결제 조회(billing lookup)에 "type": "object"를 방금 보낸 모델처럼 보이는 부분은 없었습니다.
다른 변경된 발견 사항은 지연 시간(latency)이었습니다. 중복 결제 시나리오에서 61% 증가(17.7초에서 28.5초로, 9개 쌍 중 3개)했고, 취소 및 환불(cancel-and-refund) 한 쌍에서는 63% 증가했습니다. 이는 그 자체로 작은 교훈을 줍니다. 왜냐하면 1b 모델은 깔끔하게 처리한 세 가지 시나리오에서는 더 빨랐기 때문입니다(취소 흐름(cancel flow)은 25.4초에서 12.3초로 단축되었습니다). 더 저렴한 모델은 모델이 혼란을 겪지 않는 경우에만 더 빨랐습니다. 모델이 어려움을 겪는 지점에서는 루프(loop)를 돌며 더 느려졌습니다.
살아남지 못한 헤드라인
만약 제가 각 시나리오를 한 번씩만 실행했다면 헤드라인으로 내세웠을 발견 사항은 다음과 같습니다:
i tool call dropped: cancel_subscription (9개 실행 쌍 중 6개, 베이스라인(baseline)에서도 불안정함(flaps))
1b 모델은 고객에게 모든 것이 취소되었다고 말하면서도, 대부분의 실행 쌍에서 실제 취소 호출(cancellation call)을 건너뛰었습니다. 이것이 바로 이 도구가 존재하는 이유인 악몽 같은 시나리오이며, 실제로 발생했습니다. 하지만 주석(annotation)을 보십시오: '베이스라인에서도 불안정함(also flaps in the baseline)'. 3b 모델 역시 자체적인 세 개의 베이스라인 샘플 사이에서 동일한 호출을 누락했습니다. 이 동작은 모델 교체 이전부터 존재했던 것입니다. 스크린샷을 들고 모델 다운그레이드 탓을 했다면 그것은 거짓말이 되었을 것입니다.
취소 전용 (cancel-only) 케이스에서도 같은 이야기가 반복되었습니다. 두 모델 모두 계정 ID (account id)로 쓰레기 값을 전달했습니다 (3b 모델은 빈 객체를 보냈고, 1b 모델은 "lookup_account"라는 문자열을 그대로 보냈습니다). 솔직히 말해, 이는 베이스라인의 불안정성 (flakiness) 문제였습니다. 제 에이전트는 이미 기존 질환 (pre-existing condition)을 가지고 있었고, 차이점 분석 (diff)은 제가 새 모델을 탓하는 대신 그 사실을 알려주었습니다.
단 한 번의 전/후 실행만 있었다면, 저는 공포스러운 이야기나 아무 문제 없다는 결과 중 하나를 골라낼 수 있었을 것입니다. 하지만 시나리오당 9쌍의 실행을 수행함으로써 저는 비율 (rates)을 얻게 되었고, 그 비율이 이야기를 바꾸어 놓았습니다.
텍스트 차이점(text diffs)과 느낌(vibes)이 이를 놓치는 이유
최종 답변에 대한 텍스트 차이점 (text diff)을 비교하면, 두 개의 정중한 문단을 비교하여 단지 문장이 약간 재구성된 것만을 찾아낼 것입니다. 그것은 issue_refund가 실행되었는지, 어떤 금액으로 호출되었는지, 혹은 취소가 실제로 일어났는지에 대해서는 아무런 의견을 내지 못합니다. 트랜스크립트 (transcripts)를 눈으로 훑어보는 것은 더 나쁩니다. 왜냐하면 작은 모델의 실패 모드 (failure mode)는 헛소리인 경우가 드물기 때문입니다. 그것은 누락되거나 잘못된 도구 호출 (tool call)을 감싸고 있는 자신감 넘치는 산문일 뿐입니다. 답변은 피해가 나타나는 마지막 지점입니다.
평가 (Evals)가 도움이 되긴 하지만, 그것은 다른 질문에 답합니다. 평가는 당신이 작성해야 하는 루브릭 (rubric)에 따라 버전을 점수화합니다. 행동 차이점 분석 (behavioral diff)은 작성할 것도 없고 비용을 지불할 심판 (judge)도 필요 없이, 도구 호출 (tool-call) 수준에서 이 두 버전 사이에 정확히 무엇이 변했는지를 답해줍니다. 이는 평가 스위트 (eval suite)를 실행할 필요가 있는지 결정하기 전에 실행하는 5분짜리 체크입니다.
당신의 에이전트에 직접 실행해 보세요
전체 실험은 모델을 가져오고 다섯 개의 명령어를 실행하는 것이 전부입니다:
ollama pull llama3.2:3b && ollama pull llama3.2:1b
npm install -g whatbroke-cli
node agent.mjs llama3.2:3b traces/before.jsonl 3
...
에이전트 스크립트는 프레임워크 없이 단순한 fetch 호출 190줄로 이루어져 있습니다. 당신의 에이전트에는 SDK조차 필요하지 않습니다: whatbroke record가 로컬 프록시 (local proxy)를 시작하면, OPENAI_BASE_URL을 그곳으로 지정하고 에이전트를 변경 없이 실행하면 됩니다. 모든 것은 오프라인으로 진행되며 트레이스 (traces)는 절대 당신의 기기를 떠나지 않습니다.
Repo: https://github.com/arthi-arumugam-git/whatbroke
만약 여러분이 자신의 모델 다운그레이드(downgrade) 상황에 이를 적용하여 무언가를 찾아낸다면, 저는 현재 이러한 사례들을 수집하고 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기