본문 바로가기
Spring/AI

Spring AI : Retrieval Augmented Generation 번역

by 自强不息 개발자 2025. 3. 16.
반응형

주의

이 번역본은 현재의 Spring AI 모듈의 개발 상황에 따른 문서이며 현재 활발히 개발중인 모듈이므로 사용방법이나 컨셉이 변경될 수 있다.


Retrieval Augmented Generation

검색 증강 생성은 긴 형식의 콘텐츠, 사실적 정확성과 맥락 인식에 어려움을 겪는 LLM의 한계를 극복하는데 유용한 기술이다.

Spring AI는 모듈식 아키텍쳐를 제공하여 사용자가 커스텀 RAG 플로우를 구축하거나 Advisor API를 활용하여 즉시 사용 가능한 RAG 플로우를 사용할 수 있도록 지원한다.

Advisors

Spring AI는 Advisor API를 활용하여 일반적인 흐름에 대한 즉시 사용 가능한 지원을 제공한다.

QuestionAnswerAdvisor

벡터 데이터베이스는 AI 모델이 인식하지 못하는 데이터를 데이터를 저장한다. 사용자의 질문이 AI 모델에 전송되면. QuestionAnswerAdvisor는 사용자의 질문과 관련된 문서를 벡터 데이터베이스에서 질의한다.

벡터 데이터베이스에서 받은 응답은 AI 모델이 응답을 생성할때 필요한 컨텍스트로 user text에 추가된다.

데티어를 VectorStore에 이미 로드 해놨다고 했을때, QuestionAnswerAdvisor 인스턴스를 ChatClient에 제공함으로 RAG를 수행할 수 있다.

ChatResponse response = ChatClient.builder(chatModel)
        .build().prompt()
        .advisors(new QuestionAnswerAdvisor(vectorStore))
        .user(userText)
        .call()
        .chatResponse();

이 예제에서 QuestionAnswerAdvisor는 벡터데이터베이스의 모든 문서에 대해서 유사성 검색을 수행한다. 검색되는 문서의 유형을 제한하려면, SearchRequest를 이용해 SQL과 유사한 필터 표현식을 사용한다.

이 필터 표현식은 QuestionAnswerAdvisor를 생성할때 설정할 수 있으며, 모든 ChatClient 요청에 대해 적용되거나 요청별로 런타임에 제공될 수 있다.

Dynamic Filter Expression

advisor context 파라미터인 FILTER_EXPRESSION를 이용해서 런타임에 SearchRequest 를 업데이트할 수 있다.

ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultAdvisors(new QuestionAnswerAdvisor(vectorStore, SearchRequest.builder().build()))
    .build();

// Update filter expression at runtime
String content = this.chatClient.prompt()
    .user("Please answer my question XYZ")
    .advisors(a -> a.param(QuestionAnswerAdvisor.FILTER_EXPRESSION, "type == 'Spring'"))
    .call()
    .content();

FILTER_EXPRESSION 파라미터는 제공된 표현식을 기반으로 검색 결과를 동적으로 필터링할 수 있게 해준다.

RetrievalAugmentationAdvisor (Incubating)

Spring AI는 사용자가 자신만의 RAG 흐름을 구축하는데 사용할 수 있는 RAG 모듈 라이브러를 제공한다. RetrievalAugmentationAdvisor는 모듈화된 아키텍쳐를 기반으로 가장 일반적인 RAG 플로우를 위한 즉시 사용 가능한 구현을 제공하는 실험적인 Advisor이다.

RetrievalAugmentationAdvisor는 실험적인 기능이라 향후 릴리즈에서 변경될 수 있음

Sequential RAG Flows

Naive RAG

Advisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()
        .documentRetriever(VectorStoreDocumentRetriever.builder()
                .similarityThreshold(0.50)
                .vectorStore(vectorStore)
                .build())
        .build();

String answer = chatClient.prompt()
        .advisors(retrievalAugmentationAdvisor)
        .user(question)
        .call()
        .content();

