LangChain 챗봇6장 · 문서 기반 챗봇5 / 21← 이전목차다음 →MetaContents

6장. 문서 기반 챗봇

PDF 문서 읽기

복사 가게에 두꺼운 책을 맡기면 쪽마다 한 장씩 복사해 주고, 장마다 귀퉁이에 "원본 책 이름, 몇 쪽"을 적어 줍니다. 나중에 복사본 몇 장만 뽑아 들고 다녀도 어디서 왔는지 압니다. PyPDFLoader 가 이 복사 가게입니다. PDF 를 쪽마다 Document 하나로 만들고, 귀퉁이 메모인 metadata 에 파일 이름과 쪽 번호를 적어 줍니다.

PDF 파일, 쪽마다 Document 하나, page_content 와 metadata 의 세 걸음을 이은 흐름도

첫 상자의 PDF 한 개가 둘째 상자에서 쪽 수만큼의 Document 로 나뉘고, 셋째 상자처럼 Document 하나는 글(page_content)과 귀퉁이 메모(metadata) 두 칸을 가집니다. 여기서 복사본 한 장이 Document, 복사본에 적힌 글이 page_content, 귀퉁이의 "책 이름, 몇 쪽"이 metadata 입니다. 비유와 다른 점은, 복사기는 쪽의 모양을 그대로 옮기지만 로더는 글자만 꺼낸다는 것입니다. 표, 그림, 글자 크기 같은 모양은 사라지고, 줄바꿈과 빈칸이 원래와 다르게 들어오기도 합니다.

설치와 쓰는 패키지

이 장에서 처음 쓰는 패키지는 환경설정의 pip install -r requirements.txt 로 이미 설치했습니다. 이 장의 패키지만 따로 설치하려면 아래 명령을 씁니다. 이 블록은 교안을 만들며 실행하지 않았습니다.

PowerShell
# pip = Package Installer for Python(파이썬 패키지 설치기). -m = module(모듈로 실행)
python -m pip install langchain-community pypdf langchain-text-splitters faiss-cpu langchain-classic
# show = 설치된 패키지의 판(version)과 정보를 보여 준다
python -m pip show pypdf faiss-cpu
명령과 옵션원래 말뜻
pipPackage Installer for Python파이썬 패키지 설치기
-mmodule지금 고른 파이썬(가상 환경)의 pip 를 모듈로 실행한다
install설치하다패키지를 받아 넣는다. 이름을 빈칸으로 이어 여럿을 한 번에
show보여 주다설치된 판과 정보를 보여 준다
패키지하는 일이 교안의 판
langchain-community여러 회사·도구와 잇는 부품 모음. PyPDFLoader, FAISS 가 여기 있다0.4.2
pypdfPDF 의 글자를 꺼내는 라이브러리. PyPDFLoader 가 안에서 쓴다6.21.4
langchain-text-splitters글을 조각으로 나누는 분할기 모음1.1.3
faiss-cpuFAISS 를 CPU(Central Processing Unit, 중앙 처리 장치)로 돌리는 판1.15.1
langchain-classiclangchain 1.x 본체에서 빠진 옛 체인(RetrievalQA)을 담은 패키지1.0.8

짧은 예시

여행 약관 PDF 를 읽어 쪽 수와 첫 쪽의 정보를 봅니다. metadata 에는 열쇠가 여럿 있으므로 쓸 것만 골라 찍습니다.

Python
from langchain_community.document_loaders import PyPDFLoader

# load(): PDF 를 읽어 쪽마다 Document 하나씩, 목록으로 돌려준다
pages = PyPDFLoader("travel_terms.pdf").load()
print("쪽 수:", len(pages), "/ 자료형:", type(pages[0]).__name__)

first = pages[0]
# page 는 0 부터 센 쪽 번호, page_label 은 PDF 에 적힌 쪽 이름, total_pages 는 전체 쪽 수
for key in ["source", "page", "page_label", "total_pages"]:
    print(f"{key:12}", first.metadata[key])
