홈으로
🔍

RAG 검색 증강 생성

문서 기반 AI 시스템 구축의 모든 것

12시간
intermediate
6개 챕터
중급 과정으로 돌아가기

Chapter 3: RAG를 위한 프롬프트 엔지니어링

검색 증강 생성을 최적화하는 프롬프트 전략

3.1 검색을 위한 Chain of Thought

단계별 사고를 통한 검색 품질 향상

기본 CoT 검색 프롬프트

❌ 단순한 접근

사용자 질문: {query}
검색된 문서: {documents}
위 정보를 바탕으로 답변하세요.

✅ Chain of Thought 접근

당신은 검색 증강 AI 어시스턴트입니다. 다음 단계를 따라 사용자 질문에 답변하세요:

사용자 질문: {query}

검색된 문서:
{documents}

답변 프로세스:
1. 먼저 사용자의 핵심 의도를 파악하세요
2. 검색된 각 문서의 관련성을 평가하세요
3. 가장 관련성 높은 정보를 추출하세요
4. 정보 간의 모순이나 차이점이 있는지 확인하세요
5. 종합적인 답변을 구성하세요

단계별 분석:
<thinking>
1. 사용자 의도: [여기에 분석]
2. 문서별 관련성: 
   - 문서1: [관련도 및 핵심 정보]
   - 문서2: [관련도 및 핵심 정보]
3. 정보 종합: [추출한 핵심 정보들]
4. 모순 확인: [있다면 설명, 없다면 "없음"]
</thinking>

최종 답변:
[종합적이고 명확한 답변]

고급 CoT 기법: Self-Ask

class SelfAskRAGPrompt:
    def __init__(self):
        self.template = """
사용자 질문을 분석하고 필요한 하위 질문들을 생성한 후, 
검색된 정보를 활용해 답변하세요.

원본 질문: {original_query}

<self_ask>
이 질문에 답하려면 어떤 정보가 필요한가?
1. [하위 질문 1]
2. [하위 질문 2]
3. [하위 질문 3]
</self_ask>

각 하위 질문에 대한 검색 결과:
{sub_query_results}

<integration>
하위 답변들을 어떻게 통합할 것인가?
- 정보의 신뢰도 평가
- 시간적 순서나 인과관계 고려
- 모순되는 정보 처리 방법
</integration>

최종 답변:
[통합된 종합 답변]
"""
    
    def generate_sub_queries(self, original_query):
        # LLM을 사용해 하위 질문 생성
        prompt = f"""
다음 질문을 답하기 위해 필요한 3-5개의 구체적인 하위 질문을 생성하세요:
"{original_query}"

하위 질문들:
"""
        return self.llm.generate(prompt)
    
    def search_and_answer(self, query):
        # 1. 하위 질문 생성
        sub_queries = self.generate_sub_queries(query)
        
        # 2. 각 하위 질문에 대해 검색
        sub_results = []
        for sub_q in sub_queries:
            docs = self.retriever.search(sub_q, k=3)
            sub_results.append({
                "question": sub_q,
                "documents": docs
            })
        
        # 3. 통합 답변 생성
        final_prompt = self.template.format(
            original_query=query,
            sub_query_results=sub_results
        )
        
        return self.llm.generate(final_prompt)

3.2 컨텍스트를 활용한 Few-shot 프롬프팅

예시를 통한 RAG 응답 품질 향상

효과적인 Few-shot 예시 구성

시스템: 당신은 검색된 문서를 바탕으로 정확하고 도움이 되는 답변을 제공하는 AI입니다.
다음 예시들을 참고하여 답변 스타일을 학습하세요.

예시 1:
질문: "Python에서 리스트와 튜플의 차이점은?"
검색 문서:
- 문서1: "리스트는 가변(mutable) 객체로 생성 후 수정 가능..."
- 문서2: "튜플은 불변(immutable) 객체로 한번 생성되면..."
- 문서3: "메모리 효율성 면에서 튜플이 리스트보다..."

답변: 검색된 문서를 바탕으로 Python의 리스트와 튜플의 주요 차이점을 설명하겠습니다:

1. **가변성 (Mutability)**
   - 리스트: 가변 객체로 요소 추가, 삭제, 수정 가능
   - 튜플: 불변 객체로 생성 후 변경 불가

2. **메모리 효율성**
   - 튜플이 리스트보다 메모리를 적게 사용
   - 고정된 데이터에는 튜플이 효율적

[출처: 문서1, 문서2, 문서3]

예시 2:
질문: "RESTful API의 주요 원칙은?"
검색 문서:
- 문서1: "REST는 6가지 제약 조건을 따르는..."
- 문서2: "Stateless 특성으로 서버는 클라이언트 상태를..."
- 문서3: "Uniform Interface는 리소스 식별과..."

답변: 검색 결과를 종합하여 RESTful API의 핵심 원칙을 정리하면:

1. **무상태성 (Stateless)**
   - 각 요청은 독립적이며 서버는 클라이언트 상태를 저장하지 않음

2. **균일한 인터페이스 (Uniform Interface)**
   - 리소스는 URI로 식별
   - 표준 HTTP 메서드 사용 (GET, POST, PUT, DELETE)

3. **클라이언트-서버 분리**
   - 관심사의 분리로 확장성 향상

[출처: 문서1, 문서2, 문서3]

---

이제 실제 질문에 답변하세요:
질문: {user_query}
검색 문서:
{retrieved_documents}

답변:

도메인별 Few-shot 템플릿

🏥 의료 도메인

특징: 정확성 강조, 주의사항 포함, 전문 용어 설명

답변 형식:
1. 의학적 정의
2. 증상/원인
3. 치료 방법
4. ⚠️ 주의사항
[참고 문헌 명시]

⚖️ 법률 도메인

특징: 조항 인용, 판례 참조, 면책 조항

답변 형식:
1. 관련 법령
2. 핵심 내용
3. 판례/해석
4. 💡 실무 팁
[법령/판례 출처]

3.3 시스템 프롬프트 최적화

RAG 시스템의 기본 동작 정의

RAG 최적화된 시스템 프롬프트

SYSTEM_PROMPT = """
당신은 검색 증강 생성(RAG) 시스템을 갖춘 AI 어시스턴트입니다.

핵심 원칙:
1. **정확성**: 검색된 문서에 있는 정보만을 사용하여 답변
2. **투명성**: 정보의 출처를 명확히 표시
3. **완전성**: 관련된 모든 정보를 종합적으로 제공
4. **신뢰성**: 불확실한 정보는 그 불확실성을 명시

답변 가이드라인:

## 정보 처리
- 검색된 문서를 신중히 분석하여 관련 정보 추출
- 여러 문서 간 정보가 상충할 경우 모든 관점 제시
- 시간에 민감한 정보는 날짜/버전 명시

## 답변 구조
1. 핵심 답변 (1-2문장 요약)
2. 상세 설명 (구조화된 정보)
3. 추가 고려사항 (있을 경우)
4. 출처 표시

## 특별 지침
- 검색 결과가 불충분한 경우: "제공된 문서에는 충분한 정보가 없습니다"
- 전문 용어 사용 시: 간단한 설명 추가
- 코드/명령어 포함 시: 명확한 포맷팅과 주석

## 금지 사항
- 검색되지 않은 정보를 추측하거나 생성하지 않기
- 개인적 의견이나 추천을 하지 않기 (문서 기반이 아닌 경우)
- 확실하지 않은 정보를 확실한 것처럼 표현하지 않기
"""

class RAGSystemPromptOptimizer:
    def __init__(self, domain=None):
        self.base_prompt = SYSTEM_PROMPT
        self.domain = domain
        
    def get_optimized_prompt(self, query_type=None):
        prompt = self.base_prompt
        
        # 도메인별 추가 지침
        if self.domain:
            prompt += f"\n\n도메인: {self.domain}\n"
            prompt += self.get_domain_specific_rules()
        
        # 쿼리 타입별 조정
        if query_type == "factual":
            prompt += "\n정확한 사실 확인에 중점을 두세요."
        elif query_type == "analytical":
            prompt += "\n여러 관점을 분석하고 비교하세요."
        elif query_type == "procedural":
            prompt += "\n단계별로 명확하게 설명하세요."
        
        return prompt
    
    def get_domain_specific_rules(self):
        rules = {
            "medical": "의학적 조언은 제공하지 않으며, 전문의 상담을 권고하세요.",
            "legal": "법률 자문이 아님을 명시하고, 전문가 상담을 권고하세요.",
            "financial": "투자 조언이 아님을 명시하고, 리스크를 설명하세요.",
            "technical": "코드 예제는 테스트를 거쳐야 함을 명시하세요."
        }
        return rules.get(self.domain, "")

성능 측정 지표

85%

정확도

92%

출처 명시율

3.2초

평균 응답시간

4.6/5

사용자 만족도

3.4 에러 처리 프롬프트

우아한 실패와 사용자 가이드

상황별 에러 처리 템플릿

📭 검색 결과 없음

죄송합니다. "{query}"에 대한 관련 정보를 찾을 수 없습니다.

다음과 같이 시도해보세요:
• 다른 키워드나 동의어를 사용해보세요
• 더 구체적이거나 일반적인 용어로 검색해보세요
• 철자와 띄어쓰기를 확인해보세요

예시: "{suggested_query}"

🔀 모순된 정보

검색된 문서들에서 상충하는 정보가 발견되었습니다:

관점 1: [출처: 문서A]
"{contradicting_info_1}"

관점 2: [출처: 문서B]
"{contradicting_info_2}"

