Accueil / Articles

L’IA de printemps dans les entreprises : un agent de support équipé d’outils, de RAG et d’outils de révision

Spring AI pour un assistant de tri des incidents : prompts en tant que contrats, sortie structurée, appel d’outils, découverte MCP, Qdrant RAG, suivi et boucles d’agent bornées.

Éditeur : REACTAPP.TOP13 min de lecture2964 mots

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

Une demande de support en tant que cas de test

Un atelier interne d’ingénierie IA destiné aux développeurs logiciels au sein d’une entreprise de technologie financière a vu le jour à partir d’une situation que presque tous les développeurs rencontrent tôt ou tard : une demande de support arrive, les journaux ne contiennent que des preuves partielles, et le code pertinent se trouve dans un répertoire interne. La personne de garde a besoin d’une première analyse ciblée et non d’une réponse générique.

Spring AI convient parfaitement à ce type d’exercice, car il intègre des fonctionnalités d’IA dans l’écosystème dans lequel les développeurs Java et Spring travaillent déjà au quotidien. Il propose des abstractions pour les modèles, les prompts, les sorties structurées, l’appel et la récupération d’outils, qui s’intègrent facilement dans l’architecture existante des applications sans trop de friction.

Appeler un LLM depuis une application Java n’est pas la partie intéressante. Ce qui compte, c’est de le faire tout en respectant la discipline attendue dans un environnement fintech : des contrats explicites, des outils dotés des moindres privilèges, une traçabilité, ainsi qu’un contrôle humain là où une décision a de l’importance.

Ainsi, le champ d’application ici dépasse le cadre de l’atelier. Un projet de « agent de support » sert d’exemple concret pour montrer comment relier l’IA aux systèmes existants, où le modèle doit être intégré dans l’architecture, comment il accède aux capacités et aux données de l’entreprise, et comment le système environnant maintient son comportement utile, prévisible et sous contrôle.

Un problème d’intégration, pas un problème de modèle

L’intégration de l’IA dans les systèmes d’entreprise consiste principalement en des travaux d’intégration et a peu à voir avec le choix d’un modèle. Les équipes s’en rendent généralement compte après le premier prototype. Envoyer une requête à un modèle est simple, et les résultats peuvent sembler impressionnants. Cependant, une fois que l’IA entre dans un système de production, des tensions fondamentales apparaissent.

Les logiciels d’entreprise sont conçus autour du déterminisme. Ils reposent sur des contrats stricts, une latence prévisible et un flux de données bien défini. Une entrée donnée doit produire une sortie cohérente, et le comportement peut être suivi à travers le code.

Les systèmes d’IA fonctionnent différemment. Ils sont par nature probabilistes, utilisent souvent du langage naturel à la place d’entrées structurées, et dépendent de fournisseurs externes. La même requête peut produire des réponses différentes, et une partie de la logique se trouve à l’intérieur du modèle, hors du contrôle du système.

Le résultat est un déséquilibre structurel : un composant non déterministe intégré au sein d’une architecture déterministe. Les hypothèses habituelles concernant les tests, la surveillance et la fiabilité cessent de valoir. La difficulté réside non pas dans la génération de réponses ingénieuses, mais dans leur intégration dans des systèmes qui exigent un contrôle strict.

Spring AI répond en traitant l’IA comme une simple question d’intégration au sein de l’écosystème Spring, et non comme un cas particulier. Spring abstracte déjà les bases de données, les systèmes de messagerie et les appels REST, et Spring AI ajoute des abstractions similaires pour les modèles de langage, les bases de données vectorielles et les workflows pilotés par l’IA. L’objectif n’est pas de cacher complètement la complexité, mais de l’organiser de manière à ce qu’elle corresponde aux schémas architecturaux existants.

Dans une configuration typique, l’IA n’existe pas en tant que service distinct ; elle est intégrée sous forme de code d’intégration au sein de la couche d’application.

La logique du projet fonctionne comme suit : une demande arrive, les demandes commerciales sont rejetées. Les incidents réels génèrent un ticket. L’outil Spring AI qui effectue l’appel examine les journaux, et le diagnostic est converti en un enregistrement Java. Le code source pertinent est récupéré depuis un stockage vectoriel géré par Qdrant, une suggestion de correction est générée, et le ticket est mis à jour afin qu’une personne puisse le consulter.

Pour un développeur, l’IA devient une autre dépendance, tandis que l’application conserve le contrôle du flux de travail. Les modèles sont configurés comme n’importe quel autre élément d’infrastructure, et les interactions se font via des API bien définies plutôt que des appels HTTP ad hoc.

