📚 Agentic AI - 기업용 자율 에이전트 개발 6장 · 에이전트 ④ RAG 문서검색 Agentic AI란

왜 문서를 잘게 잘라야 하나 — 청킹

한 줄 요약

문서를 500자짜리 조각으로 자르고, 50자씩 겹치게 합니다. 조각 크기를 정하는 것은 검색 정확도와 근거의 양 사이에서 균형을 잡는 일이고, 사람이 판단하는 자리입니다.


1. 왜 자르나

이유가 셋 있습니다.

① 통째로 보내면 낭비다

우리 문서는 8쪽 5,526자입니다. "반품 기간"을 물었는데 로봇청소기 매뉴얼까지 다 보낼 이유가 없습니다.

② 관련 없는 내용이 섞이면 답이 흐려진다

LLM에게 A4 10장을 주고 "여기서 반품 기간 찾아 줘"라고 하면, 엉뚱한 문장을 근거로 삼기도 합니다. 필요한 조각만 주면 그럴 여지가 줄어듭니다.

③ 어디서 나왔는지 밝혀야 한다

조각 단위로 관리해야 "환불교환정책.pdf 1쪽" 이라고 출처를 붙일 수 있습니다.


2. 로드 — PDF 읽기

아래는 4절에서 만들 code/ch06_rag_agent.py 의 일부를 미리 보는 것입니다 — 지금 붙여 넣지 않아도 됩니다.

from langchain_community.document_loaders import PyPDFLoader

pages = PyPDFLoader(str(path)).load()

한 쪽이 하나의 Document 가 됩니다. 각 Document 에는 두 가지가 들어 있습니다.

무엇
page_content 그 쪽의 텍스트
metadata 어디서 왔는지 (source, page)

우리 문서를 읽으면 이렇습니다.

환불교환정책.pdf              2쪽   1694자
멤버십정책.pdf                1쪽    864자
직원핸드북.pdf                2쪽   1164자
제품매뉴얼_스마트워치.pdf       1쪽    753자
제품매뉴얼_로봇청소기.pdf       2쪽   1051자
                            ─────────────
                            총 8쪽  5,526자

출처를 정리해 둡니다

아래는 4절에서 만들 code/ch06_rag_agent.py 의 일부를 미리 보는 것입니다 — 지금 붙여 넣지 않아도 됩니다.

for d in pages:
    d.metadata["source"] = name        # 전체 경로 대신 파일명만

PyPDFLoadersource 에 전체 경로(/Users/.../data/docs/환불교환정책.pdf)를 넣습니다. 화면에 보여 줄 때 지저분하므로 파일명만 남깁니다.

나중에 출처를 표시할 때 여기가 쓰입니다. 미리 정리해 두는 것이 사람의 일입니다.


3. 청킹 — 자르기

아래는 4절에서 만들 code/ch06_rag_agent.py 의 일부를 미리 보는 것입니다 — 지금 붙여 넣지 않아도 됩니다.

from langchain_text_splitters import RecursiveCharacterTextSplitter

chunks = RecursiveCharacterTextSplitter(
    chunk_size=500, chunk_overlap=50).split_documents(docs)

8쪽이 15조각이 됩니다.

잘라 낸 조각 하나를 청크(chunk)라고 부릅니다. 자르는 일이 청킹(chunking) 이고요. 이 교재에서는 "조각"으로 쓰고, 코드의 chunk_size·chunk_overlap 이 그 조각의 크기와 겹침입니다. 혼자 검색할 때는 "청크"라는 말로 찾으면 됩니다.

인자 우리 값
chunk_size 조각 하나의 최대 글자 수 500
chunk_overlap 이웃 조각끼리 겹치는 글자 수 50

두 값이 각각 어디를 가리키는 길이인지 그림으로 보세요.

한 페이지의 본문 (예: 1,400자)01400조각 1 [0 – 500]조각 2 [450 – 950]조각 3 [900 – 1400]50자 겹침50자 겹침색칠한 구간은 앞뒤 조각에 모두 포함됩니다. 문장이 잘리더라도 앞뒤 맥락을 알 수 있게 합니다.
조각을 50자씩 겹쳐 자른다 — 경계에 걸친 문장이 반 토막 나지 않게

겹치는 50자는 두 조각 모두에 들어갑니다. 어느 쪽이 검색되든 그 부분을 볼 수 있게요.

