AIRAGLLMunstructuredVector DBDocument AI
RAG(검색 증강 생성) 설명 및 unstructured로 비정형 문서 처리하기
RAG 아키텍처의 개념과 구현 방법, 그리고 PDF·Word·HTML 등 비정형 문서를 처리하는 unstructured 라이브러리 활용법을 설명합니다.
VWV2026-04-035분 읽기
RAG란?
Retrieval-Augmented Generation(RAG) 은 LLM의 응답 생성 시 외부 지식 베이스에서 관련 문서를 검색(Retrieve)하여 컨텍스트로 제공하는 기법입니다.
질문 → [검색 엔진] → 관련 문서 → [LLM] → 정확한 답변
왜 RAG가 필요한가?
| 문제 | RAG 해결 방법 |
|---|---|
| LLM의 지식 컷오프 | 최신 문서를 실시간 검색 |
| 환각(Hallucination) | 실제 문서 기반으로 답변 생성 |
| 사내 문서 질의 불가 | 내부 데이터를 벡터 DB에 인덱싱 |
| 긴 컨텍스트 비용 | 필요한 청크만 선택적으로 제공 |
RAG 파이프라인 구조
[문서 수집]
↓
[문서 파싱 — unstructured]
↓
[청크 분할]
↓
[임베딩 생성]
↓
[벡터 DB 저장]
↓ ← 질문 임베딩
[유사 청크 검색]
↓
[LLM 프롬프트 조합]
↓
[최종 답변]
unstructured 라이브러리
unstructured는 PDF, Word, PPT, HTML, 이미지 등 다양한 비정형 문서를 일관된 방식으로 파싱하는 오픈소스 라이브러리입니다.
설치
pip install unstructured[all-docs]
# PDF 처리 시 추가
pip install unstructured-inference pdfminer.six
지원 파일 형식
| 형식 | 지원 여부 |
|---|---|
| ✅ (텍스트/OCR) | |
| DOCX / DOC | ✅ |
| PPTX | ✅ |
| HTML | ✅ |
| Markdown | ✅ |
| CSV / XLSX | ✅ |
| 이미지 (PNG/JPG) | ✅ (OCR) |
| 이메일 (EML) | ✅ |
기본 사용법
from unstructured.partition.auto import partition
# 파일 형식 자동 감지
elements = partition("report.pdf")
for el in elements:
print(type(el).__name__, "|", str(el)[:80])
# Title | 2026 보안 위협 동향 보고서
# NarrativeText | 올해 랜섬웨어 공격은 전년 대비 34% 증가하였으며...
# Table | | 공격 유형 | 증가율 | ...
요소 타입 분류
| 타입 | 설명 |
|---|---|
Title |
제목 / 소제목 |
NarrativeText |
본문 단락 |
Table |
표 데이터 |
ListItem |
목록 항목 |
Image |
이미지 (캡션 포함) |
CodeSnippet |
코드 블록 |
전체 RAG 파이프라인 구현
from unstructured.partition.auto import partition
from unstructured.chunking.title import chunk_by_title
from sentence_transformers import SentenceTransformer
import chromadb
# 1. 문서 파싱
elements = partition("security_report.pdf")
# 2. 청크 분할 (제목 기준으로 의미 단위 분할)
chunks = chunk_by_title(elements, max_characters=500)
# 3. 임베딩 모델 로드
embedder = SentenceTransformer("BAAI/bge-m3")
# 4. 벡터 DB 초기화
client = chromadb.Client()
collection = client.create_collection("security_docs")
# 5. 임베딩 생성 및 저장
texts = [str(chunk) for chunk in chunks]
embeddings = embedder.encode(texts, normalize_embeddings=True).tolist()
collection.add(
ids=[f"chunk_{i}" for i in range(len(texts))],
embeddings=embeddings,
documents=texts,
)
# 6. 질의 응답
def answer(question: str, top_k: int = 3) -> str:
q_emb = embedder.encode([question], normalize_embeddings=True).tolist()
results = collection.query(query_embeddings=q_emb, n_results=top_k)
context = "\n\n".join(results["documents"][0])
# OpenAI API 호출 예시
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "아래 문서를 참고하여 질문에 답변하세요.\n\n" + context},
{"role": "user", "content": question},
],
)
return response.choices[0].message.content
print(answer("2026년 주요 랜섬웨어 그룹은?"))
청킹 전략 비교
| 전략 | 장점 | 단점 | 적합 상황 |
|---|---|---|---|
| 고정 크기 청킹 | 구현 단순 | 문맥 단절 가능 | 균일한 텍스트 |
| 제목 기반 청킹 | 의미 단위 유지 | 제목이 없으면 비효율 | 구조화된 문서 |
| 재귀적 청킹 | 균형적 분할 | 설정 복잡 | 혼합 문서 |
| 의미 기반 청킹 | 최고 품질 | 느림, 비용 높음 | 고품질 RAG |
고급 RAG 패턴
HyDE (Hypothetical Document Embedding)
# 질문으로 가상 답변을 먼저 생성한 뒤, 그 답변을 기반으로 검색
hypothetical_answer = llm.generate(f"다음 질문에 대한 상세 답변을 작성하세요: {question}")
search_embedding = embedder.encode([hypothetical_answer])
# 이 임베딩으로 실제 문서 검색 → 더 정확한 관련 문서 찾기
Reranking
from sentence_transformers import CrossEncoder
reranker = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2")
# 상위 10개 검색 후 리랭커로 상위 3개 재선정
candidates = collection.query(query_embeddings=q_emb, n_results=10)
pairs = [(question, doc) for doc in candidates["documents"][0]]
scores = reranker.predict(pairs)
top_3 = sorted(zip(scores, candidates["documents"][0]), reverse=True)[:3]
정리
RAG는 환각 없는 신뢰할 수 있는 LLM 응답을 구현하는 가장 실용적인 방법입니다. unstructured로 다양한 문서 형식을 손쉽게 파싱하고, 적절한 청킹 전략과 벡터 DB를 조합하면 기업 내부 문서 기반의 강력한 AI Q&A 시스템을 구축할 수 있습니다.