현장에서 다시 잰 것들 · 1편

규칙을 고친 뒤에도 남은 판정 흔들림은 공유 추론 서버에서 왔습니다

대형 유통 품질검사 에이전트가 같은 서류에 합격과 불합격을 번갈아 냈습니다. 판정 규칙, 이미지 판독, 프롬프트, 부하를 하나씩 제외하고 같은 텍스트를 10번 넣는 실험으로 공유 서버의 배치 처리까지 좁힌 과정입니다.

현장에서 다시 잰 것들 시리즈 · 1/6
전체 목차 펼치기
  1. 01규칙을 고친 뒤에도 남은 판정 흔들림은 공유 추론 서버에서 왔습니다
  2. 02그래프 서버가 없는 곳에서 빌드는 트리플 0개로 완료되고 있었습니다
  3. 0398 대 86으로 진 평가는 같은 조건의 비교가 아니었습니다
  4. 04에이전트마다 빌드하면 같은 문서를 에이전트 수만큼 다시 읽어야 했습니다
  5. 05같은 질문에 근거가 매번 달랐던 이유는 정렬 없는 LIMIT이었습니다
  6. 06업로드마다 LLM을 부르는 빌드는 자동으로 켜 둘 수 없었습니다

대형 유통 고객사의 상품 품질검사 에이전트는 같은 서류를 다시 넣으면 합격·불합격 코드가 바뀌었습니다. 판정 규칙을 결정형으로 다시 써서 실데이터 23건을 세 번씩 실행해도 판정이 바뀌지 않게 만들었지만, 실제 파일로 전체 파이프라인을 실행하자 경계에 있는 문서가 여전히 흔들렸습니다. 남은 흔들림의 원인을 무엇을 제외해 가며 공유 추론 서버까지 좁혔는지 정리합니다.


같은 서류에 판정이 바뀌면 판정을 쓸 수 없습니다

이 에이전트는 상품기술서, 시험성적서, 케어라벨 같은 서류를 파일마다 읽고 합격(01), 불합격(02), 해당 없음(03), 보류(10) 코드를 낸 뒤 상품 단위 종합 판정을 만듭니다.

기존 워크플로우의 실행 기록을 보면 같은 입력을 다시 넣었을 때 파일 판정이 바뀌는 비율이 1158%였습니다. 결과를 검토하는 judge는 재시도를 16.858.2% 일으켰는데 상당수가 정답에 형식을 문제 삼은 경우였고, 재판정이 오히려 결과를 나쁘게 만드는 경우도 있었습니다. 현업이 판정을 믿기 어려운 상태였습니다.

첫 번째 용의자는 규칙의 모호함이었습니다

원본 시스템 프롬프트 약 90KB에서 절대 규칙 54개를 뽑아 모호한 표현을 결정형으로 다시 썼습니다. 허용 오차 분기, 등급 표기 정규화(3-4급은 3.5), 기준이 없는 선택 항목은 합격 승계, 진위 검증과 판정의 분리 같은 것들입니다. judge도 수정했습니다. 기존 기준에는 응답에 세션 식별자를 넣으라는 항목이 있었는데, judge는 제출된 결과만 보고 채점했기 때문에 그 항목은 통과할 방법이 없었습니다. 사소한 형식 지적에도 0점을 주는 필수 기준이 여럿이었습니다. 통과할 수 없는 기준은 재시도만 늘리고, 재시도는 판정을 다시 뽑게 만듭니다. judge는 제출된 결과만 보고 세 단계 점수로 판단하게 바꿨고, 온도는 0.05로 낮췄습니다.

실행 기록에서 만든 실데이터 23건을 원본과 같은 모델(Qwen3.6-27B)로, 같은 입력을 세 번씩 넣어 실행했습니다. 판정이 바뀐 건은 0/23이었고 21건이 정답이었습니다. 틀린 2건은 입력에 판독값이 없거나 정답 라벨이 없는 경계 사례였고, 세 번 모두 같은 보류를 냈습니다.

다만 이 측정은 OCR 요약 텍스트를 입력으로 넣은 판정 규칙 시험이었습니다. 실제 파일을 내려받아 이미지와 스캔 문서까지 읽는 전체 파이프라인은 아니었습니다.

전체 파이프라인에서는 경계 문서만 흔들렸습니다

실제 파일로 일곱 건의 검사 요청을 실행하자 흔들림이 다시 나타났습니다. 판정 모델은 다른 사용자와 함께 쓰는 vLLM 추론 서버에서 서빙되고 있었습니다. 이번에는 흔들림의 모양이 달랐습니다. 텍스트가 분명한 PDF는 흔들리지 않았고, 경계에 있는 파일만 판정이 바뀌었습니다. 기준 충족 여부가 아슬아슬한 성적서는 합격과 해당 없음 사이에서, 판독이 어려운 도안이나 스캔 문서는 해당 없음과 보류 사이에서 오갔습니다.

