Artículo

Guía de la API de modelos de embeddings en Spring AI

Guía de la API de modelos de embeddings en Spring AI

1. Visión general

Integrar inteligencia artificial en una aplicación suele implicar trabajar con datos de texto. Una técnica crítica en este dominio es el modelo de embeddings, que convierte la información textual en embeddings que las aplicaciones pueden procesar.

En este tutorial exploraremos la API de embeddings en Spring AI. Esta potente API proporciona una abstracción que facilita adoptar diferentes modelos de embeddings con un esfuerzo mínimo y facilita que nuestra aplicación comprenda el texto.

2. Introducción a los Embeddings

Para entrenar modelos de IA que aprendan el significado semántico de textos e imágenes, generalmente convertimos estos tipos de datos en representaciones vectoriales de alta dimensión conocidas como embeddings.

Los modelos de IA comprenden las relaciones entre embeddings calculando su similaridad. Cuando dos embeddings tienen una puntuación de similitud más alta, significa que el significado contextual del texto que representan es similar.

3. APIs del Modelo de Embedding

Spring AI proporciona un conjunto de APIs para simplificar nuestro trabajo con los modelos de embedding. Las APIs son interfaces que ocultan todos los detalles de implementación de nosotros.

3.1. EmbeddingModel

Un modelo de embedding es un modelo de aprendizaje automático entrenado que convierte diferentes tipos de objetos, como párrafos e imágenes, en un espacio vectorial de alta dimensión.

Existen diferentes modelos, como BERT, que son ofrecidos por distintos proveedores. La API de embeddings de Spring AI proporciona una interfaz EmbeddingModel que encapsula los detalles de la adopción del modelo de embedding:

public interface EmbeddingModel extends Model<EmbeddingRequest, EmbeddingResponse> {
    EmbeddingResponse call(EmbeddingRequest request);

    // constructores y otros métodos
}

El método call() simplemente acepta un EmbeddingRequest que contiene la fuente de datos y lo envía al proveedor del modelo, devolviendo el EmbeddingResponse que contiene el Embedding.

3.2. EmbeddingRequest

EmbeddingRequest contiene la carga útil con una lista de textos para convertir en embeddings. Además de los textos, podríamos incluir opciones adicionales que son específicamente para nuestro EmbeddingModel:

public class EmbeddingRequest implements ModelRequest<List<String>> {
    private final List<String> inputs;
    private final EmbeddingOptions options;

    // constructores y otros métodos
}

3.3. EmbeddingResponse

EmbeddingResponse encapsula la respuesta del proveedor del modelo de embedding. Contiene una lista de objetos Embedding y metadatos adicionales, como el uso de tokens:

public class EmbeddingResponse implements ModelResponse<Embedding> {
    private final List<Embedding> embeddings;
    private final EmbeddingResponseMetadata metadata;

    // constructores y otros métodos
}

3.4. Embedding

Embedding contiene la representación vectorial en un array de float. La dimensionalidad depende del modelo de embedding que hayamos elegido, que normalmente varía de unos pocos cientos a varios miles:

public class Embedding implements ModelResult<float[]> {
    private final float[] embedding;
    private final Integer index;
    private final EmbeddingResultMetadata metadata;

    // constructores y otros métodos
}

4. Integración con OpenAI

Spring AI admite OpenAI como una de las integraciones de modelos de embedding. En esta sección adoptaremos OpenAI y crearemos un servicio Spring para convertir textos en embeddings.

4.1. Dependencia Maven

Empecemos añadiendo la siguiente dependencia de Spring AI OpenAI dependency a nuestro pom.xml:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
    <version>1.0.0-M6</version>
</dependency>

4.2. Configuración de Open AI

Para completar la integración de OpenAI con Spring AI, necesitaremos colocar la clave API para la autenticación en la API de OpenAI:

spring:
  ai:
    openai:
      api-key: "<YOUR-API-KEY>"

4.3. Auto-configuración del EmbeddingModel

Spring AI es capaz de auto-configurar el EmbeddingModel. Actívalo añadiendo una propiedad adicional para definir el modelo de embedding en application.yml:

spring:
  ai:
    openai:
      embedding:
        options:
          model: "text-embedding-3-small"

Esta propiedad model configura el modelo de embedding que vamos a usar. Actualmente OpenAI proporciona tres modelos diferentes.

Una vez que definamos este modelo de embedding, simplemente inyectamos el EmbeddingModel en el servicio Spring Boot sin especificar ningún detalle de OpenAI. Todo depende de las APIs de embedding de Spring AI:

