문서를 업로드하고 질문을 하면 불완전한 답변을 얻습니다.
출처를 확인해 보니 정보는 거기에 있습니다.
그렇다면 왜 RAG 애플리케이션은 그것을 놓쳤을까요?
때로는 문제가 문서가 어떻게 분할되었는지에서 시작됩니다.
AI 챌린지 2일차로, 청킹이 RAG 검색에 어떤 영향을 미치는지, 어떤 접근 방식이 도움이 되는지, 그리고 어디서 부족할 수 있는지 살펴보겠습니다.
답은 거기에 있지만 일부가 빠져 있습니다
한 회사 정책을 상상해 봅시다:
직원은 최대 5일의 사용하지 않은 연차를 이월할 수 있습니다. 이 옵션은 근무 1년을 완료한 후에만 이용 가능합니다.
누군가 다음과 같이 질문합니다:
“사용하지 않은 휴가를 이월할 수 있나요?”
만약 앱이 첫 번째 문장만 검색하여 답변한다면, 1년 근무 요건을 언급하지 않고 '예'라고 답할 수 있습니다.
모델은 관련 구절을 받았지만, 정책을 올바르게 설명하는 데 필요한 모든 것을 받지 못한 것입니다.
이것은 청킹 전략이 해결해야 할 RAG의 과제 중 하나입니다.
Diagram comparing five RAG chunking strategies: fixed-size with overlap, semantic grouping, recursive splitting, document structure-based splitting, and LLM-based chunking.
출처:Daily Dose of Data Science
작은 청크가 항상 더 나은 것은 아닙니다
많은 RAG 시스템에서 문서는 '청크(chunks)'라고 불리는 구절로 분할됩니다. 이 구절들이 시스템이 검색하는 조각들입니다.
작은 청크는 특정 사실을 찾기 쉽게 만들 수 있습니다. 하지만 그 사실이 제목, 정의 또는 예외로부터 분리되면 의미를 잃을 수 있습니다.
큰 청크는 더 많은 문맥을 함께 유지하지만, 관련 없는 정보를 검색 결과에 가져올 수도 있습니다.
임베딩(embedding) 문제도 있습니다. 시스템이 여러 주제를 포함하는 청크에 대해 하나의 벡터를 생성할 때, 그 벡터는 이 모든 것을 대표해야 합니다. 작은 세부 사항에 대한 질문은 매칭하기 어려울 수 있습니다.
유용한 질문은 다음과 같습니다:
시스템이 답변을 찾기 위해 얼마나 많은 텍스트가 필요하며, 모델이 그것을 정확하게 설명하는 데 얼마나 많은 텍스트가 필요한가?
이 양들이 항상 같을 필요는 없습니다.
문서에 제목(headings)이 있을 경우, 이를 사용하세요
정책, 계약서, 기술 매뉴얼 등은 정보가 어떻게 함께 속해 있는지를 알려주는 경우가 많습니다.
예를 들어, 이러한 제목들은 아래 텍스트를 해석하는 방식을 바꿉니다:
- 정규직 직원(Permanent Employees)
- 계약직 직원(Contract Employees)
- 수습 직원(Employees on Probation)
만약 처리 과정에서 이러한 제목들을 제거하면, 한 구절이 모든 사람에게 적용되는 것처럼 보일 수 있습니다.
구조 인식 청킹(Structure-aware chunking)은 제목과 섹션을 사용하여 그러한 맥락을 유지하는 데 도움을 줍니다.
청크는 다음과 같은 제목 경로를 가질 수 있습니다:
고용 정책 → 정규직 직원 → 연차 휴가 (Employment Policy → Permanent Employees → Annual Leave)
여기서 한 가지 세부 사항이 중요합니다. 이 경로를 메타데이터(metadata)로 저장하는 것이 자동으로 임베딩의 일부가 되는 것은 아닙니다.
임베딩되는 텍스트에 포함하면 검색 시스템이 해당 구절을 매칭하는 데 도움이 될 수 있습니다. 답변 모델로 전송되는 컨텍스트(context)에 포함하면 모델이 해당 구절을 해석하는 데 도움을 줄 수 있습니다.
둘 다 유용할 수 있지만, 파이프라인의 _서로 다른 지점_에서 발생합니다.
이는 문서 파싱(document parsing)이 왜 중요한지를 설명해 줍니다. 청커는 추출 과정에서 손실된 제목이나 표 간의 관계를 신뢰성 있게 보존할 수 없습니다.
구조가 복잡한 경우, 재귀적 분할(recursive splitting)로 시작하세요
모든 문서가 깨끗한 섹션으로 이루어진 것은 아닙니다.
형식이 일관되지 않은 텍스트의 경우, 재귀적 분할이 실용적인 출발점입니다. 이는 문단 구분과 같은 더 큰 경계를 시도하다가 더 작은 경계로 되돌아갑니다.
실행하기 비교적 간단하며, 모든 분할 지점을 결정하기 위해 다른 모델을 필요로 하지 않습니다.
한계는 간단합니다: 문단 구분이 설명이 완전함을 보장하지는 않습니다.
어떤 규칙은 한 단락에 있고 그 예외는 다음 단락에 있을 수 있습니다.
Chroma의 연구 보고서에 따르면 적절하게 구성된 재귀적 분할(recursive splitting)이 평가에서 경쟁력 있는 성능을 보였습니다. 이는 복잡성을 추가하기 전에 간단한 기준선(baseline)에게 공정한 비교 기회를 제공해야 한다는 유용한 상기시켜주는 내용입니다.
제목 없이 주제가 바뀔 때, 의미론적 청킹(semantic chunking)이 도움이 될 수 있습니다
일부 문서는 변화를 명확하게 표시하지 않고도 주제 사이를 이동합니다.
의미론적 청킹은 문장 유사성(sentence similarity)과 같은 신호를 사용하여 어떤 구절들이 함께 속하는지 결정하는 데 도움을 줍니다.
이는 관련 없는 논의가 동일한 청크에 포함되는 것을 방지할 수 있습니다.
하지만 함정이 있습니다. 비슷한 단어 사용이 항상 정보가 함께 속한다는 것을 의미하지는 않습니다.
두 제품은 거의 동일한 설명을 가질 수 있지만 제한 사항은 다를 수 있습니다. 정책 예외는 변경하는 규칙과 다르게 들릴 수 있습니다.
따라서 유사한 문장들을 함께 유지한다고 해서 그 청크가 답변에 필요한 모든 것을 포함한다는 것을 보장하지는 않습니다.
ACL 2025 논문 _Document Segmentation Matters for Retrieval-Augmented Generation_은 PIC라는 방법을 통해 이 문제를 탐구합니다. 이 방법은 문서 요약(document summaries)을 사용하여 문장이 어떻게 그룹화될지 안내합니다.
이 논문은 자체 벤치마크에서 개선된 점을 보고했습니다. 이는 우리가 접근 방식을 고려할 이유를 제공하지만, 모든 문서에서 승리할 것이라고 가정할 이유는 되지 못합니다.
오버랩(Overlap)이 도움이 되는 경우와 그렇지 않은 경우
청크 오버랩은 인접한 청크들 사이에 일부 텍스트를 반복하는 것입니다.
유용한 맥락이 경계 근처에 위치한다면, 오버랩은 양쪽 모두에서 그것을 유지하는 데 도움을 줄 수 있습니다.
하지만 몇 페이지 떨어진 예외 사항은 몇 문장을 반복한다고 해서 복구되지 않습니다.
너무 많은 오버랩은 또 다른 문제를 야기합니다. 바로 중복된 검색 결과입니다.
검색기가 다섯 개의 청크를 반환하고 그중 세 개가 거의 같은 텍스트를 포함한다면, 모델은 결과 수치에서 시사하는 것보다 적은 추가 증거를 받을 수 있습니다.
오버랩은 근처의 맥락을 보호하면서도 결과를 중복으로 채우지 않을 때 가장 유용합니다.
작은 구절을 검색한 다음 주변 섹션을 가져오기
RAG 설계가 더욱 흥미로워지는 지점입니다.
검색과 답변에 동일한 양의 텍스트를 사용할 필요는 없습니다.
부모-자식 검색(parent–child retrieval)을 사용하면 시스템이 다음 작업을 수행할 수 있습니다:
- 더 작은 청크를 검색하여 정확히 일치하는 내용을 찾습니다.
- 해당 매칭 청크가 속한 더 큰 섹션을 식별합니다.
- 관련 섹션을 답변 모델에 전송합니다.
예를 들어, 휴가 정책(leave-policy)의 경우, 작은 청크는 이월 수당(carry-forward allowance)을 찾는 데 도움을 줄 수 있습니다. 주변 섹션을 가져오면 자격 조건(eligibility condition)도 복구할 수 있습니다.
이는 두 가지 작업을 분리합니다: 올바른 위치를 찾고, 그것을 설명하기에 충분한 컨텍스트를 제공하는 것입니다.
여전히 제한이 필요합니다. 모든 매칭마다 전체 챕터를 반환하면 불필요한 텍스트가 추가되고 컨텍스트 예산(context budget)을 소모할 수 있습니다.
또한, 모델이 동일한 구절을 반복적으로 받지 않도록 중복되는 섹션은 결합되어야 합니다.
검색이 시작되기 전에 청크가 의미를 잃으면 어떻게 될까요?
다음 문장을 고려해 보세요:
It applies only to employees based in London.
여기서 “it”은 무엇을 가리킬까요?
이전 단락을 읽는 사람은 즉시 알 수 있을 것입니다. 하지만 해당 문장을 독립적으로 처리하는 임베딩 모델(embedding model)은 작업할 정보가 적을 수 있습니다.
이것이 후기 청킹(late chunking)이 해결하려는 문제입니다.
일반적인 접근 방식에서는 텍스트를 먼저 분할하고 각 청크를 개별적으로 임베딩합니다.
**후기 청킹(late chunking)**을 사용하면, 호환 가능한 임베딩 모델이 더 긴 구절을 먼저 처리합니다. 그런 다음 시스템은 이미 주변 텍스트를 본 토큰 표현(token representations)으로부터 개별적인 청크 벡터를 생성합니다.
이를 통해 각 청크의 벡터는 더 많은 컨텍스트를 갖게 됩니다.
하지만 답변 모델은 여전히 실제 지원 텍스트가 필요합니다. 더 나은 임베딩이 검색된 구절에 이전 단락을 자동으로 추가하지는 않습니다.
후기 청킹은 또한 호환 가능한 임베딩 파이프라인(embedding pipeline)을 요구합니다. 단순히 더 긴 문서를 표준 임베딩 API로 보내는 것만으로는 이를 구현할 수 없습니다.
청킹이 도움이 되는지 어떻게 알까요?
검색 결과가 답변을 포함하는지 확인하는 것이 시작점입니다.
하지만 저희 정책 예시의 경우, '사용하지 않은 연차 5일'을 찾는 것은 자격 조건이 누락되었다면 부분적인 성공에 불과합니다.
더 강력한 평가는 다음과 같은 질문들을 던집니다:
- 검색(retrieval)이 필요한 모든 증거를 찾았는가?
- 답변이 조건 및 예외 사항을 유지했는가?
- 모델에게 얼마나 관련 없는 텍스트가 도달했는가?
- 검색된 텍스트 중 얼마나 많은 부분이 반복되었는가?
- 인용된 구절들이 전체 답변을 뒷받침했는가?
Chroma의 보고서는 토큰 수준에서 검색을 살펴보고, 불필요하거나 반복되는 콘텐츠와 함께 관련성 있는 텍스트를 측정합니다.
이것은 답을 포함하는 큰 청크(chunk)가 여전히 비효율적인 검색 결과일 수 있는 이유를 설명하는 데 도움이 됩니다.
또한 우리는 컨텍스트 크기를 공정하게 비교해야 합니다. 짧은 청크 5개와 긴 청크 5개는 모델에게 '서로 다른 양의 정보'를 제공합니다.
실제 검색된 텍스트의 양을 기록하는 것이 비교를 더 유용하게 만듭니다.
청킹 전략 선택 시 중요한 것은 무엇인가?
문서가 어떤 종류의 정보를 포함하고 있는지, 그리고 사람들이 어떤 질문을 할 것인지부터 시작하세요.
명확한 제목은 구조 인지 분할(structure-aware splitting)을 유용하게 만듭니다. 약한 서식은 재귀적 분할(recursive splitting)을 합리적인 기준선으로 만듭니다. 표시되지 않은 주제 변경은 의미론적 청킹(semantic chunking)을 정당화할 수 있습니다.
겹침(Overlap)은 근처 경계에 도움이 됩니다. 부모-자식 검색(Parent–child retrieval)은 주변 설명들을 복구할 수 있습니다. 후기 청킹(Late chunking)은 임베딩을 생성할 때 컨텍스트 손실을 해결합니다.
이러한 접근 방식들은 서로 다른 문제를 해결하며, 결합될 수 있습니다.
목표는 충분한 의미를 보존하여 관련성 있는 검색 결과가 유용한 증거가 되도록 하는 것입니다.
RAG 앱이 조건을 놓치거나 불완전한 답변을 제공할 때, 검색된 구절들을 살펴보세요.
'모델이 전체 설명을 받았는지 아니면 단지 그렇게 보이는 문장만 받았는지?'