RAG
RAG (Retrieval-Augmented Generation)
LLM 의 지식은 학습 데이터에 한정돼요. 도메인 특화·사유 데이터를 LLM 이 알게 하려면 RAG 를 쓰거나, 파인튜닝을 하거나, 둘을 결합할 수 있어요. RAG 는 프롬프트를 LLM 에 보내기 전에 데이터에서 관련 정보 조각을 찾아 주입하는 방식이라 환각 확률을 줄여줘요.
출처: 공식문서
RAG 란
관련 정보는 다양한 정보 검색(information retrieval) 방법으로 찾을 수 있어요:
- 전문(키워드) 검색 — TF-IDF·BM25 같은 기법으로 쿼리 키워드를 문서 DB 와 대조.
- 벡터 검색(의미 검색) — 임베딩 모델로 문서를 벡터로 변환하고 쿼리 벡터와의 코사인 유사도 등으로 순위. 더 깊은 의미를 포착.
- 하이브리드 — 여러 검색 방법 결합(보통 전문+벡터)으로 효과 향상.
현재 이 페이지는 벡터 검색에 집중해요. 전문·하이브리드 검색은 현재 Azure AI Search 통합과 Elasticsearch(AzureAiSearchContentRetriever, ElasticsearchContentRetriever)에서 지원돼요.
RAG 단계
RAG 는 인덱싱(indexing) 과 검색(retrieval) 두 단계로 나뉘어요.
- 인덱싱: 문서의 검색 효율을 위한 전처리. 벡터 검색에선 문서 정리, 메타데이터 부가, 세그먼트 분할(청킹), 세그먼트 임베딩, 임베딩 스토어(벡터 DB) 저장 순서로 진행돼요. 보통 오프라인(크론잡 등)으로 수행해 엔드 유저가 기다리지 않게 해요. 다만 유저가 문서를 직접 업로드해야 하는 시나리오에서는 온라인으로 메인 애플리케이션의 일부가 되기도 해요.
- 검색: 보통 온라인으로, 유저 질문이 들어오면 쿼리를 임베딩하고 임베딩 스토어에서 유사도 검색을 해 관련 세그먼트를 프롬프트에 주입해 LLM 에 보내요.
LangChain4j 는 세 가지 RAG 종류를 제공해요: Easy RAG(가장 쉬운 시작), Naive RAG(벡터 검색 기반 기본 구현), Advanced RAG(쿼리 변환·다중 소스 검색·리랭킹이 가능한 모듈형 프레임워크).
Easy RAG
임베딩·벡터 스토어·문서 파싱·분할을 배우지 않아도 되는 가장 쉬운 시작점이에요. 품질은 맞춤 RAG 보다 낮겠지만 학습·PoC 에 적합하고, 나중에 Advanced RAG 로 자연스럽게 옮겨갈 수 있어요.
langchain4j-easy-rag의존성 추가 (dev.langchain4j:langchain4j-easy-rag:1.20.0-beta30).- 문서 로드:
FileSystemDocumentLoader.loadDocuments("/home/langchain4j/documentation")— 내부적으로 Apache Tika 가 문서 타입을 감지·파싱. 하위 디렉토리 재귀 로드는loadDocumentsRecursively, glob/regex 로 필터도 가능. - 전처리·저장: 임베딩 스토어(여기선 인메모리)에 저장.
InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
EmbeddingStoreIngestor.ingest(documents, embeddingStore);
내부적으로 EmbeddingStoreIngestor 가 각 Document 를 300 토큰 이하·30 토큰 오버랩의 TextSegment 로 나누고, SPI 로 로드한 임베딩 모델로 각 세그먼트를 Embedding 으로 변환해 저장해요. 기본 임베딩 모델은 bge-small-en-v1.5 로, 양자화 버전이 24MB 뿐이라 ONNX Runtime 으로 같은 JVM 프로세스 안에서 완전히 오프라인으로 실행할 수 있어요.
- AI Service 만들기:
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(chatModel)
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.contentRetriever(EmbeddingStoreContentRetriever.from(embeddingStore))
.build();
- 대화!
String answer = assistant.chat("How to do Easy RAG with LangChain4j?");
핵심 RAG API
- Document — PDF·웹페이지 같은 하나의 문서.
text(),metadata(),toTextSegment(),from(String, Metadata)등 유용 메서드. - Metadata — 문서의 이름·출처·최종 수정일·소유자 등 메타정보를 저장하는 key-value 맵. 프롬프트에 함께 포함해 LLM 이해를 돕거나, 검색 필터링에 쓰거나, 문서가 갱신됐을 때
EmbeddingStore의 해당 항목을 찾아 동기화하는 데 쓸 수 있어요.
Naive RAG
문서를 인제스트한 뒤 EmbeddingStoreContentRetriever 로 naive RAG 를 켤 수 있어요.
ContentRetriever contentRetriever = EmbeddingStoreContentRetriever.builder()
.embeddingStore(embeddingStore)
.embeddingModel(embeddingModel)
.maxResults(5)
.minScore(0.75)
.build();
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(model)
.contentRetriever(contentRetriever)
.build();
Advanced RAG
Advanced RAG 는 다음 핵심 컴포넌트로 구성돼요: QueryTransformer, QueryRouter, ContentRetriever, ContentAggregator, ContentInjector. 흐름은 이래요:
UserMessage가 생성되어Query로 변환QueryTransformer가Query를 하나 또는 여러 개로 변환QueryRouter가 각Query를 하나 이상의ContentRetriever로 라우팅- 각
ContentRetriever가 쿼리별 관련Content를 검색 ContentAggregator가 모든 검색 결과를 하나의 최종 순위 목록으로 결합- 이
Content목록을 원래UserMessage에 주입 - 마지막으로 원래 쿼리 + 주입된 관련 콘텐츠를 LLM 에 전송
RetrievalAugmentor 는 RAG 파이프라인의 진입점이에요. ChatMessage 를 다양한 소스에서 검색한 관련 Content 로 보강해요. AI Service 생성 시 .retrievalAugmentor(retrievalAugmentor) 로 지정하면, AI Service 가 호출될 때마다 현재 UserMessage 를 보강해요. 기본 구현으로 DefaultRetrievalAugmentor 를 제공해요.
검색 품질을 높이려면 쿼리 변환 기법을 써요:
DefaultQueryTransformer(통과),CompressingQueryTransformer(LLM 이 쿼리와 이전 대화를 독립 쿼리로 압축 — "그가 어디 살았지?" → "John Doe가 어디 살았지?"),ExpandingQueryTransformer(LLM 이 쿼리를 여러 개로 확장) 등.- 알려진 접근: 쿼리 압축, 쿼리 확장, 쿼리 재작성, 스텝백 프롬프팅, HyDE(가상 문서 임베딩).
더 알아보기
- AI Services — RAG-AI Service 구성
- 채팅 메모리 — 대화 상태와 RAG 연동
- Observability — RAG 컴포넌트 관측 리스너
- Deconstructing RAG: LangChain 블로그, arXiv:2312.10997
- Naive RAG 예제: Naive_RAG_Example.java