500자 지점이 문장 한가운데라면?… 반품을 신청할 수 있습니다.\n\n제5조 (교환) …↓ 여기서 뚝 자르지 않고↑ 앞의 문단 경계(\n\n)까지 당겨서 자른다RecursiveCharacterTextSplitter는 문단 → 줄 → 문장 → 글자 순으로 끊을 자리를 찾습니다.
500자에서 뚝 자르지 않고 앞쪽 문단 경계에 맞춰 자른다

그래서 조각이 500자보다 짧을 수 있습니다. 6번에서 다시 다룹니다.


4. chunk_size — 조각 크기에 따른 장단점

작으면 좋을까요, 크면 좋을까요? 둘 다 문제가 있습니다.

[너무 작다 — 100자]
  ✓ 검색이 정확하다 (조각 하나에 한 가지 내용만)
  ✗ 근거가 부족하다 ("30일 이내"만 나오고 무엇의 30일인지 안 보임)

[적당하다 — 500자]
  ✓ 한 조항 정도가 통째로 들어간다
  ✓ 검색도 그럭저럭 정확하다

[너무 크다 — 3000자]
  ✓ 근거가 충분하다
  ✗ 검색이 부정확하다 (여러 주제가 섞여 무엇과 비슷한지 흐려짐)
  ✗ 토큰이 많이 든다
500 — 조항 하나가 통째로 들어가는 크기검색이 정확하다근거가 충분하다근거가 부족하다검색이 부정확하다100자500자3000자조각 크기 (chunk_size)"30일 이내"어떤 기한인지 알 수 없음"상품 불량·하자의 경우 수령일로부터 30일 이내에 반품을 신청할 수 있습니다"조항 하나가 통째로 들어간다반품 규정 … 교환 규정 …멤버십 등급 … 배송비 …직원 근태 … (여러 주제)주제가 섞여 검색이 어려움조각이 너무 작으면 맥락이 빠지고, 너무 크면 여러 주제가 섞입니다. 답변에 필요한 내용이 담길 정도로 크기를 정합니다.
조각이 작으면 근거가 모자라고, 크면 검색이 흐려진다

500은 어떻게 나온 숫자인가 — 정답이 있는 건 아닙니다. 다만 감을 잡는 기준은 있습니다.

"답 하나가 들어갈 만한 크기" 로 잡습니다. 규정 문서라면 조항 하나, 매뉴얼이라면 항목 하나, 논문이라면 문단 하나. 우리 정책 문서는 조항 하나가 300~500자쯤이라 500이 맞습니다.

문서 종류에 따라 달라집니다.

문서 권장
규정·정책 (조항 단위) 300~500
매뉴얼·설명서 500~800
논문·보고서 800~1500
대화 기록·로그 200~400

5. chunk_overlap — 왜 겹치나

문장이 조각 경계에서 잘리는 것을 막기 위해서입니다.

[겹치지 않으면]조각 1: "… 상품 불량·하자의 경우 상품 수령일로부터"조각 2: "30일 이내에 반품을 신청할 수 있습니다. …"↳ 무엇이 30일인지 알 수 없다[50자 겹치면]조각 1: "… 상품 불량·하자의 경우 상품 수령일로부터 30일 이내에 반품을"조각 2: "수령일로부터 30일 이내에 반품을 신청할 수 있습니다. …"↳ 양쪽 다 뜻이 통한다
겹치지 않으면 '무엇이 30일인지' 알 수 없는 조각이 생긴다

겹치는 부분이 안전망입니다. 어느 조각이 검색되든 뜻이 통합니다.

보통 chunk_size 의 10~20% 로 잡습니다. 500의 10%면 50이죠.

너무 크게 잡으면 같은 내용이 여러 조각에 중복 저장돼 낭비입니다.


6. Recursive 가 뜻하는 것

RecursiveCharacterTextSplitter — 이름이 긴데, 자르는 방식이 이름에 들어 있습니다.

무조건 500자에서 뚝 자르는 게 아닙니다. 순서대로 시도합니다.

① 문단 경계(\n\n)에서 자를 수 있나?  → 된다면 거기서
② 안 되면 줄바꿈(\n)에서
③ 안 되면 공백에서
④ 그래도 안 되면 글자 단위로