기본적으로 RetrievalAugmentationAdvisor는 검색된 컨텍스트가 비어있는것을 허용하지 않는다. 비어있는 경우, 모델은 사용자의 쿼리에 대한 응답을 하지 않도록 지시한다. 비어있는 컨텍스트를 허용하려면 아래와 같이 설정하면 된다.

Advisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()
        .documentRetriever(VectorStoreDocumentRetriever.builder()
                .similarityThreshold(0.50)
                .vectorStore(vectorStore)
                .build())
        .queryAugmenter(ContextualQueryAugmenter.builder()
                .allowEmptyContext(true)
                .build())
        .build();

String answer = chatClient.prompt()
        .advisors(retrievalAugmentationAdvisor)
        .user(question)
        .call()
        .content();

Advanced RAG

Advisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()
        .queryTransformer(RewriteQueryTransformer.builder()
                .chatClientBuilder(chatClientBuilder.build().mutate())
                .build())
        .documentRetriever(VectorStoreDocumentRetriever.builder()
                .similarityThreshold(0.50)
                .vectorStore(vectorStore)
                .build())
        .build();

String answer = chatClient.prompt()
        .advisors(retrievalAugmentationAdvisor)
        .user(question)
        .call()
        .content();

모듈

SpringAI는 "Modular RAG: Transforming RAG Systems into LEGO-like Reconfigurable Frameworks".라는 논문에서 설며된 모듈화 개념을 통해 모듈화된 RAG 아키텍쳐를 구현한다.

⚠️ 모듈화된 RAG는 실험적 기능이며 향후 릴리즈에서 변경될 수 있다.

Pre-Retrieval (사전 검색)

사전 검색 모듈은 최상의 검색 결과를 위해 상요자 쿼리를 처리하는 역할을 한다.

Query Transformation 쿼리 변환

입력된 쿼리를 변환하여 검색 작업에 더 효과적으로 만들기 위한 구성요소로, 잘못 형성된 쿼리, 모호한 용어, 복잡한 어휘 또는 지원되지 않는 언어와 같은 문제를 해결한다.

CompressionQueryTransformer (압축 쿼리 변환기)

CompressionQueryTransformer는 LLM을 이용해 대화 이력과 후속 쿼리를 독립적인 쿼리로 압축하여 대화의 본질을 포착한다.

이 변환기는 대화 기록이 길고 후속 쿼리가 대화 맥락과 관련이 있을 때 유용하다.

Query query = Query.builder()
        .text("And what is its second largest city?")
        .history(new UserMessage("What is the capital of Denmark?"),
                new AssistantMessage("Copenhagen is the capital of Denmark."))
        .build();

QueryTransformer queryTransformer = CompressionQueryTransformer.builder()
        .chatClientBuilder(chatClientBuilder)
        .build();

Query transformedQuery = queryTransformer.transform(query);

이 컴포넌트에서 사용되는 프롬프트는 빌더에서 사용할 수 있는 promptTemplate() 메서드를 통해 커스텀할 수 있다.

RewriteQueryTransformer ( 쿼리 재작성 변환기)

RewriteQueryTransformer는 벡터 저장소나 웹 검색 엔진과 같은 타겟 시스템에 쿼리할 때 더 나은 결과를 제공하기 위해 사용자 쿼리를 재작성하는 데 LLM을 사용한다.

이 변환기는 사용자 쿼리가 장황하거나, 모호하거나, 검색 결과의 품질에 영향을 미칠 수 있는 관련 없는 정보를 포함할 때 유용하다.

Query query = new Query("I'm studying machine learning. What is an LLM?");

QueryTransformer queryTransformer = RewriteQueryTransformer.builder()
        .chatClientBuilder(chatClientBuilder)
        .build();

Query transformedQuery = queryTransformer.transform(query);

이 컴포넌트에서 사용되는 프롬프트는 빌더에서 사용할 수 있는 promptTemplate() 메서드를 통해 커스텀할 수 있다.

TranslationQueryTransformer (번역 쿼리 변환기)

