from langchain.text_splitter import RecursiveCharacterTextSplitter
# 기본 설정 text_splitter = RecursiveCharacterTextSplitter( chunk_size=1000, chunk_overlap=200, length_function=len, )
# 문서 유형별 최적화 ## 기술 문서 - 작은 청크 tech_splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=100 )
## 소설/긴 글 - 큰 청크 narrative_splitter = RecursiveCharacterTextSplitter( chunk_size=2000, chunk_overlap=400 )
1.2 의미 기반 청킹
from langchain_experimental.text_splitter import SemanticChunker from langchain_openai.embeddings import OpenAIEmbeddings
# 의미론적 유사도로 분할 semantic_chunker = SemanticChunker( OpenAIEmbeddings(), breakpoint_threshold_type="percentile", # or "standard_deviation" breakpoint_threshold_amount=95# 상위 5%에서 분할 )
# 다양성을 고려한 검색 - 중복 방지 retriever = vectorstore.as_retriever( search_type="mmr", search_kwargs={ "k": 5, # 최종 반환 문서 수 "fetch_k": 20, # 초기 후보 문서 수 "lambda_mult": 0.5# 0: 최대 다양성, 1: 최대 관련성 } )
3.3 재순위화 (Re-ranking)
from langchain.retrievers import ContextualCompressionRetriever from langchain.retrievers.document_compressors import CohereRerank
# Cohere Reranker 사용 compressor = CohereRerank( cohere_api_key="YOUR_API_KEY", top_n=3# 상위 3개만 선택 )
from langchain.chains import LLMChain from langchain.prompts import PromptTemplate
# 쿼리 확장 프롬프트 query_expansion_prompt = PromptTemplate( input_variables=["question"], template="""원래 질문: {question} 이 질문과 관련된 3가지 유사한 질문을 생성하세요: 1. 2. 3.""" )
# 원본 + 확장 쿼리로 검색 defsearch_with_expansion(question, retriever): # 원본 쿼리로 검색 original_docs = retriever.get_relevant_documents(question)
# 쿼리 확장 expanded = expansion_chain.run(question=question) expanded_questions = expanded.split('\n')
# 확장된 쿼리로 추가 검색 all_docs = original_docs for exp_q in expanded_questions: exp_docs = retriever.get_relevant_documents(exp_q.strip()) all_docs.extend(exp_docs)
# 중복 제거 unique_docs = list({doc.page_content: doc for doc in all_docs}.values()) return unique_docs[:5]
4.2 HyDE (Hypothetical Document Embeddings)
from langchain.chains import HypotheticalDocumentEmbedder
# 가상 답변 생성 후 검색 hyde_prompt = PromptTemplate( input_variables=["question"], template="""질문: {question} 이 질문에 대한 상세한 답변을 작성하세요 (실제 사실 여부와 무관):""" )
advanced_prompt = PromptTemplate( input_variables=["context", "question"], template="""당신은 정확하고 신뢰할 수 있는 AI 어시스턴트입니다. 지침: 1. 제공된 컨텍스트만을 사용하여 답변하세요 2. 컨텍스트에 답이 없으면 "제공된 정보에서 답을 찾을 수 없습니다"라고 답하세요 3. 추측하거나 외부 지식을 사용하지 마세요 4. 답변에 관련 섹션을 인용하세요 5. 간결하고 명확하게 답변하세요 컨텍스트: {context} 질문: {question} 답변 (인용 포함):""" )
5.2 Few-shot 프롬프트
few_shot_prompt = PromptTemplate( input_variables=["context", "question"], template="""다음 예제를 참고하여 답변하세요: 예제 1: 질문: RAG의 장점은? 답변: RAG의 주요 장점은 다음과 같습니다: 1. 최신 정보 활용 가능 2. 환각 현상 감소 [출처: RAG Overview, p.5] 예제 2: 질문: 벡터 데이터베이스란? 답변: 벡터 데이터베이스는 고차원 벡터를 효율적으로 저장하고 검색하는 데이터베이스입니다. [출처: Vector DB Guide, p.12] 이제 다음 질문에 답하세요: 컨텍스트: {context} 질문: {question} 답변:""" )
5.3 Chain-of-Thought 프롬프트
cot_prompt = PromptTemplate( input_variables=["context", "question"], template="""단계별로 생각하며 답변하세요: 컨텍스트: {context} 질문: {question} 답변 과정: 1. 먼저 질문의 핵심을 파악합니다: 2. 관련 정보를 컨텍스트에서 찾습니다: 3. 찾은 정보를 종합합니다: 4. 최종 답변:""" )
6. 응답 생성 최적화
6.1 스트리밍 응답
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
# 스트리밍으로 빠른 첫 응답 streaming_llm = ChatOpenAI( model="gpt-4o", temperature=0, streaming=True, callbacks=[StreamingStdOutCallbackHandler()] )
# 예측 및 평가 predictions = [] for item in test_questions: pred = qa_chain.run(item["question"]) predictions.append({"query": item["question"], "result": pred})
return { 'total_queries': len(self.metrics), 'avg_retrieval_time': sum(m['retrieval_time'] for m in self.metrics) / len(self.metrics), 'avg_generation_time': sum(m['generation_time'] for m in self.metrics) / len(self.metrics), 'avg_total_time': sum(m['total_time'] for m in self.metrics) / len(self.metrics), }
# 사용 예 monitor = RAGMonitor()
defmonitored_query(question): # 검색 시간 측정 start = time.time() docs = retriever.get_relevant_documents(question) retrieval_time = time.time() - start
# 생성 시간 측정 start = time.time() answer = qa_chain.run(question) generation_time = time.time() - start