Dans le projet, un SupportAgentController reçoit la demande entrante sous forme de SupportTicketRequest. Il transmet les résultats du raisonnement de l’IA à SupportAnalysisService, enregistre l’état via SupportTicketService, récupère le code source grâce à GithubRagService, et stocke la solution générée sur le ticket.

Un effet secondaire notable est que la logique métier reste indépendante du fournisseur de modèles. Changer de fournisseur ou ajouter des solutions de secours n’affecte pas le code principal. À long terme, il s’agit moins de flexibilité que de maintien du contrôle architectural. Si le modèle est inaccessible, l’endpoint ci-dessous doit néanmoins créer un ticket, indiquer que l’analyse par l’IA est indisponible et envoyer le cas dans la file d’attente des humains.

@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 et les prompts en tant que contrats

Spring AI se concentre sur l’API ChatClient, un moyen structuré de communiquer avec les modèles de langage. Au lieu de créer manuellement des requêtes HTTP, les développeurs utilisent une API intuitive qui gère la construction des prompts, leur exécution ainsi que le traitement des réponses.

La première tentative de classification des tickets dans le projet Support Agent ressemblait à ceci.

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();

Celui-ci ressemble à une API ordinaire, à ceci près que le contrat est défini par l’instruction donnée et non plus par un schéma JSON.

Procurera-t-il des résultats précis en environnement de production ? Probablement pas. Il mélange des instructions permanentes avec des entrées utilisateur non fiables dans une même chaîne de caractères, ne sépare pas le contenu de la requête et suppose que le modèle renverra quelque chose que Java peut interpréter comme un booléen. Une version destinée à la production ressemble davantage à un contrat d’application, avec des instructions au niveau du système séparées des entrées utilisateur.

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();

Les instructions système définissent le comportement et les limites, tandis que le message de l’utilisateur contient les entrées en cours de modification. Les prompts font partie intégrante de la conception, enregistrant les hypothèses, en fixant le ton et en influençant la manière dont les demandes sont interprétées.

Cela donne naissance à une nouvelle discipline : l’ingénierie des prompts. Un prompt n’est plus simplement une chaîne de caractères jetable. C’est un artefact qui doit être versionné, testé et maintenu, et le traiter de cette façon est indispensable pour des systèmes stables. Les pages de référence de Spring AI sur les prompts ainsi que sur ChatClient constituent la lecture suivante logique.

Sortie structurée

L’un des premiers défis lors de l’intégration de l’IA dans les systèmes backend est la réponse non structurée. Les modèles génèrent naturellement du texte libre, alors que les applications d’entreprise ont besoin de données structurées.

Spring AI comble cette lacune en convertissant directement les réponses en objets Java. Cela élimine le besoin de parsing manuel et réduit les possibilités d’ambiguïté dans la lecture des résultats.

Au lieu de recevoir un paragraphe indiquant qu’une opération a échoué, l’agent de support utilise un enregistrement Java pour préciser quel répertoire et quelle classe sont probablement impliqués dans l’incident.

@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) {
}

Ici, le modèle est orienté pour produire une réponse pouvant être directement convertie en un objet fortement typé, ce qui transfère l’intégration d’un traitement de texte souple à des contrats explicites.

Les avantages sont importants. Une sortie non structurée oblige à un parsing fragile qui échoue dès que le libellé change légèrement. Une sortie structurée peut être validée, enregistrée et transmise comme n’importe quel autre type de données. La documentation officielle décrit l’approche de conversion.

Les versions plus récentes de Spring AI offrent également une mise en correspondance d’entités typées et, avec certains fournisseurs, une sortie structurée native, deux options qui méritent d’être examinées. Imposer le contrat près du modèle signifie que l’application a besoin de moins de code de parsing.

Appel d’outil

La génération de texte est utile, mais la véritable valeur apparaît lorsque l’IA peut interagir avec le système en récupérant des données, en déclenchant des workflows ou en exécutant de la logique métier.

Les outils de appel rendent cela possible en exposant des fonctions d’application que le modèle peut invoquer en fonction de son raisonnement. Le modèle ne doit pas être considéré comme un opérateur disposant d’un accès direct à l’infrastructure. Il demande une capacité donnée, et l’application décide si cette capacité existe, comment les arguments sont validés, quels données sont renvoyées et comment l’exécution est enregistrée.

Dans le projet, SupportAnalysisService expose un LogRetrievalTool pour l’étape d’analyse des journaux. Pour définir un outil, il suffit d’utiliser l’annotation @Tool ; son paramètre de description aide le modèle à déterminer quand appeler la fonction correspondante.

@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);
}

Lorsque le modèle décide qu’il a besoin de plus d’informations, il appelle la fonction, intègre le résultat et continue de générer. En fait, le modèle devient un orchestrateur des capacités du système.

