노트 · 데이터 피드

표가 사라지면 RAG 는 틀린 숫자를 자신 있게 말한다 — 공고문 214건으로 잰 HWP·PDF 표 추출

2026-09-06 · 시즐론 이광연

공고문에서 “기술평가 배점이 얼마냐”를 물으면 검색 증강 생성이 엉뚱한 숫자를 내는 일이 있습니다. 문서를 못 찾은 게 아닙니다. 문서는 찾았는데, 그 안의 표가 본문으로 풀리면서 “기술평가” 라는 라벨과 “90” 이라는 숫자가 서로 다른 줄로 흩어진 것입니다. 모델은 가까이 있는 다른 숫자를 집습니다. 로그에는 아무 오류도 남지 않습니다.

한국 공공 문서는 핵심을 표에 둡니다. 평가 배점, 제출서류 목록, 가격 내역, 과업 일정. 그래서 추출 단계에서 표가 어떻게 되는지가 검색 품질 전체를 좌우합니다. 말로만 하면 누구나 하는 얘기라, 숫자로 재 봤습니다.

무엇을 쟀나

실패는 두 종류다

형식 추출기 셀 보존 행 무결성 라벨↔숫자 짝
HWP (108) hwp5txt 0.152 0.017 0.057
HWP (108) 나이브 추출 0.988 0.038 0.104
HWP (108) 미리보아 체인 1.000 1.000 1.000
HWPX (59) XML 태그 제거 0.994 0.038 0.070
HWPX (59) 미리보아 체인 1.000 0.991 0.997

첫 번째 실패는 글자가 사라지는 실패입니다. hwp5txt 는 표를 <표> 한 토큰으로 바꿉니다. 셀의 85% 가 없어집니다. 이건 눈에 보입니다. 출력이 짧고, 배점표가 통째로 없습니다.

두 번째 실패는 구조가 사라지는 실패입니다. 나이브 추출과 XML 태그 제거는 글자를 99% 남깁니다. 텍스트 양이 정상이라 아무도 의심하지 않습니다. 그런데 행 무결성이 4%, 라벨↔숫자 짝이 7~10% 입니다. “기술평가” 와 “90” 이 둘 다 문서 안에 있지만 서로 다른 줄에 있습니다. 검색은 이 문서를 찾아 오고, 모델은 그 안에서 가까운 숫자를 고릅니다. 두 번째 실패가 첫 번째보다 위험한 이유입니다.

표를 표로 옮기면 어떻게 되는지가 세 번째 줄입니다. 형식을 제대로 읽으면 HWP 원본에서 셀·행·짝이 전부 살아남습니다. 한 행을 한 줄로, 셀을 구분자로 직렬화하는 것뿐이지 특별한 기술이 아닙니다. 다만 그렇게 하지 않는 범용 경로가 기본값이라는 게 문제입니다.

같은 문서를 PDF 로 받으면

공고는 흔히 같은 문서를 HWP 와 PDF 로 둘 다 붙입니다. 47쌍에서 원본과 PDF 를 비교했습니다(문서 평균).

셀 보존 행 무결성 라벨↔숫자 짝
HWP·HWPX 원본 1.000 0.998 0.995
PDF, 문서마다 가장 잘한 도구 0.872
PDF, 미리보아 체인 0.819 0.657 0.558
PDF, pdfplumber 표 인식 0.725 0.672 0.572
PDF, pdftotext -layout 0.729 0.552 0.605

PDF 에서는 문서마다 가장 잘한 도구를 골라 써도 셀의 13% 가 없고, 행 구조는 최대 67%, 라벨↔숫자 짝은 최대 61% 입니다. 어느 도구를 쓰느냐보다 어느 형식을 읽느냐가 더 큰 차이를 냅니다. 공고가 둘 다 붙였으면 HWP 를 읽어야 합니다.

측정이 우리 결함을 둘 찾았다

미리보아 체인의 첫 결과는 HWP 셀 보존 0.952, PDF 0.498 이었습니다. 100% 와 0.742 가 아니라. 그 차이가 어디서 왔는지 파 보니 둘 다 우리 코드의 결함이었습니다.

첫째, 확장자를 믿었습니다. 나라장터 첨부 216파일 중 5건은 이름이 .hwpx 인데 내용은 HWP 바이너리였고, 3건은 이름이 .hwp 인데 내용은 XML 이었습니다. 우리 체인은 .hwpx 이름을 보고 zip 을 열려다 5건 전부 실패했습니다. 첫 바이트를 보고 형식을 정하도록 고쳤습니다.

둘째, PDF 6건에서 셀의 80% 를 놓쳤습니다. 처음엔 pdfplumber 가 poppler 보다 못 읽는다고 봤습니다. 원인을 파 보니 한글 2024 가 내보낸 PDF 는 낱말 사이 공백을 NUL 문자로 쓰고, pdfplumber 는 그것을 그대로 넘기고 있었습니다. poppler 는 NUL 을 공백으로 읽어서 멀쩡해 보였을 뿐입니다. 처방은 도구 교체가 아니라 NUL 을 공백으로 바꾸는 한 줄이었습니다. 원인을 보지 않고 도구를 바꿨다면 다른 문서에서 다른 것을 잃었을 겁니다.

고친 뒤 같은 214문서를 다시 쟀습니다. HWP 는 셀·행·짝 모두 1.000, PDF 는 셀 0.498 에서 0.742. 움직인 문서는 결함이 지목한 그 11건뿐이고 나머지는 소수 셋째 자리까지 그대로였습니다. 측정 없이는 몰랐을 결함이고, 골든셋을 동결해 두었기에 고친 게 다른 것을 건드리지 않았다는 것도 확인됩니다.

처방 다섯 줄

  1. 원본 형식을 읽습니다. HWP 와 PDF 가 둘 다 있으면 HWP.
  2. 형식은 확장자가 아니라 첫 바이트로 정합니다. 8건이 거짓말을 했습니다.
  3. 표는 한 행을 한 줄로 직렬화합니다. 라벨과 숫자가 같은 줄에 있어야 모델이 짝을 맞춥니다.
  4. 텍스트 레이어를 의심합니다. 글자 수가 정상이어도 NUL 같은 것이 섞여 있을 수 있습니다.
  5. 골든셋을 동결하고 재측정합니다. 고쳤다는 말은 전후 숫자가 있을 때만 성립합니다.

한계

정리

한국 공공 문서의 검색 품질은 토크나이저 이전에 추출에서 갈립니다. 글자를 잃는 실패는 눈에 보이지만, 구조를 잃는 실패는 텍스트 양이 정상이라 로그에 남지 않습니다. 원본 형식을 읽고, 형식을 바이트로 판정하고, 표를 행 단위로 옮기고, 같은 골든셋으로 다시 재는 것. 이 코퍼스에서는 그 순서로 라벨↔숫자 짝이 10% 에서 100% 가 됐습니다.

데이터 피드는 이 추출 체인을 고객이 지정한 출처에 붙여 매달 정제된 데이터를 납품하는 서비스입니다.

실험 파일

스크립트·채점 결과·문서 목록을 그대로 올립니다. 나라장터 첨부 216파일(108MB)은 공개 데이터이지만 여기 올리지 않고, 어느 공고의 어떤 파일인지 목록(manifest.json, 공고번호·파일명·크기·해시)을 올립니다. fetch_g2b.py 로 같은 공고번호의 첨부를 다시 받을 수 있습니다(입찰공고정보서비스 API 키 필요).

이 절차를 고객 코퍼스에 적용하는 서비스: 데이터 피드 →

20분 통화 예약