RAG (Retrieval-Augmented Generation)
ai
개념
LLM은 학습 데이터에 없는 정보를 모른다. 내 노트, 사내 문서 같은 건 답할 수 없다.
RAG는 질문하기 전에 관련 문서를 검색해서 LLM에 같이 넘기는 것이다.
- Retrieval — 관련 문서 검색
- Augmented — 검색 결과를 프롬프트에 붙임
- Generation — LLM이 그걸 보고 답변 생성
전부 주는 게 아니라 관련된 것만 골라서 준다. 노트가 200개여도 질문과 관련된 3~5개만 넘긴다.
전체 흐름
[사전 준비 — 한 번만]
문서 → 청킹 (적당한 크기로 자름) → 임베딩 (텍스트→벡터) → 벡터 DB에 저장
[질문할 때 — 매번]
질문 → 임베딩 → 벡터 DB에서 유사한 문서 검색 → 검색 결과 + 질문을 LLM에 전달 → 답변
임베딩
텍스트를 숫자 배열(벡터)로 변환하는 것. 의미가 비슷한 텍스트는 벡터도 비슷하다.
"접근 제어" → [0.023, -0.041, 0.087, ...]
"RBAC" → [0.025, -0.039, 0.091, ...] # 비슷한 벡터
"날씨" → [-0.512, 0.331, -0.087, ...] # 다른 벡터
그래서 "접근 제어"로 검색하면 "RBAC" 문서가 나온다. 키워드가 달라도 의미가 같으면 찾아준다.
임베딩 방식
| OpenAI API | 로컬 모델 | |
|---|---|---|
| 비용 | 유료 | 무료 |
| 속도 | 네트워크 필요 | 즉시 |
| 품질 | 높음 (1536차원) | 괜찮음 (384차원) |
| 한국어 | 좋음 | 모델마다 다름 |
임베딩은 연산이 가벼워서 로컬로 충분하다. 문서 1만 개여도 CPU로 몇 분이면 끝남.
한국어 섞인 문서는 intfloat/multilingual-e5-small 같은 다국어 모델이 무난하다.
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("intfloat/multilingual-e5-small")
vector = model.encode("RBAC는 역할 기반 접근 제어로...")
벡터 DB
임베딩된 벡터를 저장하고 유사도 검색하는 DB. 일반 DB의 WHERE 대신 코사인 유사도로 검색한다.
- ChromaDB — 로컬, 설치 간단, 개인 프로젝트에 적합
- Pinecone — 클라우드, 대규모 서비스용
import chromadb
client = chromadb.PersistentClient(path="./chroma_data")
collection = client.get_or_create_collection("notes")
# 저장
collection.add(
documents=["RBAC는 역할 기반 접근 제어..."],
embeddings=[vector],
metadatas=[{"title": "RBAC", "tags": "security"}],
ids=["rbac_chunk_1"],
)
# 검색
results = collection.query(
query_embeddings=[query_vector],
n_results=3,
)
청킹
긴 문서를 적당한 크기로 잘라야 한다. 통째로 넣으면 검색 정확도가 떨어지고, 너무 잘게 자르면 맥락이 없어진다.
- 고정 크기 — 500자씩 자르기. 단순하지만 문맥이 끊길 수 있음
- 의미 단위 — 마크다운
##헤딩 기준으로 자르기. 구조화된 문서에 적합 - 오버랩 — 앞뒤 100자씩 겹치게 자르기. 문맥 유지
RAG에서 코드로 짜는 부분 vs AI가 하는 부분
| 단계 | 누가 하나 |
|---|---|
| 파일 읽기 | 내 코드 |
| frontmatter 파싱 | 내 코드 |
| 청킹 | 내 코드 |
| 텍스트 → 벡터 변환 | 임베딩 모델 (로컬 or API) |
| 벡터 저장/검색 | 내 코드 (ChromaDB 사용) |
| 검색 결과 보고 답변 생성 | LLM API |
AI가 하는 건 임베딩이랑 답변 생성 두 군데. 나머지는 전부 엔지니어가 설계하고 짠다.
새 문서 추가 시
RAG 자체는 읽기 전용 구조다. 새 문서가 추가되면 ingest 파이프라인을 다시 돌려야 한다.
새 노트 → 청킹 → 임베딩 → 벡터 DB에 추가
하지만 기존 문서가 이미 벡터로 있으니까, 새 문서와 기존 문서의 관계를 바로 파악할 수 있다. 이걸 활용하면 자동 태깅, 자동 연결 같은 Agent 기능을 만들 수 있다.