Xây dựng Enterprise AI với Spring AI và Java: Hướng dẫn về RAG và Vector Database

AI tutorial - IT technology blog
AI tutorial - IT technology blog

Tích hợp AI cho doanh nghiệp: Thế tiến thoái lưỡng nan của cầu nối Python

Gần như mọi bài hướng dẫn về GenAI đều bắt đầu bằng Python. Khi đội ngũ kỹ sư của chúng tôi phải bổ sung tính năng tìm kiếm tài liệu nội bộ và truy vấn trò chuyện vào nền tảng ERP cốt lõi, chúng tôi đã làm theo đúng khuôn mẫu đó. Chúng tôi triển khai một microservice FastAPI chạy LangChain song song với backend Spring Boot 3. Nhưng chỉ trong vòng ba tuần, các vết nứt bắt đầu lộ rõ.

Mỗi truy vấn đều đòi hỏi sự phối hợp phức tạp giữa hai runtime. Các service Java phải tuần tự hóa (serialize) đồ thị entity JPA thành JSON, truyền qua HTTP, đợi Python tạo embedding và giải tuần tự hóa (deserialize) payload. Độ trễ P99 luôn dao động quanh mức 820ms.

Thêm vào đó, việc duy trì tính đồng bộ của schema DTO giữa hai codebase riêng biệt đã tạo ra những cơn đau đầu liên miên khi triển khai. Chúng tôi phải duy trì hai pipeline CI/CD riêng biệt, theo dõi các chỉ số Prometheus rời rạc và chuyển tiếp header xác thực JWT qua thêm một bước trung gian. Thay vì coi AI là một phần mở rộng tự nhiên của ứng dụng, chúng tôi lại tạo ra một ốc đảo vận hành biệt lập.

Spring AI xóa bỏ ranh giới này. Framework này mang đến các thành phần nguyên thủy (primitives) thuần Java cho việc điều phối LLM, nạp tài liệu và thao tác với vector store trực tiếp ngay trong Spring framework.

So sánh cách tiếp cận: Spring AI Native vs. Python Sidecar đa ngôn ngữ

Khi tích hợp các Mô hình ngôn ngữ lớn (LLM) và Kỹ thuật sinh tăng cường truy xuất (RAG) vào một hệ sinh thái Java đã hoàn thiện, các đội ngũ phải đối mặt với một lựa chọn kiến trúc nền tảng:

Tiêu chí Mô hình đa ngôn ngữ (Spring Boot + Python/FastAPI) Mô hình Native (Spring Boot + Spring AI)
Kiến trúc Mô hình hai service giao tiếp qua REST/gRPC (tăng 40–70ms độ trễ liên service) Runtime đơn xử lý in-process (overhead gọi hàm <1ms)
Mức tiêu thụ tài nguyên Tối thiểu 2 pod (JVM + container Python 1.5GB) Container JVM hợp nhất (tiết kiệm 30–40% RAM cụm)
Type Safety Mapping DTO thủ công qua ranh giới HTTP Kiểm tra kiểu dữ liệu tĩnh toàn diện bằng Java khi biên dịch
Khả năng quan sát (Observability) Dấu vết phân tán (traces) bị phân mảnh giữa OpenTelemetry và logger của Python Tích hợp sẵn bộ chỉ số Micrometer và distributed tracing trên một giao diện duy nhất
Triển khai Hai Dockerfile, các Helm chart riêng biệt, phát hành phối hợp phức tạp File JAR thực thi Spring Boot tiêu chuẩn hoặc một container image duy nhất

Ưu & nhược điểm của từng kiến trúc

Mô hình Sidecar đa ngôn ngữ (Python + LangChain / LlamaIndex)

  • Ưu điểm: Tiếp cận tức thì các bài báo nghiên cứu tiên tiến nhất chỉ vài ngày sau khi công bố, cùng danh mục giải pháp cộng đồng khổng lồ.
  • Nhược điểm: Tổn thất mạng đáng kể trên mỗi lượt truy xuất, trùng lặp mô hình dữ liệu, tăng bề mặt tấn công CVE và phân tán sự tập trung của đội ngũ vào hai mô hình lập trình khác nhau.

