Inicio / Artículos

IA de primavera en la empresa: un agente de soporte con herramientas, RAG y revisión

Spring AI para un asistente de triaje de incidentes: prompts como contratos, salida estructurada, llamadas a herramientas, descubrimiento de MCP, Qdrant RAG, seguimiento y bucles de agente limitados.

Editor: REACTAPP.TOP13 min de lectura2964 palabras

Fuentes: Leveraging Spring AI for building the next generation of enterprise applications

Un ticket de soporte como caso de prueba

Un taller interno de ingeniería de IA para desarrolladores de software en una empresa de tecnología financiera surgió a partir de una situación con la que casi todo desarrollador se encuentra tarde o temprano: llega un ticket de soporte. Los registros solo contienen pruebas parciales, y el código relevante se encuentra en un repositorio interno. Quien esté de guardia necesita un análisis inicial detallado y no una respuesta genérica.

Spring AI es adecuado para este tipo de ejercicios porque integra funcionalidades de IA en un ecosistema en el que los desarrolladores de Java y Spring ya trabajan a diario. Ofrece abstracciones para modelos, instrucciones, salidas estructuradas, llamadas a herramientas y recuperación de datos, y todo esto se integra sin grandes dificultades en la arquitectura de las aplicaciones existentes.

Llamar a un LLM desde una aplicación Java no es lo interesante. Lo importante es hacerlo manteniendo la disciplina que exige un entorno de fintech: contratos explícitos, herramientas con los menores privilegios posibles, trazabilidad y revisión humana en todos aquellos casos en que una decisión tenga peso.

Por lo tanto, el alcance aquí va más allá del taller. Un proyecto de “Agente de Soporte” sirve como ejemplo práctico de cómo conectar la IA a sistemas existentes, dónde debe ubicarse un modelo en la arquitectura, cómo accede a las capacidades y datos empresariales, y cómo el sistema circundante mantiene su comportamiento útil, predecible y bajo control.

Un problema de integración, no de modelo

Integrar la IA en los sistemas empresariales consiste principalmente en tareas de integración y tiene poco que ver con la selección de un modelo. Los equipos suelen darse cuenta de esto después del primer prototipo. Enviar una instrucción a un modelo es sencillo, y los resultados pueden parecer impresionantes. Sin embargo, una vez que la IA entra en un sistema de producción, surgen tensiones básicas.

El software empresarial se construye sobre el determinismo. Depende de contratos estrictos, una latencia predecible y un flujo de datos bien definido. Una entrada determinada debe producir una salida consistente, y el comportamiento puede rastrearse a través del código.

Los sistemas de IA funcionan de manera diferente. Son probabilísticos por naturaleza, a menudo aceptan lenguaje natural en lugar de entradas estructuradas y dependen de proveedores externos. La misma solicitud puede generar respuestas diferentes, y parte de la lógica se encuentra dentro del modelo, más allá del control del sistema.

El resultado es una incompatibilidad estructural: un componente no determinista dentro de una arquitectura determinista. Las suposiciones habituales sobre pruebas, monitoreo y fiabilidad dejan de ser válidas. Lo difícil no es generar respuestas inteligentes, sino integrarlas en sistemas que exigen control estricto.

Spring AI responde tratando a la IA como otro aspecto de integración dentro del ecosistema Spring y no como un caso especial. Spring ya abstracta bases de datos, sistemas de mensajería y llamadas REST, y Spring AI añade abstracciones similares para modelos de lenguaje, almacenes vectoriales y flujos de trabajo impulsados por IA. El objetivo no es ocultar por completo la complejidad, sino organizarla de modo que se ajuste a los patrones arquitectónicos existentes.

En una configuración típica, la IA no reside en un servicio separado; está integrada como código de integración dentro de la capa de aplicación.

La lógica del proyecto funciona de la siguiente manera: llega una solicitud y las solicitudes comerciales son rechazadas. Los incidentes reales generan un ticket. La herramienta Spring AI que realiza la llamada examina los registros, y el diagnóstico se convierte en un registro Java. Se obtiene el código fuente relevante de un almacén de vectores respaldado por Qdrant, se genera una sugerencia de solución y se actualiza el ticket para que alguien pueda revisarlo.

Para un desarrollador, la IA se convierte en otra dependencia, y la aplicación mantiene el control del flujo de trabajo. Los modelos se configuran como cualquier otra pieza de infraestructura, y las interacciones se realizan a través de APIs bien definidas en lugar de llamadas HTTP ad hoc.

