LLM 프롬프트 엔지니어링 고급 기법: 실전에서 바로 쓰는 프롬프트 패턴
Chain of Thought, Few-Shot Learning, Self-Consistency 등 실무에서 즉시 활용 가능한 고급 프롬프트 엔지니어링 기법을 코드 예제와 함께 소개합니다.
RAG(Retrieval-Augmented Generation, 검색 증강 생성)는 대규모 언어 모델(LLM)의 한계를 극복하기 위해 개발된 혁신적인 AI 기술입니다. LLM이 학습 데이터에만 의존하는 것이 아니라, 실시간으로 외부 지식 베이스를 검색하여 더 정확하고 최신의 답변을 생성하는 방식입니다.
일반적인 LLM은 학습 시점까지의 데이터만 알고 있어 최신 정보를 제공하지 못하거나, 때로는 잘못된 정보를 그럴듯하게 생성하는 “환각(hallucination)” 문제가 있습니다. RAG는 이러한 문제를 해결하기 위해 등장했습니다.
RAG는 다음 세 가지 핵심 단계로 작동합니다:
이 과정을 통해 LLM은 자신의 학습 데이터뿐만 아니라 최신의 정확한 외부 정보를 활용하여 답변할 수 있습니다.
지식 컷오프 문제
환각(Hallucination) 문제
도메인 특화 지식 부족
모델 재학습 vs RAG
RAG는 답변의 근거가 되는 원본 문서를 제시할 수 있어:
문서 수집(Document Collection)
# 다양한 소스에서 문서 수집
sources = [
"PDF 파일",
"웹페이지",
"데이터베이스",
"API 응답",
"Notion, Confluence 등 협업 도구"
]
문서 분할(Chunking)
문서를 적절한 크기의 청크(chunk)로 나누는 것이 중요합니다:
# 의미 기반 분할 예제
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=["\n\n", "\n", " ", ""]
)
chunks = text_splitter.split_documents(documents)
메타데이터 추가
각 청크에 메타데이터를 추가하여 검색 품질 향상:
임베딩은 텍스트를 고차원 벡터로 변환하는 과정입니다.
임베딩 모델 선택
| 모델 | 차원 | 장점 | 단점 |
|---|---|---|---|
| OpenAI text-embedding-3-small | 1536 | 저렴, 빠름 | 정확도 중간 |
| OpenAI text-embedding-3-large | 3072 | 높은 정확도 | 비용 높음 |
| Cohere embed-multilingual | 1024 | 다국어 지원 | 영어 성능은 OpenAI보다 낮음 |
| BGE-large | 1024 | 무료, 로컬 실행 | 설정 복잡 |
from openai import OpenAI
client = OpenAI()
def get_embedding(text):
response = client.embeddings.create(
model="text-embedding-3-small",
input=text
)
return response.data[0].embedding
임베딩된 벡터를 효율적으로 저장하고 검색하기 위한 전문 데이터베이스입니다.
주요 벡터 DB 비교
Pinecone
Weaviate
Chroma
Qdrant
# Chroma를 사용한 벡터 저장 예제
import chromadb
from chromadb.utils import embedding_functions
# 클라이언트 초기화
client = chromadb.Client()
# 컬렉션 생성
collection = client.create_collection(
name="my_documents",
embedding_function=embedding_functions.OpenAIEmbeddingFunction(
api_key="your-api-key",
model_name="text-embedding-3-small"
)
)
# 문서 추가
collection.add(
documents=["문서 내용 1", "문서 내용 2"],
metadatas=[{"source": "doc1.pdf"}, {"source": "doc2.pdf"}],
ids=["id1", "id2"]
)
기본 유사도 검색
가장 기본적인 방법으로 코사인 유사도를 사용합니다:
# 질문을 임베딩하고 유사한 문서 검색
results = collection.query(
query_texts=["RAG가 무엇인가요?"],
n_results=5
)
하이브리드 검색
벡터 검색과 키워드 검색을 결합하여 정확도를 높입니다:
from langchain.retrievers import EnsembleRetriever
from langchain.retrievers import BM25Retriever
# BM25 키워드 검색기
bm25_retriever = BM25Retriever.from_documents(documents)
bm25_retriever.k = 5
# 앙상블 검색기 (벡터 + 키워드)
ensemble_retriever = EnsembleRetriever(
retrievers=[vector_retriever, bm25_retriever],
weights=[0.7, 0.3] # 벡터 검색에 70% 가중치
)
메타데이터 필터링
특정 조건을 만족하는 문서만 검색:
results = collection.query(
query_texts=["최신 제품 정보"],
n_results=5,
where={"category": "products", "year": {"$gte": 2024}}
)
리랭킹(Re-ranking)
초기 검색 결과를 재정렬하여 품질 향상:
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import CohereRerank
# Cohere 리랭커 사용
compressor = CohereRerank(
cohere_api_key="your-api-key",
top_n=3
)
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=vector_retriever
)
검색된 문서를 효과적으로 활용하는 프롬프트 구성:
prompt_template = """
다음 문맥을 기반으로 질문에 답변해주세요.
문맥에 답이 없다면 "제공된 정보로는 답변할 수 없습니다"라고 답하세요.
문맥:
{context}
질문: {question}
답변:
"""
from langchain.prompts import PromptTemplate
prompt = PromptTemplate(
template=prompt_template,
input_variables=["context", "question"]
)
하나의 질문을 여러 각도로 재구성하여 검색:
from langchain.retrievers.multi_query import MultiQueryRetriever
# 질문을 자동으로 여러 버전으로 변환
retriever = MultiQueryRetriever.from_llm(
retriever=vector_retriever,
llm=ChatOpenAI(temperature=0)
)
예시:
자연어 질문에서 메타데이터 필터를 자동 추출:
from langchain.retrievers.self_query.base import SelfQueryRetriever
retriever = SelfQueryRetriever.from_llm(
llm=ChatOpenAI(temperature=0),
vectorstore=vectorstore,
document_contents="기술 블로그 포스트",
metadata_field_info=[
AttributeInfo(name="category", type="string"),
AttributeInfo(name="year", type="integer"),
]
)
# "2024년 AI 관련 글을 찾아줘" -> 자동으로 필터링
작은 청크로 검색하되, 전체 문서를 반환:
from langchain.retrievers import ParentDocumentRetriever
from langchain.storage import InMemoryStore
# 작은 청크로 검색, 큰 문서 반환
child_splitter = RecursiveCharacterTextSplitter(chunk_size=400)
parent_splitter = RecursiveCharacterTextSplitter(chunk_size=2000)
store = InMemoryStore()
retriever = ParentDocumentRetriever(
vectorstore=vectorstore,
docstore=store,
child_splitter=child_splitter,
parent_splitter=parent_splitter
)
질문에 대한 가상의 답변을 생성한 후, 그것을 기반으로 검색:
# 1. LLM이 가상의 답변 생성
hypothetical_answer = llm.predict(
"RAG가 무엇인지 설명해주세요"
)
# 2. 가상 답변으로 검색
results = vectorstore.similarity_search(hypothetical_answer)
이 방법은 질문과 답변의 의미적 차이를 줄여 더 정확한 검색이 가능합니다.
from langchain.chat_models import ChatOpenAI
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
from langchain.document_loaders import DirectoryLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 1. 문서 로드
loader = DirectoryLoader('./documents', glob="**/*.pdf")
documents = loader.load()
# 2. 문서 분할
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
splits = text_splitter.split_documents(documents)
# 3. 임베딩 및 벡터 저장
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(
documents=splits,
embedding=embeddings,
persist_directory="./chroma_db"
)
# 4. 검색기 구성
retriever = vectorstore.as_retriever(
search_type="similarity",
search_kwargs={"k": 5}
)
# 5. RAG 체인 생성
llm = ChatOpenAI(model_name="gpt-4", temperature=0)
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
return_source_documents=True
)
# 6. 질의 실행
query = "RAG 시스템의 주요 구성요소는 무엇인가요?"
result = qa_chain({"query": query})
print(f"답변: {result['result']}")
print(f"\n참조 문서:")
for doc in result['source_documents']:
print(f"- {doc.metadata['source']}")
청크 크기는 성능에 큰 영향을 미칩니다:
권장 사항:
검색할 문서 개수(k)의 최적값 찾기:
# 실험을 통한 최적 k 값 찾기
for k in [3, 5, 10, 20]:
retriever = vectorstore.as_retriever(search_kwargs={"k": k})
# 평가 지표 측정
일반적으로 k=5~10이 적절합니다.
동일한 질문에 대한 반복 검색 방지:
from langchain.cache import InMemoryCache
import langchain
langchain.llm_cache = InMemoryCache()
주요 평가 지표:
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy
# RAG 시스템 평가
results = evaluate(
dataset=eval_dataset,
metrics=[faithfulness, answer_relevancy]
)
문제: 관련 문서를 찾지 못함
해결책:
문제: 너무 많은 문서를 프롬프트에 포함 불가
해결책:
문제: 검색된 문서들이 서로 모순되는 정보 포함
해결책:
RAG는 현대 AI 애플리케이션의 핵심 기술로 자리잡았습니다. LLM의 환각 문제를 해결하고, 최신 정보를 제공하며, 기업의 내부 지식을 활용할 수 있게 해줍니다.
RAG를 시작하는 당신에게:
RAG는 계속 발전하는 분야입니다. 최신 기법들을 학습하고, 여러분의 도메인에 맞게 최적화하여 강력한 AI 시스템을 구축하세요!