Artículo

Google Cloud y Spring AI

Google Cloud y Spring AI

1. Visión general

Spring AI es un marco de aplicación con una interfaz común para varios LLM que nos ayuda a integrarlos en nuestras aplicaciones Spring Boot.

En este tutorial, exploraremos cómo podríamos integrar Spring AI con la plataforma Google Cloud Vertex AI y adoptar varios modelos para proporcionar capacidades de chat y embedding en nuestras aplicaciones.

2. Requisitos previos

Necesitaremos las dependencias de Spring AI Vertex AI Gemini y embedding en nuestro pom.xml:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-vertex-ai-gemini</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-vertex-ai-embedding</artifactId>
</dependency>

Estas dependencias de modelo de inicio configurarán automáticamente los modelos Vertex AI según nuestras configuraciones en application.yml.

Como primer paso, debemos habilitar la API Vertex AI en nuestra consola de Google Cloud para realizar llamadas API a Vertex AI.

Una vez habilitada, necesitaremos ejecutar dos comandos adicionales en la consola con la CLI de Google Cloud instalada.

El primer comando establece el proyecto activo para todos los comandos CLI subsiguientes:

$ gcloud config set project <PROJECT-ID>

El argumento PROJECT-ID es el ID único de nuestro proyecto de Google Cloud donde Vertex AI está habilitado.

El segundo comando autentica y nos brinda un token de acceso OAuth2 que nos otorga derechos de acceso a las API de Vertex AI:

$ gcloud auth application-default login <YOUR-ACCOUNT>

Esto abrirá una ventana del navegador web y nos pedirá que iniciemos sesión con nuestra cuenta de Google Cloud. Guardará el token de acceso OAuth2 localmente después de que iniciemos sesión.

3. Chat

Gemini es el modelo de chat disponible en Vertex AI. En esta sección, integraremos Gemini en nuestra aplicación Spring Boot.

3.1. Configuración

Necesitamos agregar algunas propiedades a nuestro application.yml para integrar el modelo de chat con Spring AI:

spring:
  ai:
    vertex:
      ai:
        gemini:
          project-id: <YOUR-GOOGLE-CLOUD-PROJECT-ID>
          location: "europe-west1"
          model: "gemini-2.0-flash-lite"

La propiedad project-id especifica qué recursos del proyecto de Google Cloud, incluida la autenticación y facturación, se usarán en nuestra aplicación.

La propiedad model especifica qué modelo de chat Gemini integraremos. Hay varios modelos Gemini para elegir.

3.2. Servicio

Creemos un simple ChatService para aceptar un prompt como argumento de entrada:

@Component
@SessionScope
public class ChatService {
    private final ChatClient chatClient;

    public ChatService(ChatModel chatModel, ChatMemory chatMemory) {
        this.chatClient = ChatClient.builder(chatModel)
          .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
          .build();
    }

    public String chat(String prompt) {
        return chatClient.prompt()
          .user(userMessage -> userMessage.text(prompt))
          .call()
          .content();
    }
}

En este servicio, inyectamos el ChatModel Gemini auto-configurado para crear nuestra instancia de ChatClient.

Como los LLM son sin estado, no tienen conocimiento de conversaciones anteriores. Por lo tanto, también inyectamos una instancia de ChatMemory para poder ofrecer una experiencia similar a una conversación.

Necesitaremos un ChatController para aceptar la consulta con fines de prueba:

@RestController
public class ChatController {
    private final ChatService chatService;

    public ChatController(ChatService chatService) {
        this.chatService = chatService;
    }

    @PostMapping("/chat")
    public ResponseEntity<String> chat(@RequestBody @NotNull String prompt) {
        String response = chatService.chat(prompt);
        return ResponseEntity.ok(response);
    }
}

Este controlador acepta una cadena en el cuerpo de la solicitud y envía el prompt al modelo de chat Gemini a través de ChatService.

3.3. Ejecución de prueba

Ahora, ejecutemos una prueba enviando un prompt a este endpoint mediante Postman. Deberíamos ver una respuesta de Gemini: gcp test run 1

4. Embedding de texto

Embeddings de texto es el proceso de convertir una entrada de texto de lenguaje natural en una representación vectorial de alta dimensión. El caso de uso de los embeddings podría ser realizar búsquedas de similitud basadas en el significado contextual.

4.1. Configuración

Necesitaremos un modelo diferente para convertir texto en un embedding. Agreguemos algunas propiedades más al application.yml:

spring:
  ai:
    vertex:
      ai:
        embedding:
          project-id: <YOUR-GOOGLE-CLOUD-PROJECT-ID>
          location: "europe-west1"
          text: 
            options:
              model: "gemini-embedding-001"

