Engineering · Evaluation

동일 하네스 조건의 모델 비교: 41분 실행과 빈 출력은 모델이 아니라 설정과 엔진 문제였습니다

같은 하네스 노드에서 메인 모델만 바꾸고 고정 심판으로 채점해 내부 vLLM의 Qwen3.6-27B와 Claude Sonnet 4.6을 실무 과제로 비교했습니다. 41분짜리 실행은 재시도 설정이, 빈 출력은 빈 도구 결과를 성공으로 세던 엔진 결함이 원인이었고, 이를 걷어낸 뒤 재현한 6개 과제에서 두 모델은 0.01~0.05 차이의 사실상 동률이었습니다.

기간
2026.06 – 2026.06
소속
플래티어 · XGEN
역할
실험 설계·엔진 결함 수정

Key Results

4승 2패
실무 컨설팅 6개 과제 재현 실행같은 하네스 노드와 설정, 고정 Sonnet 4.6 심판, 점수 차 0.01~0.05, 양쪽 모두 pass
41분 → 약 100초
코드 과제 실행 시간Qwen3.6-27B, 최대 반복 1로 재시도 제거와 max_tokens 4,096 적용
약 2배
Qwen 응답 지연재현 실행 기준 약 73~92초 대 약 38~52초

하네스 노드의 가치를 실측으로 보여야 하는 상황이었습니다. 구체적인 질문은 "설정을 제대로 갖추면 내부 vLLM에 올린 오픈 웨이트 모델이 실무 과제에서 상용 프론티어 모델과 견줄 수 있는가"였습니다. 비교 대상은 내부에서 서빙 중인 Qwen3.6-27B와 Claude Sonnet 4.6이었습니다.

모델 비교는 조건이 조금만 달라도 결론이 뒤집힙니다. 2026년 6월 26일 하루 동안 일곱 라운드를 진행했는데, 그중 두 라운드의 결과가 극단적이었습니다. 코드 과제에서 Qwen은 41분이 걸렸고, 검색 도구를 연결한 과제에서는 서두 한 문장만 남기고 끝났습니다. 두 결과 모두 모델 능력의 차이처럼 보였지만, 원인을 추적하니 하나는 재시도 설정, 다른 하나는 엔진의 도구 결과 처리 결함이었습니다.

이 글은 비교 조건을 어떻게 고정했는지, 어떤 라운드를 왜 결론에서 뺐는지, 두 이상 결과의 원인을 어떻게 좁혔는지, 그리고 재현 실행에서 결론이 어떻게 정정됐는지를 다룹니다.

변수는 메인 모델 하나로 고정했습니다

실험은 개발 서버(제품 기본 라인)의 실제 캔버스 워크플로우에서 진행했습니다. 같은 하네스 노드 구성을 두 벌 저장하고 메인 모델만 다르게 했습니다.

고정: 하네스 노드 구성, system_prompt, temperature, max_iterations, judge 설정
변수: 메인 모델 (vLLM Qwen3.6-27B ↔ Claude Sonnet 4.6)
심판: 두 변형 모두 같은 Claude Sonnet 4.6 (일부 라운드는 별도 API로 블라인드 채점)

심판을 고정한 이유는 피평가 모델이 자기 답을 채점하는 구성을 피하기 위해서입니다. 일부 라운드에서는 어느 모델의 답인지 모르는 상태로 내용만 채점하는 독립 블라인드 채점을 추가했습니다.

채점 기준도 명시했습니다. 출처 없이 검증할 수 없는 수치와 날짜를 단정하면 강하게 감점하고, 실행 가능성과 구조를 우대합니다. 실무 컨설팅형 답변에서 실제로 문제가 되는 품질 축이라고 판단했습니다.

과제는 합성 문제보다 실무에서 쓸모 있는 과제를 우선했습니다. 이 원칙 때문에 일부 라운드는 결과와 무관하게 결론에서 빠졌습니다.

실행 절차도 스크립트로 고정했습니다. 저장된 워크플로우를 불러와 과제 입력만 바꿔 저장하고, 실행 스트리밍 API로 실행한 뒤 스트림에서 judge의 검증 점수를 파싱합니다. 스트림 원본은 과제와 모델별로 파일에 남겼습니다. 같은 저장본을 같은 API 경로로 실행하는 것을 재현의 기준으로 삼았고, 뒤에서 설명할 재현 실행도 이 절차를 그대로 썼습니다.