TranslationQueryTransformer는 문서 임베딩을 생성하는데 사용된 임베딩 모델이 지원하는 타겟 언어로 쿼리를 번역하기 위해 LLM을 사용한다. 쿼리가 이미 타켓언어로 되어 있으면 변경하지 않고 반환한다. 쿼리의 언어가 알려지지 않은 경우에도 변경되지 않고 반환된다.

이 변환기는 임베딩 모델이 특정 언어로 훈련되고 사용자 쿼리가 다른 언어일 때 유용하다.

Query query = new Query("Hvad er Danmarks hovedstad?");

QueryTransformer queryTransformer = TranslationQueryTransformer.builder()
        .chatClientBuilder(chatClientBuilder)
        .targetLanguage("english")
        .build();

Query transformedQuery = queryTransformer.transform(query);

이 컴포넌트에서 사용되는 프롬프트는 빌더에서 사용할 수 있는 promptTemplate() 메서드를 통해 커스텀할 수 있다.

Query Expansion (쿼리 확장)

입력 쿼리를 쿼리 리스트로 확장하는 컴포넌트로, 잘못 형성된 쿼리와 같은 문제를 해결하기 위해 대체 쿼리 형식을 제공하거나 복잡한 문제를 더 간단한 서브쿼리로 분해한다.

MultiQueryExpander (다중 쿼리 확장기)

MultiQueryExpander는 쿼리를 여러 개의 의미적으로 다양한 변형으로 확장하기 위해 LLM을 사용하여 다양한 관점을 포착하고, 추가적인 맥락 정보를 검색하는 데 유용하며, 관련 결과를 찾을 확률을 높인다.

MultiQueryExpander queryExpander = MultiQueryExpander.builder()
    .chatClientBuilder(chatClientBuilder)
    .numberOfQueries(3)
    .build();
List<Query> queries = expander.expand(new Query("How to run a Spring Boot app?"));

기본적으로 MultiQueryExpander는 확장된 쿼리 목록에 원래 쿼리를 포함한다. 이 동작은 빌더의 includeOriginal 메서드를 통해 비활성화 할 수 있다.

MultiQueryExpander queryExpander = MultiQueryExpander.builder()
    .chatClientBuilder(chatClientBuilder)
    .includeOriginal(false)
    .build();

이 컴포넌트에서 사용되는 프롬프트는 빌더에서 사용할 수 있는 promptTemplate() 메서드를 통해 커스텀할 수 있다.

Retrieval (검색)

검색 모듈은 벡터 저장소와 같은 데이터 시스템에 쿼리를 수행하고 가장 관련성이 높은 문서를 검색하는 역할 을 한다.

Document Search (문서 검색)

기반 데이터 소스(검색엔진, 벡터저장소, 데이터베이스, 또는 지식그래프)에서 Documents를 검색하는 책임이 있는 컴포넌트이다.

VectorStoreDocumentRetriever (벡터 저장소 문서 검색기)

VectorStoreDocumentRetriever는 입력된 쿼리와 의미적으로 유사한 문서를 벡터 저장소에서 검색한다. 이는 메타데이터, 유사성 임계값, 상위 k개 결과를 기반으로 필터링을 지원한다.

DocumentRetriever retriever = VectorStoreDocumentRetriever.builder()
    .vectorStore(vectorStore)
    .similarityThreshold(0.73)
    .topK(5)
    .filterExpression(new FilterExpressionBuilder()
        .eq("genre", "fairytale")
        .build())
    .build();
List<Document> documents = retriever.retrieve(new Query("What is the main character of the story?"));

필터 표현식은 정적이거나 동적일 수 있다. 동적 표현식의 경우 Supplier 를 전달 할 수 있다.

DocumentRetriever retriever = VectorStoreDocumentRetriever.builder()
    .vectorStore(vectorStore)
    .filterExpression(() -> new FilterExpressionBuilder()
        .eq("tenant", TenantContextHolder.getTenantIdentifier())
        .build())
    .build();
List<Document> documents = retriever.retrieve(new Query("What are the KPIs for the next semester?"));

Document Join (문서 조인)

여러 쿼리와 여러 데이터 소스에서 검색된 문서를 하나의 문서 컬렉션으로 결합하는 컴포넌트이다. 결합과정의 한 부분으로, 중복 문서와 상호 순위 전략(reciprocal ranking strategies)도 처리 할 수 있다.

ConcatenationDocumentJoiner (연결 문서 결합기)

ConcatenationDocumentJoiner는 여러 쿼리와 여러 데이터소스에서 검색된 문서를 결합하여 단일 문서 모음으로 연결한다. 중복 문서의 경우 첫번째만 유지된다. 각 문서의 점수는 그대로 유지된다.

Map<Query, List<List<Document>>> documentsForQuery = ...
DocumentJoiner documentJoiner = new ConcatenationDocumentJoiner();
List<Document> documents = documentJoiner.join(documentsForQuery);

Post-Retrieval (검색 후 처리)

Post-Retrieval 모듈은 검색된 문서를 처리하여 가능한 최상의 생성결과를 달성하는 역할을 한다.

Document Ranking (문서 순위 매기기)

쿼리에 대한 관련성을 기반으로 문서를 정렬하고 순위를 매기는 컴포넌트로, 가장 연관성이 높은 문서를 목록 상단으로 가져오며, lost-in-the-middle 과 같은 문제를 해결한다.

DocumentSelector와 다르게, 이 컴포넌트는 목록에서 전체 문서를 제거하지 않고, 대신 목록의 순서/점수를 변경한다. DocumentCompressor와 달리 이 컴포넌트는 문서의 내용을 변경하지는 않는다.

Document Selection (문서 선택)

검색된 문서 목록에서 관련 없는 또는 중복된 문서를 제거하는 컴포넌트로, lost-in-the-middle 과 모델의 컨텍스트 길이 제한과 같은 문제를 해결한다.

DocumentRanker와는 달리 이 컴포넌트는 목록에서 문서의 순서/점수는 변경하지 않고, 관련 없는 또는 중복된 문서를 제거한다. DocumentCompressor와 달리 문서의 내용을 변경하지 않고, 오히려 문서 전체를 제거한다.

Document Compression (문서 압축)

각 문서의 내용을 압축하여 검색된 정보의 노이즈와 중복 구성을 줄이는 컴포넌트로, lost-in-middle 과 모델의 컨텍스트 길이 제한과 같은 문제를 해결한다.

DocumentSelector와 달리 목록에서 전체 문서를 제거하지 않고 문서의 내용을 변경한다. DocumentRanker와 달리 목록에서 문서의 순서/점수를 변경하지 않는다.

Generation (생성)

생성 모듈은 사용자의 쿼리와 검색된 문서를 기반으로 최종 응답을 생성하는 역할을 한다.

Query Augmentation (쿼리 증강)

입력 쿼리에 추가 데이터를 보강하는 컴포넌트로, LLM이 사용자 쿼리에 답변하는데 필요한 맥락을 제공하는데 유용하다.

ContextualQueryAugmenter (맥락 기반 쿼리 증강기)

ContextualQueryAugmenter는 제공된 문서의 내용에서 맥락 데이터를 사용자 쿼리에 추가한다.

QueryAugmenter queryAugmenter = ContextualQueryAugmenter.builder().build();

기본으로 ContextualQueryAugmenter는 컨텍스트가 비어있는것을 허용하지 않는다. 이 경우, 모델에게 사용자 쿼리에 답변하지 않도록 지시된다.

모델이 검색된 컨텍스트가 비어있을 때도 응답을 생성할 수 있도록 하려면 allowEmptyContext 옵션을 활성화하면 된다.

QueryAugmenter queryAugmenter = ContextualQueryAugmenter.builder()
        .allowEmptyContext(true)
        .build();

이 컴포넌트에서 사용되는 프롬프트는 빌더에서 사용할 수 있는 promptTemplate() 와 emptyContextPromptTemplate() 메서드를 이용해 커스텀할 수 있다.

출처

Spring AI / Retrieval Augmented Generation
https://docs.spring.io/spring-ai/reference/1.0/api/retrieval-augmented-generation.html

반응형

'Spring > AI' 카테고리의 다른 글

RestClient로 OpenAi API 사용하기  (2) 2025.05.21