티스토리 뷰

RAG 검색 성능을 올리기 위한 작업으로 보통은 임베딩 모델이나 리랭커부터 본다. 대부분 LLM에 의존한 작업이기 때문에 바꿔가면서 비교해보는데, 이런 저런 테스트를 해보면 생각보다 큰 차이는 없다. 언어 특화 임베딩 모델을 쓰면 좋아질 수 있지만, 모든 언어에 대응하는 임베딩 모델을 사용할 수는 없다.
그래서 이번에는 그보다 앞단인 청킹 전략을 바꿔보기로 했다. 검색이 잘 안 되는 문서가 있어서 조각을 하나씩 열어봤더니 표가 반토막 나 있고, 헤더를 제대로 읽지 못하는 현상이 있어서 그걸 계기로 시작해봤다. 위쪽 조각엔 표의 머리행만 남아 있고, 아래쪽 조각엔 숫자만 줄줄이 있었다. 남겨진 청크만 봐서는 무슨 표에 담겨진 데이터인지 알 수 없다. 당연히 검색도 잘 안 된다.
청크를 일정 크기로 자르는 방식은 단순하고 빠르지만, 표의 존재나 맥락이 어긋나는지 모르니 특정 지점에서 차별 없이 끊는다. 가장 보편적으로 알려진 청킹 방식이지만, 이런 방식은 표 뿐만 아니라 청크의 맥락을 유지하기 어렵다.
맥락을 유지하면서 자르는 방법을 사용하려면, 초거대 LLM이 필요하다. 정확도가 오를지는 알수 없지만(아마 오르겠지?) 그만큼 비용과 시간이 더 든다.
그래서 다른 아이디어로 접근해봤다. 문서 종류에 따라 다르게 자르는 템플릿 청킹을 테스트해봤다. 지난 글에서 Jev에게 문서 카테고리를 맞히게 했던 것도 이 테스트에서 나온 얘기다. 이 글은 템플릿 청킹을 테스트한 과정과, 결국 어떤 형태로 도입하기로 했는지를 정리한 글이다. RAG 구축 시 고려해야 할 것들에서 짚었던 데이터 정제 쪽 이야기이기도 하다.
RAGFlow는 어떻게 자르나
참고한 건 RAGFlow다. 오픈소스 RAG 엔진인데, 자르는 방법을 여러 개 만들어두고 그중 하나를 골라 쓴다.
이걸 템플릿이라고 부른다.
General 특별한 구조가 없는 문서. 문단과 줄바꿈을 따라 자른다
Paper 초록, 서론, 방법, 결과 같은 논문 구조를 따라 자른다
Table 표의 한 행을 조각 하나로 만든다
Laws 조항을 따라 자른다
Presentation 장표 하나를 조각 하나로 만든다
One 자르지 않는다
목록을 더 보다가 눈에 띈 게 있었다. 문서 종류로 나눈 목록이 아니었다.
Table은 "행이 최소 단위"고 Presentation은 "장표가 최소 단위"다. One은 아예 "안 자른다"다. 무슨 문서인지가 아니라 어떻게 자를지로 나눈 목록이다. 그리고 이 템플릿은 사람이 고른다.
RAGFlow에서는 문서를 넣기 전에 지식베이스를 먼저 만든다. 그때 "여기 들어갈 문서는 발표자료입니다" 하고 템플릿을 정해둔다. 문서가 어떤 카테고리인지를 사람이 미리 알려주는 구조다.
그러니 RAGFlow는 문서를 보고 카테고리를 맞힐 필요가 없다. 처음엔 이걸 놓치고 자동으로 골라주는 기능이 어디 있나 한참 찾았는데, 없는 게 맞았다. 지식베이스를 만드는 사람은 자기가 무슨 문서를 넣는지 알고 있으니까.
그대로 도입하기 어려운 이유
문제는 카테고리를 미리 알려줄 사람이 없는 경우다.
폴더째 문서를 넣고 자동으로 색인하는 검색기가 그렇다. 폴더 안에는 발표자료, 엑셀, 공고문, 보고서가 섞여 있고, 뭐가 얼마나 들어 있는지는 넣는 사람도 모른다. 문서마다 "이건 무슨 카테고리입니다" 하고 알려줄 사람이 없다.
이런 환경에 템플릿 청킹을 도입하려면 원본에 없는 단계를 하나 만들어야 한다. 문서를 보고 카테고리를 알아서 판별하는 단계다.
RAGFlow에서는 사람이 정해주니 카테고리가 틀릴 일이 없다. 자동 판별은 그 자리를 프로그램이 채우는 것이고, 프로그램은 틀릴 수 있다. 이 차이가 뒤에서 계속 문제가 된다.
일단 카테고리부터 정했다. RAGFlow처럼 종류가 아니라 자르는 방법으로 나눴다.
걷어낼 것이 있는가 머리글, 꼬리글, 쪽번호 같은 반복 요소
최소 단위가 무엇인가 장표 하나, 표 하나, 조항 하나
위쪽 제목을 붙일 것인가 조각만 떼면 무슨 얘긴지 모를 때
어디서 끊을 것인가 길이, 빈 줄, 번호 패턴
네 답이 같으면 한 카테고리로 묶었다. 그렇게 여덟 개가 나왔다.
발표자료 표 형식 법령 공공문서
계층 목차 논문 통째로 기본
결정 1. 카테고리는 규칙으로 판별한다
카테고리를 판별하는 방법은 둘이었다. 규칙을 짜거나, 작은 언어모델에게 묻거나. 처음엔 언어모델 쪽이 나아 보였다. 규칙을 손으로 짜는 것보다 빠르고, 카테고리가 늘어도 설명만 고치면 되니까. 문서 앞부분을 읽고 무슨 문서인지 가늠하는 건 언어모델이 잘할 법한 일이기도 하다.
그래서 둘을 같은 조건으로 비교했다. 같은 문서에 같은 정보를 줬고, 정답은 따로 달았다. 규칙을 만들 때 본 문서와 채점할 문서도 나눴다. 내가 만든 규칙을 내가 본 문서로 채점하면 의미가 없어서다.
결과는 규칙이 더 잘 맞혔다. 둘을 섞어봐도 규칙만 쓴 것보다 못했다. 게다가 언어모델은 문서 하나마다 호출이 한 번씩 붙는다. 색인이 그만큼 느려진다. 더 못 맞히는데 느리기까지 하니 고민할 게 없었다. 규칙은 확실한 신호부터 보는 순서로 짰다.
1 확장자 가장 확실하다
2 파일 이름과 문서 속성 제목에 단서가 있는 경우가 많다
3 쪽 비율과 구성 요소 가로로 길면 발표자료, 표가 많으면 표 형식
4 본문 첫머리 패턴 조항 번호, 목차 같은 것
5 기본값 위에서 안 걸리면 여기로
다만 이걸 "언어모델이 규칙보다 못하다"로 읽으면 안 된다. 온디바이스에서 돌릴 수 있는 크기의 작은 모델이었다. 큰 모델을 쓸 수 있는 환경이면 결과가 달랐을 수 있다.
결정 2. 여덟 개로 나누는 방식은 버린다
판별이 되니 이제 검색에 붙일 차례였다. 여덟 카테고리를 각각 다르게 잘라서 색인을 새로 만들고, 평가셋을 돌렸다.
현재 데이터셋을 기준으로, 여덟 개의 템플릿으로 문서를 나누고 청킹하는 방식은 기존보다 나빴다.
소규모 데이터 셋으로 테스트 했을 때는 좋은 결과가 나왔는데, 데이터셋을 늘렸을 때는 근소하지만 나빠졌다. 테스트 결과로는 원인을 알 수 없어서, 질문을 정답 문서의 카테고리별로 갈라서 어디서 얻고 어디서 잃었는지 분류해 봤다.
이득 표 형식, 짧은 문서 확장자와 글자 수로 가른다 자르는 자리만 옮긴다
손해 나머지 여섯 개 본문 생김새로 추측한다 제목을 덧붙이거나 일부를 걷어낸다
이득을 낸 건 둘뿐이었다. 둘 다 세기만 하면 되는 신호로 가르고, 하는 일도 자르는 자리를 옮기는 것뿐이다.
나머지 여섯은 전부 손해였다. 이쪽은 본문을 보고 어떤 문서인지 추측하고, 조각 앞에 제목을 덧붙이거나 머리글 같은 걸 걷어내는 손질까지 했다.