일곱 라운드 중 결론에 남긴 것과 뺀 것

라운드 환경 과제 결과 처리
1 하네스 노드, 도구 없음 매출 증감 함정 문제 둘 다 1.0 (Sonnet 25.4초, Qwen 49.3초) 동급
2 하네스 노드, 도구 없음 고난도 수학 6문항 Qwen 2승 4무 (평균 1.0 대 0.992) 합성 과제라 제외
3 일반 에이전트 노드, RAG 실워크플로우 5개 Qwen 2승 1패 2무 (블라인드) RAG 데이터 부재(0.0~0.3)로 제외
4 일반 에이전트 노드, 검색 도구 AI 코딩 도구 비교 리포트 Qwen 승 (Sonnet 수치·날짜 단정 감점) 하네스 노드가 아니라 제외
5 하네스 노드, 검색 도구 4번과 같은 과제 Sonnet 6,073자 완결, Qwen 빈 출력 원인 조사
6 하네스 노드, 도구 없음 주민등록번호 검증 코드와 pytest Sonnet 84초·0.7, Qwen 2,488초·0.4 원인 조사
7 하네스 노드, 도구 없음, 설정 조정 실무 컨설팅 4개 Qwen 3승 1패 결론 후보

3번과 4번은 하네스 노드가 아닌 일반 에이전트 노드에서 실행했습니다. 비교하려는 대상이 하네스 노드 위의 모델이었으므로 조건이 다른 라운드로 보고 결론에서 뺐습니다. 4번에서는 독립 심판이 정확성 0.8 대 0.7, 실무 유용성 0.95 대 0.7로 Qwen을 높게 평가했고, Qwen은 도구를 10회 호출하며 깔끔하게 진행했습니다. 독립 심판도 Sonnet이 가상의 수치와 날짜를 단정했다고 지적했지만, 이 결과를 하네스 노드의 근거로 쓰지는 않았습니다.

5번과 6번은 결과가 극단적이어서, 결론에 쓰기 전에 원인을 먼저 확인해야 했습니다.

41분은 모델 속도가 아니라 재시도 설정이었습니다

6번 코드 과제에서 Qwen은 2,488초(약 41분), Sonnet은 84초가 걸렸습니다. 30배 차이를 그대로 적으면 모델 속도 비교처럼 읽힙니다.

실행 기록을 보니 Qwen 쪽은 judge_then_loop가 첫 답을 미달로 판정할 때마다 8,192토큰 상한까지 답을 다시 생성하고 있었습니다. 2,488초 안에는 모델 추론 시간만이 아니라 재생성 회차가 들어 있었습니다.

최대 반복을 1로 두어 재시도를 없애고, max_tokens를 4,096으로 제한해 과생성을 막았습니다. 같은 모델로 실행 시간이 약 100초가 됐습니다. 이 조정은 7번 라운드에서 두 모델에 똑같이 적용했습니다. 처음의 차이는 모델 차이가 아니라 설정이 만든 차이였고, 하네스 설정이 결과에 미치는 영향이 모델 교체만큼 클 수 있다는 관찰이기도 했습니다.

빈 출력의 원인을 세 층까지 내려가 확인했습니다

5번에서 Qwen의 결과는 처음에 "빈 출력"으로 기록됐습니다. 진단용 워크플로우로 다시 실행하고 전체 스트림을 보니 정확히는 빈 출력이 아니었습니다. "리포트를 작성하겠습니다. 먼저…" 같은 서두에서 잘렸고, judge 점수는 0.00, 실행 시간은 약 45초였습니다.

스트림을 따라가며 원인을 좁혔습니다. 요약하면 다음과 같습니다.

메타 검색 도구 호출 → None 반환 (3회)
실행 요약: tools_executed=3 · success_count=3 · error_count=0 · total_chars=12
→ Qwen: "검색 결과가 없다, 다른 키워드로 다시 검색"
→ judge 0.00 → retry
→ 재시도 회차도 검색만 반복, 본문 합성 없음 → 서두 조각이 최종 출력

첫째 층은 도구입니다. 메타 검색 MCP 도구가 None을 반환했습니다. 개발 서버에 해당 검색 백엔드의 데이터가 없었습니다.

둘째 층은 하네스입니다. 엔진의 도구 실행 단계(s07_act)가 None을 성공으로 집계했습니다. 도구 결과를 오류 아님(is_error=False)으로 기록했기 때문에 실행 요약은 성공 3, 오류 0이었습니다. 모델도 judge도 "도구는 실행됐지만 데이터가 0"이라는 상황을 구분할 수 없었습니다.

