Spring AI - RAG

RAG (Retrieval-Augmented Generation) is a powerful technique that combines retrieval-based methods with generative models to enhance the capabilities of AI applications. RAG allows AI systems to access and utilize external knowledge sources, such as databases, documents, or APIs, to generate more accurate and contextually relevant responses.

The work is inspired by the Java RAG example created by Dan Vega, which demonstrates how to implement RAG using Java and Spring Boot. The example showcases how to ingest a PDF document, store it in a vector database, and use it to provide more informed responses in a chat application.

Maven Dependency

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-spring-boot-docker-compose</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pdf-document-reader</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-advisors-vector-store</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai-sdk</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-pgvector</artifactId>
</dependency>

Configuration

Update properties file with the following configuration:

1
2
3
4
5
OPENAI_API_KEY=${OPENAI_API_KEY}
spring.ai.openai.chat.model=gpt-5
spring.ai.vectorstore.pgvector.initialize-schema=true

logging.level.org.apache.pdfbox.pdmodel.font.*=ERROR

compose.yml - file should be already populated by start.spring.io, but make sure it has the following content:

1
2
3
4
5
6
7
8
9
10
11
services:
pgvector:
image: 'pgvector/pgvector:pg16'
environment:
- 'POSTGRES_DB=mydatabase'
- 'POSTGRES_PASSWORD=secret'
- 'POSTGRES_USER=myuser'
labels:
- "org.springframework.boot.service-connection=postgres"
ports:
- '5432:5432'

IngestionService

Use IngestionService to read the PDF document, split it into chunks, and store it in the vector store for later retrieval.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Component
@Slf4j
public class IngestionService implements CommandLineRunner {
private final VectorStore vectorStore;

@Value("classpath:/docs/Some_PDF_Report.pdf")
private Resource reportPdf;

public IngestionService(VectorStore vectorStore) {
this.vectorStore = vectorStore;
}

@Override
public void run(String... args) throws Exception {
// use PagePdfDocumentReader if ParagraphPdfDocumentReader is not working
var pdfReader = new PagePdfDocumentReader(reportPdf);
TextSplitter textSplitter = new TokenTextSplitter();
vectorStore.accept(textSplitter.apply(pdfReader.get()));
log.info("Document ingested successfully");
}
}

Chat Controller

Set up the ChatClient with a QuestionAnswerAdvisor that utilizes the vector store for retrieval. This allows the chat client to provide more accurate and contextually relevant responses based on the ingested document.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@RestController
public class ChatController {
private final ChatClient chatClient;

public ChatController(ChatClient.Builder builder, VectorStore vectorStore) {
this.chatClient = builder
.defaultAdvisors(new QuestionAnswerAdvisor(vectorStore))
.build();
}

@GetMapping("/")
public String chat() {
return chatClient.prompt()
.user("Your question here")
.call()
.content();
}
}

This example adds one PDF document to the vector store, but we can add multiple documents to create a more comprehensive knowledge base for the chat client to draw from.

References:

• GitHub Repository: https://github.com/danvega/java-rag
• Spring Initializer: https://start.spring.io/