← 평가·검증

Upstage API 하루 실측, 정확도를 맨 나중에 재기로 했다

영수증 50장에서 상호명 F1이 76%로 나왔다. 오답 14건을 원본과 대조하니 모델이 틀린 것은 1건이었고 나머지는 정답 쪽과 내가 쓴 스키마 문구가 만들었다.

목차
  1. 질문
  2. 판정
  3. 근거
  4. 정확도 - 표를 움직인 것은 모델이 아니었다
  5. 신뢰성 - 문서와 응답이 어긋나는 지점
  6. 노코드 - 같은 답을 주고 다른 것을 준다
  7. 한계·재검토 트리거

질문

영수증 50장에서 상호명 F1이 76%로 나왔다.
오답 14건을 원본 영수증과 하나씩 대조했다.
모델이 틀린 것은 1건이었다.

여덟 건은 정답 쪽 오탈자이거나 잘린 크롭이었다.
다섯 건은 영수증에 두 값이 다 인쇄돼 있어서 어느 쪽을 그 필드로 볼지가 정의 문제였다.
합계 오답 두 건은 내가 스키마 설명에 "결제한 최종 금액"이라고 적어서 났다.
모델은 그 지시를 따랐다.

그러면 76%는 누구의 점수인가.
이 숫자를 벤더 비교표에 그대로 올려도 되는가.

판정

정확도는 맨 나중에 잰다.
그 앞에 셋을 먼저 고정한다. 무엇을 정답으로 볼지, 응답을 어떤 형식으로 받을지, 얼마를 썼는지 코드로 확인할 수 있는지다.

이 하루에 표를 크게 움직인 원인은 세 번 다 그 셋이었다.
상호명 F1은 76%에서 92%로 올라갔는데 모델은 그대로 두고 정답 쪽 여덟 건만 고쳐서 그렇게 됐다.
JSON 파싱은 0/10에서 10/10이 됐다. 프롬프트를 고쳐서가 아니라 요청에 response_format 한 줄을 붙여서다.
노코드로 만든 에이전트는 화면과 API가 여덟 필드 전부 문자 단위로 같은 답을 냈다. 갈린 쪽은 답이 아니라 그 답에 딸려 오는 신뢰도와 과금 정보였다.

세 번 다 모델 앞뒤의 조건이 점수를 정했다.
그러니 정확도 표를 먼저 펴는 순서가 틀렸다.

근거

정확도 - 표를 움직인 것은 모델이 아니었다

주장실측측정 조건
상호명 F1을 끌어내린 것의 대부분이 모델 오답이 아니었다strict F1 76%, 오답 14건 중 모델 오답 1건2026-09-07 실측. KORIE 영수증 50장, 범용 추출 3필드, 정규화 후 완전일치. 오답 14건은 원본 이미지와 수동 대조
정답 쪽만 정정해도 표가 16%p 움직인다strict 76% → 대조 후 92%같은 50장. 정정 대상은 정답 크롭의 오탈자·부분 크롭 8건뿐이고 모델 출력은 손대지 않았다
합계 오답은 전부 스키마 문구가 만들었다amount 오답 2건, 그중 정의 문제 2건같은 50장. 스키마 설명이 결제 최종 금액을 가리켰고 두 건 다 할인이 붙은 영수증
파싱 단계의 포함률은 하한이다상호명 44/50, 거래일 50/50, 합계 50/502026-09-07 실측. 같은 50장 mode=standard. 파싱 결과 문자열 안에 정답 값이 살아 있는지만 판정
놓친 6건 중 넷은 글자가 거의 같았다문자 유사도 0.938 · 0.9 · 0.882 · 0.833같은 50장. 엄격 판정이 깨진 6건에만 보조로 계산했고 승패 판정에는 쓰지 않았다
노코드 경로도 같은 필드에서 무너졌다8필드 합계 64/77, ReceiptNumber만 4/102026-09-07 실측. 같은 세트 앞 10장, 8필드 스키마, 같은 정규화 규칙

ReceiptNumber 여섯 건을 열어보면 정답 쪽 값의 길이가 여덟 글자에서 열다섯 글자까지 흩어져 있다.
206-86-50913 같은 사업자등록번호 형태와 훨씬 긴 승인번호가 한 이름 밑에 같이 들어 있다.
모델이 못 읽은 것이라기보다 그 이름이 한 뜻이 아닌 것이다.
필드 이름을 합의하기 전에 잰 정확도는 합의 없이 잰 값이다.

