[AI] 07_RAG_컨텍스트_보강 - 100-hours-a-week/9-team-Devths-WIKI GitHub Wiki

단계 5: RAG 컨텍스트 보강 설계

Retrieval-Augmented Generation (RAG) 기반 데이터/컨텍스트 보강

목차


1. 개요 및 목적

RAG란?

사용자 질문 → 관련 문서 검색 → 컨텍스트와 함께 LLM에 전달 → 응답 생성

일반 LLM: "취업 준비 어떻게 해?" → 일반적인 조언
RAG LLM: "취업 준비 어떻게 해?" + [이력서] + [채용공고] → 개인화된 조언

요구사항

요구사항 목표 우선순위
검색 정확도 관련 문서 Top-5에 포함 높음
응답 속도 검색 포함 3초 이내 높음
컨텍스트 품질 관련성 높은 정보만 포함 높음

2. Why: 우리 서비스에 왜 필요한가?

LLM은 사용자의 이력서를 모릅니다. 일반적인 조언만 제공할 수 있습니다.

RAG가 없으면:

  • "이력서 분석해줘" → 일반적인 조언만 제공
  • "지난번 피드백 뭐였지?" → 기억 못함
  • "약점 기반 질문 해줘" → 불가능

RAG가 있으면:

  • 사용자의 이력서를 VectorDB에서 검색 → 개인 맞춤 조언
  • 이전 분석 결과 검색 → "지난번에는 ~라고 했는데..."
  • 면접 Q&A 검색 → 약점 기반 꼬리질문 생성

3. RAG 아키텍처 및 전체 데이터 흐름

전체 흐름 (10_RAG_전략 정합)

┌─────────────────────────────────────────────────────┐
│                   사용자 질문                         │
└────────────────────────┬────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────┐
│               질문 임베딩 (Gemini Embedding)          │
└────────────────────────┬────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────┐
│                VectorDB 검색 (ChromaDB)              │
│  ┌─────────┐  ┌─────────────┐  ┌──────────────────┐ │
│  │ resumes │  │job_postings │  │ analysis_results │ │
│  └─────────┘  └─────────────┘  └──────────────────┘ │
│  ┌──────────────────┐  ┌──────────────┐             │
│  │interview_feedback │  │ chat_context │             │
│  └──────────────────┘  └──────────────┘             │
└────────────────────────┬────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────┐
│              컨텍스트 구성 (최대 4000자)              │
│  "[출처: 이력서]\n{내용}\n[출처: 채용공고]\n{내용}"    │
└────────────────────────┬────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────┐
│               LLM 응답 생성 (Gemini Flash)           │
│  프롬프트 = 시스템 메시지 + 컨텍스트 + 질문           │
└────────────────────────┬────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────┐
│                   스트리밍 응답                       │
└─────────────────────────────────────────────────────┘

데이터 수집 → 저장 상세

┌─────────────────────────────────────────────────────────────────┐
│   [1. 문서 수집] 이력서(PDF/이미지) · 채용공고(텍스트) · 면접 Q&A  │
│   [2. 전처리]   OCR → 텍스트 추출, 청킹 (512 tokens)             │
│   [3. 임베딩]   Gemini text-embedding-004 (768차원)               │
│   [4. 저장]     ChromaDB Collection별 저장 (로컬 파일 시스템)      │
└─────────────────────────────────────────────────────────────────┘

4. VectorDB 구성 (ChromaDB)

사용 기술 (10_RAG_전략 기준)

항목 내용
VectorDB ChromaDB 0.4.24
저장 방식 로컬 파일 시스템
임베딩 Gemini Embedding (768차원)

VectorDB Collection 설계