Hệ sinh thái Java Native (Spring Boot + Spring AI)

  • Ưu điểm: Hỗ trợ Dependency Injection hàng đầu, ranh giới transaction cơ sở dữ liệu liền mạch, lan truyền ngữ cảnh Spring Security nhất quán và không phát sinh tuần tự hóa qua mạng giữa các tiến trình.
  • Nhược điểm: Các thuật toán mới từ cộng đồng thường xuất hiện trên Java muộn hơn vài tháng so với khi ra mắt trên Python.

Khi chúng tôi chuyển đổi cơ sở tri thức production gồm hơn 180.000 tài liệu nội bộ sang Spring AI native, hiệu quả thể hiện ngay lập tức. Việc loại bỏ các bước trung gian liên service đã giảm thời gian truy xuất trung bình từ 580ms xuống còn 195ms. Đồng thời, việc tắt các pod Python đã giải phóng 12GB RAM khỏi cụm Kubernetes. Tuyệt vời nhất là các lập trình viên Java của chúng tôi có thể viết, kiểm thử và debug các pipeline RAG bằng những mô hình Spring chuẩn mực mà họ đã tin dùng suốt nhiều năm qua.

Cấu hình đề xuất

Để có một nền tảng sẵn sàng cho môi trường doanh nghiệp, hãy triển khai bộ công nghệ đã được kiểm chứng trên production sau:

  • Runtime: Java 21 LTS với Spring Boot 3.3+
  • AI Framework: Spring AI 1.0.0-M4 hoặc mới hơn
  • Vector Database: PostgreSQL 16 với extension pgvector 0.7+ (tận dụng hạ tầng quan hệ sẵn có của bạn)
  • Nhà cung cấp mô hình: OpenAI API (gpt-4o-mini để tối ưu chi phí suy luận) hoặc Ollama tự host cho việc kiểm thử cục bộ biệt lập

Khởi chạy vector store cục bộ với Docker chỉ trong vài giây:

docker run -d \
  --name postgres-vector \
  -e POSTGRES_DB=knowledgebase \
  -e POSTGRES_USER=postgres \
  -e POSTGRES_PASSWORD=secret \
  -p 5432:5432 \
  pgvector/pgvector:pg16

Hướng dẫn triển khai: Xây dựng dịch vụ RAG chuẩn Production

1. Cấu hình Project Dependencies

Thêm Spring AI Bill of Materials (BOM) và các starter cần thiết vào file pom.xml:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>1.0.0-M4</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId>
    </dependency>
</dependencies>

2. Cấu hình Application Properties

Khai báo kết nối cơ sở dữ liệu, tham số vector index và thông tin xác thực mô hình trong src/main/resources/application.properties:

spring.application.name=enterprise-ai-service

# Cấu hình OpenAI
spring.ai.openai.api-key=${OPENAI_API_KEY}
spring.ai.openai.chat.options.model=gpt-4o-mini
spring.ai.openai.chat.options.temperature=0.2

# PostgreSQL với PgVector
spring.datasource.url=jdbc:postgresql://localhost:5432/knowledgebase
spring.datasource.username=postgres
spring.datasource.password=secret

# Cấu hình Spring AI PgVector
spring.ai.vectorstore.pgvector.initialize-schema=true
spring.ai.vectorstore.pgvector.index-type=HNSW
spring.ai.vectorstore.pgvector.distance-type=COSINE_DISTANCE
spring.ai.vectorstore.pgvector.dimensions=1536

3. Nạp tri thức doanh nghiệp vào Vector Database