같은 필드에서 노코드 경로와 API 경로가 나란히 걸린 것도 그래서다.
화면에서 한 번도 돌리지 않은 이미지를 API로만 넣어도 여덟 필드 중 둘이 어긋났고 그중 하나가 또 이 번호 필드였다.

신뢰성 - 문서와 응답이 어긋나는 지점

주장실측측정 조건
JSON 파싱 실패는 모델이 아니라 요청 한 줄이 갈랐다형식 지정 없이 원문 그대로 파싱 0/10, json_object·json_schema 각 10/102026-09-07 실측. 같은 합성 영수증·같은 프롬프트로 arm당 10회, temperature: 0.7. 프롬프트에 "코드펜스를 쓰지 마라"를 한국어로 명시한 상태
실패한 것은 내용이 아니라 겉포장이었다펜스 제거 후 스키마 준수 100%, 값 정확 100% (3 arm 전부)같은 30회. json.loads 성공률과 펜스 제거 후 구조 검사를 따로 계산
reasoning을 켠 비용의 대부분은 눈에 안 보인다출력 토큰 23,765 중 reasoning_tokens 19,8002026-09-07 실측. 20문항, reasoning_effort: "high", temperature: 0. 같은 문항 off arm은 출력 8,212토큰에 reasoning 0
파일 크기 한도가 문서 페이지마다 달랐고 실제 동작은 큰 쪽이었다57MB·79MB 파일 200·1쪽 과금, 100MB·150MB는 413. 열거형 밖 옵션 값도 200·1쪽2026-09-07 실측. 무료 키 1개, 서울에서 호출. 입력 요건 페이지는 동기 100MB, API 레퍼런스 표와 에러 코드 페이지는 50MB로 적혀 있다
표준 재시도 헤더가 없다429 34건, 429 헤더 표본에 표준 Retry-After 0건2026-09-07 실측. 직접 호출 174회 구간. 커스텀 헤더는 유닉스 시각을 담아 오는데, 문서의 rate limits 페이지는 그렇게 정의하고 에러 코드 페이지는 "초"로 설명한다
쓴 만큼을 코드로 대사할 방법이 없었다사용량 조회 후보 3경로 전부 4042026-09-07 실측. 21쪽 문서를 5초에 끊은 호출의 과금 여부를 확인하려다 막혔다
임베딩 모델 교체는 임계값을 같이 옮긴다무관 쌍 평균 코사인 0.2617 → 0.49162026-09-07 실측. 한국어 30쌍 중 무관 7쌍, 두 passage 모델에 같은 60문장. 순위 품질은 0.8489 대 0.8311로 이 세트에서 갈리지 않았다

세 번째 줄이 이 트랙에서 제일 비싼 발견이다.
출력 토큰의 83%가 사용자에게 안 보이는 텍스트인데 모델은 그 내용을 돌려주지도 않는다.
응답 본문 길이로 예산을 잡으면 몇 배가 어긋난다.

네 번째 줄은 성격이 다르다.
에러는 소리를 내지만 200은 안 낸다.
문서의 한 페이지는 파일 크기 한도를 100MB로, 다른 페이지들은 50MB로 적어둔 상태에서 79MB 파일이 통과하고 1쪽이 과금됐다. 실제 동작은 100MB 쪽이었다.
클라이언트가 스스로 입력을 검사하지 않으면 이 두 줄은 조용히 청구서로만 나타난다. 어느 페이지의 값을 임계값으로 가져갈지가 먼저 정해져야 한다.

임계값 이야기는 예전 기록과 이어진다.
2025년 11월 검색 대회에서 4,096차원 벡터를 색인에 넣지 못해 랜덤 프로젝션으로 1,536차원까지 줄여 썼다(리포 README).
그때 잃은 것이 얼마인지 이번 데이터로 다시 재보니 같은 30쌍에서 코사인 변화가 평균 0.0111, 최대 0.0444였다.
순위를 뒤집을 크기가 아니다. 당시의 우회는 정당했다. 새 모델이 처음부터 1,024차원으로 나오는 지금은 그 단계 자체가 사라진다.

노코드 - 같은 답을 주고 다른 것을 준다

