1. Visión general
Las aplicaciones web modernas están integrándose cada vez más con Modelos de Lenguaje Grande (LLM) para construir soluciones que van más allá de la simple respuesta a preguntas. Para crear agentes de IA capaces de manejar solicitudes de usuarios complejas, a menudo los conectamos a múltiples servidores Protocolo de Contexto de Modelo (MCP) que les proporcionan capacidades específicas a través de herramientas.
Sin embargo, crear y ejecutar servidores MCP puede ser excesivo para tareas de automatización ligera y local donde solo necesitamos exponer una capacidad simple a un solo agente.
Habilidades de Agente es una especificación que proporciona una forma estructurada de definir, empaquetar y exponer localmente estas capacidades a un agente de IA.
En este tutorial, exploraremos la capacidad de Habilidades de Agente en Spring AI. Configuraremos una habilidad personalizada e integrarla con un chatbot sencillo para resumir artículos.
2. ¿Qué son las Habilidades de Agente?
Habilidades de Agente es una especificación abierta para definir varias capacidades que un agente de IA puede invocar. Una habilidad es esencialmente un directorio que contiene un archivo SKILL.md, que actúa como su manifiesto, junto con cualquier código asociado como scripts de Python o Bash, o recursos adicionales en los que la habilidad depende.
El archivo SKILL.md contiene un bloque frontmatter con un nombre y una descripción, seguido de un conjunto de instrucciones en lenguaje natural que indican al agente cómo usar la habilidad.
Cuando el agente recibe una solicitud de usuario, lee las descripciones de todas las habilidades disponibles y decide si alguna es relevante. Si es así, carga los archivos relevantes en el contexto y sigue las instrucciones dentro de la habilidad coincidente para cumplir la solicitud. Y si ninguna habilidad coincide con la solicitud, el agente simplemente responde utilizando sus capacidades generales sin invocar ninguna habilidad.
Veremos un agente invocando nuestra habilidad personalizada en acción práctica en las secciones siguientes.
3. Configuración del proyecto
Antes de sumergirnos en la implementación, necesitaremos incluir las dependencias necesarias y configurar nuestra aplicación correctamente.
3.1. Dependencias
Comencemos añadiendo las dependencias necesarias al archivo pom.xml de nuestro proyecto:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.springaicommunity</groupId>
<artifactId>spring-ai-agent-utils</artifactId>
<version>0.10.0</version>
</dependency>
Aquí, primero importamos la dependencia de arranque de OpenAI de Spring AI, que utilizaremos para interactuar con un modelo de chat. El soporte para habilidades de agente está disponible en Spring AI 2 y posteriores, por lo que debemos asegurarnos de usar la versión correcta.
Luego, importamos la dependencia agent-utils del comunidad Spring AI, que nos permite agregar la capacidad de habilidades de agente a nuestros modelos de chat.
3.2. Configurando un modelo de chat
A continuación, configuremos nuestra clave API de OpenAI y el modelo de chat en el archivo application.yaml:
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat:
options:
model: gpt-5.5
Usamos el marcador de posición de propiedad ${} para cargar el valor de nuestra clave API desde una variable de entorno.
Además, especificamos el modelo GPT 5.5 de OpenAI usando el ID de modelo gpt-5.5. Alternativamente, podemos usar un modelo de chat diferente que admita la especificación de habilidades de agente, ya que el modelo de IA o proveedor específico es irrelevante para esta demostración.
Con estas dos propiedades establecidas, Spring AI crea automáticamente un bean de tipo ChatModel, lo que nos permite interactuar con el modelo especificado.
4. Definición de nuestra habilidad personalizada
Ahora definamos una habilidad de agente personalizada que pueda obtener un artículo de una URL y resumirlo.
Las habilidades de agente siguen una estructura de directorio específica, así que configuremos eso. Comenzaremos creando un directorio .openai/skills en el directorio raíz de nuestro proyecto. Podemos definir múltiples subdirectorios dentro de este, cada uno representando una habilidad de agente distinta.
A continuación, crearemos un subdirectorio article-summarizer para representar nuestra habilidad personalizada dentro de .openai/skills y definiremos nuestro archivo principal SKILL.md dentro de él:
---
name: article-summarizer
description: Summarizes articles into concise digests. Useful when user asks to summarize or get key points from an article.
---
# Article Summarizer
## Instructions
When summarizing an article:
1. If given a URL: Run `uv run scripts/fetch_article.py <url>` to retrieve the content.
2. Once content is available, extract the main thesis, few key points, and conclusion.
3. Structure the output as a TL;DR, key points, and a bottom line.
En el bloque frontmatter, definimos el nombre y la descripción de nuestra habilidad. La descripción es especialmente importante ya que el agente la utiliza para determinar si esta habilidad es relevante para una solicitud de usuario dada. Luego, definimos las instrucciones que indican al agente exactamente qué pasos seguir cuando se invoque la habilidad, incluyendo qué script ejecutar y cómo estructurar la salida final.
A continuación, creemos el script fetch_article.py dentro de un nuevo subdirectorio scripts que referencia en las instrucciones:
ARTICLE = """
... hardcoding sample article for demonstration
"""
print(ARTICLE)
Aquí, para nuestra demostración, simplemente imprimimos un artículo codificado sobre elicitaciones de MCP en lugar de hacer una solicitud web real. El modelo de IA ejecutará este script y leerá la salida estándar para obtener el contenido de este artículo independientemente de la URL en la solicitud.
Además, es importante notar que podemos definir nuestros scripts en cualquier lenguaje de nuestra elección. Solo necesitamos asegurarnos de preinstalar las runtimes requeridas para que nuestro agente ejecute los comandos necesarios.
5. Creación de un chatbot sencillo
Con nuestras configuraciones en su lugar, construyamos un chatbot sencillo.
En Spring AI, la clase ChatClient actúa como el punto de entrada principal para interactuar con nuestro modelo de chat completado configurado. Definamos su bean usando el bean ChatModel auto-configurado:
@Bean
ChatClient chatClient(ChatModel chatModel) {
String skillsRootDirectory = ".openai/skills";
return ChatClient
.builder(chatModel)
.defaultTools(
SkillsTool.builder()
.addSkillsDirectory(skillsRootDirectory)
.build(),
FileSystemTools.builder()
.allowedDirectory(skillsRootDirectory)
.build(),
ShellTools.builder()
.build()
).build();
}
En nuestra definición de bean, primero registramos nuestro directorio de habilidades personalizado usando SkillsTool, apuntándolo al directorio .openai/skills.
En segundo lugar, registramos FileSystemTools, que brinda al agente la capacidad de leer y escribir cualquier archivo en el sistema de archivos local. Para restringir las operaciones de la herramienta al directorio de habilidades configurado, usamos el método allowedDirectory().
Finalmente, registramos ShellTools, que permite al agente ejecutar comandos de shell, lo que le permite ejecutar el script de Python que hemos definido.
Sin embargo, es importante señalar que ShellTools ejecuta nuestros scripts directamente en la máquina local sin sandboxing. Por lo tanto, debemos revisar cuidadosamente los scripts que exponemos al agente y considerar contenizar nuestra aplicación para limitar la exposición potencial.
A continuación, inyectemos el bean ChatClient en una clase controlador y exponemos una API REST:
@PostMapping("/chat")
ResponseEntity<ChatbotResponse> chat(@RequestBody ChatbotRequest chatbotRequest) {
String answer = chatClient
.prompt()
.user(chatbotRequest.question)
.call()
.content();
return ResponseEntity.ok(new ChatbotResponse(answer));
}
record ChatbotRequest(String question) {}
record ChatbotResponse(String answer) {}
Aquí, simplemente pasamos la pregunta del usuario al chatClient e devolvemos la respuesta del LLM. Usaremos este punto final API para interactuar con nuestro chatbot en la sección siguiente.
6. Interacción con nuestro chatbot
Ahora que hemos construido nuestra implementación, interactuemos con nuestro chatbot y probemos la capacidad de habilidad del agente.
Usaremos la CLI HTTPie para invocar el punto final API del chatbot:
http POST :8080/chat question="Can you summarize the following article: https://www.codeja.dev/articulo-no-existe"
Aquí, le pedimos al chatbot que resuma un artículo específico pasando una URL en la pregunta. Proporcionamos deliberadamente la URL de un artículo inexistente para verificar que el chatbot resuma el artículo que hemos codificado en nuestro script de Python.
Veamos lo que obtenemos como respuesta:
{
"answer": "## TL;DR\nThis article explains how to implement MCP Elicitations in Spring AI, allowing MCP servers to request additional user information dynamically during tool execution.\n \n## Key Points\n- MCP Elicitations solve the problem of missing user information during MCP tool execution.\n \n- The tutorial demonstrates building an MCP server using Spring AI.\n \n- The MCP server exposes a tool that fetches author details and conditionally requests additional information.\n \n- The `elicit()` method is used to pause execution and gather required details from the user.\n \n- The article also demonstrates configuring an MCP client and integrating it with an Anthropic Claude model\n \n- Spring AI automatically creates MCP clients and tool callback providers from configuration.\n \n- An `@McpElicitation` handler is used on the client side to respond to elicitation requests.\n \n- The tutorial concludes with a working chatbot example and log outputs showing the complete elicitation flow.\n \n## Bottom Line\nMCP Elicitations enable interactive AI applications where tools can dynamically collect additional context from users during execution,\n making MCP-based systems more flexible and user-aware."
}
Como podemos ver, el LLM resume nuestro artículo codificado y la respuesta está estructurada exactamente como las instrucciones de nuestra habilidad prescriben, con un TL;DR, un conjunto de puntos clave y una línea final.
Detrás de escena, el agente coincidió la solicitud del usuario con la habilidad article-summarizer según su descripción, cargó las instrucciones en el contexto usando FileSystemTools, ejecutó el script fetch_article.py mediante ShellTools para recuperar el contenido del artículo, y luego estructuró la respuesta siguiendo las instrucciones en nuestro archivo SKILL.md.
7. Conclusión
En este artículo, hemos explorado el concepto de Habilidades de Agente usando Spring AI.
Empezamos por comprender qué son las Habilidades de Agente y cómo nos ayudan a exponer capacidades reutilizables a un agente de IA.
Luego, definimos una habilidad personalizada de resumidor de artículos y la conectamos a un chatbot. Finalmente, interactuamos con nuestro chatbot para confirmar que descubre e invoca correctamente nuestra habilidad.
Como siempre, todos los ejemplos de código utilizados en este artículo están disponibles en GitHub.
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.