[AI] 07_RAG_컨텍스트_보강 - 100-hours-a-week/9-team-Devths-WIKI GitHub Wiki
단계 5: RAG 컨텍스트 보강 설계
Retrieval-Augmented Generation (RAG) 기반 데이터/컨텍스트 보강
목차
- [1. 개요 및 목적](#1-개요 및 목적)
- 2. Why: 우리 서비스에 왜 필요한가?
- 3. RAG 아키텍처 및 전체 데이터 흐름
- 4. VectorDB 구성 (ChromaDB)
- 5. 검색 전략 (모드별 n_results)
- 6. 컨텍스트 관리
- 7. 임베딩 모델
- 8. 검색 구현 및 구현 참조
- 시나리오별 RAG 활용
- 과제 요구사항 대응 (단계 5)
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 도입으로:
- 개인화: 사용자 데이터 기반 맞춤 응답
- 맥락 유지: 이전 대화/분석 결과 참조
- 품질 향상: 환각(Hallucination) 감소
- 면접 고도화: 약점 기반 꼬리질문 가능
📋 과제 요구사항 대응 (단계 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: 불필요 (생성 기능 없음)