제목 덧붙이기는 조각만 떼어놓으면 무슨 얘긴지 모르니 위쪽 제목을 붙여주자는 생각이었다. 그런데 한 절 아래 조각이 스무 개면 스무 개가 전부 같은 제목을 달게 된다. 내용은 상관없는데 제목이 비슷해서 뽑히는 조각이 생긴다.
걷어내기도 마찬가지였다. 머리글은 반복되는 군더더기라 지우는 게 맞아 보였는데, 지운 것 중에 그 문서를 찾는 단서가 있었다. 문서 위쪽에 적혀 있던 사업명이나 부서명 같은 것들이다.

여기서 앞에서 본 차이가 다시 나온다.
RAGFlow에서는 이런 손질이 문제가 안 된다. 카테고리를 사람이 정해주니 논문 템플릿은 논문에만, 법령 템플릿은 법령에만 걸린다. 손질이 엉뚱한 문서에 들어갈 일이 없다.자동 판별은 그 자리를 추측으로 채운다. 추측이 틀리면 법령용 손질이 보고서에 들어가고, 좋으라고 넣은 손질이 그대로 손해가 된다.
문서마다 다르게 자른다는 생각이 틀린 게 아니었다. 카테고리를 아는 사람이 있다는 전제가 자동 색인에는 없었던 것이다.
결정 3. 추측을 빼고 경계만 지킨다
카테고리를 알려줄 사람이 없다면, 카테고리를 몰라도 되는 방식으로 자르면 된다. 이득을 낸 쪽의 공통점이 그거였다. 추측하지 않고, 자르는 자리만 옮기는 것. 그것만 남겼다.
문서에는 이미 경계가 있다. 쪽, 절, 엑셀 시트 같은 것들이다. 이걸 그대로 쓰기로 했다.
쪽마다 글자가 적은 문서 쪽 단위로 자른다
그 외 문서 절 머리(□, 1., 제1장) 앞에서 자른다
짧은 쪽이나 절 자르지 않고 통째로 둔다
여덟 개에 비하면 거의 안 나눈 거나 마찬가지다. 그리고 이건 문서가 무슨 종류인지 맞히는 게 아니다. 어디서 끊을지만 고른다. 틀리게 골라도 잃는 게 작다.
이 방식은 기존보다 검색이 좋아졌다.
결정 4. 표 규칙은 더한다
남은 건 처음에 봤던 반토막 난 표였다.
엑셀은 길이로 자르면 머리행이 첫 조각에만 남는다. 그래서 행을 묶어서 자르고, 묶음마다 머리행을 다시 넣어주는 규칙을 더해봤다. RAGFlow의 Table 템플릿에서 가져온 생각이다. 이건 추측이 필요 없다. 엑셀인지 아닌지는 확장자만 보면 된다.
전체로 보면 더하기 전과 차이가 거의 없었다. 그런데 문서 형식별로 갈라보니 엑셀에서는 이득이 있었고, 다른 형식에서 손해 본 자리는 없었다. 전체가 비슷하다면 손해 없이 얻는 자리가 있는 쪽이 낫다. 그래서 표 규칙은 넣기로 했다.
아쉬운 점도 있다. PDF 안에 든 표는 문서 분석 단계에서 표로 잡히지 않아서 이 규칙이 닿지 않는다. 지금은 엑셀에만 듣는 규칙이다.
최종 결정
템플릿 청킹을 통째로 도입하지 않고, 추측이 필요 없는 부분만 가져왔다.
채택 문서가 가진 경계(쪽, 절, 시트)를 지켜 자른다
짧은 단위는 통째로 둔다
엑셀은 행을 묶고 머리행을 반복한다
기각 본문을 보고 카테고리를 추측해 다르게 자르는 방식
카테고리 판별에 언어모델을 쓰는 방식
모델을 더 붙인 게 아니라 자르는 규칙만 바꾼 것이라 비용이 거의 없다. 조각 수도 오히려 조금 줄었다. 이득이 어디서 왔는지도 갈라봤다. 절반 정도가 짧은 문서에서 나왔다.
이유는 단순하다. 기존 방식은 세 쪽짜리 문서도 기계적으로 잘라서 문장 중간에서 끊었다. 새 방식은 짧은 쪽이나 절을 그대로 두니 온전한 조각이 된다. 경계를 잘 골라서라기보다 짧은 걸 쪼개지 않아서 얻은 이득이 절반인 셈이다.
마치며
RAGFlow의 템플릿을 그대로 사용하면 좋았겠지만 결과가 좋지 않았다. 템플릿 자체는 잘 만든 것이었는데, 그 밑에 "카테고리는 사람이 알려준다"는 전제가 깔려 있었다. 분류를 얼마나 잘 맞히는지에 매달렸는데, 분류를 제일 잘 맞히던 구성이 검색에서는 기존보다 나빴다.
이번 테스트를 통해서 데이터가 크다고 범주를 세부적으로 나누는 것보다 확실히 알 수 있는 범주로 단순화하는게 좋을 수 있다는 것을 배울 수 있었다.
그리고 RAG를 설계할 때 색인에 들어간 조각을 몇 개 열어보는 게 먼저일지도 모르겠다.
내부동작을 알 수 없는 LLM을 바꿔 끼면서 테스트를 하는 것보다, 문서에 따른 청킹 전략이 제일 쉽고 간단한 개선일 수 있다.
'개발 > AI' 카테고리의 다른 글
| Jev로 문서 카테고리 분류해보기 (0) | 2026.10.01 |
|---|---|
| 문장 대신 답만 돌려주는 모델 : Jev 시작해보기 (1) | 2026.09.21 |
| sqlite FTS5로 검색 서버 메모리 절반 줄이기 (0) | 2026.08.14 |
| RAG 검색 개선기: 회고 (0) | 2026.08.11 |
| RAG 검색 개선기: 평가지표 선정 (0) | 2026.08.04 |
- Total
- Today
- Yesterday
- ChatGPT
- 질의처리
- rag
- docker
- Kotlin
- EKS
- 오블완
- Log
- serverless
- 인프런
- terraform
- 티스토리챌린지
- elasticsearch
- lambda
- GIT
- OpenAI
- Redis
- java
- 온디바이스 AI
- 검색 품질
- AWS EC2
- 후쿠오카
- CloudFront
- AWS
- 스프링부트
- cache
- Spring
- bm25
- springboot
- S3
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