주장실측측정 조건
화면과 API가 다른 답을 내지는 않는다8필드 중 정규화 후 일치 8, 원문자열까지 동일2026-09-07 실측. 같은 에이전트·같은 이미지 1장. 다만 이 실행은 캐시 적중 응답이었다
캐시가 적중하면 usage가 전부 0으로 온다입력·출력·합계 토큰 0, 캐시가 빗나간 실행은 합계 4,3022026-09-07 실측. 같은 에이전트, 화면에서 한 번도 안 돌린 이미지 1장을 대조군으로 씀
지시한 출력 형식이 값에 새어 나온다HTML 마크업 유출 1건, null 자리에 콜론 2건, 빈 문자열 0건2026-09-07 실측. 앞 10장 × 8필드 = 80칸. 시스템 프롬프트와 필드 설명 양쪽에 "없으면 null"을 넣은 상태
API 전용 이미지도 같은 두 필드에서 걸렸다8필드 중 6 일치2026-09-07 실측. 불일치는 주소 한 글자 차이와 위의 번호 필드
Free 한도는 페이지 수가 아니라 제품 가중 퍼센트로 깎인다Parse 1쪽 = 1%, Extract 1쪽 = 4%. 앞 10장을 Parse→Extract로 돌린 뒤 100% → 50%, Agents API로 새 이미지 1건을 더 넣은 뒤 45%2026-09-07 Studio 조직 설정의 구독 페이지 화면 실측. results.json이 아니라 T4 실측 기록의 화면 인용이 근거다. 문서의 하루 100페이지 문구와 대비하면 Parse→Extract 에이전트의 실질 한도는 하루 20페이지

캐시 줄이 이 트랙의 핵심이다.
같은 파일을 다시 넣으면 usage가 전부 0으로 오고 결과 폴링도 한 번에 끝난다.
그 0을 "이 실행은 과금되지 않았다"로 읽을 수는 있지만 문서에는 캐시가 있다는 말도 그 필드 이름도 없다.
문서에 없는 동작에 과금 계산을 기대는 것은 계산이 아니라 관측이다.

쿼터 표시에서도 같은 어긋남이 나온다.
화면이 보여주는 것은 남은 페이지가 아니라 제품 가중치를 먹인 퍼센트고, Parse 한 쪽이 1%, Extract 한 쪽이 4%를 깎는다.
문서가 말하는 하루 100페이지를 믿고 용량을 잡으면 Parse→Extract 에이전트는 하루 20페이지에서 멈춘다.

출력 위생 세 건은 전부 지시를 어긴 값이다.
비어 있어야 할 필드에 라벨의 콜론만 들어왔다.
파싱 단계의 줄바꿈 태그는 문자열 값 안에 그대로 실렸다.
다행히 이 세 건은 API 응답의 신뢰도 표시로 걸러낼 수 있었다.
문제는 화면 쪽 사용자가 그 신뢰도를 가장 못 본다는 것이다.

한계·재검토 트리거

이 판정이 기대는 정답셋에 노이즈가 있다.
그래서 76%와 92% 중 어느 쪽도 단독으로 인용하면 안 된다. 둘을 같이 놓아야 이 판정이 성립한다.
92%는 내 수동 판정이 섞인 숫자다.

트랙 하나는 아예 못 쟀다.
영수증 전용 사전정의 추출기는 문서에 사용 예제까지 남아 있는데 호출하면 400을 준다. 없는 것을 있다고 쓰지 않는다.

돈을 더 낸 쪽의 값어치도 증명하지 못했다.
같은 10장에 상위 파싱 모드를 붙이면 쪽당 단가가 0.01달러에서 0.03달러로 오르고 지연 p50이 2,650ms에서 5,717ms로 늘어난다.
그런데 판정이 뒤집힌 건은 0건이었다.
이건 상위 모드가 쓸모없다는 뜻이 아니라 그것이 필요한 문서 유형이 이 세트에 없었다는 뜻이다.

무료 등급 하루치라는 것도 그대로 한계다.
한도 수치, 캐시 동작, 429 회복 시점은 전부 이 키 하나에서 나온 값이고 커밋 티어를 올리면 다시 재야 한다.
429 회복 시점은 버스트 직후 단발 프로브가 200을 받은 것까지만 확인했고 더 좁히지 못했다.

2026-09 기준 / 재검토 트리거: 같은 50장을 필드 정의를 합의한 정답으로 다시 채점했을 때 엄격 점수와 대조 후 점수의 차이가 5%p 이내로 좁혀지면 첫째 근거를 다시 연다.