En el proyecto, un SupportAgentController recibe la solicitud entrante como un SupportTicketRequest. Transfiere el razonamiento de la IA al SupportAnalysisService, guarda el estado a través del SupportTicketService, obtiene el código fuente mediante GithubRagService y almacena la solución generada en la ficha de ticket.

Un efecto secundario notable es que la lógica de negocio permanece independiente del proveedor del modelo. Cambiar proveedores o agregar soluciones alternativas no afecta al código principal. A largo plazo, esto se trata menos de flexibilidad que de mantener el control arquitectónico. Si el modelo no es accesible, el endpoint indicado debe seguir creando una ficha de ticket, marcar el análisis de la IA como no disponible y enviar el caso a la cola de atención humana.

@PostMapping("/requests")
public ResponseEntity<?> submitSupportRequest(@Valid @RequestBody SupportTicketRequest request) {
    boolean isBusiness = supportAnalysisService.isItBusinessRequest(request);

    if (isBusiness) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(ErrorResponse.businessRequest());
    }

    SupportTicket ticket = ticketService.createTicket(request);
    LogCauseReport logCauseReport = supportAnalysisService.analyzeLogs(request);

    String classCode = ragService.findRelevantClass(null, logCauseReport.repoName(), null, logCauseReport.className()).stream()
            .map(SupportAgentController::stripMetadataLines)
            .filter(content -> !content.isBlank())
            .collect(Collectors.joining(System.lineSeparator() + System.lineSeparator()));

    String solution = supportAnalysisService.suggestFix(classCode, logCauseReport.causeOfProblem());

    ticketService.updateTicket(ticket.getId(), SupportStatus.SUBMITTED,
            "Cause of the problem: " + logCauseReport.causeOfProblem() + "\n\nSuggested fix: " + solution);

    return ResponseEntity.status(HttpStatus.CREATED).body(SupportTicketResponse.from(ticket));
}

ChatClient y los prompts como contratos

Spring AI se centra en la API ChatClient, una forma estructurada de comunicarse con modelos de lenguaje. En lugar de crear solicitudes HTTP a mano, los desarrolladores utilizan una API intuitiva que gestiona la construcción de los prompts, su ejecución y el manejo de las respuestas.

El primer intento de un clasificador de tickets en el proyecto Support Agent se veía así.

String result = chatClient.prompt("Analyze if incoming support ticket is about some kind of issue or is it a business request. "
        + "If it's a business request, the text will ask you to create something new or ask for estimation, if it's a support request "
        + "the text will say that something is not working properly, something is down or needs fixing. Answer with true if it's a "
        + "business request and false if it's a support request" + request.description())
        .call()
        .content();

Se asemeja a una API ordinaria, excepto que el contrato está definido por la instrucción proporcionada y ya no por un esquema JSON.

¿Produciría resultados precisos en entornos de producción? Probablemente no. Mezcla instrucciones permanentes con datos de entrada del usuario que no son fiables en una sola cadena, no delimita el contenido del mensaje y asume que el modelo devolverá algo que Java pueda interpretar como un valor booleano. Una versión orientada a la producción se parece más a un contrato de aplicación, con las instrucciones a nivel de sistema separadas de los datos de entrada del usuario.

String result = chatClient.prompt()
        .system("""
                You classify incoming tickets for an L3 support workflow.
                Return exactly one token: true or false.
                Return true when the request asks for new functionality, estimation, planning, or project work.
                Return false when the request reports broken, degraded, unavailable, or incorrect production behavior.
                Do not explain your answer.
                """)
        .user(u -> u.text("""
                Ticket title: {title}
                Ticket source: {source}
                Ticket description:
                ---
                {description}
                ---
                """)
                .param("title", request.title())
                .param("source", request.source())
                .param("description", request.description()))
        .call()
        .content();

Las instrucciones del sistema definen el comportamiento y los límites, mientras que el mensaje del usuario contiene la entrada variable. Las instrucciones de entrada pasan a formar parte del diseño en sí, registrando suposiciones, estableciendo el tono y afectando la forma en que se interpretan las solicitudes.

Esto da lugar a una nueva disciplina: la ingeniería de instrucciones de entrada. Una instrucción de entrada ya no es simplemente una cadena temporal; es un elemento que debe versionarse, probarse y mantenerse, y tratarla de esa manera es necesario para sistemas estables. Las páginas de referencia de Spring AI sobre instrucciones de entrada y sobre ChatClient son la siguiente lectura recomendada.

Salida estructurada

Uno de los primeros desafíos al integrar la IA en los sistemas backend es la respuesta no estructurada. Los modelos generan texto de forma libre de manera natural, mientras que las aplicaciones empresariales necesitan datos estructurados.

Spring AI reduce esta brecha al mapear las respuestas directamente a objetos Java. Esto elimina la necesidad de un análisis manual y disminuye las posibilidades de ambigüedad al interpretar los resultados.

En lugar de recibir un párrafo que indique que algo falló, el agente de soporte utiliza un registro Java para indicar qué repositorio y clase probablemente están involucrados en un incidente.

@Service
@RequiredArgsConstructor
public class SupportAnalysisService {

    private final ChatClient chatClient;
    private final LogRetrievalTool logRetrievalTool;

    public LogCauseReport analyzeLogs(SupportTicketRequest request) {

        var converter = new BeanOutputConverter<>(LogCauseReport.class);
        var format = converter.getFormat();

        String response = chatClient.prompt("Analyze the support problem and provide solution, use available MCP tools to access logs. "
                + "Incoming support request description: " + request.description()
                + " \n provide response in this format " + format).tools(logRetrievalTool).call().content();

        LogCauseReport report = converter.convert(response);

        return report;
    }
}

public record LogCauseReport(String causeOfProblem, String repoName, String className) {
}

Aquí el modelo se orienta para generar una respuesta que pueda convertirse directamente en un objeto de tipo estricto, pasando la integración de un procesamiento de texto flexible a contratos explícitos.

La ventaja es considerable. La salida no estructurada obliga a un análisis frágil que falla con el más mínimo cambio en la redacción. La salida estructurada puede validarse, registrarse y transmitirse como cualquier otro tipo de dato. La documentación oficial describe el enfoque de conversión.

Las versiones más recientes de Spring AI también ofrecen mapeo de entidades de tipo y, con algunos proveedores, salida estructurada nativa; ambos merecen ser considerados. Aplicar los contratos más cerca del modelo implica que la aplicación necesite menos código de análisis.

Llamadas a herramientas

Generar texto es útil, pero el verdadero valor se manifiesta cuando la IA puede interactuar con el sistema recuperando datos, activando flujos de trabajo o ejecutando lógica empresarial.

La herramienta de llamada lo hace posible al exponer funciones de la aplicación que el modelo puede invocar según su razonamiento. No se debe tratar al modelo como un operador con acceso directo a la infraestructura. Él solicita una capacidad, y la aplicación decide si dicha capacidad existe, cómo se validan los argumentos, qué datos se devuelven y cómo se registra la ejecución.

Dentro del proyecto, SupportAnalysisService expone un LogRetrievalTool para la fase de análisis de registros. Para definir una herramienta se utiliza la anotación @Tool, y su parámetro de descripción ayuda al modelo a determinar cuándo llamar a la función.

@Tool(name = "analyze_logs", description = "Analyzes provided logs and summarizes potential issues")
public LogCauseReport analyzeLogs(
        @ToolParam(description = "Application name") String applicationName) {
    var converter = new BeanOutputConverter<>(LogCauseReport.class);
    var format = converter.getFormat();

    String prompt = """
            You are a support AI specialized in analyzing application logs to identify potential issues.
            Given the application name "%s", analyze the relevant logs and provide a structured report.
            Respond ONLY with a single JSON object that strictly matches this format:
            %s
            Do NOT return the schema itself, only valid JSON with values filled in.
            """.formatted(applicationName, format);

    String jsonResponse = chatClient.prompt()
            .user(prompt)
            .call()
            .content();

    return converter.convert(jsonResponse);
}

Cuando el modelo decide que necesita más información, llama a la función, incorpora el resultado y continúa generando. En efecto, el modelo se convierte en un orquestador de las capacidades del sistema.

No obstante, a medida que los sistemas crecen, las integraciones predefinidas se vuelven limitantes, ya que cada herramienta nueva añade un acoplamiento entre el modelo y la aplicación.

Descubriendo herramientas con MCP

El Model Context Protocol aborda este problema. MCP estandariza la forma en que las herramientas y servicios se exponen a los modelos, de modo que puedan descubrirse dinámicamente y no sea necesario integrarlos directamente en la aplicación.

MCP convierte las integraciones fijas en un ecosistema dinámico donde se pueden añadir o modificar funcionalidades sin alterar la aplicación principal, lo que representa un paso básico hacia arquitecturas escalables basadas en agentes. La documentación de llamadas a herramientas de Spring AI describe el modelo de programación y el flujo de ejecución. Cuando las herramientas requieren un descubrimiento dinámico o deben compartirse entre procesos, su soporte MCP es la continuación lógica.

Generación mejorada mediante recuperación de información