Cependant, à mesure que les systèmes se développent, les intégrations codées en dur deviennent limitantes, car chaque nouvel outil ajoute un couplage entre le modèle et l’application.

Découverte d’outils avec MCP

Le Model Context Protocol remédie à ce problème. MCP standardise la manière dont les outils et services sont exposés aux modèles, permettant ainsi leur découverte dynamique sans qu’ils aient besoin d’être intégrés directement dans l’application.

MCP transforme les intégrations fixes en un écosystème dynamique où des fonctionnalités peuvent être ajoutées ou modifiées sans affecter l’application principale, ce qui constitue une étape fondamentale vers des architectures basées sur des agents scalables. La documentation relative aux appels d’outils de Spring AI décrit le modèle de programmation ainsi que le flux d’exécution. Lorsque les outils nécessitent une découverte dynamique ou doivent être partagés entre des processus, le support MCP représente la solution logique à adopter.

Génération améliorée par la récupération d’informations

Les modèles sont puissants, mais ils ignorent complètement les données de vos systèmes. Les clients, les produits et les processus internes restent inconnus à moins que des informations ne soient fournies.

RAG résout ce problème en ajoutant une étape de récupération d’informations avant la génération. Les documents pertinents sont extraits d’un stockage vectoriel et intégrés dans le prompt, permettant ainsi d’ancrer les résultats dans des données d’entreprise telles que des manuels opérationnels, des journaux de production et du code d’application.

Pour l’agent de support, RAG fournit une correction de code pour un problème de production donné, en utilisant la classe Java mentionnée dans les journaux d’application.

La première étape consiste pour SupportAgentController à récupérer des extraits de code via GithubRagService, ce dernier fonctionnant à partir du LogCauseReport qui indique le répertoire et la classe suspects.

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()));

À l’étape suivante, le service de récupération crée des filtres de métadonnées et effectue une recherche de similarité sur le stockage vectoriel 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());

La troisième étape injecte le code récupéré dans une demande visant à obtenir une correction.

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();

Cet effet consiste à faire passer le modèle d’un assistant général à un modèle qui maîtrise un domaine spécifique. RAG réduit principalement les hallucinations et garantit que les réponses soient en accord avec les connaissances internes, au prix d’une complexité accrue.

Il intègre des modèles d’embedding, des bases de données vectorielles et une logique de récupération, tous nécessitant une gestion et des ajustements. Dans un contexte d’entreprise, cette complexité est souvent inévitable, car la qualité des réponses dépend de plusieurs facteurs : la fraîcheur des données traitées, la qualité des métadonnées, la stratégie de segmentation, les autorisations et le suivi des preuves. La documentation de Spring AI sur RAG, les bases de données vectorielles et Qdrant aborde les concepts présentés dans cette section.

Observabilité, évaluation et contrôles opérationnels

L’observabilité pour l’IA fonctionne différemment. L’enregistrement et la surveillance conventionnels partent du principe d’un comportement déterministe, tandis que l’IA introduit des dimensions supplémentaires à suivre. Suivre les temps de réponse et les taux d’erreur s’avère insuffisant ; les équipes doivent également pouvoir consulter les prompts, les sorties des modèles, l’utilisation des tokens ainsi que les interactions avec les outils.

L’agent de support utilise Micrometer comme cadre d’observabilité, en capturant :

  • les identifiants et le contexte de chaque demande, tels que le ticket, l’appelant et le système source
  • le fournisseur et le nom du modèle, la version du prompt, la latence, l’utilisation des tokens et la raison de fin
  • le fait que la conversion en sortie structurée ait réussi ou non
  • chaque appel à un outil, avec des arguments normalisés, la taille du résultat et la latence
  • les répertoires, fichiers, scores vectoriels et extraits récupérés
  • le statut final du ticket, ainsi que le fait que l’examinateur humain ait accepté ou modifié la suggestion

Ce niveau de détail est essentiel pour le débogage et l’optimisation, car il explique pourquoi le système se comporte de la manière dont il le fait. Dans une application en production, les requêtes et les résultats sensibles ne devraient pas être enregistrés tels quels. Une alternative plus fiable est l’enregistrement structuré des événements, incluant l’ID de la requête, l’ID du modèle, le nombre de tokens et le résultat de la politique appliquée.

L’évaluation des résultats ajoute une couche supplémentaire. Les systèmes peuvent évaluer la qualité des réponses selon des critères prédéfinis, sans devoir supposer que les réponses sont correctes. Cela permet des schémas tels que les boucles évaluateur-optimiseur, où une réponse est affinée avant d’être transmise à l’utilisateur.

Cela modifie également la manière dont le système évolue. Chaque fois qu’une requête, un modèle d’incorporation, une méthode de segmentation ou une version de modèle change, le résultat peut être comparé à des cas connus, permettant à l’équipe de déterminer si ce changement améliore le flux de travail et ne dépend pas de quelques exemples impressionnants.

Au préalable de mettre l’assistant à disposition d’autres équipes, un ensemble de contrôles opérationnels a été envisagé pour le projet :

  • des délais d’attente stricts pour les appels vers le modèle, les outils et le stockage vectoriel
  • des budgets de tokens par requête et par utilisateur, afin de limiter les coûts et les risques de panne
  • des tentatives de réessai uniquement lorsque c’est sécurisé et encadré
  • des états de secours en cas de panne du modèle, d’un outil ou de Qdrant
  • la suppression des secrets, des identifiants et des données personnelles avant l’envoi des requêtes et l’enregistrement des journaux
  • l’enregistrement du contenu par défaut, ou uniquement sous des contrôles d’accès approuvés
  • des règles explicites de conservation pour les requêtes, les réponses, les embeddings et les résultats des outils
  • des tableaux de bord et des alertes pour les échecs de conversion, les résultats vides, les erreurs des outils et les rejets par les évaluateurs

Le taux de rejet par les évaluateurs reflète le mieux l’utilisation réelle. Lorsque les ingénieurs réécrivent ou rejettent régulièrement les recommandations générées, l’assistant ajoute une charge de travail inutile. Un système de production devrait tirer des leçons de ce signal. Un ajustement automatique sur tout n’est pas la bonne approche ; la voie à suivre est opérationnelle : améliorer les prompts, les outils, les mécanismes de récupération et les politiques en s’appuyant sur des données concrètes. La documentation sur l’observabilité de Spring AI décrit les outils intégrés.

Conception de workflows agents limités

En combinant tout ce qui précède, un nouveau modèle émerge : le système agent.

Les applications traditionnelles suivent des workflows prédéfinis. Les systèmes agents, quant à eux, exécutent un cycle itératif d’observation, de raisonnement et d’action. Ils gèrent un état, utilisent de la mémoire et décident dynamiquement quelles actions entreprendre.

L’état, la mémoire et l’autonomie deviennent des éléments essentiels, chacun correspondant à une capacité déjà offerte par Spring AI :

  • l’état correspond à ce qui est connu au cours de l’interaction en cours
  • la mémoire correspond à ce qui persiste entre les interactions
  • les outils définissent quelles actions sont autorisées
  • les règles de contrôle indiquent ce qui est interdit ou nécessite une confirmation
  • les critères de fin de cycle précisent quand le cycle doit s’arrêter

Cela modifie le rôle de l’application par rapport à un flux de travail d’entreprise classique. Au lieu de définir chaque étape, le système définit les capacités et les contraintes, permettant au modèle d’orchestrer le comportement dans ces limites.

Dans le cadre de l’agent de support, le processus commence par une demande. L’agent analyse cette demande, utilise des outils et récupère du code. Il rédige ensuite une recommandation et met à jour le ticket. Il ne s’agit pas d’un opérateur de production autonome, et il ne devrait pas en devenir un. En technologie financière, cette frontière détermine les actions que l’agent peut entreprendre dans un contexte donné. On peut faire confiance au modèle pour résumer les journaux d’activité bien avant de lui confier la modification d’un ticket.

Créer des agents efficaces est difficile, et la documentation de Spring AI sur ce sujet constitue un bon point de départ.

Des expériences aux systèmes d’entreprise

L’IA n’est pas simplement une autre fonctionnalité. Elle ajoute une nouvelle couche aux systèmes d’entreprise, une couche probabiliste, dynamique et étroitement liée à des dépendances externes. Sans structure, cette couche conduit rapidement à un comportement fragile et imprévisible.

Spring AI propose un moyen de l’intégrer tout en préservant les pratiques d’ingénierie qui assurent la fiabilité des systèmes d’entreprise. Des abstractions familières et des modèles d’intégration bien définis permettent aux équipes d’introduire l’IA sans renoncer à des contrats explicites, à des interactions contrôlées et à un comportement prévisible.

Ainsi, le changement ne concerne pas seulement l’ajout de fonctionnalités d’IA. Il s’agit d’évoluer les systèmes afin que l’IA devienne une partie utile, gérée et observable de l’architecture. La prochaine vague d’applications d’entreprise sera évaluée en fonction de la qualité de leur intégration avec l’IA, et la manière dont elles l’utilisent importera moins.

Lectures complémentaires

  • Google Cloud Next 2026 pour les équipes de données : Looker, BigQuery et le Cloud de données agenté — Quelles modifications apporte Google Cloud Next 2026 aux projets Looker et BigQuery : agents de tableau de bord, LookML pour l’analyse conversationnelle, leçons de migration et le Catalogue des connaissances.