Al igual que el modelo de chat, debemos definir los atributos project-id y location, para los cuales podríamos aplicar los valores definidos en la sección de configuración de chat anterior.

4.2. Servicio

Ahora, nuestra aplicación está configurada para inyectar un EmbeddingModel en nuestro servicio. Podemos definir una clase TextEmbeddingService para convertir un texto en un embedding:

@Service
public class TextEmbeddingService {
    private final EmbeddingModel embeddingModel;

    public TextEmbeddingService(EmbeddingModel embeddingModel) {
        this.embeddingModel = embeddingModel;
    }

    public EmbeddingResponse getEmbedding(String... texts) {
        EmbeddingRequest request = new EmbeddingRequest(Arrays.asList(texts), null);
        return embeddingModel.call(request);
    }
}

Creemos también un TextEmbeddingController para realizar una prueba:

@RestController
public class TextEmbeddingController {
    private final TextEmbeddingService textEmbeddingService;

    public TextEmbeddingController(TextEmbeddingService textEmbeddingService) {
        this.textEmbeddingService = textEmbeddingService;
    }

    @PostMapping("/embedding/text")
    public ResponseEntity<EmbeddingResponse> getEmbedding(@RequestBody @NotNull String text) {
        EmbeddingResponse response = textEmbeddingService.getEmbedding(text);
        return ResponseEntity.ok(response);
    }
}

4.3. Ejecución de prueba

Ahora, estamos listos para probar nuestro servicio de embedding de texto. Enviemos algunos textos a este endpoint y veamos qué devuelve:

gcp test run 2

Al completarse, el endpoint devolvió tanto metadatos como, lo más importante, el embedding que encontraríamos en el atributo output.

5. Embedding multimodal

Además de los textos, Vertex AI es capaz de convertir diversos medios, como imágenes, en embeddings. No necesitamos ninguna configuración adicional con el servicio de embedding multimodal. Todo lo que necesitamos es la configuración de embedding de texto en el application.yml.

5.1. Servicio

Creemos un MultiModalEmbeddingService para convertir diferentes imágenes en embeddings:

@Service
public class MultiModalEmbeddingService {
    private final DocumentEmbeddingModel documentEmbeddingModel;

    public MultiModalEmbeddingService(DocumentEmbeddingModel documentEmbeddingModel) {
        this.documentEmbeddingModel = documentEmbeddingModel;
    }

    public EmbeddingResponse getEmbedding(MimeType mimeType, Resource resource) {
        Document document = new Document(new Media(mimeType, resource), Map.of());
        DocumentEmbeddingRequest request = new DocumentEmbeddingRequest(List.of(document));
        return documentEmbeddingModel.call(request);
    }
}

Necesitaremos el Resource de la imagen y su tipo MIME para convertirla en un embedding. Actualmente, Vertex AI acepta formatos de imagen BMP, GIF, JPG y PNG.

Creemos un controlador que acepte un archivo de imagen de la solicitud. Deriva el tipo MIME del tipo de contenido de la solicitud y envía el Resource del archivo de imagen junto con el tipo MIME al MultiModalEmbeddingService:

@RestController
public class MultiModalEmbeddingController {
    private final MultiModalEmbeddingService embeddingService;

    public MultiModalEmbeddingController(MultiModalEmbeddingService embeddingService) {
        this.embeddingService = embeddingService;
    }

    @PostMapping("/embedding/image")
    public ResponseEntity<EmbeddingResponse> getEmbedding(@RequestParam("image") @NotNull MultipartFile imageFile) {
        EmbeddingResponse response = embeddingService.getEmbedding(
          MimeType.valueOf(imageFile.getContentType()),
          imageFile.getResource());
        return ResponseEntity.ok(response);
    }
}

5.2. Ejecución de prueba

Esta vez enviaremos una imagen en lugar de texto al endpoint del controlador:

gcp test run 3

Obtenemos una respuesta similar a la del embedding de texto al completarse, y encontraremos el embedding de la imagen en el atributo output de la respuesta.

6. Conclusión

Spring AI simplifica la integración de LLM con nuestra aplicación, lo que nos ayuda a adoptar y cambiar entre diferentes LLM con un esfuerzo mínimo de desarrollo.

En este artículo, hemos explorado la configuración de Vertex AI en una aplicación Spring Boot. También hemos aprendido cómo aplicar el modelo de chat Gemini y los modelos de embedding para convertir textos e imágenes en embeddings para su posterior procesamiento y análisis.

Newsletter Semanal de Java

Cada viernes recibe lo más nuevo del ecosistema Java: frameworks, herramientas y mejores prácticas.

Sin spam. Cancela cuando quieras.

Compartir artículo