Los modelos son potentes, pero no conocen nada sobre los datos de sus sistemas. Los clientes, los productos y los procesos internos permanecen desconocidos a menos que se proporcione la información correspondiente.

RAG aborda este problema al añadir un paso de recuperación de información antes de la generación. Los documentos relevantes se obtienen de un almacén vectorial y se incorporan al prompt, lo que permite que los resultados estén basados en datos empresariales como manuales de operaciones, registros de producción y código de aplicaciones.

Para el agente de soporte, RAG proporciona una solución mediante código para un problema de producción específico, utilizando la clase de Java mencionada en los registros de la aplicación.

El primer paso consiste en que SupportAgentController obtenga los fragmentos de código fuente a través de GithubRagService, el cual funciona a partir del LogCauseReport que indica el repositorio y la clase sospechosos.

String classCode = ragService.findRelevantClass(null, logCauseReport.repoName(), null, logCauseReport.className()).stream()
        .map(SupportAgentController::stripMetadataLines)
        .filter(content -> !content.isBlank())
        .collect(Collectors.joining(System.lineSeparator() + System.lineSeparator()));

En el segundo paso, el servicio de recuperación crea filtros de metadatos y realiza una búsqueda de similitud contra el almacén de vectores Qdrant.

SearchRequest.Builder searchRequestBuilder = SearchRequest.builder();
if (expressions != null) {
    searchRequestBuilder = searchRequestBuilder.filterExpression(expressions.build());
}

Set<String> results = Objects.requireNonNull(vectorStore.similaritySearch(searchRequestBuilder.topK(10).build()))
        .stream()
        .map(Document::getFormattedContent)
        .collect(Collectors.toSet());

El tercer paso inyecta el código recuperado en un prompt que solicita una solución.

String prompt = """
        You are a Senior Java Developer. You have the following class code:
        %s
        The code has the following problem: %s
        Provide a detailed suggestion on how to fix the problem in the code.
        Format your response in markdown with clear sections.
        """.formatted(classCode, problemDescription);

String suggestion = chatClient.prompt(prompt).call().content();

El efecto es pasar el modelo de un asistente general a uno que conoce el dominio específico. RAG reduce principalmente las alucinaciones y mantiene las respuestas en línea con el conocimiento interno, a costa de una mayor complejidad.

Añade modelos de embedding, bases de datos vectoriales y lógica de recuperación, todos los cuales requieren gestión y ajustes. En el uso empresarial, esa complejidad suele ser inevitable, ya que la calidad de las respuestas depende de varios factores: cuán actualizados están los datos ingresados, la calidad de los metadatos, la estrategia de fragmentación, la autorización y el seguimiento de las pruebas. La documentación de Spring AI sobre RAG, almacenamientos vectoriales y Qdrant aborda los conceptos de esta sección.

Observabilidad, evaluación y controles operativos

La observabilidad en el caso de la IA funciona de manera diferente. El registro y monitoreo convencionales suponen un comportamiento determinista, pero la IA introduce dimensiones adicionales que deben seguirse. El seguimiento de los tiempos de respuesta y las tasas de error resulta insuficiente, y los equipos también necesitan poder ver los prompts, las salidas del modelo, el uso de tokens e las interacciones con las herramientas.

El agente de soporte utiliza Micrometer como su marco de observabilidad, capturando:

  • identificadores y contexto de cada solicitud, como el ticket, quien llama y el sistema de origen
  • el proveedor y nombre del modelo, la versión del prompt, la latencia, el uso de tokens y la razón de finalización
  • si la conversión a una salida estructurada tuvo éxito
  • cada llamada a una herramienta, con argumentos normalizados, tamaño del resultado y latencia
  • qué repositorios, archivos, puntuaciones vectoriales y fragmentos de datos se recuperaron
  • el estado final del ticket, y si el revisor humano aceptó o modificó la sugerencia

Ese nivel de detalle es vital para la depuración y la optimización, ya que explica por qué el sistema se comporta de la manera en que lo hace. En una aplicación en producción, los mensajes de entrada y las salidas sensibles no deberían registrarse literalmente. Una alternativa más sólida es el registro estructurado de eventos que incluya el ID de la solicitud, el ID del modelo, los conteos de tokens y el resultado de la política.

La evaluación de las salidas añade otra capa. Los sistemas pueden evaluar la calidad de las respuestas según criterios predefinidos y no tienen que asumir que las respuestas son correctas. Esto permite patrones como bucles de evaluador-optimizador, en los que una respuesta se refina antes de llegar al usuario.