규칙을 더 다듬고 이미지 판독 설정을 바꿔 가며 요청마다 다섯 번씩 실행했습니다. 한 요청에서 흔들림을 잡으면 다른 요청에서 새로 생겼습니다. 바뀌는 것은 어느 파일이 흔들리는지뿐이었고, 만장일치 비율은 85~100% 언저리에서 그대로였습니다.

조정 파라미터가 흔들리는 대상만 옮기고 총량을 못 바꾼다면, 원인이 그 파라미터 밖에 있을 가능성이 큽니다. 이때부터 고치기보다 원인을 좁히는 실험으로 방향을 바꿨습니다.

하나씩 고정하며 제외했습니다

가설마다 그 가설만 걷어내는 실험을 하나씩 붙였습니다.

가설 실험 결과
이미지를 매번 다르게 읽는다 OCR을 한 번만 하고 그 텍스트로 판정 여전히 흔들림
입력이나 프롬프트가 문제다 완전히 같은 OCR 텍스트를 같은 프롬프트로 10회 판정 합격 9회, 불합격 1회
동시 요청이 많아서다 짧은 지시문과 두 글자 출력으로 직접 호출, 병렬 10개로 20회 20회 모두 같음
긴 프롬프트와 긴 출력 실행기의 긴 프롬프트로 같은 텍스트 판정 저희 쪽 부하가 낮아도 5 대 5
공유 서버 같은 긴 프롬프트와 300토큰 안팎의 사유 출력을 외부 전용 엔드포인트에서 10회 10회 모두 같음

두 번째 실험이 범위를 가장 크게 줄였습니다. 입력 텍스트와 프롬프트가 글자 하나까지 같은데 9 대 1로 갈렸으니, 파일 다운로드와 이미지 판독과 OCR은 원인에서 빠집니다. 남는 것은 추론 자체였습니다.

온도 0이 같은 출력을 보장하지 않았습니다

저희가 받아들인 설명은 이렇습니다. 공유 추론 서버는 여러 사용자의 요청을 배치로 묶어 한꺼번에 계산합니다. 배치 구성이 바뀌면 부동소수점 연산 순서가 바뀌고 출력 확률이 아주 조금 달라집니다. 온도 0의 탐욕적 디코딩이라도 두 후보가 근소한 자리에서는 이 차이로 다른 토큰이 골라질 수 있습니다. 사유를 길게 쓴 뒤 코드를 내는 판정은 그런 자리를 더 많이 지나고, 경계 문서는 마지막 코드 자체가 근소하게 갈립니다.

출력을 두 글자로 줄이면 흔들림은 사라졌지만 판정이 틀렸습니다. 4.5급이 기준 3.5급을 넘는데 불합격을 주거나, 시험기관이 종합 합격이라 적은 서류에 불합격을 줬습니다. 추론할 자리를 없애면 결정성은 얻고 정확도를 잃는 상황이었습니다. 외부 전용 엔드포인트에서는 긴 사유를 써도 10회가 같았습니다.

전용 엔드포인트를 쓰기 전까지는 같은 요청을 여러 번 실행해 파일별 다수결을 내는 방식으로 종합 판정을 안정시켰습니다. 9 대 1 정도의 흔들림이라 다수결이 잘 수렴했습니다. 종합 판정은 LLM 노드에서 코드로 옮겼습니다. LLM 종합 노드는 불합격 한 건을 세고도 합격 코드를 내는 등 정해진 산술 규칙조차 비결정적으로 어겼기 때문입니다.

선택과 남은 조건

권고한 구조는 역할을 둘로 나눈 것입니다. OCR은 저희 인프라에서 실행해 이미지가 밖으로 나가지 않게 하고, 판정과 교차 검증과 종합은 결정적으로 답하는 전용 텍스트 모델과 코드가 맡습니다. 이 구조로 한 요청의 전체 파이프라인을 두 번 실행해 파일 판정이 모두 같았고, 일곱 요청을 끝까지 처리했습니다.

남은 조건은 기술 밖에 있었습니다. 전용 엔드포인트는 외부 API라 고객 문서에서 나온 OCR 텍스트가 밖으로 나갑니다. 운영 적용에는 고객사와 법무 승인이 필요했습니다.

확인하지 못한 것도 적어 둡니다. 외부 엔드포인트는 같은 Qwen 계열이지만 다른 모델이었고, 그 서비스가 내부에서 요청을 어떻게 배치로 묶는지는 알 수 없습니다. 연산 순서에 대한 설명도 출력 확률을 직접 비교해 확인한 것은 아닙니다. 그래서 원인을 완전히 분리했다고 말하기보다, 지금까지의 관측과 가장 잘 맞는 설명이라고 보고 있습니다.

같은 입력에 다른 답이 나오면 프롬프트부터 의심하게 됩니다. 이번에는 입력을 글자 단위로 고정한 반복 실험이 원인을 가장 빨리 좁혀 준 것 같습니다.