Trước khi mô hình có thể trả lời các câu hỏi về dữ liệu nội bộ, bạn cần phân tích các tài liệu nguồn, chia nhỏ chúng thành các chunk token vừa vặn, tính toán vector embedding và lưu trữ vào PostgreSQL. Dưới đây là service nạp dữ liệu chuẩn production được cấu hình để chia nhỏ tài liệu quy định thành các khối 800 token với kích thước tối thiểu 350 ký tự:

package com.itfromzero.ai.service;

import org.springframework.ai.document.Document;
import org.springframework.ai.reader.TextReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class IngestionService {

    private final VectorStore vectorStore;

    @Value("classpath:docs/enterprise-policy.txt")
    private Resource policyDocument;

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

    public void loadKnowledgeBase() {
        TextReader textReader = new TextReader(policyDocument);
        List<Document> rawDocuments = textReader.get();

        // Kích thước chunk 800 token, tối thiểu 350 ký tự mỗi chunk, độ chồng lấp (overlap) 5 token
        TokenTextSplitter splitter = new TokenTextSplitter(800, 350, 5, 10000, true);
        List<Document> chunks = splitter.apply(rawDocuments);

        vectorStore.accept(chunks);
    }
}

4. Triển khai RAG Chatbot Service

Kỹ thuật Retrieval-Augmented Generation dựa trên một cơ chế rất trực quan: truy vấn vector store để tìm các kết quả khớp về mặt ngữ nghĩa, chèn các đoạn trích đó vào prompt làm ngữ cảnh thực tế và để LLM sinh câu trả lời. Bộ công cụ ChatClient fluent API và QuestionAnswerAdvisor của Spring AI xử lý toàn bộ vòng đời này chỉ trong một cấu hình bean duy nhất:

package com.itfromzero.ai.service;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.QuestionAnswerAdvisor;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.stereotype.Service;

@Service
public class SupportBotService {

    private final ChatClient chatClient;

    public SupportBotService(ChatClient.Builder chatClientBuilder, VectorStore vectorStore) {
        this.chatClient = chatClientBuilder
                .defaultAdvisors(new QuestionAnswerAdvisor(
                        vectorStore,
                        SearchRequest.builder().topK(4).similarityThreshold(0.7).build()
                ))
                .defaultSystem("Bạn là một trợ lý CNTT doanh nghiệp hữu ích. " +
                        "Chỉ trả lời câu hỏi dựa trên ngữ cảnh được cung cấp. " +
                        "Nếu ngữ cảnh không chứa câu trả lời, hãy nói rằng bạn không biết.")
                .build();
    }

    public String askQuestion(String userPrompt) {
        return this.chatClient.prompt()
                .user(userPrompt)
                .call()
                .content();
    }
}

5. Khởi tạo REST API Controller

Sau khi advisor đã được gắn vào service, việc mở endpoint trò chuyện chỉ tốn chưa đầy hai mươi dòng code:

package com.itfromzero.ai.controller;

import com.itfromzero.ai.service.SupportBotService;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@RestController
@RequestMapping("/api/v1/ai")
public class ChatController {

    private final SupportBotService supportBotService;

    public ChatController(SupportBotService supportBotService) {
        this.supportBotService = supportBotService;
    }

    @PostMapping("/chat")
    public Map<String, String> chat(@RequestBody Map<String, String> payload) {
        String query = payload.get("message");
        String reply = supportBotService.askQuestion(query);
        return Map.of("response", reply);
    }
}

6. Kiểm thử

Kiểm thử endpoint vừa tạo bằng curl:

curl -X POST http://localhost:8080/api/v1/ai/chat \
  -H "Content-Type: application/json" \
  -d '{"message": "Chính sách làm việc từ xa của công ty chúng ta là gì?"}'

Đằng sau hậu trường, Spring AI truy vấn PostgreSQL bằng phép quét độ tương đồng cosine qua HNSW index, lọc ra các tài liệu đạt ngưỡng tương đồng 0.7 và tạo prompt tăng cường. Mô hình phản hồi trong vòng 350ms, dựa hoàn toàn trên dữ liệu nội bộ của doanh nghiệp.

Share: