라즈베리파이/로컬 환경에서도 구동 가능한 실전형 RAG(Retrieval-Augmented Generation) 운영 백엔드
단순한 문서 검색을 넘어, 데이터 인입부터 하이브리드 검색, 다중 리랭킹, 그리고 답변 검증까지 RAG 파이프라인의 A to Z를 다루는 프로젝트입니다.
- PRD(무엇을/왜):
docs/PRD.md - SPEC(어떻게):
docs/SPEC.md - DB 스키마:
docs/db-schema.md - 인제션 파이프라인:
docs/ingestion-pipeline.md - 리트리벌(하이브리드/RRF/리랭크):
docs/retrieval.md
일반적인 튜토리얼 수준의 Naive RAG(단순 벡터 검색 후 LLM 주입)는 실무에서 검색 품질 저하(키워드 누락)와 환각(Hallucination) 이라는 치명적인 한계에 부딪힙니다.
DochiBot은 이러한 한계를 극복하기 위해 검색 최적화(Retrieval Optimization) 와 신뢰성 검증(Verification) 공정을 파이프라인에 깊숙이 통합했습니다.
┌─────────────────────────────────────────────────────────────────────────────┐
│ User Query (사용자 질의) │
└──────────────────────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 1. Retrieval (하이브리드 검색 & 융합) │
│ │
│ ┌───────────────────────┐ ┌───────────────────────┐ │
│ │ Dense Retrieval │ │ Sparse Retrieval │ │
│ │ (pgvector / 의미 검색)│ │ (tsvector / 키워드) │ │
│ └───────────┬───────────┘ └───────────┬───────────┘ │
│ │ │ │
│ └───────────────────────┬───────────────────────┘ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ RRF (Reciprocal Rank Fusion) 병합 │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────┬──────────────────────────────────────┘
│ (초기 Top-K 후보군)
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 2. Re-Ranking (문맥 기반 재정렬) │
│ │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ Reranker Router (질의/상황별 라우팅) │ │
│ └────────┬──────────────────────────┬─────────────────────────┬─────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
│ │ Cross-Encoder │ │ LLM-as-a-Judge │ │ Heuristic/BM25 │ │
│ │ (고정밀/딥러닝)│ │ (추론 능력 활용)│ │ (빠른 응답속도)│ │
│ └────────┬───────┘ └────────┬───────┘ └────────┬───────┘ │
│ │ │ │ │
│ └──────────────────────────┼─────────────────────────┘ │
└──────────────────────────────────────┬──────────────────────────────────────┘
│ (재정렬된 최종 컨텍스트)
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 3. Generation & Verification (환각 통제) │
│ │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ Pre-Generation Verify (문서 무관성 필터링) │ │
│ └───────────────────────────────────┬───────────────────────────────────┘ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ LLM Generation (명확한 출처 강제 프롬프팅) │ │
│ └───────────────────────────────────┬───────────────────────────────────┘ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ Post-Generation Verify (환각 및 근거 대조) │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ Final Answer (출처가 포함된 최종 답변) │
└─────────────────────────────────────────────────────────────────────────────┘
- 문제점: 기존 Dense Vector 검색은 문맥적 의미는 잘 파악하지만, 고유명사, 품번, 제품명 등 정확한 키워드(Exact Match) 검색에 취약합니다.
- 해결책: PostgreSQL의
pgvector(Dense)와tsvectorFTS(Sparse)를 동시 수행하는 하이브리드 검색을 구축했습니다. - 최적화: 성질이 다른 두 검색 결과의 점수를 공정하게 병합하기 위해 RRF (Reciprocal Rank Fusion) 알고리즘을 적용하여 누락 없는 풍부한 후보군을 구성합니다.
- 문제점: 1단계에서 찾은 Top-K 후보군 중 상위권 문서가 실제 질문에 대한 '정답'을 담고 있다는 보장이 없습니다. (Lost in the middle 현상 발생 가능성)
- 해결책: 검색된 문서들과 사용자 질의 간의 진짜 연관성을 깊게 계산하는 리랭킹(Re-ranking) 단계를 추가했습니다.
- 최적화: 시스템 리소스와 질문의 난이도에 따라
Cross-Encoder,LLM-as-a-Judge,Heuristic등 다양한 리랭커를 동적으로 선택하는 Reranker Router 구조를 설계하여 속도와 정확도의 트레이드오프를 제어합니다.
- 문제점: 검색된 문서 내용이 부족하거나 무관해도 LLM이 그럴싸한 거짓말(Hallucination)을 지어낼 수 있습니다.
- 해결책: LLM의 응답 생성 전후로 개입하는 자체 Verify 파이프라인을 구축했습니다.
- 최적화: 주어진 문서 목록이 질문에 답할 수 있는지 사전 평가하고, 최종 답변에는 사용자가 직접 정보의 출처를 눈으로 검증할 수 있도록 원문 스니펫(Snippet)과 메타데이터(Citation)를 강제로 포함시킵니다.
- Presigned URL 기반 안전한 업로드: 대용량 파일 업로드 시 백엔드 병목을 없애기 위해 클라이언트가 S3(SeaweedFS)로 직접 업로드하는 패턴을 채택했습니다.
- Ingestion Job Worker: 무거운 문서 청킹(Chunking) 및 임베딩 로직을 메인 스레드에서 분리하여 비동기 Job으로 관리합니다. 실패 시 재처리(Retry) 및 상태 추적(Queued, Running, Failed)이 가능한 실무적 파이프라인을 구축했습니다.
- 로컬 LLM 지향: 라즈베리파이 등 제한된 엣지 환경에서도 외부 API 의존 없이 동작할 수 있도록 Ollama 기반으로 아키텍처를 구성했습니다.
- 100% Non-Blocking I/O: 적은 스레드로 높은 동시성을 확보하기 위해 Spring WebFlux와 Kotlin Coroutines 기반의 Reactive Programming을 적용했습니다.
- Backend: JDK 21, Kotlin 2.0, Spring Boot 3.4 (WebFlux) + Coroutines, Spring AI
- Database: PostgreSQL 17 (R2DBC) + Flyway (JDBC)
- Search Engine: PostgreSQL + pgvector (Dense) + FTS(tsvector, GIN) (Sparse)
- Cache: Redis 7
- AI/LLM: Ollama (Chat/Embedding)
- Storage: SeaweedFS (S3 Gateway; S3 호환, Presigned URL 용도)
# 필요 시 docker-dev/.env 값을 수정하세요.
docker compose -f docker-dev/compose.yaml --env-file docker-dev/.env up -d./gradlew clean build
./gradlew bootRunAPI 컨테이너까지 함께 올리려면 api profile을 사용합니다.
docker compose -f docker-dev/compose.yaml --env-file docker-dev/.env --profile api up -d --build- 로컬 프로필의 채팅 LLM은 기본적으로 OpenAI 호환 제공자를 사용하며, 기본 엔드포인트는 NVIDIA NIM입니다.
NVIDIA_API_KEY만 채우면 바로 사용할 수 있습니다. - 필요하면
AI_CHAT_PROVIDER=ollama로 로컬 Ollama 채팅 모델로 되돌릴 수 있습니다. - 문서 업로드는 현재
PDF(.pdf)와Markdown(.md, .markdown)만 지원합니다. - 임베딩 모델은
OLLAMA_EMBEDDING_MODEL로 바꿀 수 있습니다(기본:bge-m3).bge-m3는 100개 이상의 언어를 지원하는 multilingual 임베딩이라 한글 질문과 영어 섹션명/기술명이 섞인 RAG에 더 적합합니다.
- API 상세:
docs/phase1-api.md - DB 스키마:
docs/db-schema.md - S3/SeaweedFS:
docs/s3-config.md - 인증(JWT):
docs/auth-jwt.md - 대화 메모리 전략:
docs/chat-memory-strategy.md