Collection 저장 데이터 메타데이터 활용 시점
resumes 이력서 + 포트폴리오 user_id, resume_id 분석, 면접, 매칭
job_postings 채용공고 (JD) job_id, company 매칭, 면접 질문
analysis_results 분석/매칭 결과 user_id, type "이전 피드백" RAG
interview_feedback 면접 Q&A + 평가 session_id, score 약점 기반 질문
chat_context 중요 대화 맥락 user_id, topic 맥락 유지 대화

메타데이터 스키마 예시

{
    "user_id": "user_123",
    "created_at": "2026-01-25T10:00:00",
    "file_id": 42,
    "collection_type": "resume"
}

5. 검색 전략 (모드별 n_results)

모드 n_results 근거
일반 대화 1 속도 우선
분석 전체 정확도 우선
면접 전체 모든 정보 필요
  • 일반 대화: vectordb.query(collection="resumes", n_results=1, where={"user_id": user_id})
  • 분석/면접: vectordb.get_all_documents_by_user(user_id, collections=["resumes", "job_postings", "portfolios"])

6. 컨텍스트 관리

컨텍스트 길이 제한

MAX_CONTEXT_LENGTH = 4000  # 약 1000 토큰

def truncate_context(context: str, max_length: int = 4000) -> str:
    """컨텍스트 길이 제한"""
    if len(context) <= max_length:
        return context
    return context[:max_length] + "\n...(truncated)"

컨텍스트 구성 예시

[출처: 이력서]
3년 경력의 백엔드 개발자입니다.
주요 기술: Python, FastAPI, PostgreSQL, Docker

[출처: 채용공고]
회사: 카카오
포지션: 백엔드 개발자 (경력 3-5년)
필수 기술: Python, FastAPI, Kubernetes

질문: 내가 이 회사에 지원하면 어떨까?

프롬프트 템플릿

RAG_PROMPT = """
당신은 취업/진로 상담 전문가입니다.
사용자의 이력서와 채용공고 정보를 참고하여 개인화된 조언을 제공하세요.

## 참고 정보
{context}

## 사용자 질문
{user_message}

## 응답 가이드
- 구체적이고 실행 가능한 조언을 제공하세요
- 이력서와 채용공고를 비교 분석하세요
- 긍정적인 톤을 유지하되 현실적인 피드백을 주세요
"""

7. 임베딩 모델

항목
모델 Gemini text-embedding-004
차원 768
가격 $0.00001 / 1K 문자
선택 이유 OpenAI 대비 10배 저렴, 한국어 우수

8. 검색 구현 (의사코드) 및 구현 참조

의사코드

async def rag_search(
    query: str, 
    user_id: str, 
    collection: str, 
    k: int = 5
):
    """RAG 검색 + 컨텍스트 증강"""
    
    # 1. 쿼리 임베딩
    query_embedding = await embed(query)
    
    # 2. 유사도 검색
    results = vectordb.similarity_search(
        collection=collection,
        query=query_embedding,
        filter={"user_id": user_id},
        k=k
    )
    
    # 3. 관련성 필터링
    filtered = [r for r in results if r.score > 0.7]
    
    # 4. 프롬프트 증강 (최대 4000자)
    context = truncate_context("\n".join([r.content for r in filtered]))
    augmented_prompt = RAG_PROMPT.format(context=context, user_message=query)
    
    # 5. LLM 호출
    return await llm.invoke(augmented_prompt)

구현 참조 (10_RAG_전략 기준)

파일 역할
3.model/app/services/rag_service.py RAG 파이프라인
3.model/app/services/vectordb_service.py VectorDB 관리

RAGService 주요 메서드

class RAGService:
    async def retrieve_context(
        self,
        user_id: str,
        query: str,
        n_results: int = 1
    ) -> str:
        """쿼리 기반 컨텍스트 검색"""

    async def retrieve_all_documents(
        self,
        user_id: str
    ) -> str:
        """사용자의 모든 문서 검색"""

    async def chat_with_rag(
        self,
        user_id: str,
        message: str,
        model: str = "gemini"
    ) -> AsyncGenerator[str, None]:
        """RAG 기반 채팅 (스트리밍)"""

    async def analyze_resume_and_posting(
        self,
        user_id: str
    ) -> dict:
        """이력서-채용공고 분석"""

API 사용 예시

# 요청
{
    "room_id": "room_001",
    "user_id": "user_123",
    "message": "내 이력서로 이 회사에 지원하면 어떨까?",
    "model": "gemini",
    "context": {"mode": "general"}
}

# 내부 처리: user_123 이력서/채용공고 검색 → 컨텍스트 구성 → Gemini Flash 전달 → 스트리밍 응답 반환

9. 시나리오별 RAG 활용

시나리오 검색 Collection 쿼리 예시
이력서 분석 resumes + analysis_results "내 이력서 분석해줘"
면접 질문 생성 resumes + job_postings + interview_feedback "모의 면접 시작"
일반 대화 chat_context + resumes "지난번 피드백 뭐였지?"

면접 질문 생성 RAG 흐름

[사용자: "모의 면접 시작해줘"]
     │
     ▼
[RAG 검색]
     ├── resumes: 사용자 이력서
     ├── job_postings: 채용공고
     └── interview_feedback: 이전 면접 약점
     │
     ▼
[프롬프트 구성]
     │
     │   이력서: {React, Node.js 경험}
     │   채용공고: {백엔드 개발자, Java 필수}
     │   이전 약점: {시스템 설계 답변 부족}
     │
     ▼
[LLM 질문 생성]
     │
     │   "이력서에 Node.js 경험이 있는데,
     │    Java 기반 시스템으로 전환 시
     │    어떤 점을 준비하셨나요?"
     │
     ▼
[개인화된 질문 반환]

도입 전후 비교

시나리오 RAG 미적용 RAG 적용
이력서 분석 "일반적으로 이력서는..." "귀하의 React 경험을 보면..."
면접 질문 "자기소개 해주세요" "A 프로젝트에서 어려웠던 점은?"
대화 맥락 "무슨 말씀이신지..." "지난번 말씀하신 약점을 기반으로..."

검증 계획

항목 방법 목표
검색 정확도 Precision@5 > 0.8
응답 품질 사용자 만족도 평균 > 4.0
응답 시간 P99 Latency < 3s


결론

RAG 도입으로:

  1. 개인화: 사용자 데이터 기반 맞춤 응답
  2. 맥락 유지: 이전 대화/분석 결과 참조
  3. 품질 향상: 환각(Hallucination) 감소
  4. 면접 고도화: 약점 기반 꼬리질문 가능

📋 과제 요구사항 대응 (단계 5)

1. 텍스트/LLM 서비스: RAG적용

우리 서비스의 핵심 기능입니다.

구현 내용:

┌──────────────────────────────────────────────────────────┐
│  텍스트 RAG 파이프라인 (Hybrid Search + Reranker)        │
├──────────────────────────────────────────────────────────┤
│                                                          │
│  [1단계] 데이터 수집 및 저장                              │
│    • 이력서/포트폴리오 (PDF/이미지)                       │
│    • 채용공고 (텍스트)                                    │
│    • 면접 Q&A (저장된 답변)                               │
│    ↓                                                     │
│  [2단계] 전처리                                           │
│    • OCR → 텍스트 추출                                    │
│    • 청킹 (512 tokens)                                   │
│    ↓                                                     │
│  [3단계] 임베딩                                           │
│    • Gemini text-embedding-004 (768차원)                 │
│    ↓                                                     │
│  [4단계] VectorDB 저장                                    │
│    • ChromaDB Collection별 저장                          │
│                                                          │
│  [5단계] RAG 검색 (Hybrid Search)                        │
│    • BM25 (키워드 30%) + Vector Search (의미 70%)        │
│    • Top 20 검색                                         │
│    ↓                                                     │
│  [6단계] Reranker                                        │
│    • Cohere Rerank API                                   │
│    • Top 20 → Top 3로 정제                               │
│    ↓                                                     │
│  [7단계] 프롬프트 증강                                    │
│    • Context + Question → LLM                            │
│                                                          │
└──────────────────────────────────────────────────────────┘

적용 API:

  • /ai/chat - 일반 대화 (이력서 기반)
  • /ai/text/extract - 이력서 분석 (이전 피드백 참조)
  • /ai/interview/question - 면접 질문 생성 (약점 기반)
  • /ai/interview/report - 면접 리포트 (유사 면접 결과 참조)

기대 효과:

  • ✅ 정확도 10-15% 향상 (Hybrid Search + Reranker)
  • ✅ 개인화된 응답
  • ✅ 환각(Hallucination) 감소

2. 비전 서비스: Visual RAG ⚠️ 미적용 (우리 서비스에 불필요)

Visual RAG란?

이미지/텍스트 쌍을 검색하여 비전 모델의 이해를 향상

예시:
사용자: "이 이미지와 비슷한 디자인 찾아줘"
  ↓
Visual RAG: 이미지 DB에서 유사 이미지 검색
  ↓
VLM: 검색된 이미지 기반 응답 생성

우리 서비스에 적용 불가 이유:

항목 Visual RAG 요구사항 우리 서비스 현황 적용 가능성
이미지 DB 대규모 이미지 데이터셋 필요 이력서/포트폴리오 이미지만
이미지 검색 이미지 유사도 검색 텍스트 검색만 필요
사용 사례 이미지 기반 질문/응답 텍스트 기반 취업 상담

결론:Visual RAG는 우리 서비스에 불필요

대안:

  • ✅ 이력서/포트폴리오 이미지 → OCR로 텍스트 추출 → 텍스트 RAG 사용

3. 음성 서비스: Speech RAG미적용 (음성 기능 없음)

Speech RAG란?

음성과 텍스트를 같은 임베딩 공간에 매핑하여 
ASR 없이 음성 데이터 검색

예시:
사용자: [음성] "내 이력서 분석해줘"
  ↓
Speech RAG: 음성 임베딩 → VectorDB 검색
  ↓
LLM: 검색된 컨텍스트 기반 응답

우리 서비스에 적용 불가 이유:

항목 Speech RAG 요구사항 우리 서비스 현황 적용 가능성
음성 입력 음성 인터페이스 필요 텍스트 채팅만
음성 DB 음성 데이터셋 필요 음성 데이터 없음
사용 사례 음성 기반 질문/응답 텍스트 기반 상담

결론:Speech RAG는 우리 서비스에 불필요

향후 고려 (V4+):

  • ⚠️ 음성 면접 기능 추가 시 검토 가능
  • ⚠️ 하지만 우선순위 낮음 (텍스트 기반으로 충분)

4. 이미지/영상 생성 서비스: RA-IS, Retrieval-Augmented Video Generation미적용 (생성 기능 없음)

RA-IS (Retrieval-Augmented Image Synthesis)란?

텍스트 프롬프트에 대해 관련 이미지를 검색하여 
생성 모델에 조건으로 사용

예시:
사용자: "빨간 자동차 이미지 생성"
  ↓
RA-IS: 이미지 DB에서 빨간 자동차 검색
  ↓
생성 모델: 검색된 이미지 기반 새 이미지 생성

우리 서비스에 적용 불가 이유:

항목 RA-IS 요구사항 우리 서비스 현황 적용 가능성
생성 모델 Stable Diffusion, DALL-E 등 생성 기능 없음
이미지 DB 대규모 이미지 데이터셋 이력서 이미지만
사용 사례 이미지/영상 생성 텍스트 분석/상담

결론:RA-IS는 우리 서비스에 불필요


5. 기타: 주기적 재학습/파인튜닝 전략 ⚠️ V4+ 검토

현재 전략 (V1~V3):

┌──────────────────────────────────────────────────────────┐
│  API 기반 LLM 사용 (파인튜닝 없음)                        │
├──────────────────────────────────────────────────────────┤
│                                                          │
│  • Gemini 2.0 Flash (API)                                │
│  • GPT-5 mini (Fallback API)                             │
│                                                          │
│  장점:                                                   │
│  ✅ 파인튜닝 불필요 (비용/시간 절감)                      │
│  ✅ 최신 모델 자동 업데이트                               │
│  ✅ RAG로 충분한 개인화                                   │
│                                                          │
│  단점:                                                   │
│  ⚠️ 도메인 특화 불가                                      │
│  ⚠️ API 비용 지속 발생                                    │
│                                                          │
└──────────────────────────────────────────────────────────┘

V4+ 파인튜닝 검토 조건:

조건 현재 상태 V4 목표 파인튜닝 필요성
월 사용자 < 1,000명 > 10,000명 ⚠️ 검토
도메인 데이터 부족 충분 (10만+ 대화) ⚠️ 검토
API 비용 $100/월 $1,000/월 ⚠️ 검토
정확도 85% 95% ⚠️ 검토

파인튜닝 전략 (V4+):

# 1. 데이터 수집 (V1~V3)
# - 사용자 대화 10만+ 건
# - 면접 Q&A 1만+ 건
# - 이력서 분석 결과 5만+ 건

# 2. 파인튜닝 (V4)
from openai import OpenAI

client = OpenAI()

# 파인튜닝 데이터 준비
training_data = [
    {"messages": [
        {"role": "system", "content": "당신은 취업 상담 AI입니다."},
        {"role": "user", "content": "내 이력서 분석해줘"},
        {"role": "assistant", "content": "귀하의 React 경험을 보면..."}
    ]},
    # ... 10만+ 건
]

# 파인튜닝 실행
fine_tune_job = client.fine_tuning.jobs.create(
    training_file="training_data.jsonl",
    model="gpt-4o-mini-2024-07-18"
)

# 3. 배포 (V4)
# - 파인튜닝된 모델 API로 배포
# - RAG + 파인튜닝 모델 조합

비용 비교 (V4):

항목 API 사용 파인튜닝 모델
초기 비용 $0 $500 (1회)
월 비용 $1,000 $300
정확도 85% 95%
손익분기점 - 6개월

결론: ⚠️ V4에서 검토, V1~V3는 API 사용


📊 과제 대응 요약

과제 항목 적용 여부 이유
1. 텍스트/LLM RAG 적용 핵심 기능, Hybrid Search + Reranker
2. Visual RAG ❌ 미적용 이미지 검색 불필요, OCR로 충분
3. Speech RAG ❌ 미적용 음성 기능 없음
4. RA-IS (이미지 생성) ❌ 미적용 생성 기능 없음
5. 파인튜닝 ⚠️ V4 검토 V1~V3는 API 사용으로 충분

🎯 우리 서비스 RAG 전략

V1~V3: 텍스트 RAG 집중

1. Hybrid Search (BM25 + Vector)
2. Cohere Reranker
3. Gemini Embedding (단일 모델)
4. ChromaDB (VectorDB)
5. 5개 Collection (resumes, job_postings, analysis_results, interview_feedback, chat_context)

V4+: 고도화 검토 ⚠️

1. 파인튜닝 (조건 충족 시)
2. 음성 면접 (Speech RAG 검토)
3. 이미지 기반 포트폴리오 분석 (Visual RAG 검토)

🎊 최종 결론

우리 서비스는 텍스트 RAG만 필요합니다!

  • 텍스트 RAG: 핵심 기능, 완벽 구현
  • Visual RAG: 불필요 (OCR로 충분)
  • Speech RAG: 불필요 (음성 기능 없음)
  • RA-IS: 불필요 (생성 기능 없음)