⚡ 중급2026-05-187분
RAG 입문: 문서를 어떻게 자르고 어떻게 넣어야 AI가 틀리지 않는가
RAG(검색 증강 생성)의 핵심은 모델 선택이 아니라 문서 청킹과 컨텍스트 주입 전략에 있습니다. 가장 흔한 실수와 실용적인 설계 패턴을 소개합니다.
RAGretrievalcontext-injection
RAG가 필요한 이유
Claude는 학습 데이터 이후의 정보를 모릅니다. 사내 문서, 최신 규정, 개인화된 데이터를 다루려면 검색 결과를 프롬프트에 직접 삽입하는 RAG 패턴이 필수입니다.
RAG의 3단계 파이프라인
[문서] → ① 청킹 → ② 벡터 검색 → ③ 컨텍스트 주입 → [Claude]
각 단계에서 실수가 쌓이면 모델이 정확한 문서를 받고도 틀린 답을 냅니다.
① 청킹: 가장 과소평가되는 단계
| 전략 | 설명 | 적합한 문서 | |---|---|---| | 고정 크기 | 500토큰씩 자름 | 균일한 구조의 로그, DB 레코드 | | 문장 경계 | 마침표·단락 기준 | 일반 문서, 뉴스 | | 의미 단위 | 섹션/제목 기준 | 매뉴얼, 법령, 기술 문서 | | 슬라이딩 윈도우 | 50% 겹치게 자름 | 맥락이 경계를 넘나드는 문서 |
황금 규칙: 청크 하나가 단독으로 읽혔을 때 의미를 가져야 합니다. "위에서 설명한 것처럼"으로 시작하는 청크는 실패합니다.
② 컨텍스트 주입과 Claude 호출
아래는 검색된 청크를 Claude에 주입하는 Python 예시입니다.
import anthropic
from typing import List
client = anthropic.Anthropic()
def build_rag_prompt(query: str, retrieved_chunks: List[dict]) -> str:
"""
retrieved_chunks: [{"source": "doc_name", "content": "...", "score": 0.92}]
"""
# 관련도 낮은 청크 필터링 (임계값: 0.7)
filtered = [c for c in retrieved_chunks if c["score"] >= 0.7]
if not filtered:
return "관련 문서를 찾지 못했습니다. 질문을 바꿔보세요."
# 출처 명시 → 환각 방지의 핵심
context_block = "\n\n".join(
f"[출처: {c['source']}]\n{c['content']}"
for c in filtered[:3] # 상위 3개만 (토큰 절약)
)
return f"""아래 문서만을 근거로 질문에 답하세요.
문서에 없는 내용은 "문서에서 확인할 수 없습니다"라고 답하세요.
=== 참고 문서 ===
{context_block}
=================
질문: {query}"""
def ask_with_rag(query: str, retrieved_chunks: List[dict]) -> str:
prompt = build_rag_prompt(query, retrieved_chunks)
response = client.messages.create(
model="claude-sonnet-4-6", # 긴 컨텍스트 이해엔 Sonnet 권장
max_tokens=1024,
system="당신은 주어진 문서만을 기반으로 정확하게 답하는 어시스턴트입니다.",
messages=[{"role": "user", "content": prompt}],
)
return response.content[0].text
# 사용 예시
chunks = [
{"source": "휴가규정_2026.pdf", "content": "연차는 입사 1년 후 15일이 발생하며...", "score": 0.91},
{"source": "취업규칙.pdf", "content": "반차는 오전/오후로 구분되며...", "score": 0.74},
{"source": "급여명세서.pdf", "content": "2025년 12월 급여 내역...", "score": 0.42}, # 필터링됨
]
answer = ask_with_rag("반차 신청은 어떻게 하나요?", chunks)
print(answer)
③ 흔한 실패 패턴과 해결책
| 증상 | 원인 | 해결책 | |---|---|---| | 엉뚱한 문서 기반 답변 | 유사도 임계값 없음 | score ≥ 0.7 필터 추가 | | "위에서처럼" 오류 | 청크 경계 문제 | 슬라이딩 윈도우 또는 섹션 단위 청킹 | | 토큰 초과 오류 | 청크 수 무제한 | 상위 3~5개로 제한 | | 환각 답변 | 프롬프트에 제약 없음 | "문서에 없으면 모른다고 해" 명시 |
체크리스트
- [ ] 청크 하나를 맥락 없이 읽어도 의미 전달이 되는가?
- [ ] 유사도 임계값 필터가 적용되어 있는가?
- [ ] 프롬프트에 "문서에 없으면 모른다고 해" 지시가 있는가?
- [ ] 출처(source) 를 청크와 함께 모델에 전달하는가?
- [ ] 주입하는 청크 수를 토큰 한도에 맞게 제한하는가?
- [ ] 긴 컨텍스트 처리에 Sonnet 계열을 사용하고 있는가?