@Service
public class EmbeddingService {
    private final EmbeddingModel embeddingModel;

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

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

La auto-configuración nos brinda comodidad sin exponer la implementación real del embedding. Esto nos permite cambiar fácilmente a diferentes implementaciones simplemente actualizando nuestro application.yml.

4.4. Configuración Manual del EmbeddingModel

Aunque la auto-configuración es conveniente, no puede proporcionarnos flexibilidad en algunos casos, como cuando nuestra aplicación necesita generar embeddings usando más de un modelo de embedding o con diferentes proveedores de modelos de embedding.

En este escenario, definimos manualmente el productor del modelo de embedding en una clase de configuración:

@Configuration
public class EmbeddingConfig {
    @Bean
    public OpenAiApi openAiApi(@Value("${spring.ai.openai.api-key}") String apiKey) {
        return OpenAiApi.builder()
          .apiKey(apiKey)
          .build();
    }

    @Bean
    public OpenAiEmbeddingModel openAiEmbeddingModel(OpenAiApi openAiApi) {
        OpenAiEmbeddingOptions options = OpenAiEmbeddingOptions.builder()
          .model("text-embedding-3-small")
          .build();
        return new OpenAiEmbeddingModel(openAiApi, MetadataMode.EMBED, options);
    }
}

En nuestro ejemplo, primero creamos un cliente OpenAI, OpenAiApi, usando la clave API inyectada openAiApiKey. Luego creamos el modelo de embedding de OpenAI usando el cliente.

Actualizamos ligeramente el servicio para inyectar la implementación OpenAIEmbeddingModel en lugar de la interfaz EmbeddingModel:

@Service
public class ManualEmbeddingService {
    private final OpenAiEmbeddingModel openAiEmbeddingModel;

    public ManualEmbeddingService(OpenAiEmbeddingModel openAiEmbeddingModel) {
        this.openAiEmbeddingModel = openAiEmbeddingModel;
    }

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

5. Probando el Servicio de Embedding

Basándonos en la implementación del servicio de auto-configuración de la sección anterior, exponemos un endpoint REST que nos permite probar el servicio de embedding:

@RestController
public class EmbeddingController {
    private final EmbeddingService embeddingService;

    public EmbeddingController(EmbeddingService embeddingService) {
        this.embeddingService = embeddingService;
    }

    @PostMapping("/embeddings")
    public ResponseEntity<EmbeddingResponse> getEmbeddings(@RequestBody String text) {
        EmbeddingResponse response = embeddingService.getEmbeddings(text);
        return ResponseEntity.ok(response);
    }
}

Hagamos una solicitud con textos en el cuerpo a este endpoint mediante curl:

$ curl -X POST http://localhost:8080/embeddings -H "Content-Type: text/plain" -d "Hello world"

Obtenemos la siguiente respuesta:

{
    "metadata": {
        "model": "text-embedding-3-small",
        "usage": {
            "promptTokens": 2,
            "completionTokens": 0,
            "totalTokens": 2,
            "nativeUsage": {
                "prompt_tokens": 48,
                "total_tokens": 48
            }
        },
        "empty": true
    },
    "result": {
        "index": 0,
        "metadata": {
            "modalityType": "TEXT",
            "documentId": "",
            "mimeType": {
                "type": "text",
                "subtype": "plain",
                "parameters": {},
                "charset": null,
                "concrete": true,
                "wildcardSubtype": false,
                "subtypeSuffix": null,
                "wildcardType": false
            },
            "documentData": null
        },
        "output": [
            -0.0020785425,
            -0.049085874,
            ...
       ]
    }
}

Notablemente, esto no es una respuesta completa, ya que es muy larga. La hemos recortado para describir los dos nodos principales de alto nivel en el JSON: metadata y result.

metadata proporciona información sobre el modelo utilizado y el consumo de recursos durante la conversión de embedding. model indica el modelo OpenAI que hemos elegido, y totalTokens revela la cantidad de tokens consumidos por la conversión.

result contiene el resultado del embedding. output en el result contiene un array de float, que es el embedding convertido por el modelo de embedding a partir de nuestro texto proporcionado.

6. Conclusión

La API del modelo de embeddings en Spring AI proporciona la capa de abstracción y el soporte para proveedores de modelos como OpenAI, lo que nos permite incorporarla en nuestras aplicaciones Java.

En este artículo adoptamos un modelo de embedding de referencia, OpenAI, con auto-configuración para simplicidad y una configuración manual para flexibilidad. La API de embeddings brinda la capacidad de convertir textos en vectores de embedding.

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