
SPKE를 위해 제작한 오리지널 기사 삽화. 개념적 워크플로이며, 측정된 벤치마크가 아니다.
위험을 놓치는 초록색 체크 표시
상품 데이터 가져오기 작업이 정상적으로 끝났다고 생각해 보자. 결과는 JSON으로 파싱되고 모든 타입 검사도 통과한다. 이어서 상품 무게 필드에 1.25가 기록된다. 하지만 원문에 적힌 내용은 “포장 무게: 1250 g”이었다. 계산은 맞지만 필드가 틀렸다.
이는 가상의 예시이며, 실제로 측정한 모델 실패를 보고하는 것이 아니다. 핵심은 운영상의 문제다. 스키마는 숫자를 요구할 수 있지만, 그 숫자가 상품 자체의 무게인지, 포장 무게인지, 다른 옵션의 무게인지는 알지 못한다.
구조화된 출력은 특정 유형의 실패를 줄여 준다는 점에서 가치가 있다. 그다음에 엔지니어가 물어야 할 것은 애플리케이션이 어떤 데이터를 근거가 뒷받침하는 데이터로 승인할 것인지다. 이 글에서는 서로 독립적인 네 단계의 검증과 함께, “확정되지 않음”을 정식 상태로 표현할 수 있는 승인 기준을 권한다.
제공업체의 명세가 실제로 약속하는 것
OpenAI는 스키마를 준수하는 Structured Outputs를 문서화하고 있지만, 여전히 실수가 발생할 수 있다고 경고한다. 거부 응답도 별도로 처리해야 한다. OpenAI 가이드
DeepSeek의 Responses API 문서에는 json_schema 출력이 명시되어 있다. 이와 별개인 Chat Completions용 JSON Output 가이드는 유효한 JSON을 생성하는 모드를 설명하면서, 내용이 비어 있거나 출력이 잘릴 수 있음을 경고한다. Responses API · JSON Output
이는 2026년 10월 23일에 확인한 문서상의 사실이지, 새로운 벤치마크의 결과가 아니다. 인용한 페이지들은 어느 제공업체가 사용자의 업무 필드를 정확히 추출하는지 입증하지 않는다.
미국 업체와 중국 업체 중에서 선택하려는 팀이라면 구체적인 모델, 사용하는 API, 설정된 모드, 지원되는 스키마를 비교하는 것이 유용하다. 한 업체의 엄격한 스키마 모드를 다른 업체에 프롬프트로만 JSON을 요청하는 방식과 비교한 뒤, 그 차이를 국가별 역량 차이로 돌려서는 안 된다. 익숙한 요청 형식이라고 해서 동작까지 같다고 볼 수도 없다. 어댑터나 모델을 변경할 때마다 동일한 승인 테스트를 실행해야 한다.
레코드에 불확실성을 담을 자리를 마련하기
다음과 같이 만들어 낸 원문을 생각해 보자.
“포장 무게: 1250 g. 가격: 49.00. 상품 무게: 명시되지 않음. 할인 종료일: 04/05/26.”
데이터 가져오기 작업에 상품 무게, 가격의 통화, 모호하지 않은 할인 종료일이 필요하다고 하자. 이 텍스트로는 어느 것도 확정할 수 없다. 빈칸을 1.25 kg, USD, 4월 5일로 채우면 깔끔한 레코드는 만들어지지만, 그 과정에서 정보를 지어내게 된다.
대신 간단하고 명시적인 레코드 규약을 정의한다.
- value: 추출한 값 또는 null.
- status: supported(근거 있음), missing(누락), ambiguous(모호함), conflict(상충) 중 하나.
- evidence: 출처 식별자, 출처 내 위치, 관련 원문.
- transformation: 추출 후 수행한 결정론적 변환. 수행하지 않았다면 변환 없음.
이 예시에서 상품 무게는 null이고 status는 missing이다. 통화도 null이고 status는 missing이며, 날짜는 null이고 status는 ambiguous다. 포장 무게는 별도 필드에서 1.25 kg으로 변환할 수 있지만, 원래 값인 1250 g을 보존하고 변환 내용을 기록해야 한다. 이 값이 상품 무게 필드로 옮겨 가서는 안 된다.
필수 필드라고 해서 추측까지 필수여야 하는 것은 아니다. 키는 반드시 포함하도록 하되, 알 수 없다는 상태를 명시적으로 허용하면 된다. missing과 ambiguous를 구분해야 한다. 전자는 추가 자료가 필요하고, 후자는 이미 있는 자료의 모호함을 해소해야 한다. 출처 간 상충도 이 두 상태와 별도로 구분한다.
모델의 신뢰도 점수로 이 상태들을 뒤집어서는 안 된다. 모델이 0.99라는 점수를 부여하더라도 근거 없는 값은 여전히 근거가 없다. 신뢰도를 운영에 활용하려면 먼저 대표성 있는 별도의 평가 데이터에서 점수와 실제 정확성 사이의 관계를 검증해야 한다.
기록하기 전에 거칠 네 단계
-
완료. 응답이 사용할 수 있는 종료 상태에 도달했는지 확인한다. 빈 출력, 잘린 출력, 거부, 제공업체 오류는 서로 다른 결과다. 이를 모두 복구한다는 명목으로 정상 처리된 것처럼 보이는 빈 레코드로 바꿔서는 안 된다. 이유를 기록하고 의도한 처리 경로로 보낸다.
-
구조. 응답을 파싱하고 버전 관리되는 애플리케이션 스키마에 따라 검증한다. 타입, 필수 키, 허용된 상태뿐 아니라 제공업체가 강제하지 않는 제약 조건도 확인한다. 지원되지 않는 스키마 기능은 작동할 것이라고 암묵적으로 가정하지 말고 통합 단계에서 거부해야 한다. 제약이 더 약한 출력 모드로 대체한다면 그 사실이 텔레메트리에 드러나야 하며, 해당 모드는 별도로 평가해야 한다.
-
근거. 인용한 출처가 존재하는지, 보관한 원문 버전에 인용 구절이 실제로 있는지 확인한다. 그런 다음 그 구절이 해당 값과 필드를 뒷받침하는지 검사한다. 실제로 존재하는 인용문도 무관한 근거일 수 있다. “포장 무게: 1250 g”은 상품 무게를 뒷받침하지 않는다. 스캔 문서는 OCR 텍스트만 검사하면 OCR 오류를 놓칠 수 있으므로, 중요한 경우에는 원본 페이지를 확인해야 한다.
원문에서 근거를 확인했다는 것은 원문을 충실히 반영했다는 뜻이지, 출처 자체가 정확하거나 최신이라는 뜻은 아니다.
- 업무상 의미. 가능한 경우 단위, 통화 명시 여부, 날짜 해석, 상품 옵션의 동일성, 허용된 범주, 필드 간 일관성에 결정론적 검사를 적용한다. 일부 판단에는 사람의 검토가 필요하다. 이런 사례를 검증된 레코드처럼 포장하지 말고 검토 대기열에 넣는다.
각 단계는 서로 다른 질문에 답한다. 근거 검증에 실패한 것은 형식 문제가 아니다. 값이 나타날 때까지 같은 추출을 반복해도, 애초에 그 값을 포함하지 않은 출처의 문제는 해결되지 않는다.
앞의 가상 예시라면 status가 supported인데 evidence가 없는 값을 간단한 규칙으로 차단할 수 있다. 다른 규칙으로는 모호한 날짜가 자동으로 가져와지는 것을 막을 수 있다. 이런 규칙은 유용한 안전장치이지, 진실을 증명하는 것은 아니다. 승인 여부는 모델이 생성한 status와 별도로 판단해야 한다.
스키마가 보지 못하는 실패를 테스트하기
규모를 늘리기 전에 일반적인 운영 데이터와 유사한 예시뿐 아니라, 까다로운 사례를 모은 소규모 테스트 세트도 만든다. 최소한 다음 사례를 포함한다.
- 필요한 사실이 없다. 기대 결과는 그 누락 상태를 그대로 보존하는 것이다.
- 상품 무게가 아닌 포장 무게처럼, 다른 항목명 아래에 숫자가 있다.
- 원문의 두 버전이 서로 다르다. 레코드는 그 상충을 드러낸다.
- 날짜가 모호하거나, 데이터를 받을 시스템이 요구하는 정보가 부족하다.
- 단위 변환은 허용되지만, 원래 값을 나중에 검증할 수 있어야 한다.
- 출처에 값을 지어내라는 지시가 포함되어 있다. 이를 추출 정책이 아닌 출처의 내용으로 취급한다.
- 응답이 비어 있거나, 중단되거나, 거부되거나, 구조적으로 유효하지 않다. 각각 올바른 실패 처리 경로로 보낸다.
- 인용문은 존재하지만 제안된 값을 정당화하지 못한다.
모델을 비교하기 전에 기대 결과를 작성한다. 출처에서 답을 얻을 수 없다면 의도적으로 답을 내놓지 않는 것도 정답에 포함한다. 승인된 레코드와 보류된 레코드 양쪽에서 표본을 뽑아 독립적인 검토를 수행한다. 그렇지 않으면 어려운 사례를 피하는 것만으로 시스템이 더 안전해 보이거나, 조용히 추측하는 것만으로 더 생산적이어 보일 수 있다.
결과에서 원문의 언어와 문서 유형을 구분해 볼 수 있어야 한다. 공급업체의 중국어 표, 독일식 소수 표기, 영어 서술문은 서로 다른 테스트 구간이다. 각각의 결과가 하나의 전체 평균 속에 묻혀서는 안 된다.
파싱 성공뿐 아니라 승인 결과를 측정하기
분모를 명확히 밝히고 최소한 세 가지 수치를 보고한다. 여기서는 각 논리적 작업이 레코드 하나를 요청하며, 재시도는 새로운 작업으로 집계하지 않는다.
- 스키마 통과율: 스키마를 만족하는 레코드 수를 완료된 후보 레코드 수로 나눈다. 미완료, 거부, 오류 작업은 별도로 보고한다.
- 자동 승인 비율: 자동으로 승인된 레코드 수를 제출된 전체 작업 수로 나눈다.
- 승인된 레코드의 오류율: 감사한 승인 레코드 중 사실이나 필드 배정에 중대한 오류가 하나 이상 있는 레코드 수를 감사한 승인 레코드 수로 나눈다.
다음은 실험 결과가 아닌 가상의 비교다. 각 시스템은 동일한 작업 1,000개를 제출받고, 작업마다 완료된 후보 레코드 하나를 생성한다. 이 예시에서는 자동으로 승인되지 않은 모든 레코드를 검토 단계로 보낸다. 시스템 A는 스키마를 만족하는 레코드가 980개이고 800개를 자동으로 승인한다. 전수 감사에서는 승인된 레코드 중 20개에 중대한 오류가 발견된다. 시스템 B는 스키마를 만족하는 레코드가 970개이고 600개를 승인한다. 전수 감사에서는 승인된 레코드 중 3개에 오류가 발견된다.
A는 스키마 통과율에서 98% 대 97%, 자동 승인 비율에서 80% 대 60%로 앞선다. B는 승인된 레코드의 오류율이 0.5%로 A의 2.5%보다 낮다. 어느 쪽도 모든 상황에서 더 낫지는 않다. 선택은 잘못된 레코드가 초래하는 결과와, 추가로 보류되는 레코드 200개를 검토하는 비용에 달려 있다.
표본 감사에서는 점추정치를 보증처럼 제시하지 말고 표본 크기, 선정 방법, 불확실성을 보고한다. 검토자의 작업 시간과 복구 가능한 실패도 추적한다. 거의 모든 것을 사람에게 보내 낮은 오류율을 달성하는 시스템이 결과의 영향이 큰 업무에는 적합할 수 있지만, 그것이 광범위한 자동화를 입증한 것은 아니다.
다음으로 해 볼 일
수정 비용이 큰 필드 하나를 고른다. 어떤 원문 근거가 그 값을 뒷받침하는지, 무엇을 알 수 없는 상태로 볼지, 어떤 변환을 허용할지, 언제 사람이 결정해야 하는지를 정확히 정한다. 모델을 바꾸기 전에 까다로운 테스트 사례부터 만든다.
그다음 실제 운영 시스템에 기록하지 않고 레코드 후보만 생성하는 섀도 임포트를 실행한다. 승인 과정에서 놓친 오류와 보류로 생기는 업무량을 함께 살펴본다. 이 결과가 사전에 합의한 위험 기준을 만족할 때만 자동 기록을 확대한다.
실무적인 목표는 AI 워크플로에서 추론을 금지하는 것이 아니다. 추론한 답이 어느새 추출된 사실로 바뀌는 것을 막는 것이다. 깔끔한 JSON은 소프트웨어가 응답을 처리하는 데 도움이 된다. 잘 설계된 승인 기준은 그 응답을 언제 사용해도 되는지 사람이 판단하는 데 도움이 된다.
근거와 한계
이 글은 링크된 공식 API 문서와 SPKE의 독자적인 워크플로 분석을 결합했다. 상품 정보 발췌문, 시스템 비교, 권장 테스트 설계는 설명을 위한 예시다. 업체 간 벤치마크를 수행하거나 실제 운영 환경의 오류율을 측정하지 않았으며, 이러한 검사로 환각을 없앨 수 있음을 입증한 것도 아니다. API 기능은 바뀔 수 있으므로 배포 전에 선택한 모델과 엔드포인트를 확인해야 한다.