셋째 층은 루프입니다. Qwen은 빈 결과를 받고 다른 키워드로 재검색을 반복했고, 자체 지식으로 답을 완성하는 쪽으로 넘어가지 않았습니다. judge가 0.00을 주자 judge_then_loop가 재시도를 강제했고, 재시도 회차도 검색만 반복했습니다.

같은 조건에서 Sonnet은 빈 검색 결과를 받은 뒤 자체 지식으로 답을 완성했습니다. 5번에서 Sonnet이 이긴 이유입니다. 모델의 대응 방식에 차이가 있었지만, 그 차이를 극단적인 결과로 키운 것은 빈 결과를 성공으로 넘긴 엔진이었습니다.

먼저 설정으로 고칠 수 있는지 확인했습니다. 시스템 프롬프트에 "검색 결과가 비면 반복하지 말고 지식으로 완결하라"는 가드를 넣고 최대 반복을 3으로 늘렸습니다. 결과는 같았습니다. 1차 회차에서 검색 3회, judge 0.00, 재시도로 이어졌고, 2차 회차에서 180초 시간 초과로 끝났습니다. 프롬프트 지시만으로는 judge가 강제하는 재시도와 데이터 없는 도구의 반복 호출을 끊을 수 없었습니다.

프롬프트가 아니라 도구 결과의 계약을 고쳤습니다

선택지는 네 가지였습니다.

선택지 장점 단점 판단
프롬프트 가드 코드 변경 없음 실측에서 실패(180초 시간 초과) 기각
검색 백엔드 복구 두 모델 모두 정상화 이 도구만 해결되고 엔진 결함은 남음 별도 과제
도구 없는 과제로만 데모 즉시 가능 문제를 회피할 뿐 기각
도구 실행 단계가 빈·오류 결과를 실패로 처리 모든 도구 소스에 일반 적용, 특정 도구 분기 없음 엔진과 SDK 양쪽 배포 필요 채택

도구 실행 단계가 ToolSource 결과의 is_error를 존중하도록 바꿨습니다. is_error가 참이면 ToolError로 올려 실패로 기록하고, 기존의 연속 실패 폴백 가이드가 동작하게 했습니다. 이 폴백은 5월에 도입한 것으로, 도구가 연속으로 실패하면 연속 실패 카운터(상한 3)에 따라 모델에 대응 가이드를 주는 경로입니다. 새 분기를 만들지 않고, 이미 있는 실패 처리 경로에 빈 결과가 들어가게 한 것입니다. 원문 기준의 동작을 의사 코드로 옮기면 다음과 같습니다.

도구 실행 단계 (수정 후)
  result = 도구 소스 호출(name, args)
  if result.is_error:
      ToolError(result.content, name) 발생   → 실패로 기록 → 연속 실패 폴백
  else:
      도구 결과로 추가(result.content)
 
플랫폼 MCP 도구 소스 (수정 후)
  결과가 비었거나 None → content = "[no_data]", is_error = True

단위 검증에서는 is_error 소스가 ToolError를 올리고 정상 소스는 내용을 그대로 반환하는 것을 확인했습니다. 수정은 6월 29일 엔진과 SDK에 함께 반영해 PyPI로 배포했고, 플랫폼은 SDK 의존성 범위 안이라 다시 핀을 바꿀 필요가 없었습니다. 플랫폼 MCP 도구 소스의 [no_data] 처리는 별도 변경으로 올렸습니다.

재현했더니 3승 1패가 사실상 동률로 정정됐습니다

설정을 조정한 7번 라운드의 결과입니다.

과제 Sonnet Qwen 소요(Sonnet/Qwen) 승자
B2B SaaS 시장 진입 전략 0.94 0.97 58초/123초 Qwen
데이터 프로젝트 실패 원인 0.98 1.0 48초/90초 Qwen
원격근무 문제와 해결 0.98 1.0 52초/88초 Qwen
앱 마케팅 전략 0.96 0.9 54초/115초 Sonnet

