로컬 코딩 LLM 파이프라인을 검증하면서 처음엔 단순하게 접근했다. 모델별로 딱 한 번씩 돌려보고, 가장 빠르고 결과물이 깔끔한 모델을 고르면 된다고 생각했다. 그런데 최종 결정을 내리기 직전에 “정말 이 결과가 우연이 아닐까?“라는 의문이 들어서, 가장 유력했던 모델(DeepSeek-Coder-V2-16B)만 완전히 동일한 프롬프트로 한 번 더 돌려봤다.

두 번째 실행에서 벌어진 일

1차 실행에서는 9분 30초 만에 요청한 4개 함수(add, subtract, multiply, divide)만 정확히 작성하고 끝났다. 2차 실행은 22분 55초가 걸렸고, 요청하지 않은 함수 4개(거듭제곱, 나머지, 제곱근, 팩토리얼)를 임의로 추가했으며, 코드 스타일 오류(flake8)도 훨씬 많이 남겼다.

같은 모델, 같은 프롬프트, 같은 하드웨어인데 결과가 이렇게 달라진 것이다.

한 번의 테스트가 위험한 이유

만약 1차 실행 결과만 보고 “이 모델이 안정적이다"라고 결론을 냈다면, 완전히 잘못된 판단을 내렸을 것이다. LLM은 확률적 샘플링에 기반한 시스템이라, temperature가 0이 아닌 이상(그리고 실제로 코딩 어시스턴트들은 대부분 0보다 큰 값을 기본으로 쓴다) 같은 입력에도 매번 다른 출력이 나올 수 있다는 걸 전제로 검증해야 한다.

특히 “무인 자동화 파이프라인에 이 모델을 써도 되는가"를 판단할 때는 이 변동성이 결정적으로 중요하다. 사람이 매번 결과를 검토할 거라면 어느 정도 변동성은 감수할 수 있지만, 무인 파이프라인이라면 최선의 결과가 아니라 최악의 결과를 기준으로 신뢰도를 평가해야 한다.

최소한의 재현성 검증 방법

이번 경험을 바탕으로, 앞으로 AI 모델을 파이프라인에 투입하기 전 최소한 이 정도는 확인하기로 했다.

  1. 동일 프롬프트를 최소 2~3회 반복 실행하고 결과를 비교한다.
  2. 단순히 “성공/실패"만 볼 게 아니라, **출력 범위(요청한 것만 정확히 하는지, 임의로 더 하거나 덜 하지는 않는지)**까지 비교한다.
  3. 소요 시간의 편차도 함께 기록한다 — 평균만 보면 최악의 경우(2.4배 느려지는 경우)를 놓친다.
  4. 가능하다면 temperature 등 샘플링 파라미터를 낮춰서 재현성 자체를 높이는 것도 고려한다(다만 이번 프로젝트에서 쓴 Aider/Ollama 조합에서는 이 부분까지 세밀하게 튜닝하지는 않았다).

결론

한 번의 테스트로 내린 결론은 데이터가 아니라 우연일 수 있다. 특히 “무인으로 돌려도 되는가"를 판단하는 자리에서는, 최고의 결과 한 번이 아니라 반복했을 때의 하한선을 기준으로 삼아야 한다는 게 이번 검증 과정에서 얻은 가장 값진 교훈이었다.