⚡ 중급2026-05-097분
Claude가 모르는 최신 정보를 알려주는 법: RAG 입문 실전 구현
RAG(Retrieval-Augmented Generation)는 외부 문서를 검색해 프롬프트에 주입함으로써 Claude의 지식 한계를 뛰어넘는 패턴이다. 핵심 원리부터 간단한 Python 구현까지 한 번에 정리한다.
ragretrievalcontext-injection
Claude가 '모른다'고 할 때
Claude의 학습 데이터는 특정 시점에서 끊긴다. 사내 문서, 최신 뉴스, 제품 매뉴얼처럼 Claude가 학습하지 않은 정보를 다뤄야 할 때 RAG가 답이다.
RAG의 흐름:
사용자 질문 → 관련 문서 검색 → 문서를 컨텍스트에 삽입 → Claude 응답
Fine-tuning과 달리 모델 재학습이 필요 없고, 문서를 언제든 업데이트할 수 있다.
RAG의 세 가지 핵심 단계
1. 인덱싱 (Indexing)
문서를 청크(chunk)로 분할하고 검색 가능한 형태로 저장한다.
- 청크 크기: 일반적으로 200~500토큰
- 너무 작으면 문맥 손실, 너무 크면 노이즈 증가
2. 검색 (Retrieval)
질문과 관련된 청크를 찾는다.
- 키워드 검색: BM25 등 전통 방식, 빠르고 가볍다
- 벡터 검색: 임베딩 유사도, 의미 기반 검색에 강하다
- 하이브리드: 둘을 결합해 정확도 향상
3. 생성 (Generation)
검색된 문서를 프롬프트에 넣어 Claude에게 전달한다.
- 검색 결과는
user메시지 또는system에 삽입 - 출처 명시를 요구하면 hallucination 감소
Python 간단 구현 (키워드 기반 RAG)
실제 벡터 DB 없이 동작하는 최소 RAG 패턴이다. 프로덕션에서는 이 검색 부분만 Chroma·Pinecone 등으로 교체하면 된다.
import anthropic
from typing import TypedDict
client = anthropic.Anthropic()
# --- 1. 문서 저장소 (실제로는 벡터 DB) ---
DOCS: list[dict] = [
{
"id": "doc1",
"content": "Claude Sonnet 4는 2025년 출시된 Anthropic의 모델로, "
"코딩과 추론 능력이 크게 향상되었다.",
"source": "Anthropic 공식 블로그",
},
{
"id": "doc2",
"content": "RAG는 검색 증강 생성의 약자로, 외부 문서를 "
"실시간으로 프롬프트에 주입해 LLM의 응답 정확도를 높인다.",
"source": "AI 용어집",
},
{
"id": "doc3",
"content": "Anthropic의 API 요금은 입력 토큰과 출력 토큰을 "
"별도로 과금하며, 모델마다 단가가 다르다.",
"source": "Anthropic 요금 페이지",
},
]
class RetrievedDoc(TypedDict):
id: str
content: str
source: str
score: int
# --- 2. 간이 키워드 검색 ---
def retrieve(query: str, top_k: int = 2) -> list[RetrievedDoc]:
"""질문 키워드와 문서 내용을 단순 겹침으로 점수 계산."""
query_words = set(query.lower().split())
scored: list[RetrievedDoc] = []
for doc in DOCS:
doc_words = set(doc["content"].lower().split())
score = len(query_words & doc_words)
scored.append({**doc, "score": score}) # type: ignore
scored.sort(key=lambda d: d["score"], reverse=True)
return [d for d in scored[:top_k] if d["score"] > 0]
# --- 3. RAG 프롬프트 조립 + 생성 ---
def rag_query(question: str) -> str:
docs = retrieve(question)
if not docs:
context = "관련 문서를 찾지 못했습니다."
else:
context = "\n\n".join(
f"[출처: {d['source']}]\n{d['content']}" for d in docs
)
prompt = f"""아래 문서를 참고해 질문에 답하라.
반드시 출처를 언급하고, 문서에 없는 내용은 추측하지 마라.
=== 참고 문서 ===
{context}
=== 질문 ===
{question}"""
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=512,
messages=[{"role": "user", "content": prompt}],
)
return response.content[0].text # type: ignore
# 실행
answer = rag_query("RAG가 무엇인지 설명해줘")
print(answer)
프로덕션으로 가는 업그레이드 경로
| 단계 | 키워드 RAG | 벡터 RAG | 하이브리드 | |------|-----------|---------|----------| | 검색 정확도 | 낮음 | 높음 | 가장 높음 | | 구현 난이도 | 쉬움 | 중간 | 어려움 | | 추천 도구 | 내장 dict | Chroma, Pinecone | Weaviate | | 시작 기준 | PoC·학습용 | 실서비스 | 대규모 서비스 |
✅ RAG 구현 체크리스트
- [ ] 문서 청크 크기를 200~500토큰 범위로 설정했는가?
- [ ] 검색 결과가 없을 때의 fallback 메시지를 처리했는가?
- [ ] 프롬프트에 출처 명시 요구를 포함했는가?
- [ ] 검색된 문서가
max_tokens한도를 초과하지 않는가? - [ ] 문서 업데이트 시 인덱스도 함께 갱신하는 파이프라인이 있는가?
- [ ] 응답에 문서에 없는 내용이 포함되는지 주기적으로 감사하는가?