Qwen이 높은 점수를 받은 이유는 채점 기준에서 찾을 수 있었습니다. Sonnet은 구체적인 수치와 날짜를 출처 없이 단정하는 경향이 있었고, 이는 4번의 독립 심판도 지적한 부분입니다. Qwen은 더 신중하고 실행 가능한 형태로 썼습니다. 같은 설정에서 모델만 다른데, 고정된 Sonnet 심판이 자기 기준으로 Qwen에 더 높은 점수를 준 것입니다. 다만 이것은 채점 기준을 무엇으로 정하느냐가 승패에 직접 영향을 준다는 뜻이기도 합니다.

3승 1패를 결론으로 쓰기 전에 재현 실행을 했습니다. 저장된 두 워크플로우를 개발 서버 API로 다시 불러 같은 설정(최대 반복 1, judge_then_loop, 고정 Sonnet 심판)으로 실행하고, 처음 보는 과제 두 개를 추가했습니다.

과제 Qwen Sonnet 승자 비고
원격근무 문제와 해결 1.00 0.98 Qwen 원본과 같은 점수
B2B SaaS 시장 진입 0.95 0.92 Qwen 재현
데이터 프로젝트 실패 원인 1.00 0.98 Qwen 재현
앱 마케팅 1.00 0.95 Qwen 원본은 Sonnet 승, 이번에는 뒤집힘
스마트팩토리 로드맵(신규) 0.94 0.95 Sonnet 0.01 차
구독 이탈 대응 전략(신규) 0.94 0.95 Sonnet 0.01 차

결과는 Qwen 4승, Sonnet 2승이었고 양쪽 모두 0.92~1.00으로 pass였습니다. 서버 측에서 실제로 실행됐다는 것은 저장본을 다시 불러 확인한 입력이 실행 중인 과제와 일치한 것, 스트림에 하네스 노드 로거 기록 26건과 단계별 서버 로그가 남은 것, judge의 "검증 점수 → pass" 기록으로 확인했습니다.

이 표로 "Qwen이 이긴다"고 쓸 수는 없었습니다. 점수 차가 0.01~0.05로 작고, 원본과 비슷한 과제는 Qwen이, 완전히 새로운 과제 두 개는 Sonnet이 0.01 차로 앞섰습니다. 앱 마케팅처럼 같은 과제에서 승자가 바뀌기도 했습니다. 어느 과제를 고르느냐에 따라 양쪽 모두 "이겼다"고 말할 수 있는 상태였습니다. 그래서 결론을 "실무 컨설팅 과제에서 동급에서 근소 우위, 사실상 동률"로 정정했습니다.

속도는 일관되게 차이가 났습니다. 재현 실행에서 Qwen은 약 73~92초, Sonnet은 약 38~52초로 Qwen이 약 2배 느렸습니다. 도구를 조합하는 과제에서의 견고성도 Sonnet이 앞섰습니다.

지표 이전 이후 측정 조건
코드 과제 Qwen 실행 시간 2,488초 약 100초 최대 반복 1, max_tokens 4,096
실무 과제 비교 3승 1패(4과제, 1회) 4승 2패(6과제), 점수 차 0.01~0.05 저장 워크플로우 재실행, 고정 심판
응답 시간 해당 없음 Qwen 약 73~92초, Sonnet 약 38~52초 재현 실행
빈 도구 결과 처리 성공으로 집계 ToolError로 실패 처리 엔진 단위 검증

회고와 한계

이 실험에서 가장 쓸모 있었던 산출물은 승패표가 아니라 엔진 결함이었습니다. 5번과 6번을 모델 차이로 기록했다면 하네스의 재시도 설정과 도구 결과 처리 문제는 그대로 남았을 것입니다. 모델 비교에서 극단적인 결과가 나오면, 먼저 그 실행이 모델 외에 무엇을 함께 측정했는지 확인해야 한다는 것을 배웠습니다.

한계도 분명합니다. 심판이 비교 대상 중 한 모델과 같은 Sonnet이라, 채점 기준과 일부 블라인드 라운드로 보완해도 편향을 배제할 수 없습니다. 과제는 6개이고 과제당 실행은 1회라 표본이 작습니다. 빈 도구 결과 수정은 단위 검증과 배포까지 마쳤지만, 개발 서버 컨테이너는 시작 시점에 패키지를 설치하므로 재기동 전에는 반영되지 않았고, 문제가 됐던 워크플로우로 종단 재현은 하지 못했습니다. 결정적인 우위를 보이려면 Sonnet이 실제로 틀리는 함정형 과제가 필요하겠지만, 그런 과제를 고르는 순간 체리피킹이 될 수 있어 선택 과제로만 남겼습니다.