SpringAI 를 활용하여 문서를 기반으로 답변하는 챗봇을 만드는 과정 정리.
RAG는 크게 두 단계로 나뉘어져 있다.
Spring-AI는 이걸 아주 간단하게 구성할 수 있도록 DocumentRetriever, EmbeddingModel, VectorStore 같은 컴포넌트 및 상위 인터페이스를 제공한다.

Docker 를 통해 설치하면 편리하다.docker-compose.yml
version: '3.8'
services:
pgvector-db:
image: pgvector/pgvector:pg16 # PostgreSQL with pgvector support
container_name: pgvector-db
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
ports:
- "5432:5432"
volumes:
- ./postgres/data:/var/lib/postgresql/data
repositories {
mavenCentral()
}
ext {
set('springAiVersion', "1.0.0")
}
dependencies {
implementation 'org.springframework.ai:spring-ai-advisors-vector-store'
implementation 'org.springframework.ai:spring-ai-starter-model-ollama'
implementation 'org.springframework.ai:spring-ai-starter-vector-store-pgvector'
}
dependencyManagement {
imports {
mavenBom "org.springframework.ai:spring-ai-bom:${springAiVersion}"
}
}
spring:
datasource:
url: jdbc:postgresql://localhost:5432/postgres
username: postgres
password: postgres
ai:
ollama:
base-url: http://localhost:11434 # Ollama 서버 URL
chat:
options:
model: gemma3:4b # 사용할 chat 모델
embedding:
options:
model: # RAG 위한 임베딩 모델
vectorstore:
pgvector:
index-type: HNSW
distance-type: COSINE_DISTANCE
# dimensions: 1536 <- 없으면 각 임베딩 모델에 맞는 적절한 dimensions 로 적용됨
max-document-batch-size: 10000 # Optional: 각 배치 마다 처리될 수 있는 최대 document 수
SpringAI 에서는 검색 증강 생성 패턴을 구현하기 위한, ETL(Extract, Transform, and Load) 데이터 엔지니어링과 관련된 컴포넌트 및 인터페이스를 제공한다.
- 문서 데이터를 로드하여 가공 후 Vector DB 에 저장하는 흐름
위 그림과 같이 ETL 을 위한 인터페이스는 크게 DocumentReader, DocumentTransformer, DocumentWriter 가 존재하고, 아래의 클래스 다이어그램은 해당 인터페이스의 구현이 나와 있다.

각 구현체를 통해, 문서의 특성 및 상황을 고려하여 ETL 파이프라인을 구성할 수 있다. 또한, 각 문서 특성에 맞는 의존성을 설치하여야 한다. 예를 들어, pdf 문서를 로드하기 위해서는 아래의 의존성을 추가하면 된다.
dependencies {
implementation 'org.springframework.ai:spring-ai-pdf-document-reader'
}
@Component
public class MyPagePdfDocumentReader {
private final VectorStore vectorStore;
void getDocsFromPdf() {
PagePdfDocumentReader pdfReader = new PagePdfDocumentReader("classpath:/sample1.pdf",
PdfDocumentReaderConfig.builder()
.withPageTopMargin(0)
.withPageExtractedTextFormatter(ExtractedTextFormatter.builder()
.withNumberOfTopTextLinesToDelete(0)
.build())
.withPagesPerDocument(1)
.build());
List<Document> docs = pdfReader.get();
vectorStore.add(docs);
}
}
SpringAI 는 RAG 패턴을 쉽게 적용하기 위해 Advisor API를 사용한다.
RAG 패턴은 사용자 프롬프트와 문서의 유사성을 평가하고 비슷한 문서 chunk 를 조합하여 사용자 프롬프트에 추가하는 방식이다. 이를 위해 SearchRequest 를 사용하여 문서 필터 및 유사도 옵션을 설정 가능하다.
ChatResponse response = ChatClient.builder(chatModel)
.build().prompt()
.advisors(new QuestionAnswerAdvisor(vectorStore))
.user(userText)
.call()
.chatResponse();
SearchRequest 를 사용유사도 임계값을 0.8 으로 설정하고 검색된 상위 6개 결과를 반환
var qaAdvisor = QuestionAnswerAdvisor.builder(vectorStore) .searchRequest(SearchRequest.builder().similarityThreshold(0.8d).topK(6).build()) .build();
ChatClient 의 기본 템플릿을 구성하여 Bean 을 설정할 수도 있다.
예를 들어, QuestionAnswerAdvisor 를 기본 Advisor 로 사용하려면 아래와 같이 작성할 수 있다.
@Bean
ChatClient chatClient(ChatClient.Builder builder) {
return builder
.defaultSystem(persona, StandardCharsets.UTF_8) //llm 의 페르소나, 기본 지시사항을 설정
.defaultAdvisors(QuestionAnswerAdvisor
.builder(vectorStore)
.searchRequest(SearchRequest.builder().build())
.build())
.build();
}
위와 같이 ChatClient 에 Advisor 를 추가하여 쉽게 QA 챗봇을 구현 가능하다.
지금까지 문서를 기반으로 답변하는 챗봇, 즉 RAG 시스템을 간단하게 구성해봤다. 실제 서비스에 적용하려면 문서 전처리, 인덱싱 전략, 프롬프트 튜닝 등이 더 필요하지만, 이 정도만 해도 기본 기능은 충분히 구현 가능하다.
다음 글에서는 각 문서 특성에 맞게 문서 처리 파이프라인을 고도화하고, VectorStore 에 문서 chunk 를 저장할 때 Metadata 추가하는 내용을 정리할 것이다.