Toy Project에서 RAG + Rerank 적용기
RAG + Rerank 기반 시스템 설계 및 적용기
들어가며..
프로젝트 링크: lua-monologue (GitHub)
요즘 LLM이 안 쓰이는 곳이 어디 있을까요, 개발에서도 보안에서도 LLM과 결합되는 방식이 상당히 많아졌습니다.
그래서 LLM을 써보기만 했지 관련해서 Toy project라도 한번 개발해봐야겠다 해서 RAG를 적용한 작은 프로젝트를 하나 했었습니다.
오늘은 이 프로젝트와 더불어 RAG/Rerank에 대해서 알아보도록 하겠습니다.
※저도 사실 잘 몰라서 틀린 정보가 많을 수 있습니다 ㄷㄷ
프로젝트에서의 RAG 구조 개요
lua-monologue 프로젝트는 사용자의 일기나 메시지를 DB에 저장하고, 이를 기반으로
의미 기반 검색(RAG)과 LLM 응답 생성을 결합한 구조입니다.
사용자의 기억을 AI가 보완해주는 형태로, 개인화된 질문 응답 흐름을 구축하는 것을 목표로 하였습니다.
[User Input]
↓
[Sentence Embedding (InstructorXL)]
↓
[pgvector Similarity Search]
↓
[Retrieved Messages (top-k)]
↓
[LLM Prompt (context 포함)]
↓
[LLM Response]
- Embedding 모델:
InstructorXL(sentence-transformers) - Embedding 방식: mean pooling + CLS token 병렬 저장
- DB: PostgreSQL +
pgvector확장 사용
pgvector 기반 유사도 검색
PostgreSQL에서 pgvector 확장을 통해 임베딩 벡터 검색이 가능합니다.
예시 쿼리:
SELECT id, content
FROM messages
ORDER BY embedding <#> '[임베딩 벡터]'
LIMIT 5;
<#>는 cosine distance 연산자, 그 외에도<->Euclidean distance 가까운 순 (좌표 공간에서 물리적으로 가까운 정도)<#>Cosine distance 가까운 순 (방향이 가까운 정도)<=>Inner product (dot product) 큰 순(같은 방향 + 길이가 클수록)embedding,embedding_cls모두 pgvector 타입으로 저장
이를 통해 의미 기반 검색을 빠르게 수행할 수 있습니다.
문장 임베딩 방식: Mean Pooling vs CLS Token
- Mean Pooling: 모든 토큰 벡터 평균 → 안정적, 단어 기반 의미 보존
- CLS Token: 문장 맨 앞 특수 토큰 출력 → 문장 전체 대표 요약
CLS를 좀더 살펴보면 BERT 계열의 모델에서 문장 전체를 대표하는 Classification 을 위한 특수 요약 토큰으로, 만약
예시 문장이 있다면
[CLS] 예시 문장 [SEP]로 [CLS]는 문장 전체 대표, [SEP]는 문장 구분용으로 그리고 "예시 문장"에 대한 각각의 토큰 [예시], [문장] 이런 식으로 이루어지게 됩니다.
| 방식 | 장점 | 약점 |
|---|---|---|
| Mean Pooling | 단어 기반 정보 풍부, robust | 문장 구조 반영 적음 |
| CLS Token | 요약된 문장 의미 제공 / 보통 속도 빠름 | 학습 상태 따라 품질 편차 |
보통은 Mean Pooling 방식을 많이 사용하는 것 같지만 lua-monologue 프로젝트에서는 두 가지를 모두 저장해, 향후 질문 유형/문맥 유형에 따라 다르게 활용할 수 있도록 설계하였습니다.
입력 문장 → InstructorXL 모델 → mean 벡터 & CLS 벡터 → pgvector에 저장
현재 Rerank 구조 및 향후(?!) 계획
※ 사실 추가로 더 고도화를 할지 모르겠음 ㅎㅎ
현재는 1차 유사도 검색 결과를 바로 LLM에 전달하지만, 향후에는 Reranker 모델을 도입하여 응답 품질을 높일 계획입니다.
Rerank 전략 구성(계획만)
- top-10 유사도 검색
- LLM 또는 BERT 기반 Reranker로 재정렬
- 시간순/감정/주제 가중치 반영
[User Input]
↓
[Vector Search → top-10]
↓
[Reranker (LLM or BERT)]
↓
[최종 top-k 문맥]
↓
[LLM Prompt 생성]
이렇게 하면 유사하지만 덜 관련된 응답을 필터링하고, 실제 질문에 더 관련성 높은 맥락을 제공할 수 있지 않을까 싶습니다.
향후 계획 및 마무리
그 외에도 추가로 아래와 같이 RAG 관련해서 더 해보고 싶은 계획들은 있지만 적어만 보고 마무리 하도록 하겠습니다.
- 시간 기반 가중치 및 감정 기반 문맥 우선순위 적용
- hybrid RAG: Sparse + Dense 통합 검색
먼저 시간에 따라 사용자의 성향이 충분히 달라질 수 있어 최신 정보가 훨씬 중요하게 작용할 수 있다고 생각되고 감정에 따라서도 성향이 달라질 수 있다고도 생각되어 위에 따른 가중치나 우선순위 조정도 가능하면 해보고 싶었습니다.
Spare는 아래에서 간단하게 설명하겠지만 보통 단어, 키워드 기반 유사도 검색에서 많이 사용됩니다.
그런데 예전에 자동분류 등을 위해서 유사도 판별을 했을 때 의외로 Keyword 기반 Sparse가 더 좋은 성능을 낸 케이스도 존재가 하여
이 둘을 hybrid로 사용하는 것도 충분히 고려해볼만 하다고 생각했습니다.
(코드등이 담겨있는 복잡한 Report에 대한 유사도 비교에선 코드가 있어서 그런지 단어 기반 유사도가 잘 찾아준 Report 유형도 존재했습니다.
아무래도 코드나 전문 용어에서 의미기반을 추출하긴 힘들었어서 같은 코드, 같은 파일 위치 등을 잘 파악할 수 있지 않았을까 싶습니다.)
마무리로 해당 프로젝트는 완성형이 아니며, 실험과 학습의 목적이 컸습니다. RAG 구조를 처음 설계하거나 Embedding에 대해 고민 중이라면, 저와 비슷한 고민에 대해서 같이 생각해볼 수 있는 도움이 되었길 바라며 오늘 글도 마무리하도록 하겠습니다.
참고 (Embedding 전략 비교: Sparse vs Dense)
| 구분 | Sparse Embedding | Dense Embedding |
|---|---|---|
| 기반 | 단어 빈도 (TF-IDF, BM25) | 딥러닝 모델 (BERT, Instructor 등) |
| 차원 | 수천~수만 차원 (희소) | 고정 저차원 (예: 768, 1024) |
| 장점 | 정확한 키워드 매칭 | 의미 기반 유사도 |
| 단점 | 의미 유사어 약함 | 오류 가능성, 해석 어려움 |