💡 이러한 차이는 다음과 같은 이유일 수 있습니다:
• 정보의 업데이트 시점 차이
• 서로 다른 맥락이나 조건
• 출처의 관점 차이

최신 정보나 공식 출처를 확인하시기 바랍니다.

⚠️ 부분적 정보

요청하신 정보의 일부만 찾을 수 있었습니다:

✅ 찾은 정보:
{found_information}

❌ 찾지 못한 정보:
{missing_information}

추가 정보가 필요하시면:
1. 더 구체적인 질문을 해주세요
2. 다른 측면에서 접근해보세요
3. 관련 전문가나 공식 문서를 참조하세요

3.5 다중 턴 대화 관리

컨텍스트를 유지하는 연속 대화

대화 컨텍스트 관리 시스템

class MultiTurnRAGManager:
    def __init__(self, max_history=5):
        self.conversation_history = []
        self.retrieved_context = {}
        self.max_history = max_history
        
    def process_turn(self, user_query, turn_number):
        # 대화 기록 기반 쿼리 개선
        enhanced_query = self.enhance_query_with_context(user_query)
        
        # 이전 검색 결과 재사용 여부 결정
        should_reuse = self.check_context_relevance(user_query)
        
        if should_reuse:
            # 기존 컨텍스트 활용
            documents = self.retrieved_context.get('documents', [])
            prompt = self.build_contextual_prompt(
                user_query, 
                documents, 
                self.conversation_history
            )
        else:
            # 새로운 검색 수행
            documents = self.retriever.search(enhanced_query)
            self.retrieved_context = {
                'query': enhanced_query,
                'documents': documents,
                'turn': turn_number
            }
            prompt = self.build_fresh_prompt(user_query, documents)
        
        return prompt
    
    def build_contextual_prompt(self, query, documents, history):
        return f"""
이전 대화 내용:
{self.format_history(history[-3:])}  # 최근 3개 턴만

현재 질문: {query}

관련 컨텍스트 (이전에 검색된 정보):
{self.format_documents(documents)}

지침:
1. 이전 대화의 맥락을 고려하여 답변하세요
2. 이미 언급된 내용은 간략히 참조만 하세요
3. 새로운 관점이나 추가 정보에 집중하세요
4. 대화의 흐름을 자연스럽게 이어가세요

답변:
"""
    
    def enhance_query_with_context(self, current_query):
        if not self.conversation_history:
            return current_query
            
        # 대명사 해결 및 컨텍스트 추가
        context_keywords = self.extract_keywords(self.conversation_history[-2:])
        
        # 쿼리에 컨텍스트가 부족한 경우 보강
        if self.is_query_ambiguous(current_query):
            enhanced = f"{context_keywords} {current_query}"
            return enhanced
        
        return current_query
    
    def check_context_relevance(self, current_query):
        """이전 검색 결과의 재사용 가능성 판단"""
        if not self.retrieved_context:
            return False
            
        # 의미적 유사도 계산
        prev_query = self.retrieved_context.get('query', '')
        similarity = self.calculate_similarity(prev_query, current_query)
        
        # 임계값 이상이면 재사용
        return similarity > 0.7

# 사용 예시
manager = MultiTurnRAGManager()

# Turn 1
response1 = manager.process_turn("파이썬의 장점은 무엇인가요?", 1)

# Turn 2 - 컨텍스트 활용
response2 = manager.process_turn("그럼 단점은요?", 2)  # "그럼"이 파이썬을 지칭

# Turn 3 - 새로운 검색 필요
response3 = manager.process_turn("자바와 비교하면 어떤가요?", 3)

실습 과제

RAG 프롬프트 엔지니어링 실습

📋 과제 1: 도메인별 프롬프트 템플릿 구축

  1. 1. 3개 이상의 도메인 선택 (예: 의료, 법률, 기술)
  2. 2. 각 도메인별 시스템 프롬프트 작성
  3. 3. Few-shot 예시 3개씩 준비
  4. 4. 에러 처리 시나리오 정의
  5. 5. 실제 질문으로 테스트 및 평가

🎯 과제 2: Chain of Thought 최적화

  • • Self-Ask 방식으로 복잡한 질문 분해
  • • 각 단계별 추론 과정 명시화
  • • 검색 효율성과 답변 품질 측정
  • • A/B 테스트로 개선 효과 검증

💡 과제 3: 다중 턴 대화 시뮬레이션

고객 지원 챗봇 시나리오로 다음을 구현:

  • • 5턴 이상의 연속 대화 처리
  • • 컨텍스트 유지 및 참조 해결
  • • 대화 기록 기반 개인화
  • • 만족도 평가 시스템 구축

📊 평가 지표

정량적 지표:

  • • 응답 정확도
  • • 처리 시간
  • • 토큰 사용량

정성적 지표:

  • • 답변의 자연스러움
  • • 컨텍스트 이해도
  • • 에러 처리 품질