청킹을 공부하며 알게 된 것: 형식마다 다르게 잘라야 해요
RAG 를 만들면서 「파일을 어떻게 자를까」를 공부했어요. 글자 수로 자르면 될 것 같지만, 형식마다 잘리는 자리가 달랐어요. 공부하면서 알게 된 것을 적어 둬요.
By heysep6 min read청킹 RAG
조각은 혼자 읽혀야 해요#
RAG 는 문서를 먼저 작은 조각으로 나눠 색인해 두고, 질문이 오면 관련 조각 몇 개를 찾아 모델에게 건네요. 모델은 그 조각만 보고 답해요. 그래서 조각 하나만 봐도 뜻이 통해야 해요. 표의 한 행이 열 이름 없이 115% 하나만 남으면, 사람도 모델도 무엇의 비율인지 몰라요.
글자 수로 일정하게 자르는 방식은 줄글에는 그럭저럭 맞아요. 하지만 표·JSON·로그처럼 구조가 뜻을 들고 있는 형식에서는 구조를 자르는 순간 뜻이 같이 잘려요. 이걸 눈으로 확인하고 싶어서 형식마다 일부러 어려운 반례 파일을 만들었어요. 값은 전부 지어낸 것이에요.
형식마다 이렇게 깨졌어요#
반례를 넣어 돌려 보니 이런 것들이 나왔어요. 오른쪽 열은 그 뒤에 잡은 방향이에요.
| 형식 | 그대로 자르면 | 잡은 방향 |
|---|---|---|
| CSV | 셀 안에 줄바꿈이 있으면 그 줄이 열 이름 없이 떠다녀요 | 줄마다 열 이름이 붙게 만들고, 행 하나가 한 조각 안에 들게 해요 |
| 엑셀 · 문서 표 | 병합된 제목 칸을 머리 행으로 읽어서 모든 열 이름이 같은 제목이 돼요 | 제목 행과 머리 행을 구분해요 |
| 중첩 JSON | 겉에 있는 키만 남고 안쪽 값은 대부분 조각 글에서 사라져요 | 안쪽 값마다 어디에 있던 값인지 함께 적어서 값이 하나도 빠지지 않게 해요 |
| 부모·자식이 있는 표 | 자식 행이 가리키는 부모가 다른 조각에 있어서, 조각 하나로는 관계를 따라갈 수 없어요 | 부모 설명을 짧게 덧붙여요. 덧붙인 글은 원문이 아니라서 인용 근거에서는 빼요 |
| 코드 · 로그 | 함수 한가운데나 여러 줄짜리 오류 기록 한가운데서 잘려요 | 함수 하나, 기록 한 건이 한 조각에 들어오게 해요 |
CSV 의 셀 안 줄바꿈이 제일 의외였어요. 파서는 따옴표 안의 줄바꿈을 한 셀로 읽는데, 그 셀을 글로 풀어 쓸 때 줄이 나뉘면 두 번째 줄부터는 어느 열의 값인지 알 수 없어요. 중첩 JSON 은 더 심했어요. 주문 내역처럼 고객·주소·상품 옵션이 겹겹이 들어 있는 파일에서, 겉 키 한두 개만 조각에 남고 나머지 값은 거의 없었어요. 검색은 조각 글을 대상으로 하니까, 글에 없는 값은 아무리 잘 물어도 찾을 수 없어요.
원본 표 (지어낸 값)
| 이름 | 분류 | 메모 |
|---|---|---|
| 라온 | 공지 | 첫째 줄 둘째 줄은 이어진 글 |
| 미르 | 행사 | 간단 메모 |
| 새봄 | 안내 | 접수 마감 |
① 글자 수로 자르면
길이만 보고 일정하게 잘라요. 셀 한가운데서 끊기고, 조각 대부분에 열 이름이 없어요.
- 이름,분류,메모라온,공지,행이 중간에서 끊겨요
- "첫째 줄둘째 줄은 이어진열 이름 없음 · 셀이 갈라져요
- 글"미르,행사,간단 메모열 이름 없음
- 새봄,안내,접수 마감열 이름 없음
② 구조 보고 자르면
행을 가운데서 자르지 않고, 조각마다 열 이름을 같이 둬요.
- 이름라온분류공지메모첫째 줄 둘째 줄은 이어진 글
- 이름미르분류행사메모간단 메모
- 이름새봄분류안내메모접수 마감
작은 예시로 보면#
장난감 데이터로 두 가지를 그려 볼게요. 먼저 CSV 예요. 둘째 행의 메모 칸에 줄바꿈이 들어 있어요.
이름,분류,메모
라온,공지,"첫째 줄
둘째 줄은 이어진 글"
미르,행사,간단 메모이걸 줄 단위로 읽어 글로 바꾸면 메모: 첫째 줄 다음에 열 이름 없는 둘째 줄은 이어진 글 이 따로 떠요. 조각이 그 줄에서 끊기면 두 번째 조각은 무슨 값인지 모르는 문장으로 시작해요. 셀 하나는 끝까지 한 덩어리로 읽어야 해요.
다음은 중첩 JSON 이에요.
{
"주문": "A-1",
"고객": { "이름": "고객1", "주소": { "우편": "01000" } },
"상품": [ { "옵션": { "색": "파랑" } } ]
}겉 키(주문)만 읽으면 파랑 이나 01000 은 글에 없어요. 「파란색 옵션 주문」을 물어도 이 조각은 후보에 오를 수 없어요. 안쪽 값 앞에 고객 > 주소 > 우편 같은 위치를 붙여 풀어 쓰면 값이 모두 글에 남아요. 위치가 같이 있으니 「어느 주소의 우편 번호」인지도 구분돼요.
구조는 두 군데서 사라져요#
내가 만든 RAG 는 파서와 조각내기가 나뉘어 있어요. 파서는 파일을 읽어 「레코드」로 바꾸고, 조각내기는 그 글이 줄글인지 코드인지 표인지 보고 자르는 전략을 골라요. 반례를 두 단계로 나눠 보니 구조가 사라지는 자리도 둘이었어요. 병합 제목을 머리 행으로 읽는 건 파서의 문제이고, 함수나 로그 한 건을 가운데서 자르는 건 조각내기의 문제예요. 어느 단계에서 틀렸는지 알아야 고칠 곳이 보여요.
하나 조심한 게 있어요. 조각이 파서가 읽은 원문이 아니라 덧붙인 글이면, 그 글은 검색에는 쓰되 인용 근거로는 못 쓰게 했어요. 부모 설명을 덧붙인 경우가 그래요. 원문에 없는 문장을 「출처」로 내보내면 안 되니까요. 이 이야기는 다음 글에서 이어져요.
눈으로 보는 도구를 만든 이유#
질문과 답으로 품질을 재는 평가도 따로 있었어요. 거기서 표 문항은 모두 통과했어요. 그런데 돌아보니 다른 형식의 표 자료에는 병합 제목 같은 반례가 아예 없었어요. 평가가 틀린 게 아니라, 없는 사례는 점수에도 안 나타난다는 걸 알게 됐어요.
그래서 점수 대신 조각 자체를 펼쳐 보는 작은 도구를 만들었어요. 파일을 넣으면 조각이 어떻게 나뉘었는지 화면에 보여 주고, 몇 가지를 자동으로 검사해서 통과 · 경고 · 실패 · 거절로 표시해요. 검사는 이런 질문들이에요.
- 원본에 있던 값이 어느 조각 글에든 남아 있나요?
- 표의 행이 중간에서 잘리지 않았나요? 조각마다 머리 행이 있나요?
- 줄마다 열 이름이 붙어 있나요?
- 부모를 가리키는 참조가 같은 조각 안에서 풀리나요?
- 함수나 로그 한 건이 경계에 걸려 잘리지 않았나요?
고치기 전과 후를 같은 검사로 비교했어요. 통과한 샘플은 두 배 넘게 늘었고, 실패는 거의 사라졌고, 나빠진 샘플은 하나도 없었어요. 다만 이 샘플의 상당수가 내가 어려운 반례로 직접 만든 것이라서, 「크게 좋아졌다」는 말은 그 시험지 안에서만 맞는 말이에요.
아직 못 한 것, 확인 못 한 것#
- 조각 글에서 값이 사라지면 검색이 못 찾는다는 건 구조상 분명해요. 하지만 실제 임베딩 검색에서 얼마나 틀리는지는 재지 못했어요. 이 부분은 확인 못 했어요.
- 실제 사용자 파일로는 해 보지 않았어요. 지어낸 샘플뿐이에요.
- 행이 아주 많은 표, 좌표가 들어 있는 지리 데이터에서 좌표로 검색하는 것, 회로 도면 같은 전문 파일은 아직 안 돼요. 일부는 경고로 남겨 뒀고 일부는 받지 않고 거절해요.
그래도 하나는 분명히 배웠어요. 형식을 아는 것과 그 형식이 잘리는 자리를 아는 것은 다른 일이에요. 반례 파일을 만들어 눈으로 보지 않았다면, 점수는 계속 좋게 나왔을 거예요.