청크와 chunk_id
문서를 검색 가능한 조각으로 자른 것.
왜 자르나
RFP 한 건이 수백 페이지다. 통째로 벡터 하나에 넣으면 뜻이 뭉개지고, LLM에 통째로 넘길 수도 없다. 그래서 의미 단위로 자른다.
우리는 임베딩 모델 제한에 맞춰 512토큰 기준으로 자른다.
chunk_id — 위치 기반이라는 함정
doc_id::순번 예) 봉화군_재난통합관리시스템 고도화::0013
순번이 위치다. 그래서 다시 인덱싱하면 같은 id에 다른 본문이 들어갈 수 있다.
🔴 실제로 겪은 사고
골든셋 정답을 chunk_id로 저장해뒀는데, 다른 인덱스로 실험을 돌렸다.
| 인덱스 | id 매칭 | 본문 일치 |
|---|---|---|
| 구버전 | 56/56 (100%) | 0/56 (0%) |
| 정본 | 56/56 (100%) | 56/56 (100%) |
id는 100% 맞아서 에러 없이 돌아간다. 본문만 전부 다르다. 아무 경고 없이 완전히 틀린 결과가 나온다. 가장 위험한 실패 모드.
대응: 실험할 때 인덱스 경로를 항상 명시하고, 정답 청크의 본문까지 대조한다.
표 청크
우리 코퍼스는 63.3%가 표 청크다. RFP의 핵심 정보(배점표·요구사항·장비 규격)가 거의 다 표 안에 있기 때문.
표 청크는 Dense 검색에 불리하다 — 셀 수십 개가 벡터 하나로 뭉개져 핵심 토큰의 신호가 희석된다. BM25가 이걸 보완할 후보였던 이유.