También cambia la forma en que evoluciona el sistema. Cada vez que cambia un mensaje de entrada, un modelo de incrustación, un enfoque de fragmentación o una versión del modelo, el resultado se puede comparar con casos conocidos, de modo que el equipo pueda determinar si el cambio mejora el flujo de trabajo y no depende de unos pocos ejemplos impresionantes.

Antes de abrir el asistente a otros equipos, se consideró un conjunto de controles operativos para el proyecto:

  • tiempos de espera estrictos para las llamadas al modelo, a las herramientas y al almacén de vectores
  • presupuestos de tokens por solicitud y por usuario, lo que limita los costos y los posibles fallos
  • reintentos solo cuando son seguros y están delimitados
  • estados de respaldo en caso de que el modelo, una herramienta o Qdrant estén inactivos
  • eliminación de secretos, credenciales y datos personales antes de enviar prompts y registrar logs
  • registro de contenido de forma predeterminada, o solo bajo controles de acceso aprobados
  • reglas explícitas de retención para prompts, respuestas completas, embeddings y resultados de las herramientas
  • paneles de control y alertas para fallos en la conversión, búsquedas vacías, errores en las herramientas y rechazos por parte de los revisores

La tasa de rechazo por parte de los revisores refleja mejor el uso real. Cuando los ingenieros reescriben o descartan regularmente las recomendaciones generadas, el asistente añade trabajo adicional. Un sistema de producción debería aprender de esa señal. El ajuste automático en todo no es la solución adecuada. La ruta más eficaz es la operativa: mejorar los prompts, las herramientas, la recuperación de información y las políticas mediante evidencia medible. La documentación de observabilidad de Spring AI abarca las herramientas integradas.

Diseño de flujos de trabajo agentes con límites

Al combinar todo lo anterior surge un nuevo patrón: el sistema agente.

Las aplicaciones tradicionales siguen flujos de trabajo predefinidos. Las agente ejecutan un bucle iterativo de observación, razonamiento y acción. Mantienen un estado, utilizan memoria y deciden dinámicamente qué acciones tomar.

El estado, la memoria y la autonomía se convierten en aspectos prioritarios, y cada uno se relaciona con una capacidad que Spring AI ya ofrece:

  • El estado es lo que se conoce en la interacción actual
  • La memoria es lo que persiste a través de las interacciones
  • Las herramientas definen qué acciones están permitidas
  • Las restricciones indican qué está prohibido o necesita confirmación
  • Los criterios de finalización determinan cuándo debe terminar el bucle

Esto cambia el papel de la aplicación en comparación con un flujo de trabajo empresarial clásico. En lugar de definir cada paso, el sistema define capacidades y restricciones y permite que el modelo orqueste el comportamiento dentro de esos límites.

Dentro del Agente de Soporte, el ciclo comienza con una solicitud. El agente analiza la solicitud, utiliza herramientas y obtiene código. Luego elabora una recomendación y actualiza el ticket. No se trata de un operador de producción autónomo, y no debería convertirse en uno. En la tecnología financiera, ese límite determina qué acciones puede tomar el agente en un contexto dado. Se puede confiar en que el modelo resuma los registros de manera adecuada mucho antes de poder confiar en que modifique un ticket.

Crear agentes eficaces es difícil, y la documentación de Spring AI sobre el tema constituye un buen punto de partida.

De los experimentos a los sistemas empresariales

La IA no es solo otra característica más. Agrega una nueva capa a los sistemas empresariales, una capa probabilística, dinámica y estrechamente vinculada a dependencias externas. Sin estructura, esa capa conduce rápidamente a un comportamiento frágil e impredecible.

Spring AI ofrece una forma de integrarlo manteniendo las prácticas de ingeniería que garantizan la fiabilidad de los sistemas empresariales. Las abstracciones familiares y los patrones de integración bien definidos permiten a los equipos incorporar la IA sin abandonar los contratos explícitos, las interacciones controladas y el comportamiento predecible.

Por lo tanto, el cambio no se trata simplemente de añadir capacidades de IA. Se trata de evolucionar los sistemas para que la IA se convierta en una parte útil, regulada y observable de la arquitectura. La próxima ola de aplicaciones empresariales será evaluada según cuán bien integren la IA, y cómo la utilicen tendrá menos importancia.

Leer más

  • Google Cloud Next 2026 para equipos de datos: Looker, BigQuery y la nube de datos agente — Qué cambios trae Google Cloud Next 2026 para los proyectos de Looker y BigQuery: agentes de paneles, LookML para análisis conversacional, lecciones sobre migración y el Catálogo de conocimientos.