페이지 목차
이 페이지에서 검색
멀티 에이전트
이 페이지에 묻기
멀티 에이전트 · 현재 차시와 목차 본문에서만 찾아 답합니다. 답을 지어내지 않고 해당하는 대목을 그대로 보여 주며, 여기에 없으면 없다고 알려 드립니다.
생성형 AI가 아니라 이 페이지 본문을 찾아 그대로 보여 주는 방식입니다. 외부로 나가는 요청이 없습니다.
MULTI · AGENT
멀티 에이전트
생성 수정
도구 실행·검색·기억·그래프 흐름·전문 에이전트의 협업을 구성하고 권한·평가·사람의 확인으로 통제합니다.
26개 문서 · 2일차 · 13 / 26
12차시 · 문서 로딩과 청킹
지도 위치 : 2일차 · README 4장의 ③ 지식 층 — 지식 층의 첫 칸, 문서를 검색할 수 있는 조각 으로 만드는 자리 | 이전: 11차시 프레임워크 기반 에이전트 구성 | 다음: 13차시 임베딩과 벡터 DB 키워드 : RAG, 청킹, 메타데이터
이 차시를 다 읽으면
- RAG를 유와 종차로 정의하고, 밖에서 찾는가 ↔ 안을 고치는가 축으로 파인튜닝과 가를 수 있다.
- RAG의 전체 흐름을 적재 와 질의 두 줄로 그리고, 12 · 13 · 14차시가 각각 어느 칸인지 짚을 수 있다.
- PDF 로딩에서 생기는 함정(표 · 머리글 · 스캔본)을 말하고, 추출 결과를 눈으로 확인하는 이유를 설명할 수 있다.
- 청크 크기와 겹침의 트레이드오프를 표로 설명하고, 규정 문서에 조항 단위 청킹 이 맞는 이유를 말할 수 있다.
- 청크에 붙일 메타데이터를 고르고, 그것이 출처 표기 와 권한 필터 에 어떻게 쓰이는지 말할 수 있다.
0. 이 차시의 판별 축
| # | 축 | 왼쪽 (0) | 오른쪽 (1) | 이 축으로 갈리는 것 | 큰 축 (README 5장) |
|---|---|---|---|---|---|
| 1 | ⭐ 밖에서 찾는가 ↔ 안을 고치는가 | RAG — 문서를 찾아 프롬프트에 넣음 | 파인튜닝 — 모델 가중치를 고침 | 사내 지식을 모델에게 주는 방법 | 파생 축 (부모 ⑧) |
| 2 | 하나 ↔ 여럿 | 문서 통째 | 청크 여러 개 | 무엇을 검색 · 주입 단위로 삼는가 | ⑤ 하나 ↔ 여럿 |
| 3 | 작은 청크 ↔ 큰 청크 | 정확히 맞지만 맥락이 부족 | 맥락은 많지만 잡음 · 비용 | 청크 크기 | 이 차시의 축 |
| 4 | 글자 수 기준 ↔ 구조 기준 | 500자마다 자름 | 조항 · 제목 단위로 자름 | 청크가 뜻의 단위와 맞는가 | 이 차시의 축 |
이 차시의 질문 — 오피스봇에게 "경조휴가 며칠이야?"라고 물으면 모델은 우리 회사 취업규칙을 모릅니다. 9 · 10차시의
get_leave_policy는 고정된 문구를 돌려주는 가짜였습니다. 이제 진짜 규정 PDF를 근거로 삼아야 합니다. 이 차시는 그 첫 단계로 문서를 읽어 들이고, 검색할 단위로 자르고, 각 조각에 꼬리표를 붙이는 일을 다룹니다. 1일차 내내 보던 축 ⑤(하나 ↔ 여럿)가 이번에는 문서 에 적용됩니다.
1. ⭐ RAG — 밖에서 찾아 넣는다
1.1 정의와 경계
RAG (Retrieval-Augmented Generation, 검색 증강 생성) — [LLM 응용 기법]인데, [ 질문과 관련된 문서를 찾아 프롬프트에 함께 넣어 근거로 답하게 한다 ]는 점에서 모델의 가중치를 고치는 파인튜닝과 구별된다.
파인튜닝 (Fine-tuning) — [모델 학습 방법]인데, [ 이미 학습된 모델을 우리 데이터로 추가 학습해 가중치 자체를 바꾼다 ]는 점에서 가중치는 그대로 두고 입력만 바꾸는 RAG · 프롬프트 설계와 구별된다.
| RAG (밖에서 찾는다) | 파인튜닝 (안을 고친다) | |
|---|---|---|
| 지식이 있는 곳 | 문서 저장소 (모델 바깥) | 모델 가중치 안 |
| 규정이 개정되면 | 문서만 다시 적재 (13차시) | 다시 학습 |
| 출처 표기 | ⭕ 어느 문서 몇 조인지 댈 수 있음 | ❌ 어디서 배웠는지 댈 수 없음 |
| 부서별 권한 | ⭕ 검색할 때 걸러냄 | ❌ 한 번 배우면 모두에게 |
| 잘하는 일 | 사실 · 최신 정보 · 사내 문서 | 말투 · 형식 · 특정 작업 방식 |
1.2 왜 필요한가
| 문제 | RAG 없이 | RAG로 |
|---|---|---|
| 환각 — 그럴듯하지만 근거 없는 답 | 모델이 일반적인 회사 규정을 지어냄 | 우리 규정 문장을 근거로 줌 |
| 최신성 | 모델은 학습 시점 이후를 모름 | 오늘 개정된 문서를 적재하면 반영 |
| 사내 문서 | 모델은 우리 취업규칙을 본 적이 없음 | 사내 문서를 직접 찾아 넣음 |
1.3 반례 — 이건 RAG가 아닙니다
| 대상 | 왜 아닌가 |
|---|---|
| 사내 검색 엔진 | 찾기만 하고 답을 생성하지 않음 |
| 시스템 프롬프트에 취업규칙 전문을 붙여 넣기 | 찾는 단계가 없음. 통째로 넣기 (1.4) |
| 규정 Q&A로 모델을 추가 학습 | 가중치를 고침. 파인튜닝 |
1.4 컨텍스트가 100만 토큰인데, 통째로 넣으면 안 되나? (축 ⑤)
최신 Claude 모델은 한 번에 100만 토큰까지 받습니다. 규정 몇 개쯤은 통째로 들어갑니다. 그래도 나누는 이유가 있습니다.
| 통째로 넣기 (하나) | 청크를 골라 넣기 (여럿) | |
|---|---|---|
| 비용 · 지연 | 질문마다 문서 전체 토큰 (10차시 3.3의 곱셈) | 관련 조각만 |
| 정확도 | 긴 글 속에서 필요한 문장이 묻힐 수 있음 | 관련 부분이 앞에 놓임 |
| 권한 | 부서 전용 문서를 섞으면 걸러낼 수 없음 | 청크마다 걸러냄 (5절) |
| 출처 | "이 긴 글 어딘가" | "제23조, p.12" |
💡 문서가 작고 질문이 적다면 통째로 넣는 쪽이 더 단순하고 충분할 수 있습니다. README 문장 1 — 가장 단순한 것부터, 충분하면 멈춘다. 오피스봇처럼 문서가 여럿이고, 개정되고, 부서별 권한이 있으면 RAG가 맞습니다.
2. RAG 전체 흐름 — 적재와 질의
[적재] 미리 한 번 · 문서가 바뀔 때마다 다시 ← 결과는 디스크에 영구 저장 (축 ①)
규정 PDF ─▶ ① 로딩 ─▶ ② 청킹 ─▶ ③ 임베딩 ─▶ ④ 저장(벡터 DB)
└──── 12차시 ────┘ └───── 13차시 ─────┘
[질의] 질문이 올 때마다
질문 ─▶ ⑤ 검색 ─▶ ⑥ 주입(프롬프트에 근거로) ─▶ ⑦ 생성(답 + 출처)
13 · 15차시 └────────── 14차시 ──────────┘
| 칸 | 하는 일 | 차시 |
|---|---|---|
| ① 로딩 | PDF · HTML · Word에서 텍스트와 위치 정보를 꺼냄 | 12 |
| ② 청킹 | 검색 단위로 자르고 메타데이터를 붙임 | 12 |
| ③ 임베딩 · ④ 저장 | 조각을 의미 벡터로 바꿔 벡터 DB에 넣음 | 13 |
| ⑤ 검색 | 질문과 가까운 조각 k개를 찾음 | 13, 15 |
| ⑥ 주입 · ⑦ 생성 | 조각을 근거로 넣고, 출처를 붙여 답하게 함 | 14 |
⚠️ 적재 단계의 실수는 질의 단계에서 고칠 수 없습니다. 표가 깨진 채로, 조항이 반쯤 잘린 채로 저장되면, 검색을 아무리 잘해도 깨진 조각이 나옵니다. 그래서 이 차시가 RAG 품질의 바닥을 정합니다.
앞으로 다시 만납니다 — ③ · ④는 13차시, ⑤의 개선은 15차시, ⑥ · ⑦은 14차시에서 회수합니다.
3. 문서 로딩 — 파일에서 글자 꺼내기
3.1 정의와 코드
문서 로딩 (Document Loading) — [RAG 적재 단계]인데, [ PDF · HTML · Word 같은 파일에서 글자와 위치 정보(페이지 등)를 꺼내 텍스트로 바꾼다 ]는 점에서 꺼낸 텍스트를 자르는 청킹과 구별된다.
from pypdf import PdfReader
reader = PdfReader("취업규칙_2026.pdf")
pages = [(i + 1, page.extract_text()) for i, page in enumerate(reader.pages)]
🔍 읽는 포인트 : 페이지마다 (페이지 번호, 텍스트)로 꺼냅니다. ⭐ 페이지 번호를 이 단계에서 버리면 나중에 "p.12"라고 출처를 댈 수 없습니다. LangChain의 문서 로더(document loaders)를 써도 원리는 같습니다 — 텍스트 + 메타데이터(파일명 · 페이지).
3.2 ⚠️ PDF의 함정
PDF는 "화면에 어떻게 보일지"를 담은 형식이지 "글의 순서와 구조"를 담은 형식이 아닙니다.
| 함정 | 추출 결과에서 보이는 모습 | 대응 |
|---|---|---|
| 표 | "구분 일수 결혼 5 사망 3" — 칸 경계가 사라져 한 줄로 뭉개짐 | 표 추출 도구를 따로 쓰거나 표를 "결혼: 5일" 문장으로 바꿔 둠 |
| 머리글 · 바닥글 | 매 페이지 "대외비 — 인사팀", "- 12 -"가 본문 사이에 끼어듦 | 반복되는 줄을 찾아 제거 |
| 2단 편집 | 왼쪽 단과 오른쪽 단의 문장이 섞임 | 다른 추출기 시도, 원본 형식(Word · HTML)이 있으면 그쪽을 |
| 스캔 PDF | 텍스트가 비어 있음 (글자가 아니라 그림) | 글자 인식(OCR) 필요 |
| 각주 · 개정 이력 표 | 본문 중간에 "(2025.3.1 개정)"이 흩어짐 | 개정일은 메타데이터로 옮김 (5절) |
⭐ 추출 결과는 사람이 직접 몇 페이지 열어 봅니다. 특히 표가 있는 페이지. 코드를 몰라도 "원본 PDF와 같은 내용인가"는 누구나 확인할 수 있습니다.
4. ⭐ 청킹 — 검색 단위로 자르기
4.1 정의
청킹 (Chunking) — [문서 전처리]인데, [ 문서를 검색 한 번에 꺼낼 단위로 잘라, 질문과 관련된 부분만 골라 넣을 수 있게 한다 ]는 점에서 문서를 통째로 다루는 방식과 구별된다. 잘린 조각 하나를 청크(chunk) 라고 부릅니다.
4.2 크기와 겹침 — 트레이드오프
| 작은 청크 (예: 200자) | 큰 청크 (예: 2,000자) | |
|---|---|---|
| 검색 정확도 | 질문과 딱 맞는 조각이 잡힘 | 관련 없는 내용이 섞여 순위가 흐려짐 |
| 맥락 | ⚠️ "다만 그 경우에는…" — 무엇의 경우인지 잘려 나감 | 앞뒤 맥락이 함께 들어 있음 |
| 주입 비용 | 적음 | 많음 |
| 출처 정밀도 | 정확한 위치 | "이 근처 어딘가" |
겹침 (Overlap) — 이웃한 청크가 경계 부분을 조금씩 함께 갖게 하는 것입니다. 경계에서 문장이 잘려 뜻이 사라지는 것을 줄입니다. 대신 같은 내용이 두 번 저장되어 저장량 · 중복 검색이 늘어납니다.
청크 1: [──────────────────────]
청크 2: [──────────────────────]
└ 겹침 ┘ (경계의 문장이 양쪽에 다 있음)
from langchain_text_splitters import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.split_text(page_text)
🔍 읽는 포인트 : chunk_size(크기)와 chunk_overlap(겹침) 두 숫자만 보면 됩니다. 기본은 글자 수 기준입니다. 이름의 "Recursive"는 문단 → 문장 → 글자 순으로, 큰 경계부터 시도해 크기를 맞춘다는 뜻입니다. 값은 예시이며, 정답은 평가로 정합니다(15차시).
4.3 구조 기반 청킹 — 조항 단위
규정 문서에는 이미 뜻의 단위 가 있습니다 — 제N조. 글자 수로 자르면 조항이 중간에서 잘리고, 한 청크에 두 조항이 섞입니다.
| ❌ 글자 수로 자른 청크 | ⭕ 조항으로 자른 청크 |
|---|---|
| "…3일 전까지 신청한다. 다만, 부득이한 경우에는 사후에 신청할 수 있다. 제24조(경조휴가) ① 직원은 다음" | "제23조(연차휴가 신청) 연차휴가는 사용 3일 전까지 신청한다. 다만, 부득이한 경우에는 사후에 신청할 수 있다." |
| 두 조항의 반쪽씩, 제목 없음 | 한 조항 전체, 제목이 앞에 |
import re
articles = re.split(r"(?m)^(?=제\d+조)", full_text) # 줄 맨 앞의 "제N조" 바로 앞에서 자름
🔍 읽는 포인트 : ^(줄 맨 앞)이 중요합니다. 본문 속의 "제12조에 따라" 같은 인용 에서 잘리지 않게 합니다.
| 방식 | 장점 | 약점 | 맞는 문서 |
|---|---|---|---|
| 글자 수 기준 | 어떤 문서에도 적용, 크기가 고름 | 뜻의 경계를 무시 | 구조가 없는 메일 · 회의록 |
| 구조 기준 (조항 · 제목) | 뜻의 단위 = 검색 단위, 출처가 정확 | 조항 길이가 들쭉날쭉 | ⭐ 규정 · 매뉴얼 · FAQ |
| 혼합 | 조항으로 자르고, 너무 긴 조항만 다시 글자로 | 구현이 조금 복잡 | 실무의 기본값 |
💡 혼합 방식에서 긴 조항을 다시 자를 때는, 각 조각 앞에 조항 제목을 다시 붙여 둡니다("제24조(경조휴가) — 이어서: …"). 잘린 조각만 꺼내도 무엇에 관한 글인지 알 수 있게 합니다.
5. 메타데이터 — 청크의 꼬리표
5.1 정의
메타데이터 (Metadata) — [데이터에 붙은 속성 값]인데, [ 본문과 따로 출처 · 위치 · 권한 같은 정보를 담아, 검색 후 걸러내기와 출처 표기에 쓴다 ]는 점에서 검색 대상이 되는 본문 텍스트와 구별된다. 축 ③으로 보면 본문도 메타데이터도 속성(값) 이지만, 본문은 "뜻으로 찾는 값", 메타데이터는 "조건으로 거르는 값"입니다.
5.2 오피스봇 청크의 꼬리표
{
"text": "제23조(연차휴가 신청) 연차휴가는 사용 3일 전까지 신청한다. ...",
"metadata": {
"source": "취업규칙_2026.pdf", "doc_title": "취업규칙",
"article": "제23조", "page": 12,
"revised_at": "2026-03-01", "access_dept": "ALL",
},
}
🔍 읽는 포인트 : text 하나와 metadata 여섯 칸입니다.
| 꼬리표 | 쓰는 곳 | 없으면 |
|---|---|---|
doc_title, article, page | ⭐ 출처 표기 — [취업규칙 제23조, p.12] (14차시) | 답의 근거를 댈 수 없음 |
revised_at | 최신 판만 검색, "2026.3.1 개정 기준" 안내 | 옛 규정으로 답함 |
access_dept | ⭐ 권한 필터 — 인사팀 전용 문서는 인사팀 직원 질문에만 (13차시) | 다른 부서 전용 내용이 답에 섞임 |
source | 문서 개정 시 그 파일의 청크만 지우고 다시 적재 (13차시) | 옛 청크와 새 청크가 섞임 |
⚠️ 조항이 두 페이지에 걸치면 page를 "12-13"처럼 범위로 둡니다. 그리고 access_dept는 적재할 때 사람이 정한 값 입니다. 모델이 추측하게 두지 않습니다 — 권한은 강제의 영역입니다(10차시 2절).
앞으로 다시 만납니다 — 13차시에서
access_dept로 검색 결과를 거르는 메타데이터 필터 를, 14차시에서doc_title·article·page로 출처를 붙이는 법을 회수합니다.
6. 오피스봇 적용 — 문서별 청킹 계획
| 문서 | 형식 | 청킹 방식 | 주의 | 접근 |
|---|---|---|---|---|
| 취업규칙 | PDF, 조항 구조 | 조항 단위 (혼합) | 경조휴가 일수 표 | 전 직원 |
| 복리후생 안내 | PDF, 표 많음 | 제목 단위 + 표를 문장으로 | 지원 금액 표 | 전 직원 |
| 보안 규정 | Word, 조항 구조 | 조항 단위 | 부칙 · 개정 이력 | 전 직원 |
| 인사 평가 지침 | 조항 단위 | — | ⚠️ 인사팀만 | |
| IT 장애 FAQ | HTML, 질문-답 쌍 | 질문-답 한 쌍 = 한 청크 | 사내 은어 (15차시) | 전 직원 |
⭐ 한 가지 방식으로 모든 문서를 자르지 않습니다. 문서의 구조가 청킹 방식을 정합니다.
7. 실습 — 맡기고 검수하기
지시
오피스봇 규정 문서 적재의 앞 단계(로딩·청킹)를 만들어줘. 임베딩·DB 저장은 아직 하지 말 것.
- 대상: docs/ 폴더의 취업규칙.pdf, 복리후생.pdf, 보안규정.docx (원본 수정 금지)
- 참고: pypdf, langchain-text-splitters 공식 문서. 설치 버전을 보고에 적을 것
- 로딩: 페이지 번호 보존, 반복되는 머리글·바닥글 제거, 표가 있는 페이지는 목록으로 보고
- 청킹: 조항(줄 맨 앞 "제N조") 단위, 1,000자 넘는 조항만 RecursiveCharacterTextSplitter로 다시 자르되 조각 앞에 조항 제목을 붙일 것
- 메타데이터: source, doc_title, article, page, revised_at, access_dept
(access_dept는 내가 준 표대로. 추측해서 채우지 말 것)
- 결과를 chunks.jsonl로 저장
추측하지 말고 실행 출력 원문을 붙여.
보고 형식: [문서 | 페이지 수 | 청크 수 | 평균 길이 | 최대 길이 | 표가 있던 페이지 | 메타데이터 빈칸 수] 표
+ 무작위 청크 5개 원문 + 제23조 청크 원문
받을 증거
- 위 형식의 표와 청크 원문 샘플
- 표가 있는 페이지의 추출 결과 원문 (뭉개졌는지 보기 위해)
chunks.jsonl에서 메타데이터 빈칸을 센 명령과 출력
내가 직접 확인할 것
| 볼 것 | 이걸로 아는 것 |
|---|---|
| 원본 PDF의 경조휴가 표와 추출 결과를 나란히 놓고 숫자가 같은가 | 표가 뭉개지지 않았는가 (3.2) |
| 무작위 청크 5개가 각각 한 조항 으로 시작하는가 | 조항 단위 청킹이 됐는가 (4.3) |
| 청크 안에 "대외비" · 페이지 번호 줄이 남아 있는가 | 머리글 · 바닥글 제거 (3.2) |
제23조 청크의 page를 원본 PDF에서 펼쳐 맞는가 | 출처 표기의 바탕이 맞는가 (5.2) |
인사 평가 지침 청크가 전부 access_dept=인사팀인가 | 권한 필터의 바탕이 맞는가 (5.2) |
| 최대 길이가 비정상적으로 큰 청크가 있는가 | 자르기 실패 (부칙 · 표가 통째로 남음) |
부록 A. 스스로 확인하기
| # | 질문 | 답이 있는 곳 |
|---|---|---|
| 1 | RAG와 파인튜닝을 유와 종차로 가르면? | 1.1 |
| 2 | RAG가 해결하는 문제 세 가지는? | 1.2 |
| 3 | 컨텍스트가 100만 토큰인데도 청크로 나누는 이유는? | 1.4 |
| 4 | 적재와 질의의 일곱 칸과 담당 차시는? | 2절 |
| 5 | PDF 로딩의 함정 중 표와 스캔본은 어떻게 보이나? | 3.2 |
| 6 | 청크 크기가 작을 때와 클 때의 장단점은? | 4.2 |
| 7 | 겹침은 무엇을 막고 무엇을 늘리나? | 4.2 |
| 8 | 규정 문서에 조항 단위 청킹이 맞는 이유는? | 4.3 |
| 9 | 메타데이터의 두 가지 핵심 용도는? | 5.2 |
부록 B. 자주 하는 실수
| 실수 | 바로잡기 |
|---|---|
| 로딩할 때 페이지 번호를 버림 | ❌ 출처 표기가 불가능해짐 |
| 추출 결과를 안 보고 바로 청킹 | ⚠️ 표 · 머리글 · 스캔본 문제는 눈으로 발견 |
| 모든 문서를 500자로 일괄 자름 | ⚠️ 문서 구조에 맞춰 (조항 · 질문-답 · 제목) |
| 겹침을 크게 할수록 좋다 | ❌ 중복 저장 · 중복 검색이 늘어남 |
access_dept를 모델에게 추측시킴 | ❌ 권한은 사람이 정한 값으로 |
| "컨텍스트가 크니 RAG는 필요 없다" | ⚠️ 비용 · 묻힘 · 권한 · 출처를 따져 결정 |
| 개정된 문서를 새로 적재하면서 옛 청크를 그대로 둠 | ❌ source 기준으로 지우고 다시 (13차시) |
부록 C. NotebookLM 복습 프롬프트
이 12차시 노트만 근거로 6문제를 내줘. 비전공자 기준으로.
"RAG ↔ 파인튜닝(밖에서 찾는가 ↔ 안을 고치는가)", "적재와 질의의 흐름", "청크 크기·겹침 트레이드오프와 조항 단위 청킹", "메타데이터의 두 용도"는 반드시 포함해줘.
답은 내가 답한 다음에 채점하고, 틀린 이유를 노트의 절 번호를 근거로 설명해줘.
LangTailor