RAG:MLOps
MLOps 플랫폼 RAG (Retrieval-Augmented Generation) 가이드
RAG 적용 시나리오
MLOps 플랫폼에서 RAG가 적합한 사용 사례:
플랫폼 문서/ADR 검색 (가장 현실적)
- docs/plans/ 하위에 40개 이상의 계획서, docs/reviews/, README, 계약서 등 존재
- 예: "YOLO 학습 런타임 변경 방법은?", "테넌트 격리는 어떻게 되는가?"
- 문서 단락을 근거와 함께 반환
- Tool calling은 구조적 데이터(API)에는 강점이 있으나, 비정형 문서의 부분적 매칭은 RAG가 우위
Training Log/Error 지식베이스
- 과거 실패한 학습 로그, 에러 메시지, 해결 방법 벡터 인덱싱
- 예: "CUDA out of memory가 났는데 과거에 어떻게 해결했어?"
- 유사한 로그 컨텍스트 + 해결 방법 반환
- MLflow metrics는 tool calling, 로그 텍스트는 RAG
OpenAPI/Swagger 스펙 검색
- API 엔드포인트가 많을 때 "데이터셋 삭제 관련 엔드포인트는?" 같은 질문
- 스펙 문서에서 관련 섹션 retrieve
권장 아키텍처
Tool Calling 과 보완하는 형태:
사용자 질문
│
▼
분류기 (의도 판단)
├── 구조적 데이터 조회 → Tool Calling (Plan 40 그대로)
├── 문서/지식 검색 → RAG (docs, logs, plans)
└── 둘 다 필요 → Hybrid (RAG context + tool results 모두 주입)
FAQ: RAG 아키텍처 - LLM 모델 변경 시 RAG 재빌드가 필요한가요?
LLM 모델 변경 시 RAG 재빌드 필요 여부: 아니오.
RAG는 두 가지 독립된 모델로 구성된다:
┌─────────────────────────────────┐
│ Embedding Model (인덱싱) │ ← 벡터 생성
│ (예: text-embedding-3-small) │
│ 문서 → 벡터 → 벡터 DB │
└─────────────────────────────────┘
│
▼ (검색된 컨텍스트)
┌─────────────────────────────────┐
│ LLM (생성) │ ← 답변 생성
│ (Qwen, DeepSeek, GPT, 등) │
│ 프롬프트 + 검색 결과 → 답변 │
└─────────────────────────────────┘
- LLM만 바꿀 때: 인덱싱, 벡터 DB, 검색 로직 전부 그대로 사용
- 다시 빌드해야 할 때: embedding model만 바꿨을 때
- 다른 embedding model은 동일한 텍스트를 다른 벡터 공간에 매핑
구현 방법
권장 스택: PostgreSQL + pgvector
기존 PostgreSQL에 extension 추가하는 방식:
| 장점 |
| 새 인프라 없음 — 이미 돌아있는 Postgres에 extension 추가 |
| Alembic 마이그레이션으로 스키마 관리 (기존 패턴 동일) |
| SQLAlchemy에서 직접 vector 컬럼 사용 가능 |
| 단일 DB = 백업/장애/보안 간소화 |
| 의존성: pgvector + pgvector-python 두 개만 추가 |
대체안 비교
| 옵션 | 적합도 | 이유 |
| pgvector | ⭐ 최고 | 기존 PG 재사용, 인프라 0추가 |
| ChromaDB | ◐ 보통 | docker-compose에 별도 컨테이너 필요 |
| Qdrant | ◐ 보통 | 전용 벡터 DB이지만 오버킬 |
| Cloudflare Vectorize | ○ 낮음 | 앱이 k3s에서 돌고 Workers에서 안 돔 |
구현 단계
- 의존성 추가: pyproject.toml → "pgvector"',"pgvector-python"''
- '마이그레이션:
- alembic revision -m "add rag_vector_store"
- CREATE EXTENSION vector
- CREATE TABLE rag_documents (id, tenant_id, chunk_text, embedding(vector), source_file, metadata(jsonb), created_at)
- Embedding 서비스:
- src/geo_mlops/services/rag_embedding.py
- OpenAI 호환 /v1/embeddings endpoint 호출
- '인덱싱 Job:
- docs/, README.md, plan 문서 → chunking → embedding → DB insert
- '검색 서비스:
- src/geo_mlops/services/rag_service.py
- query embedding → vector nearest-neighbor 검색 → top-K chunk 반환
- 'RAG router:
- src/geo_mlops/routers/rag.py
- POST /api/v1/rag/query
전체 파이프라인
OpenAI API + pgvector는 전체 파이프라인 중 2개 단계만 담당.
| 단계 | 설명 | 추가 라이브러리 |
| 1. Document loading | docs/*.md, README, 코드 주석 읽기 | langchain or unstructured or 직접 구현 |
| 2. Chunking | 문서를 적절한 크기로 분할 (중복/겹침 포함) | langchain.text_splitter or 직접 구현 |
| 3. Embedding | 텍스트 → 벡터 | OpenAI /v1/embeddings or vLLM |
| 4. Storage | 벡터 + 메타데이터 보관 | pgvector |
| 5. Retrieval | 쿼리 → 유사도 검색 + 필터(테넌트, 출처) | pgvector HNSW index |
| 6. Context assembly | 검색 결과 → prompt 조립 | 직접 구현 |
| 7. Generation | prompt + context → LLM → 답변 | Plan 40의 llm_endpoint |
| 8. Re-indexing | 문서 변경 시 자동/수동 재인덱싱 | file watcher or cron |
┌──────────────┐ ┌──────────┐ ┌─────────┐ ┌──────────┐ ┌──────┐
│ 원본 문서 │ ──→ │ Chunking │ ──→ │ Embedding│ ──→ │ pgvector │
│ docs/* │ │ 분할 │ │ OpenAI API│ │ 저장 │
└──────────────┘ └──────────┘ └─────────┘ └──────────┘
↑
┌──────┐ ┌───────────┐ ┌──────────┐ ┌──────────┐ │
│ LLM │ ←── │ Prompt 조립 │ ←── │ Top-K │ ←── │ Query │ ────┘
│ 답변 │ │ context │ │ 검색 │ │ Embedding│
└──────┘ └───────────┘ └──────────┘ └──────────┘
(pgvector)
OpenAI API + pgvector가 담당하는 부분은 3번(Embedding)과 4번(Storage)만. 나머지 6개는 직접 구현해야 함.
단순화 방안:
- langchain이 1~7을 대부분 커버
- 그러나 프로젝트가 FastAPI + SQLAlchemy 패턴으로 일관되게 작성되어 있으므로 직접 구현이 기존 코드 스타일과 더 적합
FAQ: 자동화 가능성 - 사람이 개입하여 전체 자동화에 방해되는 요소가 있는가?
문서 기반 RAG는 전체 자동화 가능. 이미지 라벨링과 근본적으로 다름.
| 이미지 라벨링 | RAG | |
| 입력 | 원시 이미지 (의미 없음) | 문서 (의미 이미 존재) |
| 인간 역할 | "이게 결함이다" 라벨 필요 | 문서 작성 이미 끝난 상태 |
| 핵심 문제 | 의미 부여 (supervised) | 의미 검색 (unstructured → vector) |
자동화 파이프라인
docs/*.md, README.md, 코드
│
▼ (자동)
Chunking — 토큰 기준 분할
│
▼ (자동, API 호출)
Embedding — 텍스트 → 벡터
│
▼ (자동)
pgvector 저장
│
▼ (자동)
HNSW index 생성
│
▼ (자동)
Query → 검색 → LLM → 답변
선택적 인간 개입
다음은 선택사항이며, 설정 이후에는 완전히 자동:
- 답변 품질 평가: "이 대답이 맞나?" 판단 (optional)
- Chunk 파라미터 튜닝: chunk size, overlap 조정 (default로도 가능)
- 인덱싱 범위 결정: "어떤 문서를 포함할까?" (일회성 판단)
이미지 라벨링처럼 지속적으로 사람이 개입하는 작업은 없음.