¿Buscas nuestro logo?
Aquí te dejamos una copia, pero si necesitas más opciones o quieres conocer más, visita nuestra área de marca.
¿Buscas nuestro logo?
Aquí te dejamos una copia, pero si necesitas más opciones o quieres conocer más, visita nuestra área de marca.
dev
Simón Rodríguez Hace 1 día Cargando comentarios…
En el post anterior se realizó la primera inmersión en Spring AI y sus APIs. Hablamos de clases como ChatClient y ChatModel, que son las principales para interactuar con los LLMs. En este post seguimos examinando el módulo para ver features como la multimodularidad, prompts o algo tan transversal como la observabilidad en los modelos.
Los seres humanos obtenemos nuestros conocimientos desde varias “fuentes de datos” (imágenes, sonido, texto, etc), por lo que se puede decir que nuestras experiencias son multimodales. Por el contrario, el Machine Learning se había enfocado hacia una única modalidad hasta que, recientemente, empezaron a aparecer modelos que pueden procesar varias entradas como imagen y texto o audio y vídeo al mismo tiempo.
La multimodularidad se refiere a la capacidad del modelo de poder procesar información de varias fuentes y Spring AI, a través de su Message API, ofrece este soporte. El campo content de la clase UserMessage se usa para textos, mientras que el campo media permite añadir contenido adicional de otras formas como imágenes, audio y vídeo, indicado por el MimeType correspondiente.
@RestController
public class MultimodalController {
private OllamaChatModel chatModel;
public MultimodalController(OllamaChatModel chatModel) {
this.chatModel = chatModel;
}
@GetMapping("/multimodal")
String multimodal() {
ClassPathResource imageResource = new ClassPathResource("/static/multimodal.jpg");
UserMessage userMessage = new UserMessage("Explicame lo que ves en la imagen", new Media(MimeTypeUtils.IMAGE_JPEG, imageResource));
return chatModel.call(new Prompt(List.of(userMessage))).getResult().getOutput().getText();
}
}
Para verlo en un ejemplo, pediremos una descripción sobre la siguiente imagen:
Se obtienen respuestas como:
Como se ve, el modelo usado puede producir respuestas extrañas incluso mezclando idiomas. Otro aspecto importante a tener en cuenta es el tiempo de respuesta (en general a todas las llamadas al modelo), por lo que es importante manejar la asincronía además de la mejora de rendimiento en el código (como por ejemplo con virtual-threads).
Como se mencionó anteriormente, los prompts son las entradas que se envían a los modelos. El diseño y cómo están estructurados influyen significativamente en las respuestas de los modelos. En última instancia, Spring AI maneja los prompts con los modelos de la misma forma que se maneja la capa “vista” en Spring MVC. Esto implica la creación de marcadores que se sustituyen por el valor correspondiente para enviar contenido de forma dinámica.
La estructura de los prompts ha ido evolucionando con la IA, al principio eran simples strings y han ido creciendo hasta existir algunos que pueden incluir placeholders para entradas concretas (como por ejemplo USER:<usuario>
A cada mensaje se le asocia un rol. Los roles categorizan el mensaje aportando contexto y finalidad para el modelo, permitiendo mejorar la eficacia en las respuestas. Los roles básicos son:
En Spring AI, los roles son un enumerado.
public enum MessageType {
USER("user"),
ASSISTANT("assistant"),
SYSTEM("system"),
TOOL("tool");
...
}
La clase PromptTemplate es la clave en la gestión de plantillas para los prompts, facilitando la creación de prompts estructurados.
public class PromptTemplate implements PromptTemplateActions, PromptTemplateMessageActions {}
Las interfaces que implementa la clase proporcionan distintos aspectos para la creación de prompts:
Algunos ejemplos de usos de PromptTemplate:
@GetMapping("/simple")
String simplePromptTemplate(@RequestParam String adjective, @RequestParam String country) {
PromptTemplate promptTemplate = new PromptTemplate("Give a {adjective} city from {country}");
Prompt prompt = promptTemplate.create(Map.of("adjective", adjective, "country", country));
return chatModel.call(prompt).getResult().getOutput().getText();
}
@GetMapping("/system")
String systemPromptTemplate(@RequestParam String tema) {
String userText = """
Dame información sobre Barcelona.
Responde por lo menos con como mínimo 5 líneas.
""";
Message userMessage = new UserMessage(userText);
String systemText = """
Eres una asistente de IA que ayuda a la gente con información sobre ciudades.
Tienes que responder con información de la ciudad sobre el tema {tema}.
Responde como si estuvieras creando un blog de viajes
""";
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemText);
Message systemMessage = systemPromptTemplate.createMessage(Map.of("tema", tema));
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
return chatModel.call(prompt).getResult().getOutput().getText();
}
Además de usar Strings para la creación de prompts, Spring AI soporta la creación de prompts a través de recursos (Resource), permitiendo indicar el prompt en un fichero que luego pueda ser usado como PromptTemplate:
@Value("classpath:/static/prompts/system-message.st")
private Resource systemResource;
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource);
Como se mencionaba anteriormente, la calidad y estructura de los prompts tienen una gran repercusión en las respuestas del modelo, llevando esto a generar una nueva profesión. En la comunidad de desarrollo se analiza y comparte continuamente la forma de mejorar los prompts en distintas situaciones, lo que deriva en algunos puntos importantes para la creación de prompts eficientes como:
En la propia documentación de Spring nos enlazan a múltiples recursos para mejorar los prompts, existiendo también en internet una gran comunidad dedicada a esta tarea.
Como en cualquier framework/módulo de Spring existente, no puede faltar la sección de observabilidad, puesto que es clave de cara a mantener y comprender cómo funcionan las aplicaciones, sobre todo cuando aparecen los problemas. Spring AI proporciona métricas y trazas sobre los distintos componentes comentados en las secciones anteriores como pueden ser ChatClient, ChatModel y otros que se comentarán más adelante.
Algunas de las métricas y trazas que se pueden encontrar ofrecen información como los parámetros que se le indican al modelo, tiempos de respuesta, tokens y modelos usados, etc. Se debe tener especial cuidado en los valores que se pintan en los logs, puesto que se puede filtrar información sensible de los usuarios. En la documentación se pueden encontrar los distintos datos que se pueden visualizar.
Habilitando las trazas con las dependencias de Actuator, Zipkin y Opentelemetry se pueden ver algunas métricas como:
De las que se pueden extraer datos como tokens, modelos usados, tiempos, etc.
Además, al habilitar la trazabilidad con Zipkin y Opentelemetry, también se obtendrán las trazas en el servidor Zipkin:
Si nos fijamos, podemos ver el input del usuario (propiedad spring.ai.chat.client.user.text). Esto es posible porque se ha habilitado con la propiedad spring.ai.chat.client.observations.include-input, aunque hay que indicar que se deben extremar las precauciones porque se puede exponer información sensible del usuario (también nos lo indica Spring AI con un warning en las trazas).
Con todos los conocimientos previos, ya podemos crear una demo con Ollama. Una vez se tiene Ollama habilitado en el sistema, se crea la app con distintos endpoints que permiten comprobar el funcionamiento de las distintas features de Spring AI:
{
"result": {
"output": {
"messageType": "ASSISTANT",
"metadata": {
"messageType": "ASSISTANT"
},
"toolCalls": [],
"media": [],
"text": "..."
},
"metadata": {
"finishReason": "stop",
"contentFilters": [],
"empty": true
}
},
"metadata": {
"id": "",
"model": "llama3.2:1b",
"rateLimit": {
"requestsLimit": 0,
"requestsRemaining": 0,
"requestsReset": "PT0S",
"tokensLimit": 0,
"tokensRemaining": 0,
"tokensReset": "PT0S"
},
"usage": {
"promptTokens": 34,
"completionTokens": 428,
"totalTokens": 462
},
"promptMetadata": [],
"empty": false
},
"results": [
{
"output": {
"messageType": "ASSISTANT",
"metadata": {
"messageType": "ASSISTANT"
},
"toolCalls": [],
"media": [],
"text": "..."
},
"metadata": {
"finishReason": "stop",
"contentFilters": [],
"empty": true
}
}
]
}
Como apunte, este endpoint es muy susceptible de fallar puesto que la transformación correcta de la respuesta en formato JSON falla mucho (es una de las grandes features pendientes de Spring AI y de los modelos, puesto que en muchas ocasiones no crean JSON en formato correcto).
En la aplicación se incluyen las dependencias y propiedades necesarias para tener observabilidad sobre las distintas peticiones realizadas a los modelos. De cara a exportar las trazas a un servidor Zipkin, se puede habilitar un servidor local de Zipkin con Docker mediante el comando:
docker run -d -p 9411:9411 openzipkin/zipkin
En general, también hay que indicar que, dependiendo del prompt indicado y de la precisión del modelo, se pueden encontrar respuestas sin sentido o que no cumplan los requisitos establecidos, pudiendo producirse errores de formato o incluso de memoria, mostrándose trazas como:
java.lang.RuntimeException: [500] Internal Server Error - {"error":"model requires more system memory (6.1 GiB) than is available (5.3 GiB)"}
Puedes descargar el código de la aplicación de ejemplo en este enlace.
En esta segunda parte de la serie de Spring AI, hemos podido ver cómo ejecutar modelos multimodales además de cómo gestionar la entrada al modelo de distintas formas a través de los prompts. Otro apartado siempre importante en los módulos de Spring es la observabilidad, ofreciendo en este caso las métricas correspondientes al comportamiento de los modelos empleados, importantes tanto para el buen funcionamiento de las aplicaciones como para la posible facturación de los LLMs.
Si bien es cierto que lo visto hasta ahora se puede considerar como el “Hello world” dentro de la IA, hay que tener en cuenta que es la parte más básica del módulo de Spring AI y que ya nos ofrece una multitud de opciones a tener en cuenta en los distintos casos de uso de la vida real, especialmente enfocado en los chatbots.
En sucesivos posts revisaremos otras funciones más avanzadas de Spring AI. ¡Te leo en comentarios! 👇
Referencias
Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.
Cuéntanos qué te parece.