print("글자 수:", len(first.page_content))
print("첫 두 줄:", first.page_content.split("\n")[:2])
출력
쪽 수: 4 / 자료형: Document
source       travel_terms.pdf
page         0
page_label   1
total_pages  4
글자 수: 476
첫 두 줄: ['구름다리여행 여행 약관 (연습용 가상 문서)', '제1장 총칙과 예약']

PDF 4쪽이 Document 4개가 되었습니다. 첫 쪽의 page 는 0 이고 page_label 은 "1"입니다. 사람에게 보여 줄 때는 page + 1 이나 page_label 을 씁니다. 첫 줄은 모든 쪽 맨 위에 찍힌 머리글(header, 쪽마다 위에 되풀이되는 글)이고, 둘째 줄이 장 제목입니다. 이 머리글이 나중에 검색을 흐리는 범인이 됩니다. 실습 1에서 쪽마다 확인합니다.

코드가 의미하는 것

이 코드는 복사 가게에 책을 맡기고 첫 장을 받아 귀퉁이 메모를 읽어 보는 일입니다. 쪽마다 복사하고(load), 첫 장의 메모와 글을 확인하는 두 동작입니다.

코드하는 일챗봇에서 뜻하는 것
PyPDFLoader("travel_terms.pdf").load()PDF 를 읽어 쪽마다 Document 하나씩 목록으로 돌려준다챗봇이 읽을 수 있는 꼴로 문서를 바꾸는 첫걸음입니다. 이 목록이 색인 단계의 재료가 됩니다
first.metadata[key]파일 이름, 쪽 번호, 쪽 이름, 전체 쪽 수를 꺼낸다답 옆에 "약관 몇 쪽"이라는 근거를 붙일 때 쓰는 정보입니다. 조각으로 나눠도 이 메모가 따라갑니다
len(first.page_content)쪽의 글자 수를 센다이 쪽을 통째로 검색 조각으로 쓸지, 더 잘게 나눌지 판단하는 숫자입니다
page_content.split("\n")[:2]쪽의 맨 위 두 줄을 본다로더가 꺼낸 글이 실제로 어떤 모양인지 눈으로 확인합니다. 머리글 같은 군더더기는 여기서 처음 보입니다

결과에서 읽을 것은 두 가지입니다.

  • 쪽 번호는 0 부터, 쪽 이름은 1 부터입니다. page 는 0, page_label 은 1 입니다. 손님에게 근거를 보여 줄 때 0쪽이라고 적지 않도록 어느 열쇠를 쓸지 정해 둡니다.
  • 한 쪽이 476자입니다. 쪽 하나에 여러 조항이 들어 있어서, 이대로 검색하면 질문과 관계없는 조항까지 함께 딸려 옵니다. 다음 절의 청킹이 필요한 까닭입니다.

주의할 점

  • langchain-community 0.4 판을 불러오면, 파일로 실행할 때 "이 패키지는 더 이상 적극적으로 관리하지 않는다(sunset, 해가 지다)"는 DeprecationWarning 이 뜹니다. 지금 판에서는 그대로 돌아가고, 경고는 따로 된 통합 패키지로 옮겨 가라고 안내합니다. 이 장의 실습은 경고를 읽었다고 보고 warnings.filterwarnings 로 숨깁니다.
  • 스캔한 그림으로 된 PDF 는 글자가 없어서 page_content 가 빈 글이 됩니다. 이때는 글자를 읽어 내는 OCR(Optical Character Recognition, 광학 문자 인식)이 따로 필요합니다. 이 장의 PDF 는 글자로 만든 것입니다.
  • 쪽마다 하나인 Document 는 검색 조각으로 쓰기에 깁니다. 다음 절에서 나눕니다.
← 이전 절6장 · RAG 파이프라인의 두 단계다음 절 →6장 · 텍스트 나누기(청킹)