최대한 자연스러운 경계에서 자릅니다. 그래서 조각이 500자보다 짧을 수도 있습니다.

이게 왜 중요한가 — 문장 한가운데를 자르면 조각의 뜻이 망가지고, 임베딩도 엉뚱해집니다. 문단 경계에서 자르면 조각 하나가 하나의 완결된 이야기가 됩니다. 검색 정확도가 올라갑니다.


7. 잘린 결과 보기

우리 문서의 첫 조각입니다.

(주)승승장구  SEUNGSEUNG COMMERCE
CS-POL-2026-014
대외 공개 · 무단 배포 금지
- 1 -
고객지원본부
환불·교환·반품 운영 정책
Return, Exchange & Refund Policy — 승승장구몰 고객지원본부
 문서번호      CS-POL-2026-014
 제·개정일     2026-05-01
 관리부서      고객지원본부
 시행일        2026-05-15
 문서등급      대외 공개
 버전          v3.2
본 정책은 「전자상거래 등에서의 소비자보호에 관한 법률」에 근거하여
승승장구몰(이하 '회사')이 판매하는 상품의 환불·교환·반품 ...

머리말과 문서 정보가 한 조각을 차지했습니다. 실제 규정 내용은 다음 조각부터입니다.

PDF에서 뽑은 텍스트는 지저분합니다. 표가 줄줄이 풀리고, 쪽번호(- 1 -)가 섞이고, 줄바꿈이 이상합니다. 실무에서는 이런 것을 정리(전처리) 하는 데 시간을 꽤 씁니다. 이 과정에서는 그냥 두고 갑니다 — 어차피 검색은 됩니다. 다만 "PDF 텍스트는 원래 이렇다"는 것은 알아 두세요.


8. 정리 — AI가 한 것과 사람이 한 것

누가 무엇
라이브러리 PDF 읽기, 자르기
사람 어떤 문서를 넣을지
사람 chunk_size 를 몇으로 할지
사람 chunk_overlap 을 몇으로 할지
사람 출처를 파일명으로 정리

AI는 아직 등장하지도 않았습니다. 여기까지는 전부 평범한 텍스트 처리입니다.


스스로 해 보기

먼저 4절에서 실습 파일을 만든 뒤에 해 보세요. chunk_size 를 바꿔 조각 수가 어떻게 달라지는지 확인합니다.

직접 고쳐 보기 — 4절에서 만든 agentic_ai/code/ch06_rag_agent.py 를 열어 아래처럼 바꾸거나 덧붙인 뒤 다시 실행하세요.

for size in [200, 500, 1000, 2000]:
    ch = RecursiveCharacterTextSplitter(chunk_size=size, chunk_overlap=50).split_documents(docs)
    print(f"chunk_size={size:5d} → {len(ch):3d}조각")

그리고 각 설정으로 만든 인덱스에서 같은 질문을 던져 검색 결과가 어떻게 달라지는지 비교해 보세요.

인덱스를 다시 만들려면 data/policy_index 폴더를 지워야 합니다. 다음 절에서 이유를 설명합니다.


핵심 정리

  • 문서를 자르는 이유 셋 — 비용 · 정확도 · 출처 표시.
  • PyPDFLoader 로 읽으면 한 쪽이 하나의 Document 가 되고, metadata 에 출처가 들어갑니다.
  • chunk_size=500 — 조각 크기는 검색 정확도와 근거의 양 사이에서 균형을 잡는 값입니다.
  • 기준은 "답 하나가 들어갈 만한 크기" 입니다. 규정이면 조항 하나, 매뉴얼이면 항목 하나.
  • chunk_overlap=50 — 문장이 경계에서 잘려도 뜻이 남게 하는 안전망입니다. 보통 chunk_size 의 10~20%.
  • Recursive 는 문단 → 줄 → 공백 순으로 자연스러운 경계를 찾아 자른다는 뜻입니다.
  • 우리 문서는 8쪽 → 15조각이 됩니다.
  • 조각 하나를 청크(chunk), 자르는 일을 청킹(chunking) 이라고 부릅니다. 이 교재는 "조각" 으로 씁니다.
  • 여기까지는 AI가 등장하지 않습니다. 평범한 텍스트 처리입니다.
← 이전 절01. 표가 아니라 긴 글에 답이 있을 때다음 절 →03. 의미가 비슷하면 숫자도 비슷하다 임베딩