<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title><![CDATA[IA - Elasticsearch Labs]]></title>
    <description><![CDATA[Articles and tutorials from the Search team at Elastic]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[IA - Elasticsearch Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1121c0bf0e8a6e65/6a88da6340a1841030ef456f/search-labs-thumbnail.png</url>
      <link>https://www.elastic.co/fr/search-labs/blog/category/ai</link>
    </image>
    <link>https://www.elastic.co/fr/search-labs/blog/category/ai</link>
    <atom:link href="https://www.elastic.co/fr/search-labs/rss/category/ai.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[fr]]></language>
    <lastBuildDate>Tue, 29 Sep 2026 09:34:13 GMT</lastBuildDate>
  <item>
    <title><![CDATA[Décrivez, ne dessinez pas : tableaux de bord Kibana IA natifs via MCP et ES|QL]]></title>
    <description><![CDATA[Du prompt au tableau de bord. Apprenez à créer des tableaux de bord Kibana en langage naturel grâce à example-mcp-dashbuilder : une application MCP open source qui écrit des requêtes ES|QL, crée des graphiques interactifs et exporte des tableaux de bord entièrement fonctionnels directement vers Kibana.]]></description>
    <content:encoded><![CDATA[<p>example-mcp-dashbuilder est une application MCP open source qui transforme un simple prompt de commande en un tableau de bord Kibana interactif et en temps réel, directement dans la fenêtre de chat de votre éditeur. Décrivez le tableau de bord souhaité : l'IA détecte la structure de votre index, génère les agrégations ES|QL appropriées pour chaque visualisation et affiche un aperçu en temps réel. Une fois la configuration terminée, une simple commande permet d'exporter un tableau de bord Kibana entièrement fonctionnel : visualisations Lens, disposition en grille exacte et couleurs personnalisées conservées. Six types de graphiques sont actuellement pris en charge ; l'ensemble des fonctionnalités de Kibana Lens sera intégré ultérieurement.</p><h2>Qu'est-ce qu'un générateur de tableaux de bord Kibana ?</h2><p>Et si vous pouviez décrire le tableau de bord que vous souhaitez en langage clair et le voir apparaître, avec des graphiques interactifs, une mise en page par glisser-déposer et une exportation vers Kibana en un clic ?</p><p>C'est exactement ce que fait <a href="https://github.com/elastic/example-mcp-dashbuilder.git"><strong>example-mcp-dashbuilder</strong></a>. Il s'agit d'une application open source (Model Context Protocol (MCP)) qui connecte les assistants IA à Elasticsearch, vous permettant de créer des tableaux de bord Kibana complets par chat. Pas besoin de cliquer dans les menus. Pas de configurations de visualisation à écrire manuellement. Il suffit de décrire ce dont vous avez besoin, et l'IA explore vos données, écrit les requêtes en langage de requête Elasticsearch (ES|QL), construit les graphiques et propose un tableau de bord interactif en direct, le tout dans la fenêtre de chat de votre éditeur.</p><h2><strong>Du prompt au tableau de bord en quelques secondes</strong></h2><p>Voici à quoi cela ressemble concrètement. Vous tapez quelque chose comme :</p><p>"Crée-moi un tableau de bord de trafic web à partir de logstash-* avec le nombre total de requêtes, les nombre d'octets transférés au fil du temps, les principales sources géographiques et une répartition des codes de réponse"</p><p>L'IA va alors :</p><ol><li><p><strong>Découvre vos données</strong> : liste les index, inspecte les mappings de champs.</p></li><li><p><strong>Rédige des requêtes ES|QL</strong> : adaptées à votre schéma, utilisant les bonnes agrégations.</p></li><li><p><strong>Crée des visualisations</strong> : graphiques à barres, graphiques linéaires, métriques avec sparklines, cartes thermiques, graphiques circulaires.</p></li><li><p><strong>Organise tout</strong> : sections pliables, titres compréhensibles, mise en page adéquate.</p></li><li><p><strong>Affiche un aperçu interactif</strong> : directement dans le chat, avec des infobulles, un sélecteur de temps et un système de glisser-déposer.</p></li></ol><p>Chaque graphique apparaît en ligne au fur et à mesure qu'il est créé, ce qui vous permet de voir les progrès réalisés en temps réel. Ensuite, <code>view_dashboard</code> affiche le tableau de bord complet avec tous les panneaux disposés dans la grille à 48 colonnes de Kibana.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt75af5d9042d141b5/6a17e99dbe608675a4004792/dcbf47c4f17bf1a184fb0167408ebeb861ef6c9d-1404x1568.png" alt="Deux graphiques affichés dans l'interface example-mcp-dashbuilder. Le premier est un graphique à barres verticales intitulé &quot;Principales sources géographiques&quot;, montrant le nombre de requêtes par code pays. Le second est un graphique circulaire intitulé &quot;Répartition des codes de réponse HTTP&quot;, montrant des segments pour les réponses 200, 404 et 503." /><p><em>Aperçu du graphique unique intégré.</em></p><h2><strong>Propulsé par ES|QL</strong></h2><p>Toutes les recherches de données utilisent <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql.html">ES|QL</a>, le langage de requête d'Elasticsearch. L'IA ne se contente pas de traiter les requêtes brutes, elle utilise aussi une connaissance intégrée d'ES|QL ainsi que des informations sur la structure de vos données pour écrire des requêtes correctes et efficaces pour chaque type de visualisation.</p><p>Le serveur inclut une référence ES|QL complète en tant que ressource MCP. Avant d'écrire une requête, l'IA lit cette référence pour comprendre les commandes, fonctions et schémas disponibles. Associée à un guide des bonnes pratiques de visualisation de données (également utilisé comme ressource), l'IA sait non seulement <em>comment</em> interroger, mais aussi <em>ce qui</em> génère une bonne visualisation :</p><ul><li><p>Utilisez <code>BUCKET(@timestamp, 1 day)</code> pour les séries temporelles ; toujours <code>SORT</code> par le champ temporel.</p></li><li><p>Limitez les graphiques circulaires à six tranches avec <code>| SORT value DESC | LIMIT 6</code>.</p></li><li><p>Choisissez des graphiques à barres pour les comparaisons de catégories, des graphiques linéaires pour les tendances, des métriques pour les indicateurs clés de performance (KPIs).</p></li></ul><h2><strong>Exploration de données pilotée par l'IA avec analyse ouverte</strong></h2><p>Créer un tableau de bord que vous avez déjà imaginé est une chose. Se demander "Qu'y a-t-il d'intéressant dans cet index?" et obtenir une réponse utile est plus difficile ; cela exige que l'IA sache <em>explorer</em>, et pas seulement dessiner.</p><p>Example-mcp-dashbuilder propose une ressource <code>analysis://guidelines</code> qui définit un flux d'exploration structuré : profiler les données, effectuer des agrégations ciblées, faire apparaître des schémas dignes d'être étudiés, créer des graphiques pour les résultats les plus intéressants et proposer des requêtes d'exploration que l'utilisateur pourrait souhaiter ensuite. Des expressions déclencheurs, telles que "analyser mes logs" ou "trouver des tendances dans cet index", incitent l'IA à lire le playbook avant toute autre action. Ainsi, une requête ouverte produit une analyse cohérente plutôt qu'une multitude de graphiques aléatoires.</p><p>Résultat : vous pouvez fournir à l'IA un index inconnu et obtenir en retour un point de départ : un tableau de bord accompagné d'une courte liste de questions du type "Voici ce que j'ai remarqué, voulez-vous que j'approfondisse certains de ces points ?".</p><h2><strong>Exportation et importation du tableau de bord Kibana : le processus complet</strong></h2><p>C'est au niveau de l'exportation/importation que example-mcp-dashbuilder devient réellement utile pour les équipes qui travaillent déjà avec Kibana. example-mcp-dashbuilder est un outil à part entière, une interface de tableau de bord conversationnelle intégrée à votre éditeur, mais qui ne confine pas votre travail à cet éditeur. Les tableaux de bord créés ici peuvent être transférés vers Kibana à votre guise, et inversement, les tableaux de bord Kibana existants peuvent être importés pour une édition assistée par l'IA.</p><h3><strong>Exporter vers Kibana</strong></h3><p>Lorsque vous êtes satisfait de votre tableau de bord, une commande permet de l'exporter :</p><p>"Exporter ce tableau de bord vers Kibana"</p><p>Chaque panneau est traduit en une véritable visualisation Kibana Lens. La traduction préserve :</p><ul><li><p><strong>Requêtes ES|QL</strong> : transférées directement en tant que sources de données Lens ES|QL.</p></li><li><p><strong>Positions de la grille</strong> : le même système à 48 colonnes que celui utilisé par Kibana, votre mise en page est donc identique.</p></li><li><p><strong>Couleurs personnalisées :</strong> Palettes de séries, arrière-plans de métriques, rampes de couleurs de carte thermique.</p></li></ul><p>Le résultat est un tableau de bord Kibana entièrement fonctionnel. Pas une capture d'écran. Pas une intégration. Un vrai tableau de bord que vous pouvez partager et continuer à modifier dans Kibana.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1921c74c2833cabe/6a17e99f6864a4a712b687da/5e27777bc0a82cafb373943f65298bdb21d66176-1999x902.png" alt="Deux tableaux de bord sont affichés côte à côte. Le tableau de bord de gauche intitulé &quot;Modifier le trafic web – Logstash (Dashbuilder)&quot; affiche les métriques de trafic récentes, ainsi qu'un panneau de volume de trafic, un histogramme géographique et un diagramme circulaire des codes de réponse. Le tableau de bord de droite propose une présentation similaire, mais avec des totaux plus élevés ; il comprend également un panneau de volume de trafic, un graphique à barres géographique et un graphique circulaire des codes de réponse." /><p><em>Tableau de bord Kibana et tableau de bord dans le chat Cursor côte à côte.</em></p><h3><strong>Importer depuis Kibana</strong></h3><p>L'aller-retour fonctionne également dans l'autre sens :</p><p>"Importer le tableau de bord Kibana avec l'identifiant abc-123"</p><p>Cette opération récupère un tableau de bord Kibana existant, traduit ses visualisations Lens en configurations de graphiques éditables, préserve la disposition de la grille et les sections, puis charge le tout dans example-mcp-dashbuilder. À partir de là, vous pouvez le modifier en langage naturel et le réexporter.</p><p>L'IA devient ainsi un collaborateur de votre workflow Kibana existant, sans le remplacer.</p><h2><strong>Thèmes et couleurs personnalisés</strong></h2><p>Vous souhaitez un tableau de bord personnalisé ? Il suffit de demander :</p><p>"Créer un tableau de bord à thème rose avec des couleurs personnalisées"</p><p>Chaque type de visualisation prend en charge la configuration personnalisée des couleurs :</p><ul><li><p><strong>Graphiques</strong> : <code>palette</code> accepte un tableau de couleurs hexadécimales pour les séries et les tranches.</p></li><li><p><strong>Métriques</strong> : <code>color</code> définit la couleur de fond.</p></li><li><p><strong>Cartes thermiques</strong> : <code>colorRamp</code> définit le gradient, des valeurs faibles aux valeurs élevées.</p></li></ul><p>L'IA interprète naturellement les demandes de thème. Par exemple, si vous dites "Thème Océan", elle choisira des bleus et des turquoises. Si vous dites "Respecter les couleurs de notre marque" et fournissez les valeurs hexadécimales, elles seront automatiquement intégrées à Kibana lors de l'exportation.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2bc7cddbdef81354/6a17e9a1ec0f89ee155a665e/4aceba013ac9cbb4a541109efd6acddf8a6ec47d-1562x1568.png" alt="Un tableau de bord e-commerce thématique aux couleurs roses personnalisées. La mise en page affiche les KPI relatifs au chiffre d'affaires et aux commandes en haut, une section de tendances réduite et deux graphiques par catégorie en dessous : un graphique à barres pour le chiffre d'affaires par catégorie et un graphique circulaire pour les commandes par catégorie." /><p><em>Un tableau de bord thématique avec des couleurs personnalisées.</em></p><p><strong>Fonctionnement de example-mcp-dashbuilder : architecture MCP</strong></p><p>example-mcp-dashbuilder est basé sur <a href="https://modelcontextprotocol.io/">MCP</a>, la norme ouverte permettant de connecter les assistants IA à des outils et données externes. Voici l'architecture générale :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6c0cd879646e9947/6a17e9a36864a4c408b687df/cbfeabe151ec1ee2b0655f4d17468c9bb358df7e-1024x559.png" alt="Diagramme d'architecture montrant l'hôte MCP connecté au serveur MCP, qui contient des outils, des ressources et des instructions. En dessous, un cadre de l'application MCP inclut les graphiques Elastic et la disposition en grille de Kibana. Elasticsearch et Kibana apparaissent en bas avec des flèches les reliant à l'application MCP." /><p>Le <strong>serveur MCP</strong> expose 25 outils directement accessibles à l'IA, permettant notamment l'exécution de requêtes ES|QL et l'exportation de tableaux de bord. Il propose également quelques outils internes "application uniquement", utilisés par l'aperçu intégré pour récupérer des données, enregistrer les modifications de mise en page et détecter les champs temporels. Trois ressources sont disponibles : un guide des bonnes pratiques de visualisation des données, une documentation de référence ES|QL et un playbook d'analyse approfondie, déclenché par des prompts ouverts ("analyse mes logs", "qu'y a-t-il d'intéressant dans cet index"). Le serveur fonctionne via les E/S standard (stdio) ou HTTP. Le protocole HTTP prend en charge les réponses par flux et la gestion des sessions, permettant ainsi à plusieurs clients de se connecter simultanément.</p><p>L'<strong>application MCP</strong> est l'aperçu interactif. Elle est développée avec React, <a href="https://elastic.github.io/elastic-charts">Elastic Charts</a>, et l'<a href="https://eui.elastic.co/">interface utilisateur Elastic</a>, le tout intégré dans un seul fichier HTML autonome. Lorsque l'IA appelle <code>view_dashboard</code> ou crée un graphique, l'hôte affiche ce HTML dans une iframe en sandbox. L'application communique avec le serveur exclusivement via le <a href="https://modelcontextprotocol.io/extensions/apps/overview">protocole MCP Apps</a>, en utilisant <code>callServerTool()</code> sur postMessage pour récupérer les données, enregistrer les mises en page et détecter les champs temporels. Il n'y a pas de serveur local, pas de port à configurer, aucune dépendance réseau externe.</p><p>Cela signifie qu'il fonctionne avec n'importe quel client compatible MCP : Cursor, Claude Desktop, Claude.ai, VS Code avec Copilot et bien d'autres.</p><h2><strong>Quels sont les types de graphiques pris en charge par example-mcp-dashbuilder ?</strong></h2><p>Au moment de la rédaction de cet article, six types de graphiques couvrant les scénarios de tableaux de bord les plus courants sont pris en charge :</p><p>Type</p><p>Idéal pour</p><p>Exemple</p><p>À barres</p><p>Comparaison des catégories</p><p>Requêtes par source géographique</p><p>Linéaire</p><p>Tendances au fil du temps</p><p>Octets transférés par heure</p><p>Zone</p><p>Volume au fil du temps</p><p>Volume de requêtes au fil du temps</p><p>Tarte</p><p>Partie du tout (six tranches maximum)</p><p>Distribution des codes de réponse</p><p>Métrique</p><p>KPI unique avec sparkline</p><p>Nombre total de requêtes avec tendance horaire</p><p>Carte thermique</p><p>Schémas en deux dimensions</p><p>Requêtes par jour de la semaine et heure</p><p>Les tableaux de bord prennent en charge les sections pliables pour l'organisation, un sélecteur temporel avec détection automatique des champs temporels, ainsi que la possibilité d'enregistrer et de basculer entre plusieurs tableaux de bord ; les sessions de chat parallèles restent isolées les unes des autres via un <code>dashboardId</code> intégré à chaque appel d'outil.</p><h2><strong>Comment installer et exécuter example-mcp-dashbuilder</strong></h2><p>example-mcp-dashbuilder est open source et prêt à être utilisé. Vous aurez besoin de Node.js 22+, d'une instance Elasticsearch (locale ou Elastic Cloud) et d'un client compatible MCP.</p><p><strong>Claude Desktop</strong> : téléchargez la dernière version <code>.mcpb</code> depuis <a href="https://github.com/elastic/example-mcp-dashbuilder/releases">GitHub Releases</a>, et double-cliquez dessus. Claude Desktop vous demandera vos identifiants Elasticsearch.</p><p><strong>Cursor/Claude Code/VS Code Copilot</strong> : indiquez à votre configuration MCP l'emplacement de l'archive tar publiée ; pas de clone, pas de <code>npm install</code> :</p>{
  "mcpServers": {
    "example-mcp-dashbuilder": {
      "type": "stdio",
      "command": "npx",
      "args": ["https://github.com/elastic/example-mcp-dashbuilder/releases/latest/download/example-mcp-dashbuilder.tgz"]
    }
  }
}<p>Définissez <code>ES_NODE, ES_API_KEY</code> (ou <code>ES_USERNAME / ES_PASSWORD</code>) et <code>KIBANA_URL</code> comme variables d'environnement. Si vous préférez travailler à partir de la source, clonez le dépôt et exécutez <code>npm run setup</code> pour lancer un assistant interactif qui gère à la fois Elasticsearch local et Elastic Cloud (Cloud ID + clé API).</p><p>Et commence à créer :</p><p>"Explorer l'index des logs et me créer le tableau de bord le plus pertinent possible ?"</p><p>L'IA prend le relais. 😉</p><h2><strong>Roadmap : nouveautés à venir concernant example-mcp-dashbuilder</strong></h2><p>Il s'agit d'une version préliminaire, et nous travaillons activement à son développement. Voici quelques axes sur lesquels nous nous concentrons :</p><ul><li><p><strong>Autres types de graphiques</strong> : jauge, diagramme en anneau, arborescence, table de données et nuage de tags pour correspondre à toutes les capacités de Lens.</p></li><li><p><strong>Transférez les tableaux de bord vers Git</strong> : inscrivez les configurations des tableaux de bord dans un référentiel pour le contrôle des versions et les workflows de révision du code.</p></li><li><p><strong>Meilleure expérience utilisateur en cas d'erreur</strong> : commentaires plus détaillés en cas d'échec des requêtes ES|QL, avec des suggestions de solutions courantes.</p></li><li><p><strong>Flux d'analyse plus riches</strong> : extension du playbook d'analyse approfondie pour couvrir davantage de formes de données (logs, métriques, traces).</p></li></ul><p>Nous serions ravis de savoir ce que vous allez en faire. Essayez-le, signalez les problèmes et faites-nous savoir quelles visualisations et quels workflows seraient les plus utiles pour votre équipe.</p><p><a href="https://github.com/elastic/example-mcp-dashbuilder">GitHub : elastic/example-mcp-dashbuilder</a></p><h3>Remerciements</h3><p>Merci à <a href="mailto:walter.rafelsberger@elastic.co">Walter Rafelsberger</a> et <a href="mailto:tim.schnell@elastic.co">Tim Schnell</a> pour leurs contributions à la mise en œuvre.</p><h3>FAQ</h3><p><strong>Qu'est-ce que example-mcp-dashbuilder ?</strong> example-mcp-dashbuilder est une application MCP (Model Context Protocol) open source qui connecte les assistants IA à Elasticsearch. Il vous permet de décrire un tableau de bord Kibana en langage clair, de générer automatiquement des requêtes ES|QL, de créer des visualisations et de diffuser un tableau de bord interactif en direct dans la fenêtre de chat de votre éditeur.</p><p><strong>Quel langage de requête example-mcp-dashbuilder utilise-t-il pour récupérer les données ?</strong> Toutes les récupérations de données utilisent ES|QL, le langage de requête canalisé d'Elasticsearch. Le serveur MCP inclut une référence ES|QL intégrée que l'IA lit avant d'écrire toute requête, garantissant une syntaxe correcte et des agrégations efficaces pour chaque type de visualisation.</p><p><strong>Puis-je exporter vers Kibana des tableaux de bord créés avec example-mcp-dashbuilder ?</strong> Oui. L'exécution de "Exporter ce tableau de bord vers Kibana" traduit chaque panneau en une véritable visualisation Kibana Lens, préservant les requêtes ES|QL, la mise en page de la grille à 48 colonnes, les couleurs personnalisées et les palettes de séries. Le résultat est un tableau de bord Kibana entièrement fonctionnel, et non une capture d'écran ou une intégration.</p><p><strong>Puis-je importer un tableau de bord Kibana existant dans example-mcp-dashbuilder pour une édition assistée par l'IA ?</strong> Oui. Il suffit de fournir l'identifiant du tableau de bord Kibana pour le récupérer, convertir ses visualisations Lens en configurations de graphique modifiables et les charger dans example-mcp-dashbuilder. Vous pouvez ensuite modifier le tableau de bord en langage naturel et le réexporter vers Kibana.</p><p><strong>Quels sont les clients MCP compatibles avec example-mcp-dashbuilder ?</strong> example-mcp-dashbuilder fonctionne avec n'importe quel client compatible MCP, y compris Cursor, Claude Desktop, Claude.ai, et VS Code avec Copilot. Il prend en charge les protocoles stdio et HTTP, sans aucun serveur local ni configuration de port nécessaire.</p><p><strong>Quels sont les types de graphiques pris en charge par example-mcp-dashbuilder ?</strong> La version actuelle prend en charge six types de graphiques : à barres, linéaire, à aires, en camembert, métriques (avec sparkline) et cartes thermiques. Les ajouts prévus incluent les graphiques à jauge, en anneau, arborescents, tables de données et nuages de tags afin de correspondre à l'ensemble des fonctionnalités de Kibana Lens.</p><p><strong>De quoi ai-je besoin pour exécuter example-mcp-dashbuilder ?</strong> Vous avez besoin de Node.js 22 ou version ultérieur, d'une instance Elasticsearch (locale ou Elastic Cloud) et d'un client compatible MCP. Définissez les variables d'environnement ES_NODE, ES_API_KEY (ou ES_USERNAME/ES_PASSWORD) et KIBANA_URL. Pour Claude Desktop, téléchargez le fichier .mcpb depuis les versions GitHub et double-cliquez dessus pour l'installer.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/kibana-dashboard-builder-mcp-esql</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/kibana-dashboard-builder-mcp-esql</guid>
    <category><![CDATA[Kibana]]></category>
    <category><![CDATA[IA]]></category>
    <dc:creator><![CDATA[Stratoula Kalafateli]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2a69a35d6d51ff47/6a17e9a5b1e11339cd79f2b3/0d38385fd64c1445b2e955ba20532570f7f38679-1280x720.png" length="0" type="image/png"/>
    <pubDate>Fri, 22 May 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Résolution d'entités avec Elasticsearch, partie 4 : le défi ultime]]></title>
    <description><![CDATA[Relever et évaluer les problématiques de réconciliation d’entités dans un ensemble de données complexe et varié, dont la structure interdit l’usage de méthodes simplifiées ou de contournements.]]></description>
    <content:encoded><![CDATA[<p>Nous avons maintenant vu la résolution intelligente des entités implémentée de deux manières. Les deux approches commencent de la même manière : préparation et extraction des entités, suivies de la récupération des candidats avec Elasticsearch. À partir de là, nous évaluons ces candidats en utilisant un grand modèle de langage (LLM), soit par génération JSON basée sur des invites, soit par appel de fonction, et nous demandons au modèle de fournir une explication transparente de son jugement.</p><p>Comme nous l’avons vu dans l’<a href="https://www.elastic.co/search-labs/blog/elasticsearch-entity-resolution-llm-function-calling">article précédent</a>, cette régularité, permise par l’appel de fonctions, constitue la pierre angulaire de la fiabilité du système, bien au-delà d’un simple gain d’efficacité. Une fois que nous avons éliminé les erreurs structurelles de la boucle d'évaluation, les résultats sur les scénarios standards (tels que ceux du jeu de données de niveau 4) se sont considérablement améliorés.</p><p>Il reste cependant une interrogation manifeste à laquelle il nous faut répondre :</p><p><em>Cette méthode est-elle toujours viable lorsque les données et les processus s’avèrent véritablement désordonnés ?</em></p><p>En pratique, ce ne sont pas les cas élémentaires qui mettent en défaut les systèmes de réconciliation d’entités. La résolution d’entités s’effondre dès que les noms se heurtent à la diversité des langues, des contextes culturels, des alphabets, des périodes historiques ou des structures administratives différentes. Le système s’effondre quand l’identification repose sur des titres honorifiques, des changements de noms de sociétés ou des translittérations aléatoires, et que seul le contexte environnant permet d’identifier l’entité physique derrière la mention textuelle.</p><p>Donc, pour le dernier billet de cette série, nous avons soumis le système à ce que nous avons appelé <strong>le défi ultime</strong>.</p><h2>Qu’est-ce qui fait de ce test le « défi ultime » ?</h2><p>Nous avons soumis le système à des tests progressifs, en employant des ensembles de données de plus en plus sophistiqués au fil des étapes de validation. Au moment d’atteindre le palier 4, le système gérait déjà des données hybrides mêlant appellations familières, titres honorifiques et variantes linguistiques, exigeant une analyse contextuelle fine. Les tests ont prouvé la pertinence de l’architecture globale, tout en révélant que des erreurs de structure de données, comme des syntaxes JSON incorrectes, bridaient artificiellement les performances de récupération.</p><p>Avec les appels de fonctions en place, nous avions enfin une base stable. Cela nous a donné l'occasion de poser une question plus intéressante :</p><p><em>Un pipeline unifié peut-il gérer </em><em><strong>plusieurs types</strong></em><em> de problèmes de résolution d'entités simultanément ?</em></p><p>Cet ensemble de données de test a été élaboré spécifiquement pour mettre à l’épreuve cette variable critique, en ne laissant aucune place à l’approximation.</p><p>Au lieu de se concentrer sur une seule difficulté (comme les surnoms ou la translittération), cet ensemble de données combine <strong>plus de 50 types de défis distincts</strong>, notamment :</p><ul><li><p>Conventions de dénomination culturelles.</p></li><li><p>Références basées sur les titres.</p></li><li><p>Relations commerciales et changements historiques de nom.</p></li><li><p>Mentions multilingues et systèmes d’écriture croisés.</p></li><li><p>Des défis complexes qui combinent plusieurs des éléments ci-dessus.</p></li></ul><p>L'essentiel, ce n'est pas d'optimiser pour un cas d'utilisation restreint. Il s'agit de vérifier si le <em>modèle de conception</em> tient la route lorsque les règles changent d'une entité à l'autre.</p><h2>L'ensemble de données en un coup d’œil</h2><p>L'ensemble de données du défi ultime consiste en :</p><ul><li><p><strong>50 entités</strong>, couvrant des personnes, des organisations et des institutions.</p></li><li><p><strong>~60 articles</strong>, dont la structure et la complexité linguistique varient.</p></li><li><p><strong>51 catégories de défis distinctes</strong>, regroupées globalement en :</p><ul><li><p>Conventions de dénomination culturelles.</p></li><li><p>Titres et contexte professionnel.</p></li><li><p>Relations commerciales et organisationnelles.</p></li><li><p>Les défis du multilinguisme et de la translittération.</p></li><li><p>Scénarios combinés et cas limites.</p></li></ul></li></ul><p>Plus tôt dans cette série, nous avons vu que l’utilisation de l’IA générative (GenAI) pour créer des jeux de données peut s’avérer être une arme à double tranchant. Sans elle, il serait extrêmement difficile de rassembler des données de test suffisamment vastes et diversifiées. Toutefois, sans un contrôle rigoureux, le modèle incline naturellement vers la simplification des cas de test.</p><p>On a remarqué, lors d’un premier passage de génération, que l’IA avait inséré des mentions telles que « le président russe » comme synonymes directs dans la fiche d’identité de Vladimir Poutine. Bien que cela paraisse logique à première vue, une telle approche invalide le test en supprimant la nécessité de comprendre le contexte pour identifier l’entité. Que se passe-t-il si l’article traite de la Russie des années 1990 ? L’objectif est que l’intelligence du moteur réside dans sa capacité d’inférence contextuelle plutôt que dans une simple table de correspondance statique.</p><p>C'est pourquoi cet ensemble de données a été délibérément conçu pour que les <strong>raccourcis ne fonctionnent pas</strong>. Les alias ne sont pas explicitement énumérés lorsque le système est censé en déduire la signification. Les phrases descriptives ne sont pas pré-liées à des entités. Les bonnes correspondances dépendent souvent du contexte de l'article, et pas seulement du texte local.</p><p><strong>Remarque importante :</strong> bien que nous démontrions les capacités du système dans divers scénarios, il s'agit toujours d'un prototype éducatif. Les systèmes de production gérant la surveillance d'entités sanctionnées dans le monde réel nécessiteraient une validation supplémentaire, des contrôles de conformité, des pistes d'audit et une gestion spécialisée pour les cas d'utilisation sensibles.</p><h2>Pourquoi ces scénarios sont difficiles</h2><p>Dès le premier article de cette série, nous avons introduit un exemple simple mais ambigu : « La nouvelle mise à jour de Swift est arrivée ! » Le défi réside dans le fait que « Swift » peut renvoyer à plusieurs entités du monde réel, selon le contexte. Cet exemple illustre une vérité plus profonde : le langage naturel est intrinsèquement ambigu.</p><p>La résolution d’entités n’est donc pas seulement un problème de correspondance de chaînes de caractères. Nous utilisons instinctivement notre bagage culturel et le contexte immédiat pour interpréter les références, une opération mentale si fluide qu’elle nous semble totalement naturelle.</p><p>Voici quelques cas courants :</p><ul><li><p>L’expression « le président » est une coquille vide si elle n’est pas ancrée dans une géographie et une époque données.</p></li><li><p>Le nom d’une entreprise peut désigner une société mère, une filiale ou une ancienne marque, selon la date à laquelle l’article a été rédigé.</p></li><li><p>Le nom d’une personne peut apparaître dans des ordres différents, des alphabets variés ou des translittérations diverses, selon la langue et la culture.</p></li><li><p>La même phrase peut légitimement faire référence à des entités différentes dans des contextes différents, et le système doit être en mesure de <em>rejeter</em> les correspondances avec autant d'assurance qu'il les accepte.</p></li></ul><p>Aucun système de règles figées ne peut, à lui seul, traiter l’intégralité de ces nuances de manière satisfaisante. Cette approche explique pourquoi ce prototype applique une séparation des préoccupations aussi stricte :</p><ul><li><p>Elasticsearch réduit l'espace réservé aux candidats de manière efficace et transparente.</p></li><li><p>Le LLM n’est utilisé que là où un jugement est requis, et il est contraint de justifier sa décision.</p></li><li><p>La récupération et le raisonnement demeurent des étapes distinctes.</p></li></ul><p>Cette distinction devient encore plus importante à mesure que la diversité des types de défis augmente.</p><h2>Comment le système gère la diversité sans recourir à des cas particuliers</h2><p>L'un des résultats les plus intéressants de cette évaluation est ce qui <em>n’a pas</em> changé :</p><ul><li><p>Nous <strong>n'avons pas</strong> ajouté de logique spéciale pour les noms japonais.</p></li><li><p>Nous <strong>n'avons pas</strong> ajouté de règles personnalisées pour les patronymes arabes.</p></li><li><p>Nous n'avons <strong>pas</strong> ajouté de mapping codé en dur pour les noms d'entreprises historiques.</p></li></ul><p>À la place, le système s’est appuyé sur les mêmes ingrédients fondamentaux présentés plus tôt dans cette série :</p><ul><li><p>Entités enrichies par le contexte et indexées pour la recherche sémantique.</p></li><li><p>La récupération hybride (exacte, alias et sémantique) dans Elasticsearch.</p></li><li><p>Un ensemble restreint et bien défini de candidats.</p></li><li><p>Le jugement du LLM est contraint par l’appel de fonctions et des schémas minimaux.</p></li></ul><p>Cela suggère que la flexibilité du système provient de la <strong>représentation et de l'architecture</strong>, et non d'une collection de règles qui ne cesse de croître.</p><p>Lorsque le système réussit, c’est parce que les bons candidats ont été récupérés et que le LLM dispose d’assez de contexte pour expliquer pourquoi une référence correspond (ou non) à une entité spécifique.</p><h2>Résultats : Comment s’est-il comporté ?</h2><p>Sur l’ensemble de données du défi ultime, le système a produit les résultats globaux suivants :</p><ul><li><p><strong>Précision :</strong> ~91 %</p></li><li><p><strong>Rappel :</strong> ~86 %</p></li><li><p><strong>Score F1 :</strong> ~89 %</p></li><li><p><strong>Taux d'acceptation des LLM :</strong> ~72 %</p></li></ul><h3>Performances selon les types de défis</h3><p>L’analyse des résultats par type de défi révèle des forces et des limites bien précises :</p><p><strong>Les performances les plus solides (un score F1 de 100 %)</strong> ont été observées dans des domaines tels que :</p><ul><li><p>Appariement entre différents systèmes d’écriture (entités commerciales en cyrillique, coréen ou chinois).</p></li><li><p>Scénarios en hébreu (patronymes, titres professionnels, titres religieux, translittération).</p></li><li><p>Hiérarchies d’entreprises (aérospatiale, industrie diversifiée, conglomérats multidivisionnels).</p></li><li><p>Titres professionnels (académiques, militaires, politiques, religieux).</p></li><li><p>Scénarios japonais combinés impliquant plusieurs systèmes d'écriture.</p></li></ul><p><strong>Une performance solide (score F1 de 80 à 99 %)</strong> a été enregistrée dans les catégories suivantes :</p><ul><li><p>Personnalités politiques internationales (98 %).</p></li><li><p>Changements de noms historiques (90 %).</p></li><li><p>Hiérarchies d’entreprise complexes (89 %).</p></li><li><p>Noms de sociétés japonais (93 %).</p></li><li><p>Translittération entre différents systèmes d’écriture (86 %).</p></li><li><p>Patronymes arabes (86 %).</p></li></ul><p>Les <strong>domaines les plus difficiles</strong> sont les suivants :</p><ul><li><p>Translittération avancée (chinois, coréen) : 0 % F1.</p></li><li><p>Certains scénarios japonais (titres honorifiques, ordre des noms, variations du système d’écriture) : ~67 % F1.</p></li><li><p>Quelques scénarios en arabe (noms d'entreprises, références institutionnelles) : ~40 % F1.</p></li></ul><p>Ce qui importe ici, c’est de comprendre <em>pourquoi</em> le système a éprouvé des difficultés dans ces cas précis. Les échecs n’étaient pas dus à une défaillance de l’approche globale, mais à des limitations de composants spécifiques, tout particulièrement le modèle de vecteurs denses utilisé pour la recherche sémantique dans certains scénarios multilingues.</p><p>La recherche et le jugement étant clairement séparés, il n’est pas nécessaire de réécrire le système pour améliorer les performances. L’intégration d’un modèle d’embedding multilingue plus performant, l’enrichissement du contexte des entités ou l’affinement des stratégies de récupération amélioreraient les résultats dans ces catégories sans modifier l’architecture de noyau.</p><p>Du point de vue architectural, c’est le véritable indicateur de réussite.</p><h2>Ce que ces résultats nous révèlent sur l'architecture du système</h2><p>Si l'on considère l'ensemble de la série, quelques tendances se dégagent :</p><ul><li><p><strong>La préparation est plus importante qu'une combinaison intelligente. </strong>L’enrichissement des entités avec leur contexte dès le départ réduit considérablement l’ambiguïté par la suite.</p></li><li><p><strong>Les LLM sont bien plus précieux en tant que juges qu’en tant qu’outils de recherche. </strong>Leur demander d'expliquer <em>pourquoi</em> une correspondance est logique est bien plus efficace que de leur demander de rechercher.</p></li><li><p><strong>La fiabilité permet la précision. </strong>L'appel de fonction n'a pas seulement nettoyé le JSON ; il a débloqué la récupération qui était déjà latente dans l'étape de récupération.</p></li><li><p><strong>La généralisation l’emporte sur la spécialisation. </strong>Un petit nombre d’abstractions bien choisies a permis de gérer des dizaines de types de défis sans avoir recours à une logique personnalisée.</p></li></ul><p>Cette approche explique pourquoi le prototype s’appuie nativement sur Elasticsearch tout en limitant l’usage des modèles de langage à une stricte nécessité. L’objectif n’est pas de se substituer aux moteurs de recherche classiques, mais d’apporter une couche d’explication quand la compréhension du contexte est cruciale.</p><h2>Conclusions</h2><p>L’enjeu final n’était pas d’atteindre des statistiques idéales, mais de s’attaquer à une interrogation bien plus essentielle :</p><p><em>Une architecture transparente, axée sur rechercher et assistée par LLM, peut-elle gérer l'ambiguïté des entités du monde réel sans s'effondrer en règles ou en boîtes noires ?</em></p><p>Pour ce prototype pédagogique, la réponse est oui, avec des réserves explicites concernant la mise en production, la conformité, la surveillance et la qualité des données. Si vous concevez des systèmes devant justifier <em>pourquoi</em> une correspondance d’entités a été établie, ce modèle mérite une attention toute particulière. J’espère que cette série de publications a démontré que la résolution d’entités n’a rien d’un processus mystérieux. Avec une séparation adéquate des responsabilités, la résolution d’entités devient un processus que l’on peut analyser, mesurer et améliorer.</p><p>Ce travail suggère également un modèle d’architecture plus large. On voit apparaître ici un glissement méthodologique important par rapport à l’architecture RAG classique. Au lieu de laisser la recherche alimenter directement la génération, nous introduisons une étape d’évaluation explicite. Le LLM est d’abord utilisé pour juger et vérifier la pertinence des candidats récupérés, et seuls les résultats approuvés sont autorisés à enrichir la génération. Vous pouvez voir cela comme un « Generation-Augmented Retrieval-Augmented Generation with Evaluation », ou GARAGE, parce que tout le monde adore les bons acronymes.</p><p>Quels autres cas d'utilisation pourraient bénéficier de ce modèle ? Les systèmes exigeant de la confiance, de la transparence et un raisonnement défendable sont des candidats naturels pour ce modèle. Les travaux futurs dans ce domaine s’annoncent tout aussi passionnants que les résultats présentés ici, et j’ai hâte de voir comment la communauté s’en emparera pour la suite.</p><h2>Prochaines étapes : À vous de jouer</h2><p>Envie de voir comment ce système relève le défi le plus complexe ? Consultez le <a href="https://github.com/jesslm/entity-resolution-lab-public/tree/main/notebooks#:~:text=5%20minutes%20ago-,05_ultimate_challenge_v3.ipynb,-Initial%20public%20lab"><strong>carnet de notes Ultimate Challenge</strong></a> pour une présentation complète avec des implémentations réelles, des explications détaillées et des exemples pratiques.</p><p>Le pipeline complet de résolution d'entités démontre les concepts fondamentaux et l'architecture nécessaires à une utilisation en production. Cette structure sert de fondation pour bâtir des outils de veille médiatique capables d’identifier des entités et de justifier chaque correspondance, garantissant ainsi la traçabilité des informations extraites.
</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/entity-resolution-elasticsearch-llm-challenges</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/entity-resolution-elasticsearch-llm-challenges</guid>
    <category><![CDATA[IA]]></category>
    <category><![CDATA[Recherche hybride]]></category>
    <dc:creator><![CDATA[Jessica Moszkowicz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc58be329ffebcd60/6a17043e47d49c0bc62d88ab/70fb0ff949f6db9ac9b8a28ecb4329ab915ebf46-720x420.png" length="0" type="image/png"/>
    <pubDate>Fri, 13 Mar 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Résolution d’entités avec Elasticsearch et les LLM, partie 2 : mise en correspondance d’entités avec le jugement des LLM et la recherche sémantique]]></title>
    <description><![CDATA[Utiliser la recherche sémantique et le jugement transparent des LLM pour la résolution d’entités dans Elasticsearch.]]></description>
    <content:encoded><![CDATA[<p>Dans<a href="https://www.elastic.co/search-labs/blog/entity-resolution-llm-elasticsearch"> la Partie 1</a>, nous avons préparé notre liste de surveillance et extrait les mentions d'entités. Nous sommes maintenant prêts à répondre à la question clé : à quelle entité une mention renvoie-t-elle réellement ? Revenons à l’exemple présenté dans le premier article de cette série, qui expliquait pourquoi nous avons besoin de la résolution d’entités : « The Swift update is here ! » Imaginons que ce titre soit accompagné d’un peu plus de contexte :</p><ol><li><p>La nouvelle mise à jour de Swift est arrivée ! Les développeurs sont impatients de tester les nouvelles fonctionnalités.</p></li><li><p>La nouvelle mise à jour de Swift est arrivée ! Le nouvel album sortira le mois prochain.</p></li></ol><p>Avec ce contexte supplémentaire, nous devrions être en mesure d’associer le nom « Swift » à la bonne entité.</p><p>Dans l'<a href="https://www.elastic.co/search-labs/blog/entity-resolution-llm-elasticsearch">article précédent</a>, nous avons constitué notre liste de surveillance et enrichi les entités avec un contexte supplémentaire. En reprenant nos exemples ci-dessus, nous devons disposer au minimum des deux entités suivantes dans la liste : Taylor Swift et le langage de programmation Swift. Nous avons également expliqué comment extraire les mentions d’entités à partir d’un texte. Dans ces deux exemples, la mention extraite serait « Swift ». Avec ces éléments en place — la liste de surveillance enrichie et les entités extraites — nous sommes enfin prêts à introduire la vedette du moment : la mise en correspondance des entités.</p><p><strong>Rappel :</strong> il s’agit d’un prototype pédagogique conçu pour illustrer les concepts de mise en correspondance d’entités. En production, les systèmes peuvent utiliser différents grands modèles de langage (LLM), des règles de correspondance personnalisées, des pipelines d’évaluation spécialisés ou encore des approches d’ensemble combinant plusieurs stratégies de correspondance.</p><h2>Le problème : pourquoi la mise en correspondance est complexe</h2><p>Le langage humain est une chose remarquable. L’une de ses caractéristiques les plus intéressantes est sa créativité sans fin. Nous pouvons générer et comprendre un nombre infini de nouvelles phrases. Dès lors, est-il surprenant que les correspondances exactes soient rares en résolution d’entités ? Les auteurs s’efforcent d’être créatifs dès qu’ils le peuvent. Il serait vite fastidieux de devoir écrire et lire les noms complets chaque fois qu’une entité est mentionnée. Ainsi, si les correspondances exactes sont simples, la réalité est que nous avons besoin d’une approche plus sophistiquée de la résolution d’entités : une approche suffisamment robuste pour gérer au moins une partie de la créativité sans limite des auteurs humains. C’est pourquoi nous décomposons le problème en deux étapes : utiliser Elasticsearch pour récupérer des candidats plausibles à grande échelle, puis recourir à un LLM pour déterminer si ces candidats renvoient réellement à la même entité du monde réel.</p><h2>La solution : une mise en correspondance en trois étapes avec un jugement LLM transparent</h2><p>Nous vivons un changement de paradigme dans notre manière d’utiliser les ordinateurs. Tout comme l’essor d’Internet nous a fait passer d’une informatique localisée à un réseau mondialement connecté, l’IA générative transforme en profondeur la façon dont le contenu, le code et l’information sont créés. En réalité, le prototype pédagogique qui accompagne cette série a été presque entièrement « vibe codé » à l’aide d’un LLM, avec des instructions soigneusement rédigées par l’auteur. Cela ne signifie pas que les LLM atteignent — ou atteindront — le niveau de productivité propre au langage humain, mais cela veut dire que nous disposons désormais d’une ressource puissante pour faciliter la résolution d’entités.</p><p>Un schéma courant avec l'IA générative est la génération augmentée par récupération (RAG). Ici, <em>récupération</em> signifie que l’on récupère des candidats d’entités (et non que l’on génère des réponses), et que le LLM est utilisé exclusivement pour évaluer les correspondances et en expliquer la logique. Bien que je <em>puisse</em> demander à un LLM de prendre en charge l’ensemble du processus de résolution d’entités, de bout en bout, cette approche serait coûteuse, tant en temps qu’en ressources financières. La RAG aide les LLM à accomplir leur tâche en leur fournissant du contexte de manière plus efficace, ce qui leur permet de contribuer plus efficacement à la résolution d’entités.</p><p>Pour la partie récupération de la RAG, nous faisons à nouveau appel à Elasticsearch. Nous identifions d’abord des correspondances potentielles en combinant la correspondance exacte, la correspondance sur des alias et la recherche hybride, qui associe recherche par mots-clés et recherche sémantique. Une fois ces correspondances potentielles identifiées, nous les transmettons à un LLM pour évaluation. Le LLM agit comme évaluateur final des correspondances. Nous demandons également au LLM d’expliquer son raisonnement, un élément différenciant important par rapport à d’autres systèmes de résolution d’entités. Sans ces explications, la résolution d’entités reste une boîte noire ; avec elles, nous pouvons comprendre pourquoi une correspondance est pertinente.</p><h2>Concepts clés : mise en correspondance en trois étapes, recherche hybride et jugement LLM transparent</h2><p><strong>Qu’est-ce que la mise en correspondance en trois étapes ?</strong> Au début de ce projet, nous avons émis l’hypothèse que la recherche sémantique jouerait un rôle clé dans le système, mais toutes les correspondances ne nécessitent pas un niveau de recherche aussi sophistiqué. Afin de trouver des correspondances efficacement, nous adoptons une approche progressive du problème. Tout d’abord, nous vérifions les correspondances exactes à l’aide de la recherche par mots-clés. Si nous trouvons une telle correspondance, le travail est terminé et nous pouvons passer à l’étape suivante. Si la correspondance exacte échoue, nous passons à la correspondance par alias. Dans le prototype, la correspondance par alias est également effectuée à l’aide d’une correspondance exacte sur des mots-clés, par souci de simplicité. En production, cette étape peut être enrichie par des règles de normalisation, de translittération, de correspondance approximative (fuzzy matching) ou par des tables d’alias maintenues. Si, après ces deux premières étapes, aucune correspondance potentielle n’a été trouvée, nous faisons appel à la recherche sémantique via la recherche hybride d’Elasticsearch, utilisant la méthode Reciprocal Rank Fusion (RRF).</p><p><strong>Qu’est-ce que la recherche hybride ?</strong> Dans Elasticsearch, nous pouvons utiliser la recherche sémantique pour identifier des correspondances pertinentes en tenant compte du contexte. Elasticsearch est largement utilisé pour la recherche vectorielle et la récupération hybride. La similarité sémantique est puissante pour capter le sens, mais elle ne remplace pas le filtrage structuré (par exemple, par plages temporelles, emplacements ou identifiants). Elle est souvent inutile lorsqu’une correspondance exacte est disponible. Elasticsearch s’est d’abord imposé grâce à la recherche lexicale, particulièrement efficace lorsque la recherche sémantique n’est pas adaptée. Pour tirer pleinement parti des deux approches, nous combinons la recherche lexicale et la recherche sémantique au sein d’une requête hybride unique. Nous fusionnons ensuite les résultats afin d’identifier les correspondances les plus probables à l’aide de la méthode RRF. Dans le prototype, les deux premiers résultats deviennent des correspondances potentielles pouvant être soumises à l’évaluation du LLM.</p><p><strong>Pourquoi faire appel au jugement LLM ?</strong> Les jugements et explications fournis par le LLM permettent à notre système de gérer l’ambiguïté et le contexte de manière transparente. C’est essentiel pour des cas comme « le président », qui peut désigner plusieurs entités selon le contexte. Cela permet également de gérer efficacement les surnoms et les variations culturelles. Enfin, lorsque nous traitons des tâches critiques — comme l’identification d’entités figurant sur des listes de sanctions — il est indispensable de comprendre pourquoi une correspondance a été acceptée afin de pouvoir faire confiance au système. Point essentiel : le LLM ne parcourt pas l’intégralité du corpus. Il évalue uniquement le petit ensemble de candidats renvoyé par Elasticsearch.</p><h2>Résultats concrets : mise en correspondance avec raisonnement du LLM</h2><p>Un défi majeur pour toute tâche de traitement automatique du langage naturel est la création d’un document de référence, une « answer key » indiquant quels sont les résultats attendus. Sans cela, il est quasiment impossible d’évaluer la performance d’un système sur une tâche donnée. Or, la création d’un tel document peut s’avérer laborieuse. Pour le prototype de résolution d’entités, nous avons de nouveau fait appel à l'IA générative afin de générer des données sur lesquelles nous pourrions effectuer des tests.</p><p>Nous avons d’abord défini plusieurs types de défis, comme les surnoms et la translittération, puis demandé au LLM de créer une collection hiérarchisée de jeux de données, devenant progressivement plus volumineux et plus complexes pour le système. La création des jeux de données s’est révélée moins simple qu’on aurait pu l’espérer. Le LLM avait une forte tendance à « tricher » en rendant la bonne réponse trop facile à trouver. Par exemple, l’un des types de défis portait sur le contexte sémantique. Ce type incluait des cas tels que faire correspondre « auteur russe » à « Leo Tolstoy ». Le LLM a incorrectement défini « auteur russe » comme un alias de « Leo Tolstoy », ce qui supprimait la nécessité d’une recherche hybride pour identifier la correspondance.</p><p>Après plusieurs refactorisations pour corriger ce type de problèmes, nous disposions de cinq niveaux d’ensembles de données. Les niveaux 1 à 4 devenaient progressivement plus volumineux et intégraient davantage de types de défis. Le niveau 5 constituait le « défi ultime », composé des exemples les plus complexes issus de tous les types de défis. L’ensemble des données de test est disponible dans un <a href="https://github.com/jesslm/entity-resolution-lab-public/tree/main/comprehensive_evaluation">répertoire d’évaluation complet</a>.</p><p>Pour évaluer notre approche de résolution d’entités basée sur des prompts, nous avons concentré notre analyse sur le jeu de données de niveau 4. Il est important de noter que l’évaluation a été menée dans le cadre d’une expérience contrôlée afin de nous concentrer sur la qualité de mise en correspondance des entités. Les données de la liste de correspondances ont été préalablement enrichies avec du contexte, et les entités ont été extraites de l’article en amont. Cela a permis de s’assurer que l’évaluation se concentrait sur la correspondance plutôt que sur la précision de l’extraction. Cela isole la qualité de correspondance ; les performances de bout en bout dépendraient en outre du rappel d'extraction et de la qualité d'enrichissement.</p><h3>Ensemble de données d’évaluation</h3><p>L'ensemble de données d'évaluation de niveau 4 fournit un test complet des capacités du système : [1]</p><ul><li><p><strong>Entités de la liste de surveillance :</strong> 66 entités couvrant différents types (personnes, organisations, lieux).</p></li><li><p><strong>Articles de test :</strong> 69 articles couvrant des scénarios réels de résolution d’entités.</p></li><li><p><strong>Correspondances attendues :</strong> 206 correspondances attendues pour l'ensemble des articles.</p></li><li><p><strong>Types de défis : </strong>15 types de défis différents mettant à l'épreuve divers aspects de la résolution d'entités.</p></li></ul><p>Les types de défis inclus dans l’ensemble de données sont les suivants :</p><ul><li><p><strong>Surnoms :</strong> « Bob Smith » → « Robert Smith » (sept articles).</p></li><li><p><strong>Titres et titres honorifiques : « Dr. »</strong> Sarah Williams » → « Sarah Williams » (cinq articles).</p></li><li><p><strong>Contexte sémantique :</strong> « auteur russe » → « Leo Tolstoy » (huit articles).</p></li><li><p><strong>Noms multilingues :</strong> traitement des noms dans différentes écritures (six articles).</p></li><li><p><strong>Entités commerciales :</strong> variations de noms d’entreprises (sept articles).</p></li><li><p><strong>Références exécutives : </strong>« PDG de Microsoft » → « Satya Nadella » (cinq articles).</p></li><li><p><strong>Dirigeants politiques :</strong> références basées sur un titre (cinq articles).</p></li><li><p><strong>Initiales :</strong> « J. Smith » → « John Smith » (trois articles).</p></li><li><p><strong>Variations dans l’ordre des noms :</strong> différentes conventions d’ordre des noms (trois articles).</p></li><li><p><strong>Noms tronqués :</strong> correspondances partielles de noms (trois articles).</p></li><li><p><strong>Découpage des noms :</strong> noms séparés dans le texte (trois articles).</p></li><li><p><strong>Espaces ou tirets manquants :</strong> variations de mise en forme (deux articles).</p></li><li><p><strong>Translittération :</strong> correspondance de noms entre différents systèmes d’écriture (deux articles).</p></li><li><p><strong>Défis combinés :</strong> plusieurs défis dans un même article (six articles).</p></li><li><p><strong>Cas d’entreprise complexes :</strong> relations hiérarchiques entre entités commerciales (cinq articles).</p></li></ul><p>Examinons comment la résolution d’entités basée sur des prompts s’est comportée.</p><h3>Performance globale</h3><p>Les résultats montrent un fort potentiel pour l’évaluation des correspondances assistée par LLM, mais ils mettent également en évidence un problème significatif de fiabilité. Chaque paire candidate doit être évaluée par le LLM. Des erreurs dans la sortie structurée peuvent réduire la précision et le rappel, même lorsque la phase de récupération fonctionne correctement.</p><p>Métrique</p><p>Valeur</p><p>Précision</p><p>83,8 %</p><p>Rappel</p><p>62,6 %</p><p>Score F1</p><p>71,7 %</p><p>Nombre total de correspondances trouvées</p><p>344</p><p>Taux d'acceptation des LLM</p><p>44,8 %</p><p>Taux d'erreur</p><p>30,2 %</p><h3>Le problème du taux d'erreur</h3><p>Rappelons que la première étape du prototype consiste à créer des paires de correspondances potentielles à l’aide d’Elasticsearch. Chacune de ces correspondances potentielles doit ensuite être évaluée par le LLM. Pour traiter efficacement l’ensemble de ces correspondances, nous regroupons les appels au LLM par lots. Cela réduit les coûts d’API et la latence, mais augmente également le risque d’obtenir un JSON mal formé en sortie. À mesure que la taille des lots augmente, le JSON devient plus long et plus complexe, ce qui accroît la probabilité que le LLM génère un JSON invalide. C’est de là que provient le taux d’erreur de 30 %. Dans cette évaluation, nous avons utilisé une taille de lot de cinq correspondances par requête. Même avec cette taille de lot conservatrice, nous constatons toujours des échecs d'analyse JSON, ce qui fausse considérablement les résultats de l'évaluation.</p><h2>Prochaine étape : optimiser l’intégration des LLM</h2><p>Maintenant que nous avons mis en correspondance des entités à l’aide de la recherche sémantique et du jugement d’un LLM, nous disposons d’un pipeline complet de résolution d’entités. Cependant, cette approche introduit un nouveau mode de défaillance : le jugement du modèle peut être correct, mais sa sortie inutilisable. Nous pouvons optimiser l’intégration du LLM afin d’améliorer la fiabilité et la rentabilité. Dans le prochain article, nous verrons comment utiliser le function calling pour produire une sortie structurée, garantissant une structure et un typage sûrs, tout en réduisant les erreurs et les coûts.</p><h2>Essayez par vous-même</h2><p>Envie de voir la mise en correspondance d’entités en action ? Consultez le <a href="https://github.com/jesslm/entity-resolution-lab-public/tree/main/notebooks#:~:text=5%20minutes%20ago-,03_entity_matching_v3.ipynb,-Initial%20public%20lab">carnet de notes sur l'appariement des entités</a> pour une présentation complète avec des implémentations réelles, des explications détaillées et des exemples pratiques. Le carnet vous montre exactement comment faire correspondre les entités à l'aide de la recherche en trois étapes, de la recherche hybride avec RRF et du jugement raisonné basé sur le LLM.</p><p><strong>Rappel :</strong> il s’agit d’un prototype pédagogique conçu pour illustrer les concepts. Lors de la mise en œuvre d’un système en production, tenez compte de facteurs supplémentaires tels que la sélection du modèle, l’optimisation des coûts, les exigences en matière de latence, la validation de la qualité, la gestion des erreurs et la supervision — des aspects qui ne sont pas couverts dans ce prototype à visée pédagogique.</p><h2>Remarques</h2><ol><li><p>Ces ensembles de données sont synthétiques et conçus à des fins pédagogiques. Ils reflètent des défis réels, mais ne représentent aucun domaine de production spécifique.</p></li></ol>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-entity-resolution-llm-semantic-search</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-entity-resolution-llm-semantic-search</guid>
    <category><![CDATA[IA]]></category>
    <category><![CDATA[Recherche hybride]]></category>
    <dc:creator><![CDATA[Jessica Moszkowicz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltefc59243d9990405/6a17056ab339d5778f769ebf/473ca4357c7d60f690edbd2a844acda169aca9c3-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 26 Feb 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Automatisation de l'analyse des logs dans Streams avec le ML]]></title>
    <description><![CDATA[Découvrez comment une approche hybride de ML a atteint une précision de 94 % pour l'analyse syntaxique des logs et 91 % pour le partitionnement des logs grâce à des expériences d’automatisation avec l’empreinte des formats de log dans Streams.]]></description>
    <content:encoded><![CDATA[<p>Dans les piles d'observabilité modernes, l'ingestion de logs non structurés provenant de divers fournisseurs de données dans des plateformes comme Elasticsearch reste un défi. La dépendance à des règles de traitement manuellement créées engendre des pipelines fragiles, où même des mises à jour mineures du code en amont entraînent des échecs de traitement et des données non indexées. Cette fragilité est aggravée par le défi de la scalabilité : dans les environnements de microservices dynamiques, l’ajout continu de nouveaux services transforme la maintenance manuelle des règles en un cauchemar opérationnel.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte8f5bd0e4986b04c/6a170e6acdacbf612e7d2a9e/9108ec303339dd091faa3c363c7cf5c228155f49-3840x2160.png" alt="" /><p>Notre objectif était de passer à une approche automatisée et adaptative capable de gérer à la fois l’analyse des logs (extraction de champs) et le partitionnement des logs (identification de la source). Nous avons émis l’hypothèse que les grands modèles de langage (LLM), grâce à leur compréhension inhérente de la syntaxe du code et des modèles sémantiques, pourraient automatiser ces tâches avec une intervention humaine minimale.</p><p>Nous sommes heureux d'annoncer que cette fonctionnalité est déjà disponible dans <a href="http://elastic.co/elasticsearch/streams"><u>Streams</u></a>.</p><h2>Description de l'ensemble de données</h2><p>Nous avons choisi une <a href="https://github.com/logpai/loghub"><strong>Loghub</strong></a>collection de logs à des fins de PoC. Pour notre enquête, nous avons sélectionné des échantillons représentatifs issus des domaines clés suivants :</p><ul><li><p>Systèmes distribués : nous avons utilisé le HDFS (Hadoop Distributed File System) et les ensembles de données Spark. Ils contiennent un mélange de messages d'information, de débogage et d'erreur typiques des plateformes de big data.</p></li><li><p>Applications serveur et web : les logs des serveurs web Apache et d’OpenSSH ont constitué une source précieuse d’informations sur les accès, les erreurs et les événements liés à la security. Ces éléments sont essentiels pour surveiller le trafic web et détecter les menaces potentielles.</p></li><li><p>Systèmes d'exploitation : nous avons inclus les logs de Linux et Windows. Ces ensembles de données représentent les événements communs et semi-structurés au niveau du système auxquels les équipes d’opérations sont confrontés quotidiennement.</p></li><li><p>Systèmes mobiles : pour garantir que notre modèle puisse gérer les logs provenant d'environnements mobiles, nous avons inclus l'ensemble de données Android. Ces logs sont souvent volumineux et capturent un large éventail d'activités au niveau de l'application et du système sur les appareils mobiles.</p></li><li><p>Superordinateurs : pour tester les performances sur des environnements de calcul haute performance (HPC), nous avons intégré l'ensemble de données BGL (Blue Gene/L), qui présente des logs très structurés avec une terminologie de domaine spécifique.</p></li></ul><p>L'un des principaux avantages de la collection Loghub est que les logs sont en grande partie non nettoyés et non étiquetés, reflétant un environnement de production en direct bruyant avec une architecture de microservices.</p><p>Exemples de logs :</p>[Sun Dec 04 20:34:21 2005] [notice] jk2_init() Found child 2008 in scoreboard slot 6
[Sun Dec 04 20:34:25 2005] [notice] workerEnv.init() ok /etc/httpd/conf/workers2.properties
[Mon Dec 05 11:06:51 2005] [notice] workerEnv.init() ok /etc/httpd/conf/workers2.properties
17/06/09 20:10:58 INFO output.FileOutputCommitter: Saved output of task 'attempt_201706092018_0024_m_000083_1138' to hdfs://10.10.34.11:9000/pjhe/test/1/_temporary/0/task_201706092018_0024_m_000083
17/06/09 20:10:58 INFO mapred.SparkHadoopMapRedUtil: attempt_201706092018_0024_m_000083_1138: Committed<p>De plus, nous avons créé un cluster Kubernetes avec une application web typique et une base de données pour extraire des logs supplémentaires dans le domaine le plus courant.</p><p>Exemple de champs de log communs : horodatage, niveau de log (INFO, AVERTISSEMENT, ERREUR), source, message.</p><h2>Analyse de logs en few-shot avec un LLM</h2><p>Notre premier ensemble d'expériences s'est concentré sur une question fondamentale : <strong>Un LLM peut-il identifier de manière fiable les champs clés et générer des règles de parsing cohérentes pour les extraire ?</strong></p><p>Nous avons demandé à un modèle d'analyser des échantillons de journaux bruts et de générer des règles d'analyse de journaux sous forme d'expressions régulières (regex) et de formats <a href="https://www.elastic.co/docs/explore-analyze/scripting/grok">Grok</a>. Nos résultats ont montré que cette approche présente beaucoup de potentiel, mais aussi des défis importants dans sa mise en œuvre.</p><h3>Confiance élevée et conscience du contexte</h3><p>Les premiers résultats étaient prometteurs. Le LLM a démontré une forte capacité à générer des règles d'analyse qui correspondaient aux exemples fournis avec une grande confiance. Outre la simple correspondance de modèles, le modèle a démontré une capacité de compréhension des logs — il pouvait identifier et nommer correctement la source du log (par exemple, application de suivi de santé, application web Nginx, base de données Mongo).</p><h3>Le dilemme des échantillons d'entrée « Boucles d'or »</h3><p>Nos expériences ont rapidement mis en évidence un manque important de robustesse en raison d'une<strong> sensibilité extrême à l'échantillon d'entrée.</strong> Les performances du modèle fluctuent considérablement en fonction des exemples de logs spécifiques inclus dans l'invite. Nous avons observé un problème de similitude des logs, dans lequel l'échantillon de log devait inclure <em>juste assez de logs diversifiés </em> :</p><ul><li><p>Trop homogène (surapprentissage)<strong> :</strong> si les logs d'entrée sont trop similaires, le LLM a tendance à <strong>surspécifier</strong>. Il traite les données variables — telles que des noms spécifiques de classes Java dans une trace de pile — comme des parties statiques du modèle. Il en résulte des règles fragiles qui ne couvrent qu'une infime partie des logs et extraient des champs inutilisables.</p></li><li><p>Trop hétérogène (confusion) : à l'inverse, si l'échantillon contient une variance de formatage significative, ou pire, des « logs poubelles » comme des barres de progression, des tableaux de mémoire ou de l'art ASCII, le modèle peine à trouver un dénominateur commun. Il en vient souvent à générer des regex complexes et cassés ou à généraliser paresseusement toute la ligne en un seul champ de bloc de message.</p></li></ul><h3>La contrainte de la fenêtre contextuelle</h3><p>Nous avons également rencontré un goulot d'étranglement au niveau de la fenêtre contextuelle. Lorsque les logs d'entrée étaient longs, hétérogènes, ou riches en champs extractibles, la sortie du modèle se détériorait souvent, devenant « désordonnée » ou trop longue pour s'intégrer dans la fenêtre de contexte de sortie. Bien entendu, le découpage aide dans ce cas. En divisant les logs à l'aide de délimiteurs basés sur les caractères et sur les entités, nous pourrions aider le modèle à se concentrer sur l'extraction des champs principaux sans être submergé par le bruit.</p><h3>L'écart de cohérence et de standardisation</h3><p>Même lorsque le modèle a généré des règles avec succès, nous avons noté de légères incohérences :</p><ul><li><p>Variantes de dénomination des services : le modèle propose différents noms pour une même entité (par exemple, en étiquetant la source « Spark », « Apache Spark » et « Spark Log Analytics » dans différentes exécutions).</p></li><li><p>Variations dans la dénomination des champs : les noms des champs n'étaient pas normalisés (par exemple, <code>id</code> vs. <code>service.id</code> vs. <code>device.id</code>). Nous avons normalisé les noms en utilisant une <a href="https://www.elastic.co/docs/reference/ecs/ecs-field-reference">nomenclature de champ Elastic standardisée</a>.</p></li><li><p>Variance de résolution : la résolution de l'extraction de champ variait en fonction de la similarité des logs d'entrée les uns avec les autres.</p></li></ul><h2>Empreinte de format de log</h2><p>Pour relever le défi de la similarité des logs, nous introduisons une heuristique haute performance : <strong>l'empreinte de format de log (LFF)</strong>.</p><p>Au lieu d'introduire des logs bruts et bruyants directement dans un LLM, nous appliquons d'abord une transformation déterministe pour révéler la structure sous-jacente de chaque message. Cette étape de pré-traitement rend abstraites les données variables, générant une « empreinte » simplifiée qui nous permet de regrouper les logs connexes.</p><p>La logique de mapping est simple pour garantir la vitesse et la cohérence :</p><ol><li><p>Abstraction des chiffres : toute séquence de chiffres (0-9) est remplacée par un simple ‘0’.</p></li><li><p>Abstraction du texte : toute séquence de caractères alphabétiques avec des espaces blancs est remplacée par un seul « a ».</p></li><li><p>Normalisation des espaces blancs : toutes les séquences d'espaces blancs (espaces, tabulations, nouvelles lignes) sont regroupées en un seul espace.</p></li><li><p>La préservation des symboles : la ponctuation et les caractères spéciaux (par exemple, :, [, ], /) sont préservés, car ils sont souvent les indicateurs les plus forts de la structure des logs.</p></li></ol><p>Nous introduisons l'approche de mapping des logs. Les modèles de mapping de base comprennent les éléments suivants :</p><ul><li><p>Chiffres 0 à 9, quelle que soit leur longueur, de &gt; à « 0 ».</p></li><li><p>Texte (caractères alphabétiques avec espaces) de n'importe quelle longueur -&gt; à 'a'.</p></li><li><p>Espaces blancs, onglets et nouvelles lignes : &gt; pour un seul espace.</p></li></ul><p>Examinons un exemple de la manière dont ce mapping nous permet de transformer les logs.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf91eebab0ad79ccd/6a170e6c67045ba94f45c29c/78fa2887486eb9417804354ee3bf2a4fdb0f6383-846x252.png" alt="" /><p>Par conséquent, nous obtenons les masques de log suivants :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt438d74dcb921578b/6a170e6d1949f74aa0e7aae3/ec439a3d3a25002498b97defcff733ea5ebc6b55-826x94.png" alt="" /><p>Remarquez les empreintes digitales des deux premiers logs. Malgré des horodatages, des classes de sources et un contenu de message différents, leurs préfixes (<code>0/0/0 0:0:0 a a.a:</code>) sont identiques. Cet alignement structurel nous permet de regrouper automatiquement ces logs dans le même cluster.</p><p>Le troisième log, cependant, produit une empreinte digitale complètement divergente (<code>0-0-0...</code>). Cela nous permet de le séparer algorithmiquement du premier groupe <em>avant même d'</em> invoquer un LLM.</p><h2>Partie bonus : implémentation instantanée avec ES|QL</h2><p>C'est aussi simple que de passer cette requête dans Discover.</p><p><strong>Décomposition de la requête :</strong></p><p><strong>FROM</strong> loghub : cible notre index contenant les données de journal brut.</p><p><strong>EVAL</strong> pattern = ... : la logique du mapping de base. Nous enchaînons les fonctions REPLACE pour effectuer l'abstraction (par exemple, les chiffres en '0', le texte en 'a', etc.) et enregistrons le résultat dans un champ "pattern".</p><p><strong>STATS </strong>[column1 =] expression1, …<strong> BY </strong>SUBSTRING(pattern, 0, 15) :</p><p>Ceci est une étape du clustering. Nous regroupons les logs qui partagent les 15 premiers caractères de leur schéma et créons des champs agrégés tels que le nombre total de logs par groupe, la liste des sources de données des logs, le préfixe du schéma, 3 exemples de logs</p><p><strong>SORT</strong> total_count DESC | <strong>LIMIT</strong> 100 : affiche les 100 schémas de log les plus fréquents</p><p>Les résultats de la requête sur LogHub sont affichés ci-dessous :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfa3960cf94ccf331/6a170e6fdc55decfa3e00e7c/b119498f124376c41d242a099bf9081fd6536be8-1600x394.png" alt="Résultats de la requête d'analyse des logs sur LogHub." /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2dbcde2a22e06367/6a170e71961e693a18c4cfb6/4dcfc0a5b7fa753497cc5def5ea3cd54449c0481-1600x719.png" alt="" /><p>Comme démontré dans la visualisation, cette approche « sans LLM » partitionne les logs avec une grande précision. Elle a réussi à clusterer complètement 10 sources de données sur 16 (basées sur les étiquettes LogHub) (&gt;90 %) et a atteint un clustering majoritaire dans 13 sources sur 16 (&gt;60 %) — le tout sans nécessiter de nettoyage, de prétraitement ou de réglage fin supplémentaire.</p><p>L'empreinte digitale du format du log offre une alternative pragmatique et à fort impact, ainsi qu'un complément aux solutions de ML sophistiquées telles que l'<a href="https://www.elastic.co/docs/reference/aggregations/search-aggregations-bucket-categorize-text-aggregation">analyse du modèle de log</a>. Il offre des informations immédiates sur les relations entre les logs et gère efficacement les grands ensembles de logs.</p><ul><li><p>Polyvalence en tant que primitif </p></li></ul><p>Grâce à la mise en œuvre d'<a href="https://www.elastic.co/blog/getting-started-elasticsearch-query-language">ES|QL</a>, LFF sert à la fois d'outil autonome pour des diagnostics/visualisations rapides des données, et d'élément de base dans les pipelines d'analyse des journaux pour les cas d'utilisation à grand volume. </p><ul><li><p>Flexibilité</p></li></ul><p>LFF est facile à personnaliser et à étendre pour capturer des modèles spécifiques, c'est-à-dire des nombres hexadécimaux et des adresses IP.</p><ul><li><p>Stabilité déterministe</p></li></ul><p>Contrairement aux algorithmes de clustering basés sur le ML, la logique LFF est simple et déterministe. Les nouveaux logs entrants n'affectent pas rétroactivement les clusters de logs existants.</p><ul><li><p>Performance et mémoire</p></li></ul><p>Il nécessite peu de mémoire, pas de formation ni de GPU, ce qui le rend idéal pour les environnements à haut débit et en temps réel.</p><h2>Combiner une empreinte digitale au format log avec un LLM</h2><p>Pour valider l'architecture hybride proposée, chaque expérience contenait un sous-ensemble aléatoire de 20 % des logs de chaque source de données. Cette contrainte simule un environnement de production réel où les logs sont traités par lots plutôt que comme un bloc monolithique de données historiques.</p><p>L’objectif était de démontrer que le LFF agit comme une couche de compression efficace. Nous avons cherché à prouver que des règles d’analyse à haute couverture pouvaient être générées à partir de petits échantillons sélectionnés et généralisées avec succès à l’ensemble de l’ensemble de données.</p><h2>Pipeline d'exécution</h2><p>Nous avons mis en œuvre un pipeline à plusieurs étapes qui filtre, regroupe et applique un échantillonnage stratifié aux données avant qu'elles n'atteignent le LLM.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt26635762891b3a41/6a170e73509168eea4e1bb91/b3f46ea471760b406a32fc7d4bc74cc03faaced2-3840x1660.png" alt="" /><p>1. clustering hiérarchique en deux étapes</p><ul><li><p>Sous-classes (correspondance exacte) : les logs sont agrégés par empreintes identiques. Tous les logs d'une sous-classe partagent exactement la même structure de format.</p></li><li><p>Nettoyage des aberrations. Nous écartons toutes les sous-classes qui représentent moins de 5 % du volume total des logs. Cela garantit que le LLM se concentre sur le signal dominant et ne sera pas distrait par le bruit ou les logs malformés.</p></li><li><p>Métaclasses (correspondance avec le préfixe) : les sous-classes restantes sont regroupées en métaclasses en fonction des N premiers caractères de l'empreinte digitale du format. Nous avons choisi N=5 pour le Log parsing et N=15 pour le Log partitioning lorsque les sources de données sont inconnues.</p></li></ul><p>2. Échantillonnage stratifié. Une fois l'arbre hiérarchique établit, nous construisons l'échantillon de log pour le LLM. L'objectif stratégique est de maximiser la couverture de variance tout en minimisant l'utilisation des jetons.</p><ul><li><p>Nous sélectionnons des logs représentatifs de <em>chaque</em> sous-classe valide au sein de la métaclasse plus large.</p></li><li><p>Pour gérer un cas limite de sous-classes trop nombreuses, nous appliquons un échantillonnage aléatoire pour s'adapter à la taille de la fenêtre cible.</p></li></ul><p>3. Génération de règles Finally, nous demandons au LLM de générer une règle d'analyse regex qui convient à tous les logs dans l'échantillon fourni pour chaque Métaclasse. Pour notre PoC, nous avons utilisé le mini-modèle GPT-4o.</p><h2>Résultats expérimentaux et observations</h2><p>Nous avons atteint une précision de parsing de 94 % et une précision de partitionnement de 91 % sur l'ensemble de données Loghub.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1b896b41b3b70e7e/6a170e757d8d67601a70e7d9/49b2b6a1401dd1f33951da68e5a3fac37d0b5aaa-1600x1506.png" alt="94 % de précision de parsing et 91 % de précision de partitionnement sur l’ensemble de données Loghub." /><p>La matrice de confusion ci-dessus illustre les résultats du partitionnement des logs. L’axe vertical représente les sources de données réelles, et l’axe horizontal représente les sources de données prédites. L'intensité de la carte thermique correspond au volume de logs, les vignettes plus claires indiquant un nombre plus élevé. L'alignement diagonal démontre la haute fidélité du modèle dans l'attribution des sources, avec un minimum de dispersion.</p><h2>Nos informations sur les benchmarks de performance :</h2><ul><li><p><strong>Base optimale :</strong> une fenêtre contextuelle de <strong>30 à 40 échantillons de logs</strong> par catégorie s'est avérée être le « point idéal », produisant régulièrement une analyse syntaxique robuste avec des motifs Regex et Grok.</p></li><li><p><strong>Minimisation de l'entrée :</strong> nous avons réduit la taille de l'entrée à 10 logs par catégorie pour les motifs Regex et n'avons observé qu'une baisse de 2 % des performances d'analyse, ce qui confirme que l'échantillonnage basé sur la diversité est plus critique que le volume brut.</p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/log-parsing-partitioning-automation-experiments-streams</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/log-parsing-partitioning-automation-experiments-streams</guid>
    <category><![CDATA[Recherche ML]]></category>
    <category><![CDATA[IA]]></category>
    <dc:creator><![CDATA[Nastia Havriushenko]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc1df5a7cae463d59/6a170e76a6c2b907d7e797ab/965c58f19742361160593c38fcaa8b2f4b0d6cc5-3838x2159.png" length="0" type="image/png"/>
    <pubDate>Fri, 02 Jan 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Premiers pas avec Elastic Agent Builder et le SDK Strands Agents]]></title>
    <description><![CDATA[Découvrez comment créer un agent avec Elastic Agent Builder, puis explorez comment utiliser cet agent via le protocole A2A orchestré à l’aide du SDK Strands Agents.]]></description>
    <content:encoded><![CDATA[<p>Vous avez une idée d’agent IA ? Cela implique sans doute d’exploiter des données : pour que l’agent exécute une action utile, il doit prendre une décision, et pour cela, il lui faut les bonnes données.</p><p>Elastic Agent Builder simplifie la création d’agents IA connectés aux données. Nous vous expliquons comment faire dans cet article de blog. Voici toutes les étapes pour créer un agent avec un outil MCP capable d’accéder aux données stockées dans Elastic. Nous utiliserons ensuite le SDK Strands Agents et ses fonctionnalités Agent2Agent (A2A) pour piloter l’agent. Le <a href="https://strandsagents.com/">SDK Strands Agents</a> est une plateforme de développement d’IA multi-agents, conçue pour créer des applications autonomes avec juste ce qu’il faut de code pour obtenir les résultats souhaités.</p><p>Construisons un agent IA capable de jouer à RPS+, une version revisitée du jeu classique « Pierre, feuille, ciseaux », enrichie de quelques choix supplémentaires pour les joueurs.</p><h2>Produits requis</h2><p>Voici ce dont vous avez besoin pour suivre les étapes décrites dans cet article de blog :</p><ul><li><p>Un éditeur de texte fonctionnant sur votre ordinateur local</p><ul><li><p>Nous utiliserons <a href="https://code.visualstudio.com/download">Visual Studio Code</a> pour suivre les exemples présentés dans cet article de blog.</p></li></ul></li><li><p><a href="https://www.python.org/downloads/">Python 3.10 ou version supérieure</a> installé localement sur votre machine</p></li></ul><h2>Créez un projet sans serveur</h2><p>La première étape consiste à créer un projet Elasticsearch Serverless, qui inclut Elastic Agent Builder.</p><p>Accédez à <a href="http://cloud.elastic.co/">cloud.elastic.co</a> et créez un nouveau projet Elasticsearch Serverless.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3472edce39ec0b81/6a17060e66c4f93c17f8bf57/31b6a5c1c30dacbb4d5e58d1c566071e7143a0c8-1600x879.gif" alt="" /><h2>Créer un index et ajouter des données</h2><p>Ensuite, nous allons ajouter des données à notre projet Elasticsearch. Ouvrez les Developer Tools pour exécuter des commandes : nous allons créer un nouvel index et y insérer des données. Dans le menu principal, sélectionnez Developer Tools.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaedaa94068c07a17/6a17060f961e697558c4ce5f/f97d5af077504463155655a9e27c171a7f974f71-1600x879.jpg" alt="" /><p>Copiez-collez la commande PUT suivante dans la zone de saisie de requêtes de la console Developer Tools. Cette commande crée un index Elasticsearch nommé « game-docs ».</p>PUT /game-docs
{
  "mappings": {
    "properties": {
      "title": { "type": "text" },
      "content": { 
        "type": "text"
      },
      "filename": { "type": "keyword" },
      "last_modified": { "type": "date" }
    }
  }
}<p>Cliquez sur le bouton <strong>Envoyer la requête</strong> à droite de l’instruction dans Developer Tools. Une notification doit confirmer que l’index <em>game-docs</em> a bien été créé, dans la zone de réponse de Developer Tools.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt430c357b479d93af/6a170611a6c2b98191e79624/be0555a1930e4d4f58b7ed8b669c9b702532ed17-1600x880.jpg" alt="" /><p>L’index nommé <em>game-docs</em> est l’endroit idéal pour stocker les données du jeu que nous créons. Ajoutons maintenant un document nommé <em>rps++-md</em> dans cet index, contenant toutes les données nécessaires au jeu. Copiez-collez la commande PUT suivante dans la console Developer Tools.</p>PUT /game-docs/_doc/rps+-md
{
  "title": "Rock Paper Scissors +",
  "content": "
# Game Name
RPS+

# Starting Prompt
Let's play RPS+ !
---
What do you choose?

# Game Objects
1. Rock 🪨 👊
2. Paper 📜 🖐
3. Scissors ✄ ✌️
4. Light ☼ 👍
5. Dark Energy ☄ 🫱

# Judgement of Victory
* Rock beats Scissors
  * because rocks break scissors
* Paper beats Rock
  * because paper covers rock
* Scissors beat Paper
  * because scissors cut paper
* Rock beats Light
  * because you can build a rock structure to block out light
* Paper beats Light
  * because knowledge stored in files and paper books helps us understand light
* Light beats Dark Energy
  * because light enables humans to lighten up and laugh in the face of dark energy as it causes the eventual heat death of the universe
* Light beats Scissors
  * because light is needed to use scissors safely
* Dark Energy beats Rock
  * because dark energy rocks more than rocks. It rocks rocks and everything else in its expansion of the universe
* Dark Energy beats Paper
  * because humans, with their knowledge stored in files and paper books, can't explain dark energy 
* Scissors beat Dark Energy
  * because a human running with scissors is darker than dark energy

# Invalid Input
I was hoping for an worthy opponent
  - but alas it appears that time has past
  - but alas there's little time for your todo list when [todo:fix this] is so vast

# Cancel Game
The future belongs to the bold. Goodbye..
",
  "filename": "RPS+.md",
  "last_modified": "2025-11-25T12:00:00Z"
}<p>Cliquez sur le bouton <strong>Envoyer la requête</strong> à côté de l’instruction pour l’exécuter et ajouter le document <em>rps++-md</em> à l’index game-docs.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt64d49e13754d5b25/6a17061214b270524be3c55d/3c01d8a4602de5c33337457591a388a4a4e3fad3-1600x879.jpg" alt="" /><p>Nous avons maintenant des données à interroger, et avec Agent Builder, c’est plus simple que jamais.</p><p>Dans le menu de navigation principal, sélectionnez <strong>Agents</strong>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb4d374bf2ba9135c/6a1706147d8d67468570e63e/82dbd2e9a439cabd5a5eea3d0ce005b87df0c3ea-1600x879.jpg" alt="" /><p>Ensuite, il vous suffit de demander à l'agent Elastic AI par défaut, « Quelles données ai-je ? »</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc0f879cf28772718/6a1706161949f7f25ee7a92d/f7a2f39c9d1486bdf02d9e88a732b540ac2e2cd1-1600x872.gif" alt="" /><p>L’agent IA Elastic analyse les données et fournit une explication claire des informations disponibles.</p><h2>Créer un outil</h2><p>Nous avons maintenant des données dans Elastic, mettons-les à profit. Agent Builder inclut un support natif pour créer des outils <a href="https://modelcontextprotocol.io/">MCP</a>, qui permettent aux agents d’accéder aux données dont ils ont besoin pour disposer du bon contexte. Créons un outil simple pour récupérer les données de notre jeu.</p><p>Cliquez sur le menu des actions dans Agent Builder.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7802a6b94e81440c/6a170618ab7f085287db9db4/0e327c202674dda33bcc0e494d2b588fa8b32e4f-1600x879.png" alt="" /><p>Dans les options du menu, sélectionnez <strong>Afficher tous les outils</strong>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9f52ffe114fb6ea7/6a17061a4a531b801b36a884/1ebf58650e9fb56750d3f0b1700fab50b44f9bdf-1600x879.png" alt="" /><p>Cliquez sur <strong>+ Nouvel outil</strong>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8090769f6c4d1899/6a17061c286714294093e219/6c03a7f28b99ac2d805f34f39948979893316a00-1600x879.png" alt="" /><p>Dans le formulaire <strong>Créer un outil</strong> , sélectionnez <a href="https://www.elastic.co/docs/explore-analyze/query-filter/languages/esql"><strong>ES|QL</strong></a> <strong>Saisissez</strong> l'outil et entrez les valeurs suivantes.</p><p>Pour <strong>Tool ID </strong>:</p>example.get_game_docs<p>Pour <strong>Description</strong> :</p>Get RPS+ doc from Elasticsearch game-docs index.<p>Pour <strong>Configuration, </strong>saisissez la requête suivante dans la zone de texte <strong>ES|QL Query</strong> :</p>FROM game-docs | WHERE filename == "RPS+.md"<p>Le formulaire <strong>Créer outil</strong> complété devrait ressembler à ceci. Cliquez sur <strong>Enregistrer</strong> pour créer l'outil.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt77034c305198217a/6a17061e66c4f9e54ef8bf5e/b6c93e344600f319b9d2c3030020cf2d171ac1c4-1600x1312.png" alt="" /><p>Un nouvel outil a été ajouté à notre tableau de bord. Les outils ne sont pas faits pour rester inutilisés : ils doivent être mis à profit. Créons un agent qui saura exploiter notre nouvel outil personnalisé.</p><h2>Créez un agent et attribuez-lui un outil</h2><p>Créer un agent est d’une simplicité rafraîchissante avec Agent Builder. Il vous suffit de saisir quelques instructions pour l’agent, avec quelques détails : c’est tout. Créons un agent dès maintenant.</p><p>Cliquez sur <strong>Gérer les agents.</strong></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaa8a83fc2f3758a9/6a1706201949f71a10e7a931/53934b93db07187e251d4b321cb9ca647e2fd51b-1600x858.png" alt="" /><p>Cliquez<strong> + Nouvel agent.</strong></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3778403c5101a000/6a17062160084be12f3c449e/fae3ad8f31e71a6dfd044e1daa025a4e280b4e68-1600x490.png" alt="" /><p>Saisissez les informations suivantes dans le formulaire <strong>Nouvel agent</strong>.</p><p>Pour <strong>l'identification de l'agent, </strong>saisissez le texte ci-dessous :</p>rps_plus_agent<p>Dans la section texte <strong>Instructions personnalisées </strong>, saisissez les instructions suivantes :</p>When prompted, if the prompt contains an integer, then select the corresponding numbered item in the list of "Game Objects" from your documents. Otherwise select a random game object. This is your chosen game object for a single round of the game.

# General Game Rules
* 2 players
    - the user: the person playing the game
    - you: the agent playing the game and serving as the game master
* Each player chooses a game object which will be compared and cause them to tie, win or lose.

# Start the game
1. This is the way each new game always starts. You make the first line of your response only the name of your chosen game object. 

2. The remainder of your response should be the "Starting Prompt" text from your documents and generate a list of "Game Objects" for the person playing the game to choose a game object from.  

# End of Game: The game ends in one of the following three outcomes:
1. Invalid Input: If the player responds with an invalid game object choice, respond with variations of the "Invalid Input" text from your documents and then end the game.

2. Tie: The game ends in a tie if the user chooses the same game object as your game object choice.

3. Win or Lose: The game winner is decided based on the "Judgement of Victory" conditions from your documents. Compare the user's game object choice and your game object choice and determine who chose the winning game object.

# Game conclusion
Respond with a declaration of the winner of the game by outputting the corresponding text in the "Judgement of Victory" section of your documents.<p>Pour le <strong>Nom d'affichage, </strong>entrez le texte ci-dessous :</p>RPS+ Agent<p>Pour la <strong>Description de l’affichage, </strong>saisissez le texte ci-dessous :</p>An agent that plays the game RPS+<p>Donnez à l'agent l'outil personnalisé que nous avons créé précédemment en cliquant sur l'onglet <strong>Outils</strong>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte5b0fe00abdde07c/6a17062314b2704bc4e3c563/1778f64bc3a1b4004998dc3668ef7f666788e193-1600x1390.png" alt="" /><p>Sélectionnez uniquement <em>example.get_game_docs</em>, l'outil que nous avons créé précédemment.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2210212e07e06104/6a170625a929cf3277ae08d1/7d734cd80161bcc058817482eb330ffcf1cb567b-1600x1363.png" alt="" /><p>Cliquez sur <strong>Enregistrer</strong> pour créer le nouvel agent.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6e3afc1918e26f14/6a170627ab7f084746db9db8/c0014faf605ce50c03679ed0d073bd9f3ae7234d-1600x468.png" alt="" /><p>Testons notre nouvel agent. Un lien pratique vous permet de démarrer une conversation avec n’importe quel agent de la liste.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blteb4b69dc5971d3a0/6a1706286f7f046840914743/b7d6943ad90a4f68691207caf66b81742e712145-1600x560.png" alt="" /><p>Saisissez simplement « start game » pour lancer la partie. Ça fonctionne !</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte5b621d602223dff/6a17062ab339d568a1769ef8/984d008e4cc3f08cc1f101720673b0f7347c066c-1600x874.gif" alt="" /><p>L’agent affiche son choix pour la partie en haut de sa réponse. Cela permet de visualiser le choix de l’agent et de vérifier que le jeu fonctionne comme prévu. Cela dit, connaître le choix de votre adversaire avant de jouer n’est pas idéal pour une partie de pierre-feuille-ciseaux. Pour peaufiner le jeu, on peut utiliser une plateforme d’orchestration d’agents capable de les piloter via du code.</p><p>Le SDK Strands Agents entre en scène.</p><h2>Strands Agents SDK</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt73901ec745a97fbf/6a17062c964cea23c808bab3/c195bba6ff2754f5d8fda174a0c1d247bc283710-456x156.png" alt="" /><p>Si vous souhaitez essayer de nouveaux frameworks de développement d'agents, le <a href="https://strandsagents.com/latest/">SDK Strands Agents</a> vaut la peine d'être essayé. <a href="https://aws.amazon.com/blogs/opensource/introducing-strands-agents-an-open-source-ai-agents-sdk/">Le SDK Strands Agents a été publié par AWS (mai 2025)</a> en tant qu'implémentation <a href="https://github.com/strands-agents/sdk-python">Python</a> open source, et il existe désormais une version <a href="https://dev.to/aws/strands-agents-now-speaks-typescript-a-side-by-side-guide-12b3">Typescript</a>.</p><h2>Premiers pas avec le SDK Strands Agents en Python</h2><p>Lancez vos environnements de développement : nous allons cloner et exécuter une application d’exemple qui utilise Strands Agents pour piloter l’<em>agent RPS+</em> via le protocole A2A. Créons une version personnalisée du jeu RPS+ dans laquelle le choix de l’agent est révélé après votre propre décision, afin de préserver l’effet de surprise et le côté ludique du jeu Pierre-Feuille-Ciseaux.</p><p>Sur votre ordinateur local, ouvrez <a href="https://code.visualstudio.com/download">Visual Studio Code</a> et ouvrez un nouveau terminal.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3de752025d62993f/6a17062d0c4857f16501a997/2339cc37c89a3524f2b2a21684bc61dae958e1cf-915x460.jpg" alt="" /><p>Dans le terminal que vous venez d’ouvrir, exécutez la commande suivante pour cloner le dépôt Elasticsearch Labs :</p>git clone https://github.com/elastic/elasticsearch-labs<p>Exécution de la commande <em>cd </em>suivante pour changer de répertoire dans le répertoire Elasticsearch Labs :</p>cd elasticsearch-labs<p>Ensuite, exécutez cette commande pour ouvrir le dépôt dans Visual Studio Code :</p>code .<p>Dans l'explorateur de fichiers de Visual Studio, développez les dossiers <em>supporting-blog-content</em> et <em>agent-builder-a2a-strands-agents</em> , puis ouvrez le fichier <em>elastic_agent_builder_a2a_rps+.py.</em> Voici à quoi ressemble le fichier ouvert dans Visual Studio Code :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt65ef8036a70bcaf1/6a17062f1949f7af36e7a935/d153b19e0e016c701576edb99ccab5af7c554f34-1484x1530.jpg" alt="" /><p>Voici le contenu de <em>elastic_agent_builder_a2a_rps+.py </em>que vous devriez voir dans votre éditeur de texte :</p>import asyncio
from dotenv import load_dotenv
from uuid import uuid4
import httpx
import os
import random
from a2a.client import A2ACardResolver, ClientConfig, ClientFactory
from a2a.types import Message, Part, Role, TextPart

DEFAULT_TIMEOUT = 60  # set request timeout to 1 minute


def create_message(*, role: Role = Role.user, text: str, context_id=None) -&gt; Message:
    return Message(
        kind="message",
        role="user",
        parts=[Part(TextPart(kind="text", text=text))],
        message_id=uuid4().hex,
        context_id=context_id,
    )


async def main():
    load_dotenv()
    a2a_agent_host = os.getenv("ES_AGENT_URL")
    a2a_agent_key = os.getenv("ES_API_KEY")
    custom_headers = {"Authorization": f"ApiKey {a2a_agent_key}"}

    async with httpx.AsyncClient(
        timeout=DEFAULT_TIMEOUT, headers=custom_headers
    ) as httpx_client:
        # Get agent card
        resolver = A2ACardResolver(httpx_client=httpx_client, base_url=a2a_agent_host)
        agent_card = await resolver.get_agent_card(
            relative_card_path="/rps_plus_agent.json"
        )
        # Create client using factory
        config = ClientConfig(
            httpx_client=httpx_client,
            streaming=True,
        )
        factory = ClientFactory(config)
        client = factory.create(agent_card)
        # Use the client to communicate with the agent
        print("\nSending 'start game' message to Elastic A2A agent...")
        random_game_object = random.randint(1, 5)
        msg = create_message(text=f"start with game object {random_game_object}")
        async for event in client.send_message(msg):
            if isinstance(event, Message):
                context_id = event.context_id
                response_complete = event.parts[0].root.text
                # Get agent choice from the first line of the response
                parsed_response = response_complete.split("\n", 1)
                agent_choice = parsed_response[0]
                print(parsed_response[1])
        # User choice sent for game results from the agent
        prompt = input("Your Choice  : ")
        msg = create_message(text=prompt, context_id=context_id)
        async for event in client.send_message(msg):
            if isinstance(event, Message):
                print(f"Agent Choice : {agent_choice}")
                print(event.parts[0].root.text)


if __name__ == "__main__":
    asyncio.run(main())<p>Examinons ce qui se passe dans ce code. En commençant par la méthode <em><code>main()</code></em> , le code commence par accéder aux variables d’environnement pour l’URL de l’agent et la clé API. Ensuite, nous utilisons ces valeurs pour créer un <em><code>httpx</code></em><code> client</code> que nous pouvons utiliser pour obtenir la carte d'agent de l'agent. Le client utilise ensuite les détails de la carte d’agent pour envoyer une demande de « démarrer la partie » à l’agent. Il est intéressant de noter que nous incluons la valeur <code>random_game_object</code> dans la requête <code>"start game"</code>. Cette valeur est un nombre aléatoire généré par le module <em>aléatoire</em> de la bibliothèque standard de Python. La raison pour laquelle nous faisons cela est qu'il s'avère que les puissants LLM (qui rendent possibles les agents IA) ne sont pas très doués pour le hasard. Pas de problème, Python vient à la rescousse.</p><p>En poursuivant dans le code, une fois que l’agent a répondu à la requête start game, le code extrait le choix de l’agent et le stocke dans la variable <em>agent_choice</em>. Le reste de la réponse est affiché sous forme de texte à destination de l’utilisateur final. L’utilisateur est ensuite invité à saisir son propre choix d’objet de jeu, qui est envoyé à l’agent. Le code affiche ensuite le choix de l’agent, ainsi que le résultat final du jeu selon l’agent.</p><h2>Définir l’URL de votre agent et la clé API comme variables d’environnement</h2><p>Comme l’application d’exemple sera exécutée en local, nous devons fournir au SDK Strands Agents une URL A2A et une clé API pour communiquer avec notre agent. L’application d’exemple utilise un fichier nommé <em>.env</em> pour stocker ces valeurs.</p><p>Faites une copie du fichier <em>env.example</em> et nommez le nouveau fichier <em>.env</em></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta17961cbcb42985c/6a170631b0367dc5a072bc55/25ead5f15a17dedb777132a082097cffb06cae4d-1600x843.jpg" alt="" /><p>Retournez dans Elastic Agent Builder pour récupérer les deux valeurs nécessaires.</p><p>Sélectionnez <strong>Afficher tous les outils</strong> dans le menu d'action d'Agent Builder en haut à droite de la page.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt140885d7ebfcb969/6a1706327d8d67b17670e646/9c4f4e4a3bd76e11e0a182fa007a2f6aec7777b4-1600x880.jpg" alt="" /><p>Cliquez sur le menu déroulant <strong>MCP Server</strong> en haut de la page Outils et sélectionnez <strong>Copier l’URL MCP Server.</strong></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc153c2caa27e949b/6a170634a292997793d00f6d/6cde0de678bb6f81bef8a59deffb110ad6c6ce26-1600x882.jpg" alt="" /><p>Collez <strong>l’URL du serveur MCP</strong> dans le <em>fichier .env</em> fichier en remplacement de la valeur d'espace réservé <strong>&lt;YOUR-ELASTIC-AGENT-BUILDER-URL&gt; </strong>. Nous devons maintenant apporter une modification à l'URL, à savoir remplacer le texte final « mcp » par « a2a », car le <a href="https://a2a-protocol.org/">protocole A2A</a> est celui que le SDK Agent Strands utilisera pour communiquer avec l'agent exécuté dans Elastic Agent Builder.</p><p>L’URL modifiée devrait ressembler à ceci :</p>https://rps-game-project-12345a.kb.us-east-1.aws.elastic.cloud/api/agent_builder/a2a<p>L'autre valeur dont nous avons besoin d'obtenir pendant que nous sommes ici dans Elastic Cloud est une clé d'API. Cliquez sur <strong>Elasticsearch </strong>dans la navigation principale.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltada5de819f31d8ff/6a170635b339d55ae9769efc/651676b9be65178cdad50b5d24f26441c0bf3f97-1600x549.jpg" alt="" /><p>Cliquez sur le <strong>bouton Copier la clé API </strong>pour copier la clé API.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta18f85790df00706/6a170637cf4f257145b2d0bd/17f1e2ed5c7682630c71e75b0b09ffb1d9036210-1600x879.jpg" alt="" /><p>Maintenant, de retour dans Visual Studio Code, collez la Clé d'API dans le fichier <em>.env</em> fichier pour remplacer le texte de l'espace réservé <strong>&lt;YOUR-ELASTIC-API-KEY&gt; </strong>. Votre <em>.env</em> Le fichier devrait ressembler à ceci :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt92ab4b37cdcca85e/6a1706386f7f0472ed914747/a357947e07f29c8c03382e00c7baedf04a399297-1600x286.jpg" alt="" /><h2>Lancer l’application d’exemple</h2><p>Ouvrez un nouveau terminal dans Visual Studio Code.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8702d826849755d0/6a17063a60084b45ca3c44a2/33e1174c68ea1ed47c7fe62ab6a6da657c606f56-1413x711.jpg" alt="" /><p>Commencez par exécuter la commande <em>cd</em> suivante dans le terminal :</p>cd elasticsearch-labs/supporting-blog-content/agent-builder-a2a-strands-agents<p>Exécutez la commande suivante pour créer un environnement virtuel Python.</p>python -m venv .venv<p>Selon le système d’exploitation de votre machine, exécutez la commande suivante pour activer l’environnement virtuel.</p><ul><li><p>MacOS/Linux</p></li></ul>source .venv/bin/activate<ul><li><p>Windows</p></li></ul>.venv\Scripts\activate<p>L’application d’exemple utilise le SDK Strands Agents, et nous arrivons à l’étape où il faut l’installer. Exécutez la commande suivante pour installer le SDK Strands Agents avec toutes ses dépendances Python.</p>pip install -r requirements.txt<p>Il est temps de dégager la rampe de lancement et de commencer le compte à rebours. Nous sommes prêts à lancer cette application. Reculez un peu. Lançons-la avec la commande suivante :</p>python elastic_agent_builder_a2a_rps+.py<p>Le défi ? Une partie de RPS+ vous attend. Bravo et bonne chance pour la suite !</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbb3715672995fcfa/6a17063c6234e07b76db195f/041df81fbf1776f09e1243af0a435c4c0af6aca1-1600x948.gif" alt="" /><h2>Créez vos applications d'IA avec un contexte pertinent</h2><p>La création d'un agent IA fait désormais partie de vos compétences. Vous avez vu aussi combien il est facile d'utiliser Elastic Agent Builder via A2A dans des frameworks de développement d'agents comme les SDK Strands Agents. <a href="https://cloud.elastic.co/registration?utm_source=agentic-ai-category&amp;utm_medium=search-labs&amp;utm_campaign=agent-builder">Essayez Elastic</a> pour créer des agents IA connectés au contexte pertinent dans vos données personnalisées.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/agent-builder-a2a-strands-agents-guide</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/agent-builder-a2a-strands-agents-guide</guid>
    <category><![CDATA[IA]]></category>
    <category><![CDATA[IA agentique]]></category>
    <dc:creator><![CDATA[Jonathan Simon]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3472edce39ec0b81/6a17060e66c4f93c17f8bf57/31b6a5c1c30dacbb4d5e58d1c566071e7143a0c8-1600x879.gif" length="0" type="image/gif"/>
    <pubDate>Mon, 15 Dec 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Créez un workflow de recherche IA financière avec LangGraph.js et Elasticsearch]]></title>
    <description><![CDATA[Apprenez à utiliser LangGraph.js avec Elasticsearch pour créer un workflow de recherche financière alimenté par l'IA qui transforme les requêtes en langage naturel en filtres dynamiques et conditionnels pour l'analyse des investissements et du marché.]]></description>
    <content:encoded><![CDATA[<p>La création d'applications de recherche IA implique souvent la coordination de plusieurs tâches, la récupération et l'extraction de données dans un workflow fluide. LangGraph simplifie ce processus en permettant aux développeurs d'orchestrer les agents d'IA à l'aide d'une structure basée sur des nodes. Dans cet article, nous allons construire une solution financière en utilisant <a href="https://langchain-ai.github.io/langgraphjs/">LangGraph.js</a>.</p><h2>Qu'est-ce que LangGraph ?</h2><p><a href="https://langchain-ai.github.io/langgraphjs/">LangGraph</a> est un framework pour construire des agents d’IA et les orchestrer dans un workflow afin de créer des applications assistées par l’IA. LangGraph dispose d’une architecture de nodes où nous pouvons déclarer des fonctions représentant des tâches et les assigner comme nodes du workflow. Le résultat de l'interaction de plusieurs nodes sera un graphe. LangGraph fait partie du <a href="https://js.langchain.com/docs/introduction/">LangChain</a> écosystème plus large, qui fournit des outils pour construire des systèmes d'IA modulaires et composables.</p><p>Pour mieux comprendre l’utilité de LangGraph, résolvons une situation problématique en l’utilisant.</p><h2>Aperçu de la solution</h2><p>Dans une société de capital-risque, les investisseurs ont accès à une vaste base de données avec de nombreuses options de filtrage, mais lorsqu'ils veulent combiner des critères, cela devient difficile et lent. Il se peut donc que certaines start-ups pertinentes ne soient pas trouvées pour l'investissement. Cela conduit à passer beaucoup de temps à essayer d'identifier les meilleurs candidats, voire à perdre des opportunités.</p><p>Avec LangGraph et Elasticsearch, vous pouvez effectuer des recherches filtrées en utilisant le langage naturel, ce qui évite aux utilisateurs de devoir construire manuellement des requêtes complexes avec des dizaines de filtres. Pour plus de flexibilité, le workflow choisit automatiquement, en fonction de l'entrée de l'utilisateur, entre deux types de requêtes :</p><ul><li><p><strong>Requêtes d’investissement</strong> : elles visent les données financières et de financement des start-up, notamment les <a href="https://www.investopedia.com/articles/personal-finance/102015/series-b-c-funding-what-it-all-means-and-how-it-works.asp">tours de table</a>, la valorisation ou le <a href="https://www.investopedia.com/terms/r/revenue.asp">CA</a>. <em>Exemple :</em> « Trouvez des startups avec un financement de série A ou série B entre 8 millions et 25 millions de dollars et un chiffre d’affaires mensuel supérieur à 500 000 $. »</p></li><li><p><strong>Requêtes axées sur le marché</strong>: elles se concentrent sur <a href="https://en.wikipedia.org/wiki/Vertical_market">les secteurs d’activité</a>, <a href="https://en.wikipedia.org/wiki/Target_market">les marchés géographiques</a> ou <a href="https://www.investopedia.com/terms/b/businessmodel.asp">les modèles économiques</a>, en aidant à identifier des opportunités dans des secteurs ou régions spécifiques. <em>Exemple :</em> « Trouvez des startups de la fintech et de la santé à San Francisco, New York ou Boston. »</p></li></ul><p>Pour garantir la robustesse des requêtes, nous allons faire en sorte que le LLM génère des <a href="https://www.elastic.co/docs/solutions/search/search-templates">modèles de recherche</a> au lieu de <a href="https://www.elastic.co/docs/explore-analyze/query-filter/languages/querydsl">requêtes DSL</a> complètes. De cette façon, vous obtenez toujours la requête souhaitée, et le LLM n'a qu'à compléter les informations manquantes sans avoir à élaborer la requête dont vous avez besoin à chaque fois.</p><h2>Ce dont vous avez besoin pour commencer</h2><ul><li><p>Clé API Elasticsearch</p></li><li><p>Clé d'API OpenAPI</p></li><li><p>Node 18 ou version ultérieure</p></li></ul><h2>Instructions étape par étape</h2><p>Dans cette section, voyons comment l'application sera présentée. Nous utiliserons <a href="https://www.typescriptlang.org/">TypeScript</a>, un sur-ensemble de JavaScript qui ajoute des types statiques. Cela rend le code plus fiable, plus facile à maintenir et plus sûr en détectant les erreurs dès le début, tout en assurant une compatibilité totale avec JavaScript.</p><p>Le flux des nœuds se présentera comme suit :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt90db8f03f372608c/6a170986dc55de6e16e00d93/b47d7f238c4964a6febc0de7fe5e68b186f539c3-363x555.png" alt="" /><p>L'image ci-dessus est générée par LangGraph et représente le workflow qui définit l'ordre d'exécution et la logique conditionnelle entre les nodes :</p><ul><li><p><strong>decideStrategy : </strong>utilise un LLM pour rechercher la requête de l'utilisateur et choisir entre deux stratégies de recherche spécialisées, axée sur l'investissement ou axée sur le marché.</p></li><li><p><strong>PrepareInvestmentSearch : </strong>extrait les valeurs de filtre de la requête et crée un modèle prédéfini mettant l'accent sur les paramètres financiers et liés au financement.</p></li><li><p><strong>PrepareMarketSearch</strong>: extrait également les valeurs des filtres, mais crée dynamiquement des paramètres en mettant l'accent sur le marché, le secteur et le contexte géographique.</p></li><li><p><strong>ExecuteSearch : </strong>envoie la recherche construite à Elasticsearch à l'aide d'un modèle de recherche et extrait les documents de démarrage correspondants.</p></li><li><p><strong>VisualiserResults : </strong>met en forme les résultats finaux sous la forme d'un résumé clair et lisible présentant les principaux attributs de la start-up tels que le financement, le secteur d'activité et le chiffre d'affaires.</p></li></ul><p>Ce flux comprend un <a href="https://langchain-ai.github.io/langgraphjs/how-tos/branching/?h=conditional#how-to-create-branches-for-parallel-node-execution">branchement conditionnel</a>, fonctionnant comme une instruction « si », qui détermine s'il faut rechercher le chemin d'investissement ou de recherche de marché en fonction de l'entrée de l'utilisateur. Cette logique de décision, pilotée par le LLM, rend le workflow adaptatif et conscient du contexte, un mécanisme que nous explorerons plus en détail dans les sections suivantes.</p><h3>État de LangGraph</h3><p>Avant de voir chaque node individuellement, nous devons comprendre comment les nodes communiquent et partagent les données. Pour cela, LangGraph nous permet de définir l'état du workflow. Cela définit l'état partagé qui sera transmis entre les nodes.</p><p>L’état agit comme un conteneur partagé stockant les données intermédiaires du workflow : il enregistre d’abord la requête en langage naturel de l’utilisateur, puis la stratégie de recherche choisie, les paramètres prêts pour Elasticsearch, les résultats de recherche et, pour finir, le résultat formaté.</p><p>Cette architecture permet à chaque nœud de lire et de modifier l’état, ce qui garantit un flux d’informations constant, de l’entrée de l’utilisateur jusqu’à la visualisation finale.</p>const VCState = Annotation.Root({
  input: Annotation&lt;string&gt;(), // User's natural language query
  searchStrategy: Annotation&lt;string&gt;(), // Search strategy chosen by LLM
  searchParams: Annotation&lt;any&gt;(), // Prepared search parameters
  results: Annotation&lt;any[]&gt;(), // Search results
  final: Annotation&lt;string&gt;(), // Final formatted response
});<h3>Configurer l'application</h3><p>Tout le code de cette section se trouve dans le <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch">dépôt elasticsearch-labs</a>.</p><p>Dans le dossier où l’application sera installée, ouvrez un terminal et initialisez une application Node.js avec la commande :</p>npm init -y<p>Nous pouvons maintenant installer les dépendances nécessaires à ce projet :</p>npm install @elastic/elasticsearch @langchain/langgraph @langchain/openai @langchain/core dotenv zod &amp;&amp; npm install --save-dev @types/node tsx typescript<ul><li><p><strong><code>@elastic/elasticsearch</code></strong>: Permet de gérer les requêtes Elasticsearch, comme l’ingestion et la récupération des données.</p></li><li><p><strong><code>@langchain/langgraph</code></strong>: Dépendance JS pour fournir tous les outils LangGraph.</p></li><li><p><strong><code>@langchain/openai</code></strong>: Client OpenAI LLM pour LangChain.</p></li><li><p>@langchain/core : Offre les composantes de base essentielles aux applications LangChain, notamment les modèles d’invite.</p></li><li><p><strong><code>dotenv</code></strong>: Dépendance nécessaire pour utiliser les variables d'environnement en JavaScript.</p></li><li><p><strong><code>zod</code></strong>: Dépendance au type de données.</p></li></ul><p><code>@types/node</code> <code>tsx</code> <code>typescript</code> nous permet d'écrire et d'exécution du code TypeScript.</p><p>Créez maintenant les fichiers suivants :</p><ul><li><p><code>elasticsearchSetup</code><a href="http://ingest.ts/"><code>.ts</code></a>: Créera les mapping d'index, chargera les données à partir d'un fichier JSON, et ingérera les données dans Elasticsearch.</p></li><li><p><a href="http://main.ts/"><code>main.ts</code></a>: inclura l’application LangGraph.</p></li><li><p><code>.env</code>: fichier pour stocker les variables d’environnement</p></li></ul><p>Dans le fichier <code>.env</code>, ajoutons les variables d’environnement suivantes :</p>ELASTICSEARCH_ENDPOINT="your-endpoint-here"
ELASTICSEARCH_API_KEY="your-key-here"
OPENAI_API_KEY="your-key-here"<p>La clé APIK de l'OpenAPI ne sera pas utilisée directement dans le code ; elle sera utilisée en interne par la bibliothèque <code>@langchain/openai</code>.</p><p>Toute la logique concernant la création de mappages, la création de modèles de recherche et l’ingestion des ensembles de données se trouve dans le fichier <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/elasticsearchSetup.ts"><code>elasticsearchSetup.ts</code></a>. Dans les prochaines étapes, nous nous concentrerons sur le fichier <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/main.ts"><code>main.ts</code></a>. Vous pouvez également consulter l'ensemble de données pour mieux comprendre l'aspect des données sur le site <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/dataset.json"><code>dataset.json</code></a>.</p><h3>Application LangGraph</h3><p>Dans le fichier <code>main.ts</code>, importons certaines dépendances nécessaires pour consolider l'application LangGraph. Dans ce fichier, vous devez également inclure les fonctions node et la déclaration d’état. La déclaration du graphe sera effectuée dans une méthode <code>main</code> dans les prochaines étapes. Le fichier <code>elasticsearchSetup.ts</code> contiendra les aides Elasticsearch que nous allons utiliser dans les Nodes dans les étapes suivantes.</p>import { writeFileSync } from "node:fs";
import { StateGraph, Annotation, START, END } from "@langchain/langgraph";
import { ChatOpenAI } from "@langchain/openai";
import { z } from "zod";
import {
  esClient,
  ingestDocuments,
  createSearchTemplates,
  INDEX_NAME,
  INVESTMENT_FOCUSED_TEMPLATE,
  MARKET_FOCUSED_TEMPLATE,
  createIndex,
} from "./elasticsearchSetup.js";

const llm = new ChatOpenAI({ model: "gpt-4o-mini" });<p>Ainsi que nous l’avons vu, le client LLM sera mobilisé pour générer les paramètres du modèle de recherche Elasticsearch en fonction de la question de l’utilisateur.</p>async function saveGraphImage(app: any): Promise&lt;void&gt; {
  try {
    const drawableGraph = app.getGraph();
    const image = await drawableGraph.drawMermaidPng();
    const arrayBuffer = await image.arrayBuffer();

    const filePath = "./workflow_graph.png";
    writeFileSync(filePath, new Uint8Array(arrayBuffer));
    console.log(`📊 Workflow graph saved as: ${filePath}`);
  } catch (error: any) {
    console.log("⚠️  Could not save graph image:", error.message);
  }
}<p>La méthode ci-dessus génère l'image du graphe au format png et utilise l'<a href="https://mermaid.ink/">API Mermaid.INK</a> en arrière-plan. Ceci est utile si vous souhaitez voir comment les nodes de l'application interagissent dans le cadre d'une visualisation stylisée.</p><h3>Nodes LangGraph</h3><p>À présent, voyons chaque node en détail :</p><h3>Node decideSearchStrategy</h3><p>Le node <code>decideSearchStrategy</code> analyse les entrées de l'utilisateur et détermine s'il convient d'effectuer une rechercher axée sur les investissements ou axée sur le marché. Il utilise un LLM avec un schéma de sortie structuré (défini avec Zod) pour classer le type de requête. Avant de prendre la décision, il récupère les filtres disponibles de l'index en utilisant une agrégation, en garantissant que le modèle dispose d'un contexte à jour sur les industries, les localisations et les données de financement.</p><p>Pour extraire les valeurs possibles des filtres et les envoyer au LLM, utilisons une requête d'<a href="https://www.elastic.co/docs/explore-analyze/query-filter/aggregations">agrégation</a> pour les récupérer directement depuis l'index Elasticsearch. Cette logique est allouée dans une méthode appelée <code>getAvailableFilters</code>:</p>async function getAvailableFilters() {
  try {
    const response = await esClient.search({
      index: INDEX_NAME,
      size: 0,
      aggs: {
        industries: {
          terms: { field: "industry", size: 100 },
        },
        locations: {
          terms: { field: "location", size: 100 },
        },
        funding_stages: {
          terms: { field: "funding_stage", size: 20 },
        },
        business_models: {
          terms: { field: "business_model", size: 10 },
        },
        lead_investors: {
          terms: { field: "lead_investor", size: 100 },
        },
        funding_amount_stats: {
          stats: { field: "funding_amount" },
        },
      },
    });

    return response.aggregations;
  } catch (error) {
    console.error("❌ Error getting available filters:", error);
    return {};
  }
}<p>Avec la requête d'agrégation ci-dessus, nous avons les résultats suivants :</p>{
  "industries": {
    "doc_count_error_upper_bound": 0,
    "sum_other_doc_count": 0,
    "buckets": [
      {
        "key": "logistics",
        "doc_count": 5
      },
      ...
    ]
  },
  "locations": {
    "doc_count_error_upper_bound": 0,
    "sum_other_doc_count": 0,
    "buckets": [
      {
        "key": "San Francisco, CA",
        "doc_count": 4
      },
      {
        "key": "New York, NY",
        "doc_count": 3
      },
      ...
    ]
  },
  "funding_stages": {
    "doc_count_error_upper_bound": 0,
    "sum_other_doc_count": 0,
    "buckets": [
      {
        "key": "Series A",
        "doc_count": 8
      },
      ...
    ]
  },
  "business_models": {
    "doc_count_error_upper_bound": 0,
    "sum_other_doc_count": 0,
    "buckets": [
      {
        "key": "B2B",
        "doc_count": 13
      },
      ...
    ]
  },
  "lead_investors": {
    "doc_count_error_upper_bound": 0,
    "sum_other_doc_count": 0,
    "buckets": [
      {
        "key": "Battery Ventures",
        "doc_count": 1
      },
      {
        "key": "Benchmark Capital",
        "doc_count": 1
      },
      ...
    ]
  },
  "funding_amount_stats": {
    "count": 20,
    "min": 4500000,
    "max": 35000000,
    "avg": 14075000,
    "sum": 281500000
  }
}<p>Découvrez tous les résultats <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/responses/aggregationsResponse.json">ici</a>.</p><p>Pour les deux stratégies, nous allons utiliser la recherche hybride afin de détecter à la fois la partie structurée de la question (filtres) et les parties plus subjectives (sémantique). Voici un exemple des deux requêtes utilisant des <a href="https://www.elastic.co/docs/solutions/search/search-templates">modèles de recherche</a> :</p>await esClient.putScript({
      id: INVESTMENT_FOCUSED_TEMPLATE,
      script: {
        lang: "mustache",
        source: `{
          "size": 5,
          "retriever": {
            "rrf": {
              "retrievers": [
                {
                  "standard": {
                    "query": {
                      "semantic": {
                        "field": "semantic_field",
                        "query": "{{query_text}}"
                      }
                    }
                  }
                },
                {
                  "standard": {
                    "query": {
                      "bool": {
                        "filter": [
                          {"terms": {"funding_stage": {{#join}}{{#toJson}}funding_stage{{/toJson}}{{/join}}}},
                          {"range": {"funding_amount": {"gte": {{funding_amount_gte}}{{#funding_amount_lte}},"lte": {{funding_amount_lte}}{{/funding_amount_lte}}}}},
                          {"terms": {"lead_investor": {{#join}}{{#toJson}}lead_investor{{/toJson}}{{/join}}}},
                          {"range": {"monthly_revenue": {"gte": {{monthly_revenue_gte}}{{#monthly_revenue_lte}},"lte": {{monthly_revenue_lte}}{{/monthly_revenue_lte}}}}}
                        ]
                      }
                    }
                  }
                }
              ],
              "rank_window_size": 100,
              "rank_constant": 20
            }
          }
        }`,
      },
    });<p>Regardez les requêtes détaillées dans le fichier <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/elasticsearchSetup.ts#L119"><code>elasticsearchSetup.ts</code></a> . Dans le node suivant, il sera décidé laquelle des deux requêtes sera utilisée :</p>// Node 1: Decide search strategy using LLM
async function decideSearchStrategy(state: typeof VCState.State) {
  // Zod schema for specialized search strategy decision
  const SearchDecisionSchema = z.object({
    search_type: z
      .enum(["investment_focused", "market_focused"])
      .describe("Type of specialized search strategy to use"),
    reasoning: z
      .string()
      .describe("Brief explanation of why this search strategy was chosen"),
  });

  const decisionLLM = llm.withStructuredOutput(SearchDecisionSchema);

  // Get dynamic filters from Elasticsearch
  const availableFilters = await getAvailableFilters();

  const prompt = `Query: "${state.input}"
    Available filters: ${JSON.stringify(availableFilters, null, 2)}

    Choose between two specialized search strategies:
    
    - investment_focused: For queries about funding stages, funding amounts, monthly revenue, lead investors, financial performance
    
    - market_focused: For queries about industries, locations, business models, market segments, geographic markets
    
    Analyze the query intent and choose the most appropriate strategy.
  `;

  try {
    const result = await decisionLLM.invoke(prompt);
    console.log(
      `🤔 Search strategy: ${result.search_type} - ${result.reasoning}`
    );

    return {
      searchStrategy: result.search_type,
    };
  } catch (error: any) {
    console.error("❌ Error in decideSearchStrategy:", error.message);
    return {
      searchStrategy: "investment_focused",
    };
  }
}<h3>Nodes prepareInvestmentSearch et prepareMarketSearch</h3><p>Les deux nœuds utilisent une fonction d’assistance partagée, <code>extractFilterValues</code>, qui exploite le LLM pour identifier les filtres pertinents mentionnés dans les entrées de l’utilisateur, tels que l’industrie, la localisation, le stade de financement, le modèle économique, etc. Nous utilisons ce schéma pour construire notre <a href="https://www.elastic.co/docs/solutions/search/search-templates">modèle de recherche</a>.</p>// Extract all possible filter values from user input
async function extractFilterValues(input: string) {
  const FilterValuesSchema = z.object({
    // Investment-focused filters
    funding_stage: z
      .array(z.string())
      .default([])
      .describe("Funding stage values mentioned in query"),
    funding_amount_gte: z
      .number()
      .default(0)
      .describe("Minimum funding amount in USD"),
    funding_amount_lte: z
      .number()
      .default(100000000)
      .describe("Maximum funding amount in USD"),
    lead_investor: z
      .array(z.string())
      .default([])
      .describe("Lead investor values mentioned in query"),
    monthly_revenue_gte: z
      .number()
      .default(0)
      .describe("Minimum monthly revenue in USD"),
    monthly_revenue_lte: z
      .number()
      .default(10000000)
      .describe("Maximum monthly revenue in USD"),
    industry: z
      .array(z.string())
      .default([])
      .describe("Industry values mentioned in query"),
    location: z
      .array(z.string())
      .default([])
      .describe("Location values mentioned in query"),
    business_model: z
      .array(z.string())
      .default([])
      .describe("Business model values mentioned in query"),
  });

  const extractorLLM = llm.withStructuredOutput(FilterValuesSchema);
  const availableFilters = await getAvailableFilters();

  const extractPrompt = `Extract ALL relevant filter values from: "${input}"
    Available options: ${JSON.stringify(availableFilters, null, 2)}
    Extract only values explicitly mentioned in the query. Leave fields empty if not mentioned.`;

  return await extractorLLM.invoke(extractPrompt);
}<p>Selon l'intention détectée, le workflow sélectionne l'un des deux chemins :</p><p><strong>prepareInvestmentSearch :</strong> définit des paramètres de rechercher orientés sur la finance, notamment l''étape du financement, le montant du financement, les informations relatives à l''investisseur et au renouvellement. Vous pouvez trouver le modèle complet de requête dans le fichier <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/elasticsearchSetup.ts"><code>elasticsearchSetup.ts</code></a> :</p>// Node 2A: Prepare Investment-Focused Search Parameters 
async function prepareInvestmentSearch(state: typeof VCState.State) {
  console.log(
    "💰 Preparing INVESTMENT-FOCUSED search parameters with financial emphasis..."
  );

  try {
    // Extract all filter values from input
    const values = await extractFilterValues(state.input);

    let searchParams: any = {
      template_id: INVESTMENT_FOCUSED_TEMPLATE,
      query_text: state.input,
      ...values,
    };

    return { searchParams };
  } catch (error) {
    console.error("❌ Error preparing investment-focused params:", error);
    return {
      searchParams: {},
    };
  }
}<p><strong>prepareMarketSearch :</strong> crée des paramètres orientés vers le marché, axés sur les industries, les régions géographiques et les modèles économiques. Voir l’intégralité de la requête dans le fichier <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/elasticsearchSetup.ts"><code>elasticsearchSetup.ts</code></a> :</p>// Node 2B: Prepare Market-Focused Search Parameters
async function prepareMarketSearch(state: typeof VCState.State) {
  console.log(
    "🔍 Preparing MARKET-FOCUSED search parameters with market emphasis..."
  );

  try {
    // Extract all filter values from input
    const values = await extractFilterValues(state.input);

    let searchParams: any = {
      template_id: MARKET_FOCUSED_TEMPLATE,
      query_text: state.input,
      ...values,
    };

    return { searchParams };
  } catch (error) {
    console.error("❌ Error preparing market-focused params:", error);
    return {};
  }
}<h3>Node executeSearch</h3><p>Ce node prend les paramètres de rechercher générés à partir de l'état et les envoie d'abord à Elasticsearch, en utilisant l'<a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-render-search-template">API _render</a> pour visualiser la requête à des fins de débogage, puis envoie une demande pour récupérer les résultats.</p>// Node 3: Execute Search
async function executeSearch(state: typeof VCState.State) {
  const { searchParams } = state;

  try {
    // getting formed query from template for debugging
    const renderedTemplate = await esClient.renderSearchTemplate({
      id: searchParams.template_id,
      params: searchParams,
    });

    console.log(
      "📋 Complete query:",
      JSON.stringify(renderedTemplate.template_output, null, 2)
    );

    const results = await esClient.searchTemplate({
      index: INDEX_NAME,
      id: searchParams.template_id,
      params: searchParams,
    });

    return {
      results: results.hits.hits.map((hit: any) =&gt; hit._source),
    };
  } catch (error: any) {
    console.error(`❌ ${state.searchParams.search_type} search error:`, error);
    return { results: [] };
  }
}<h3>Node visualizeResults</h3><p>Enfin, ce node affiche les résultats d’Elasticsearch.</p>// Node 4: Visualize results
async function visualizeResults(state: typeof VCState.State) {
  const results = state.results || [];

  let formattedResults = `🎯 Found ${results.length} startups matching your criteria:\n\n`;

  results.forEach((startup: any, index: number) =&gt; {
    formattedResults += `${index + 1}. **${startup.company_name}**\n`;
    formattedResults += `   📍 ${startup.location} | 🏢 ${startup.industry} | 💼 ${startup.business_model}\n`;
    formattedResults += `   💰 ${startup.funding_stage} - $${(
      startup.funding_amount / 1000000
    ).toFixed(1)}M\n`;
    formattedResults += `   👥 ${startup.employee_count} employees | 📈 $${(
      startup.monthly_revenue / 1000
    ).toFixed(0)}K MRR\n`;
    formattedResults += `   🏦 Lead: ${startup.lead_investor}\n`;
    formattedResults += `   📝 ${startup.description}\n\n`;
  });

  return {
    final: formattedResults,
  };
}<p>Par programmation, l'ensemble du graphe ressemble à ceci :</p>  const workflow = new StateGraph(VCState)
    // Register nodes - these are the processing functions
    .addNode("decideStrategy", decideSearchStrategy)
    .addNode("prepareInvestment", prepareInvestmentSearch)
    .addNode("prepareMarket", prepareMarketSearch)
    .addNode("executeSearch", executeSearch)
    .addNode("visualizeResults", visualizeResults)
    // Define execution flow with conditional branching
    .addEdge(START, "decideStrategy") // Start with strategy decision
    .addConditionalEdges(
      "decideStrategy",
      (state: typeof VCState.State) =&gt; state.searchStrategy, // Conditional function
      {
        investment_focused: "prepareInvestment", // If investment focused -&gt; RRF template preparation
        market_focused: "prepareMarket", // If market focused -&gt; dynamic query preparation
      }
    )
    .addEdge("prepareInvestment", "executeSearch") // Investment prep -&gt; execute
    .addEdge("prepareMarket", "executeSearch") // Market prep -&gt; execute
    .addEdge("executeSearch", "visualizeResults") // Execute -&gt; visualize
    .addEdge("visualizeResults", END); // End workflow<p>Comme vous pouvez le constater, nous avons une arête conditionnelle où l'application décide quel « chemin » ou node sera exécuté ensuite. Cette fonctionnalité est utile lorsque les workflows nécessitent une logique de branchement, comme le choix entre plusieurs outils ou l’inclusion d’une étape humaine dans la boucle.</p><p>Maintenant que vous maîtrisez les fonctionnalités clés de LangGraph, nous pouvons préparer l’application qui exécutera le code :</p><p>Rassemblons tout dans une méthode <code>main</code> , ici nous déclarons le graphe avec tous les éléments sous la variable workflow :</p>async function main() {
  await createIndex();
  await createSearchTemplates();
  await ingestDocuments();

  // Create the workflow graph with shared state
  const workflow = new StateGraph(VCState)
    // Register nodes - these are the processing functions
    .addNode("decideStrategy", decideSearchStrategy)
    .addNode("prepareInvestment", prepareInvestmentSearch)
    .addNode("prepareMarket", prepareMarketSearch)
    .addNode("executeSearch", executeSearch)
    .addNode("visualizeResults", visualizeResults)
    // Define execution flow with conditional branching
    .addEdge(START, "decideStrategy") // Start with strategy decision
    .addConditionalEdges(
      "decideStrategy",
      (state: typeof VCState.State) =&gt; state.searchStrategy, // Conditional function
      {
        investment_focused: "prepareInvestment", // If investment focused -&gt; RRF template preparation
        market_focused: "prepareMarket", // If market focused -&gt; dynamic query preparation
      }
    )
    .addEdge("prepareInvestment", "executeSearch") // Investment prep -&gt; execute
    .addEdge("prepareMarket", "executeSearch") // Market prep -&gt; execute
    .addEdge("executeSearch", "visualizeResults") // Execute -&gt; visualize
    .addEdge("visualizeResults", END); // End workflow


  const app = workflow.compile();

  await saveGraphImage(app);

  const query =
    "Find startups with Series A or Series B funding between $8M-$25M and monthly revenue above $500K";

  const marketResult = await app.invoke({ input: query });
  console.log(marketResult.final);
}<p>La variable de requête simule l'entrée utilisateur saisie dans une barre de recherche hypothétique :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltba7189d5f4e63403/6a1709880e2e49cc3041a076/e8d76909eb2bc1bb62f3ca9a8b3e4b85fcec2893-1600x164.png" alt="" /><p>D’après la phrase en langage naturel « Trouvez des startups avec un financement de la série A ou de la série B entre 8 millions et 25 millions de dollars et un chiffre d’affaires mensuel supérieur à 500 000 $ », tous les filtres seront extraits.</p><p>Enfin, invoquez la méthode principale :</p>main().catch(console.error);<h3>Résultats</h3>🔍 Checking if index exists...
🏗️ Creating index...
✅ Index created successfully!
Ingesting documents...
✅ Documents ingested successfully!
✅ Investment-focused template created successfully!
✅ Market-focused template created successfully!

📊 Workflow graph saved as: ./workflow_graph.png

🔍 Query: "Find startups with Series A or Series B funding between $8M-$25M and monthly revenue above $500K"

🤔 Search strategy: investment_focused - The query specifically seeks profitable fintech startups with defined funding amounts and high monthly revenue, which aligns closely with financial performance metrics and investment-related criteria.

💰 Preparing INVESTMENT-FOCUSED search parameters with financial emphasis...

📋 Complete query: {
  "size": 5,
  "retriever": {
    "rrf": {
      "retrievers": [
        {
          "standard": {
            "query": {
              "semantic": {
                "field": "semantic_field",
                "query": "Find startups with Series A or Series B funding between $8M-$25M and monthly revenue above $500K"
              }
            }
          }
        },
        {
          "standard": {
            "query": {
              "bool": {
                "filter": [
                  {
                    "terms": {
                      "funding_stage": [
                        "Series A",
                        "Series B"
                      ]
                    }
                  },
                  {
                    "range": {
                      "funding_amount": {
                        "gte": 8000000,
                        "lte": 25000000
                      }
                    }
                  },
                  {
                    "terms": {
                      "lead_investor": []
                    }
                  },
                  {
                    "range": {
                      "monthly_revenue": {
                        "gte": 500000,
                        "lte": 0
                      }
                    }
                  }
                ]
              }
            }
          }
        }
      ],
      "rank_window_size": 100,
      "rank_constant": 20
    }
  }
}
🎯 Found 5 startups matching your criteria:

1. **TechFlow**
   📍 San Francisco, CA | 🏢 logistics | 💼 B2B
   💰 Series A - $8.0M
   👥 45 employees | 📈 $500K MRR
   🏦 Lead: Sequoia Capital
   📝 TechFlow optimizes supply chain operations using AI-powered route optimization and real-time tracking. Founded in 2023, shows remarkable growth with $500K monthly revenue.

2. **DataViz**
   📍 New York, NY | 🏢 enterprise software | 💼 B2B
   💰 Series A - $10.0M
   👥 42 employees | 📈 $450K MRR
   🏦 Lead: Battery Ventures
   📝 DataViz creates intuitive data visualization tools for enterprise customers. No-code platform allows business users to create dashboards without technical expertise.

3. **FinanceAI**
   📍 San Francisco, CA | 🏢 fintech | 💼 B2C
   💰 Series C - $25.0M
   👥 120 employees | 📈 $1200K MRR
   🏦 Lead: Tiger Global Management
   📝 FinanceAI provides AI-powered investment advisory services to retail investors. Uses machine learning to analyze market trends with over 100,000 active users.

4. **UrbanMobility**
   📍 New York, NY | 🏢 logistics | 💼 B2B2C
   💰 Series B - $15.0M
   👥 78 employees | 📈 $750K MRR
   🏦 Lead: Kleiner Perkins
   📝 UrbanMobility revolutionizes urban transportation through autonomous delivery drones and smart logistics hubs. Partners with major retailers for same-day delivery across Manhattan and Brooklyn.

5. **HealthTech Solutions**
   📍 Boston, MA | 🏢 healthcare | 💼 B2B
   💰 Series B - $18.0M
   👥 95 employees | 📈 $900K MRR
   🏦 Lead: General Catalyst
   📝 HealthTech Solutions develops medical devices and software for remote patient monitoring. Comprehensive telehealth platform reducing hospital readmissions by 30%.

✨  Done in 18.80s.<p>Pour l'entrée envoyée, l'application choisit le chemin <strong>axé sur l'investissement</strong> et, par conséquent, nous pouvons voir la requête Elasticsearch générée par le workflow, qui extrait les valeurs et les plages de l'entrée de l'utilisateur. Nous pouvons également voir la requête envoyée à Elasticsearch avec les valeurs extraites appliquées, et enfin, les résultats formatés par le nœud <code>visualizeResults</code> avec les résultats.</p><p>Testons maintenant le node <strong>axé sur le marché</strong> en utilisant la requête « Trouver des startups fintech et de la santé à San Francisco, New York ou Boston » :</p>...

🔍 Query: Find fintech and healthcare startups in San Francisco, New York, or Boston

🤔 Search strategy: market_focused - The query is focused on finding fintech startups in San Francisco that are disrupting traditional banking and payment systems, which pertains to specific industries (fintech) and locations (San Francisco). Thus, a market-focused strategy is more appropriate.

🔍 Preparing MARKET-FOCUSED search parameters with market emphasis...

📋 Complete query: {
  "size": 5,
  "retriever": {
    "rrf": {
      "retrievers": [
        {
          "standard": {
            "query": {
              "semantic": {
                "field": "semantic_field",
                "query": "Find fintech and healthcare startups in San Francisco, New York, or Boston"
              }
            }
          }
        },
        {
          "standard": {
            "query": {
              "bool": {
                "filter": [
                  {
                    "terms": {
                      "industry": [
                        "fintech",
                        "healthcare"
                      ]
                    }
                  },
                  {
                    "terms": {
                      "location": [
                        "San Francisco, CA",
                        "New York, NY",
                        "Boston, MA"
                      ]
                    }
                  },
                  {
                    "terms": {
                      "business_model": []
                    }
                  }
                ]
              }
            }
          }
        }
      ],
      "rank_window_size": 50,
      "rank_constant": 10
    }
  }
}
🎯 Found 5 startups matching your criteria:

1. **FinanceAI**
   📍 San Francisco, CA | 🏢 fintech | 💼 B2C
   💰 Series C - $25.0M
   👥 120 employees | 📈 $1200K MRR
   🏦 Lead: Tiger Global Management
   📝 FinanceAI provides AI-powered investment advisory services to retail investors. Uses machine learning to analyze market trends with over 100,000 active users.

2. **CryptoWallet**
   📍 Miami, FL | 🏢 fintech | 💼 B2C
   💰 Series B - $16.0M
   👥 73 employees | 📈 $820K MRR
   🏦 Lead: Coinbase Ventures
   📝 CryptoWallet provides secure digital wallet solutions for cryptocurrency trading and storage. Multi-chain support with enterprise-grade security features.

...

✨  Done in 7.41s.<h2>Enseignements</h2><p>Pendant le processus d'écriture, j'ai appris :</p><ul><li><p>Nous devons montrer au LLM les valeurs exactes des filtres, sinon nous attendons de l'utilisateur qu'il saisisse les valeurs exactes des éléments. Pour une faible cardinalité, cette approche convient, mais lorsque la cardinalité est élevée, nous avons besoin d'un mécanisme pour filtrer les résultats</p></li><li><p>Utiliser des modèles de recherche rend les résultats bien plus cohérents que de laisser le LLM écrire la requête Elasticsearch, et c’est aussi plus rapide</p></li><li><p>Les arêtes conditionnelles constituent un mécanisme puissant pour construire des applications avec de multiples variantes et chemins de branchement.</p></li><li><p>La sortie structurée est extrêmement utile lors de la génération d'informations avec des LLM, car elle applique des réponses prévisibles et sécurisées. Cela améliore la fiabilité et réduit les erreurs d'interprétation.</p></li></ul><p>La combinaison de la recherche sémantique et de la recherche structurée par le biais d'une recherche hybride produit des résultats meilleurs et plus pertinents, en équilibrant précision et compréhension du contexte.</p><h2>Conclusion</h2><p>Dans cet exemple, nous combinons LangGraph.js avec Elasticsearch pour créer un workflow dynamique capable d'interpréter les requêtes en langage naturel et de décider entre des stratégies de recherche axées sur la finance ou le marché. Cette approche réduit la complexité de la création de requêtes manuelles tout en améliorant la flexibilité et la précision pour les analystes en capital-risque.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ai-agent-workflow-finance-langgraph-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-agent-workflow-finance-langgraph-elasticsearch</guid>
    <category><![CDATA[IA]]></category>
    <category><![CDATA[IA agentique]]></category>
    <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt013eba5d152f11f3/6a1709892b835f6784f4b1a6/12b6057d84c6356267cd178a3c6c1a5c61123ece-2000x1256.png" length="0" type="image/png"/>
    <pubDate>Fri, 05 Dec 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Construire un agent d'IA pour les RH avec Elastic Agent Builder et GPT-OSS]]></title>
    <description><![CDATA[Découvrez comment créer un agent d'intelligence artificielle capable de répondre à des requêtes en langage naturel sur les données RH de vos employés en utilisant Elastic Agent Builder et GPT-OSS.]]></description>
    <content:encoded><![CDATA[<h2>Introduction</h2><p>Cet article vous montrera comment construire un agent d'IA pour les RH en utilisant <a href="https://openai.com/index/introducing-gpt-oss/">GPT-OSS</a> et Elastic Agent Builder. L'agent peut répondre à vos questions sans envoyer de données à OpenAI, Anthropic ou tout autre service externe.</p><p>Nous allons utiliser LM Studio pour servir GPT-OSS localement et le connecter à Elastic Agent Builder.</p><p>À la fin de cet article, vous disposerez d'un agent d'IA personnalisé capable de répondre à des questions en langage naturel sur les données de vos employés, tout en conservant un contrôle total sur vos informations et votre modèle.</p><h2>Produits requis</h2><p>Pour cet article, vous avez besoin de :</p><ul><li><p><a href="https://www.elastic.co/cloud">Elastic Cloud</a> hébergé 9.2, déploiement sans serveur ou <a href="https://www.elastic.co/docs/deploy-manage/deploy/self-managed/local-development-installation-quickstart">local</a>.</p></li><li><p>Machine avec 32 Go de RAM recommandée (minimum 16 Go pour GPT-OSS 20B)</p></li><li><p><a href="https://lmstudio.ai/">LM Studio</a> installé</p></li><li><p><a href="https://www.docker.com/products/docker-desktop/">Docker Desktop</a> installé</p></li></ul><h2>Pourquoi utiliser GPT-OSS ?</h2><p>Avec un LLM local, vous avez la possibilité de le déployer dans votre propre infrastructure et de l'adapter à vos besoins. Tout cela en gardant le contrôle sur les données que vous partagez avec le modèle et, bien sûr, sans avoir à payer de licence à un fournisseur externe.</p><p>OpenAI <a href="https://openai.com/index/introducing-gpt-oss/">a publié GPT-OSS</a> le 5 août 2025, dans le cadre de son engagement envers l'écosystème des modèles ouverts.</p><p>Le modèle de paramètres 20B offre :</p><ul><li><p><strong>Capacités d'utilisation des outils</strong></p></li><li><p><strong>Inférence efficace</strong></p></li><li><p><strong>Compatible avec le SDK OpenAI</strong></p></li><li><p><strong>Compatible avec les flux de travail agentiques</strong></p></li></ul><p>Comparaison des points de repère :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt58fab956edb40412/6a170cfcb0367da43a72bd80/29160e3345352088e8213297630882f252b00c47-1600x680.png" alt="" /><h2>Architecture de la solution</h2><p>L'architecture fonctionne entièrement sur votre machine locale. Elastic (exécuté dans Docker) communique directement avec votre LLM local via LM Studio, et Elastic Agent Builder utilise cette connexion pour créer des agents d'IA personnalisés qui peuvent interroger les données de vos employés.</p><p>Pour plus de détails, consultez cette <a href="https://www.elastic.co/docs/solutions/observability/connect-to-own-local-llm">documentation</a>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt80db5bb0a797f51b/6a170cfd0e2e492f2c41a16f/a4a886750ff25fa8bb7aefc7448161e52cf73ed3-1600x896.png" alt="" /><h2>Construire un agent d'IA pour les RH : étapes</h2><p>Nous diviserons la mise en œuvre en 5 étapes :</p><ol><li><p>Configurer LM studio avec un modèle local</p></li><li><p>Déployer Elastic local avec Docker</p></li><li><p>Créer le connecteur OpenAI dans Elastic</p></li><li><p>Téléchargement des données des employés vers Elasticsearch</p></li><li><p>Créez et testez votre agent d'intelligence artificielle</p></li></ol><h2>Étape 1 : Configurer LM Studio avec GPT-OSS 20B</h2><p>LM Studio est une application conviviale qui vous permet d'exécuter localement de grands modèles linguistiques sur votre ordinateur. Il fournit un serveur d'API compatible avec OpenAI, ce qui facilite l'intégration avec des outils tels qu'Elastic sans processus de configuration complexe. Pour plus de détails, reportez-vous à la <a href="https://lmstudio.ai/docs/app">documentation de LM Studio</a>.</p><p>Tout d'abord, téléchargez et installez LM Studio depuis le site officiel. Une fois installée, ouvrez l'application.</p><h3>Dans l'interface de LM Studio :</h3><ol><li><p>Allez dans l'onglet recherche et cherchez "GPT-OSS"</p></li><li><p>Sélectionnez le site <code>openai/gpt-oss-20b</code> à partir d'OpenAI</p></li><li><p>Cliquez sur télécharger</p></li></ol><p>La taille de ce modèle devrait être d'environ <strong>12,10 Go.</strong> Le téléchargement peut prendre quelques minutes, en fonction de votre connexion internet.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2dc341a6625e34b7/6a170cff839dfa2eb4dcff44/5d01bc4dcb377b5259fc6b521fe2425a31b90ca4-1312x872.png" alt="" /><h4>Une fois le modèle téléchargé :</h4><ol><li><p>Aller dans l'onglet du serveur local</p></li><li><p>Sélectionner l'openai/gpt-oss-20b</p></li><li><p>Utiliser le port par défaut 1234</p></li><li><p>Dans le panneau de droite, cliquez sur <strong>Charger </strong>et réglez la longueur du contexte sur <strong>40K</strong> ou plus.</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3704ca1b28465cc4/6a170d00d7c022ed8fde64ef/e546033f916381647b876815b2c1f1ae2a08365f-326x337.png" alt="" /><p>5. Cliquez sur démarrer le serveur</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7b9170a4945ff857/6a170d0266c4f9ffadf8c0a6/28ee78a3caa84d14e04db3d42f30acbe4d4d005a-1312x872.png" alt="" /><p>Vous devriez voir ceci si le serveur est en cours d'exécution.</p>[LM STUDIO SERVER] Success! HTTP server listening on port 1234
[LM STUDIO SERVER] Supported endpoints:
[LM STUDIO SERVER] -&gt;	GET  http://localhost:1234/v1/models
[LM STUDIO SERVER] -&gt;	POST http://localhost:1234/v1/responses
[LM STUDIO SERVER] -&gt;	POST http://localhost:1234/v1/chat/completions
[LM STUDIO SERVER] -&gt;	POST http://localhost:1234/v1/completions
[LM STUDIO SERVER] -&gt;	POST http://localhost:1234/v1/embeddings
Server started.<h2>Étape 2 : Déployer Elastic local avec Docker</h2><p>Nous allons maintenant configurer Elasticsearch et Kibana localement à l'aide de Docker. Elastic fournit un script pratique qui gère l'ensemble du processus d'installation. Pour plus de détails, voir la <a href="https://www.elastic.co/docs/deploy-manage/deploy/self-managed/local-development-installation-quickstart">documentation officielle</a>.</p><h3>Exécuter le script start-local</h3><p>Exécutez la commande suivante dans votre terminal :</p>curl -fsSL https://elastic.co/start-local | sh<p>Ce script va :</p><ul><li><p>Télécharger et configurer Elasticsearch et Kibana</p></li><li><p>Démarrer les deux services à l'aide de Docker Compose</p></li><li><p>Activation automatique d'une licence d'essai Platinum de 30 jours</p></li></ul><h3>Résultats attendus</h3><p>Attendez le message suivant et enregistrez le mot de passe et la clé API indiqués ; vous en aurez besoin pour accéder à Kibana :</p>🎉 Congrats, Elasticsearch and Kibana are installed and running in Docker!
🌐 Open your browser at http://localhost:5601
   Username: elastic
   Password: KSUlOMNr
🔌 Elasticsearch API endpoint: http://localhost:9200
🔑 API key: cnJGX0pwb0JhOG00cmNJVklUNXg6cnNJdXZWMnM4bncwMllpQlFlUTlWdw==
Learn more at https://github.com/elastic/start-local<h3>Accéder à Kibana</h3><p>Ouvrez votre navigateur et naviguez vers :</p>http://localhost:5601<p>Connectez-vous en utilisant les informations d'identification obtenues dans la sortie du terminal.</p><h3>Activer le constructeur d'agents</h3><p>Une fois connecté à Kibana, naviguez vers <strong>Management </strong>&gt;<strong> AI </strong>&gt;<strong> Agent Builder </strong>et activez l'Agent Builder.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0a934bd99fa6a0ce/6a170d046234e019c3db1a5a/92e104cb846c20d875865ded8a3d37f5c7daae9b-1491x1528.png" alt="" /><h2>Étape 3 : Créer le connecteur OpenAI dans Elastic</h2><p>Nous allons maintenant configurer Elastic pour qu'il utilise votre LLM local.</p><h3>Connecteurs d'accès</h3><ol><li><p>Dans Kibana</p></li><li><p>Allez dans <strong>Paramètres du projet</strong> &gt; <strong>Gestion</strong></p></li><li><p>Sous <strong>Alertes et aperçus</strong>, sélectionnez <strong>Connecteurs</strong></p></li><li><p>Cliquez sur Créer un connecteur</p></li></ol><h3>Configurer le connecteur</h3><p>Sélectionnez <strong>OpenAI</strong> dans la liste des connecteurs. LM Studio utilise le SDK OpenAI, ce qui le rend compatible.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt762023c39781eb78/6a170d06a29299a59ed01087/5ac87042e086c7a2bd47a8039e646ec831f0dcc6-923x974.png" alt="" /><p>Remplissez les champs avec ces valeurs :</p><ul><li><p><strong>Nom du connecteur : </strong>LM Studio - GPT-OSS 20B</p></li><li><p><strong>Sélectionnez un fournisseur OpenAI : </strong>Autre (Service compatible avec l'OpenAI)</p></li><li><p><strong>URL : </strong><code>http://host.docker.internal:1234/v1/chat/completions</code></p></li><li><p><strong>Modèle par défaut : </strong>openai/gpt-oss-20b</p></li><li><p><strong>Clé API :</strong> testkey-123 (n'importe quel texte fonctionne, car le serveur LM Studio ne nécessite pas d'authentification).</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt980e595f80e2be2e/6a170d086f7f0468a19148cc/2084ac32fcf1fb810c8b54ecab1c85a1e3e8905b-672x1302.png" alt="" /><p>Pour terminer la configuration, cliquez sur <strong>Save &amp; test.</strong></p><p><strong>Important :</strong> Activez l'option "<strong>Enable native function calling</strong>" (activer l'appel de fonctions natives) ; cette option est nécessaire pour que l'Agent Builder fonctionne correctement. Si vous ne l'activez pas, vous obtiendrez une erreur <strong><code>No tool calls found in the response</code></strong>.</p><h3>Tester la connexion</h3><p>Elastic devrait automatiquement tester la connexion. Si tout est configuré correctement, vous obtiendrez un message de réussite comme celui-ci :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4d2e815dd558f881/6a170d090e2e49076541a177/f567d767f1969c4730c1daa92f651789dc3742ac-1042x812.png" alt="" /><p>Réponse :</p>{
  "status": "ok",
  "data": {
    "id": "chatcmpl-flj9h0hy4wcx4bfson00an",
    "object": "chat.completion",
    "created": 1761189456,
    "model": "openai/gpt-oss-20b",
    "choices": [
      {
        "index": 0,
        "message": {
          "role": "assistant",
          "content": "Hello! 👋 How can I assist you today?",
          "reasoning": "Just greet.",
          "tool_calls": []
        },
        "logprobs": null,
        "finish_reason": "stop"
      }
    ],
    "usage": {
      "prompt_tokens": 69,
      "completion_tokens": 23,
      "total_tokens": 92
    },
    "stats": {},
    "system_fingerprint": "openai/gpt-oss-20b"
  },
  "actionId": "ee1c3aaf-bad0-4ada-8149-118f52dad757"
}<h2>Étape 4 : Téléchargement des données des employés vers Elasticsearch</h2><p>Nous allons maintenant télécharger l'<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/gpt-oss-with-elasticsearch/hr-employees-bulk.json">ensemble de données relatives aux employés des RH</a> afin de montrer comment l'agent travaille avec des données sensibles. J'ai généré un ensemble de données fictives avec cette structure.</p><h3>Structure de l'ensemble de données</h3>{
  "employee_id": "0f4dce68-2a09-4cb1-b2af-6bcb4821539b",
  "full_name": "Daffi Stiebler",
  "email": "lscutchings0@huffingtonpost.com",
  "date_of_birth": "1975-06-20T15:39:36Z",
  "hire_date": "2025-07-28T00:10:45Z",
  "job_title": "Physical Therapy Assistant",
  "department": "HR",
  "salary": "108455",
  "performance_rating": "Needs Improvement",
  "years_of_experience": 2,
  "skills": "Java",
  "education_level": "Master's Degree",
  "manager": "Carl MacGibbon",
  "emergency_contact": "Leigha Scutchings",
  "home_address": "5571 6th Park"
}<h3>Créer l'index avec les correspondances</h3><p>Tout d'abord, créez l'index avec les correspondances appropriées. Notez que nous utilisons des champs <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text">semantic_text</a> pour certains champs clés ; cela permet des capacités de recherche sémantique pour notre index.</p>​​PUT hr-employees
{
  "mappings": {
    "properties": {
      "@timestamp": {
        "type": "date"
      },
      "employee_id": {
        "type": "keyword"
      },
      "full_name": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "email": {
        "type": "keyword"
      },
      "date_of_birth": {
        "type": "date",
        "format": "iso8601"
      },
      "hire_date": {
        "type": "date",
        "format": "iso8601"
      },
      "job_title": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "department": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "salary": {
        "type": "double"
      },
      "performance_rating": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "years_of_experience": {
        "type": "long"
      },
      "skills": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "education_level": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "manager": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "emergency_contact": {
        "type": "keyword"
      },
      "home_address": {
        "type": "keyword"
      },
      "employee_semantic": {
        "type": "semantic_text"
      }
    }
  }
}<h3>Index avec Bulk API</h3><p>Copiez et collez le <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/gpt-oss-with-elasticsearch/hr-employees-bulk.json">jeu de données</a> dans votre Dev Tools dans Kibana et exécutez-le :</p>POST hr-employees/_bulk
{"index": {}}
{"employee_id": "57728b91-e5d7-4fa8-954a-2384040d3886", "full_name": "Filide Gane", "email": "vhallahan1@booking.com", "job_title": "Business Systems Development Analyst", "department": "Marketing", "salary": "$52330.27", "performance_rating": "Meets Expectations", "years_of_experience": 12, "skills": "Java", "education_level": "Bachelor's Degree", "date_of_birth": "2000-02-07T16:49:32Z", "hire_date": "2023-11-07T13:03:16Z", "manager": "Freedman Kings", "emergency_contact": "Vilhelmina Hallahan", "home_address": "75 Dennis Junction"}
{"index": {}}
{"employee_id": "...", ...}<h3>Vérifier les données</h3><p>Exécutez une requête pour vérifier :</p>GET hr-employees/_search<h2>Étape 5 : Créer et tester votre agent d'intelligence artificielle</h2><p>Une fois tout configuré, il est temps de créer un agent d'IA personnalisé à l'aide d'Elastic Agent Builder. Pour plus de détails, voir la <a href="https://www.elastic.co/docs/solutions/search/agent-builder/get-started">documentation Elastic</a>.</p><h3>Ajouter le connecteur</h3><p>Avant de pouvoir créer notre nouvel agent, nous devons configurer notre Agent builder pour qu'il utilise notre connecteur personnalisé appelé <code>LM Studio - GPT-OSS 20B</code>, car le connecteur par défaut est <a href="https://www.elastic.co/docs/reference/kibana/connectors-kibana/elastic-managed-llm">Elastic Managed LLM</a>. Pour cela, nous devons aller dans <strong>Project Setting</strong> &gt; <strong>Management</strong> &gt; <strong>GenAI Settings</strong>; nous sélectionnons alors celui que nous avons créé et cliquons sur <strong>Save.</strong></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc42f079c5e756057/6a170d0acf4f2501d9b2d1c7/11e830c3e2fb4c298b020c928fa5422f3397ba08-1600x1152.png" alt="" /><h3>Agent d'accès Constructeur</h3><ol><li><p>Aller aux <strong>agents</strong></p></li><li><p>Cliquez sur <strong>Créer un nouvel agent</strong></p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb8e734817c5a7c6a/6a170d0ca929cf867cae0a34/c1e60541563650163f972ac9088dc1ed1de759a7-1600x1054.png" alt="" /><h3>Configurer l'agent</h3><p>Pour créer un nouvel agent, les champs obligatoires sont l'<strong>identifiant de l'agent</strong>, le <strong>nom d'affichage</strong> et les <strong>instructions d'affichage.</strong></p><p>Mais il existe d'autres options de personnalisation, comme les instructions personnalisées qui indiquent comment votre agent va se comporter et interagir avec vos outils, à la manière d'une invite système, mais pour notre agent personnalisé. Les étiquettes permettent d'organiser les agents, la couleur de l'avatar et le symbole de l'avatar.</p><p>Ceux que j'ai choisis pour notre agent sur la base de l'ensemble des données sont les suivants :

<strong>Agent ID :</strong> <code>hr_assistant</code></p><p><strong>Instructions personnalisées :</strong></p>You are an HR Analytics Assistant that helps answer questions about employee data.
When responding to queries:
- Provide clear, concise answers
- Include relevant employee details (name, department, salary, skills)
- Format monetary values with currency symbols
- Be professional and maintain data confidentiality<p>
Étiquettes : <code>Human Resources</code> et <code>GPT-OSS</code></p><p>Nom d'affichage : <code>HR Analytics Assistant</code></p><p>Description de l'affichage :</p>A specialized AI assistant for Human Resources that helps analyze employee data, compensation, performance metrics, and talent management. Ask questions about employees, departments, salaries, or performance analytics.<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt23fb011e5b4f4d49/6a170d0e7d8d67f47a70e77f/f94bb2bf08497e5e756ca76b30a3a51f42927756-1424x1217.png" alt="" /><p>Une fois toutes les données saisies, nous pouvons cliquer sur <strong>Enregistrer</strong> notre nouvel agent.</p><h3>Tester l'agent</h3><p>Vous pouvez désormais poser des questions en langage naturel sur les données de vos employés, et GPT-OSS 20B comprendra l'intention et générera une réponse appropriée.</p><h4>Prompt :</h4>Which employee is the one with the highest salary in the hr-employees index?<h4>Réponse :</h4><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc0c52faacf63b583/6a170d0f0e2e497bfd41a17b/94ad19f80b96304028a59f60beca51dfc9aecc8a-899x631.png" alt="" /><p>Le processus de l'agent a été le suivant :</p><p>1. Comprendre votre question à l'aide du connecteur GPT-OSS</p><p>2. Générer la requête Elasticsearch appropriée (à l'aide des outils intégrés ou d'<a href="https://www.elastic.co/docs/reference/query-languages/esql">ES|QL</a> personnalisés)</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte32a8a7e6363c7f2/6a170d115091680077e1bb44/6f2961d0d1b97475f6dda300acee84da540938e6-844x466.png" alt="" /><p>3. Récupérer les enregistrements des salariés correspondants</p><p>4. Présenter les résultats en langage naturel avec un formatage approprié</p><p>Contrairement à la recherche lexicale traditionnelle, l'agent alimenté par GPT-OSS comprend l'intention et le contexte, ce qui facilite la recherche d'informations sans connaître les noms exacts des champs ou la syntaxe de la requête. Pour plus de détails sur le processus de réflexion de l'agent, voir cet <a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-experiments-performance">article</a>.</p><h2>Conclusion</h2><p>Dans cet article, nous avons construit un agent d'IA personnalisé à l'aide de l'Agent Builder d'Elastic pour se connecter au modèle OpenAI GPT-OSS fonctionnant localement. En déployant à la fois Elastic et le LLM sur votre machine locale, cette architecture vous permet de tirer parti des capacités d'IA générative tout en conservant un contrôle total sur vos données, le tout sans envoyer d'informations à des services externes.</p><p>Nous avons utilisé GPT-OSS 20B à titre expérimental, mais les modèles officiellement recommandés pour Elastic Agent Builder sont référencés <a href="https://www.elastic.co/docs/solutions/search/agent-builder/models#recommended-models">ici.</a> Si vous avez besoin de capacités de raisonnement plus avancées, il existe également la <a href="https://huggingface.co/openai/gpt-oss-120b">variante de paramètre 120B</a> qui est plus performante pour les scénarios complexes, bien qu'elle nécessite une machine plus sophistiquée pour fonctionner localement. Pour plus de détails, consultez la <a href="https://openai.com/open-models/">documentation officielle d'OpenAI</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/build-an-ai-agent-hr-elastic-agent-builder-gpt-oss</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/build-an-ai-agent-hr-elastic-agent-builder-gpt-oss</guid>
    <category><![CDATA[IA agentique]]></category>
    <category><![CDATA[IA]]></category>
    <dc:creator><![CDATA[Tomás Murúa]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt664f490053e46e6b/6a170d13b0367d2d7e72bd84/05d2d0513fff67d975f9223d75108aa9f50646bc-1600x914.png" length="0" type="image/png"/>
    <pubDate>Wed, 26 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Principaux projets d'Elastic Agent Builder et enseignements tirés de Cal Hacks 12.0]]></title>
    <description><![CDATA[Explorez les meilleurs projets Elastic Agent Builder de Cal Hacks 12.0 et plongez dans nos commentaires techniques sur Serverless, ES|QL et les architectures d'agents.]]></description>
    <content:encoded><![CDATA[<p>Il y a quelques semaines, nous avons eu l'incroyable opportunité de sponsoriser <a href="https://cal-hacks-12-0.devpost.com/">Cal Hacks 12.0</a>, l'un des plus grands hackathons en personne avec plus de 2000 participants venus du monde entier. Nous avons proposé une piste de prix dédiée à la meilleure utilisation d'Elastic Agent Builder sur Serverless, et la réponse a été phénoménale. En seulement 36 heures, nous avons reçu 29 soumissions qui utilisaient Agent Builder de manière créative, de la construction d'outils de renseignement sur les incendies de forêt aux validateurs StackOverflow.</p><p>Au-delà des projets impressionnants, l'expérience de Cal Hacks 12.0 nous a également apporté quelque chose de tout aussi précieux : un retour d'information rapide et non filtré de la part de développeurs qui découvrent notre pile pour la première fois. Les hackathons sont des tests de pression uniques, avec des délais serrés, une absence totale de connaissances préalables et des obstacles imprévisibles (comme les fameuses pannes de WiFi). Ils révèlent exactement les points forts de l'expérience du développeur et ceux sur lesquels il faut encore travailler. Cela est d'autant plus important aujourd'hui que les développeurs interagissent avec la pile Elastic de nouvelles façons, de plus en plus par le biais de flux de travail pilotés par LLM. Dans cet article de blog, nous allons approfondir ce que les participants ont construit avec Agent Builder et ce que nous avons appris au cours du processus.</p><h2>Les projets gagnants</h2><h3>Première place : AgentOverflow</h3><p>Stack Overflow reconstruit pour l'ère du LLM et de l'agent.</p><p>Pour en savoir plus sur AgentOverflow <a href="https://devpost.com/software/agentoverflow">, cliquez ici.</a></p><p>AgentOverflow s'attaque à un problème que rencontrent la plupart des développeurs d'IA : Les LLM hallucinent, les historiques de conversation disparaissent et les développeurs perdent du temps à résoudre les mêmes problèmes.</p><p>AgentOverflow capture, valide et fait réapparaître de véritables paires problème-solution, afin que les développeurs puissent sortir de la spirale de l'hallucination et livrer plus rapidement.</p><h4>Comment cela fonctionne-t-il ?</h4><p><strong>1. Partager JSON - le "schéma de solution".</strong></p><p>Un clic à partir d'un partage de Claude permet de récupérer, d'extraire et d'assembler une solution de partage JSON, qui est un format structuré contenant :</p><ul><li><p>Problème</p></li><li><p>Contexte</p></li><li><p>Code</p></li><li><p>Balises</p></li><li><p>Vérification des étapes de la solution.</p></li></ul><p>Un validateur (LAVA) vérifie et renforce la structure, l'utilisateur ajoute une ligne de contexte supplémentaire, puis le tout est stocké et indexé dans Elasticsearch.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte7bc35b6d54921e8/6a17f0176df73162760a0fe6/45a3e96f4474050a855419628c2a7338bb12c706-1600x877.png" alt="En cliquant sur &quot;Partager la solution&quot;, la session en cours sera récupérée avec les métadonnées correspondantes." /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9967f52007fff99e/6a17f019ec0f8987c45a6701/2d65cb154d8ee32fc96ff17dfa5b0bf2636e3777-1600x1002.png" alt="Les utilisateurs fournissent un contexte supplémentaire par l'intermédiaire de l'interface web, puis le JSON est indexé dans Elasticsearch." /><p><strong>2. Trouver la solution</strong></p><p>Lorsque vous êtes bloqué, cliquez sur <code>Find Solution</code> et AgentOverflow scrapera votre conversation actuelle, l'utilisera pour construire une requête, et lancera une recherche hybride Elasticsearch pour remonter à la surface :</p><ul><li><p>Corrections classées et validées par la communauté</p></li><li><p>Les invites exactes qui ont résolu le problème à l'origine</p></li></ul><p>Cela permet aux développeurs de copier, coller et débloquer rapidement leur session en cours.</p><p><strong>3. MCP - injection de contexte pour les LLM</strong></p><p>En se connectant aux solutions structurées stockées dans Elasticsearch via MCP (Model Context Protocol), les LLM sont alimentés en contexte à haut signal (code, journaux, configurations, corrections antérieures) au moment de l'exécution sans bruit supplémentaire.</p><p>AgentOverflow utilise Agent Builder avec Elasticsearch comme couche de mémoire structurée qui injecte un contexte pertinent dans les LLM. Ils passent ainsi du statut de chatbots passifs à celui de résolveurs de problèmes conscients du contexte.</p><h3>Deuxième prix : MarketMind</h3><p>Une vue interprétable en temps réel de l'énergie du marché, alimentée par six agents élastiques.</p><p>Pour en savoir plus sur MarketMind <a href="https://devpost.com/software/marketmind-b6cy2q">, cliquez ici.</a></p><p>MarketMind a gagné sa place en offrant aux traders débutants une plateforme qui convertit les données fragmentées du marché en signaux clairs et en temps réel. Au lieu de jongler avec l'évolution des prix, les fondamentaux, le sentiment et la volatilité sur différents outils, MarketMind consolide toutes ces informations sur une seule plateforme, aidant ainsi les traders à obtenir des informations exploitables. Ce projet a également utilisé des requêtes ES|QL complexes lors de la création de ses agents.</p><h4>Comment cela fonctionne-t-il ?</h4><p><strong>1. Collecter des données de marché en temps réel</strong></p><p>MarketMind extrait de Yahoo Finance des données sur l'évolution des cours, les fondamentaux, le sentiment, la volatilité et le risque. Ces données sont ingérées et organisées en plusieurs index Elasticsearch.</p><p><strong>2. Six agents spécialisés analysent le marché</strong></p><p>Chaque agent, créé avec Agent Builder, se concentre sur une couche différente du marché. Ils lisent un index Elasticsearch, calculent leurs propres mesures spécifiques au domaine et génèrent une sortie JSON standardisée avec des scores et un raisonnement.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd4ba582f9872b65b/6a17f01b7f6f15c2d8c09c1c/7d9716cca06a047a2b3584378b5c7e592a785ba1-1284x878.png" alt="6 agents spécialisés GOOGL AI qui analysent le marché" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd86ed3bfe4b8bd2b/6a17f01c5ea30f868164b6ba/5aac6a833347c0d2e596c02049ec4b4d3aae5cd7-794x764.png" alt="Capacités d'analyse des anomalies de volume et de détection des catastrophes des agents spécialisés de GOOGL" /><p><strong>3. Agréger les signaux dans un modèle unifié d'"énergie de marché".</strong></p><p>Les résultats combinés apparaissent sous forme d'impulsions lumineuses autour de chaque action, indiquant si la dynamique se renforce, si le risque augmente ou si le sentiment change.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5af7c7c838308275/6a17f01e42022917b629f6ca/46b3da8e3d528c5dd4e2829416c5446098acb3aa-744x718.png" alt="Modèle unifié d'&quot;énergie de marché&quot; des agents spécialisés de GOOGL" /><p><strong>4. Visualiser les informations</strong></p><p>Le frontend a été construit avec React et <a href="https://github.com/vercel/next.js">Next.js</a>, en utilisant TypeScript, des visuels SVG basés sur la physique, et <a href="https://github.com/chartjs">Chart.js</a> pour les graphiques de chandeliers en direct. L'analyse brute est ainsi transformée en un retour d'information exploitable en temps réel.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt775e1880aa7afacc/6a17f01f1d1b83ce1f93e528/3f000c043117b77ed4127202be5a49c12e3682ba-1600x930.png" alt="Comment visualiser les résultats de l'analyse des agents spécialisés de GOOGL ?" /><h2>Autres projets intéressants :</h2><p>Voici d'autres concurrents de taille qui ont utilisé Elastic dans différentes parties de leur pile :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltffe292009e446a70/6a17f0216df731068c0a0fea/76c49a853426844f475cd6b2a74999e60af20e8c-926x1080.png" alt="" /><p>Vous trouverez <a href="https://cal-hacks-12-0.devpost.com/submissions/search?utf8=%E2%9C%93&amp;prize_filter%5Bprizes%5D%5B%5D=91882">ici la</a> liste complète des projets qui ont été soumis à notre circuit.</p><h2>Ce que nous ont appris les développeurs</h2><ul><li><p><strong>Agent Builder est convivial :</strong></p></li></ul><p>La plupart des équipes n'avaient jamais utilisé Elastic auparavant et étaient encore en mesure de créer des agents rapidement avec peu de soutien. Nous avons organisé un atelier pour ceux qui avaient besoin de plus de conseils, mais la plupart ont été en mesure d'ingérer leurs données et de créer un agent pour effectuer des actions sur ces données.</p><ul><li><p><strong>Les LLM excellent dans les </strong><strong> requêtes</strong><strong><code>kNN</code></strong>, mais ont encore besoin d'être guidés dans la création d'ES|QL :</p></li></ul><p>Demander à ChatGPT-5 de générer des requêtes ES|QL renvoyait des informations incorrectes, mélangeant souvent ES|QL et SQL. Alimenter le LLM avec les documents dans un fichier markdown semblait être une solution viable.</p><ul><li><p><strong>Les fonctions ES|QL en mode instantané ont fait l'objet d'une fuite dans la documentation :</strong></p></li></ul><p>Les fonctions d'agrégation <code>FIRST</code> et <code>LAST</code> ont été glissées involontairement dans nos documents ES|QL. Parce que nous avons fourni ces documents à ChatGPT, le modèle a consciencieusement utilisé ces fonctions, même si elles ne sont pas encore disponibles dans Serverless. Grâce au feedback du groupe, l'ingénierie a rapidement ouvert et fusionné un correctif pour supprimer les fonctions de la documentation publiée<a href="https://github.com/elastic/elasticsearch/pull/137341">(PR #137341</a>).</p><ul><li><p><strong>Absence d'orientations spécifiques à Serverless :</strong></p></li></ul><p>Une équipe a essayé d'activer <code>LOOKUP JOIN</code> sur un index qui n'a pas été créé en mode consultation. Le message d'erreur les a envoyés à la recherche de commandes qui n'existent pas sur Serverless. Nous avons relayé cette information à l'équipe produit, qui a immédiatement ouvert un correctif pour un message actionnable spécifique à Serverless. À plus long terme, l'objectif est de masquer entièrement la complexité de la réindexation<a href="https://github.com/elastic/elasticsearch-serverless/issues/4838">(problème n° 4838</a>).</p><ul><li><p><strong>Valeur des événements en personne :</strong></p></li></ul><p>Les hackathons en ligne sont formidables, mais rien n'égale la boucle de rétroaction rapide que vous obtenez lorsque vous déboguez épaule contre épaule avec des constructeurs. Nous avons vu des équipes intégrer Agent Builder dans différents cas d'utilisation, repérer où l'expérience des développeurs avec ES|QL pouvait être améliorée, et résoudre les problèmes beaucoup plus rapidement qu'en essayant de le faire sur des canaux asynchrones.</p><h2>Conclusion</h2><p>Cal Hacks 12.0 nous a offert plus qu'un week-end de démonstrations intéressantes ; il nous a également permis de comprendre comment les nouveaux développeurs interagissent avec la pile Elastic. En seulement 36 heures, nous avons vu des équipes prendre en main Agent Builder, ingérer des données dans Elasticsearch, concevoir des systèmes multi-agents et tester nos fonctionnalités de différentes manières. L'événement nous a également rappelé pourquoi les événements en personne sont importants. Les boucles de rétroaction rapides, les conversations réelles et le débogage pratique nous ont aidés à comprendre les besoins actuels des développeurs. Nous sommes ravis d'apporter ce que nous avons appris à l'équipe d'ingénieurs. Nous vous donnons rendez-vous au prochain hackathon.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/agent-builder-projects-learnings-cal-hacks-12-0</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/agent-builder-projects-learnings-cal-hacks-12-0</guid>
    <category><![CDATA[IA]]></category>
    <category><![CDATA[IA agentique]]></category>
    <dc:creator><![CDATA[JD Armada]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0f079179be9832d4/6a17f023631730a69c585b6d/8ba034a6f19b50521f541b8131756a8acdb52975-1280x960.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 25 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Création d'une salle de presse avec agent LLM, protocole A2A et MCP dans Elasticsearch : Partie II]]></title>
    <description><![CDATA[Découvrez comment construire une salle de presse hybride spécialisée pour les agents LLM en utilisant le protocole A2A pour la collaboration entre agents et MCP pour l'accès aux outils dans Elasticsearch.]]></description>
    <content:encoded><![CDATA[<h2>A2A et MCP : le code en action</h2><p>Cet article est le complément de l'article "Creating an LLM Agent newsroom with A2A protocol and MCP in Elasticsearch !", qui expliquait les avantages de la mise en œuvre des architectures A2A et MCP au sein du même agent afin de profiter pleinement des avantages uniques des deux frameworks. Un <a href="https://github.com/justincastilla/elastic-newsroom">référentiel</a> est disponible si vous souhaitez exécuter la démo par vous-même.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt232e466d2153c764/6a17f15f631730042d585b8d/7196f004089127f83547b2e5dc3f663205cfcdce-1162x1600.png" alt="A2A &amp; Flux de travail de l'agent de protocole MCP" /><p>Voyons comment les agents de notre salle de presse collaborent en utilisant à la fois A2A et MCP pour produire un article. Le référentiel d'accompagnement pour voir les agents en action est disponible <a href="https://github.com/justincastilla/elastic-newsroom">ici.</a></p><h3>Étape 1 : Attribution de l'histoire</h3><p>Le <strong>chef de l'information</strong> (agissant en tant que client) attribue un sujet :</p>{
  "message_type": "task_request",
  "sender": "news_chief",
  "receiver": "reporter_agent",
  "payload": {
    "task_id": "story_renewable_energy_2024",
    "assignment": {
      "topic": "Renewable Energy Adoption in Europe",
      "angle": "Policy changes driving solar and wind expansion",
      "target_length": 1200,
      "deadline": "2025-09-30T18:00:00Z"
    }
  }
}<h3>Étape 2 : Le journaliste demande des recherches</h3><p>L'<strong>agent rapporteur</strong> reconnaît qu'il a besoin d'informations générales et délègue à l'<strong>agent chercheur</strong> par l'intermédiaire de l'A2A :</p>{
  "message_type": "task_request",
  "sender": "reporter_agent",
  "receiver": "researcher_agent",
  "payload": {
    "task_id": "research_eu_renewable_2024",
    "parent_task_id": "story_renewable_energy_2024",
    "capability": "fact_gathering",
    "parameters": {
      "queries": [
        "EU renewable energy capacity 2024",
        "Solar installations growth Europe",
        "Wind energy policy changes 2024"
      ],
      "depth": "comprehensive"
    }
  }
}<h3>Étape 3 : Le rapporteur demande le contexte historique à l'agent d'archivage</h3><p>L'<strong>agent rapporteur</strong> reconnaît que le contexte historique renforcerait l'histoire. Il délègue à l'<strong>agent d'archivage</strong> (alimenté par l'<a href="https://www.elastic.co/docs/solutions/search/elastic-agent-builder">agent A2A d'Elastic</a>), via A2A, le soin d'effectuer des recherches dans les archives d'articles de la salle de presse alimentées par Elasticsearch :</p>{
  "message_type": "task_request",
  "sender": "reporter_agent",
  "receiver": "archive_agent",
  "payload": {
    "task_id": "archive_search_renewable_2024",
    "parent_task_id": "story_renewable_energy_2024",
    "capability": "search_archive",
    "parameters": {
      "query": "European renewable energy policy changes and adoption trends over past 5 years",
      "focus_areas": ["solar", "wind", "policy", "Germany", "France"],
      "time_range": "2019-2024",
      "result_count": 10
    }
  }
}<h3>Étape 4 : L'agent d'archivage utilise l'agent Elastic A2A avec MCP</h3><p>L'<strong>agent d'archivage</strong> utilise l'agent A2A d'Elastic, qui à son tour utilise MCP pour accéder aux outils Elasticsearch. Ceci démontre l'architecture hybride où A2A permet la collaboration des agents tandis que MCP fournit l'accès aux outils :</p># Archive Agent using Elastic A2A Agent
async def search_historical_articles(self, query_params):
    # The Archive Agent sends a request to Elastic's A2A Agent
    elastic_response = await self.a2a_client.send_request(
        agent="elastic_agent",
        capability="search_and_analyze",
        parameters={
            "natural_language_query": query_params["query"],
            "index_pattern": "newsroom-articles-*",
            "filters": {
                "topics": query_params["focus_areas"],
                "date_range": query_params["time_range"]
            },
            "analysis_type": "trend_analysis"
        }
    )
    
    # Elastic's A2A Agent internally uses MCP tools:
    # - platform.core.search (to find relevant articles)
    # - platform.core.generate_esql (to analyze trends)
    # - platform.core.index_explorer (to identify relevant indices)
    
    return elastic_response<p>L'<strong>agent d'archivage</strong> reçoit des données historiques complètes de l'agent A2A d'Elastic et les renvoie au rapporteur :</p>{
  "message_type": "task_response",
  "sender": "archive_agent",
  "receiver": "reporter_agent",
  "payload": {
    "task_id": "archive_search_renewable_2024",
    "status": "completed",
    "archive_data": {
      "historical_articles": [
        {
          "title": "Germany's Energiewende: Five Years of Solar Growth",
          "published": "2022-06-15",
          "key_points": [
            "Germany added 7 GW annually 2020-2022",
            "Policy subsidies drove 60% of growth"
          ],
          "relevance_score": 0.94
        },
        {
          "title": "France Balances Nuclear and Renewables",
          "published": "2023-03-20",
          "key_points": [
            "France increased renewable target to 40% by 2030",
            "Solar capacity doubled 2021-2023"
          ],
          "relevance_score": 0.89
        }
      ],
      "trend_analysis": {
        "coverage_frequency": "EU renewable stories increased 150% since 2019",
        "emerging_themes": ["policy incentives", "grid modernization", "battery storage"],
        "coverage_gaps": ["Small member states", "offshore wind permitting"]
      },
      "total_articles_found": 47,
      "search_confidence": 0.91
    }
  }
}<p>Cette étape montre comment l'agent A2A d'Elastic s'intègre dans le flux de travail de la salle de presse. L'Archive Agent (un agent spécifique à la salle de presse) se coordonne avec l'A2A Agent d'Elastic (un spécialiste tiers) pour exploiter les puissantes capacités de recherche et d'analyse d'Elasticsearch. L'agent Elastic utilise MCP en interne pour accéder aux outils Elasticsearch, ce qui montre la séparation nette entre la coordination de l'agent (A2A) et l'accès aux outils (MCP).</p><h3>Étape 5 : Le chercheur utilise les serveurs MCP</h3><p>L'<strong>agent chercheur</strong> accède à plusieurs serveurs MCP pour recueillir des informations :</p># Researcher Agent using MCP to access tools
async def gather_facts(self, queries):
    results = []
    
    # Use News API MCP Server
    news_data = await self.mcp_client.invoke_tool(
        server="news_api_mcp",
        tool="search_articles",
        parameters={
            "query": queries[0],
            "date_range": "2024-01-01 to 2024-09-30",
            "sources": ["reuters", "bloomberg", "euractiv"]
        }
    )
    
    # Use Statistics MCP Server
    stats_data = await self.mcp_client.invoke_tool(
        server="eurostat_mcp",
        tool="get_energy_statistics",
        parameters={
            "metrics": ["solar_capacity_mw", "wind_capacity_mw"],
            "countries": ["EU27"],
            "year": 2024
        }
    )
    
    # Use Fact-Checking MCP Server
    verified_facts = await self.mcp_client.invoke_tool(
        server="factcheck_mcp",
        tool="verify_claims",
        parameters={
            "claims": [
                "EU solar capacity increased 40% in 2024",
                "Germany leads in wind installations"
            ]
        }
    )
    
    return self._synthesize_research(news_data, stats_data, verified_facts)<h3>Étape 6 : Le chercheur renvoie les données au rapporteur</h3><p>L'<strong>agent chargé de la recherche</strong> renvoie la recherche complète par l'intermédiaire de l'A2A :</p>{
  "message_type": "task_response",
  "sender": "researcher_agent",
  "receiver": "reporter_agent",
  "payload": {
    "task_id": "research_eu_renewable_2024",
    "status": "completed",
    "research_data": {
      "key_facts": [
        {
          "claim": "EU solar capacity reached 260 GW in 2024",
          "source": "Eurostat Q3 2024 Report",
          "confidence": 0.95,
          "verified": true
        },
        {
          "claim": "Germany installed 12 GW of wind capacity in 2024",
          "source": "German Federal Network Agency",
          "confidence": 0.92,
          "verified": true
        }
      ],
      "statistics": {
        "solar_growth_rate": "35%",
        "wind_growth_rate": "28%"
      },
      "sources_count": 15
    }
  }
}<h3>Étape 7 : Le journaliste rédige un article</h3><p>L'<strong>agent rapporteur</strong> utilise les données de recherche et ses propres capacités LLM pour rédiger l'article. Pendant la rédaction, le Reporter utilise les serveurs MCP pour le style et les modèles :</p># Reporter Agent writing with MCP assistance
async def write_article(self, research_data, assignment):
    # Get style guidelines via MCP
    style_guide = await self.mcp_client.get_resource(
        server="newsroom_mcp",
        resource="style://editorial/ap_style_guide"
    )
    
    # Get article template via MCP
    template = await self.mcp_client.get_resource(
        server="newsroom_mcp",
        resource="template://articles/news_story"
    )
    
    # Generate article using LLM + research + style
    draft = await self.llm.generate(
        prompt=f"""
        Write a news article following these guidelines:
        {style_guide}
        
        Using this template:
        {template}
        
        Based on this research:
        {research_data}
        
        Assignment: {assignment}
        """
    )
    
    # Self-evaluate confidence in claims
    confidence_check = await self._evaluate_confidence(draft)
    
    return draft, confidence_check<h3>Étape 8 : le manque de confiance déclenche une nouvelle recherche</h3><p>L'<strong>agent déclarant</strong> évalue son projet et constate qu'une créance a un faible degré de confiance. Il envoie une autre demande à l'<strong>agent du chercheur :</strong></p>{
  "message_type": "collaboration_request",
  "sender": "reporter_agent",
  "receiver": "researcher_agent",
  "payload": {
    "request_type": "fact_verification",
    "claims": [
      {
        "text": "France's nuclear phase-down contributed to 15% increase in renewable capacity",
        "context": "Discussing policy drivers for renewable growth",
        "current_confidence": 0.45,
        "required_confidence": 0.80
      }
    ],
    "urgency": "high"
  }
}<p>Le <strong>chercheur</strong> vérifie l'allégation à l'aide des serveurs MCP de vérification des faits et renvoie les informations mises à jour :</p>{
  "message_type": "collaboration_response",
  "sender": "researcher_agent",
  "receiver": "reporter_agent",
  "payload": {
    "verified_claims": [
      {
        "original_claim": "France's nuclear phase-down contributed to 15% increase...",
        "verified_claim": "France's renewable capacity increased 18% in 2024, partially offsetting reduced nuclear output",
        "confidence": 0.88,
        "corrections": "Percentage was 18%, not 15%; nuclear phase-down is gradual, not primary driver",
        "sources": ["RTE France", "French Energy Ministry Report 2024"]
      }
    ]
  }
}<h3>Étape 9 : Le journaliste révise le texte et le soumet au rédacteur en chef</h3><p>Le <strong>rapporteur</strong> incorpore les faits vérifiés et envoie le projet complet à l'<strong>agent rédacteur</strong> par l'intermédiaire de l'A2A :</p>{
  "message_type": "task_request",
  "sender": "reporter_agent",
  "receiver": "editor_agent",
  "payload": {
    "task_id": "edit_renewable_story",
    "parent_task_id": "story_renewable_energy_2024",
    "content": {
      "headline": "Europe's Renewable Revolution: Solar and Wind Surge 30% in 2024",
      "body": "[Full article text...]",
      "word_count": 1185,
      "sources": [/* array of sources */]
    },
    "editing_requirements": {
      "check_style": true,
      "check_facts": true,
      "check_seo": true
    }
  }
}<h3>Étape 10 : Examens des éditeurs à l'aide des outils MCP</h3><p>L'<strong>agent rédacteur</strong> utilise plusieurs serveurs MCP pour réviser l'article :</p># Editor Agent using MCP for quality checks
async def review_article(self, content):
    # Grammar and style check
    grammar_issues = await self.mcp_client.invoke_tool(
        server="grammarly_mcp",
        tool="check_document",
        parameters={"text": content["body"]}
    )
    
    # SEO optimization check
    seo_analysis = await self.mcp_client.invoke_tool(
        server="seo_mcp",
        tool="analyze_content",
        parameters={
            "headline": content["headline"],
            "body": content["body"],
            "target_keywords": ["renewable energy", "Europe", "solar", "wind"]
        }
    )
    
    # Plagiarism check
    originality = await self.mcp_client.invoke_tool(
        server="plagiarism_mcp",
        tool="check_originality",
        parameters={"text": content["body"]}
    )
    
    # Generate editorial feedback
    feedback = await self._generate_feedback(
        grammar_issues, 
        seo_analysis, 
        originality
    )
    
    return feedback<p>Le <strong>rédacteur en chef</strong> approuve l'article et le transmet :</p>{
  "message_type": "task_response",
  "sender": "editor_agent",
  "receiver": "reporter_agent",
  "payload": {
    "status": "approved",
    "quality_score": 9.2,
    "minor_edits": [
      "Changed 'surge' to 'increased' in paragraph 3 for AP style consistency",
      "Added Oxford comma in list of countries"
    ],
    "approved_content": "[Final edited article]"
  }
}<h3>Étape 11 : L'éditeur publie via CI/CD</h3><p>Enfin, l'<strong>agent imprimeur</strong> publie l'article approuvé en utilisant les serveurs MCP pour le CMS et le pipeline CI/CD :</p># Publisher Agent publishing via MCP
async def publish_article(self, content, metadata):
    # Upload to CMS via MCP
    cms_result = await self.mcp_client.invoke_tool(
        server="wordpress_mcp",
        tool="create_post",
        parameters={
            "title": content["headline"],
            "body": content["body"],
            "status": "draft",
            "categories": metadata["categories"],
            "tags": metadata["tags"],
            "featured_image_url": metadata["image_url"]
        }
    )
    
    post_id = cms_result["post_id"]
    
    # Trigger CI/CD deployment via MCP
    deploy_result = await self.mcp_client.invoke_tool(
        server="cicd_mcp",
        tool="trigger_deployment",
        parameters={
            "pipeline": "publish_article",
            "environment": "production",
            "post_id": post_id,
            "schedule": "immediate"
        }
    )
    
    # Track analytics
    await self.mcp_client.invoke_tool(
        server="analytics_mcp",
        tool="register_publication",
        parameters={
            "post_id": post_id,
            "publish_time": datetime.now().isoformat(),
            "story_id": metadata["story_id"]
        }
    )
    
    return {
        "status": "published",
        "post_id": post_id,
        "url": f"https://newsroom.example.com/articles/{post_id}",
        "deployment_id": deploy_result["deployment_id"]
    }<p>L'<strong>éditeur</strong> confirme la publication via A2A :</p>{
  "message_type": "task_complete",
  "sender": "printer_agent",
  "receiver": "news_chief",
  "payload": {
    "task_id": "story_renewable_energy_2024",
    "status": "published",
    "publication": {
      "url": "https://newsroom.example.com/articles/renewable-europe-2024",
      "published_at": "2025-09-30T17:45:00Z",
      "post_id": "12345"
    },
    "workflow_metrics": {
      "total_time_minutes": 45,
      "agents_involved": ["reporter", "researcher", "archive", "editor", "printer"],
      "iterations": 2,
      "mcp_calls": 12
    }
  }
}<p>Voici la séquence complète du flux de travail A2A dans le référentiel d'accompagnement en utilisant les mêmes agents que ceux décrits ci-dessus.</p><p>#</p><p>De</p><p>Pour</p><p>Action</p><p>Protocole</p><p>Description</p><p>1</p><p>Utilisateur</p><p>Chef de l'information</p><p>Attribuer l'histoire</p><p>HTTP POST</p><p>L'utilisateur soumet le sujet et l'angle de l'article</p><p>2</p><p>Chef de l'information</p><p>Interne</p><p>Créer une histoire</p><p>-</p><p>Création d'un enregistrement d'histoire avec un identifiant unique</p><p>3</p><p>Chef de l'information</p><p>Reporter</p><p>Affectation des délégués</p><p>A2A</p><p>Envoi de l'article via le protocole A2A</p><p>4</p><p>Reporter</p><p>Interne</p><p>Accepter l'affectation</p><p>-</p><p>Stockage interne de l'affectation</p><p>5</p><p>Reporter</p><p>Serveur MCP</p><p>Générer un plan</p><p>MCP/HTTP</p><p>Création d'un plan d'article et de questions de recherche</p><p>6a</p><p>Reporter</p><p>Chercheur</p><p>Demande de recherche</p><p>A2A</p><p>Envoie des questions (parallèle avec 6b)</p><p>6b</p><p>Reporter</p><p>Archiviste</p><p>Recherche dans les archives</p><p>A2A JSONRPC</p><p>Recherche d'articles historiques (parallèle avec 6a)</p><p>7</p><p>Chercheur</p><p>Serveur MCP</p><p>Questions de recherche</p><p>MCP/HTTP</p><p>Utilise l'Anthropique via le MCP pour répondre aux questions</p><p>8</p><p>Chercheur</p><p>Reporter</p><p>Recherche en matière de retour</p><p>A2A</p><p>Réponses à la recherche sur les retours</p><p>9</p><p>Archiviste</p><p>Elasticsearch</p><p>Index de recherche</p><p>API REST ES</p><p>Requêtes news_archive index</p><p>10</p><p>Archiviste</p><p>Reporter</p><p>Retour à l'archive</p><p>A2A JSONRPC</p><p>Renvoie les résultats de la recherche historique</p><p>11</p><p>Reporter</p><p>Serveur MCP</p><p>Générer un article</p><p>MCP/HTTP</p><p>Création d'un article dans un contexte de recherche ou d'archives</p><p>12</p><p>Reporter</p><p>Interne</p><p>Projet de magasin</p><p>-</p><p>Sauvegarde interne du projet</p><p>13</p><p>Reporter</p><p>Chef de l'information</p><p>Soumettre le projet</p><p>A2A</p><p>Soumission d'un projet achevé</p><p>14</p><p>Chef de l'information</p><p>Interne</p><p>Mise à jour de l'histoire</p><p>-</p><p>Stocke le projet, met à jour le statut à "draft_submitted"</p><p>15</p><p>Chef de l'information</p><p>Éditeur</p><p>Projet de révision</p><p>A2A</p><p>Routes automatiques vers l'éditeur pour révision</p><p>16</p><p>Éditeur</p><p>Serveur MCP</p><p>Article de synthèse</p><p>MCP/HTTP</p><p>Analyse du contenu à l'aide d'Anthropic via MCP</p><p>17</p><p>Éditeur</p><p>Chef de l'information</p><p>Retourner à l'examen</p><p>A2A</p><p>Envoi d'un retour d'information et de suggestions éditoriales</p><p>18</p><p>Chef de l'information</p><p>Interne</p><p>Revue des magasins</p><p>-</p><p>Stocke les commentaires des éditeurs</p><p>19</p><p>Chef de l'information</p><p>Reporter</p><p>Appliquer les modifications</p><p>A2A</p><p>Acheminement du retour d'information sur l'examen au rapporteur</p><p>20</p><p>Reporter</p><p>Serveur MCP</p><p>Appliquer les modifications</p><p>MCP/HTTP</p><p>Révision de l'article en fonction du retour d'information</p><p>21</p><p>Reporter</p><p>Interne</p><p>Projet de mise à jour</p><p>-</p><p>Mise à jour du projet avec des révisions</p><p>22</p><p>Reporter</p><p>Chef de l'information</p><p>Retour Révisé</p><p>A2A</p><p>Retourne l'article révisé</p><p>23</p><p>Chef de l'information</p><p>Interne</p><p>Mise à jour de l'histoire</p><p>-</p><p>Stocker le projet révisé, statut à "révisé"</p><p>24</p><p>Chef de l'information</p><p>Éditeur</p><p>Publier un article</p><p>A2A</p><p>Routes automatiques vers l'éditeur</p><p>25</p><p>Éditeur</p><p>Serveur MCP</p><p>Générer des étiquettes</p><p>MCP/HTTP</p><p>Création d'étiquettes et de catégories</p><p>26</p><p>Éditeur</p><p>Elasticsearch</p><p>Index Article</p><p>API REST ES</p><p>Indexe l'article dans l'index news_archive</p><p>27</p><p>Éditeur</p><p>Système de fichiers</p><p>Sauvegarder la démarque</p><p>Fichier E/S</p><p>Enregistre l'article au format .md fichier dans /articles</p><p>28</p><p>Éditeur</p><p>Chef de l'information</p><p>Confirmer la publication</p><p>A2A</p><p>Renvoie l'état de réussite</p><p>29</p><p>Chef de l'information</p><p>Interne</p><p>Mise à jour de l'histoire</p><p>-</p><p>Mise à jour du statut de l'article à "publié"</p><h2>Conclusion</h2><p>L'A2A et le MCP ont tous deux un rôle important à jouer dans le paradigme moderne de l'infrastructure augmentée-LLM. L'A2A offre une certaine souplesse pour les systèmes multi-agents complexes, mais potentiellement moins de portabilité et une plus grande complexité opérationnelle. MCP offre une approche standardisée pour l'intégration des outils, plus simple à mettre en œuvre et à maintenir, bien qu'il ne soit pas conçu pour gérer l'orchestration multi-agents.</p><p>Le choix n'est pas binaire. Comme le montre notre exemple de salle de presse, les systèmes les plus sophistiqués et les plus efficaces soutenus par le LLM combinent souvent les deux approches : les agents se coordonnent et se spécialisent par le biais de protocoles A2A tout en accédant à leurs outils et à leurs ressources par le biais de serveurs MCP. Cette architecture hybride offre les avantages organisationnels des systèmes multi-agents ainsi que les avantages de la normalisation et de l'écosystème du MCP. Cela suggère qu'il n'est peut-être pas nécessaire de faire un choix : il suffit d'utiliser les deux en tant qu'approche standard.</p><p>C'est à vous, en tant que développeur ou architecte, de tester et de déterminer le meilleur mélange de ces deux solutions pour obtenir le bon résultat pour votre cas d'utilisation spécifique. Comprendre les points forts, les limites et les applications appropriées de chaque approche vous permettra de construire des systèmes d'IA plus efficaces, plus faciles à maintenir et plus évolutifs.</p><p>Que vous construisiez une salle de presse numérique, une plateforme de service à la clientèle, un assistant de recherche ou toute autre application alimentée par le LLM, l'examen attentif de vos besoins de coordination (A2A) et des exigences d'accès aux outils (MCP) vous mettra sur la voie de la réussite.</p><h2>Ressources supplémentaires</h2><ul><li><p><strong>Elasticsearch Agent Builder </strong><a href="https://www.elastic.co/docs/solutions/search/elastic-agent-builder">: https://www.elastic.co/docs/solutions/search/elastic-agent-builder</a></p></li><li><p><strong>Spécification A2A</strong> <a href="https://a2a-protocol.org/latest/specification/">: https://a2a-protocol.org/latest/specification/</a></p></li><li><p><strong>Intégration A2A et MCP</strong> <a href="https://a2a-protocol.org/latest/topics/a2a-and-mcp/">: https://a2a-protocol.org/latest/topics/a2a-and-mcp/</a></p></li><li><p><strong>Modèle de protocole de contexte</strong> <a href="https://modelcontextprotocol.io/">: https://modelcontextprotocol.io</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/a2a-protocol-mcp-llm-agent-workflow-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/a2a-protocol-mcp-llm-agent-workflow-elasticsearch</guid>
    <category><![CDATA[IA agentique]]></category>
    <category><![CDATA[IA]]></category>
    <dc:creator><![CDATA[Justin Castilla]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1b1f22cdc2130333/6a17f161ec0f8917fa5a6712/f87330e5d4ca961593b3cfb861ca850a4cc34186-1519x1173.png" length="0" type="image/png"/>
    <pubDate>Mon, 24 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Vous savez, pour le contexte - Partie II : L'IA agentique et le besoin d'ingénierie contextuelle]]></title>
    <description><![CDATA[Découvrez comment l'évolution des LLM vers l'IA agentique augmente le besoin d'ingénierie contextuelle pour résoudre les limites du contexte RAG et la gestion de la mémoire.]]></description>
    <content:encoded><![CDATA[<p>Avec ce <a href="https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-evolution-agentic-ai">contexte</a> (relativement étendu) sur la façon dont les LLM ont changé les processus sous-jacents de la recherche d'informations, voyons comment ils ont également changé la façon dont nous interrogeons les données.</p><h2>Une nouvelle façon d'interagir avec les données</h2><p>L'IA générative (genAI) et l'IA agentique agissent différemment de la recherche traditionnelle. Alors que nous commencions à rechercher des informations par une recherche ("laissez-moi chercher cela sur Google..."), l'action initiale de l'IA générique et des agents se fait généralement par le biais d'un langage naturel saisi dans une interface de dialogue en ligne. L'interface de chat est une discussion avec un LLM qui utilise sa compréhension sémantique pour transformer notre question en une réponse distillée, une réponse résumée semblant provenir d'un oracle qui a une connaissance étendue de toutes sortes d'informations. Ce qui fait vraiment la différence, c'est la capacité du LLM à produire des phrases cohérentes et réfléchies qui rassemblent les éléments de connaissance qu'il fait apparaître - même s'ils sont inexacts ou totalement hallucinés, ils ont une certaine <a href="https://en.wikipedia.org/wiki/Truthiness">véracité</a>.</p><p>Cette vieille barre de recherche avec laquelle nous avons été tellement habitués à interagir peut être considérée comme le moteur RAG que nous utilisions lorsque <em><strong>nous</strong></em> étions nous-mêmes l'agent de raisonnement. Aujourd'hui, même les moteurs de recherche Internet transforment notre expérience de recherche lexicale bien connue en aperçus pilotés par l'IA qui répondent à la requête par un résumé des résultats, ce qui permet aux utilisateurs d'éviter de cliquer et d'évaluer eux-mêmes les résultats individuels.</p><h2>IA générative &amp; RAG</h2><p>L'IA générative tente d'utiliser sa compréhension sémantique du monde pour analyser l'intention subjective exprimée dans une demande de chat, puis utilise ses capacités d'inférence pour créer une réponse d'expert à la volée. L'interaction générative de l'IA comporte plusieurs parties : elle commence par l'entrée/la requête de l'utilisateur, les conversations précédentes dans la session de chat peuvent être utilisées comme contexte supplémentaire, et l'instruction qui indique au LLM comment raisonner et quelles sont les procédures à suivre pour construire la réponse. Les messages-guides ont évolué, passant d'une simple orientation du type ", "Expliquez-moi cela comme si j'étais un enfant de cinq ans", à des descriptions complètes de la manière de traiter les demandes. Ces décompositions comprennent souvent des sections distinctes décrivant les détails du personnage/rôle de l'IA, le raisonnement avant la génération/le processus de réflexion interne, les critères objectifs, les contraintes, le format de sortie, le public, ainsi que des exemples pour aider à démontrer les résultats attendus.</p><p>En plus de la requête de l'utilisateur et de l'invite du système, la génération augmentée de recherche (RAG) fournit des informations contextuelles supplémentaires dans ce que l'on appelle une "fenêtre contextuelle". RAG a été un ajout essentiel à l'architecture ; c'est ce que nous utilisons pour informer le LLM des pièces manquantes dans sa compréhension sémantique du monde.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbfa000ccfdd9d184/6a17ddb57b54f955f38b37da/5b9671d5d07d4caefde372bb3188000754a91eed-1470x746.png" alt="Comment les LLM traitent les demandes des utilisateurs et créent un contexte" /><p>Les fenêtres contextuelles peuvent être un peu <a href="https://www.dbreunig.com/2025/06/22/how-contexts-fail-and-how-to-fix-them.html">tatillonnes</a> en ce qui concerne le contenu, l'emplacement et la quantité que vous leur donnez. Le contexte sélectionné est bien sûr très important, mais le rapport signal/bruit du contexte fourni est également important, de même que la longueur de la fenêtre.</p><h3>Trop peu d'informations</h3><p>Le fait de fournir trop peu d'informations dans une fenêtre de requête, d'invite ou de contexte peut entraîner des hallucinations, car le LLM ne peut pas déterminer avec précision le contexte sémantique correct à partir duquel générer une réponse. La similarité vectorielle de la taille des morceaux de documents pose également des problèmes - une question courte et simple peut ne pas correspondre sémantiquement aux documents riches et détaillés trouvés dans nos bases de connaissances vectorisées. Des techniques d'expansion des requêtes telles que <a href="https://medium.com/data-science/how-to-use-hyde-for-better-llm-rag-retrieval-a0aa5d0e23e8">Hypothetical Document Embeddings (HyDE)</a> ont été développées. Elles utilisent les LLM pour générer une réponse hypothétique qui est plus riche et plus expressive que la requête courte. Le danger ici, bien sûr, est que le document hypothétique est lui-même une hallucination qui éloigne encore plus le LLM du contexte correct.</p><h3>Trop d'informations</h3><p>Tout comme pour nous, un excès d'informations dans une fenêtre contextuelle peut submerger un MLD et le rendre confus quant aux éléments importants. Le débordement de contexte (ou "<a href="https://research.trychroma.com/context-rot">pourriture de contexte</a>") affecte la qualité et les performances des opérations d'IA générative ; il a un impact considérable sur le "budget d'attention" du LLM (sa mémoire de travail) et dilue la pertinence parmi de nombreux éléments concurrents. Le concept de "rotation du contexte" comprend également l'observation selon laquelle les LLM ont tendance à avoir un <a href="https://alexandrabarr.beehiiv.com/p/context-windows">biais de position</a> - ils préfèrent le contenu au début ou à la fin d'une fenêtre contextuelle au contenu de la section centrale.</p><h3>Informations distrayantes ou contradictoires</h3><p>Plus la fenêtre contextuelle est grande, plus il y a de chances qu'elle contienne des informations superflues ou contradictoires qui peuvent distraire le LLM de la sélection et du traitement du contexte correct. D'une certaine manière, il s'agit d'un problème d'entrée et de sortie de déchets : le simple fait de déverser un ensemble de résultats de documents dans une fenêtre contextuelle donne au LLM beaucoup d'informations à mâcher (potentiellement trop), mais en fonction de la manière dont le contexte a été sélectionné, il y a une plus grande possibilité que des informations contradictoires ou non pertinentes s'infiltrent dans le système.</p><h2>IA agentique</h2><p>Je vous avais dit qu'il y avait beaucoup de terrain à couvrir, mais nous l'avons fait - nous parlons enfin de sujets liés à l'IA agentique ! L'IA agentique est une nouvelle utilisation très intéressante des interfaces de chat LLM qui développe la capacité de l'IA générative (peut-on déjà l'appeler "ancienne" ?) à synthétiser des réponses basées sur ses propres connaissances et sur les informations contextuelles que vous lui fournissez. Au fur et à mesure que l'IA générative gagnait en maturité, nous avons réalisé qu'il existait un certain niveau de tâches et d'automatisation que nous pouvions confier aux LLM, initialement reléguées à des activités fastidieuses à faible risque qui peuvent facilement être vérifiées/validées par un être humain. En peu de temps, ce champ d'application initial s'est élargi : une fenêtre de discussion LLM peut désormais être l'étincelle qui envoie un agent d'intelligence artificielle planifier, exécuter, évaluer et adapter son plan de manière itérative afin d'atteindre l'objectif spécifié. Les agents ont accès au raisonnement de leur LLM, à l'historique des discussions et à la mémoire de pensée (telle qu'elle est), et ils disposent également d'outils spécifiques qu'ils peuvent utiliser à cette fin. Nous voyons aussi maintenant des architectures qui permettent à un agent de haut niveau de fonctionner comme l'orchestrateur de plusieurs <a href="https://www.philschmid.de/the-rise-of-subagents">sous-agents</a>, chacun avec ses propres chaînes logiques, ses jeux d'instructions, son contexte et ses outils.</p><p>Les agents sont le point d'entrée d'un flux de travail essentiellement automatisé : ils sont autodirigés en ce sens qu'ils sont capables de discuter avec un utilisateur et d'utiliser ensuite la "logique" pour déterminer les outils dont ils disposent pour répondre à la question de l'utilisateur. Les outils sont généralement considérés comme passifs par rapport aux agents et construits pour effectuer un seul type de tâche. Les <em>types de</em> tâches qu'un outil pourrait accomplir sont en quelque sorte illimités (ce qui est vraiment passionnant !), mais l'une des principales tâches des outils est de rassembler des informations contextuelles qu'un agent doit prendre en compte lors de l'exécution de son flux de travail.</p><p>En tant que technologie, l'IA agentique en est encore à ses balbutiements et est sujette à l'équivalent LLM du trouble déficitaire de l'attention - elle oublie facilement ce qu'on lui a demandé de faire, et part souvent faire d'autres choses qui ne faisaient pas du tout partie du cahier des charges. Sous cette apparente magie, les capacités de "raisonnement" des LLM sont toujours basées sur la prédiction du prochain jeton le plus probable dans une séquence. Pour que le raisonnement (ou, un jour, l'intelligence artificielle générale (AGI)) devienne fiable et digne de confiance, nous devons être en mesure de vérifier que, lorsqu'on leur donne les informations correctes et les plus récentes, ils raisonnent de la manière que nous attendons d'eux (et nous donnent peut-être ce petit plus auquel nous n'aurions pas pensé nous-mêmes). Pour ce faire, les architectures agentiques devront être capables de communiquer clairement (protocoles), de respecter les flux de travail et les contraintes que nous leur imposons (garde-fous), de se rappeler où elles en sont dans une tâche (état), de gérer leur espace mémoire disponible et de valider que leurs réponses sont exactes et répondent aux critères de la tâche.</p><h2>Parlez-moi dans une langue que je peux comprendre</h2><p>Comme c'est souvent le cas dans les nouveaux domaines de développement (en particulier dans le monde des LLM), il existait initialement plusieurs approches pour les communications entre agents et outils, mais elles ont rapidement convergé vers le <a href="https://modelcontextprotocol.io/docs/getting-started/intro">protocole de contexte de modèle (MCP)</a> en tant que norme de facto. La définition du protocole de contexte de modèle est vraiment dans le nom - c'est le <strong>protocole</strong> qu'un <strong>modèle</strong> utilise pour demander et recevoir des informations <strong>contextuelles</strong>. MCP agit comme un adaptateur universel permettant aux agents LLM de se connecter à des outils et à des sources de données externes ; il simplifie et normalise les API de manière à ce que les différents cadres et outils LLM puissent facilement interopérer. Cela fait de MCP une sorte de point de pivot entre la logique d'orchestration et les invites du système données à un agent pour qu'il les exécute de manière autonome au service de ses objectifs, et les opérations envoyées à des outils pour qu'ils les exécutent de manière plus isolée (isolée au moins par rapport à l'agent qui en est à l'origine).</p><p>Cet écosystème est tellement nouveau que chaque direction d'expansion semble être une nouvelle frontière. Nous disposons de protocoles similaires pour les interactions entre agents<a href="https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/">(Agent2Agent (A2A)</a> natch !) ainsi que d'autres projets visant à améliorer la mémoire de raisonnement des agents<a href="https://venturebeat.com/ai/new-memory-framework-builds-ai-agents-that-can-handle-the-real-worlds">(ReasoningBank</a>), à sélectionner le meilleur serveur MCP pour le travail à effectuer<a href="https://arxiv.org/abs/2505.03275">(RAG-MCP</a>), et à utiliser l'analyse sémantique telle que la classification "zero-shot" et la détection de motifs sur les entrées et les sorties comme <a href="https://openai.github.io/openai-guardrails-python/">garde-fous</a> pour contrôler ce sur quoi un agent est autorisé à opérer.</p><p>Vous avez peut-être remarqué que l'intention sous-jacente de chacun de ces projets est d'améliorer la qualité et le contrôle des informations renvoyées à une fenêtre contextuelle agent/genAI ? Alors que l'écosystème de l'IA agentique continue de développer la capacité à mieux traiter ces informations contextuelles (pour les contrôler, les gérer et les exploiter), il sera toujours nécessaire d'extraire les informations contextuelles <em>les plus pertinentes</em> pour que l'agent puisse les mouliner.</p><h2>Bienvenue dans l'ingénierie contextuelle !</h2><p>Si vous êtes familier avec les termes de l'IA générative, vous avez probablement entendu parler de "l'ingénierie des messages" - à ce stade, il s'agit presque d'une pseudo-science à part entière. L'ingénierie des invites est utilisée pour trouver les moyens les meilleurs et les plus efficaces de décrire de manière proactive les comportements que vous souhaitez que le MLD utilise pour générer sa réponse. L'"<a href="https://www.elastic.co/search-labs/blog/context-engineering-overview">ingénierie du contexte</a>" étend les techniques d'"ingénierie de l'invite" au-delà du côté de l'agent pour couvrir également les sources de contexte et les systèmes disponibles du côté des outils du protocole MCP, et comprend les thèmes généraux de la gestion, du traitement et de la génération du contexte :</p><ul><li><p><strong>Gestion du contexte </strong>- liée au maintien de l'efficacité de l'état et du contexte dans des flux de travail agentiques de longue durée et/ou plus complexes. Planification itérative, suivi et orchestration des tâches et de l'utilisation des outils pour atteindre les objectifs de l'agent. En raison du "budget d'attention" limité dont disposent les agents, la gestion du contexte concerne principalement les techniques qui permettent d'affiner la fenêtre contextuelle afin de capturer à la fois la portée la plus complète et les éléments les plus importants du contexte (sa précision par rapport à son rappel !). Les techniques comprennent la compression, le résumé et la persistance du contexte des étapes précédentes ou des appels d'outils pour faire de la place dans la mémoire de travail pour le contexte supplémentaire des étapes suivantes.</p></li><li><p><strong>Traitement du contexte </strong>- Les étapes logiques et, espérons-le, essentiellement programmatiques visant à intégrer, normaliser ou affiner le contexte acquis à partir de sources disparates afin que l'agent puisse raisonner sur l'ensemble du contexte d'une manière quelque peu uniforme. Le travail sous-jacent consiste à faire en sorte que le contexte provenant de toutes les sources (invites, RAG, mémoire, etc.) soit consommé par l'agent le plus efficacement possible. </p></li><li><p><strong>Génération de contexte </strong>- Si le traitement du contexte consiste à rendre le contexte récupéré utilisable par l'agent, alors la génération de contexte donne à l'agent la possibilité de demander et de recevoir ces informations contextuelles supplémentaires à volonté, mais aussi avec des contraintes.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5e1e68c08fe050bc/6a17ddb7414c645035945073/4a8240e1eb078b2294b8d981b9caa8593589cac4-1600x900.png" alt="L'ingénierie contextuelle dans les programmes d'éducation et de formation tout au long de la vie" /><p>Les différents éphémères des applications de chat du LLM correspondent directement (et parfois de manière redondante) à ces fonctions de haut niveau de l'ingénierie contextuelle :</p><ul><li><p><strong>Instructions / invite du système</strong> - Les invites constituent l'échafaudage de la manière dont l'activité générative (ou agentique) de l'IA orientera sa réflexion vers la réalisation de l'objectif de l'utilisateur. Les messages-guides constituent un contexte à part entière ; il ne s'agit pas seulement d'instructions tonales - ils comprennent aussi souvent une logique d'exécution des tâches et des règles telles que "réfléchir étape par étape" ou "respirer profondément" avant de répondre afin de s'assurer que la réponse répond pleinement à la demande de l'utilisateur. Des tests récents ont montré que les langages de balisage sont très efficaces pour encadrer les différentes parties d'une invite, mais il faut également veiller à calibrer les instructions de manière à ce qu'elles soient à la fois trop vagues et trop spécifiques ; nous voulons donner suffisamment d'instructions pour que le LLM trouve le bon contexte, mais sans être trop prescriptif au point de passer à côté d'idées inattendues.</p></li><li><p><strong>Mémoire à court terme</strong> (état/historique) - La mémoire à court terme correspond essentiellement aux interactions de la session de chat entre l'utilisateur et le LLM. Ils sont utiles pour affiner le contexte lors des sessions en direct et peuvent être sauvegardés pour être retrouvés et poursuivis ultérieurement. </p></li><li><p><strong>Mémoire à long terme</strong> - La mémoire à long terme doit être constituée d'informations utiles pour plusieurs sessions. Et il ne s'agit pas seulement de bases de connaissances spécifiques à un domaine auxquelles on accède par le biais de RAG ; des recherches récentes utilisent les résultats de demandes d'IA agentique/générative antérieures pour apprendre et se référer aux interactions agentiques actuelles. Certaines des innovations les plus intéressantes dans le domaine de la mémoire à long terme sont liées à l'ajustement de la manière dont l'état est <a href="https://steve-yegge.medium.com/introducing-beads-a-coding-agent-memory-system-637d7d92514a">stocké et relié</a> afin que les agents puissent reprendre là où ils se sont arrêtés. </p></li><li><p><strong>Sortie structurée</strong> - La cognition nécessite un effort, il n'est donc pas surprenant que même avec des capacités de raisonnement, les LLM (tout comme les humains) veulent dépenser moins d'effort lorsqu'ils pensent, et en l'absence d'une API ou d'un protocole défini, avoir une carte (un schéma) sur la façon de lire les données renvoyées par un appel d'outil est extrêmement utile. L'inclusion de <a href="https://platform.openai.com/docs/guides/structured-outputs?lang=javascript">sorties structurées</a> dans le cadre agentique contribue à rendre ces interactions machine-machine plus rapides et plus fiables, en réduisant les besoins d'analyse.</p></li><li><p><strong>Outils disponibles</strong> - Les outils peuvent faire toutes sortes de choses, de la collecte d'informations supplémentaires (par exemple, en émettant des requêtes RAG vers les référentiels de données de l'entreprise, ou par le biais d'API en ligne) à l'exécution d'actions automatisées au nom de l'agent (comme la réservation d'une chambre d'hôtel sur la base des critères de la demande de l'agent). Les outils peuvent également être des sous-agents disposant de leur propre chaîne de traitement agentique. </p></li><li><p><strong>Retrieval Augmented Generation (RAG)</strong> - J'aime beaucoup la description de RAG en tant qu'"intégration dynamique des connaissances". Comme décrit précédemment, le RAG est la technique permettant de fournir les informations supplémentaires auxquelles le LLM n'a pas eu accès lors de sa formation, ou bien il s'agit d'une réitération des idées que nous pensons être les plus importantes pour obtenir la bonne réponse - celle qui est la plus pertinente par rapport à notre requête subjective.</p></li></ul><h2>Une puissance cosmique phénoménale, un espace de vie minuscule !</h2><p>L'IA agentique a tant de nouveaux domaines fascinants et passionnants à explorer ! Il y a encore beaucoup de problèmes traditionnels de recherche et de traitement de données à résoudre, mais aussi de toutes nouvelles catégories de défis qui commencent seulement à être exposés à la lumière du jour dans la nouvelle ère des LLM. Bon nombre des problèmes immédiats auxquels nous sommes confrontés aujourd'hui sont liés à l'ingénierie contextuelle, c'est-à-dire au fait de fournir aux MFR les informations contextuelles supplémentaires dont ils ont besoin sans surcharger leur espace de mémoire de travail, qui est limité.</p><p>La flexibilité des agents semi-autonomes ayant accès à un ensemble d'outils (et à d'autres agents) donne lieu à tant de nouvelles idées pour la mise en œuvre de l'IA qu'il est difficile d'imaginer les différentes façons dont nous pourrions assembler les pièces du puzzle. La plupart des recherches actuelles s'inscrivent dans le domaine de l'ingénierie contextuelle et se concentrent sur la construction de structures de gestion de la mémoire capables de gérer et de suivre de plus grandes quantités de contexte. En effet, les problèmes de réflexion approfondie que nous voulons vraiment que les LLM résolvent présentent une complexité accrue et des étapes de réflexion plus longues et multiphases, où la mémorisation est extrêmement importante.</p><p>Une grande partie de l'expérimentation en cours dans le domaine consiste à essayer de trouver la gestion optimale des tâches et les configurations d'outils pour alimenter la gueule de l'agent. Chaque appel d'outil dans la chaîne de raisonnement d'un agent entraîne un coût cumulatif, à la fois en termes de calcul pour exécuter la fonction de l'outil et d'impact sur la fenêtre contextuelle limitée. Certaines des dernières techniques de gestion du contexte pour les agents LLM ont provoqué des effets en chaîne involontaires tels que l'"<a href="https://venturebeat.com/ai/ace-prevents-context-collapse-with-evolving-playbooks-for-self-improving-ai">effondrement du contexte</a>", où la compression/le résumé du contexte accumulé pour les tâches de longue durée entraîne <em>trop</em> de pertes. Le résultat souhaité est de disposer d'outils qui renvoient un contexte succinct et précis, sans que des informations superflues ne viennent empiéter sur l'espace mémoire précieux de la fenêtre de contexte.</p><h3>Tant/trop de possibilités</h3><p>Nous voulons une séparation des tâches avec la possibilité de réutiliser les outils/composants, il est donc tout à fait logique de créer des outils agentiques dédiés pour se connecter à des sources de données spécifiques - chaque outil peut se spécialiser dans l'interrogation d'un type de référentiel, d'un type de flux de données, ou même d'un cas d'utilisation. Mais attention : dans le but de gagner du temps/de l'argent/de prouver que quelque chose est possible, la tentation sera grande d'utiliser les MLD comme outil de fédération... Essayez de ne pas le faire, nous sommes déjà passés par <a href="https://www.elastic.co/pdf/elastic-distributed-not-federated-search.pdf">là</a>! La recherche fédérée agit comme un "traducteur universel" qui convertit une requête entrante dans la syntaxe que le référentiel distant comprend, et qui doit ensuite rationaliser les résultats provenant de sources multiples en une réponse cohérente. La fédération en tant que technique <em>fonctionne</em> <em>bien</em> à petite échelle, mais à grande échelle et surtout lorsque les données sont multimodales, la fédération tente de combler des lacunes qui sont tout simplement trop importantes.</p><p>Dans le monde agentique, l'agent serait le fédérateur et les outils (par l'intermédiaire de MCP) seraient les connexions définies manuellement vers des ressources disparates. L'utilisation d'outils dédiés pour accéder à des sources de données non connectées peut sembler être une nouvelle façon puissante d'unir dynamiquement différents flux de données sur la base d'une requête, mais l'utilisation d'outils pour poser la même question à plusieurs sources finira probablement par causer plus de problèmes qu'elle n'en résoudra. Chacune de ces sources de données est probablement constituée de différents types de référentiels, chacun ayant ses propres capacités de récupération, de classement et de sécurisation des données qu'il contient. Ces écarts ou "décalages d'impédance" entre les référentiels augmentent bien entendu la charge de traitement. Ils peuvent également introduire des informations ou des signaux contradictoires, où quelque chose d'apparemment inoffensif comme un décalage de notation peut perturber considérablement l'importance accordée à un élément de contexte renvoyé, et affecter la pertinence de la réponse générée en fin de compte.</p><h3>Le changement de contexte est également difficile pour les ordinateurs</h3><p>Lorsque vous envoyez un agent en mission, sa première tâche consiste souvent à trouver toutes les données pertinentes auxquelles il a accès. Tout comme pour les humains, si chaque source de données à laquelle l'agent se connecte fournit des réponses dissemblables et désagrégées, il y aura une charge cognitive (mais pas exactement du même type) associée à l'extraction des éléments contextuels saillants du contenu récupéré. Cela prend du temps/du calcul, et chaque petit morceau s'additionne dans la chaîne logique agentique. Cela conduit à la conclusion que, à l'instar de ce qui est discuté pour <a href="https://blog.cloudflare.com/code-mode/">MCP</a>, la plupart des outils agentiques devraient plutôt se comporter comme des API - des fonctions isolées avec des entrées et des sorties connues, réglées pour répondre aux besoins de différents types d'agents. Ils <a href="https://arxiv.org/html/2501.12372v5">parviennent</a> beaucoup mieux à relier les points sémantiques, en particulier lorsqu'il s'agit d'une tâche telle que la traduction du langage naturel en syntaxe structurée, lorsqu'ils disposent d'un schéma auquel se référer (RTFM en effet !).</p><h2>7ème manche !</h2><p>Nous avons maintenant abordé l'<a href="https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-evolution-agentic-ai">impact des LLM sur la recherche et l'interrogation de données</a>, ainsi que la manière dont la fenêtre de discussion évolue vers l'expérience de l'IA agentique. Mettons les deux sujets ensemble et voyons comment nous pouvons utiliser nos nouvelles capacités de recherche et d'extraction pour améliorer nos résultats en matière d'ingénierie contextuelle. En route pour la <a href="https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-agentic-ai-accuracy">troisième partie : la puissance de la recherche hybride dans l'ingénierie contextuelle</a>!</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/context-engineering-llm-evolution-agentic-ai</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/context-engineering-llm-evolution-agentic-ai</guid>
    <category><![CDATA[IA agentique]]></category>
    <category><![CDATA[IA]]></category>
    <dc:creator><![CDATA[Woody Walton]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5f98889141fba45b/6a17ddb80b0bed0822dd34a2/79c0378b68d74d9e018c35ee2c1fd17daeee9f2c-1080x608.webp" length="0" type="image/webp"/>
    <pubDate>Tue, 18 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Création d'une salle de presse LLM Agent avec le protocole A2A et MCP dans Elasticsearch : Partie I]]></title>
    <description><![CDATA[Explorer les concepts du protocole A2A et du MCP dans le cadre d'un exemple pratique de salle de presse où des agents LLM spécialisés collaborent à la recherche, à la rédaction, à l'édition et à la publication d'articles de presse.]]></description>
    <content:encoded><![CDATA[<h2>Introduction</h2><p>Les systèmes actuels soutenus par le LLM évoluent rapidement au-delà des applications à modèle unique vers des réseaux complexes où des agents spécialisés travaillent ensemble pour accomplir des tâches que l'informatique moderne n'aurait jamais cru possibles auparavant. Au fur et à mesure que ces systèmes gagnent en complexité, l'infrastructure permettant la communication entre les agents et l'accès aux outils devient l'objectif principal du développement. Deux approches complémentaires sont apparues pour répondre à ces besoins : Les protocoles <strong>Agent2Agent (A2A)</strong> pour la coordination multi-agents, et le <strong>Model Context Protocol (MCP)</strong> pour l'accès standardisé aux outils et aux ressources.</p><p>Comprendre quand utiliser l'un et l'autre en harmonie ou non peut avoir un impact significatif sur l'évolutivité, la maintenabilité et l'efficacité de vos applications. Cet article explore les concepts et les implémentations de l'<strong>A2A</strong> dans l'exemple pratique d'une salle de presse numérique, où des agents LLM spécialisés collaborent à la recherche, à la rédaction, à l'édition et à la publication d'articles de presse.</p><p>Un référentiel d'accompagnement est disponible <a href="https://github.com/justincastilla/elastic-newsroom/tree/main">ici</a>, et nous examinerons des exemples concrets d'A2A en action vers la fin de l'article, à la section 5.</p><h3>Produits requis</h3><p>Le <a href="https://github.com/justincastilla/elastic-newsroom/tree/main">référentiel</a> est constitué d'implémentations basées sur Python des agents A2A. Un serveur API est fourni en Flask, ainsi qu'un service de messagerie Python personnalisé appelé Event Hub, qui achemine les messages pour la journalisation et les mises à jour de l'interface utilisateur. Enfin, une interface utilisateur React est fournie pour une utilisation autonome des fonctionnalités de la salle de presse. Tout est contenu dans une image Docker pour faciliter la mise en œuvre. Si vous souhaitez utiliser les services directement sur votre machine, vous devez vous assurer que ces technologies sont installées :</p><p>Langages et moteurs d'exécution</p><ul><li><p>Python 13.12 - Langage de base du backend</p></li><li><p>Node.js 18+ - React UI en option</p></li></ul><p>Cadres de base et SDKS :</p><ul><li><p>A2A SDK 0.3.8 - Coordination et communication des agents</p></li><li><p>Anthropic SDK - Intégration de Claude pour la génération d'IA</p></li><li><p>Uvicorn - Serveur ASGI pour l'exécution des agents</p></li><li><p>FastMCP 2.12.5+ - Implémentation du serveur MCP</p></li><li><p>React 18.2 - Cadre d'interface utilisateur frontale</p></li></ul><p>Données &amp; recherche</p><ul><li><p>Elasticsearch 9.1.1+ - Indexation et recherche d'articles</p></li></ul><p>Déploiement de Docker (facultatif, mais recommandé)</p><ul><li><p>Docker 28.5.1+</p></li></ul><h2>Section 1 : Qu'est-ce que l'Agent2Agent (A2A) ?</h2><h3>Définition et concepts de base</h3><p>Agent2Agent (A2A) est un protocole standardisé pour l'interaction entre des agents LLM indépendants. Plutôt que de confier toutes les tâches à un seul système monolithique, l'A2A permet à plusieurs agents spécialisés de communiquer, de coordonner et de collaborer afin d'accomplir des flux de travail complexes qui seraient difficiles, lents ou carrément impossibles à gérer efficacement par un seul agent.</p><p><strong>Spécification officielle</strong> <a href="https://a2a-protocol.org/latest/specification/">: https://a2a-protocol.org/latest/specification/</a></p><h3>Origines et évolution</h3><p>Le concept de communication Agent2Agent, ou de systèmes multi-agents, trouve ses racines dans les systèmes distribués, les microservices et la recherche multi-agents qui remonte à <a href="https://en.wikipedia.org/wiki/Multi-agent_system">plusieurs dizaines d'années</a>. Les premiers travaux sur l'intelligence artificielle distribuée ont jeté les bases d'agents capables de négocier, de coordonner et de collaborer. Ces premiers systèmes étaient destinés à des <a href="https://www.jasss.org/5/1/7.html">simulations sociales</a> à grande échelle, à la <a href="https://arxiv.org/html/2410.09403v1">recherche universitaire</a> et à la <a href="https://www.researchgate.net/publication/334765661_Generation_Expansion_Planning_Considering_Investment_Dynamic_of_Market_Participants_Using_Multi-agent_System">gestion des réseaux électriques</a>.</p><p>Avec l'arrivée des LLM et la réduction des coûts d'exploitation, les systèmes multi-agents sont devenus accessibles aux marchés "grand public", avec le soutien de Google et de l'ensemble de la communauté des chercheurs en intelligence artificielle. Désormais connu sous le nom de systèmes Agent2Agent, l'ajout du protocole A2A a évolué pour devenir une norme moderne conçue spécifiquement pour l'ère des modèles linguistiques multiples et de grande envergure coordonnant les efforts et les tâches.</p><p>Le protocole A2A garantit une communication et une coordination transparentes entre les agents en appliquant des normes et des principes cohérents aux points d'interaction où les MFR se connectent et communiquent. Cette normalisation permet aux agents de différents développeurs - utilisant différents modèles sous-jacents - de travailler ensemble de manière efficace.</p><p>Les protocoles de communication ne sont pas nouveaux et sont largement ancrés dans presque toutes les transactions numériques effectuées sur l'internet. Si vous avez tapé <a href="https://www.elastic.co/search-labs">https://www.elastic.co/search-labs</a> dans un navigateur pour accéder à cet article, il y a de fortes chances que les protocoles TCP/IP, de transport HTTP et de recherche DNS aient tous été exécutés, ce qui nous garantit une expérience de navigation cohérente.</p><h3>Caractéristiques principales</h3><p>Les systèmes A2A reposent sur plusieurs principes fondamentaux qui garantissent une communication fluide. Le fait de s'appuyer sur ces principes garantit que différents agents, basés sur des LLM, des cadres et des langages de programmation différents, interagissent tous de manière transparente.</p><p>Voici les quatre grands principes :</p><ul><li><p><strong>Transmission de messages</strong>: Les agents communiquent par le biais de messages structurés dont les propriétés et les formats sont bien définis.</p></li><li><p><strong>Coordination</strong>: Les agents orchestrent des flux de travail complexes en se déléguant des tâches et en gérant les dépendances sans bloquer les autres agents.</p></li><li><p><strong>Spécialisation</strong>: Chaque agent se concentre sur un domaine ou une capacité spécifique, devenant ainsi un expert dans son domaine et offrant la possibilité d'accomplir des tâches basées sur cet ensemble de compétences.</p></li><li><p><strong>État distribué</strong>: L'état et les connaissances sont répartis entre les agents plutôt que centralisés, les agents ayant la possibilité de s'informer mutuellement de l'état d'avancement des tâches et des retours partiels (artefacts).</p></li></ul><h3>La salle de presse : Un exemple concret</h3><p>Imaginez une salle de rédaction numérique alimentée par des agents d'IA, chacun spécialisé dans un aspect différent du journalisme :</p><ul><li><p><strong>Chef de l'information</strong> (coordinateur/client) : Assigne les sujets et supervise le flux de travail</p></li><li><p><strong>Agent Reporter</strong>: Rédige des articles sur la base de recherches et d'interviews</p></li><li><p><strong>Agent de recherche</strong>: Recueille des faits, des statistiques et des informations de base</p></li><li><p><strong>Agent d'archivage</strong>: Recherche d'articles historiques et identification de tendances à l'aide d'Elasticsearch</p></li><li><p><strong>Agent rédacteur</strong>: Vérifie la qualité, le style et l'optimisation du référencement des articles.</p></li><li><p><strong>Agent de publication</strong>: Publie les articles approuvés sur la plateforme de blogs via CI/CD</p></li></ul><p>Ces agents ne travaillent pas isolément ; lorsque le chef de l'information confie un article sur l'<em>adoption des énergies renouvelables</em>, le journaliste a besoin du chercheur pour rassembler des statistiques, du rédacteur en chef pour réviser le projet et de l'éditeur pour publier l'article final. Cette coordination s'effectue par le biais de protocoles A2A.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb6c7215a96326481/6a17f2dd445de953024d0243/cc0760dbd74c49b92fa00dafbb8c2e8740eb70b6-963x693.png" alt="" /><h2>Section 2 : comprendre l'architecture A2A</h2><h3>Rôles de l'agent client et de l'agent distant</h3><p>Dans l'architecture A2A, les agents jouent deux rôles principaux. L'<strong>agent client</strong> est chargé de formuler et de communiquer des tâches aux autres agents du système. Il identifie les agents distants et leurs capacités, et utilise ces informations pour prendre des décisions éclairées en matière de délégation de tâches. L'agent client coordonne le flux de travail global, en veillant à ce que les tâches soient correctement réparties et à ce que le système progresse vers ses objectifs.</p><p>L'<strong>agent à distance</strong>, quant à lui, s'occupe des tâches déléguées par les clients. Il fournit des informations ou entreprend des actions spécifiques en réponse à des demandes, mais n'entreprend pas d'actions de manière indépendante. Les agents à distance peuvent également communiquer avec d'autres agents à distance si nécessaire pour s'acquitter des responsabilités qui leur sont confiées, créant ainsi un réseau collaboratif de capacités spécialisées.</p><p>Dans notre salle de presse, le chef de l'information joue le rôle d'agent client, tandis que le journaliste, le chercheur, le rédacteur en chef et l'éditeur sont des agents distants qui répondent aux demandes et se coordonnent les uns avec les autres.</p><h3>Capacités essentielles de l'A2A</h3><p>Les protocoles A2A définissent plusieurs capacités permettant une collaboration multi-agents :</p><h4>1. La découverte</h4><p>Les serveurs A2A doivent annoncer leurs capacités afin que les clients sachent quand et comment les utiliser pour des tâches spécifiques. Pour ce faire, les cartes d'agent sont des documents JSON qui décrivent les capacités, les entrées et les sorties d'un agent. Les cartes d'agent sont disponibles à des points d'extrémité cohérents et bien connus (tels que le point d'extrémité recommandé <code>/.well-known/agent-card.json</code> ), ce qui permet aux clients de découvrir et d'interroger les capacités d'un agent avant d'entamer une collaboration.</p><p>Voici un exemple de carte d'agent pour l'agent d'archivage personnalisé d'Elastic "Archie Archivist". Notez que les fournisseurs de logiciels tels qu'Elastic hébergent leurs agents A2A et fournissent une adresse URL pour l'accès :</p>{
  "name": "Archie Archivist",
  "description": "Helps find historical news documents in the Elasticsearch Index of archived news articles and content.",
  "url": "https://xxxxxxxxxxxxx-abc123.kb.us-central1.gcp.elastic.cloud/api/agent_builder/a2a/archive-agent",
  "provider": {
    "organization": "Elastic",
    "url": "https://elastic.co"
  },
  "version": "0.1.0",
  "protocolVersion": "0.3.0",
  "preferred_transport": "JSONRPC",
  "documentationURL": "https://www.elastic.co/docs/solutions/search/agent-builder/a2a-server"
  "capabilities": {
    "streaming": false,
    "pushNotifications": false,
    "stateTransitionHistory": false
  },
  "skills": [
    {
      "id": "platform.core.search",
      "name": "platform.core.search",
      "description": "A powerful tool for searching and analyzing data within your Elasticsearch cluster.",
      "inputModes": ["text/plain", "application/json"],
      "outputModes": ["text/plain", "application/json"]
    },
    {
      "id": "platform.core.index_explorer",
      "name": "platform.core.index_explorer",
      "description": "List relevant indices, aliases and datastreams based on a natural language query.",
      "inputModes": ["text/plain", "application/json"],
      "outputModes": ["text/plain", "application/json"]
    }
  ],
  "defaultInputModes": ["text/plain"],
  "defaultOutputModes": ["text/plain"]
}<p>Cette carte d'agent révèle plusieurs aspects importants de l'agent d'archivage d'Elastic. L'agent s'identifie comme "Archie Archivist" et indique clairement son objectif : aider à trouver des documents d'actualités historiques dans un index Elasticsearch. La carte précise le fournisseur (Elastic) et la version du protocole (0.3.0), ce qui garantit la compatibilité avec d'autres agents conformes à la norme A2A. Plus important encore, le tableau <code>skills</code> énumère les capacités spécifiques offertes par cet agent, notamment une puissante fonctionnalité de recherche et une exploration intelligente de l'index. Chaque compétence définit les modes d'entrée et de sortie qu'elle prend en charge, ce qui permet aux clients de savoir exactement comment communiquer avec cet agent. Cet agent est dérivé du service Agent Builder d'Elastic, qui fournit une suite d'outils et de points d'extrémité d'API natifs soutenus par LLM pour avoir une conversation avec votre magasin de données, et pas seulement pour en extraire des données. L'accès aux agents A2A dans Elasticsearch peut être trouvé <a href="https://www.elastic.co/docs/solutions/search/agent-builder/a2a-server">ici.</a></p><h4>2. Négociation</h4><p>Les clients et les agents doivent se mettre d'accord sur les méthodes de communication - que les interactions se fassent par le biais de textes, de formulaires, d'iframes ou même d'audio/vidéo - afin de garantir une interaction correcte entre les utilisateurs et l'échange de données. Cette négociation a lieu au début de la collaboration des agents et établit les protocoles qui régiront leur interaction tout au long du flux de travail. Par exemple, un agent du service clientèle basé sur la voix peut négocier pour communiquer via des flux audio, tandis qu'un agent chargé de l'analyse des données peut préférer JSON structuré. Le processus de négociation permet aux deux parties d'échanger efficacement des informations dans un format adapté à leurs capacités et aux exigences de la tâche à accomplir.</p><p>Les capacités énumérées dans l'extrait JSON ci-dessus ont toutes des schémas d'entrée et de sortie ; ces schémas définissent la manière dont les autres agents doivent interagir avec cet agent.</p><h4>3. Gestion des tâches et des états</h4><p>Les clients et les agents ont besoin de mécanismes pour communiquer l'état des tâches, les changements et les dépendances tout au long de l'exécution des tâches. Il s'agit notamment de gérer l'ensemble du cycle de vie d'une tâche, depuis sa création et son affectation jusqu'aux mises à jour et aux changements d'état. Les statuts typiques sont les suivants : en attente, en cours, terminé ou en échec. Le système doit également suivre les dépendances entre les tâches afin de s'assurer que les travaux préalables sont achevés avant que les tâches dépendantes ne commencent. La gestion des erreurs et la logique de réessai sont également des éléments essentiels, qui permettent au système de se remettre gracieusement des défaillances et de continuer à progresser vers l'objectif principal.</p><p>Exemple de message de tâche :</p>{
  "message_id": "msg_789xyz",
  "message_type": "task_request",
  "sender": "news_chief",
  "receiver": "researcher_agent",
  "timestamp": "2025-09-30T10:15:00Z",
  "payload": {
    "task_id": "task_456abc",
    "capability": "fact_gathering",
    "parameters": {
      "query": "renewable energy adoption rates in Europe 2024",
      "sources": ["eurostat", "iea", "ember"],
      "depth": "comprehensive"
    },
    "context": {
      "story_id": "story_123",
      "deadline": "2025-09-30T18:00:00Z",
      "priority": "high"
    }
  }
}<p>Cet exemple de message de tâche démontre plusieurs aspects clés de la communication A2A.</p><ul><li><p>La structure du <strong>message</strong> comprend des métadonnées telles qu'un identifiant de message unique, le type de message envoyé, l'identification de l'expéditeur et du destinataire, et un horodatage pour le suivi et le débogage.</p></li><li><p>La <strong>charge utile</strong> contient les informations relatives à la tâche proprement dite, spécifiant la capacité invoquée sur l'agent distant et fournissant les paramètres nécessaires à l'exécution de cette capacité.</p></li><li><p>La section <strong>contexte</strong> fournit des informations supplémentaires qui aident l'agent récepteur à comprendre le flux de travail général, y compris les délais et les niveaux de priorité qui indiquent comment l'agent doit allouer ses ressources et planifier son travail.</p></li></ul><h4>4. La collaboration</h4><p>Les clients et les agents <strong>doivent</strong> permettre une interaction dynamique mais structurée, permettant aux agents de demander des clarifications, des informations ou des sous-actions au client, à d'autres agents ou à des utilisateurs. Cela crée un environnement de collaboration dans lequel les agents peuvent poser des questions complémentaires lorsque les instructions initiales sont ambiguës, demander un contexte supplémentaire pour prendre de meilleures décisions, déléguer des sous-tâches à d'autres agents ayant une expertise plus appropriée et fournir des résultats intermédiaires pour obtenir un retour d'information avant de procéder à l'ensemble de la tâche. Cette communication multidirectionnelle garantit que les agents ne travaillent pas de manière isolée, mais qu'ils sont au contraire engagés dans un dialogue permanent qui aboutit à de meilleurs résultats.</p><h3>Communication distribuée, d'égal à égal</h3><p>L'A2A permet une communication distribuée où les agents peuvent être hébergés par différentes organisations, certains agents étant maintenus en interne tandis que d'autres sont fournis par des services tiers. Ces agents peuvent fonctionner sur différentes infrastructures - couvrant potentiellement plusieurs fournisseurs de services en nuage ou des centres de données sur site. Ils peuvent utiliser différents LLM sous-jacents, certains agents étant alimentés par des modèles GPT, d'autres par Claude, et d'autres encore par des alternatives à code source ouvert. Les agents peuvent même opérer dans différentes régions géographiques pour se conformer aux exigences en matière de souveraineté des données ou pour réduire les temps de latence. Malgré cette diversité, tous les agents conviennent d'un protocole de communication commun pour l'échange d'informations, ce qui garantit l'interopérabilité indépendamment des détails de la mise en œuvre. Cette architecture distribuée offre une certaine souplesse dans la manière dont les systèmes sont construits et déployés, ce qui permet aux organisations de combiner les agents et les infrastructures les mieux adaptés à leurs besoins spécifiques.</p><p>Il s'agit de l'architecture finale de l'application de la salle de presse :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt74d59cd9267f54d8/6a17f2de505ac31129ad8c71/82e01a0d9746038eafd69d11177042b5390507ae-1600x838.png" alt="" /><h2>Section 3 : Protocole de contexte de modèle (PCM)</h2><h3>Définition et objectif</h3><p>Le Model Context Protocol (MCP) est un protocole standardisé développé par Anthropic pour améliorer et renforcer un LLM individuel avec des outils, des ressources et des invites définis par l'utilisateur, ainsi que d'autres ajouts supplémentaires à la base de code. MCP fournit une interface universelle entre les modèles linguistiques et les ressources externes dont ils ont besoin pour accomplir efficacement leurs tâches. Cet <a href="https://www.elastic.co/search-labs/blog/mcp-current-state">article</a> présente l'état actuel du MCP avec des exemples de cas d'utilisation, les tendances émergentes et la propre mise en œuvre d'Elastic.</p><h3>Concepts de base du MCP</h3><p>MCP fonctionne selon une architecture client-serveur avec trois composants principaux :</p><ul><li><p><strong>Clients :</strong> applications (comme Claude Desktop ou des applications IA personnalisées) qui se connectent aux serveurs MCP pour accéder à leurs capacités.</p></li><li><p><strong>Serveurs</strong>: applications qui exposent les ressources, les outils et les messages-guides aux modèles linguistiques. Chaque serveur est spécialisé dans l'accès à des capacités ou à des sources de données spécifiques.</p><ul><li><p><strong>Outils</strong>: fonctions définies par l'utilisateur que les modèles peuvent invoquer pour effectuer des actions, telles que la recherche dans des bases de données, l'appel à des API externes ou l'exécution de transformations sur les données.</p></li><li><p><strong>Ressources :</strong> sources de données que les modèles peuvent lire, servies avec des données dynamiques ou statiques, et accessibles via des modèles d'URI (similaires aux routes REST).</p></li><li><p><strong>Invitations : </strong>modèles d'invitations réutilisables avec des variables qui guident le modèle dans l'accomplissement de tâches spécifiques.</p></li></ul></li></ul><h3>Modèle demande-réponse</h3><p>MCP suit un modèle d'interaction demande-réponse familier, similaire aux API REST. Le client (LLM) demande une ressource ou invoque un outil, puis le serveur MCP traite la demande et renvoie le résultat, que le LLM utilise pour poursuivre sa tâche. Ce modèle centralisé avec des serveurs périphériques offre un modèle d'intégration plus simple que la communication d'agent pair à pair.</p><h3>MCP dans la salle de presse</h3><p>Dans notre exemple de salle de presse, les agents individuels utilisent des serveurs MCP pour accéder aux outils et aux données dont ils ont besoin :</p><ul><li><p><strong>Le chercheur</strong> utilise l'agent:</p><ul><li><p>Serveur MCP News API (accès aux bases de données d'actualités)</p></li><li><p>Fact-Checking MCP Server (vérification des affirmations par rapport à des sources fiables)</p></li><li><p>Base de données académique MCP Server (articles et recherches universitaires)</p></li></ul></li><li><p>Utilisation de l'<strong>agent rapporteur</strong>:</p><ul><li><p>Guide de style MCP Server (normes de rédaction des salles de presse)</p></li><li><p>Serveur de modèles MCP (modèles et formats d'articles)</p></li><li><p>Bibliothèque d'images MCP Server (photos d'archives et graphiques)</p></li></ul></li><li><p>L<strong>'éditeur</strong> utilise l'agent:</p><ul><li><p>Grammar Checker MCP Server (outils de qualité linguistique)</p></li><li><p>Serveur MCP de détection du plagiat (vérification de l'originalité)</p></li><li><p>Analyse SEO MCP Server (optimisation des titres et des mots-clés)</p></li></ul></li><li><p>L'<strong>agent éditeur</strong> utilise :</p><ul><li><p>Serveur CMS MCP (système de gestion de contenu API)</p></li><li><p>Serveur CI/CD MCP (pipeline de déploiement)</p></li><li><p>Serveur Analytics MCP (suivi et contrôle)</p></li></ul></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt195fe0bd36d36a48/6a17f2e0b1e113afe479f36c/b67311e3b58b27f9eb1b42a7b1dbad47ef3be4ad-808x535.png" alt="" /><h2>
Section 4 : comparaison des architectures</h2><h3>Quand utiliser A2A</h3><p>L'architecture A2A excelle dans les <strong>scénarios nécessitant une véritable collaboration multi-agents</strong>. Les flux de travail à plusieurs étapes nécessitant une coordination bénéficient grandement de l'A2A, en particulier lorsque les tâches impliquent plusieurs étapes séquentielles ou parallèles, les flux de travail nécessitant une itération et un affinement, et les processus avec des points de contrôle et des besoins de validation. Dans notre exemple de salle de presse, le flux de travail de l'article exige que le journaliste écrive, mais il peut être nécessaire de revenir au chercheur si la confiance en certains faits est faible, puis de passer au rédacteur en chef et enfin à l'éditeur.</p><p>La <strong>spécialisation spécifique à un domaine</strong> est un autre cas d'utilisation important de l'A2A. Lorsque plusieurs experts dans différents domaines sont nécessaires pour accomplir une tâche plus importante, chaque agent apportant une connaissance approfondie du domaine et des capacités de raisonnement spécialisées pour différents aspects, A2A fournit le cadre de coordination nécessaire pour établir ces connexions. La salle de rédaction en est un parfait exemple : le chercheur se spécialise dans la collecte d'informations, le journaliste dans la rédaction et le rédacteur en chef dans le contrôle de la qualité, chacun ayant une expertise distincte.</p><p>La nécessité d'un comportement autonome des agents rend l'A2A particulièrement utile. Les agents capables de<strong> prendre des décisions indépendantes, d'adopter un comportement proactif en fonction de l'évolution des conditions et de s'adapter de manière dynamique aux exigences du flux de travail</strong> s'épanouissent dans une architecture A2A. L'échelonnement horizontal des fonctions spécialisées est un autre avantage clé : plutôt que d'avoir un seul maître à tout faire, plusieurs agents spécialisés travaillent en coordination, et plusieurs instances du même agent peuvent gérer des tâches secondaires de manière asynchrone. Dans notre salle de presse, par exemple, lors d'une nouvelle de dernière minute, plusieurs agents de Reporter peuvent travailler simultanément sur différents aspects d'un même sujet.</p><p>Enfin, les tâches nécessitant une véritable collaboration multi-agents sont idéales pour l'A2A. Cela inclut les mécanismes d'<a href="https://arxiv.org/abs/2404.18796">évaluation du LLM en tant que jury</a>, les systèmes de consensus et de vote, et la <strong>résolution collaborative de problèmes où de multiples perspectives sont nécessaires</strong> pour atteindre le meilleur résultat.</p><h3>Quand utiliser MCP</h3><p>Le protocole de contexte de modèle est idéal pour étendre les capacités d'un modèle d'IA unique. Lorsqu'un modèle d'IA unique doit accéder à plusieurs outils et sources de données, MCP fournit la solution parfaite avec un raisonnement centralisé associé à des outils distribués et à une intégration simple des outils. Dans notre exemple de salle de presse, l'agent chercheur (un modèle) doit avoir accès à plusieurs sources de données, notamment l'API des actualités, les services de vérification des faits et les bases de données universitaires, toutes accessibles par l'intermédiaire de serveurs MCP normalisés.</p><p>L'intégration d'outils normalisés devient une priorité lorsque le partage et la réutilisation des intégrations d'outils sont importants. MCP se distingue ici par son écosystème de serveurs MCP préconstruits qui réduisent considérablement le temps de développement pour les intégrations courantes. Lorsque la simplicité et la facilité de maintenance sont requises, les modèles demande-réponse de MCP sont familiers aux développeurs, plus faciles à comprendre et à déboguer que les systèmes distribués, et leur complexité opérationnelle est moindre.</p><p>Enfin, le MCP est souvent proposé par les fournisseurs de logiciels pour faciliter la communication à distance avec leurs systèmes. Ces serveurs MCP proposés par les fournisseurs réduisent considérablement le temps d'intégration et de développement tout en offrant une interface standardisée avec les systèmes propriétaires, ce qui rend l'intégration beaucoup plus simple que le développement d'API personnalisées.</p><h3>Quand utiliser les deux (A2A ❤️'s MCP)</h3><p>De nombreux systèmes sophistiqués bénéficient de la combinaison d'A2A et de MCP, comme l'indique la <a href="https://a2a-protocol.org/latest/topics/a2a-and-mcp/">documentation d'A2A sur l'intégration de MCP</a>. Les systèmes nécessitant à la fois une coordination et une normalisation sont des candidats idéaux pour une approche hybride. A2A s'occupe de la coordination des agents et de l'orchestration du flux de travail, tandis que MCP permet aux agents individuels d'accéder aux outils. Dans notre exemple de salle de presse, les agents se coordonnent via A2A, le flux de travail passant du journaliste au chercheur, puis au rédacteur en chef et à l'éditeur. Cependant, chaque agent utilise des serveurs MCP pour ses outils spécialisés, ce qui crée une séparation architecturale nette.</p><p>Plusieurs agents spécialisés, chacun utilisant MCP pour l'accès aux outils, représentent un modèle commun où il y a une couche de coordination des agents gérée par A2A et une couche d'accès aux outils gérée par MCP. Cette séparation claire des préoccupations rend les systèmes plus faciles à comprendre et à entretenir.</p><p>Les avantages de la combinaison de ces deux approches sont considérables. Vous bénéficiez des avantages organisationnels des systèmes multi-agents, notamment la spécialisation, l'autonomie et le traitement parallèle, tout en profitant de la normalisation et des avantages de l'écosystème du MCP, tels que l'intégration des outils et l'accès aux ressources. Il existe une séparation claire entre la coordination des agents (A2A) et l'accès aux ressources (MCP) et, surtout, l'A2A n'est pas nécessaire pour les petites tâches telles que l'accès à l'API uniquement - MCP les gère efficacement sans les frais généraux de l'orchestration multi-agents.</p><p><strong>FAQ : A2A vs. MCP - Cas d'utilisation</strong></p><p>Fonctionnalité</p><p>Agent2Agent (A2A)</p><p>Protocole de contexte de modèle (MCP)</p><p>Hybride (A2A + MCP)</p><p>Objectif principal</p><p>Coordination multi-agents : Permet à une équipe d'agents spécialisés de travailler ensemble sur des flux de travail complexes à plusieurs étapes.</p><p>Amélioration de l'agent unique : Extension des capacités d'un seul LLM/Agent à l'aide d'outils, de ressources et de données externes.</p><p>Une force combinée : A2A gère le flux de travail de l'équipe, tandis que MCP fournit des outils à chaque membre de l'équipe.</p><p>Exemple d'équipe de salle de presse</p><p>La chaîne de travail : Chef de l'information → Reporter → Chercheur → Rédacteur en chef → Éditeur. Il s'agit de la couche de coordination.</p><p>Outils individuels de l'agent : L'agent rapporteur accède au serveur de guides de style et au serveur de modèles (via MCP). Il s'agit de la couche d'accès aux outils.</p><p>Le système complet : Le journaliste se coordonne avec le rédacteur en chef (A2A) et le journaliste utilise le serveur MCP de la bibliothèque d'images pour trouver un graphique pour l'article.</p><p>Quand utiliser quoi ?</p><p>Lorsque vous avez besoin d'une véritable collaboration, d'une itération et d'un perfectionnement, ou d'une expertise spécialisée répartie entre plusieurs agents.</p><p>Lorsqu'un agent unique a besoin d'accéder à plusieurs outils et sources de données ou nécessite une intégration standardisée avec des systèmes propriétaires.</p><p>Lorsque vous avez besoin des avantages organisationnels des systèmes multi-agents et des avantages de normalisation et d'écosystème du MCP.</p><p>Prestations de base</p><p>Autonomie et mise à l'échelle : Les agents peuvent prendre des décisions indépendantes et le système permet une mise à l'échelle horizontale des fonctions spécialisées.</p><p>Simplicité et normalisation : Le raisonnement centralisé facilite le débogage et la maintenance et fournit une interface universelle pour les ressources.</p><p>Séparation claire des préoccupations : Facilite la compréhension du système : A2A = travail d'équipe, MCP = accès aux outils.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1735ea5de41e10fd/6a17f2e26864a4125cb688c4/ddf6a29b1107ac6a63e94ecef703abc561a29e1e-986x656.png" alt="" /><h2>Conclusion</h2><p>Il s'agit de la première section de deux articles couvrant la mise en œuvre d'agents basés sur A2A et renforcés par des serveurs MCP pour fournir un support et un accès externe aux données et aux outils. La prochaine partie explorera le code réel pour démontrer qu'ils travaillent ensemble afin d'émuler les activités d'une salle de rédaction en ligne. Bien que les deux cadres soient extrêmement compétents et flexibles, vous verrez à quel point ils se complètent lorsqu'ils travaillent en tandem.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/a2a-protocol-mcp-llm-agent-newsroom-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/a2a-protocol-mcp-llm-agent-newsroom-elasticsearch</guid>
    <category><![CDATA[IA agentique]]></category>
    <category><![CDATA[IA]]></category>
    <dc:creator><![CDATA[Justin Castilla]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2716d804698ec878/6a17f2e41480095fd7b48888/9f938d8e2f0fdf7509edf028816c48bdbc8b3fc7-1600x900.png" length="0" type="image/png"/>
    <pubDate>Thu, 13 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Configurer le découpage récursif pour les documents structurés dans Elasticsearch]]></title>
    <description><![CDATA[Apprenez à configurer le découpage récursif dans Elasticsearch avec la taille des morceaux, les groupes de séparateurs et les listes de séparateurs personnalisées pour une indexation optimale des documents structurés.]]></description>
    <content:encoded><![CDATA[<p>Depuis la version 8.16, les utilisateurs peuvent configurer la stratégie de découpage utilisée lors de l'ingestion de longs documents dans des champs de texte sémantique. Depuis la version 9.1 / 8.19, nous avons introduit une nouvelle stratégie de découpage récursif configurable qui utilise une liste d'expressions régulières pour découper le document. L'objectif du découpage en morceaux est de diviser un long document en sections qui encapsulent un contenu apparenté. Nos stratégies existantes permettent de diviser le texte selon une granularité de mots/phrases, mais les documents écrits dans des formats structurés (ex. Markdown) contiennent souvent des contenus connexes dans des sections définies par des chaînes de séparation (ex. ). Pour ces types de documents, nous introduisons la stratégie de découpage récursif afin d'exploiter le format des documents structurés pour créer de meilleurs morceaux !</p><h2>Qu'est-ce que le découpage récursif ?</h2><p>Le découpage récursif parcourt une liste de sections fournies en séparant les modèles afin de diviser progressivement un document en segments plus petits jusqu'à ce qu'ils atteignent une taille maximale souhaitée.</p><h3>Comment configurer le découpage récursif ?</h3><p>Les valeurs configurables fournies par l'utilisateur pour le découpage récursif sont les suivantes :</p><ul><li><p>(obligatoire) <code>max_chunk_size</code>: Le nombre maximum de mots dans un bloc.</p></li><li><p>L'un ou l'autre :</p><ul><li><p><code>separators</code>: Une liste de motifs de chaînes regex qui seront utilisés pour découper le document en morceaux.</p></li><li><p><code>separator_group</code>: Une chaîne qui correspondra à une liste par défaut de séparateurs définis par Elastic à utiliser pour des types de documents spécifiques. Actuellement, <code>markdown</code> et <code>plaintext</code> sont disponibles.</p></li></ul></li></ul><h3>Comment fonctionne le découpage récursif ?</h3><p>Le processus de découpage récursif d'un document d'entrée, d'un <code>max_chunk_size</code> (mesuré en mots) et d'une liste de chaînes de séparation est le suivant :</p><ol><li><p>Si le document d'entrée est déjà compris dans la taille maximale des morceaux, il renvoie un seul morceau couvrant l'ensemble du document d'entrée.</p></li><li><p>Découper le texte en morceaux potentiels sur la base des occurrences du séparateur. Pour chaque morceau potentiel :</p><ol><li><p>Si le morceau potentiel ne dépasse pas la taille maximale, il est ajouté à la liste des morceaux à renvoyer à l'utilisateur.</p></li><li><p>Sinon, répétez l'étape 2, en utilisant uniquement le texte du morceau potentiel et en le séparant à l'aide du séparateur suivant dans la liste. S'il n'y a plus de séparateurs à essayer, il faut se rabattre sur le découpage en phrases.</p></li></ol></li></ol><h2>Exemples de configuration du découpage récursif</h2><p>Outre la taille des morceaux, la principale configuration du découpage récursif consiste à sélectionner les séparateurs à utiliser pour diviser vos documents. Si vous ne savez pas par où commencer, Elasticsearch propose quelques groupes de séparateurs par défaut qui peuvent être utilisés pour des cas d'utilisation courants.</p><h3>Utilisation de groupes de séparation</h3><p>Pour utiliser un groupe séparateur, il suffit d'indiquer le nom du groupe que vous souhaitez utiliser lors de la configuration des paramètres de regroupement. Par exemple :</p>"chunking_settings": {
    "strategy": "recursive",
    "max_chunk_size": 25,
    "separator_group": "plaintext"
}<p>Vous obtiendrez ainsi une stratégie de découpage récursif qui utilise la liste de séparateurs <code>["(?&lt;!\\n)\\n\\n(?!\\n)", "(?&lt;!\\n)\\n(?!\\n)")]</code>. Cela fonctionne bien pour les applications génériques de texte brut, en séparant deux caractères de retour à la ligne, suivis d'un caractère de retour à la ligne.</p><p>Nous proposons également un groupe de séparateurs <code>markdown</code> qui utilisera la liste des séparateurs :</p>[
"\n# ",
       "\n## ",
       "\n### ",
       "\n#### ",
       "\n##### ",
       "\n###### ",
       "\n^(?!\\s*$).*\\n-{1,}\\n",
       "\n^(?!\\s*$).*\\n={1,}\\n"
]<p>Cette liste de séparateurs fonctionnera bien pour les cas d'utilisation généraux de markdown, en séparant chacun des 6 niveaux d'en-tête et les caractères de coupure de section.</p><p>Lors de la création d'une ressource (point d'inférence/champ textuel sémantique), la liste des séparateurs correspondant au groupe de séparateurs du moment sera stockée dans vos configurations. Si le groupe de séparateurs est mis à jour ultérieurement, cela ne modifiera pas le comportement des ressources déjà créées.</p><h3>Utilisation d'une liste de séparateurs personnalisée</h3><p>Si l'un des groupes de séparateurs prédéfinis ne convient pas à votre cas d'utilisation, vous pouvez définir une liste personnalisée de séparateurs répondant à vos besoins. Notez que des expressions régulières peuvent être fournies dans la liste des séparateurs. Voici un exemple de paramètres de regroupement configurés avec des séparateurs personnalisés :</p>"chunking_settings": {
    "strategy": "recursive",
    "max_chunk_size": 25,
    "separators": ["\n\n", "\n", "&lt;my-custom-separator&gt;"]
}<p>La stratégie de découpage en morceaux découpera 2 caractères de nouvelle ligne, suivis d'un caractère de nouvelle ligne, et enfin une chaîne de caractères <code>“&lt;my-custom-separator&gt;”</code>.</p><h2>Un exemple de découpage récursif en action</h2><p>Voyons un exemple de découpage récursif en action. Pour cet exemple, nous utiliserons les paramètres de découpage suivants avec une liste personnalisée de séparateurs qui découpent un document markdown en utilisant les deux premiers niveaux d'en-tête :</p>"chunking_settings": {
    "strategy": "recursive",
    "max_chunk_size": 25,
    "separators": ["\n# ", "\n## "]
}<p>Examinons un simple document Markdown non tronqué :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdb5f41d1bd43ba50/6a17e831e9ea87c1d8a9c5f3/3a5507f4a1288065097231548e5b18e240508785-1302x1446.png" alt="Un document Markdown non tronqué" /><p>Utilisons maintenant les paramètres de découpage définis ci-dessus pour découper le document en morceaux :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfffda162c7b9c87a/6a17e83296142aefa8eb1b0b/a3313c4c40ff39b8dbcdd7c4878c723f088e6c1a-1600x1187.png" alt="Chunking d'un document dans Elasticsearch" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt96f65346a8e09e3a/6a17e834445de9157b4d015e/79a2921943191ea631df94c9d465818ec8d3e738-1600x1206.png" alt="Splitting on second separator- chunking a document in Elasticsearch" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt28381c8f85aedf07/6a17e836ec0f89801e5a6640/459e695cce7540267422396b9a62ff4ad35f61db-1600x1260.png" alt="Derniers morceaux d'un document après un découpage basé sur les phrases dans Elasticsearch" /><p>Remarque : la nouvelle ligne à la fin de chaque morceau (à l'exception du morceau 3) n'est pas mise en évidence, mais elle est incluse dans les limites du morceau.</p><h3>Commencez dès aujourd'hui à utiliser le découpage récursif !</h3><p>Pour plus d'informations sur l'utilisation de cette fonctionnalité, consultez la documentation sur la configuration des paramètres de regroupement.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/recursive-chunking-structured-documents-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/recursive-chunking-structured-documents-elasticsearch</guid>
    <category><![CDATA[Les bases]]></category>
    <category><![CDATA[À l'intérieur d'Elastic]]></category>
    <category><![CDATA[IA]]></category>
    <dc:creator><![CDATA[Daniel Rubinstein]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf442dc4941f37be7/6a17e838505ac3eaf8ad8b3d/591872e31880768ca927507654a621addc0d124d-1600x960.png" length="0" type="image/png"/>
    <pubDate>Tue, 11 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Recherche multimodale de sommets avec Elasticsearch et SigLIP-2 ]]></title>
    <description><![CDATA[Apprenez à mettre en œuvre la recherche multimodale texte-image et image-image en utilisant les encastrements SigLIP-2 et la recherche vectorielle Elasticsearch kNN. Objectif du projet : trouver des photos du sommet du mont Ama Dablam prises lors d'un trekking dans l'Everest.]]></description>
    <content:encoded><![CDATA[<p>Avez-vous déjà voulu rechercher votre album photo par signification ? Essayez des requêtes telles que "montrez-moi mes photos où je porte une veste bleue et suis assis sur un banc", "montrez-moi des photos du mont Everest" ou "saké et sushi". Prenez une tasse de café (ou votre boisson préférée) et poursuivez votre lecture. Dans ce blog, nous vous montrons comment créer une application de recherche hybride multimodale. Multimodale signifie que l'application peut comprendre et rechercher différents types d'entrées - texte, images et audio - et pas seulement des mots. Hybride signifie qu'il combine des techniques telles que la correspondance de mots-clés, la recherche vectorielle kNN et la géolocalisation pour fournir des résultats plus précis.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdfa1ec1ccd450e94/6a17da751d1b8308ee93e344/0ec6bbb45013846b59ee00d2bf73ee2182ee7392-1920x1080.gif" alt="Bibliothèque de différentes photos de sommets de montagne de la randonnée du Mont Everest." /><p>Pour ce faire, nous utilisons le logiciel SigLIP-2 de Google pour générer des encastrements vectoriels pour les images et le texte, et les stocker dans la base de données vectorielles Elasticsearch. Au moment de la requête, nous convertissons l'entrée de la recherche, texte ou image, en encastrements et exécutons des recherches vectorielles kNN rapides pour extraire les résultats. Cette configuration permet une recherche efficace de texte à image et d'image à image. Une interface utilisateur Streamlit donne vie à ce projet en nous fournissant un frontend qui nous permet non seulement d'effectuer une recherche textuelle pour trouver et afficher les photos correspondantes de l'album, mais aussi d'identifier le sommet de la montagne à partir de l'image téléchargée et d'afficher d'autres photos de cette montagne dans l'album photo.
Nous présentons également les mesures que nous avons prises pour améliorer la précision des recherches, ainsi que des conseils et astuces pratiques. Pour une exploration plus approfondie, nous fournissons un <a href="https://github.com/navneet83/multimodal-mountain-peak-search">dépôt GitHub</a> et un <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/notebooks/multimodal_mountain_peak_search.ipynb">carnet de notes Colab</a>.</p><h2>Comment cela a commencé</h2><p>Ce billet a été inspiré par un enfant de 10 ans qui m'a demandé de lui montrer toutes les photos du mont Ama Dablam prises lors de mon trek au camp de base de l'Everest. En parcourant l'album photo, on m'a également demandé d'identifier plusieurs autres pics montagneux, dont certains que je n'arrivais pas à nommer.</p><p>Cela m'a donné l'idée d'un projet amusant de vision par ordinateur. Ce que nous voulions réaliser :</p><ul><li><p>trouver des images d'un sommet de montagne par son nom</p></li><li><p>deviner le nom du sommet d'une montagne à partir d'une image et trouver des sommets similaires dans l'album photo</p></li><li><p>faire fonctionner les requêtes conceptuelles<em>(personne</em>, <em>rivière</em>, <em>drapeaux de prière</em>, <em>etc.)</em></p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf82df9d7005fc3fe/6a17da78abe0f2e77bdfe8b9/e9d0d720a9b565d5b749bdc915068852d4f157ad-1200x1600.png" alt="Mont Ama Dablam " /><h2>Assembler l'équipe de rêve : SigLIP-2, Elasticsearch &amp; Streamlit</h2><p>Il est rapidement apparu que pour que cela fonctionne, nous devions transformer à la fois le texte ("Ama Dablam") et les images (photos de mon album) en vecteurs pouvant être comparés de manière significative, c'est-à-dire dans le même espace vectoriel. Une fois cette étape franchie, la recherche se résume à "trouver les voisins les plus proches".</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5f80b69a9d5bd28a/6a17da7a4b055ddd1243209e/20e6f8b7d4fa48414f407ec200adbe00ee28d517-1536x1024.png" alt="SigLIP-2, Elasticsearch &amp; Streamlit- dream team." /><p>Pour générer des enregistrements d'images, nous utilisons un<a href="https://huggingface.co/blog/vlms-2025"> encodeur multilingue</a> vision-langage, de sorte qu'une photo d'une montagne et une phrase comme "Ama Dablam" se retrouvent dans le même espace vectoriel.</p><p><a href="https://huggingface.co/blog/siglip2"><strong>SigLIP-2</strong></a>, récemment publié par Google, s'inscrit parfaitement dans ce cadre. Il peut générer des enchâssements sans formation spécifique à une tâche (un réglage <strong>zéro)</strong> et fonctionne bien pour notre cas d'utilisation : des photos non étiquetées et des pics avec des noms et des langues différents. Parce qu'il est formé à la correspondance texte ↔ image, une photo de montagne prise lors d'une randonnée et un court texte d'incitation se retrouvent proches en tant qu'ancrages, même lorsque la langue ou l'orthographe de la requête varient.</p><p>SigLIP-2 offre un excellent rapport qualité-vitesse, prend en charge plusieurs résolutions d'entrée et fonctionne à la fois avec le CPU et le GPU. SigLIP-2 est conçu pour être plus résistant aux photos prises en extérieur que les modèles précédents tels que le CLIP original. Lors de nos tests, SigLIP-2 a toujours produit des résultats fiables. Il est également très bien supporté, ce qui en fait un choix évident pour ce projet.</p><p>Ensuite, nous avons besoin d'une base de données vectorielle pour stocker les encastrements et effectuer des recherches puissantes. Il devrait permettre non seulement la recherche par cosinus kNN sur des images intégrées, mais aussi l'application de filtres de géographie et de texte en une seule requête. Elasticsearch convient bien ici : il gère très bien les vecteurs (HNSW kNN sur les champs dense_vector), prend en charge la recherche hybride qui combine le texte, les vecteurs et les requêtes géographiques, et propose d'emblée le filtrage et le tri. Il est également évolutif horizontalement, ce qui permet de passer facilement d'une poignée de photos à des milliers. Le client officiel <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/python">Elasticsearch Python</a> simplifie la plomberie et s'intègre parfaitement au projet. Enfin, nous avons besoin d'un frontal léger où nous pouvons saisir des requêtes de recherche et afficher les résultats. Pour une démonstration rapide, basée sur Python, Streamlit est une solution idéale. Il fournit les primitives dont nous avons besoin - le téléchargement de fichiers, une grille d'images réactive et des menus déroulants pour le tri et la géolocalisation. Il est facile de le cloner et de l'exécuter localement, et il fonctionne également dans un cahier Colab.</p><h2>Implémentation</h2><h3>Conception et stratégie d'indexation Elasticsearch</h3><p>Nous utiliserons deux indices pour ce projet : <code>peaks_catalog</code> et <code>photos</code>.</p><h4>Index du catalogue des pics</h4><p>Cet index constitue un catalogue compact des principaux sommets visibles pendant le trek du camp de base de l'Everest. Chaque document de cet index correspond à un sommet de montagne, comme le mont Everest. Pour chaque document relatif à un pic montagneux, nous stockons les noms/alias, les coordonnées facultatives de latitude et de longitude, ainsi qu'un vecteur prototype unique construit en mélangeant les messages-guides SigLIP-2 (+ images de référence facultatives).</p><p><strong>Mappage de l'index :</strong></p><p>Champ d'application</p><p>Type</p><p>Exemple</p><p>Objectif/Notes</p><p>Vecteur/Indexation</p><p>id</p><p>mot-clé</p><p>ama-dablam</p><p>Slug/id stable</p><p>-</p><p>noms</p><p>texte + sous-champ mot-clé</p><p>["Ama Dablam","Amadablam"]</p><p>Alias / noms multilingues ; names.raw pour les filtres exacts</p><p>-</p><p>latlon</p><p>geo_point</p><p>{"lat":27.8617,"lon":86.8614}</p><p>Coordonnées GPS du pic sous la forme d'une combinaison latitude/longitude (facultatif)</p><p>-</p><p>elev_m</p><p>entier</p><p>6812</p><p>Élévation (facultatif)</p><p>-</p><p>texte_embed</p><p>dense_vector</p><p>768</p><p>Prototype mixte (invites et éventuellement 1 à 3 images de référence) pour ce pic</p><p>index:true, similarité :"cosine", index_options :{type:"hnsw", m:16, ef_construction:128}</p><p>Cet index est principalement utilisé pour des recherches d'image à image, telles que l'identification de sommets de montagne à partir d'images. Nous utilisons également cet index pour améliorer les résultats de recherche texte-image.</p><p>En résumé, le site <code>peaks_catalog</code> transforme la question "Quelle est cette montagne ?" en un problème ciblé de plus proche voisin, séparant efficacement la compréhension conceptuelle des complexités des données d'image.</p><p><strong>Stratégie d'indexation pour l'index peaks_catalog : </strong>Nous commençons par créer une liste des sommets les plus importants visibles lors de la randonnée EBC. Pour chaque pic, nous stockons sa position géographique, son nom, ses synonymes et son altitude dans un <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/data/peaks.yaml">fichier yaml</a>. L'étape suivante consiste à <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L351">générer l'intégration</a> pour chaque pic et à la stocker dans le champ <code>text_embed</code>. Afin de générer des encastrements robustes, nous utilisons la technique suivante :</p><ul><li><p>Créer un prototype de texte en utilisant :</p><ul><li><p>noms des sommets</p></li><li><p>l'<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L301">ensemble des invites</a> (utilisation de plusieurs invites différentes pour tenter de répondre à la même question), par exemple :</p><ul><li><p>"photo naturelle du sommet de la montagne {name} dans l'Himalaya, Népal".</p></li><li><p>"{name} sommet emblématique de la région du Khumbu, paysage alpin"</p></li><li><p>"{name} sommet montagneux, neige, ligne de crête rocheuse"</p></li></ul></li><li><p><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L333">anti-concept</a> optionnel (indiquant à SigLIP-2 ce qu'il ne faut pas faire) : soustraire un petit vecteur pour "peinture, illustration, affiche, carte, logo" afin de privilégier les photos réelles.</p></li></ul></li><li><p><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L388C13-L388C29">Créer éventuellement un prototype d'image</a> si des images de référence du pic sont fournies.</p></li></ul><p>Nous <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L392">fusionnons ensuite les prototypes de texte et d'image</a> pour générer l'intégration finale. Enfin, le document est <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L396">indexé</a> avec tous les champs obligatoires :</p>def l2norm(v: np.ndarray) -&gt; np.ndarray:
    return v / (np.linalg.norm(v) + 1e-12)
def compute_blended_peak_vec(
        emb: Siglip2,
        names: List[str],
        peak_id: str,
        peaks_images_root: str,
        alpha_text: float = 0.5,
        max_images: int = 3,
) -&gt; Tuple[np.ndarray, int, int, List[str]]:
    """
    Build blended vector for a single peak.

    Returns:
      vec           : np.ndarray (L2-normalized)
      found_count   : number of reference images discovered
      used_count    : number of references used (&lt;= max_images)
      used_filenames: list of filenames used (for logging)
    """
    # 1) TEXT vector
    tv = embed_text_blend(emb, names)

    # 2) IMAGE refs: prefer folder by id; fallback to slug of the primary name
    root = Path(peaks_images_root)
    candidates = [root / peak_id]
    if names:
        candidates.append(root / slugify(names[0]))

    all_refs: List[Path] = []
    for c in candidates:
        if c.exists() and c.is_dir():
            all_refs = list_ref_images(c)
            if all_refs:
                break

    found = len(all_refs)
    used_list = all_refs[:max_images] if (max_images and found &gt; max_images) else all_refs
    used = len(used_list)

    img_v = embed_image_mean(emb, used_list) if used_list else None

    # 3) Blend TEXT and IMAGE vectors, clamp alpha to [0,1]
    a = max(0.0, min(1.0, float(alpha_text)))
    vec = l2norm(tv if img_v is None else (a * tv + (1.0 - a) * img_v)).astype("float32")
    return vec, found, used, [p.name for p in used_list]<p>Exemple de document provenant de l'index <code>peaks_catalog</code>:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1219f5d0e39b512c/6a17da7c57726263161bcace/bc05fbd0c4f8d721d5170c28a3884a9eda80bb7d-1210x1132.png" alt="Un exemple de document provenant de l'index peaks_catalog dans Elasticsearch." /><h4>Index des photos</h4><p>Cet index primaire contient des informations détaillées sur toutes les photos de l'album. Chaque document représente une seule photo, contenant les informations suivantes :</p><ul><li><p>Chemin d'accès relatif à la photo dans l'album photo. Cette option permet de visualiser l'image correspondante ou de charger l'image dans l'interface de recherche.</p></li><li><p>Informations sur le GPS et l'heure de la photo.</p></li><li><p>Vecteur dense pour le codage d'images généré par SigLIP-2.</p></li><li><p><code>predicted_peaks</code> qui nous permet de filtrer par nom de pic.

<strong>Cartographie de l'index</strong></p></li></ul><p>Champ d'application</p><p>Type</p><p>Exemple</p><p>Objectif/Notes</p><p>Vecteur / Indexation</p><p>chemin</p><p>mot-clé</p><p>data/images/IMG_1234.HEIC</p><p>Comment l'interface utilisateur ouvre la vignette/l'image complète</p><p>-</p><p>clip_image</p><p>dense_vector</p><p>768</p><p>Intégration d'images SigLIP-2</p><p>index:true, similarité :"cosine", index_options :{type:"hnsw", m:16, ef_construction:128}</p><p>pics_prédits</p><p>mot-clé</p><p>["ama-dablam","pumori"]</p><p>Les suppositions Top-K au moment de l'indexation (filtre / facette UX bon marché)</p><p>-</p><p>GPS</p><p>geo_point</p><p>{"lat":27.96,"lon":86.83}</p><p>permet d'utiliser des filtres géographiques</p><p>-</p><p>heure de la prise de vue</p><p>date</p><p>2023-10-18T09:41:00Z</p><p>temps de capture : tri/filtre</p><p>-</p><p><strong>Stratégie d'indexation pour l'index des photos : </strong>Pour chaque photo de l'album, nous procédons comme suit :
Extraire les informations sur les images <code>shot_time</code> et <code>gps</code> <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L526">à partir des métadonnées de l'image</a>.</p><ul><li><p><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L511">Intégration d'images SigLIP-2</a>: passage de l'image dans le modèle et normalisation L2 du vecteur. Stocker l'intégration dans le champ <code>clip_image</code>.</p></li><li><p><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L519">Prédire les pics</a> et les stocker dans le champ <code>predicted_peaks</code>. Pour ce faire, nous prenons d'abord le vecteur image de la photo généré à l'étape précédente, puis nous effectuons une recherche rapide par kNN sur le champ text_embed de l'index <code>peaks_catalog</code>. Nous conservons les 3-4 premiers sommets et ignorons les autres.</p></li><li><p>Nous calculons le champ <code>_id</code> en effectuant un <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L509">hachage</a> du nom de l'image et du chemin d'accès. Cela permet de s'assurer qu'il n'y a pas de doublons après plusieurs exécutions.</p></li></ul><p>Une fois que nous avons déterminé tous les champs de la photo, les documents photo sont indexés par lots à l'aide de l'indexation <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L530">en bloc :</a></p>def bulk_index_photos(
        es: Elasticsearch,
        images_root: str,
        photos_index: str = "photos",
        peaks_index: str = "peaks_catalog",
        topk_predicted: int = 5,
        batch_size: int = 200,
        refresh: str = "false",
) -&gt; None:
    """Walk a folder of images, embed + enrich, and bulk index to Elasticsearch."""
    root = Path(images_root)
    if not root.exists():
        raise SystemExit(f"Images root not found: {images_root}")

    emb = Siglip2()
    batch: List[Dict[str, Any]] = []
    n_indexed = 0

    for p in iter_images(root):
        rel = relpath_within(root, p)
        _id = id_for_path(rel)

        # 1) Image embedding (and reuse it for predicted_peaks)
        try:
            with Image.open(p) as im:
                ivec = emb.image_vec(im.convert("RGB")).astype("float32")
        except (UnidentifiedImageError, OSError) as e:
            print(f"[skip] {rel} — cannot embed: {e}")
            continue

        # 2) Predict top-k peak names
        try:
            top_names = predict_peaks(es, ivec.tolist(), peaks_index=peaks_index, k=topk_predicted)
        except Exception as e:
            print(f"[warn] predict_peaks failed for {rel}: {e}")
            top_names = []

        # 3) EXIF enrichment (safe)
        gps = get_gps_decimal(str(p))
        shot = get_shot_time(str(p))

        # 4) Build doc and stage for bulk
        doc = {"path": rel, "clip_image": ivec.tolist(), "predicted_peaks": top_names}
        if gps:
            doc["gps"] = gps
        if shot:
            doc["shot_time"] = shot

        batch.append(
            {"_op_type": "index", "_index": photos_index, "_id": _id, "_source": doc}
        )

        # 5) Periodic flush
        if len(batch) &gt;= batch_size:
            helpers.bulk(es, batch, refresh=refresh)
            n_indexed += len(batch)
            print(f"[photos] indexed {n_indexed} (last: {rel})")
            batch.clear()

    # Final flush
    if batch:
        helpers.bulk(es, batch, refresh=refresh)
        n_indexed += len(batch)
        print(f"[photos] indexed {n_indexed} total.")

    print("[done] photos indexing")<p>Exemple de document de l'index des photos :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt744b7e6326937cfc/6a17da7e6df731d3040a0da8/1dc1406ac2a97440b6804838795b3c2205c4c6b2-1080x1234.png" alt="Un exemple de document provenant de l'index des photos dans Elasticsearch." /><p>En résumé, l'index des photos est le magasin rapide, filtrable et prêt pour le kNN de toutes les photos de l'album. Sa cartographie est volontairement minimale - juste assez de structure pour permettre une recherche rapide, un affichage propre et une répartition des résultats dans l'espace et dans le temps. Cet index sert aux deux types de recherche. Le script Python permettant de créer les deux indices est disponible <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/create_indices.py">ici.</a></p><p>La visualisation des cartes Kibana ci-dessous affiche les documents de l'album photo sous forme de points verts et les pics montagneux de l'index <code>peaks_catalog</code> sous forme de triangles rouges, les points verts correspondant bien au sentier de randonnée du camp de base de l'Everest.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb5bf016e8d9c3e84/6a17da80be608681f10045e6/1c75d0ed0ce53d28a94bf2f47a354e25581d2baf-1600x1402.png" alt="Visualisation de cartes Kibana affichant les documents de l'album photo sous forme de points verts et les sommets de l'index peaks_catalog sous forme de triangles rouges, les points verts correspondant bien au sentier de trekking du camp de base de l'Everest." /><h2>Cas d'utilisation de la recherche</h2><p><strong>Recherche par nom (texte-image) :</strong> Cette fonction permet aux utilisateurs de localiser des photos de sommets de montagne (et même des concepts abstraits comme les "drapeaux de prière") à l'aide de requêtes textuelles. Pour ce faire, l'entrée texte est <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L87C5-L87C20">convertie en un vecteur texte</a> à l'aide de SigLIP-2. Pour la génération de vecteurs de texte robustes, nous utilisons la même stratégie que pour la création d'enchâssements de texte dans l'index <code>peaks_catalog</code>: <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L104">combinaison de l'</a> entrée texte avec un petit <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L100">ensemble d'invites</a>, soustraction d'un<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L103"> vecteur anti-concept</a> mineur et application de la <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L104">normalisation L2</a> pour produire le vecteur d'interrogation final. Une <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L140">requête</a> kNN est ensuite exécutée sur le champ <code>photos.clip_image</code> pour récupérer les pics les plus proches, sur la base de la similarité cosinusoïdale pour trouver les images les plus proches. Il est possible de rendre les résultats de la recherche plus pertinents en appliquant des <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L152">filtres</a> géographiques et de date, et/ou un filtre de terme <code>photos.predicted_peaks</code> dans le cadre de la requête (voir les exemples de requêtes ci-dessous). Cela permet d'exclure les sommets qui ressemblent à d'autres et qui ne sont pas visibles lors de la randonnée.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9bb9abf5ce64fcbb/6a17da81e8fbce20db3a17da/b5fac28ffdbedb820505365ca07df125cd01b939-946x370.png" alt="Comment fonctionne la recherche multimodale par nom (texte-image) dans Elasticsearch." /><p><strong>Requête Elasticsearch avec filtre géographique :</strong></p>POST photos/_search
{
  "knn": {
    "field": "clip_image",
    "query_vector": [ ... ],
    "k": 60,
    "num_candidates": 2000
  },
  "query": {
    "bool": {
      "filter": [
        { "geo_bounding_box": { "gps": { "top_left": "...", "bottom_right": "..." } } }
      ]
    }
  },
  "_source": ["path","predicted_peaks","gps","shot_time"]
}

Response (first two documents):
{
 "hits": {
   "total": {
     "value": 56,
     "relation": "eq"
   },
   "max_score": 0.5779596,
   "hits": [
     {
       "_index": "photos",
       "_id": "d01da3a1141981486c3493f6053c79e92a788463",
       "_score": 0.5779596,
       "_source": {
         "path": "IMG_2738.HEIC",
         "predicted_peaks": [
           "Pumori",
           "Kyajo Ri",
           "Khumbila",
           "Nangkartshang",
           "Kongde Ri"
         ],
         "gps": {
           "lat": 27.97116388888889,
           "lon": 86.82331111111111
         },
         "shot_time": "2023-11-03T08:07:13"
       }
     },
     {
       "_index": "photos",
       "_id": "c79d251f07adc5efaedc53561110a7fd78e23914",
       "_score": 0.5766071,
       "_source": {
         "path": "IMG_2761.HEIC",
         "predicted_peaks": [
           "Kyajo Ri",
           "Makalu",
           "Baruntse",
           "Cho Oyu",
           "Khumbila"
         ],
         "gps": {
           "lat": 27.975558333333332,
           "lon": 86.82515
         },
         "shot_time": "2023-11-03T08:51:08"
       }
     }
}<p><strong>Recherche par image (image à image) :</strong> Cette fonction permet d'identifier une montagne sur une photo et de trouver d'autres images de cette même montagne dans l'album photo. Lorsqu'une image est téléchargée, elle est traitée par l'encodeur d'images SigLIP-2 pour générer un <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/identify_from_picture_find_similar_peaks.py#L228">vecteur d'image</a>. Une <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/identify_from_picture_find_similar_peaks.py#L234">recherche kNN</a> est ensuite effectuée sur le champ <code>peaks_catalog.text_embed</code> pour identifier les noms de pics qui correspondent le mieux. Ensuite, un <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/identify_from_picture_find_similar_peaks.py#L257">vecteur de texte est généré</a> à partir des noms de pics correspondants, et une autre <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/identify_from_picture_find_similar_peaks.py#L263">recherche kNN</a> est effectuée sur l'index des photos pour localiser les images correspondantes.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltab9d16333e2a9e69/6a17da827f6f155448c099cc/3a3d5635bee7a222b95529dd7f9fbee016381610-1226x550.png" alt="Comment fonctionne la recherche multimodale par image (image à image) dans Elasticsearch." /><p><strong>Requête Elasticsearch :</strong></p><p>Étape 1 : Trouver les noms de pics correspondants</p>GET peaks_catalog/_search
{
 "knn": {
   "field": "text_embed",
   "query_vector": [...image-vector... ],
   "k": 3,
   "num_candidates": 500
 },
 "_source": [
   "id",
   "names",
   "latlon",
   "text_embed"
 ]
}


Response (first two documents):
{
 "took": 2,
 "timed_out": false,
 "_shards": {
   "total": 1,
   "successful": 1,
   "skipped": 0,
   "failed": 0
 },
 "hits": {
   "total": {
     "value": 3,
     "relation": "eq"
   },
   "max_score": 0.58039916,
   "hits": [
     {
       "_index": "peaks_catalog",
       "_id": "pumori",
       "_score": 0.58039916,
       "_source": {
         "id": "pumori",
         "names": [
           "Pumori",
           "Pumo Ri"
         ],
         "latlon": {
           "lat": 28.01472,
           "lon": 86.82806
         },
         "text_embed": [
                  ... embeddings...
         ]
       }
     },
     {
       "_index": "peaks_catalog",
       "_id": "kyajo-ri",
       "_score": 0.57942784,
       "_source": {
         "id": "kyajo-ri",
         "names": [
           "Kyajo Ri",
           "Kyazo Ri"
         ],
         "latlon": {
           "lat": 27.909167,
           "lon": 86.673611
         },
         "text_embed": [
           ... embeddings...
         ]
       }
     }
   ]
 }
}<p>Étape 2 : Effectuer une recherche dans l'index <code>photos</code> pour trouver les images correspondantes (même requête que dans le cas d'utilisation de la recherche texte-image) :</p>POST photos/_search
{
 "knn": {
   "field": "clip_image",
   "query_vector": [ ...image-vector... ],
   "k": 30,
   "num_candidates": 2000
 },
 "_source": [
   "path",
   "gps",
   "shot_time",
   "predicted_peaks",
   "clip_image"
 ],
 "query": {
   "bool": {
     "filter": [
       {
         "term": {
           "predicted_peaks": "Pumori"
         }
       }
     ]
   }
 }
}


Response (first two documents):
{
 "hits": {
   "total": {
     "value": 56,
     "relation": "eq"
   },
   "max_score": 0.5779596,
   "hits": [
     {
       "_index": "photos",
       "_id": "d01da3a1141981486c3493f6053c79e92a788463",
       "_score": 0.5779596,
       "_source": {
         "path": "IMG_2738.HEIC",
         "predicted_peaks": [
           "Pumori",
           "Kyajo Ri",
           "Khumbila",
           "Nangkartshang",
           "Kongde Ri"
         ],
         "gps": {
           "lat": 27.97116388888889,
           "lon": 86.82331111111111
         },
         "shot_time": "2023-11-03T08:07:13"
       }
     },
     {
       "_index": "photos",
       "_id": "c79d251f07adc5efaedc53561110a7fd78e23914",
       "_score": 0.5766071,
       "_source": {
         "path": "IMG_2761.HEIC",
         "predicted_peaks": [
           "Kyajo Ri",
           "Makalu",
           "Baruntse",
           "Cho Oyu",
           "Khumbila"
         ],
         "gps": {
           "lat": 27.975558333333332,
           "lon": 86.82515
         },
         "shot_time": "2023-11-03T08:51:08"
       }
     }
}<h2>Streamlit UI</h2><p>Pour réunir le tout, nous avons créé une interface utilisateur Streamlit simple qui nous permet de réaliser les deux cas d'utilisation de la recherche. La barre de gauche affiche une liste déroulante de pics (agrégés à partir de <code>photos.predicted_peaks</code>) avec des cases à cocher et un filtre mini-carte/géo. En haut, il y a un champ de <strong>recherche par nom</strong> et un bouton d'<strong>identification à partir du</strong> téléchargement d'une photo. Le volet central présente une grille de vignettes réactive indiquant les scores kNN, les badges de pic prédit et les heures de capture. Chaque image comporte un bouton " <strong>Voir l'image"</strong> qui permet d'obtenir un aperçu en pleine résolution.</p><p><strong>Recherchez en téléchargeant une image :</strong> Nous prédisons le pic et trouvons les pics correspondants dans l'album photo.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd1fb2b0304a310d2/6a17da8425daab7cda08a0fa/dca540cbf5279e6d6102c5a0c0351ddd4ac91cda-1600x1112.png" alt="Une interface utilisateur simple et claire qui permet d'effectuer une recherche multimodale texte-image et image-image pour les pics du mont Ama Dablam." /><p><strong>Recherche par texte</strong>: Trouver les sommets correspondants dans l'album à partir d'un texte.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt496c1ae8f7886320/6a17da86abe0f2da48dfe8bd/b1e8618db746cd49ea4962d3dc73031387b975dd-1600x1166.png" alt="Comment rechercher un sommet du mont Everest avec la recherche par texte dans la bibliothèque des sommets de montagne." /><h2>Conclusion</h2><p>Tout a commencé par <em>la possibilité de voir les </em><em> photos de</em><em><strong>l'Ama Dablam.</strong></em> s'est transformé en un petit système de <strong>recherche multimodale</strong> fonctionnel. Nous avons pris des photos brutes de trek, les avons transformées en <strong>encastrements SigLIP-2</strong> et avons utilisé <strong>Elasticsearch</strong> pour effectuer un <strong>kNN</strong> rapide sur les vecteurs, ainsi que des filtres géo/temporels simples pour faire remonter à la surface les bonnes images en fonction de leur <em>signification</em>. En cours de route, nous avons séparé les préoccupations en deux indices : un minuscule <code>peaks_catalog</code> de prototypes mélangés (pour l'identification) et un index évolutif <code>photos</code> de vecteurs d'images et d'EXIF (pour la recherche). Il est pratique, reproductible et facile à étendre.</p><p>Si vous souhaitez l'accorder, vous pouvez jouer avec quelques paramètres :</p><ul><li><p><strong>Paramètres de temps de recherche :</strong> <code>k</code> (nombre de voisins à récupérer) et <code>num_candidates</code> (étendue de la recherche avant la notation finale). Ces paramètres sont abordés dans le blog <a href="https://www.elastic.co/search-labs/blog/elasticsearch-knn-and-num-candidates-strategies">ici.</a></p></li><li><p><strong>Paramètres de temps d'indexation :</strong> <code>m</code> (connectivité du graphique) et <code>ef_construction</code> (précision du temps de construction par rapport à la mémoire). Pour les requêtes, expérimentez avec <code>ef_search</code> également - une valeur plus élevée signifie généralement un meilleur rappel avec un certain compromis en termes de latence. Consultez <a href="https://www.elastic.co/search-labs/blog/hnsw-graph">ce blog</a> pour plus de détails sur ces paramètres.</p></li></ul><p>A l'avenir, des modèles natifs/rerankers pour la recherche <strong>multimodale</strong> et <strong>multilingue</strong> seront bientôt intégrés à l'écosystème Elastic, ce qui devrait rendre la recherche d'images/de textes et le classement hybride encore plus performants.<a href="https://ir.elastic.co/news/news-details/2025/Elastic-Completes-Acquisition-of-Jina-AI-a-Leader-in-Frontier-Models-for-Multimodal-and-Multilingual-Search/default.aspx?utm_source=chatgpt.com"> ir.elastic.co+1</a></p><p>Si vous souhaitez essayer vous-même :</p><ul><li><p><strong>GitHub repo</strong> <a href="https://github.com/navneet83/multimodal-mountain-peak-search"><em>: https://github.com/navneet83/multimodal-mountain-peak-search</em></a></p></li><li><p><strong>Colab quickstart</strong> <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/notebooks/multimodal_mountain_peak_search.ipynb">: https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/notebooks/multimodal_mountain_peak_search.ipynb</a></p></li></ul><p>Notre voyage s'achève donc et il est temps de prendre l'avion du retour. J'espère que cela vous a été utile et si vous le cassez (ou l'améliorez), j'aimerais savoir ce que vous avez changé.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdce2fff1569d2a8b/6a17da894b055dd1f24320a2/d324d1e1472f1bfbd8f25747f57bdeeb9c7f16b2-1600x1200.png" alt="" />]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/multimodal-search-siglip-2-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/multimodal-search-siglip-2-elasticsearch</guid>
    <category><![CDATA[Base vectorielle]]></category>
    <category><![CDATA[Recherche hybride]]></category>
    <category><![CDATA[IA]]></category>
    <category><![CDATA[Python]]></category>
    <dc:creator><![CDATA[Navneet Kumar]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltccb66279debb05f9/6a17da8b63baffe228741b15/ffcf93358a7c5dadcea82faf3de460bf060d003c-1600x1200.png" length="0" type="image/png"/>
    <pubDate>Tue, 04 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Serveur MCP élastique : Expose les outils de l'Agent Builder à n'importe quel agent d'intelligence artificielle]]></title>
    <description><![CDATA[Découvrez comment utiliser le serveur Elastic MCP intégré dans Agent Builder pour étendre en toute sécurité n'importe quel agent d'IA avec un accès à vos données privées et à vos outils personnalisés.]]></description>
    <content:encoded><![CDATA[<p>Elastic Agent Builder est une plateforme permettant de créer des outils et des agents profondément intégrés à vos propres données dans Elasticsearch. Par exemple, vous pouvez créer des outils qui effectuent des recherches sémantiques sur des documents internes, analysent les journaux d'observabilité ou interrogent les alertes de sécurité.</p><p>Mais la véritable magie s'opère lorsque vous pouvez introduire ces outils personnalisés et conscients des données dans les environnements où vous passez le plus clair de votre temps. Et si l'agent de votre éditeur de code pouvait accéder en toute sécurité à la base de connaissances privée de votre organisation ?</p><p>C'est là qu'intervient le <strong>protocole de contexte de modèle (MCP).</strong> Elastic Agent Builder est livré avec un serveur MCP intégré qui permet d'accéder aux outils de la plateforme.</p><h2>Pourquoi utiliser le serveur MCP Elastic Agent Builder ?</h2><p>Les agents d'IA sont incroyablement puissants, mais leurs connaissances sont généralement limitées aux données sur lesquelles ils ont été formés et aux informations qu'ils peuvent activement rechercher sur l'internet public. Ils ne connaissent pas les documents de conception internes de votre entreprise, les manuels de déploiement spécifiques de votre équipe ou la structure unique des journaux de votre application.</p><p>Le défi consiste à donner à votre assistant d'intelligence artificielle le contexte spécialisé dont il a besoin. C'est précisément le problème que le MCP est censé résoudre. <strong>MCP est une norme ouverte qui permet à un modèle ou à un agent d'IA de découvrir et d'utiliser des outils externes.</strong></p><p>Pour rendre cela possible, l'Elastic Agent Builder expose nativement vos outils personnalisés par le biais d'un serveur MCP intégré. Cela signifie que vous pouvez facilement connecter n'importe quel client compatible MCP, comme <strong>Cursor</strong>, <strong>VS Code</strong> ou <strong>Claude Desktop</strong>, avec les outils spécialisés et sensibles aux données que vous avez construits avec Elastic Agent Builder.</p><h2>Quand utiliser MCP (et quand ne pas le faire)</h2><p>Elastic Agent Builder comprend plusieurs protocoles pour prendre en charge différents modèles d'intégration. Il est essentiel de choisir la bonne solution pour mettre en place des flux de travail efficaces en matière d'IA.</p><ul><li><p><strong>Utilisez </strong><a href="https://www.elastic.co/docs/solutions/search/agent-builder/mcp-server"><strong>MCP</strong></a> pour compléter votre agent d'intelligence artificielle (comme dans <strong>Cursor</strong> ou <strong>VS Code</strong>) avec des outils spécialisés. Il s'agit de l'approche ""apportez vos propres outils"", qui permet d'améliorer l'assistant que vous utilisez déjà grâce à un accès sécurisé à vos données privées. Seuls les outils sont exposés par l'intermédiaire du serveur MCP - les agents d'Elastic sont distincts.</p></li><li><p><strong>Utilisez le </strong><a href="https://www.elastic.co/docs/solutions/search/agent-builder/a2a-server"><strong>protocole A2A</strong></a> pour permettre à votre agent Elastic personnalisé de collaborer avec d'autres agents autonomes (comme dans <a href="https://www.elastic.co/search-labs/blog/a2a-protocol-elastic-agent-builder-gemini-enterprise"><strong>Gemini Enterprise de Google)</strong></a>. Il s'agit de la délégation d'agent à agent, où chaque agent travaille comme un pair pour résoudre un problème.</p></li><li><p><strong>Utilisez les </strong><a href="https://www.elastic.co/docs/solutions/search/agent-builder/kibana-api"><strong>API de l'Agent Builder</strong></a> pour un contrôle programmatique complet lors de la création d'une application personnalisée à partir de zéro.</p></li></ul><p>Pour un développeur qui cherche à obtenir des réponses dans sa documentation interne sans quitter son IDE, le MCP est la solution idéale.</p><h2>Exemple : vos outils personnalisés dans Cursor avec le serveur MCP Agent Builder</h2><p>Prenons un exemple pratique que j'utilise quotidiennement. Tout d'abord, j'ai exploré et indexé notre documentation technique interne dans un index Elasticsearch appelé <code>elastic-dev-docs</code>. Nous pourrions utiliser les outils génériques intégrés disponibles dans Agent Builder, mais nous allons créer notre propre outil personnalisé pour interroger cette base de connaissances spécifique.</p><p>La raison de la construction d'un outil sur mesure est simple : <strong>contrôle et précision</strong>. Cette approche nous permet d'exécuter une requête sémantique rapide directement sur notre index <code>elastic-dev-docs</code>. Nous avons un contrôle total sur l'index ciblé et sur la manière dont les données sont extraites.</p><p>Voici maintenant comment nous pouvons utiliser cette base de connaissances personnalisée dans un éditeur de code doté d'une intelligence artificielle, comme Cursor.</p><h3>Étape 1 : Créer un outil de base de connaissances personnalisé dans Agent Builder</h3><p>Tout d'abord, créez un nouvel outil dans Agent Builder. Une description claire et précise de l'outil est importante car c'est ainsi que tout agent d'IA, qu'il s'agisse de l'agent Elastic interne ou d'un outil externe tel que Cursor se connectant via MCP, découvre et sélectionne votre outil pour la bonne tâche.</p><p>Une description solide doit être explicite. Par exemple : "Effectue une recherche sémantique sur l'index elastic-dev-docs pour trouver la documentation d'ingénierie interne, les runbooks et les procédures de mise en production."</p><p>L'outil est alors configuré pour effectuer une recherche sémantique dans notre index spécifique. Une fois sauvegardé, il est immédiatement disponible pour être servi.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt011118f0a9279185/6a17f367dbb4ffc4f3fb581a/1eea079908fdf7cc72dbe81abd07ff51601a43d4-1472x1600.png" alt="Création d'un outil de base de connaissances personnalisé dans Agent Builder." /><p>Avant de le connecter au monde extérieur, vous pouvez le tester directement dans l'interface utilisateur. Il suffit de cliquer sur le bouton <strong>Test</strong> pour remplir manuellement les paramètres, émuler ce que le LLM fera, et inspecter les résultats pour confirmer que tout fonctionne correctement.</p><h3>Étape 2 : Connecter le curseur au serveur Elastic MCP</h3><p>Elastic Agent Builder expose automatiquement tous les outils disponibles via un point de terminaison MCP sécurisé. Vous pouvez trouver l'URL unique de votre serveur dans l'interface utilisateur Outils de Kibana.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdd0e62ae0f394c3d/6a17f368e317916ec32d5933/ba137be30f0eaa7f028b96bd8af4e2779c3f8a33-1600x589.png" alt="Comment connecter le curseur dans l'interface outils de Kibana au serveur Elastic MCP." /><p>Pour se connecter à Cursor, il suffit d'ajouter cette URL à son fichier de configuration, ainsi qu'une clé API Elastic pour l'authentification<a href="https://www.elastic.co/docs/deploy-manage/api-keys/elasticsearch-api-keys">(découvrez comment créer une clé API ES</a>). Nous utilisons une clé API pour l'autorisation, car elle garantit que les outils ne s'exécutent qu'avec les permissions que vous avez accordées, en respectant toutes vos règles de contrôle d'accès.</p><p>La configuration MCP dans le site <code>~/.cursor/mcp.json</code> de Cursor se présente comme suit :</p>{
  "mcpServers": {
    "elastic-agent-builder": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://your-kibana.kb.company.io/api/agent_builder/mcp",
        "--header",
        "Authorization:${AUTH_HEADER}"
      ],
      "env": {
        "AUTH_HEADER": "ApiKey &lt;ELASTIC_API_KEY&gt;"
      }
    }
  }
}<p>Une fois la configuration sauvegardée, vous devriez voir l'outil Elastic Agent Builder MCP server disponible dans Cursor.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2837638263e628ed/6a17f36adbb4ffeb9cfb5820/d302c6d3609fbf14fd40e21b9e69e567bf12553f-1600x1002.png" alt="Une image de l'outil serveur Elastic Agent Builder MCP disponible dans Cursor." /><h3>Étape 3 : Posez vos questions !</h3><p>Une fois la connexion établie, les agents Cursor peuvent maintenant invoquer vos outils personnalisés pour répondre à vos questions ou guider le processus de génération de code.</p><p>Posons une question précise :</p><p><em>"Consulter les étapes pour libérer le service de crawler de la documentation interne de l'ingénierie d'elastic search org"</em></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt83fa357261b30e93/6a17f36c4b055d16d1432326/14f572730203c23615bb9dd38234bcb3b0f81155-1600x1468.png" alt="Agents curseurs invoquant des outils personnalisés pour répondre aux questions et guider le processus de génération de code." /><p>En coulisses, la magie opère :</p><ol><li><p>L'agent du curseur décide de la meilleure façon de répondre à votre question et décide d'appeler la fonction <code>engineering_documentation_internal_search</code></p></li><li><p>Il invoque l'outil à l'aide d'une requête en langage naturel</p></li><li><p>L'outil effectue une recherche sémantique dans l'index <code>elastic-dev-docs</code> et renvoie les procédures les plus pertinentes et les plus récentes.</p></li></ol><p>Nous obtenons une réponse précise et fiable, basée sur notre documentation interne, sans jamais quitter l'éditeur de code. L'expérience est transparente et puissante.</p><h2>À vous de construire</h2><p>Vous avez maintenant vu comment utiliser le serveur MCP intégré dans Elastic Agent Builder pour étendre vos assistants IA avec un accès sécurisé à vos données privées. Pour que les modèles soient réellement utiles, il est essentiel de les ancrer dans vos propres informations.</p><p>Pour récapituler, nous avons couvert les principales étapes :</p><ul><li><p>Choisir le bon protocole pour vos besoins (MCP).</p></li><li><p>Création d'un outil de base de connaissances personnalisé.</p></li><li><p>Connecter cet outil à un assistant IDE comme Cursor.</p></li></ul><p>Vos agents et vos outils n'ont plus besoin d'être déconnectés de leur contexte le plus précieux. Nous espérons que ce guide vous aidera à créer des flux de travail plus efficaces et tenant compte des données. Bonne construction !</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elastic-mcp-server-agent-builder-tools</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elastic-mcp-server-agent-builder-tools</guid>
    <category><![CDATA[IA agentique]]></category>
    <category><![CDATA[IA]]></category>
    <category><![CDATA[Outils d'IA ]]></category>
    <dc:creator><![CDATA[Jedr Blaszyk,Joe McElroy]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta5b61961b6269ab1/6a17f36ea29299d839d02db2/ef5153551a1d14833c7f512fede554d1dfb31553-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 20 Oct 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Évaluation des agents d'IA : Comment Elastic teste les frameworks agentiques]]></title>
    <description><![CDATA[Découvrez comment nous évaluons et testons les changements apportés à un système agentique avant de les communiquer aux utilisateurs d'Elastic afin de garantir des résultats précis et vérifiables.]]></description>
    <content:encoded><![CDATA[<h2>Introduction</h2><p>Dans la pile Elastic, il existe de nombreuses applications agentiques alimentées par LLM, telles que le futur agent Elastic AI dans<a href="https://www.elastic.co/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder"> Agent Builder</a> (actuellement en tech preview) et <a href="https://www.elastic.co/docs/solutions/security/ai/attack-discovery">Attack Discovery</a> <a href="https://www.elastic.co/blog/whats-new-elastic-security-9-0-0">(GA</a> dans 8.18 et 9.0+), et d'autres sont en cours de développement. Pendant le développement, et même après le déploiement, il est important de répondre à ces questions :</p><ul><li><p>Comment évaluer la qualité des réponses de ces applications d'IA ?</p></li><li><p>Si nous apportons un changement, comment pouvons-nous garantir qu'il s'agit réellement d'une amélioration et qu'il n'entraînera pas une dégradation de l'expérience de l'utilisateur ?</p></li><li><p>Comment pouvons-nous facilement tester ces résultats de manière reproductible ?</p></li></ul><p>Contrairement aux tests de logiciels traditionnels, l'évaluation des applications d'IA générative fait appel à des méthodes statistiques, à un examen qualitatif nuancé et à une compréhension approfondie des objectifs des utilisateurs.</p><p>Cet article détaille le processus employé par l'équipe de développeurs d'Elastic pour effectuer des évaluations, garantir la qualité des changements avant leur déploiement et contrôler les performances du système. Nous voulons nous assurer que chaque changement est étayé par des preuves, ce qui permet d'obtenir des résultats fiables et vérifiables. Une partie de ce processus est intégrée directement dans Kibana, ce qui reflète notre engagement en matière de transparence dans le cadre de notre éthique des logiciels libres. En partageant ouvertement une partie de nos données d'évaluation et de nos paramètres, nous cherchons à renforcer la confiance de la communauté et à fournir un cadre clair à tous ceux qui développent des agents d'intelligence artificielle ou utilisent nos produits.</p><h2>Exemples de produits</h2><p>Les méthodes utilisées dans ce document ont servi de base à l'itération et à l'amélioration de solutions telles que Attack Discovery et Elastic AI Agent. Une brève présentation des deux, respectivement :</p><h3>Découverte d'attaques par Elastic Security</h3><p>Attack Discovery utilise les LLM pour identifier et résumer les séquences d'attaques dans Elastic. À partir des alertes d'Elastic Security dans un délai donné (24 heures par défaut), le flux de travail agentique d'Attack Discovery déterminera automatiquement si une ou plusieurs attaques ont eu lieu, ainsi que des informations importantes telles que l'hôte ou les utilisateurs compromis, et les alertes qui ont contribué à la conclusion de l'attaque.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb70932abe8d4de75/6a17f04ea292990c52d02d61/20fabb47642dad7b588daaaa8c3a98de860ad01d-1251x758.png" alt="" /><p></p><p>L'objectif est que la solution basée sur le LLM produise un résultat au moins aussi bon que celui d'un humain.</p><h3>Agent d'IA élastique</h3><p>L'<strong>Elastic Agent Builder</strong> est notre nouvelle plateforme pour la création d'agents d'intelligence artificielle sensibles au contexte qui tirent parti de toutes nos capacités de recherche. Il est livré avec l'<strong>agent Elastic AI</strong>, un agent général préconstruit conçu pour aider les utilisateurs à comprendre et à obtenir des réponses à partir de leurs données par le biais d'une interaction conversationnelle.</p><p>L'agent y parvient en identifiant automatiquement les informations pertinentes dans Elasticsearch ou dans les bases de connaissances connectées et en tirant parti d'une série d'outils prédéfinis pour interagir avec elles. Cela permet à l'agent Elastic AI de répondre à un large éventail de requêtes d'utilisateurs, allant de la simple Q&amp;A sur un seul document à des demandes complexes nécessitant une agrégation et des recherches en une ou plusieurs étapes dans plusieurs index.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3b9dbede85a56bd6/6a17f050e8fbce88943a1a30/d29dee100bb8a17bb623acd745773a5164a1df4f-1600x1014.png" alt="" /><h2>Mesurer les améliorations au moyen d'expériences</h2><p>Dans le contexte des agents d'intelligence artificielle, une expérience est une modification structurée et testable du système conçue pour améliorer les performances sur des aspects bien définis (par exemple, l'utilité, l'exactitude, la latence). L'objectif est de répondre de manière définitive à la question suivante "Si nous fusionnons ce changement, pouvons-nous garantir qu'il s'agit d'une véritable amélioration et qu'il ne dégradera pas l'expérience de l'utilisateur ?"</p><p>La plupart des expériences que nous menons comprennent généralement</p><ul><li><p><strong>Une hypothèse :</strong> Une affirmation spécifique et falsifiable. <em>Exemple :</em> "L'accès à un outil de découverte d'attaques améliore l'exactitude des requêtes liées à la sécurité.</p></li><li><p><strong>Critères de réussite :</strong> Des seuils clairs qui définissent ce qu'on entend par "succès". <em>Exemple :</em> "+5% amélioration du score de justesse sur l'ensemble de données de sécurité, pas de dégradation ailleurs".</p></li><li><p><strong>Plan d'évaluation :</strong> Comment nous mesurons le succès (mesures, ensembles de données, méthode de comparaison)</p></li></ul><p>Une expérience réussie est un processus systématique de recherche. Chaque changement, qu'il s'agisse d'une modification mineure ou d'un changement architectural majeur, suit ces sept étapes afin de garantir que les résultats sont significatifs et exploitables :</p><ul><li><p>Étape 1 : Identifier le problème</p></li><li><p>Étape 2 : Définir les indicateurs</p></li><li><p>Étape 3 : Formuler une hypothèse claire</p></li><li><p>Étape 4 : Préparation de l'ensemble de données d'évaluation</p></li><li><p>Étape 5 : Exécuter l'expérience</p></li><li><p>Étape 6 : Analyse des résultats + itération</p></li><li><p>Étape 7 : Prendre une décision et la documenter</p></li></ul><p>Un exemple de ces étapes est illustré à la <em>figure 1.</em> Les sous-sections suivantes expliquent chaque étape, et nous développerons les détails techniques de chaque étape dans les documents à venir.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt06bfe2f0e4205a18/6a17f052faa91358eb93c968/3a9f5a3e92dd4922a795a19104c6e4ad8c98958d-2400x1352.png" alt="" /><h2>Une démonstration pas à pas avec des exemples réels d'Elastic</h2><h3>Étape 1 : Identifier le problème</h3><p><em>Quel est exactement le problème que ce changement vise à résoudre ?</em></p><p>Exemple de découverte d'attaques : Les résumés sont parfois incomplets, ou une activité bénigne est signalée à tort comme une attaque (faux positifs).</p><p>Exemple d'agent d'IA élastique : La sélection des outils de l'agent, en particulier pour les requêtes analytiques, est sous-optimale et incohérente, conduisant souvent au choix du mauvais outil. Cela entraîne une augmentation des coûts des jetons et de la latence.</p><h3>Étape 2 : Définir les indicateurs</h3><p><em>Rendre le problème mesurable, afin de pouvoir comparer un changement à l'état actuel.</em></p><p>Les mesures courantes comprennent la <a href="https://developers.google.com/machine-learning/crash-course/classification/accuracy-precision-recall">précision et le rappel</a>, la <a href="https://en.wikipedia.org/wiki/Semantic_similarity">similarité sémantique</a>, la factualité, etc. Selon le cas d'utilisation, nous utilisons des vérifications de code pour calculer les mesures, telles que la correspondance des identifiants d'alerte ou des URL correctement récupérés, ou nous utilisons des techniques telles que LLM-as-judge pour des réponses plus libres.</p><p>Voici quelques exemples de mesures<em>(non exhaustifs</em>) utilisées dans les expériences :</p><p><strong>Attack Discovery</strong></p><p>Métrique</p><p>Description</p><p>Précision &amp; rappel</p><p>Faire correspondre les identifiants d'alerte entre les sorties réelles et les sorties prévues afin de mesurer la précision de la détection.</p><p>Similitude</p><p>Utilisez BERTScore pour comparer la similarité sémantique du texte de la réponse.</p><p>Factualité</p><p>Les principaux IOC (indicateurs de compromission) sont-ils présents ? Les tactiques de MITRE (taxonomie industrielle des attaques) sont-elles correctement prises en compte ?</p><p>Cohérence de la chaîne d'attaque</p><p>Comparez le nombre de découvertes pour vérifier si l'attaque a été sur ou sous-déclarée.</p><p><strong>Agent d'IA élastique</strong></p><p>Métrique</p><p>Description</p><p>Précision &amp; rappel</p><p>Faire correspondre les documents/informations récupérés par l'agent pour répondre à une requête de l'utilisateur avec les informations ou documents réels nécessaires pour répondre à la requête afin de mesurer la précision de la recherche d'informations.</p><p>Factualité</p><p>Les faits essentiels nécessaires pour répondre à la demande de l'utilisateur sont-ils présents ? Les faits sont-ils dans le bon ordre pour les questions de procédure ?</p><p>Pertinence de la réponse</p><p>La réponse contient-elle des informations périphériques ou sans rapport avec la requête de l'utilisateur ?</p><p>Complétude de la réponse</p><p>La réponse répond-elle à toutes les parties de la requête de l'utilisateur ? La réponse contient-elle toutes les informations présentes dans la vérité terrain ?</p><p>Validation ES|QL</p><p>La syntaxe de l'ES|QL générée est-elle correcte ? Est-il fonctionnellement identique à la vérité de terrain ES|QL ?</p><h3>Étape 3 : Formuler une hypothèse claire</h3><p><em>Établir des critères de réussite clairs en utilisant le problème et les paramètres définis ci-dessus.</em></p><p>Exemple d'agent d'IA élastique :</p><ol><li><p>Apporter des <strong>modifications aux descriptions des outils relevance_search et nl_search afin de définir clairement leurs fonctions spécifiques et leurs cas d'utilisation</strong>.</p></li><li><p>Nous prévoyons d'<strong>améliorer la</strong> <strong>précision de l'invocation des outils de</strong> <strong>25%</strong>.</p></li><li><p>Nous vérifierons qu'il s'agit d'un résultat positif net en nous assurant qu'il n'y a pas d'impact négatif sur d'autres indicateurs, par exemple l'<strong>exactitude et l'exhaustivité des données</strong>.</p></li><li><p>Nous pensons que cela fonctionnera parce que des <strong>descriptions précises des outils aideront l'agent à sélectionner et à appliquer avec plus de précision l'outil de recherche le plus approprié pour différents types de requêtes, ce qui réduira les erreurs d'application et améliorera l'efficacité globale de la recherche.</strong></p></li></ol><h3>Étape 4 : Préparation de l'ensemble de données d'évaluation</h3><p><em>Pour mesurer les performances du système, nous utilisons des ensembles de données qui représentent des scénarios réels.</em></p><p>Selon le type d'évaluation que nous menons, nous pouvons avoir besoin de différents types de formats de données, tels que les données brutes transmises à un LLM (par ex. scénarios d'attaque pour la découverte d'attaques) et les résultats attendus. Si l'application est un chatbot, les entrées peuvent être des requêtes d'utilisateurs, et les sorties peuvent être des réponses correctes du chatbot, des liens corrects qu'il aurait dû récupérer, etc.</p><p>Exemple de découverte d'attaque :</p><p>10 nouveaux scénarios d'attaque</p><p>8 épisodes de Oh My Malware (ohmymalware.com)</p><p>4 scénarios multi-attaques (créés en combinant les attaques des 2 premières catégories)</p><p>3 scénarios bénins</p><p>Exemple de jeu de données d'évaluation d'un agent d'IA élastique<a href="https://github.com/elastic/kibana/blob/main/x-pack/platform/packages/shared/onechat/kbn-evals-suite-onechat/evals/kb/kb.spec.ts">(lien vers le jeu de données Kibana</a>) :</p><p>14 Indices utilisant des ensembles de données open source pour simuler des sources multiples dans KB.</p><p>5 Types de requêtes (analytique, recherche de texte, hybride...)</p><p>7 Types d'intentions d'interrogation (procédurale, factuelle - classification, enquête ; ...)</p><h3>Étape 5 : Exécuter l'expérience</h3><p>Exécuter l'expérience en générant des réponses à partir de l'agent existant et de la version modifiée par rapport à l'ensemble de données d'évaluation. Calculer des paramètres tels que la factualité (voir étape 2).</p><p>Nous mélangeons plusieurs évaluations basées sur les paramètres requis à l'étape 2 :</p><ul><li><p>Évaluation basée sur des règles (par exemple utiliser Python/TypeScript pour vérifier si .json est valide)</p></li><li><p>LLM-as-judge (demander à un LLM séparé si une réponse est cohérente avec un document source)</p></li><li><p>Examen humain dans la boucle pour les contrôles de qualité des nuances</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt17ec63af0850d8dd/6a17f054505ac3e508ad8c1e/8648e75818d3291f0ac66f069438a500d42b8225-1600x1099.png" alt="Voici un exemple de résultat d'évaluation généré par notre cadre interne. Il présente diverses mesures issues d'une expérience menée sur différents ensembles de données." /><h3>Étape 6 : Analyse des résultats + itération</h3><p>Maintenant que nous disposons des mesures, nous analysons les résultats. <u><em>Même si les résultats satisfont aux critères de réussite définis à l'étape 3, il faut encore procéder à un examen humain avant de fusionner la modification avec la production</em></u>; si les résultats ne satisfont pas aux critères, il faut procéder à une itération et résoudre les problèmes, puis effectuer les évaluations sur la nouvelle modification.</p><p>Nous pensons qu'il faudra quelques itérations pour trouver la meilleure modification avant de procéder à la fusion. De la même manière que l'on exécute des tests logiciels locaux avant de valider un projet, les évaluations hors ligne peuvent être exécutées avec des modifications locales ou plusieurs propositions de modifications. Il est utile d'automatiser l'enregistrement des résultats d'expériences, des scores composites et des visualisations pour rationaliser l'analyse.</p><h3>Étape 7 : Prendre une décision et la documenter</h3><p>Sur la base d'un cadre de décision et de critères d'acceptation, décider de fusionner les changements et documenter l'expérience. La prise de décision comporte de multiples facettes et peut prendre en compte des facteurs au-delà de l'ensemble de données d'évaluation, tels que la vérification des scénarios de régression sur d'autres ensembles de données ou l'évaluation du rapport coût-bénéfice d'un changement proposé.</p><p>Exemple : Après avoir testé et comparé quelques itérations, choisissez la modification la mieux notée et envoyez-la aux chefs de produit et aux autres parties prenantes concernées pour approbation. Joignez les résultats des étapes précédentes pour vous aider à prendre une décision. Pour plus d'exemples sur la découverte d'attaques, voir <a href="https://www.elastic.co/blog/elastic-security-generative-ai-features">Dans les coulisses des fonctions d'IA générative d'Elastic Security.</a></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt62a466f3a0da114a/6a17f056faa91342c393c96c/74c80b8f34dce8ddd20873ecb2f553873587ed35-1600x618.png" alt="" /><h2>Conclusion</h2><p>Dans ce blog, nous avons parcouru le processus de bout en bout d'un flux d'expérimentation, illustrant comment nous évaluons et testons les changements apportés à un système agentique avant de les diffuser aux utilisateurs d'Elastic. Nous avons également fourni quelques exemples d'amélioration des flux de travail basés sur des agents dans Elastic. Dans les prochains billets de blog, nous développerons les détails des différentes étapes, telles que la création d'un bon ensemble de données, la conception de mesures fiables et la prise de décisions lorsque plusieurs mesures sont impliquées.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ai-agent-evaluation-elastic</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-agent-evaluation-elastic</guid>
    <category><![CDATA[IA agentique]]></category>
    <category><![CDATA[IA]]></category>
    <dc:creator><![CDATA[Susan Chang,Abhimanyu Anand]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte578b636637be6b1/6a17f057e8fbcebe9e3a1a36/ef3922076713872163e1aab47735361513b2c9ee-2400x1352.heif" length="0" type="image/*"/>
    <pubDate>Mon, 13 Oct 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Connexion des agents élastiques à Gemini Enterprise via le protocole A2A]]></title>
    <description><![CDATA[Apprenez à utiliser Agent Builder pour exposer votre agent Elastic personnalisé à des services externes tels que Gemini Enterprise avec le protocole A2A.]]></description>
    <content:encoded><![CDATA[<p><strong>Elastic Agent Builder</strong> est un ensemble de fonctionnalités permettant de créer des agents d'intelligence artificielle pilotés par les données directement dans Elasticsearch. Dans les articles précédents de cette <a href="https://www.elastic.co/search-labs/blog/series/context-aware-ai-agentic-workflows-with-elastic">série</a>, nous avons montré comment doter les agents personnalisés d'outils leur permettant d'effectuer des tâches complexes et leur fournir un ensemble d'instructions personnalisées pour guider leur comportement.</p><p>Mais qu'en est-il si vous souhaitez utiliser vos agents personnalisés avec les applications et les outils de productivité dont vous disposez déjà ?</p><p>C'est là qu'intervient le <strong>protocole Agent-to-Agent (A2A)</strong>. A2A est une <a href="https://github.com/a2aproject/A2A">norme ouverte</a> d'interopérabilité qui permet aux agents de différentes plateformes de communiquer et de collaborer. Et nous l'avons intégré directement dans l'Elastic Agent Builder.</p><p>Aujourd'hui, nous allons vous montrer comment prendre un agent personnalisé que vous avez construit et l'exposer à d'autres services, en particulier <strong>Gemini Enterprise </strong>(anciennement Agentspace).</p><h2>Le pouvoir des normes ouvertes : l'importance de l'A2A</h2><p>Dans l'article de blog <a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch">Votre premier agent Elastic</a>, nous avons montré comment créer des agents personnalisés, tels qu'un agent <em>Assistant financier</em> avec un accès sécurisé à vos données de marché. Mais sa valeur est limitée si vous ne pouvez pas rendre ses informations disponibles dans d'autres environnements, comme Gemini Enterprise, sans avoir à reconstruire votre travail.</p><p>C'est ce défi de l'interopérabilité qui freine l'IA agentique. Les agents ont besoin d'un langage commun pour communiquer entre les plateformes, ce qui est précisément le rôle du protocole A2A. Il fournit une couche de communication standard qui vous permet non seulement d'interagir directement avec votre agent, mais qui ouvre également la voie à un avenir où les agents spécialisés de votre organisation pourront collaborer et partager des informations.</p><p>Pour rendre cela possible, l'Elastic Agent Builder supporte nativement le protocole A2A à travers deux points de terminaison standard pour tous vos agents :</p><ol><li><p><strong>Le point de terminaison de la carte d'agent (</strong><strong><code>GET {your-kibana-url}/api/agent_builder/a2a/{agentId}.json</code></strong> )<strong>- </strong>Il s'agit de la carte de visite de votre agent personnalisé. Il fournit des métadonnées sur votre agent (nom, description, capacités, etc.) à tout service compatible A2A.</p></li><li><p><strong>Le point de terminaison du protocole A2A (</strong><strong><code>POST {your-kibana-url}/api/agent_builder/a2a/{agentId}</code></strong><strong>)</strong> - Il s'agit du canal de communication. D'autres agents envoient leurs demandes ici, et votre agent les traite et renvoie une réponse, conformément à la <a href="https://a2a-protocol.org/latest/specification/">spécification du protocole A2A</a>.</p></li></ol><h2>Testez votre agent avec l'inspecteur A2A</h2><p>Avant de connecter notre agent à un système de production, il est bon de vérifier qu'il communique correctement. Le moyen le plus simple d'y parvenir est d'utiliser l'<strong>inspecteur A2A</strong>, un outil spécialement conçu pour tester et déboguer les intégrations A2A.</p><p>La mise en route de l'inspecteur est simple. Vous pouvez cloner le dépôt <a href="https://github.com/a2aproject/a2a-inspector">a2a-inspector</a> et suivre les instructions du README pour <a href="https://github.com/a2aproject/a2a-inspector?tab=readme-ov-file#3-run-the-application">lancer l'application</a>. Une fois lancée, l'interface utilisateur est disponible par défaut à l'adresse <code>http://localhost:5001/</code>.</p><p>Pour connecter l'inspecteur A2A à votre agent, vous devez fournir deux informations essentielles :</p><ul><li><p>URL de la carte d'agent : Il s'agit du point de terminaison qui décrit votre agent. Pour l'<a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch">agent assistant financier de notre poste précédent</a>, cette URL serait <code>{your-kibana-url}/api/agent_builder/a2a/financial_assistant.json</code>.</p></li><li><p>En-tête d'authentification : Nous utiliserons une clé API standard pour l'authentification.</p></li></ul><p>Une fois que vous avez saisi ces informations dans l'interface utilisateur de l'inspecteur, vous pouvez vous connecter et commencer à dialoguer avec votre agent immédiatement.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6381135e3fb297df/6a17ef4bec0f898b0c5a66ea/7231c72bf30bed2a854f58658c1eca2843f43bfc-1600x1296.png" alt="Configuration de la carte d'agent A2A et de l'inspecteur d'agent" /><p>Cette simple validation nous donne l'assurance que notre agent est configuré correctement et qu'il est prêt pour l'étape suivante.</p><h2>En direct ! Votre agent personnalisé dans Gemini Enterprise</h2><p>Passons maintenant à la partie la plus excitante : donner vie à notre agent financier personnalisé au sein de Gemini Enterprise (anciennement Agentspace). Cette intégration s'appuie sur l'<a href="https://console.cloud.google.com/marketplace/product/elastic-prod/elastic-ai-agent">agent Elastic AI, disponible sur Google Cloud Marketplace</a>.</p><p>Une fois connecté, Gemini Enterprise utilise le protocole A2A pour communiquer directement avec votre agent. C'est là que la véritable puissance de l'interopérabilité se manifeste : les utilisateurs peuvent désormais accéder aux informations approfondies et axées sur les données de votre agent Elasticsearch personnalisé sans jamais quitter leur environnement familier. Vous pouvez voir votre agent Elastic personnalisé dans la liste des agents :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7f54f0bb15216d8e/6a17ef4d6df73107d90a0fdb/37a39e92ebf3d72c6c8014397cd8e846336173a4-1600x834.png" alt="Affichage d'un agent personnalisé dans une liste Google Agentspace" /><p>Imaginez qu'un utilisateur de Gemini Enterprise demande :</p><p><em>"Je m'inquiète du sentiment du marché. Pouvez-vous m'indiquer quels sont nos clients les plus exposés aux mauvaises nouvelles ?</em>"</p><p>En coulisses, Gemini Enterprise achemine cette requête via le protocole A2A vers votre agent Elastic personnalisé. Votre agent utilise alors ses outils spécialisés pour interroger vos données, formuler une réponse et la renvoyer. Pour l'utilisateur final, l'expérience est transparente.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte130c332ee0648a6/6a17ef4fe9ea874426a9c6bb/e5f126c1a27a51c6e69a767aa87c9f746b62e39c-1600x1044.png" alt="Un utilisateur demande à Agentspace de répondre à une requête et ce qui se passe dans les coulisses." /><p>Et cela ne s'arrête pas là ! La réponse obtenue avec l'agent Elastic peut maintenant être utilisée comme contexte pour vos prochaines questions qui peuvent déclencher un agent spécialisé différent (par exemple, l'agent Elastic). l'agent de votre plateforme d'investissement pour ajuster l'exposition aux sociétés cotées). Le tout sans quitter votre barre de recherche.</p><p>Avec vos agents Elastic déployés sur Gemini Enterprise avec A2A, vous pouvez unifier l'accès, l'orchestration et les flux de travail en éliminant les frictions entre l'IA, la recherche et les systèmes d'entreprise en offrant une interface utilisateur unique où les utilisateurs parlent à leurs données et à leurs outils - le tout en contexte. Pour les utilisateurs, cela signifie moins de changements d'outils et des assistants d'IA plus intuitifs et plus compétents. Pour les organisations, cela signifie une gouvernance cohérente, une évolutivité et une interopérabilité intégrées.</p><h2>À vous de construire</h2><p>Vous disposez désormais des outils nécessaires pour que vos agents élastiques soient disponibles partout. En tirant parti du protocole ouvert A2A, vous pouvez étendre la portée de vos agents personnalisés et sensibles aux données.</p><p>Dans cet article, nous vous avons présenté les principales étapes :</p><ul><li><p>Exposer votre agent via la carte d'agent A2A et les points d'extrémité du protocole.</p></li><li><p>Tester la connexion avec l'inspecteur A2A.</p></li><li><p>Intégrer votre agent en direct dans un service externe tel que Gemini Enterprise de Google.</p></li></ul><p>Vos agents n'ont plus besoin d'être isolés. Nous sommes impatients de voir les systèmes puissants et interconnectés que vous créerez. Bonne construction !</p><p>Le moyen le plus simple de commencer est d'essayer gratuitement Elastic Cloud sur <a href="https://console.cloud.google.com/marketplace/product/elastic-prod/elastic-cloud?pli=1">Google Cloud Marketplace</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/a2a-protocol-elastic-agent-builder-gemini-enterprise</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/a2a-protocol-elastic-agent-builder-gemini-enterprise</guid>
    <category><![CDATA[IA agentique]]></category>
    <category><![CDATA[IA]]></category>
    <dc:creator><![CDATA[Jedr Blaszyk,Valerio Arvizzigno,Joe McElroy]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt63d7675adc5bc211/6a17ef51ddf97d38e8910bdf/5be8a425fab55dca2f9717d2e50812b0450fa625-1440x840.png" length="0" type="image/png"/>
    <pubDate>Thu, 09 Oct 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Votre premier agent Elastic : D'une simple requête à un chat alimenté par l'IA]]></title>
    <description><![CDATA[Apprenez à utiliser le constructeur d'agents d'IA d'Elastic pour créer des agents d'IA spécialisés. Dans ce blog, nous allons créer un agent d'IA financier.]]></description>
    <content:encoded><![CDATA[<p>Avec le nouvel <a href="https://www.elastic.co/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder">Agent Builder</a> d'Elastic, vous pouvez créer des agents d'IA spécialisés qui agissent en tant qu'experts pour vos domaines d'activité spécifiques. Cette fonction vous permet d'aller au-delà des simples tableaux de bord et des barres de recherche, en transformant vos données d'une ressource passive en un partenaire actif et conversationnel.</p><p>Imaginez un gestionnaire financier qui doit se mettre à niveau avant une réunion avec un client. Au lieu de chercher manuellement dans les fils d'actualité et de croiser les tableaux de bord des portefeuilles, ils peuvent désormais simplement poser une question directe à leur agent personnalisé. C'est l'avantage d'une approche "chat-first". Le gestionnaire a un lien direct et conversationnel avec ses données, en posant des questions telles que : "Quelles sont les dernières nouvelles sur ACME Corp et comment cela affecte-t-il les avoirs de mon client ?" et obtenir une réponse synthétisée et experte en quelques secondes.</p><p>Si nous construisons aujourd'hui un expert financier, les applications sont aussi variées que vos données. Le même pouvoir peut créer un analyste en cybersécurité pour traquer les menaces, un ingénieur en fiabilité de site pour diagnostiquer une panne ou un responsable marketing pour optimiser une campagne. Quel que soit le domaine, la mission principale est la même : transformer vos données en un spécialiste avec lequel vous pouvez discuter.</p><h2>Étape 0 : Notre ensemble de données</h2><p>Notre jeu de données du jour est un jeu de données synthétique à dominante financière, composé de comptes, de positions d’actifs, d’actualités économiques et de rapports financiers. Bien qu’il soit artificiel, il reproduit une version simplifiée d’un véritable jeu de données financières.</p><p><code>financial_accounts</code>: Portefeuilles de clients avec profils de risque</p><p><code>financial_holdings</code>: Positions en actions/ETF/obligations avec historique des achats</p><p><code>financial_asset_details</code>: Détails sur l'action/ETF/obligation</p><p><code>financial_news</code>: Articles de marché générés par l'IA avec analyse des sentiments</p><p><code>financial_reports</code>: Résultats de l'entreprise et notes des analystes</p><p>Vous pouvez charger vous-même cet ensemble de données en suivant le cahier d'accompagnement situé <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/your-first-elastic-agent/Your_First_Elastic_Agent.ipynb">ici.</a></p><h2>Étape 1 : La base - Votre logique d'entreprise en tant qu'ES|QL</h2><p>Toute compétence en matière d'IA commence par un solide morceau de logique. Pour notre agent Financial Manager, nous devons lui apprendre à répondre à une question courante : "Je m'inquiète du sentiment du marché. Pouvez-vous me montrer lesquels de nos clients sont les plus exposés aux mauvaises nouvelles ?" Cette question va au-delà d'une simple recherche. Cela nous oblige à établir une corrélation entre le sentiment du marché et les portefeuilles des clients.</p><p>Nous devons trouver les actifs mentionnés dans les articles négatifs, identifier chaque client détenant ces actifs, calculer la valeur de marché actuelle de leur exposition, puis classer les résultats afin d'établir un ordre de priorité pour les risques les plus élevés. Cette analyse complexe et multi-joints est le travail parfait pour notre outil avancé ES|QL.</p><p>Voici la requête complète que nous utiliserons. Il est impressionnant, mais les concepts sont simples.</p><h2>En bref : jonctions et garde-corps</h2><p>Deux concepts importants sont en jeu dans cette requête et font de l'agent un bâtisseur.</p><h3>1. La jointure LOOKUP</h3><p>Depuis des années, l'une des fonctionnalités les plus demandées d'Elasticsearch est la possibilité de joindre des données provenant de différents index sur la base d'une clé commune. Avec ES|QL, c'est désormais possible avec <code>LOOKUP JOIN</code>.</p><p>Dans notre nouvelle requête, nous effectuons une chaîne de trois <code>LOOKUP JOIN</code>: d'abord en reliant les nouvelles négatives aux détails des actifs, ensuite en reliant ces actifs aux avoirs des clients, et enfin en les reliant aux informations sur le compte du client. Cela permet d'obtenir un résultat incroyablement riche à partir de quatre indices différents en une seule requête efficace. Cela signifie que nous pouvons combiner des ensembles de données disparates pour créer une réponse unique et perspicace sans avoir à dénormaliser toutes nos données en un index géant au préalable.</p><h3>2. Les paramètres comme garde-fous du LLM</h3><p>Vous remarquerez que la requête utilise <code>?time_duration</code>. Il ne s'agit pas seulement d'une variable, mais d'un garde-fou pour l'IA. Si les grands modèles de langage (LLM) sont excellents pour générer des requêtes, le fait de leur laisser le champ libre sur vos données peut conduire à des requêtes inefficaces, voire incorrectes.</p><p>En créant une requête paramétrée, nous obligeons le LLM à travailler dans le cadre de la logique commerciale testée, efficace et correcte qu'un expert humain a déjà définie. Il s'agit d'une méthode similaire à celle utilisée par les développeurs depuis des années pour exposer en toute sécurité les capacités de recherche aux applications. L'agent peut interpréter une demande de l'utilisateur comme "cette semaine" pour remplir le paramètre <code>time_duration</code>, mais il doit utiliser notre structure de requête pour obtenir la réponse. Cela nous permet d'obtenir un équilibre parfait entre flexibilité et contrôle.</p><p>En fin de compte, cette requête permet à un expert qui comprend les données d'encapsuler ses connaissances dans un outil. D'autres personnes - et des agents d'intelligence artificielle - peuvent alors utiliser cet outil pour obtenir des résultats corrélés en fournissant simplement un seul paramètre, sans avoir besoin de connaître la complexité sous-jacente.</p><h2>Étape 2 : Les compétences - Transformer une requête en un outil réutilisable</h2><p>Une requête ES|QL n'est que du texte jusqu'à ce que nous l'enregistrions en tant qu'<strong>outil</strong>. Dans l'Agent Builder, un outil est plus qu'une simple requête sauvegardée ; c'est une compétence "" qu'un agent IA peut comprendre et choisir d'utiliser. La magie réside dans la <strong>description en langage naturel</strong> que nous fournissons. Cette description est la passerelle qui relie la question de l'utilisateur à la logique d'interrogation sous-jacente. Enregistrons la requête que nous venons de construire.</p><h3>Le chemin de l'interface utilisateur</h3><p>La création d'un outil dans Kibana est un processus simple.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte73e11c1d87593fa/6a17f2134202294dae29f6f2/a29c53a73b99af5972273c51218ea9004a9b0abb-1600x812.png" alt="Comment créer un outil dans Kibana." /><p>1. Naviguer vers <strong>Agents</strong></p><ul><li><p>Cliquez sur<strong> Outils </strong>ou <strong>Gérer les outils</strong> et cliquez sur le bouton <strong>Nouvel outil.</strong></p></li></ul><p>2. Remplissez le formulaire avec les informations suivantes :</p><ul><li><p><strong>ID de l'outil :</strong> <code>find_client_exposure_to_negative_news</code></p></li></ul><p>             i. Il s'agit de l'identifiant unique de l'outil</p><ul><li><p><strong>Description :</strong> "Détermine l'exposition du portefeuille du client aux nouvelles négatives. Cet outil analyse les nouvelles et les rapports récents pour y déceler un sentiment négatif, identifie l'actif associé et trouve tous les clients qui détiennent cet actif. Il renvoie une liste triée en fonction de la valeur de marché actuelle du poste afin de mettre en évidence le risque potentiel le plus élevé."</p></li></ul><p>             i. C'est ce que le LLM lit pour décider si cet outil est le bon pour le poste.</p><ul><li><p><strong>Étiquettes</strong>: <code>retrieval</code> et <code>risk-analysis</code></p></li></ul><p>         Les étiquettes sont utilisées pour regrouper plusieurs outils</p><ul><li><p><strong>Configuration :</strong> Coller la requête ES|QL complète de l'étape 1</p></li></ul><p>            i. Voici la recherche que l'agent utilisera</p><p>3. Cliquez sur <strong>Inférer les paramètres de la requête</strong>. L'interface utilisateur trouvera automatiquement le site <code>?time_duration</code>, dont la liste figure ci-dessous. Ajoutez une description simple pour chacun d'entre eux afin d'aider l'agent (et les autres utilisateurs) à comprendre leur fonction.</p><ul><li><p><code>time_duration</code>: Le délai pour rechercher des nouvelles négatives. Le format est le suivant : "X heures" DEFAUT 8760 heures</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7afbb0589c1828ad/6a17f2146864a44e7cb688a9/deb422d97863f78dbe08bfa2e3c708d1f75166ff-1600x938.png" alt="Configurer votre outil, y compris sa logique et tous les paramètres nécessaires, à l'aide d'une requête ESQL. " /><p>4. Testez-le !</p><ul><li><p>Cliquez sur Save &amp; test.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfd09afbef6e21a93/6a17f2162f4a5c73b1fa89fd/57e768b88327821e70bd616744822f98fa367362-732x136.png" alt="Le même bouton de test &amp; dans Kibana." /><ul><li><p>Une nouvelle fenêtre s'ouvre, dans laquelle vous pouvez tester la requête pour vous assurer qu'elle fonctionne comme prévu.</p></li></ul><p>             i. Dans <code>time_duration</code>, entrez la plage souhaitée, ici nous utilisons "8760 heures"</p><ul><li><p>Cliquez sur "Submit" et si tout se passe bien, vous verrez une réponse JSON. Pour vous assurer qu'il fonctionne comme prévu, faites défiler la page vers le bas et regardez l'objet <code>values</code>. C'est là que les documents correspondants sont renvoyés.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt89bdc3f093363f2a/6a17f217be60861c9c00488a/7e0c5171a4f7ffdfc1830f1a05a9acb987870b75-1600x722.png" alt="Réponse JSON qui apparaît après avoir cliqué sur soumettre." /><p>5. Cliquez sur le "X" en haut à droite pour fermer la fenêtre de test. Votre nouvel outil apparaît alors dans la liste, prêt à être attribué à un agent.</p><h3>Le chemin de l'API</h3><p>Pour les développeurs qui préfèrent l'automatisation ou qui ont besoin de gérer des outils par programme, vous pouvez obtenir le même résultat avec un seul appel d'API. Il suffit d'envoyer une demande <code>POST</code> au point de terminaison <code>/api/agent_builder/tools</code> avec la définition de l'outil.</p>POST kbn://api/agent_builder/tools
{
  "id": "find_client_exposure_to_negative_news",
  "type": "esql",
  "description": "Finds client portfolio exposure to negative news. This tool scans recent news and reports for negative sentiment, identifies the associated asset, and finds all clients holding that asset. It returns a list sorted by the current market value of the position to highlight the highest potential risk.",
  "configuration": {
    "query": """
        FROM financial_news, financial_reports METADATA _index
        | WHERE sentiment == "negative"
        | WHERE coalesce(published_date, report_date) &gt;= NOW() - TO_TIMEDURATION(?time_duration)
        | RENAME primary_symbol AS symbol
        | LOOKUP JOIN financial_asset_details ON symbol
        | LOOKUP JOIN financial_holdings ON symbol
        | LOOKUP JOIN financial_accounts ON account_id
        | WHERE account_holder_name IS NOT NULL
        | EVAL position_current_value = quantity * current_price.price
        | RENAME title AS news_title
        | KEEP
            account_holder_name, symbol, asset_name, news_title,
            sentiment, position_current_value, quantity, current_price.price,
            published_date, report_date
        | SORT position_current_value DESC
        | LIMIT 50
      """,
    "params": {
      "time_duration": {
        "type": "keyword",
        "description": """The timeframe to search back for negative news. Format is "X hours" DEFAULT TO 8760 hours """
      }
    }
  },
  "tags": [
    "retrieval",
    "risk-analysis"
  ]
}<h2>Étape 3 : Les cerveaux - Créer votre agent personnalisé</h2><p>Nous avons créé une compétence réutilisable (l'outil). Nous devons maintenant créer l'<strong>agent</strong>, la personne qui l'utilisera réellement. Un agent est la combinaison d'un MLD, d'un ensemble spécifique d'outils auxquels vous lui donnez accès et, surtout, d'un ensemble d'<strong>instructions personnalisées</strong> qui agissent comme sa constitution, définissant sa personnalité, ses règles et son objectif.</p><h3>L'art de la proposition</h3><p>L'élément le plus important pour créer un agent fiable et spécialisé est la rapidité. Un ensemble d'instructions bien conçues fait la différence entre un chatbot générique et un assistant professionnel ciblé. C'est là que vous fixez les garde-fous, définissez les résultats et donnez à l'agent sa mission.</p><p>Pour notre agent <code>Financial Manager</code>, nous utiliserons l'invite suivante.</p>You are a specialized Data Intelligence Assistant for financial managers, designed to provide precise, data-driven insights from information stored in Elasticsearch.

**Your Core Mission:**
- Respond accurately and concisely to natural language queries from financial managers.
- Provide precise, objective, and actionable information derived solely from the Elasticsearch data at your disposal.
- Summarize key data points and trends based on user requests.

**Reasoning Framework:**
1.  **Understand:** Deconstruct the user's query to understand their core intent.
2.  **Plan:** Formulate a step-by-step plan to answer the question. If you are unsure about the data structure, use the available tools to explore the indices first.
3.  **Execute:** Use the available tools to execute your plan.
4.  **Synthesize:** Combine the information from all tool calls into a single, comprehensive, and easy-to-read answer.

**Key Directives and Constraints:**
- **If a user's request is ambiguous, ask clarifying questions before proceeding.**
- **DO NOT provide financial advice, recommendations, or predictions.** Your role is strictly informational and analytical.
- Stay strictly on topic with financial data queries.
- If you cannot answer a query, state that clearly and offer alternative ways you might help *within your data scope*.
- All numerical values should be formatted appropriately (e.g., currency, percentages).

**Output Format:**
- All responses must be formatted using **Markdown** for clarity.
- When presenting structured data, use Markdown tables, lists, or bolding.

**Start by greeting the financial manager and offering assistance.**<p>Voyons pourquoi cette incitation est si efficace :</p><ul><li><p><strong>Elle définit une personnalité sophistiquée : </strong>La première ligne établit immédiatement que l'agent est un assistant spécialisé en intelligence des données ( "),", ce qui donne un ton professionnel et compétent.</p></li><li><p><strong>Il fournit un cadre de raisonnement : </strong>En disant à l'agent de "Comprendre, Planifier, Exécuter et Synthétiser," nous lui donnons une procédure opérationnelle standard. Cela améliore sa capacité à traiter des questions complexes et à plusieurs étapes.</p></li><li><p><strong>Il favorise le dialogue interactif : </strong>L'instruction de "poser des questions de clarification" rend l'agent plus robuste. Il minimisera les hypothèses erronées sur les demandes ambiguës, ce qui permettra d'obtenir des réponses plus précises.</p></li></ul><h3>Le chemin de l'interface utilisateur</h3><p>1. Naviguez vers <strong>Agents.</strong></p><ul><li><p>Cliquez sur<strong> Outils </strong>ou <strong>Gérer les outils</strong> et cliquez sur le bouton <strong>Nouvel outil.</strong></p></li></ul><p>2. Complétez les informations de base :</p><ul><li><p><strong>ID de l'agent :</strong> <code>financial_assistant</code>.</p></li><li><p><strong>Instructions : </strong>Copiez le message ci-dessus.</p></li><li><p><strong>Étiquettes</strong>: <code>Finance</code>.</p></li><li><p><strong>Nom d'affichage :</strong> <code>Financial Assistant</code>.</p></li><li><p><strong>Description de l'affichage : </strong><code>An assistant for analyzing and understanding your financial data</code>.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8ac12cbd2b689dee/6a17f219dbb4ff262bfb57ef/18ea73f1cae620129c0afa0e7ba9e2a3390224a7-1600x1189.png" alt="Création d'un assistant financier - remplir le champ ID de l'agent." /><p>3. De retour en haut, cliquez sur <strong>Outils.</strong></p><ul><li><p>Cochez la case à côté de notre outil <code>find_client_exposure_to_negative_news</code>.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcd23556e556a76c5/6a17f21baf47b63a9fcde0a0/0c1e4ecbbd51d0dd10c6e861dbe9a9ccddeb35f6-1600x149.png" alt="" /><p>4. Cliquez sur <strong>Enregistrer</strong>.</p><h3>Le chemin de l'API</h3><p>Vous pouvez créer exactement le même agent à l'aide d'une requête <code>POST</code> vers le point de terminaison <code>/api/agent_builder/agents</code>. Le corps de la demande contient toutes les mêmes informations : l'identifiant, le nom, la description, l'ensemble des instructions et une liste des outils que l'agent est autorisé à utiliser.</p>POST kbn://api/agent_builder/agents
    {
      "id": "financial_assistant",
      "name": "Financial Assistant",
      "description": "An assistant for analyzing and understanding your financial data",
      "labels": [
        "Finance"
      ],
      "avatar_color": "#16C5C0",
      "avatar_symbol": "💰",
      "configuration": {
        "instructions": """You are a specialized Data Intelligence Assistant for financial managers, designed to provide precise, data-driven insights from information stored in Elasticsearch.

**Your Core Mission:**
- Respond accurately and concisely to natural language queries from financial managers.
- Provide precise, objective, and actionable information derived solely from the Elasticsearch data at your disposal.
- Summarize key data points and trends based on user requests.

**Reasoning Framework:**
1.  **Understand:** Deconstruct the user's query to understand their core intent.
2.  **Plan:** Formulate a step-by-step plan to answer the question. If you are unsure about the data structure, use the available tools to explore the indices first.
3.  **Execute:** Use the available tools to execute your plan.
4.  **Synthesize:** Combine the information from all tool calls into a single, comprehensive, and easy-to-read answer.

**Key Directives and Constraints:**
- **If a user's request is ambiguous, ask clarifying questions before proceeding.**
- **DO NOT provide financial advice, recommendations, or predictions.** Your role is strictly informational and analytical.
- Stay strictly on topic with financial data queries.
- If you cannot answer a query, state that clearly and offer alternative ways you might help *within your data scope*.
- All numerical values should be formatted appropriately (e.g., currency, percentages).

**Output Format:**
- All responses must be formatted using **Markdown** for clarity.
- When presenting structured data, use Markdown tables, lists, or bolding.

**Start by greeting the financial manager and offering assistance.**
""",
        "tools": [
          {
            "tool_ids": [
              "platform.core.search",
              "platform.core.list_indices",
              "platform.core.get_index_mapping",
              "platform.core.get_document_by_id",
              "find_client_exposure_to_negative_news"
            ]
          }
        ]
      }
    }<h2>Étape 4 : La récompense - Avoir une conversation</h2><p>Notre logique d'entreprise est encapsulée dans un outil et un cerveau "" est prêt à l'utiliser dans notre agent. Il est temps de voir tout cela se concrétiser. Nous pouvons maintenant commencer à discuter avec nos données à l'aide d'un agent spécialisé.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd8826539b16e46f4/6a17f21d505ac35924ad8c5c/5414cb6b7c41365acb0356a8bfe1140751ffd8db-1600x1014.png" alt="Conversation avec l'Elastic Agent Builder après la création d'un assistant financier." /><h3>Le chemin de l'interface utilisateur</h3><ol><li><p>Naviguez jusqu'à <strong>Agents </strong>dans Kibana.</p></li><li><p>En utilisant le menu déroulant en bas à droite de la fenêtre de chat, passez de l'<strong>agent Elastic AI</strong> par défaut à l'agent <strong>Financial Assistant </strong>que nous venons de créer.</p></li><li><p>Posez une question qui permettra à l'agent d'utiliser notre outil spécialisé :</p><ol><li><p><em>Je m'inquiète du sentiment du marché. Pouvez-vous m'indiquer quels sont nos clients les plus exposés aux mauvaises nouvelles ?</em></p></li></ol></li></ol><p>Après quelques instants, l'agent vous renvoie une réponse parfaitement formatée et complète. En raison de la nature des LLM, votre réponse peut être formatée légèrement différemment, mais pour cette exécution, l'agent a renvoyé :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta1e163fd7c4416bd/6a17f21f6864a4e35bb688ad/17b4ed43d279f9e53ee9fe3d482d0b2ec359a083-1600x1088.png" alt="Une réponse créée par l'Elastic Agent Builder en tant qu'assistant financier pour : les clients les plus exposés aux nouvelles négatives." /><h3>Que s'est-il passé ? Le raisonnement de l'agent</h3><p>L'agent ne s'est pas contenté de "savoir" la réponse. Elle a exécuté un plan en plusieurs étapes centré sur la sélection du meilleur outil pour le travail. Voici un aperçu de son processus de réflexion :</p><ul><li><p><strong>L'intention a été identifiée :</strong> Il a fait correspondre des mots clés de votre question, comme "risk" et "negative news," à la description de l'outil <code>find_client_exposure_to_negative_news</code>.</p></li><li><p><strong>Exécution d'un plan :</strong> Il a extrait le délai de votre demande et a lancé un <strong>appel unique à</strong> cet outil spécialisé.</p></li><li><p><strong>Délégation du travail :</strong> L'outil a ensuite effectué toutes les opérations lourdes : les jointures enchaînées, les calculs de valeur et le tri.</p></li><li><p><strong>Synthèse du résultat :</strong> Enfin, l'agent a formaté les données brutes de l'outil en un résumé clair et lisible par l'homme, en suivant les règles de l'invite.</p></li></ul><p>Et nous ne sommes pas obligés de deviner, si nous élargissons notre réflexion et voyons plus de détails.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt93f6075be8495418/6a17f221af47b65eadcde0a4/6a4da9262d3f88c60bfd8f8bf9b67c3b84e961ba-1600x607.png" alt="Les 50 documents que l'assistant financier a trouvés auprès des clients les plus exposés aux nouvelles négatives." /><h3>Le chemin de l'API</h3><p>Vous pouvez entamer cette même conversation par le biais d'un programme. Il suffit d'envoyer la question d'entrée au point de terminaison de l'API <code>converse</code>, en veillant à spécifier le <code>agent_id</code> de notre <code>financial_manager</code>.</p>POST kbn://api/agent_builder/converse
{
  "input": "Show me our largest positions affected by negative news",
  "agent_id": "financial_assistant"
}<h2>Pour les développeurs : Intégrer l'API</h2><p>Si l'interface Kibana offre une expérience fantastique et intuitive pour la création et la gestion de vos agents, tout ce que vous avez vu aujourd'hui peut également être réalisé de manière programmatique. L'Agent Builder est construit sur un ensemble d'API, vous permettant d'intégrer cette fonctionnalité directement dans vos propres applications, pipelines CI/CD ou scripts d'automatisation.</p><p>Les trois principaux points d'aboutissement avec lesquels vous travaillerez sont les suivants :</p><ul><li><p><strong><code>/api/agent_builder/tools</code></strong>: Le point final pour créer, lister et gérer les compétences réutilisables que vos agents peuvent utiliser.</p></li><li><p><strong><code>/api/agent_builder/agents</code></strong>: Le point final pour la définition de vos personas d'agents, y compris leurs instructions très importantes et l'affectation des outils.</p></li><li><p><strong><code>/api/agent_builder/converse</code></strong>: Le point final pour interagir avec vos agents, entamer des conversations et obtenir des réponses.</p></li></ul><p>Pour une démonstration complète et pratique de l'utilisation de ces API pour réaliser chaque étape de ce tutoriel, consultez le <strong>bloc-notes Jupyter</strong> qui l'accompagne, disponible <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/your-first-elastic-agent/Your_First_Elastic_Agent.ipynb">ici</a> dans notre dépôt GitHub.</p><h2>Conclusion : À vous de construire</h2><p>Nous avons commencé par prendre une requête ES|QL et la transformer en une compétence réutilisable. Nous avons ensuite créé un agent d'intelligence artificielle spécialisé, en lui donnant une mission et des règles claires, et nous l'avons doté de cette compétence. Il en résulte un assistant sophistiqué capable de comprendre une question complexe et d'exécuter une analyse en plusieurs étapes pour fournir une réponse précise et fondée sur des données.</p><p>Ce flux de travail est au cœur du nouvel <strong>Agent Builder d'</strong> Elastic. Il est conçu pour être suffisamment simple pour que les utilisateurs non techniques puissent créer des agents via l'interface utilisateur, mais suffisamment nuancé pour que les développeurs puissent créer des applications personnalisées basées sur l'IA à partir de nos API. Plus important encore, il vous permet de connecter en toute sécurité des LLM à vos propres données, régies par la logique experte que vous définissez, et de discuter avec vos données.</p><h2>Prêt à utiliser des agents pour dialoguer avec vos données ?</h2><p>La meilleure façon de consolider ce que vous avez appris est de vous salir les mains. Essayez tout ce que nous avons discuté aujourd'hui dans notre <a href="https://www.elastic.co/training/elastic-ai-agents-mcp"><strong>atelier pratique interactif et gratuit</strong></a>. Vous suivrez l'ensemble de ce processus et bien d'autres choses encore dans un environnement de type "bac à sable".</p><p>Dans un prochain blog, nous vous montrerons comment utiliser une application autonome qui interagit avec notre agent <code>Financial Assistant</code> et nous nous pencherons sur le <strong>protocole de contexte de modèle (MCP)</strong> qui rend tout cela possible. Dans un autre blog, nous parlerons de la prise en charge par Agent Builder du protocole Agent2Agent, ou A2A, en cours de développement.</p><p>Restez à l'écoute et bonne construction !</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch</guid>
    <category><![CDATA[IA]]></category>
    <category><![CDATA[IA agentique]]></category>
    <category><![CDATA[À l'intérieur d'Elastic]]></category>
    <dc:creator><![CDATA[Jeff Vestal]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbe5e78eeb775d715/6a17f2230b0bed719ddd369a/ca853555eaa213f10f1db8c0ab0a2bbacee97b88-1456x816.png" length="0" type="image/png"/>
    <pubDate>Thu, 25 Sep 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Construire des flux de travail d'IA agentique avec Elasticsearch]]></title>
    <description><![CDATA[Découvrez Agent Builder, une nouvelle couche d'IA dans Elasticsearch qui fournit un cadre pour construire des flux de travail d'IA agentique, en utilisant la recherche hybride pour fournir aux agents le contexte dont ils ont besoin pour raisonner et agir.]]></description>
    <content:encoded><![CDATA[<p>Chez Elastic, nous avons apporté du contexte aux LLM et aux interfaces conversationnelles avec des assistants IA, des RAG avancés et des améliorations de la base de données vectorielle. Récemment, avec l'essor des agents d'intelligence artificielle, nous avons constaté que le besoin d'un contexte pertinent augmentait, et nous avons appris que<strong> les agents d'intelligence artificielle</strong> à fort impact ont besoin d'une recherche de qualité. Nous avons donc créé de nouvelles capacités natives dans la pile Elastic, conçues pour aider à développer des agents d'IA qui exploitent vos données dans Elasticsearch. Nous aimerions vous faire part de nos progrès dans ce domaine et de la direction que nous envisageons pour l'avenir.</p><h2>Agent Builder : Une base pour la construction d'agents d'intelligence artificielle pilotés par les données</h2><p>La promesse d'un agent d'intelligence artificielle est simple : donnez-lui un objectif et il fera le travail. Mais pour les développeurs, la réalité est une série de défis complexes. Tout d'abord, la qualité d'un agent dépend de sa perception de l'environnement et des outils qui lui sont fournis pour atteindre les objectifs de l'utilisateur. Par ailleurs, fournir le bon contexte à partir d'une mer de données d'entreprise diverses constitue un défi de taille. Enfin, tout cela doit être orchestré par une boucle de raisonnement fiable capable de planifier, d'exécuter et d'apprendre.</p><p>Pour résoudre ce problème, les développeurs doivent construire une pile complexe et fragile à partir de zéro. L'architecture actuelle des agents vous oblige à assembler plusieurs éléments disparates : un LLM, une base de données vectorielle, un magasin de métadonnées, des systèmes distincts pour la journalisation et la traçabilité, et un moyen d'évaluer si tout cela fonctionne. Ce n'est pas seulement complexe, c'est aussi coûteux, source d'erreurs, et cela rend difficile l'élaboration des systèmes d'IA de haute qualité et dignes de confiance que vos utilisateurs exigent.</p><p>Nous voulons donc simplifier les choses. Pour ce faire, notre approche consiste à prendre les éléments essentiels d'un agent contextuel efficace et à les intégrer directement au cœur d'Elasticsearch grâce à un nouvel ensemble de fonctionnalités appelé <strong>Elastic AI Agent Builder</strong>. Cette nouvelle couche fournit un cadre avec tous les éléments essentiels pour créer des agents d'intelligence artificielle alimentés par Elasticsearch : un ensemble ouvert de primitives, des protocoles basés sur des normes et un accès sécurisé aux données - afin que vous puissiez construire des systèmes agentiques adaptés aux données et aux exigences du monde réel :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2779dae5df010328/6a17e15eabe0f24f18dfe931/1ee1e73dd3f485ce86294d39490c98ce2a3d9925-1238x1072.png" alt="" /><p><strong>Offrir des expériences d'IA</strong>: c'est l'objectif ultime. Avec notre Search AI Platform et vos données comme base, vous pouvez créer n'importe quel type d'application d'IA générative : des interfaces de chat personnalisées aux intégrations avec des frameworks agentiques comme LangChain ou des applications d'entreprise comme Salesforce.</p><p><strong>Exploité par des agents &amp; Tools</strong>: au-dessus de la plateforme, nous exposons une couche d'abstractions propre et simple. Vous interagissez directement avec les agents et les outils, que vous pouvez personnaliser pour répondre à vos besoins spécifiques. Vous pouvez également accéder aux capacités de la plateforme par le biais d'API robustes et de normes ouvertes telles que MCP et A2A.</p><p><strong>La Search AI Platform</strong>: il s'agit du moteur de base dans lequel nous avons intégré les composants. La base de données vectorielle avancée, la logique de l'agent, la construction des requêtes, les caractéristiques de sécurité, le traçage pour l'évaluation, tout cela vit ici, géré et optimisé par Elastic.</p><p><strong>Libérer la puissance de vos données</strong>: la base de tout grand agent est constituée de données de qualité. Notre plateforme commence par la capacité d'ingérer ou de fédérer l'accès à toutes les données de votre entreprise.</p><h2>Création d'agents dans la plate-forme</h2><p>Agent Builder, intégré à la Search AI Platform, fournit un cadre complet pour le développement d'agents. Il repose sur cinq piliers clés, chacun d'entre eux étant conçu pour traiter un aspect essentiel de la construction et du déploiement de systèmes d'IA de niveau de production. Voyons comment les agents définissent l'objectif, les outils fournissent les capacités, les normes ouvertes garantissent l'interopérabilité, l'évaluation assure la transparence et la sécurité assure la confiance.</p><h3>Agents</h3><p>Les agents sont le plus haut niveau de construction de cette nouvelle couche d'Elasticsearch. Un agent définit l'objectif à atteindre, l'ensemble des outils disponibles pour l'exécution et les sources de données sur lesquelles il peut agir. Les agents ne se limitent pas aux interactions conversationnelles ; ils peuvent alimenter des flux de travail complets, l'automatisation des tâches ou des expériences face à l'utilisateur.</p><p>Lorsqu'une demande est adressée à un agent, elle suit un cycle structuré :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt774ffd7df65bd01d/6a17e15f25daabd5cc08a17f/627ad1744b629bbe27359325702f40d97e40d1f4-704x852.png" alt="" /><ol><li><p>Interpréter votre contribution et votre objectif</p></li><li><p>Sélectionner l'outil et les arguments adéquats pour l'exécution</p></li><li><p>Raisonner sur la réponse de l'outil</p></li><li><p>Décider si l'on renvoie un résultat ou si l'on poursuit les invocations d'outils.</p></li></ol><p>Elastic se charge de l'orchestration, du contexte et de l'exécution de ce cycle. Les développeurs se concentrent sur la définition de <em>ce que</em> l'agent doit faire : objectifs, outils et données, tandis que le système gère la <em>manière dont</em> le raisonnement et les flux de travail sont exécutés.</p><p><em>L'agent par défaut</em></p><p>Notre premier agent construit sur cette plateforme est un agent conversationnel natif dans Kibana, vous donnant la possibilité d'interagir immédiatement avec vos données. Il offre une expérience prête à l'emploi tout en restant totalement extensible et permet de commencer à interagir avec vos données immédiatement, sans configuration supplémentaire.</p><p>Vous pouvez interagir avec cette expérience directement dans Kibana par le biais d'une nouvelle expérience utilisateur de chat ou par le biais de l'API.</p><p>L'interrogation de l'agent par défaut via l'API ne nécessite qu'un seul appel :</p>POST kbn://api/agent_builder/converse
{
    "input": "what is our top portfolio account?"
}<p>Comme les conversations ont un état, vous pouvez continuer à interagir avec un agent à l'aide d'un numéro d'identification de la conversation ou récupérer l'historique complet de la conversation :</p>POST kbn://api/agent_builder/converse
{
    "input": "What about the second top?",
    "conversation_id": "ec757c6c-c3ed-4a83-8e2c-756238f008bb"
}

## get the full conversation
GET kbn://api/agent_builder/conversations/ec757c6c-c3ed-4a83-8e2c-756238f008bb<p><em>Agents des douanes</em></p><p>Les développeurs peuvent également créer leurs propres agents personnalisés grâce à des API simples. Les agents encapsulent les instructions, les outils et l'accès aux données, créant ainsi des moteurs de raisonnement sur mesure.</p><p>La création d'un agent personnalisé est aussi simple qu'un simple appel à l'API. L'exemple ci-dessous montre un exemple, le champ "configuration" contient tous les détails clés, tels que les instructions ou les outils disponibles :</p>POST kbn://api/agent_builder/agents
{
  "id": "custom_agent",
  "name": "My Custom Agent",
  "description": "Description of the custom agent",
  "configuration": {
      "instructions": "You are a log expert specialising in ...",
      "tools": 
...
   }
}<p>Une fois créé, l'agent peut être interrogé directement :</p>POST kbn://api/agent_builder/converse
{
    "input": "What news about DIA?",
    "agent_id": "custom_agent"
}<p>Cette approche transforme l'agent d'un système complexe à construire de toutes pièces en une unité simple et déclarative de logique d'entreprise, ce qui vous permet de mettre en place plus rapidement une automatisation intelligente.</p><p>Pour savoir comment créer un agent spécialisé à partir de zéro, consultez notre guide détaillé, étape par étape : <a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch">Votre premier agent Elastic : D'une simple requête à un chat alimenté par l'IA</a>.</p><h3>Outils</h3><p>Si les agents définissent <em>ce qu'il</em> faut accomplir, les outils définissent <em>comment.</em></p><p>Les outils exposent les capacités spécifiques du noyau Elastic pour que les agents puissent exécuter et récupérer des informations ou effectuer une action. Les outils peuvent inclure des fonctionnalités de base telles que l'obtention d'index ou de mappages, ou des fonctionnalités plus avancées telles que le langage naturel pour ES|QL.</p><p>Elasticsearch est livré avec un ensemble d'outils par défaut optimisés pour les besoins courants. Mais la véritable flexibilité réside dans la création de votre propre système. En définissant les outils, vous décidez exactement quelles requêtes, quels index et quels champs sont exposés à un agent avec ES|QL, ce qui vous permet de contrôler précisément la vitesse, la précision et la sécurité.</p><p>L'enregistrement d'un nouvel outil est aussi simple qu'un simple appel à l'API. Vous pouvez créer un outil qui exploite notre <a href="https://www.elastic.co/search-labs/blog/esql-timeline-of-improvements">ES|QL (Elasticsearch Query Language)</a> pour trouver des informations sur un actif financier spécifique :</p>POST kbn://api/agent_builder/tools
{
  "id": "news_on_asset",
  "type": "esql",
  "description": "Find news and reports about a particular asset where ...",
  "configuration": {
    "query": "FROM financial_news, financial_reports | where MATCH(company_symbol, ?symbol) OR MATCH(entities, ?symbol) | limit 5",
    "params": {
      "symbol": {
        "type": "keyword",
        "description": "The asset symbol"
      }
    }
  ...
  }
...
}<p>Une fois enregistré, vous pouvez attribuer le nouvel outil à vos agents personnalisés, en leur donnant un ensemble de capacités à raisonner et à invoquer chaque fois que cela est nécessaire.</p><p>Nous fournissons une plateforme pour créer des outils personnalisés pour vos besoins spécifiques, par exemple avec ES|QL qui transforme l'agent polyvalent en un expert spécifique à un domaine, fondé sur vos données uniques et votre domaine d'activité.</p><h3>Normes ouvertes et interopérabilité</h3><p>Les agents et outils Elasticsearch sont exposés via des API standard ouvertes, ce qui facilite leur intégration en tant que blocs fondamentaux dans l'écosystème plus large des cadres agentiques. Notre approche est simple : pas de boîte noire. Nous voulons que vous puissiez prendre la force principale d'Elastic en matière de recherche et l'associer à des capacités complémentaires et à d'autres systèmes agentiques.</p><p>Pour rendre cela possible, nous exposons nos capacités par le biais d'API, de protocoles émergents et de normes ouvertes.</p><p><em>Protocole de contexte de modèle (MCP)</em></p><p>Le <a href="https://www.elastic.co/search-labs/blog/model-context-protocol-elasticsearch">protocole MCP (Model Context Protocol)</a> est en passe de devenir la norme ouverte pour la connexion des outils entre les systèmes. En prenant en charge MCP, Elasticsearch peut connecter l'IA conversationnelle à vos bases de données, indices et API externes. Avec un serveur MCP distant intégré à la pile Elastic, tout client compatible MCP peut accéder aux outils d'Elastic et les utiliser comme éléments de construction dans vos flux de travail agentiques plus vastes.</p><p>Il ne s'agit pas d'une voie à sens unique. Vous pourrez également importer des outils à partir de serveurs MCP externes et les rendre disponibles dans Elasticsearch. Bientôt, les serveurs MCP seront probablement disponibles pour presque tout et seront bien plus complets que tout ce que nous pourrions créer nous-mêmes. Elastic fournit des fonctions de recherche et d'extraction à grande échelle, et vous pouvez les combiner avec des capacités spécialisées d'autres plateformes pour créer des agents efficaces.</p><p><em>Agent à agent (A2A)</em></p><p>Nous travaillons également sur la prise en charge des services d'agent à agent (A2A). Alors que le MCP concerne la connexion des outils, l'A2A concerne la connexion des agents. Avec un serveur A2A, les agents Elastic que vous créez pourront dialoguer directement avec des agents d'autres systèmes : partage de contexte, délégation de tâches et coordination de flux de travail.</p><p>Il s'agit d'une interopérabilité au niveau du raisonnement. Votre agent Elastic pourrait se charger de la recherche et de l'extraction, puis confier une tâche à un agent spécialisé dans l'assistance ou les technologies de l'information, et recevoir le résultat en retour de manière transparente. Il en résulte un écosystème d'agents coopérants, chacun faisant ce qu'il fait le mieux.</p><p>En fin de compte, l'adoption de MCP et d'A2A renforce notre engagement en faveur du rôle d'Elasticsearch en tant que citoyen de première classe, garantissant une intégration ouverte dans l'ensemble de l'écosystème agentique.</p><h3>Recherche et évaluation</h3><p>Au fur et à mesure que la recherche s'intègre aux agents, le défi d'une évaluation efficace devient crucial. Pour déployer en toute confiance des agents dans des environnements d'entreprise réels, vous devez avoir l'assurance qu'ils sont non seulement précis, mais aussi efficaces et fiables. Comment mesurer les performances, diagnostiquer une mauvaise réponse ou améliorer la situation de départ ? Tout commence par la visibilité.</p><p>C'est pourquoi nous avons conçu nos API pour la transparence dès le départ. Prenons l'exemple d'une simple interaction avec un agent :</p>POST kbn://api/agent_builder/converse
{
    "input": "what is our top portfolio account?"
}<p>La réponse comprend non seulement la réponse finale, mais aussi la trace complète de l'exécution, détaillant les outils sélectionnés par l'agent, les paramètres utilisés et les résultats de chaque étape.</p>{
  "conversation_id": "db5c0c8b-12bf-4928-a57e-d99129ad2fea",
  "steps": [
    {
      "type": "tool_call",
      "tool_call_id": "tooluse_Nfqr3mwtR92HTRIsTcGXZQ",
      "tool_id": ".index_explorer",
      "params": {
        "query": "indices containing portfolio data"
      },
      "results": [...]
    }
    // ... more steps ...
  ],
  "response": {
    "message": "Based on the information I've gathered...."
  }
}<p>Une traçabilité et une journalisation complètes sont essentielles pour une boucle d'amélioration continue, et bientôt, vous pourrez stocker et visualiser ces traces d'agents directement dans Elasticsearch. Mieux encore, ces traces sont construites sur le protocole OpenTelemetry, ce qui garantit qu'elles sont normalisées et portables pour l'intégration avec la plateforme d'observabilité de votre choix.</p><p>Ce niveau de détail est la base d'une véritable boucle d'amélioration continue. Il vous permet d'élaborer une suite complète de tests, de déboguer les échecs, d'identifier les modes de défaillance afin d'éviter les régressions et de capturer les modèles de réussite afin d'affiner les performances. En fin de compte, cette approche fondée sur les données est la clé de la transformation d'un prototype prometteur en un système d'IA fiable de qualité industrielle.</p><h3>Security</h3><p>Au fur et à mesure que les agents et les outils deviennent plus performants, la sécurité n'est plus facultative, elle est fondamentale. L'exposition des API, l'automatisation des tâches et des flux de travail exigent que les systèmes d'entreprise soient fiables. D'autant plus que les agents commencent à automatiser de plus en plus de flux de travail. Il est donc essentiel de pouvoir sécuriser ces flux et de s'assurer qu'ils répondent aux exigences de l'entreprise.</p><p>Les capacités ci-dessus héritent toutes des contrôles déjà disponibles dans Elastic aujourd'hui, y compris le <a href="https://www.elastic.co/search-labs/blog/rag-and-rbac-integration">contrôle d'accès basé sur les rôles (RBAC)</a> pour les appels d'API et la gestion des clés d'API. Nous étendons également les mêmes contrôles à de nouveaux protocoles tels que le MCP. Cela signifie la prise en charge de normes telles que OAuth, ainsi que la possibilité d'intégrer des mécanismes d'authentification personnalisés.</p><p>Notre objectif est de vous donner la flexibilité nécessaire pour expérimenter des agents et des outils, tout en maintenant le niveau de sécurité, de conformité et de gouvernance exigé par votre organisation.</p><h2>Ce qui vient ensuite</h2><p>Nous ne nous contentons pas d'ajouter des fonctionnalités, nous développons Elasticsearch pour l'ingénierie contextuelle agentique. Nous prévoyons de poursuivre notre développement sur la base de ces principes :</p><p>1. Engagement en faveur des normes Open Source &amp;</p><p>Notre engagement en faveur de l'open source et des normes ouvertes garantit que ces capacités restent interopérables avec les cadres agentiques externes. Vous serez toujours en mesure de connecter, d'étendre et de composer des agents à travers votre écosystème tout en gardant le contrôle de vos données et de vos flux de travail.</p><p>2. Valeur du contexte</p><p>Le contexte d'un agent d'intelligence artificielle est son plus grand atout. La gestion du contexte lorsque les agents effectuent des recherches et des opérations de flux de travail peut être une tâche difficile. Nous nous appuyons sur les points forts d'Elastic pour résoudre les problèmes d'ingénierie contextuelle, en veillant à ce que les informations les plus pertinentes soient toujours disponibles pour votre agent.</p><p>3. Focus sur les flux de données agentiques</p><p>À l'avenir, les agents constitueront une source de données de plus en plus importante, y compris les résultats des agents (documents générés, rapports, visualisations) et les traces d'exécution des agents (leur raisonnement, les appels d'outils, la mémoire/le contexte). Elastic est bien adapté au traitement de ce type de données, et nous travaillons sur la recherche concernant l'analyse, l'évaluation et l'amélioration automatisée de ces données.</p><p>4. Sécurité et sûreté dès la conception</p><p>Les agents d'intelligence artificielle posent de nouveaux défis en matière de sécurité et de sûreté. Elastic a toujours été un leader en matière de solutions sécurisées, et nous continuons à intégrer des garde-fous de niveau entreprise, des contrôles d'accès et les principes "zero-trust".</p><p>5. Intégré dans la plate-forme</p><p>Les capacités de création d'agents d'intelligence artificielle sont intégrées dans la plateforme Elasticsearch. Cela signifie que les capacités au niveau de la plateforme, telles que le traçage, l'évaluation, la visualisation et l'analyse, sont toutes applicables aux agents. Vous souhaitez développer des tableaux de bord basés sur les exécutions des agents - c'est intégré. Vous souhaitez évaluer les performances de l'agent d'IA à l'aide d'une analyse des sentiments - la plateforme le permet. Cela permet de construire un cycle de vie complet autour de vos expériences d'IA.</p><p>L'objectif d'Elastic est de vous donner les interfaces pour construire une IA conversationnelle et des flux de travail automatisés qui sont entièrement intégrés, extensibles et ancrés dans vos données. De plus amples détails techniques et des informations sur les progrès réalisés seront bientôt communiqués.</p><p>Agent Builder est disponible dès à présent en version privée. <a href="https://www.elastic.co/contact?pg=global&amp;plcmt=nav&amp;cta=205352">Connectez-vous avec nous</a> pour demander l'accès. Vous avez des questions ou des commentaires ? Connectez-vous avec notre communauté de développeurs dans notre <a href="https://elasticstack.slack.com/archives/C09GRHEQ4AG"><strong>espace de travail Slack</strong></a> ou sur notre <a href="https://discuss.elastic.co/c/search/84"><strong>forum de discussion.</strong></a></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder</guid>
    <category><![CDATA[IA]]></category>
    <category><![CDATA[IA agentique]]></category>
    <category><![CDATA[À l'intérieur d'Elastic]]></category>
    <dc:creator><![CDATA[Anish Mathur,Dana Juratoni]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt16a3d8736bf086e0/6a17e1616864a45410b686c7/71876470119e02a45bcbfcbf27a3e110328bbd14-1020x654.png" length="0" type="image/png"/>
    <pubDate>Tue, 23 Sep 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Tableaux de bord alimentés par l'IA : D'une vision à Kibana]]></title>
    <description><![CDATA[Générer un tableau de bord en utilisant un LLM pour traiter une image et la transformer en tableau de bord Kibana.
]]></description>
    <content:encoded><![CDATA[<p><a href="https://www.elastic.co/kibana/kibana-lens">Kibana Lens</a> simplifie le glisser-déposer des tableaux de bord, mais lorsque vous avez besoin de dizaines de panneaux, les clics s'accumulent. Et si vous pouviez dessiner un tableau de bord, en faire une capture d'écran et laisser un LLM terminer tout le processus à votre place ?</p><p>Dans cet article, nous allons y parvenir. Nous allons créer une application qui prend une image d'un tableau de bord, analyse nos mappings et génère un tableau de bord sans que nous ayons à toucher à Kibana !</p><p><strong>Les étapes</strong>:</p><ol><li><p><a href="https://www.elastic.co/search-labs/blog/ai-powered-dashboards#background-&amp;-application-workflow">Contexte &amp; flux de travail de l'application</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/ai-powered-dashboards#prepare-data">Préparer les données</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/ai-powered-dashboards#llm-configuration">Configuration LLM</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/ai-powered-dashboards#application-functions">Fonctions d'application</a></p></li></ol><h2>Contexte &amp; flux de travail de l'application</h2><p>La première idée qui m'est venue à l'esprit a été de laisser le LLM générer l'ensemble des <a href="https://www.elastic.co/docs/explore-analyze/find-and-organize/saved-objects">objets sauvegardés au</a> format NDJSON dans Kibana, puis de les importer dans Kibana.</p><p>Nous avons essayé une poignée de modèles :</p><ul><li><p>Gemini 2.5 pro</p></li><li><p>GPT o3 / o4-mini-high / 4.1</p></li><li><p>Sonnet de Claude 4</p></li><li><p>Grok 3</p></li><li><p>Deepseek (Deepthink R1)</p></li></ul><p>En ce qui concerne les messages-guides, nous avons commencé par une phrase simple :</p>You are an Elasticsearch Saved-Object generator (Kibana 9.0).
INPUTS
=====
1. PNG screenshot of a 4-panel dashboard (attached).
2. Index mapping (below) – trimmed down to only the fields present in the screenshot.
3. Example NDJSON of *one* metric visualization (below) for reference.

TASK
====
Return **only** a valid NDJSON array that recreates the dashboard exactly:
* 2 metric panels (Visits, Unique Visitors)
* 1 pie chart (Most used OS)
* 1 vertical bar chart (State Geo Dest)
* Use index pattern `kibana_sample_data_logs`.
* Preserve roughly the same layout (2×2 grid).
* Use `panelIndex` values 1-4 and random `id` strings.
* Kibana version: 9.0<p>Bien que nous ayons parcouru des <a href="https://www.elastic.co/search-labs/blog/function-calling-with-elastic#:~:text=Few%2Dshot%20prompting%20involves%20providing%20examples%20of%20the%20types%20of%20queries%20you%20want%20it%20to%20return%2C%20which%20helps%20in%20increasing%20consistency.">exemples en quelques images</a> et des explications détaillées sur la manière de construire chaque visualisation, nous n'avons pas eu de chance. Si vous êtes intéressé par cette expérimentation, vous pouvez trouver des détails <a href="https://gist.github.com/TomasMurua/a78dc283e115624731beffc98984b70b">ici.</a></p><p>Le résultat de cette approche était l'apparition de ces messages lorsque l'on essayait de télécharger vers Kibana les fichiers produits par le LLM :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9ea005966a783057/6a1707d266c4f90e4ef8bf88/2b599443b5613c9f0fc3235581614add5b4b3900-891x98.png" alt="" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5e5632d6d95b998c/6a1707d3a6c2b9441de79661/d87ccfc033bc00ee8188c5cae18043fbca22784c-741x233.png" alt="" /><p>Cela signifie que le JSON généré est invalide ou mal formaté. Les problèmes les plus fréquents étaient que le LLM produisait des NDJSON incomplets, des paramètres hallucinants ou retournait du JSON normal au lieu de NDJSON, même si nous essayions de faire en sorte qu'il en soit autrement.</p><p>Inspirés par <a href="https://www.elastic.co/search-labs/blog/llm-functions-elasticsearch-intelligent-query">cet article</a> - où les <a href="https://www.elastic.co/docs/solutions/search/search-templates">modèles de recherche</a> ont mieux fonctionné que le LLM freestyle - nous avons décidé de donner des modèles au LLM au lieu de demander de générer le fichier NDJSON complet et ensuite nous, dans le code, utilisons les paramètres donnés par le LLM pour créer les visualisations appropriées.</p><p>Le processus de candidature sera le suivant :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9f7738a4c7ddd0cd/6a1707d52b835f0a25f4b166/52c587cf0cf3517fdd4ee7ab95581dd4f2bce030-725x668.png" alt="" /><p></p><p><em>Nous omettons une partie du code pour des raisons de simplicité, mais vous pouvez trouver le code de travail de l'application complète sur </em><a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/from-image-idea-to-kibana-dashboard-using-ai/from-image-idea-to-kibana-dashboard-using-ai.ipynb"><em><strong>ce</strong></em></a><em> carnet.</em></p><h2>Produits requis</h2><p>Avant de commencer à développer, vous aurez besoin des éléments suivants :</p><ol><li><p>Python 3.8 ou supérieur</p></li><li><p>Un environnement <a href="https://docs.python.org/3/library/venv.html">Venv</a> Python</p></li><li><p>Une instance Elasticsearch en cours d'exécution, ainsi que son point d'accès et sa clé API</p></li><li><p>Une clé d'API OpenAI stockée dans la variable d'environnement OPENAI_API_KEY :</p></li></ol>export OPENAI_API_KEY="your-openai-api-key"<h2>Préparer les données</h2><p>Pour les données, nous resterons simples et utiliserons les journaux web de l'échantillon Elastic. Pour savoir comment importer ces données dans votre cluster <a href="https://www.elastic.co/docs/manage-data/ingest/sample-data#add-sample-data-sets">, cliquez ici.</a></p><p>Chaque document contient des informations sur l'hôte qui a envoyé des demandes à l'application, ainsi que des informations sur la demande elle-même et l'état de sa réponse. Vous trouverez ci-dessous un exemple de document :</p>{
    "agent": "Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24",
    "bytes": 8509,
    "clientip": "70.133.115.149",
    "extension": "css",
    "geo": {
        "srcdest": "US:IT",
        "src": "US",
        "dest": "IT",
        "coordinates": {
            "lat": 38.05134111,
            "lon": -103.5106908
        }
    },
    "host": "cdn.elastic-elastic-elastic.org",
    "index": "kibana_sample_data_logs",
    "ip": "70.133.115.149",
    "machine": {
        "ram": 5368709120,
        "os": "osx"
    },
    "memory": null,
    "message": "70.133.115.149 - - [2018-08-30T23:35:31.492Z] \"GET /styles/semantic-ui.css HTTP/1.1\" 200 8509 \"-\" \"Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24\"",
    "phpmemory": null,
    "referer": "http://twitter.com/error/john-phillips",
    "request": "/styles/semantic-ui.css",
    "response": 200,
    "tags": [
        "success",
        "info"
    ],
    "@timestamp": "2025-07-03T23:35:31.492Z",
    "url": "https://cdn.elastic-elastic-elastic.org/styles/semantic-ui.css",
    "utc_time": "2025-07-03T23:35:31.492Z",
    "event": {
        "dataset": "sample_web_logs"
    },
    "bytes_gauge": 8509,
    "bytes_counter": 51201128
}<p>Prenons maintenant les mappings de l'index que nous venons de charger, <code>kibana_sample_data_logs</code>:</p>INDEX_NAME = "kibana_sample_data_logs"

es_client = Elasticsearch(
    [os.getenv("ELASTICSEARCH_URL")],
    api_key=os.getenv("ELASTICSEARCH_API_KEY"),
)

result = es_client.indices.get_mapping(index=INDEX_NAME)
index_mappings = result[list(result.keys())[0]]["mappings"]["properties"]<p>Nous allons transmettre les mappings avec l'image que nous chargerons plus tard.</p><h2>Configuration LLM</h2><p>Configurons le LLM pour qu'il utilise la <a href="https://python.langchain.com/docs/concepts/structured_outputs/">sortie structurée</a> afin d'entrer une image et de recevoir un JSON contenant les informations que nous devons transmettre à notre fonction pour produire les objets JSON.</p><p>Nous installons les dépendances :</p>pip install elasticsearch pydantic langchain langchain-openai -q<p>Elasticsearch nous aidera à récupérer les <a href="https://www.elastic.co/docs/manage-data/data-store/mapping">mappages d'index</a>. Pydantic nous permet de définir des schémas en Python pour demander au LLM de les suivre, et <a href="https://www.elastic.co/search-labs/integrations/langchain">LangChain</a> est le cadre qui facilite l'appel aux LLM et aux outils d'IA.</p><p>Nous allons créer un schéma pydantique pour définir les résultats que nous voulons obtenir du LLM. Ce que nous devons savoir à partir de l'image, c'est le type de graphique, le champ, le titre de la visualisation et le titre du tableau de bord :</p>class Visualization(BaseModel):
    title: str = Field(description="The dashboard title")
    type: List[Literal["pie", "bar", "metric"]]
    field: str = Field(
        description="The field that this visualization use based on the provided mappings"
    )


class Dashboard(BaseModel):
    title: str = Field(description="The dashboard title")
    visualizations: List[Visualization]<p>Pour la saisie de l'image, nous enverrons un tableau de bord que je viens de dessiner :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7870f6421986d11d/6a1707d78b73cb3408189fa3/36441d7b5dc1f3ff2ac2a30710208d57ad41c716-1600x898.jpg" alt="" /><p>Nous déclarons maintenant l'appel au modèle LLM et le chargement de l'image. Cette fonction recevra les mappings de l'index Elasticsearch et une image du tableau de bord que nous voulons générer.</p><p>Avec <code>with_structured_output</code>, nous pouvons utiliser notre schéma Pydantic <code>Dashboard</code> comme objet de réponse que le LLM produira. Avec <a href="https://docs.pydantic.dev/latest/">Pydantic</a>, nous pouvons définir des modèles de données avec validation, ce qui garantit que la sortie LLM correspond à la structure attendue.</p><p>Pour convertir l'image en base64 et l'envoyer en entrée, vous pouvez utiliser un <a href="https://www.base64-image.de/">convertisseur en ligne</a> ou le faire <a href="https://www.geeksforgeeks.org/python-convert-image-to-string-and-vice-versa/">en code</a>.</p>prompt = f"""
    You are an expert in analyzing Kibana dashboards from images for the version 9.0.0 of Kibana.

    You will be given a dashboard image and an Elasticsearch index mapping.

    Below are the index mappings for the index that the dashboard is based on.
    Use this to help you understand the data and the fields that are available.

    Index Mappings:
    {index_mappings}

    Only include the fields that are relevant for each visualization, based on what is visible in the image.
    """

message = [
    {
        "role": "user",
        "content": [
            {"type": "text", "text": prompt},
            {
                "type": "image",
                "source_type": "base64",
                "data": image_base64,
                "mime_type": "image/png",
            },
        ],
    }
]


try:
    llm = init_chat_model("gpt-4.1-mini")
    llm = llm.with_structured_output(Dashboard)
    dashboard_values = llm.invoke(message)

    print("Dashboard values generated by the LLM successfully")
    print(dashboard_values)
except Exception as e:
    print(f"Failed to analyze image and match fields: {str(e)}")<p>Le LLM connaît déjà le contexte des tableaux de bord Kibana, nous n'avons donc pas besoin de tout expliquer dans l'invite, juste quelques détails pour s'assurer qu'il n'oublie pas qu'il travaille avec Elasticsearch et Kibana.</p><p>Décortiquons l'invitation :</p><p>Section</p><p>Raison</p><p>Vous êtes un expert en analyse de tableaux de bord Kibana à partir d'images pour la version 9.0.0 de Kibana.</p><p>En insistant sur le fait qu'il s'agit d'Elasticsearch et de la version d'Elasticsearch, nous réduisons la probabilité que le LLM hallucine des paramètres anciens/invalides.</p><p>Vous recevrez une image de tableau de bord et un mappage d'index Elasticsearch.</p><p>Nous expliquons que l'image concerne les tableaux de bord afin d'éviter toute interprétation erronée de la part du LLM.</p><p>Vous trouverez ci-dessous les correspondances d'index pour l'index sur lequel le tableau de bord est basé, ce qui vous aidera à comprendre les données et les champs disponibles. Mappages d'index : {index_mappings}</p><p>Il est essentiel de fournir les correspondances afin que le LLM puisse sélectionner les champs valides de manière dynamique. Sinon, nous pourrions coder en dur les correspondances ici, ce qui est trop rigide, ou compter sur le fait que l'image contienne les bons noms de champs, ce qui n'est pas fiable.</p><p>N'incluez que les champs pertinents pour chaque visualisation, en fonction de ce qui est visible dans l'image.</p><p>Nous avons dû ajouter ce renforcement parce qu'il arrive que l'on essaie d'ajouter des champs qui ne sont pas pertinents pour l'image.</p><p>Cela renvoie un objet contenant un tableau de visualisations à afficher :</p>"Dashboard values generated by the LLM successfully
title=""Client, Extension, OS, and Response Keyword Analysis""visualizations="[
   "Visualization(title=""Count of Client IP",
   "type="[
      "metric"
   ],
   "field=""clientip"")",
   "Visualization(title=""Extension Keyword Distribution",
   "type="[
      "pie"
   ],
   "field=""extension.keyword"")",
   "Visualization(title=""Most Used OS",
   "type="[
      "bar"
   ],
   "field=""machine.os.keyword"")",
   "Visualization(title=""Response Keyword Distribution",
   "type="[
      "bar"
   ],
   "field=""response.keyword"")"
]<h2>Traitement de la réponse au mécanisme d'apprentissage tout au long de la vie</h2><p>Nous avons créé un exemple de tableau de bord 2x2 panneaux à l'adresseet l'avons exporté en JSON à l'aide de l'<a href="https://www.elastic.co/docs/api/doc/kibana/operation/operation-get-dashboards-dashboard">API Get a dashboard</a>, puis nous avons stocké les panneaux en tant que modèles de visualisation (camembert, barre, métrique) dans lesquels nous pouvons remplacer certains paramètres pour créer de nouvelles visualisations avec différents champs en fonction de la question.</p><p>Vous pouvez consulter les fichiers JSON du modèle <a href="https://github.com/Delacrobix/elasticsearch-labs/tree/supporting-blog-content/from-image-idea-to-kibana-dashboard-using-ai/supporting-blog-content/from-image-idea-to-kibana-dashboard-using-ai/templates"><strong>ici.</strong></a> Notez que nous avons modifié les valeurs de l'objet que nous voulons remplacer plus tard par {<code>variable_name</code>}
</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc55d69d84a08e668/6a1707d8a2929903acd00fb8/ec7e1ac0cd8b470df13e60940162b56778acb386-315x234.png" alt="" /><p>Grâce aux informations fournies par le mécanisme d'apprentissage tout au long de la vie, nous pouvons décider du modèle à utiliser et des valeurs à remplacer.</p><p><code>fill_template_with_analysis</code> recevra les paramètres pour un seul panneau, y compris le modèle JSON de la visualisation, un titre, un champ et les coordonnées de la visualisation sur la grille.</p><p>Ensuite, il remplacera les valeurs du modèle et renverra la visualisation JSON finale.</p>def fill_template_with_analysis(
    template: Dict[str, Any],
    visualization: Visualization,
    grid_data: Dict[str, Any],
):
    template_str = json.dumps(template)
    replacements = {
	 "{visualization_id}": str(uuid.uuid4()),
        "{title}": visualization.title,
        "{x}": grid_data["x"],
        "{y}": grid_data["y"],
    }

    if visualization.field:
        replacements["{field}"] = visualization.field

    for placeholder, value in replacements.items():
        template_str = template_str.replace(placeholder, str(value))

    return json.loads(template_str)<p>Pour faire simple, nous aurons des coordonnées statiques que nous assignerons aux panneaux que le LLM décidera de créer et nous produirons un tableau de bord à grille 2x2 comme l'image ci-dessus.</p># Filling templates fields
panels = []    
grid_data = [
    {"x": 0, "y": 0},
    {"x": 12, "y": 0},
    {"x": 0, "y": 12},
    {"x": 12, "y": 12},
]


i = 0

for vis in dashboard_values.visualizations:
    for vis_type in vis.type:
        template = templates.get(vis_type, templates.get("bar", {}))
        filled_panel = fill_template_with_analysis(template, vis, grid_data[i])
        panels.append(filled_panel)
        i += 1<p>En fonction du type de visualisation décidé par le LLM, nous choisirons un modèle de fichier JSON et remplacerons les informations pertinentes à l'aide de <code>fill_template_with_analysis</code> , puis nous ajouterons le nouveau panneau à un tableau que nous utiliserons ultérieurement pour créer le tableau de bord.</p><p>Lorsque le tableau de bord est prêt, nous utilisons l'<a href="https://www.elastic.co/docs/api/doc/kibana/operation/operation-post-dashboards-dashboard-id">API Create a dashboard</a> pour envoyer le nouveau fichier JSON à Kibana afin de générer le tableau de bord :
</p>try:
    dashboard_id = str(uuid.uuid4())

    # post request to create the dashboard endpoint
    url = f"{os.getenv('KIBANA_URL')}/api/dashboards/dashboard/{dashboard_id}"

    dashboard_config = {
        "attributes": {
            "title": dashboard_values.title,
            "description": "Generated by AI",
            "timeRestore": True,
            "panels": panels,  # Visualizations with the values generated by the LLM
            "timeFrom": "now-7d/d",
            "timeTo": "now",
        },
    }

    headers = {
        "Content-Type": "application/json",
        "kbn-xsrf": "true",
        "Authorization": f"ApiKey {os.getenv('ELASTICSEARCH_API_KEY')}",
    }

    requests.post(
        url,
        headers=headers,
        json=dashboard_config,
    )

    # Url to the generated dashboard
    dashboard_url = f"{os.getenv('KIBANA_URL')}/app/dashboards#/view/{dashboard_id}"

    print("Dashboard URL: ", dashboard_url)
    print("Dashboard ID: ", dashboard_id)

except Exception as e:
    print(f"Failed to create dashboard: {str(e)}")<p>Pour exécuter le script et générer le tableau de bord, exécutez la commande suivante dans la console :</p>python &lt;file_name&gt;.py<p>Le résultat final sera le suivant :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5ceffed004153a4f/6a1707d9a929cf9147ae0901/e909afbf0e47d9a6e0f7bd07dfb2efcfa5cf06ac-921x715.png" alt="" /><h2>Conclusion</h2><p>Les LLM démontrent leurs fortes capacités visuelles lorsqu'ils transforment du texte en code ou des images en code. L'API des tableaux de bord permet également de transformer des fichiers JSON en tableaux de bord, et avec un LLM et un peu de code, nous pouvons transformer des images en tableau de bord Kibana.</p><p>L'étape suivante consiste à améliorer la flexibilité des visuels des tableaux de bord en utilisant différents paramètres de grille, différentes tailles de tableau de bord et différentes positions. De plus, la prise en charge de visualisations et de types de visualisation plus complexes serait un ajout utile à cette application.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ai-powered-dashboards</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-powered-dashboards</guid>
    <category><![CDATA[Kibana]]></category>
    <category><![CDATA[IA]]></category>
    <dc:creator><![CDATA[Jeffrey Rengifo,Tomás Murúa]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt41727cbee6155a68/6a1707dbb0367dd2fd72bc86/eb60ceb2fbc3941745b21ae3357cbb6ea8fab18c-1443x811.png" length="0" type="image/png"/>
    <pubDate>Wed, 16 Jul 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Construire un assistant agentique RAG avec JavaScript, Mastra et Elasticsearch]]></title>
    <description><![CDATA[Apprendre à construire des agents d'intelligence artificielle dans l'écosystème JavaScript]]></description>
    <content:encoded><![CDATA[<p>Cette idée m'est venue alors que je me trouvais au beau milieu d'une ligue de basket-ball fantastique passionnante et aux enjeux considérables. Je me suis posé la question : <em>Pourrais-je construire un agent IA qui m'aiderait à dominer mes matchs hebdomadaires ? Absolument !</em></p><p>Dans ce billet, nous allons voir comment construire un assistant RAG agentique en utilisant <a href="https://mastra.ai/en/docs">Mastra</a> et une application web JavaScript légère pour interagir avec lui. En connectant cet agent à Elasticsearch, nous lui donnons accès aux données structurées des joueurs et la possibilité d'exécuter des agrégations statistiques en temps réel, afin de vous donner des recommandations fondées sur les statistiques des joueurs. Rendez-vous sur le <a href="https://github.com/jdarmada/nba-ai-assistant-js.git">repo</a> GitHub pour suivre le processus ; le <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/README.md">README</a> fournit des instructions sur la manière de cloner et d'exécuter l'application par vos propres moyens. </p><p>Voici à quoi il devrait ressembler une fois assemblé :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt63ea3e7a09306fbf/6a17f1d97f6f150e22c09c50/1c73bd1dc1b5fe54f025c7a2b7c322acc9122f3a-1999x1393.png" alt="" /><p>Remarque : cet article de blog s'appuie sur "<a href="https://www.elastic.co/search-labs/blog/ai-agents-ai-sdk-elasticsearch">Building AI Agents with AI SDK and Elastic"</a> ( Créer des agents d'intelligence artificielle avec AI SDK et Elastic). Si vous ne connaissez pas encore les agents d'intelligence artificielle en général et leur utilité, commencez par là.
</p><h2><strong>Aperçu de l'architecture</strong></h2><p>Au cœur du système se trouve un grand modèle de langage (LLM), qui agit comme le moteur de raisonnement de l'agent (le cerveau). Il interprète les données de l'utilisateur, décide des outils à appeler et orchestre les étapes nécessaires pour générer une réponse pertinente.</p><p>L'agent lui-même est soutenu par Mastra, un cadre d'agent dans l'écosystème JavaScript. Mastra intègre le LLM à une infrastructure dorsale, l'expose en tant que point d'extrémité de l'API et fournit une interface pour définir les outils, les invites du système et le comportement de l'agent.</p><p>Sur le frontend, nous utilisons <a href="https://vite.dev/guide/">Vite</a> pour mettre en place rapidement une application web React qui fournit une interface de chat pour envoyer des requêtes à l'agent et recevoir ses réponses.</p><p>Enfin, nous avons Elasticsearch, qui stocke les statistiques des joueurs et les données de correspondance que l'agent peut interroger et agréger.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte13f09493f217047/6a17f1db1d1b83178d93e546/443bdc00d84ed1dd49e9f9e431e86ca4b0892563-1999x977.png" alt="" /><h2><strong>Arrière-plan</strong></h2><p>Passons en revue quelques concepts fondamentaux :</p><h3><strong>Qu'est-ce que le RAG agentique ?</strong></h3><p>Les agents d'intelligence artificielle peuvent interagir avec d'autres systèmes, fonctionner de manière indépendante et effectuer des actions en fonction de paramètres définis. Le RAG agentique combine l'autonomie d'un agent d'intelligence artificielle avec les principes de la génération augmentée par récupération, ce qui permet à un LLM de choisir les outils à utiliser et les données à utiliser comme contexte pour générer une réponse. Pour en savoir plus sur le RAG <a href="https://www.elastic.co/search-labs/blog/retrieval-augmented-generation-rag">, cliquez ici.</a></p><h3><strong>Pourquoi aller plus loin que AI-SDK dans le choix d'un framework ?</strong></h3><p>Il existe de nombreuses structures d'agents d'IA et vous avez probablement entendu parler des plus populaires comme <a href="https://www.elastic.co/search-labs/blog/using-crewai-with-elasticsearch">CrewAI</a>, <a href="https://www.elastic.co/search-labs/blog/using-autogen-with-elasticsearch">AutoGen</a> et <a href="https://www.elastic.co/search-labs/blog/build-rag-workflow-langgraph-elasticsearch">LangGraph</a>. La plupart de ces cadres partagent un ensemble commun de fonctionnalités, notamment la prise en charge de différents modèles, l'utilisation d'outils et la gestion de la mémoire.</p><p>Voici une <a href="https://docs.google.com/spreadsheets/d/1B37VxTBuGLeTSPVWtz7UMsCdtXrqV5hCjWkbHN8tfAo/edit?gid=0#gid=0">fiche comparative de</a> Harrison Chase (PDG de LangChain).</p><p>Ce qui a suscité mon intérêt pour Mastra, c'est qu'il s'agit d'un framework JavaScript conçu pour les développeurs full-stack afin d'intégrer facilement des agents dans leur écosystème. L'AI-SDK de Vercel permet également de réaliser la plupart de ces tâches, mais c'est lorsque vos projets incluent des flux de travail d'agents plus complexes que Mastra brille. Mastra améliore les modèles de base définis par l'AI-SDK et, dans ce projet, nous les utiliserons en tandem.</p><h3><strong>Cadres et considérations sur le choix du modèle</strong></h3><p>Si ces frameworks peuvent vous aider à créer rapidement des agents d'intelligence artificielle, ils présentent néanmoins certains inconvénients. Par exemple, l'utilisation d'autres cadres en dehors des agents d'IA ou de toute couche d'abstraction en général vous fait perdre un peu de contrôle. Si le LLM n'utilise pas les outils correctement ou fait quelque chose que vous ne voulez pas qu'il fasse, l'abstraction rend le débogage plus difficile. Cependant, à mon avis, ce compromis vaut la facilité et la rapidité que vous obtenez lors de la construction, en particulier parce que ces cadres gagnent du terrain et font l'objet d'itérations constantes.</p><p>Encore une fois, ces cadres sont agnostiques, ce qui signifie que vous pouvez brancher et utiliser différents modèles. N'oubliez pas que les modèles varient en fonction des ensembles de données sur lesquels ils ont été formés et qu'à leur tour, ils varient en fonction des réponses qu'ils donnent. Certains modèles ne prennent même pas en charge l'appel d'outils. Il est donc possible de changer et de tester différents modèles pour voir lequel vous donne les meilleures réponses, mais gardez à l'esprit que vous devrez probablement réécrire l'invite du système pour chacun d'entre eux. Par exemple, en utilisant Llama3.3 par rapport au GPT-4o, implique beaucoup plus d'invites et d'instructions spécifiques pour obtenir la réponse souhaitée.</p><h3><strong>Basket-ball fantaisie NBA</strong></h3><p>Le basket-ball fantaisie consiste à créer une ligue avec un groupe d'amis (attention, selon le degré de compétition de votre groupe, cela peut affecter le statut de vos amitiés), généralement avec de l'argent en jeu. Chacun d'entre vous constitue ensuite une équipe de 10 joueurs pour affronter les 10 joueurs d'un autre ami, en alternance chaque semaine. Les points qui contribuent à votre score global sont les résultats obtenus par chacun de vos joueurs contre leurs adversaires au cours d'une semaine donnée.</p><p>Si un joueur de votre équipe se blesse, est suspendu, etc., il y a une liste d'agents libres disponibles pour compléter votre équipe. C'est là qu'intervient une grande partie de la réflexion dans les sports fantastiques, car vous ne disposez que d'un nombre limité de choix et tout le monde est constamment à la recherche du meilleur joueur.</p><p>C'est là que notre assistant NBA AI va briller, en particulier dans les situations où vous devez rapidement décider quel joueur choisir. Au lieu de devoir rechercher manuellement les performances d'un joueur contre un adversaire spécifique, l'assistant peut trouver ces données rapidement et comparer les moyennes pour vous donner une recommandation éclairée.</p><p>Maintenant que vous connaissez les bases du RAG agentique et du basket-ball fantastique NBA, voyons ce qu'il en est dans la pratique.</p><h2><strong>Construire le projet</strong></h2><p>Si vous êtes bloqué à un moment ou à un autre ou si vous ne voulez pas le construire à partir de zéro, veuillez vous référer au <a href="https://github.com/jdarmada/nba-ai-assistant-js.git">repo</a>.</p><h3><strong>Ce que nous allons couvrir</strong></h3><ol><li><p><strong>L'échafaudage du projet :</strong></p><ol><li><p><strong>Backend (Mastra) :</strong> Utilisez npx create mastra@latest pour échafauder le backend et définir la logique de l'agent.</p></li><li><p><strong>Frontend (Vite + React) :</strong> Utilisez npm create vite@latest pour construire l'interface de chat frontale pour interagir avec l'agent.</p></li></ol></li><li><p><strong>Mise en place de variables d'environnement</strong></p><ol><li><p>Installer dotenv pour gérer les variables d'environnement.</p></li><li><p>Créer un fichier .env et fournir les variables nécessaires.</p></li></ol></li><li><p><strong>Configuration d'Elasticsearch</strong></p><ol><li><p>Mettre en place un cluster Elasticsearch (localement ou sur le cloud).</p></li><li><p>Installer le client Elasticsearch officiel.</p></li><li><p>S'assurer que les variables d'environnement sont accessibles.</p></li><li><p>Établir la connexion avec le client.</p></li></ol></li><li><p><strong>Acquisition en masse de données NBA dans Elasticsearch</strong></p><ol><li><p>Créez un index avec les mappings appropriés pour permettre les agrégations.</p></li><li><p>Intégrez en masse les statistiques de jeu des joueurs à partir d'un fichier CSV dans un index Elasticsearch.</p></li></ol></li><li><p><strong>Définir les agrégations Elasticsearch</strong></p><ol><li><p>Requête pour calculer les moyennes historiques contre un adversaire spécifique.</p></li><li><p>Requête pour calculer les moyennes de la saison contre un adversaire spécifique.</p></li></ol></li><li><p><strong>Fichier utilitaire de comparaison des joueurs</strong></p><ol><li><p>Consolidation des fonctions d'aide et des agrégations Elasticsearch.</p></li></ol></li><li><p><strong>Construction de l'agent</strong></p><ol><li><p>Ajouter la définition de l'agent et l'invite du système.</p></li><li><p>Installer les outils zod et define.</p></li><li><p>Ajout d'une configuration intermédiaire pour gérer CORS.</p></li></ol></li><li><p><strong>Intégration de l'interface utilisateur</strong></p><ol><li><p>Utilisation de la fonction useChat de l'AI-SDK pour interagir avec l'agent.</p></li><li><p>Créer l'interface utilisateur pour tenir des conversations correctement formatées.</p></li></ol></li><li><p><strong>Exécution de l'application</strong></p><ol><li><p>Démarrez le backend (serveur Mastra) et le frontend (application React).</p></li><li><p>Exemples de requêtes et d'utilisation.</p></li></ol></li><li><p><strong>Et maintenant ? Rendre l'agent plus intelligent</strong></p><ol><li><p>Ajout de capacités de recherche sémantique pour permettre des recommandations plus pertinentes.</p></li><li><p>Activer l'interrogation dynamique en déplaçant la logique de recherche vers le serveur Elasticsearch MCP (Model Context Protocol).</p></li></ol></li></ol><h3><strong>Produits requis</strong></h3><ul><li><p><strong>Node.js et npm</strong>: Le backend et le frontend fonctionnent tous deux sur Node. Assurez-vous d'avoir installé Node 18+ et npm v9+ (qui est fourni avec Node 18+).</p></li><li><p><strong>Cluster Elasticsearch :</strong> Un cluster Elasticsearch actif, soit localement, soit sur le cloud.</p></li><li><p><strong>Clé API OpenAI</strong>: Générez-en une sur la page des clés API du <a href="https://platform.openai.com/api-keys">portail des développeurs d'OpenAI</a>.</p></li></ul><p></p><h3><strong>Structure du projet</strong></h3><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt749baa120552e4ab/6a17f1dd1d1b83bfe993e54a/1c0bde11ad0eead523a95e03b9b905aa776e3fd1-1420x934.png" alt="" /><h4><strong>Étape 1 : Échafaudage du projet</strong></h4><ol><li><p>Tout d'abord, créez le répertoire nba-ai-assistant-js et naviguez à l'intérieur en utilisant : </p></li></ol>mkdir nba-ai-assistant-js &amp;&amp; cd nba-ai-assistant-js<p><strong>Backend :</strong></p><ol><li><p>Utilisez l'outil de création Mastra avec la commande : </p></li></ol>npx create-mastra@latest<p>2. Vous devriez obtenir quelques invites dans votre terminal, pour la première, nous nommerons le projet backend :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt65abf68fe588e968/6a17f1de63baff2814741d5b/de2725031ed6837db99a979efcdd0ece1e197dbb-608x84.png" alt="" /><p>3. Ensuite, nous conserverons la structure par défaut pour le stockage des fichiers Mastra, en saisissant <code>src/</code>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt89bd829fcf0ae6b9/6a17f1e04b055dd30e432302/88919d9ff1852126395e1fcd700ecb1b59aac63c-866x116.png" alt="" /><p>4. Ensuite, nous choisirons OpenAI comme fournisseur LLM par défaut.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfd167cc77a40b9a8/6a17f1e11480099e29b48863/2328761e769f3ded134e5a21e8a0bf8f41e88f68-404x210.png" alt="" /><p>5. Enfin, il vous demandera votre clé API OpenAI. Pour l'instant, nous choisirons d'ignorer l'option et nous la fournirons plus tard dans un fichier<code> .env</code>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt12654151ed495370/6a17f1e22f4a5c0f84fa89f9/0662de9bd28758e377e4c63df8d08b479068ce63-444x120.png" alt="" /><p><strong>Frontend :</strong></p><ol><li><p>Naviguez à nouveau vers le répertoire racine et exécutez l'<a href="https://vite.dev/guide/">outil de création Vite</a> à l'aide de cette commande : <code>npm create vite@latest frontend -- --template react</code></p></li></ol><p>Cela devrait créer une application React légère nommée <code>frontend</code> avec un modèle spécifique pour React.</p><p>Si tout se passe bien, à l'intérieur de votre répertoire de projet, vous devriez trouver un répertoire backend qui contient le code Mastra et un répertoire <code>frontend</code> avec votre application React.</p><p></p><h4><strong>Étape 2 : Configuration des variables d'environnement</strong></h4><ol><li><p>Pour gérer les clés sensibles, nous utiliserons le paquetage <code>dotenv</code> pour charger nos variables d'environnement à partir du fichier .env. fichier. Naviguez vers le répertoire backend et installez <code>dotenv</code>:</p></li></ol>cd backend
npm install dotenv --save<p>2. Dans le répertoire du backend, un fichier example.env est fourni avec les variables appropriées à remplir. Si vous créez le vôtre, veillez à inclure les variables suivantes :</p># OpenAI Configuration
OPENAI_API_KEY=your_openai_api_key_here

# Elasticsearch Configuration
ELASTIC_ENDPOINT=your_elasticsearch_endpoint_here
ELASTIC_API_KEY=your_elasticsearch_api_key_here
<p></p><p>Note : Assurez-vous que ce fichier est exclu de votre contrôle de version en ajoutant <code>.env</code> à <code>.gitignore</code>.</p><h4><strong>Étape 3 : Configuration d'Elasticsearch</strong></h4><p>Tout d'abord, vous devez disposer d'un cluster Elasticsearch actif. Deux options sont possibles :</p><ul><li><p><strong>Option A : utiliser Elasticsearch Cloud</strong></p><ul><li><p>S'inscrire à <a href="https://cloud.elastic.co/registration">Elastic Cloud</a></p></li><li><p>Créer un nouveau déploiement</p></li><li><p>Obtenez l'URL de votre point de terminaison et la clé API (encodée)</p></li></ul></li><li><p><strong>Option B : Exécuter Elasticsearch localement</strong></p><ul><li><p>Installer et exécuter Elasticsearch localement</p></li><li><p>Utilisez http://localhost:9200 comme point d'arrivée</p></li><li><p>Générer une clé API</p></li></ul></li></ul><p></p><p><strong>Installation du client Elasticsearch sur le backend :</strong></p><ol><li><p>Tout d'abord, installez le client Elasticsearch officiel dans votre répertoire backend :</p></li></ol>npm install @elastic/elasticsearch<p>2. Créez ensuite un répertoire lib pour contenir les fonctions réutilisables et naviguez-y :</p>mkdir lib &amp;&amp; cd lib<p>3. À l'intérieur, créez un nouveau fichier appelé <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/lib/elasticClient.js">elasticClient.js</a>. Ce fichier initialise le client Elasticsearch et l'expose pour qu'il soit utilisé dans votre projet.</p><p>4. Comme nous utilisons des modules ECMAScript (ESM), le nom de fichier __dirname and __n'est pas disponible. Pour vous assurer que vos variables d'environnement sont correctement chargées à partir du fichier .env dans le dossier backend, ajoutez cette configuration au début de votre fichier :</p>import { config } from 'dotenv';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { Client } from '@elastic/elasticsearch';

// Grab current directory and load .env from backend folder
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const envPath = join(__dirname, '../.env');

// Load environment variables from the correct path
config({ path: envPath });<p>5. Maintenant, initialisez le client Elasticsearch en utilisant vos variables d'environnement et vérifiez la connexion :</p>//Elastic client Initialization, make sure environment variables are being loaded in correctly
const config= {
    node: `${process.env.ELASTIC_ENDPOINT}`,
    auth: {
        apiKey: `${process.env.ELASTIC_API_KEY}`,
    },
};

export const elasticClient = new Client(config);

//Check if the client is connected
async function checkConnection() { 
    try {
        const info = await elasticClient.info();
        console.log('Elasticsearch is connected:', info);
    } catch (error) {
        console.error('Elasticsearch connection error:', error);
    }
}

checkConnection();
<p>Maintenant, nous pouvons importer cette instance client dans n'importe quel fichier qui doit interagir avec votre cluster Elasticsearch.</p><p></p><h4><strong>Étape 4 : Intégration en masse des données NBA dans Elasticsearch</strong></h4><p><strong>Ensemble de données :</strong></p><p>Pour ce projet, nous ferons référence aux ensembles de données disponibles dans le répertoire <a href="https://github.com/jdarmada/nba-ai-assistant-js/tree/main/backend">backend/data</a> de la base de données. Notre assistant NBA utilisera ces données comme base de connaissances pour effectuer des comparaisons statistiques et générer des recommandations.</p><ul><li><p><a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/data/sample_nba_data.csv">sample_player_game_stats.csv</a> - Exemple de statistiques de jeu d'un joueur (par exemple, points, rebonds, interceptions, etc.) par match et par joueur sur l'ensemble de sa carrière en NBA. Nous utiliserons cet ensemble de données pour effectuer des agrégations. (Remarque : il s'agit de données fictives, générées à des fins de démonstration et ne provenant pas de sources officielles de la NBA).</p></li><li><p><a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/data/playerAndTeamInfo.js">playerAndTeamInfo.js</a> - Remplace les métadonnées sur les joueurs et les équipes qui seraient normalement fournies par un appel à l'API afin que l'agent puisse faire correspondre les noms des joueurs et des équipes aux identifiants. Comme nous utilisons des données d'échantillon, nous ne voulons pas nous encombrer d'une API externe, c'est pourquoi nous avons codé en dur certaines valeurs auxquelles l'agent peut se référer.</p></li></ul><p></p><p><strong>Mise en œuvre :</strong></p><ol><li><p>Dans le répertoire <code>backend/lib</code>, créez un fichier nommé <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/lib/playerDataIngestion.js">playerDataIngestion.js</a>.</p></li><li><p>Configurer les importations, résoudre le chemin du fichier CSV et configurer l'analyse. Là encore, puisque nous utilisons ESM, nous devons reconstruire <code>__dirname</code> pour résoudre le chemin d'accès à l'échantillon CSV. Nous importerons également le module <a href="http://node.js/">Node.js</a> les modules intégrés, <code>fs</code> et <code>readline</code>, pour analyser le fichier CSV donné ligne par ligne.</p></li></ol>import fs from 'fs';
import readline from 'readline';
import path from 'path';
import { fileURLToPath } from 'url';
import { elasticClient } from './elasticClient.js';

const indexName = 'sample-nba-player-data'; //Replace with your preferred index name

//Since we are using ES modules __dirname and __filename don't exist, so this is a workaround that allows us to use the absolute file path for our sample data.
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const filePath = path.resolve(__dirname, '../data/sample_nba_data.csv');<p>Cela vous permet de lire et d'analyser efficacement le fichier CSV lorsque nous passons à l'étape de l'ingestion en masse.</p><p>3. Créez un index avec la correspondance appropriée. Bien qu'Elasticsearch puisse déduire automatiquement les types de champs avec le <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/dynamic">mappage dynamique</a>, nous voulons être explicites ici pour que chaque statut soit traité comme un champ numérique. Ceci est important car nous utiliserons ces champs pour les agrégations par la suite. Nous voulons également utiliser le type <code>float </code>pour les statistiques telles que les points, les rebonds, etc., afin de nous assurer que nous incluons des valeurs décimales. Enfin, nous voulons ajouter la propriété de mappage <code>dynamic: 'strict'</code> afin qu'Elasticsearch ne mappe pas dynamiquement les champs non reconnus. 
</p>// Function to create an index with mappings
async function createIndex() {
    try {
        // Check if the index already exists
        const exists = await elasticClient.indices.exists({ index: indexName });

        if (exists) {
            console.log(`Index "${indexName}" already exists, deleting it now.`);
            await elasticClient.indices.delete({ index: indexName });
            console.log(`Deleted index "${indexName}".`);
        }
        // Create the index with mappings
        const response = await elasticClient.indices.create({
            index: indexName,
            body: {
                mappings: {
                    dynamic: 'strict', // Prevent dynamic mapping
                    properties: {
                        game_id: { type: 'integer' },
                        game_date: { type: 'date' },
                        player_id: { type: 'integer' },
                        player_full_name: { type: 'text' },
                        player_team_id: { type: 'integer' },
                        player_team_name: { type: 'text' },
                        home_team: { type: 'boolean' },
                        opponent_team_id: { type: 'integer' },
                        opponent_team_name: { type: 'text' },
                        points: { type: 'float' },
                        rebounds: { type: 'float' },
                        assists: { type: 'float' },
                        steals: { type: 'float' },
                        blocks: { type: 'float' },
                        fg_percentage: { type: 'float' },
                        minutes_played: { type: 'float' },
                    },
                },
            },
        });

        console.log('Index created:', response);
        return true;
    } catch (error) {
        console.error('Error creating index:', error);
        return false;
    }
}
<p>4. Ajoutez la fonction permettant d'intégrer en masse les données CSV dans votre index Elasticsearch. À l'intérieur du bloc de code, nous sautons la ligne d'en-tête. Ensuite, divisez chaque ligne par une virgule et insérez-les dans l'objet document. Cette étape permet également de les nettoyer et de s'assurer qu'ils sont du bon type. Ensuite, nous plaçons les documents dans le tableau bulkBody avec les informations d'index, qui serviront de charge utile pour l'ingestion en masse dans Elasticsearch.</p>async function bulkIngestCsv(filePath) {
    const readStream = fs.createReadStream(filePath);
    const rl = readline.createInterface({
        input: readStream,
        crlfDelay: Infinity,
    });

    const bulkBody = [];
    let lineNum = 0;

    //Skip the header line
    let headerLine = true;
    for await (const line of rl) {
        if (headerLine) {
            headerLine = false;
            continue;
        }
        lineNum++;

        // Split the line by comma and remove whitespace
        const [
            game_id,
            game_date,
            player_id,
            player_full_name,
            player_team_id,
            player_team_name,
            home_team,
            opponent_team_id,
            opponent_team_name,
            points,
            rebounds,
            assists,
            steals,
            blocks,
            fg_percentage,
            minutes_played,
        ] = line.split(',');

        // Create a document object
        const document = {
            game_id: parseInt(game_id),
            game_date: game_date.trim(),
            player_id: parseInt(player_id),
            player_full_name: player_full_name.trim(),
            player_team_id: parseInt(player_team_id),
            player_team_name: player_team_name.trim(),
            home_team: home_team.trim() === 'True', // Converts True/False into a boolean
            opponent_team_id: parseInt(opponent_team_id),
            opponent_team_name: opponent_team_name.trim(),
            points: parseFloat(points),
            rebounds: parseFloat(rebounds),
            assists: parseFloat(assists),
            steals: parseFloat(steals),
            blocks: parseFloat(blocks),
            fg_percentage: parseFloat(fg_percentage),
            minutes_played: parseFloat(minutes_played),
        };

        // Prepare the bulk operation format
        bulkBody.push({ index: { _index: indexName } });
        bulkBody.push(document);
    }

    console.log(`Parsed ${lineNum} lines from CSV`);
<p>5. Ensuite, nous pouvons utiliser l'<a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-bulk">API Bulk</a> d'Elasticsearch avec <code>elasticClient.bulk()</code> pour ingérer plusieurs documents en une seule demande. La gestion des erreurs ci-dessous est structurée de manière à vous indiquer le nombre de documents qui n'ont pas été ingérés et le nombre de documents qui ont été ingérés avec succès.</p>try {
        // Perform the bulk request
        const response = await elasticClient.bulk({ body: bulkBody });

        if (response.errors) {
            console.log('Bulk Ingestion had some hiccups:');

            // Count successful vs failed operations
            let successCount = 0;
            let errorCount = 0;
            const errorDetails = [];

            response.items.forEach((item, index) =&gt; {
                const operation = item.index || item.create || item.update || item.delete;
                if (operation.error) {
                    errorCount++;
                    errorDetails.push({
                        document: index + 1,
                        error: operation.error,
                    });
                } else {
                    successCount++;
                }
            });

            console.log(`Successfully indexed: ${successCount} documents`);
            console.log(`Failed to index: ${errorCount} documents, here are the details`, errorDetails);

        } else {
            console.log(`Bulk Ingestion fully successful!`);
        }

    } catch (error) {
        console.error('Error performing bulk ingestion:', error);
    }
}
<p>6. Exécutez la fonction <code>main()</code> ci-dessous pour exécuter séquentiellement les fonctions <code>createIndex()</code> et <code>bulkIngestCsv()</code>.</p>// Run this function
async function main() {
    const result = await createIndex();
    if (!result) {
        console.error('Index setup failed. Aborting.');
        return;
    }

    await bulkIngestCsv(filePath);
    console.log('Bulk ingestion completed!');
}

main();
<p>Si vous voyez un journal de console indiquant que l'ingestion en masse a réussi, effectuez une vérification rapide de votre index Elasticsearch pour voir si les documents ont effectivement été ingérés avec succès.</p><h4><strong>Étape 5 : Définition des agrégations Elasticsearch et consolidation</strong></h4><p>Ce sont les principales fonctions qui seront utilisées lorsque nous définirons les outils de l'agent IA afin de comparer les statistiques des joueurs entre eux.</p><p>1. Naviguez jusqu'au répertoire <code>backend/lib</code> et créez un fichier appelé <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/lib/elasticAggs.js">elasticAggs.js</a>.</p><p>2. Ajoutez la requête ci-dessous pour calculer les moyennes historiques d'un joueur contre un adversaire spécifique. Cette requête utilise un <a href="https://www.elastic.co/search-labs/tutorials/search-tutorial/full-text-search/filters">filtre</a> <code>bool</code> avec 2 conditions : l'une correspondant à <code>player_id</code> et l'autre à <code>opponent_team_id</code>, afin de récupérer uniquement les jeux pertinents. Nous n'avons pas besoin de renvoyer de documents, nous ne nous intéressons qu'aux agrégations, c'est pourquoi nous définissons <code>size:0</code>. Sous le bloc <code>aggs</code>, nous exécutons plusieurs <a href="https://www.elastic.co/docs/explore-analyze/query-filter/aggregations">agrégations</a> métriques en parallèle sur des champs tels que <code>points, rebounds, assists, steals, blocks</code> et <code>fg_percentage</code> pour calculer leurs valeurs moyennes. Les LLM peuvent être aléatoires dans leurs calculs et ce processus est déchargé sur Elasticsearch, ce qui garantit à notre assistant NBA AI l'accès à des données exactes.</p>export async function getHistoricalAveragesAgainstOpponent(player_id, opponent_team_id) {
    try {
        //Query for Historical Averages
        const historicalQuery = await elasticClient.search({
            index: 'sample-nba-player-data', 
            size: 0,
            query: {
                bool: {
                    must: [
                        {
                            term: {
                                player_id: {
                                    value: player_id,
                                },
                            },
                        },
                        {
                            term: {
                                opponent_team_id: {
                                    value: opponent_team_id,
                                },
                            },
                        },
                    ],
                },
            },
            aggs: {
                avg_points: { avg: { field: 'points' } },
                avg_rebounds: { avg: { field: 'rebounds' } },
                avg_assists: { avg: { field: 'assists' } },
                avg_steals: { avg: { field: 'steals' } },
                avg_blocks: { avg: { field: 'blocks' } },
             avg_fg_percentage: { avg: { field: 'fg_percentage' } },
            },
        });

        return {
            points: historicalQuery.aggregations.avg_points.value || 0,
            rebounds: historicalQuery.aggregations.avg_rebounds.value || 0,
            assists: historicalQuery.aggregations.avg_assists.value || 0,
            steals: historicalQuery.aggregations.avg_steals.value || 0,
            blocks: historicalQuery.aggregations.avg_blocks.value || 0,
            fgPercentage: historicalQuery.aggregations.avg_fg_percentage.value || 0,
        };
    } catch (error) {
        console.error('Query error from getHistoricalAveragesAgainstOpponent function:', error);
        return { error: 'Queries failed in getting historical averages against opponent.' };
    }
}
<p>3. Pour calculer les moyennes saisonnières d'un joueur contre un adversaire spécifique, nous utiliserons pratiquement la même requête que la requête historique. La seule différence dans cette requête est que le filtre <code>bool</code> est assorti d'une condition supplémentaire pour <code>game_date</code>. Le champ <code>game_date</code> doit se situer dans la fourchette de la saison NBA en cours. Dans ce cas, la fourchette est comprise entre <code>2024-10-01</code> et <code>2025-06-30</code>. Cette condition supplémentaire ci-dessous garantit que les agrégations qui suivent n'isoleront que les matchs de cette saison.
</p>        {
                            range: {
                    //Range for this season, change to match current season
                                game_date: {
                                    gte: '2024-10-01',
                                    lte: '2025-06-30',
                                },
                            },
<h4><strong>Étape 6 : Utilitaire de comparaison des joueurs</strong></h4><p>Pour que notre code reste modulaire et facile à maintenir, nous allons créer un fichier utilitaire qui consolide les fonctions d'aide aux métadonnées et les agrégations Elasticsearch. Il s'agit de l'outil principal utilisé par l'agent. Nous y reviendrons plus tard :</p><p>1. Créez un nouveau fichier <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/lib/comparePlayers.js">comparePlayers.js</a> dans le répertoire <code>backend/lib</code>.</p><p>2. Ajoutez la fonction ci-dessous pour consolider les aides aux métadonnées et la logique d'agrégation Elasticsearch en une seule fonction qui alimente l'outil principal utilisé par l'agent.
</p>import { playersByName } from '../data/playerAndTeamInfo.js';
import { teamsByName } from '../data/playerAndTeamInfo.js';
import { upcomingMatchups } from '../data/playerAndTeamInfo.js';
import { getHistoricalAveragesAgainstOpponent } from './elasticAggs.js';
import { getSeasonAveragesAgainstOpponent } from './elasticAggs.js';

//Simple helper functions to simulate API calls for player and team metadata. These reference the hardcoded values from playerAndTeamInfo.js in the data directory
export function getPlayerInfo(playerFullName) {
    return playersByName[playerFullName];
}

export function getTeamID(teamFullName) {
    return teamsByName[teamFullName];
}

export function getUpcomingMatchups(teamId) {
    return upcomingMatchups[teamId];
}

//Main function used by the 'playerComparisonTool' agent tool
export async function comparePlayersForNextMatchup(player1Name, player2Name) {
    //Get Player Info
    const player1Info = getPlayerInfo(player1Name);
    const player2Info = getPlayerInfo(player2Name);

    //Get upcoming matchups
    const player1NextGame = getUpcomingMatchups(player1Info.team_id)[0];
    const player2NextGame = getUpcomingMatchups(player2Info.team_id)[0];

    //Get season and historical averages against next opponent for player 1
    const player1SeasonAverages = await getSeasonAveragesAgainstOpponent(
        player1Info.player_id,
        player1NextGame.opponent_team_id
    );
    const player1HistoricalAverages = await getHistoricalAveragesAgainstOpponent(
        player1Info.player_id,
        player1NextGame.opponent_team_id
    );

    //Get season and historical averages against next opponent for player 2
    const player2SeasonAverages = await getSeasonAveragesAgainstOpponent(
        player2Info.player_id,
        player2NextGame.opponent_team_id
    );
    const player2HistoricalAverages = await getHistoricalAveragesAgainstOpponent(
        player2Info.player_id,
        player2NextGame.opponent_team_id
    );

    const player1 = {
        name: player1Name,
        playerId: player1Info.player_id,
        teamId: player1Info.team_id,
        nextOpponent: {
            teamId: player1NextGame.opponent_team_id,
            teamName: player1NextGame.opponent_team_name,
            home: player1NextGame.home,
        },
        stats: {
            seasonAverages: player1SeasonAverages,
            historicalAverages: player1HistoricalAverages,
        },
    };

    const player2 = {
        name: player2Name,
        playerId: player2Info.player_id,
        teamId: player2Info.team_id,
        nextOpponent: {
            teamId: player2NextGame.opponent_team_id,
            teamName: player2NextGame.opponent_team_name,
            home: player2NextGame.home,
        },
        stats: {
            seasonAverages: player2SeasonAverages,
            historicalAverages: player2HistoricalAverages,
        },
    };

    return [player1, player2];
}
<h4><strong>Étape 7 : Création de l'agent</strong></h4><p>Maintenant que vous avez créé les échafaudages frontend et backend, ingéré les données du jeu NBA et établi une connexion à Elasticsearch, nous pouvons commencer à assembler toutes les pièces pour construire l'agent.</p><p><strong>Définition de l'agent</strong></p><p>1. Accédez au fichier <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/src/mastra/agents/index.ts">index.ts</a> dans le répertoire <code>backend/src/mastra/agents</code> et ajoutez la définition de l'agent. Vous pouvez spécifier des champs tels que :</p><ul><li><p><strong>Nom :</strong> Donnez à votre agent un nom qui sera utilisé comme référence lorsqu'il sera appelé sur le frontend.</p></li><li><p><strong>Instructions/Instructions du système : </strong>Une invite système donne au MLD le contexte initial et les règles à suivre pendant l'interaction. Il s'agit d'une invite similaire à celle que les utilisateurs envoient par l'intermédiaire de la boîte de dialogue, mais celle-ci est donnée avant toute entrée de l'utilisateur. Là encore, cela varie en fonction du modèle que vous choisissez.</p></li><li><p><strong>Modèle :</strong> Quel LLM utiliser (Mastra soutient OpenAI, Anthropic, les modèles locaux, etc.)</p></li><li><p><strong>Outils :</strong> Une liste de fonctions d'outils que l'agent peut appeler.</p></li><li><p><strong>Mémoire :</strong> (Facultatif) si nous voulons que l'agent se souvienne de l'historique des conversations, etc. Pour des raisons de simplicité, nous pouvons commencer sans mémoire persistante, bien que Mastra la prenne en charge.</p></li></ul><p></p>import { openai } from '@ai-sdk/openai';
import { Agent } from '@mastra/core/agent';
import { playerComparisonTool } from '../tools';

export const basketballAgent = new Agent({
    name: 'Basketball Agent',
    instructions: `
      You are a NBA Basketball expert.
      Your primary function is to compare two NBA players and recommend which one is the better fantasy pickup.

      Only compare players from the following list:
      - LeBron James
      - Stephen Curry
      - Jayson Tatum
      - Jaylen Brown
      - Nikola Jokic
      - Luka Doncic
      - Kyrie Irving
      - Anthony Davis
      - Kawhi Leonard
      - Russell Westbrook

      Input Handling Rules:
      - If the user asks about a player that is not on this list, respond with the list of available players for comparison.
      - If the user only inputs one player, ask the user to add another player from the list provided.
      - If the user inputs a player with the wrong spelling or capitalizations, infer from the list of available players provided.
      - IMPORTANT: If the user asks a question or asks you to generate a response about anything outside of basketball or the scope of this project, DO NOT answer and affirm you can only talk about basketball.

      Tool Usage:
      - Extract and standardize player names to match the list exactly.
      - Use the playerComparisonTool, passing both names as strings.
      - The tool will return an object with game information, stats, and analysis.

      Format your response using Markdown syntax. Use:

        Example output format:

       
        #### Next Game Info
        - ***LeBron James** vs Warriors, May 24 (Home)  
        - ***Stephen Curry** vs Lakers, May 24 (Away)


        #### Stats Comparison  
        \`\`\`  
        Stat                  LeBron James (vs Warriors)    Stephen Curry (vs Lakers)  
        --------------------  -----------------------------  ----------------------------  
        Historical Points     28.3                          30.3  
        Historical Assists    6.7                           8.7  
        Season Points         28.8                          23.3  
        Season Assists        6.2                           4.7  
        \`\`\`

        #### Fantasy Recommendation  
        Explain which player is the better fantasy pickup and why.
      
    `,
    model: openai('gpt-4o'),
    tools: { playerComparisonTool },
});
<p><strong>
Définition des outils</strong></p><ol><li><p>Naviguez jusqu'au fichier <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/src/mastra/tools/index.ts">index.ts</a> dans le répertoire <code>backend/src/mastra/tools</code>.</p></li><li><p>Installez Zod à l'aide de la commande :</p></li></ol>npm install zod<p>3. Ajouter des définitions d'outils. Notez que nous importons la fonction dans le fichier <code>comparePlayers.js</code> en tant que fonction principale que l'agent utilisera lorsqu'il appellera cet outil. En utilisant la fonction <code>createTool()</code> de Mastra, nous enregistrerons notre <code>playerComparisonTool</code>. Les domaines concernés sont les suivants :</p><ul><li><p><code>id</code>: Il s'agit d'une description en langage naturel qui aide l'agent à comprendre ce que fait l'outil.</p></li><li><p><code>input schema</code>: Pour définir la forme de l'entrée de l'outil, Mastra utilise le schéma <a href="https://zod.dev/">Zod</a>, qui est une bibliothèque de validation de schéma TypeScript. Zod s'assure que l'agent saisit des données correctement structurées et empêche l'outil de s'exécuter si la structure de l'entrée ne correspond pas.</p></li><li><p><code>description</code>: Il s'agit d'une description en langage naturel qui aide l'agent à comprendre quand il doit appeler et utiliser l'outil.</p></li><li><p><code>execute</code>: La logique qui s'exécute lorsque l'outil est appelé. Dans notre cas, nous utilisons une fonction d'aide importée pour renvoyer des statistiques de performance.</p></li></ul>import { comparePlayersForNextMatchup } from '../../../lib/comparePlayers.js'
import { createTool } from "@mastra/core/tools";
import { z } from "zod";

export const playerComparisonTool = createTool({
    id: "Compare two NBA players",
    inputSchema: z.object({
        player1:z.string(),
        player2:z.string()
    }),
    description: "Use this tool to compare two players given in the user prompt.",
    execute: async ({ context: { player1, player2 } }) =&gt; {
        return await comparePlayersForNextMatchup(player1, player2);
      },
})<p><strong>Ajout d'un logiciel intermédiaire pour gérer CORS</strong></p><p>Ajouter un middleware dans le serveur Mastra pour gérer <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS">CORS</a>. On dit qu'il y a trois choses dans la vie qu'on ne peut pas éviter : la mort, les impôts, et pour les développeurs web, c'est CORS. En bref, le partage des ressources inter-origines est une fonction de sécurité du navigateur qui empêche le front-end d'envoyer des requêtes à un back-end fonctionnant sur un domaine ou un port différent. Même si nous exécutons le backend et le frontend sur localhost, ils utilisent des ports différents, ce qui déclenche la politique CORS. Nous devons ajouter l'intergiciel spécifié dans la <a href="https://mastra.ai/en/docs/server-db/middleware">documentation de Mastra</a> afin que notre backend autorise ces requêtes depuis le frontend.</p><p>1. Naviguez jusqu'au fichier <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/src/mastra/index.ts">index.ts</a> dans le répertoire <code>backend/src/mastra</code> et ajoutez la configuration pour CORS :</p><ul><li><p><code>origin: ['http://localhost:5173']</code></p><ul><li><p>Autorise les demandes provenant uniquement de cette adresse (adresse par défaut de Vite)</p></li></ul></li><li><p><code>allowMethods: ["GET", "POST"]</code></p><ul><li><p>Méthodes HTTP autorisées. La plupart du temps, il utilisera POST.</p></li></ul></li><li><p><code>allowHeaders: ["Content-Type", "Authorization", "x-mastra-client-type, "x-highlight-request", "traceparent"],</code></p><ul><li><p>Ils déterminent quels en-têtes personnalisés peuvent être utilisés dans les requêtes</p></li></ul></li></ul><p></p>import { Mastra } from '@mastra/core/mastra';
import { basketballAgent } from './agents';

console.log('Starting Mastra server...');

export const mastra = new Mastra({
  agents: { basketballAgent },
  server:{
    timeout: 10 * 60 * 1000, // 10 minutes
    cors: {
      origin: ['http://localhost:5173'],
      allowMethods: ["GET", "POST"],
      allowHeaders: [
        "Content-Type",
        "Authorization",
        "x-mastra-client-type",
        "x-highlight-request",
        "traceparent",
      ],
      exposeHeaders: ["Content-Length", "X-Requested-With"],
      credentials: false,
    },
  },

});

console.log('Mastra server configured.'); // Log after server configuration
<h4><strong>Étape 8 : Intégration de l'interface utilisateur</strong></h4><p>Ce composant React fournit une interface de chat simple qui se connecte à l'agent IA Mastra en utilisant le hook <a href="https://mastra.ai/en/docs/frameworks/agentic-uis/ai-sdk#using-the-usechat-hook">useChat()</a> de <code>@ai-sdk/react</code>. Nous allons également utiliser ce crochet pour afficher l'utilisation des jetons, les appels d'outils et pour rendre la conversation. Dans l'invite système ci-dessus, nous demandons également à l'agent de produire la réponse en format markdown, nous utiliserons donc <code>react-markdown</code> pour formater correctement la réponse.</p><p></p><p>1. Dans le répertoire frontend, installez le paquetage @ai-sdk/react pour utiliser le hook useChat().</p>npm install @ai-sdk/react<p>2. Dans le même répertoire, installez React Markdown pour que nous puissions formater correctement la réponse générée par l'agent.</p>npm install react-markdown<p>3. Mettre en œuvre <code>useChat()</code>. Ce hook va gérer l'interaction entre votre frontend et votre agent IA backend. Il gère l'état des messages, les entrées de l'utilisateur, l'état et vous donne des crochets de cycle de vie à des fins d'observabilité. Les options que nous transmettons sont les suivantes :</p><ul><li><p><code>api:</code> Ceci définit le point final de votre agent Mastra AI. Le port par défaut est le port 4111 et nous voulons également ajouter la route qui prend en charge les réponses en continu.</p></li><li><p><code>onToolCall</code>: Cette fonction s'exécute chaque fois que l'agent appelle un outil ; nous l'utilisons pour savoir quels outils notre agent appelle.</p></li><li><p><code>onFinish</code>: Cette opération s'exécute après que l'agent a fourni une réponse complète. Même si nous avons activé le streaming, <code>onFinish</code> sera toujours exécuté après la réception du message complet et non après chaque morceau. Ici, nous l'utilisons pour suivre l'utilisation de nos jetons. Cela peut s'avérer utile pour contrôler et optimiser les coûts de la gestion du cycle d'apprentissage tout au long de la vie.</p></li></ul><p>4. Enfin, nous nous rendons au composant <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/frontend/components/ChatUI.jsx">ChatUI.jsx</a> dans le répertoire <code>frontend/components</code> pour créer l'interface utilisateur de notre conversation. Ensuite, la réponse est enveloppée dans un composant <code>ReactMarkdown</code> afin de formater correctement la réponse de l'agent.</p>import React, { useState } from 'react';
import { useChat } from '@ai-sdk/react';
import ReactMarkdown from 'react-markdown';

export default function ChatUI() {
    const [totalTokenUsage, setTotalTokenUsage] = useState(0);
    const [promptTokenUsage, setPromptTokenUsage] = useState(0);
    const [completionTokenUsage, setCompletionTokenUsage] = useState(0);
    const [toolsCalled, setToolsCalled] = useState([]);

    const { messages, input, handleInputChange, handleSubmit, status } = useChat({
        api: 'http://localhost:4111/api/agents/basketballAgent/stream', //Replace with your own endpoint for your agent
        id: 'my-chat-session',

        //Optional parameter to check agent tool calls
        onToolCall: ({ toolCall }) =&gt; {
            setToolsCalled((prev) =&gt; [...prev, toolCall.toolName]);
        },

        //Optional parameter to check token usages
        onFinish: (message, { usage }) =&gt; {
            setTotalTokenUsage((prev) =&gt; prev + usage.totalTokens);
            setPromptTokenUsage((prev) =&gt; prev + usage.promptTokens);
            setCompletionTokenUsage((prev) =&gt; prev + usage.completionTokens);
        },

        //Optional parameter for error handling
        onError: (error) =&gt; {
            console.error('Agent error:', error);
        },
    });

    return (
        &lt;div&gt;
            &lt;div className="agent-info"&gt;
                &lt;h4 className="stats-title"&gt;What's My Agent Doing?&lt;/h4&gt;

                &lt;div className="stats-box"&gt;
                    &lt;strong className="stats-sub-title"&gt;Tools Called:&lt;/strong&gt;
                    &lt;ul className="tool-list"&gt;
                        {toolsCalled.map((tool, idx) =&gt; (
                            &lt;li key={idx}&gt;{tool}&lt;/li&gt;
                        ))}
                        {toolsCalled.length === 0 &amp;&amp; &lt;li&gt;No tools called yet.&lt;/li&gt;}
                    &lt;/ul&gt;

                    &lt;div className="usage-stats"&gt;
                        &lt;p&gt;Prompt Token Usage: {promptTokenUsage}&lt;/p&gt;
                        &lt;p&gt;Completion Token Usage: {completionTokenUsage}&lt;/p&gt;
                        &lt;p&gt;Total Token Usage: {totalTokenUsage}&lt;/p&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;

            &lt;strong&gt;Conversation:&lt;/strong&gt;
            &lt;div className="convo-box"&gt;
                {messages.map((msg) =&gt; (
                    &lt;div key={msg.id} className="message-item"&gt;
                        &lt;strong className="message-role"&gt;{msg.role === 'assistant' ? 'Basketbot' : 'You'}:&lt;/strong&gt;
                        &lt;ReactMarkdown&gt;{msg.content}&lt;/ReactMarkdown&gt;
                    &lt;/div&gt;
                ))}
            &lt;/div&gt;

            &lt;form onSubmit={handleSubmit}&gt;
                &lt;input
                    type="text"
                    value={input}
                    onChange={handleInputChange}
                    placeholder="Input two players you want to compare."
                    className="input-box"
                /&gt;
                &lt;button type="submit" disabled={status === 'streaming'}&gt;
                    {status === 'streaming' ? 'Thinking...' : 'Send'}
                &lt;/button&gt;
            &lt;/form&gt;
        &lt;/div&gt;
    );
}<h4><strong>Étape 9 : Exécution de l'application</strong></h4><p>Félicitations ! Vous êtes maintenant prêt à exécuter l'application. Suivez ces étapes pour démarrer le backend et le frontend.</p><ol><li><p>Dans une fenêtre de terminal, à partir du répertoire racine, naviguez jusqu'au répertoire backend et démarrez le serveur Mastra :</p></li></ol>cd backend

npm run dev<p>2. Dans une autre fenêtre de terminal, à partir du répertoire racine, naviguez jusqu'au répertoire frontend et démarrez l'application React :</p><p></p>cd frontend

npm run dev<p></p><p>3. Allez dans votre navigateur et naviguez jusqu'à :</p><p></p><p><a href="http://localhost:5173/">http://localhost:5173</a></p><p></p><p>Vous devriez voir l'interface de chat. Essayez les exemples suivants :</p><ul><li><p>"Comparer LeBron James et Stephen Curry"</p></li><li><p>"Qui choisir entre Jayson Tatum et Luka Doncic ?"</p></li></ul><p></p><h3><strong>Et maintenant ? Rendre l'agent plus intelligent</strong></h3><p>Pour rendre l'assistant plus agentive et les recommandations plus perspicaces, j'ajouterai quelques améliorations clés dans la prochaine itération.</p><p></p><p><strong>Recherche sémantique pour les nouvelles de la NBA</strong></p><p>Il y a une tonne de facteurs qui peuvent affecter les performances des joueurs, dont beaucoup n'apparaissent pas dans les statistiques brutes. Des choses comme les rapports sur les blessures, les changements de composition, ou même une analyse d'après-match, vous ne pouvez les trouver que dans des articles de presse. Pour saisir ce contexte supplémentaire, j'ajouterai des capacités de recherche sémantique afin que l'agent puisse retrouver des articles pertinents de la NBA et tenir compte de ce récit dans ses recommandations.</p><p></p><p><strong>Recherche dynamique avec le serveur Elasticsearch MCP</strong></p><p>Le protocole MCP (Model Context Protocol) devient rapidement la norme pour la connexion des agents aux sources de données. Je vais migrer la logique de recherche dans le serveur Elasticsearch MCP, qui permet à l'agent de construire dynamiquement des requêtes plutôt que de s'appuyer sur les fonctions de recherche prédéfinies que nous fournissons. Cela nous permet d'utiliser davantage de flux de travail en langage naturel et de réduire la nécessité de rédiger manuellement chaque requête de recherche. Pour en savoir plus sur le serveur Elasticsearch MCP et l'état actuel de l'écosystème <a href="https://www.elastic.co/search-labs/blog/mcp-current-state">, cliquez ici.</a></p><p></p><p>Ces changements sont déjà en cours, restez à l'écoute !</p><h3><strong>Conclusion</strong></h3><p></p><p>Dans ce blog, nous avons construit un assistant RAG agentique qui fournit des recommandations personnalisées pour votre équipe de basket-ball fantasy en utilisant JavaScript, Mastra et Elasticsearch. Nous avons couvert :</p><ul><li><p><strong>Les principes fondamentaux de la RAG agentique</strong> et la manière dont la combinaison de l'autonomie d'un agent d'intelligence artificielle avec les outils permettant d'utiliser efficacement la RAG peut déboucher sur des agents plus nuancés et plus dynamiques.</p></li><li><p><strong>Elasticsearch </strong>et comment ses capacités de stockage de données et ses puissantes agrégations natives en font un partenaire idéal en tant que base de connaissances pour un LLM.</p></li><li><p><strong>Le </strong>cadre Mastra et la manière dont il simplifie la construction de ces agents pour les développeurs de l'écosystème JavaScript.</p></li></ul><p>Que vous soyez fanatique de basket-ball, que vous cherchiez à construire des agents d'intelligence artificielle, ou les deux comme moi, j'espère que ce blog vous a donné quelques éléments de base pour commencer. Le repo complet est disponible sur <a href="https://github.com/jdarmada/nba-ai-assistant-js">GitHub</a>, n'hésitez pas à le cloner et à le modifier. Maintenant, allez gagner cette ligue de fantasy !</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/agentic-rag</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/agentic-rag</guid>
    <category><![CDATA[IA]]></category>
    <category><![CDATA[IA agentique]]></category>
    <category><![CDATA[JavaScript]]></category>
    <dc:creator><![CDATA[JD Armada]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8ffd561836a4cb20/6a17f1e47b54f978588b39e4/8132ed781c1ea5d46ca244182f421ed5c721f23b-1200x628.png" length="0" type="image/png"/>
    <pubDate>Tue, 01 Jul 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Utiliser Azure LLM Functions avec Elasticsearch pour des requêtes plus intelligentes]]></title>
    <description><![CDATA[Explorez un exemple d'application de recherche immobilière qui utilise Azure Gen AI LLM Functions avec Elasticsearch pour fournir des résultats de recherche hybrides flexibles. Voyez étape par étape comment configurer et exécuter l'application d'exemple dans GitHub Codespaces.]]></description>
    <content:encoded><![CDATA[<p>Précision. Quand c'est important, c'est très important. Lorsque vous recherchez quelque chose de spécifique, la précision est très importante. Cependant, il arrive qu'une requête trop précise ne donne aucun résultat. Il est donc avantageux d'avoir la possibilité d'élargir le champ d'une requête afin de trouver d'autres données potentiellement pertinentes.</p><p>Cet article de blog traite de l'utilisation d'Elasticsearch et d'Azure Open AI pour créer un exemple d'application qui démontre comment trouver des résultats exacts lors de la recherche de biens immobiliers très spécifiques tout en fournissant des résultats pertinents lorsqu'une correspondance spécifique n'est pas disponible. Nous verrons toutes les étapes nécessaires à la création d'un index Elasticsearch et d'un modèle de recherche. Ensuite, nous présenterons l'ensemble du processus de création d'une application qui utilise Azure OpenAI pour prendre en compte les requêtes des utilisateurs et les transformer en requêtes de modèles de recherche Elasticsearch qui peuvent produire des résultats étonnamment personnalisés.</p><p>Voici une liste de toutes les ressources que nous utiliserons pour créer notre exemple d'application de recherche immobilière :</p><ul><li><p>Index et modèle de recherche Elasticsearch</p></li><li><p>Azure OpenAI</p></li><li><p>API Azure Maps</p></li><li><p><a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb">Codespaces Jupyter Notebook</a></p></li><li><p>Noyau sémantique</p></li><li><p>Application C# avec Blazor Frontend</p></li></ul><h2>Processus d'interrogation intelligent</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0461b58012efd772/6a17f73fa292997d52d02e19/0c4a7c835e06c514f158c00ab1055a7ba719a35f-1600x765.png" alt="Processus d'interrogation intelligent" /><p>Ce flux de travail combine le LLM, les outils LLM et la recherche pour transformer les requêtes en langage naturel en résultats de recherche structurés et pertinents :</p><ul><li><p><strong>LLM (Large Language Model)</strong> - Interprète les requêtes complexes des utilisateurs et orchestre les outils pour extraire les intentions de recherche et enrichir le contexte.</p></li><li><p><strong>Outils LLM</strong> - Chaque outil LLM est un programme C# que nous avons créé pour cet article. Il existe trois outils :</p><ul><li><p><em>Outil d'extraction de paramètres</em>: il extrait de la requête des attributs clés tels que les chambres, les salles de bain, les caractéristiques et le prix.</p></li><li><p><em>Outil GeoCode</em>: convertit les noms de lieux en latitude/longitude pour le filtrage spatial.</p></li><li><p><em>Outil de recherche</em>: remplit un modèle de recherche Elasticsearch avec des paramètres de requête et exécute une recherche. <strong>Recherche hybride</strong> - Exécute une recherche hybride (texte intégral + vecteur dense) avec inférence ML intégrée. Cette approche stratifiée garantit à l'utilisateur final des expériences de recherche plus intelligentes et adaptées au contexte.</p></li></ul></li></ul><h2>Architecture de l'application</h2><p>Voici un schéma de l'architecture du système de l'application d'exemple. Nous utiliserons un carnet Jupyter de Codespaces pour interagir avec Elastic Cloud.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7617ae80295a3e2b/6a17f74196142a35deeb1cb0/2880afee184cd9270c0eb4310e51418e2339784d-936x452.png" alt="Schéma d'architecture d'une application Azure LLM Functions." /><h2>Produits requis</h2><p>Vous n'aurez besoin que de votre navigateur, car nous utiliserons GitHub Codespaces pour cloner l'application d'exemple, la configurer et l'exécuter. Pour la partie Elastic de la solution, nous utiliserons Elastic Cloud pour créer un projet Elasticsearch Serverless. Nous utiliserons <a href="https://portal.azure.com/">Azure Portal</a> pour travailler avec les ressources Azure.</p><h2>Cloner le repo de l'application exemple dans Codespaces</h2><p>Commencez par cloner le code de l'application d'exemple. Vous pouvez le faire dans <a href="https://github.com/codespaces/">GitHub Codespaces</a>, qui permet de cloner et d'exécuter des applications. Cliquez sur <strong>New Codespace</strong>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd4ed9c67e2d79c41/6a17f7436df73146a20a10bc/b89cbec491659b6c8a0bb9551ed2629f7a37f9fd-1600x427.png" alt="Clonage d'un repo d'application d'exemple dans Codespaces." /><p>Ensuite, sélectionnez le repo <a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo">jwilliams-elastic/msbuild-intelligent-query-demo</a> dans la liste déroulante <strong>Repository</strong> et cliquez sur <strong>Create Codespace</strong>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdfcc992a6cb7a7d7/6a17f7450b0bed67c1dd3750/43ea377554527af9578400f16cd2342bf8fff3a2-1600x1049.png" alt="Repository et cliquez sur Create Codespace." /><h2>Créer un fichier .env fichier</h2><p>Nous utiliserons un carnet Jupyter Python pour accéder et interagir avec Elastic Cloud en utilisant les valeurs de configuration qui sont stockées dans un fichier de configuration. Le fichier de configuration de l'ordinateur portable doit avoir le nom de fichier <em><strong>.env.</strong></em> et vous allez le créer maintenant.</p><ol><li><p>Dans GitHub Codespaces, cliquez sur le bouton <strong>New File </strong>et ajoutez un fichier nommé <em><strong>.env.</strong></em></p></li><li><p>Ajoutez le contenu suivant au <em><strong> fichier .env</strong></em> nouvellement créé fichier</p></li></ol>ELASTIC_URL=
ELASTIC_API_KEY=<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1a7dcb2fd2bb24f5/6a17f7462f4a5c21abfa8aa9/84d4f327948858ba61db0001dd8cf780d42fe0a7-1600x875.gif" alt="un carnet de notes Python Jupyter pour accéder à Elastic Cloud et interagir avec lui, en utilisant les valeurs de configuration stockées dans un fichier de configuration. " /><p>Comme vous pouvez le voir, nous avons quelques valeurs manquantes, <strong>ELASTIC_URL</strong> et <strong>ELASTIC_API_KEY,</strong> qui doivent être ajoutées au <em> fichier .env.</em> fichier. Commençons maintenant par créer un projet Elasticsearch serverless qui servira de backend pour alimenter la fonctionnalité de recherche de notre application d'exemple.</p><h2>Créer un projet Elastic Serverless</h2><ol><li><p>Allez sur <a href="http://cloud.elastic.co">cloud.elastic.co</a> et cliquez sur <strong>Create New Serverless project (Créer un nouveau projet sans serveur)</strong>.</p></li><li><p>Cliquez sur <strong>Suivant </strong>pour la solution <strong>Elasticsearch</strong></p></li><li><p>Sélection <strong>optimisée pour les vecteurs</strong></p></li><li><p>Définir le <strong>fournisseur de services en nuage</strong> comme étant <strong>Azure</strong></p></li><li><p>Cliquez sur <strong>Créer un projet sans serveur</strong></p></li><li><p>Cliquez sur " <strong>Getting Started"</strong> dans le menu de navigation principal et faites défiler vers le bas pour copier les " <strong>Connection Details" (détails de connexion)</strong>.</p></li><li><p>Cliquez sur le bouton <strong>Copier </strong>pour copier le <strong>point de terminaison Elasticsearch</strong> à partir des <strong>détails de la connexion.</strong></p></li><li><p>Mise à jour du <em><strong> fichier .env</strong></em> pour définir l'<strong>URL ELASTIC</strong> comme étant le <strong>point de terminaison Elasticsearch</strong>copié.</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt95b7fefa05173822/6a17f748ec0f89f05f5a67c6/77a35e55446d396066b68cfd132d1543a07b81cc-1600x875.gif" alt="Comment créer un nouveau projet Serverless dans Elasticsearch." /><h2>Créer une clé API Elastic</h2><ol><li><p>Ouvrez la page Elasticsearch <strong>Getting Started</strong>, cliquez sur <strong>New</strong> dans la section <strong>Add an API Key.</strong></p></li><li><p>Saisir un <strong>nom de</strong>clé</p></li><li><p>Cliquez sur<strong> Créer une clé API</strong></p></li><li><p>Cliquez sur le bouton Copier pour copier la valeur de la clé API.</p></li><li><p>De retour dans <strong>Codespaces,</strong> où nous avons le fichier <em><strong>.env</strong></em>ouvert pour édition, collez la valeur copiée pour définir la<strong> clé ELASTIC_API_KEY</strong></p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf6b1c85d267e6ad0/6a17f74a4b055d118143239a/20168cba493d8e2c0d9ae7704eb0ae707df58e4c-1600x875.gif" alt="Comment créer une clé API Elastic." /><h2>Ouvrez le Codespaces Notebook et installez les dépendances de la bibliothèque.</h2><p>Dans l'explorateur de fichiers, sélectionnez le fichier <em><strong>VectorDBSetup.ipynb</strong></em> pour ouvrir le carnet de notes. Une fois le Notebook chargé, recherchez la <a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L40-L52">section du </a><a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L40-L52"><strong>Notebook intitulée Installer</strong></a><strong> les bibliothèques. </strong>Cliquez sur le bouton de lecture de la section.</p><p>Si c'est la première fois que vous exécutez un Notebook dans GitHub Codespaces, vous serez invité à sélectionner un Codespaces Kernel et à configurer l'environnement Python.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt970f4fa30c6c9302/6a17f74c505ac30ee6ad8ceb/2272f70615dfb9dcbeb91f39b6dd5076213e24a5-1600x875.gif" alt="Installation des dépendances de la bibliothèque dans le Codespaces Notebook." /><h2>Définir les importations et charger les variables d'environnement à l'aide de Codespaces Notebook</h2><p>Passez à la section suivante du Carnet de notes <a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L58-L104">intitulée </a><a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L58-L104"><strong>Définir les importations et charger les variables d'environnement</strong></a>. Cliquez sur le bouton de lecture de la section.</p><p>Ce code importe les bibliothèques Python utilisées par le Notebook et charge les variables d'environnement à partir du fichier <em>.env </em>que vous avez créée précédemment.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6d74b41258420d5a/6a17f74e6317301f2f585c16/aa9f9198ff452ac0c4ce33b00f253731dbee22c5-1600x875.gif" alt="Définition des importations et chargement des variables d'environnement à l'aide de Codespaces Notebook." /><h2>Créer un point de terminaison d'inférence Elastic ML à l'aide de Codespaces Notebook</h2><p>Passez à la <a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L112-L157">section suivante du Notebook intitulée </a><a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L112-L157"><strong>Create ML inference endpoint (Créer un point de terminaison d'inférence</strong></a> ML). Cliquez sur le bouton de lecture de la section.</p><p>Cela créera un nouveau point de terminaison d'inférence ML dans le projet Elasticsearch que nous utiliserons pour générer des enchâssements de texte à partir de nos données. Les text embeddings sont des représentations vectorielles du texte qui seront stockées dans Elasticsearch pour alimenter la recherche sémantique.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt87581300d4d0b66e/6a17f750e9ea875a81a9c795/97c1afab3e64027ee5ae77f377d56ba406ae1765-1600x875.gif" alt="Création d'un point de terminaison d'inférence Elastic ML à l'aide de Codespaces Notebook." /><h2>Créer un index Elasticsearch avec Codespaces Notebook</h2><p>Passez à la <a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L165-L224">section suivante du </a><a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L165-L224"><strong>Notebook intitulée Create Elasticsearch index (Créer</strong></a> un index Elasticsearch). Cliquez sur le bouton de lecture de la section.</p><p>Cela créera l'index Elasticsearch qui stockera nos données d'exemple et toutes les données vectorielles associées générées via le point de terminaison de l'inférence ML.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta2d2d5a10c84a1b7/6a17f7527f6f15775cc09cd6/23a66283ee41239e24fb8455c3cd95641982ca6b-1600x875.gif" alt="Créer un index Elasticsearch en utilisant Codespaces Notebook." /><h2>Créer un modèle de recherche Elasticsearch à l'aide de Codespaces Notebook</h2><p>Passez à la <a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L232-L384">section suivante du carnet </a><a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L232-L384"><strong>de notes intitulée Modèle de</strong></a> recherche. Cliquez sur le bouton de lecture de la section.</p><p>Cela permet de créer un <a href="https://www.elastic.co/fr/docs/solutions/search/search-templates">modèle de recherche</a>, que notre exemple d'application utilisera en tant que modèle rempli avec les mots analysés à partir de la requête de recherche de l'utilisateur. Cela nous permet de configurer et de contrôler à quel point nous sommes spécifiques lorsque nous interrogeons des données dans l'index Elasticsearch.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt194d6557d096ac25/6a17f7545772628d901bcda0/4c001a3e4d1cca4cfb5c043fea92c7ccaf9cb64a-1600x875.gif" alt="Création d'un modèle de recherche Elasticsearch à l'aide de Codespaces Notebook." /><h2>Ingérer des données dans l'index Elasticsearch à l'aide de Codespaces Notebook</h2><p>Passez à la section suivante du Carnet de notes <a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L392-L450">, intitulée </a><a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L392-L450"><strong>Acquisition de données sur les biens immobiliers</strong></a>. Cliquez sur le bouton d'exécution de la section.</p><p>L'exécution de cette section de code permet de charger en bloc les données de l'exemple contenues dans le fichier <em>properties.jsonl. </em> Au bout de quelques minutes, vous devriez voir apparaître une confirmation indiquant que le processus s'est déroulé avec succès. Vous pouvez confirmer que l'index contient l'enregistrement attendu en allant dans la section <strong>Gestion de l'index</strong> dans Elastic Cloud.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaf1b5ad59c75716d/6a17f7566864a4528fb6894b/e9698c798541ccfc08143a939846597028e3c566-1600x875.gif" alt="Ingestion de données dans l'index Elasticsearch à l'aide de Codespaces Notebook." /><h2>Créer appsetting.json pour configurer l'application C#</h2><p>Notre index Elasticsearch étant créé et alimenté en données, nous sommes maintenant prêts à configurer l'application d'exemple pour qu'elle fonctionne avec Elastic et Azure Cloud. L'application d'exemple C# utilise un fichier nommé <em>appsettings.json</em> pour stocker et charger ses informations d'accès telles que les clés API. Vous allez créer le fichier <em>appsettings.json</em> en utilisant l'éditeur de Codespaces.</p><p>1. Créer <em>appsettings.json</em> dans le dossier <strong>HomeFinderApp </strong></p><p>2. Collez le code suivant dans le fichier <em>appsettings.json</em></p>{
 "ElasticSettings": {
   "Url": "",
   "ApiKey": "",
   "IndexName": "properties",
   "TemplateId": "properties-search-template"
 },
 "AzureOpenAISettings": {
   "Endpoint": "",
   "ApiKey": "",
   "DeploymentName": "gpt-4o"
 },
 "AzureMapsSettings": {
   "Url": "https://atlas.microsoft.com/geocode",
   "ApiKey": ""
 },
 "Logging": {
   "LogLevel": {
 	"Default": "Information",
 	"Microsoft.AspNetCore": "Warning"
   }
 },
 "AllowedHosts": "*"
}
<p>3. Trouvez les valeurs <strong>Url</strong> et <strong>ApiKey</strong> dans la section <strong>ElasticSettings </strong>. Définissez les mêmes valeurs que celles que vous avez définies dans le fichier <em>.env</em> dans une étape précédente.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt28afc316da403ec9/6a17f758faa913584d93ca28/00dad25bacdea2adcbd1e6eca7658867a49b0d8c-1600x875.gif" alt="Création de appsetting.json pour configurer l'application C#." /><h2>Créer un service Azure OpenAI</h2><p>Notre application d'exemple va utiliser Azure OpenAI pour analyser la requête de l'utilisateur de l'application et ensuite soumettre une demande à Elasticsearch en remplissant le modèle de recherche pour tenter de communiquer de manière flexible ce que l'utilisateur recherche.</p><ol><li><p>Ouvrez un nouvel onglet de navigateur et accédez à <a href="https://portal.azure.com/#blade/Microsoft_Azure_ProjectOxford/CognitiveServicesHub/OpenAI">AI Foundry | Azure OpenAI</a> dans le portail Azure. Cliquez sur<strong>+Créer</strong></p></li><li><p>Dans le formulaire de création, sélectionnez un <strong>groupe de ressources</strong>.</p></li><li><p>Saisir un <strong>nom</strong></p></li><li><p>Choisir un <strong>niveau de tarification</strong></p></li><li><p>Cliquez sur <strong>Suivant</strong></p></li><li><p>Dans l'onglet <strong>Réseau</strong>, cliquez sur<strong> Suivant</strong></p></li><li><p>Dans l'onglet <strong>Tags</strong>, cliquez sur <strong>Suivant</strong></p></li><li><p>Dans l'onglet <strong>Réviser et soumettre</strong>, cliquez sur <strong>Créer</strong></p></li><li><p>Une fois la création terminée, cliquez sur <strong>Aller à la ressource.</strong></p></li><li><p>Sélectionnez <strong>Keys and Endpoint</strong> dans le menu de navigation de gauche.</p></li><li><p>Copiez le <strong>point de terminaison</strong> et collez-le dans le fichier <em>appsettings.json</em> que vous avez créé dans l'onglet de votre navigateur où se trouve l'éditeur Codespaces.</p></li><li><p>Retournez ensuite à l'onglet de votre navigateur contenant la <strong>clé</strong> Azure OpenAI et la page du point de terminaison. Cliquez sur le bouton copier pour la <strong>clé 1</strong>, et collez la valeur copiée dans le fichier <em>appsettings.json</em>, dans l'onglet de votre navigateur où se trouve l'éditeur Codespaces ouvert.</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd01a5e82d9003d02/6a17f75a148009be65b48904/6d49197302d110410dca0a53b6ae90237cf2dfd6-1600x875.gif" alt="Création d'un service Azure OpenAI." /><h2>Ajouter le déploiement du modèle gpt-4o au service Azure Open AI</h2><p>Super, nous avons maintenant un service Azure OpenAI qui fonctionne, mais il a encore besoin d'un déploiement de modèle pour nous donner les capacités LLM dont notre application d'exemple a besoin. Il existe une multitude de modèles. Déployons <em>gpt-4o</em> puisqu'il est déjà spécifié dans le fichier <em>appsettings.json</em> que vous avez créé.</p><p></p><ol><li><p>Accédez à <a href="https://ai.azure.com/resource/playground">Azure AI Foundry</a> et cliquez sur <strong>Créer un déploiement.</strong></p></li><li><p>Rechercher <em>gpt-4o</em> et le sélectionner dans les résultats</p></li><li><p>Cliquez sur <strong>Confirmer</strong> pour le sélectionner</p></li><li><p>Cliquez sur <strong>Déployer</strong> pour déployer le modèle</p></li></ol><p>Une fois que vous avez déployé avec succès le modèle <em>gpt-4o</em>, vous pouvez sélectionner <strong>Déploiements</strong> dans le menu de navigation de gauche et confirmer que le déploiement <em><strong>gpt-4o</strong></em> est listé avec l'<strong>état</strong> " <strong>Succeeded"</strong>.
</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte82341dd8a4982b0/6a17f75c4b055d9e0943239e/1b817ab67c05634e9c72777593b4d1a2c6c28191-1600x875.gif" alt="Ajout d'un déploiement de modèle gpt-4o au service Azure Open AI." /><h2>Créer un compte Azure Maps</h2><p>Nous voulons que les utilisateurs de notre exemple d'application puissent rechercher des biens immobiliers dans des zones spécifiques, mais sans avoir à être trop précis. Si quelqu'un veut chercher une propriété près du marché local, Azure Maps est un service que l'OpenAI LLM peut utiliser pour obtenir les coordonnées de latitude et de longitude du marché. Les coordonnées peuvent ensuite être incluses dans les requêtes basées sur le modèle de recherche envoyé à Elasticsearch pour les requêtes des utilisateurs qui incluent des emplacements spécifiques et des considérations de géo-distance.</p><ol><li><p>Cliquez sur <strong>Créer</strong> dans les <a href="https://portal.azure.com/#browse/Microsoft.Maps%2Faccounts">comptes Azure Maps</a></p></li><li><p>Sélectionner un <strong>groupe de ressources</strong></p></li><li><p>Saisir un <strong>nom</strong></p></li><li><p>Accepter la licence et la déclaration de confidentialité</p></li><li><p>Cliquez sur <strong>Réviser et créer</strong></p></li><li><p>Cliquez sur <strong>Créer</strong></p></li><li><p>Une fois la création du compte terminée, cliquez sur <strong>Aller à la ressource.</strong></p></li><li><p>Cliquez sur <strong>Authentification</strong> dans le menu de navigation de gauche</p></li><li><p>Copier la valeur de la <strong>clé primaire</strong> et la coller comme valeur de l'<strong>ApiKey</strong> dans la section <strong>AzureMapsSettings</strong> du fichier <em>appsettings.json</em>, de nouveau dans l'onglet de votre navigateur contenant l'éditeur Codespaces.</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt42a4ab4bb7a96d24/6a17f75edbb4ff4f91fb5892/90fadd48e366682e2bad91e32988f93c6354e126-1600x875.gif" alt="Création d'un compte Azure Maps." /><h2>Essayez l'application d'exemple</h2><p>Passons maintenant à la partie la plus amusante. Il est temps d'exécuter l'application d'exemple. Nous avons mis en place tous les détails de configuration ainsi que les ressources Elastic Cloud et Azure Cloud dont nous avons besoin pour faire fonctionner l'application.</p><p>1. Ouvrez une fenêtre Terminal dans l'éditeur Codespaces.</p><p>2. Utilisez la commande suivante pour modifier le répertoire actif afin qu'il devienne le dossier de l'application d'exemple.
</p>cd HomeFinderApp<p>3. Utilisez la commande <em>dotnet</em> suivante pour exécuter l'application.</p>dotnet run<p>4. Cliquez sur le bouton <strong>Ouvrir dans le navigateur </strong>lorsqu'il apparaît.</p><p>5. Testez la recherche par défaut, puis testez vos propres recherches personnalisées. Si vous souhaitez obtenir plus de détails sur ce qui est exécuté en amont pour générer les résultats de la recherche, vous pouvez cliquer sur le lien <strong>Afficher </strong>situé à côté de <strong>Invocations d'outils.</strong></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt41adf6631ba91be1/6a17f760505ac30986ad8cf1/821fe7b9446de5ed646d938cc9484a7ddad21030-1600x875.gif" alt="Essai de l'application d'exemple." /><p><strong>Bonus : </strong>Si vous voulez vraiment tester GPT-4o, essayez la recherche suivante : <em>Je cherche une propriété près de Disney World Fl avec plus de 30 chambres et plus de 20 salles de bain, une piscine, un garage et près de la plage pour moins de 200K. </em>Cette requête renverra des résultats après plusieurs invocations de l'outil de recherche.</p><h2>Elastic est votre solution pour Search AI</h2><p>L'application en cours est un exemple de recherche guidée Gen AI LLM utilisant Elasticsearch via des modèles de recherche comme source de données de base. N'hésitez pas à expérimenter et à personnaliser l'application d'exemple pour créer une expérience de recherche à la fois précise et flexible, afin d'aider vos utilisateurs à trouver ce qu'ils recherchent.</p><p>Merci de votre lecture. Essayez <a href="https://cloud.elastic.co/registration">Elastic Cloud</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/azure-llm-functions-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/azure-llm-functions-elasticsearch</guid>
    <category><![CDATA[IA]]></category>
    <dc:creator><![CDATA[Jonathan Simon,James Williams]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt93dd59caccfd7fc8/6a17f7614202292a7129f799/1431b90c7e00de06574c1e33c44a2e89296c824e-1200x628.png" length="0" type="image/png"/>
    <pubDate>Fri, 13 Jun 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[L'état actuel du MCP (Model Context Protocol)]]></title>
    <description><![CDATA[Découvrez MCP, les mises à jour du projet, les fonctionnalités, les défis de sécurité, les cas d'utilisation émergents, et comment bricoler avec le serveur Elasticsearch MCP d'Elastic.]]></description>
    <content:encoded><![CDATA[<p>J'ai récemment assisté au <a href="https://mcpdevsummit.ai/">sommet des développeurs MCP</a> à San Francisco et il est clair que le protocole MCP (Model Context Protocol) devient rapidement un élément fondamental pour les agents d'intelligence artificielle et les applications d'intelligence artificielle riches en contexte. Chez Elastic, nous nous orientons dans cette direction en exposant les serveurs MCP directement à partir d'<a href="https://www.elastic.co/fr/elasticsearch/agent-builder">Agent Builder</a>, faisant d'Elasticsearch un contexte de premier ordre et un fournisseur d'outils pour tout agent compatible MCP. Dans ce billet, je vais passer en revue les principales mises à jour de l'événement, les cas d'utilisation émergents, ce qui se profile à l'horizon pour MCP, et comment vous pouvez utiliser Agent Builder pour rendre Elasticsearch disponible aux agents via MCP.</p><h2>Qu'est-ce que le protocole de contexte de modèle (MCP) ?</h2><p>Pour ceux qui ne connaissent pas, <a href="https://modelcontextprotocol.io/introduction">Model Context Protocol</a> est une norme ouverte qui offre un moyen structuré et bidirectionnel de connecter les modèles d'IA à diverses sources de données et à divers outils, ce qui leur permet de générer des réponses plus pertinentes et mieux informées. Il est communément appelé "<a href="https://modelcontextprotocol.io/introduction">port USB-C pour les applications d'intelligence artificielle</a>".</p><p>Voici un schéma architectural qui met en évidence son caractère bidirectionnel :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5ff0e141b5dfda29/6a17e7ffe8fbcee5263a1946/5eba1e59514eb58a5220bb92bb49e6328ee83cd7-674x466.png" alt="Architecture du protocole de contexte de modèle (MCP)" /><p>Il s'agit d'un changement important pour les praticiens de l'IA, car l'un des principaux défis de la mise à l'échelle des applications d'IA est de devoir créer des intégrations personnalisées pour chaque nouvelle source de données. MCP offre une architecture durable et réutilisable pour la gestion et la fourniture de contexte aux modèles. Il est indépendant des modèles et des serveurs et est entièrement open source.</p><p>MCP est la dernière itération d'une lignée de spécifications d'API visant à normaliser l'intégration entre les applications. Dans le passé, nous avions OpenAPI pour les services RESTful, GraphQL pour l'interrogation des données et gRPC pour la communication des microservices. MCP ne partage pas seulement la rigueur structurée de ces anciennes spécifications, mais l'intègre également dans un cadre d'IA générative, ce qui facilite l'intégration des agents dans différents systèmes sans avoir recours à des connecteurs personnalisés. À bien des égards, MCP vise à faire pour les agents d'intelligence artificielle ce que HTTP a fait pour le web. Tout comme HTTP a normalisé la communication entre les navigateurs et les sites web, MCP cherche à normaliser la façon dont les agents d'intelligence artificielle interagissent avec le monde des données qui les entoure.</p><h2>MCP par rapport à d'autres protocoles d'agents</h2><p>Le paysage des protocoles d'agents se développe rapidement, avec plus d'une douzaine de normes émergentes en concurrence pour définir la manière dont les agents interagissent. <a href="https://x.com/seldo">Laurie Voss</a> de LlamaIndex décrit comment la plupart peuvent être catégorisés en 2 types : les protocoles inter-agents qui se concentrent sur les agents qui parlent entre eux et les protocoles orientés vers le contexte comme MCP qui se concentrent sur la fourniture d'un contexte structuré aux LLMs.</p><p>D'autres protocoles populaires comme <a href="https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/">A2A</a> (Agent to Agent) de Google, <a href="https://agentcommunicationprotocol.dev/introduction/welcome">ACP</a> (Agent Communication Protocol) de Cisco et IBM, et <a href="https://agoraprotocol.org/">Agora</a>, visent à permettre les négociations entre agents, la création de coalitions et même des systèmes d'identité décentralisés. MCP adopte une approche un peu plus pragmatique en ce sens qu'il se concentre sur la manière dont les agents accèdent aux outils et aux données et pas nécessairement sur la manière dont ils communiquent entre eux (bien que MCP puisse également permettre cela à l'avenir de différentes manières).</p><p>Actuellement, ce qui distingue MCP, c'est sa traction et son élan. Comme React dans les premiers jours des frameworks frontaux, MCP a commencé avec un problème de niche et est maintenant l'un des protocoles d'agent les plus adoptés et les plus extensibles dans la pratique.</p><h2>Récapitulation du sommet : Évolution des priorités pour le MCP</h2><p>Le sommet a accueilli des intervenants de Anthropic, Okta, OpenAI, AWS, GitHub et bien d'autres. Les discussions ont porté sur l'amélioration des protocoles de base et sur leur mise en œuvre dans le monde réel, et ont mis en évidence les priorités immédiates et à long terme. Ces exposés ont montré que l'on s'éloignait de l'expérimentation précoce et de l'appel à de simples outils pour passer à la construction de systèmes d'IA fiables, évolutifs et modulaires en utilisant le MCP comme base.</p><p>Plusieurs orateurs ont évoqué un avenir où le MCP est plus qu'un simple protocole, il peut devenir le fondement d'un web orienté vers l'intelligence artificielle. Tout comme JavaScript permet aux utilisateurs de cliquer et d'interagir avec les pages web, MCP pourrait permettre aux agents d'effectuer ces mêmes actions en notre nom. Par exemple, dans le domaine du commerce électronique, au lieu de naviguer manuellement sur un site web pour faire des achats, les utilisateurs pourraient simplement demander à un agent de se connecter, de trouver un produit spécifique, de l'ajouter à leur panier et de passer à la caisse.</p><p>Il ne s'agit pas non plus d'une pure spéculation ou d'un battage médiatique : PayPal a présenté sa nouvelle boîte à outils pour agents et son serveur MCP lors du sommet, qui permet exactement cette expérience de commerce agentique. Grâce à MCP qui fournit un accès sécurisé et fiable aux outils et aux sources de données, les agents ne se contenteront pas de lire le web, ils pourront agir en conséquence. Aujourd'hui, MCP est déjà une norme puissante qui a le vent en poupe et, à terme, elle pourrait devenir la norme des interactions utilisateur améliorées par l'IA sur l'ensemble du web.</p><h2>Mise à jour du projet MCP : Transport, élicitation et outils structurés</h2><p><a href="https://x.com/JeromeSwannack">Jerome Swannack</a>, l'un des principaux contributeurs à MCP, a fait part de quelques mises à jour de la spécification du protocole au cours des six derniers mois. Les principaux objectifs de ces changements sont les suivants</p><ol><li><p>Pour activer le MCP à distance avec l'ajout du HTTP en continu</p></li><li><p>Permettre des modèles d'interaction entre agents plus riches grâce à l'ajout de schémas d'élicitation et de sortie d'outils.</p></li></ol><p>MCP étant un logiciel libre, les développeurs peuvent déjà mettre en œuvre des modifications telles que le protocole HTTP fluide. Les schémas d'élicitation et de sortie de l'outil ne sont pas encore publiés ; ils sont à l'état de projet et peuvent évoluer.</p><p><strong>Streamable HTTP </strong><a href="https://modelcontextprotocol.io/specification/2025-03-26/basic/transports">(publié le 26 mars 2025</a>)<strong>:</strong> Une mise à jour technique importante a été l'introduction du protocole HTTP en flux continu en tant que nouveau mécanisme de transport. Il remplace les événements envoyés par le serveur (SSE) par un modèle bidirectionnel plus évolutif qui prend en charge le codage de transfert par morceaux et la transmission progressive de messages sur une seule connexion HTTP. Cela vous permet de déployer des serveurs MCP sur une infrastructure cloud comme AWS Lambda et de prendre en charge les contraintes du réseau d'entreprise sans connexions à longue durée de vie ni nécessité de polling.</p><p><strong>Elicitation </strong><a href="https://modelcontextprotocol.io/specification/2025-06-18/client/elicitation">(publié le 18 juin 2025</a>)<strong>:</strong> L'élicitation permet aux serveurs de définir un schéma sur la façon dont ils veulent que le contexte soit structuré à partir d'un client. Essentiellement, le serveur peut décrire ce dont il a besoin et le type d'entrée qu'il attend. Cela a plusieurs implications : Les constructeurs de serveurs peuvent créer des interactions agentiques plus complexes. Les concepteurs de clients peuvent mettre en œuvre des interfaces utilisateur dynamiques qui s'adaptent à ces schémas. Cependant, l'élicitation ne doit pas être utilisée pour extraire des informations sensibles ou personnellement identifiables des utilisateurs. Les développeurs doivent suivre les <a href="https://modelcontextprotocol.io/specification/draft/client/elicitation#security-considerations">meilleures pratiques</a> pour s'assurer que les messages d'élicitation restent sûrs et appropriés, en particulier au fur et à mesure que le MCP évolue. Ceci est lié à des problèmes de sécurité plus larges que nous aborderons plus loin dans ce billet.</p><p><strong>Schémas de sortie d'outil </strong><a href="https://modelcontextprotocol.io/specification/draft/server/tools#output-schema">(publié le 18 juin 2025</a>)<strong>: </strong>Ce concept permet au client et au LLM de connaître à l'avance les formes de sortie de l'outil. Les schémas de sortie des outils permettent aux développeurs de décrire les résultats attendus d'un outil. Ces schémas répondent à l'une des principales limites de l'appel direct d'outils, à savoir l'utilisation inefficace de la fenêtre contextuelle. La fenêtre contextuelle est considérée comme l'une des ressources les plus importantes lorsque vous travaillez avec des LLM. Lorsque vous appelez directement un outil, celui-ci renvoie un contenu brut qui est entièrement transféré dans le contexte du LLM. Les schémas de sortie des outils peuvent vous aider à mieux utiliser vos jetons et la fenêtre contextuelle en permettant au serveur MCP de fournir des données structurées. Voici quelques <a href="https://modelcontextprotocol.io/specification/draft/server/tools#security-considerations">bonnes pratiques</a> concernant les outils en général.</p><p>Ensemble, ces nouvelles mises à jour et les ajouts futurs aideront le MCP à devenir un protocole d'agent plus modulaire, typé et prêt pour la production.</p><h2>Caractéristiques de puissance sous-utilisées : Échantillonnage et racines</h2><p>Bien qu'ils ne soient pas nouveaux dans la spécification MCP, l'échantillonnage et les racines ont été mis en évidence lors de la présentation. Ces deux primitives sont actuellement négligées et sous-explorées, mais elles peuvent contribuer de manière significative à des interactions plus riches et plus sûres entre les agents.</p><p><strong>Échantillonnage - Les serveurs peuvent demander des compléments au client : </strong>L'<a href="https://modelcontextprotocol.io/docs/concepts/sampling">échantillonnage</a> permet aux serveurs MCP de demander des compléments au LLM côté client. Cela ajoute à la nature bidirectionnelle du protocole, où le serveur ne se contente pas de répondre aux demandes ; il peut inciter et demander au modèle du client de générer une réponse. Cela permet au client de garder un contrôle total sur le coût, la sécurité et le modèle utilisé par le serveur MCP. Ainsi, dans le cas de l'utilisation d'un serveur MCP externe avec un modèle préconfiguré, vous n'aurez pas besoin de fournir vos propres clés API ou de configurer votre propre abonnement à ce modèle, car le serveur peut simplement demander le modèle déjà connecté au client. Cela permet d'obtenir des comportements d'agents plus complexes et plus interactifs.</p><p><strong>Racines - Accès limité aux ressources : </strong>Les<a href="https://modelcontextprotocol.io/docs/concepts/roots">racines</a> ont été conçues pour permettre aux clients d'informer les serveurs des ressources et des espaces de travail pertinents sur lesquels ils doivent se concentrer. Il s'agit d'un outil puissant pour définir le champ d'action des serveurs. Il est important de noter que les racines sont "<a href="https://modelcontextprotocol.io/docs/concepts/roots#how-roots-work">informatives et non strictement exécutoires</a>", ce qui signifie qu'elles ne définissent pas de droits ou de permissions pour les serveurs ou les agents MCP. En d'autres termes, vous ne pouvez pas vous fier uniquement aux racines pour empêcher un serveur ou un agent d'exécuter certains outils ou d'effectuer des actions d'écriture. En ce qui concerne les racines, les autorisations doivent toujours être gérées du côté du client, avec des mécanismes d'approbation par l'utilisateur. Par ailleurs, les développeurs doivent toujours veiller à utiliser des serveurs conçus pour respecter les limites fixées par les racines et utiliser les <a href="https://modelcontextprotocol.io/docs/concepts/roots#best-practices">meilleures pratiques.</a></p><h2>Authentification des agents : OAuth 2.1 et métadonnées protégées</h2><p>Cette section se concentre sur OAuth 2.1, la dernière itération d'OAuth 2.0, qui supprime les flux non sécurisés et consolide les meilleures pratiques.</p><p>La prise en charge d'OAuth était un sujet très attendu, d'autant plus que la sécurité et l'évolutivité sont considérées comme les principaux obstacles qui empêchent MCP de devenir la norme pour la connexion des agents aux outils. <a href="https://x.com/aaronpk">Aaron Parecki</a> (éditeur d'OAuth 2.1 et expert en normes d'identité chez Okta) a expliqué comment MCP peut adopter un flux OAuth propre et évolutif qui décharge les développeurs de serveurs de la majeure partie de la complexité. La spécification d'autorisation officielle OAuth 2.1 a été récemment publiée dans la dernière révision du protocole le <a href="https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization">18 juin 2025</a>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4d53e7bb091b4f43/6a17e80163baff80dc741c56/2ea159116fe5e03ff800f077adf16d6ca9f1c1d1-1594x1280.png" alt="Authentification MCP pour les agents" /><p>Dans cette implémentation, les responsabilités OAuth peuvent être réparties entre le client MCP et le serveur. La majeure partie du flux d'authentification est initiée et gérée par le client MCP, le serveur n'intervenant qu'à la fin pour recevoir et vérifier le jeton sécurisé. Cette scission permet de résoudre un problème critique de mise à l'échelle, à savoir comment s'authentifier à travers de nombreux outils sans exiger des développeurs qu'ils configurent chaque connexion, et garantit que les développeurs de serveurs MCP n'ont pas à devenir des experts OAuth.</p><p>Deux points saillants de l'exposé :</p><ol><li><p><a href="https://datatracker.ietf.org/doc/rfc9728/"><strong>Métadonnées des ressources protégées</strong></a>: Les serveurs MCP peuvent publier un fichier JSON décrivant leur objectif, leurs points d'accès et leurs méthodes d'authentification. Cela permet aux clients de démarrer des flux OAuth avec seulement l'URL du serveur, ce qui simplifie le processus de connexion. En savoir plus : <a href="https://aaronparecki.com/2025/04/03/15/oauth-for-model-context-protocol">Corrigeons OAuth dans MCP</a></p></li><li><p><a href="https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13"><strong>Prise en charge des IDP et du SSO</strong></a>: les entreprises peuvent intégrer des fournisseurs d'identité pour gérer l'accès de manière centralisée. C'est une victoire pour l'expérience de l'utilisateur et la sécurité. Les utilisateurs n'auraient pas besoin de cliquer sur dix écrans de consentement différents et les équipes de sécurité pourraient observer chaque connexion.</p></li></ol><p>En transférant la logique OAuth au client et en s'appuyant sur les métadonnées des serveurs, l'écosystème MCP évite un goulot d'étranglement majeur. Le MCP s'aligne ainsi plus étroitement sur la manière dont les API modernes sont sécurisées dans les environnements de production d'aujourd'hui.</p><p>Lecture complémentaire : <a href="https://aaronparecki.com/oauth-2-simplified/">OAuth 2 simplifié</a>.</p><h2>Défis de sécurité dans un écosystème composable</h2><p>Les nouveaux développements s'accompagnent également de nouvelles surfaces d'attaque. Arjun Sambamoorthy, de Cisco, énumère les principales menaces qui pèsent sur le paysage MCP :</p><p>Profils</p><p>Description</p><p>Remédiation &amp; meilleures pratiques</p><p>Injection rapide &amp; Empoisonnement des outils</p><p>Un moyen d'injecter une invite malveillante dans le contexte du système LLM ou dans la description de l'outil, amenant le LLM à effectuer des actions involontaires telles que la lecture de fichiers ou la fuite de données.</p><p>Utilisez des outils tels que MCP Scan pour vérifier les métadonnées des outils. Validez les descriptions et les paramètres avant de les inclure dans les invites. Enfin, envisagez de mettre en place des approbations d'utilisateurs pour les outils à haut risque. Pour plus de détails, voir le guide OWASP Prompt Injection dans la liste de lectures complémentaires sous le tableau.</p><p>Attaques par échantillonnage</p><p>Dans le contexte de MCP, l'échantillonnage ouvre la porte au serveur MCP pour effectuer des attaques d'injection rapide sur le LLM.</p><p>Désactiver l'échantillonnage pour les serveurs non fiables et envisager d'ajouter des approbations humaines en boucle pour les demandes d'échantillonnage.</p><p>Serveurs MCP malveillants</p><p>Dans les collections actuelles de serveurs MCP, il est difficile de contrôler chacun d'entre eux pour garantir la sécurité. Des serveurs malveillants peuvent discrètement collecter vos données et les exposer à des acteurs malveillants.</p><p>Ne vous connectez qu'à des serveurs MCP provenant de registres de confiance ou de listes internes. Exécuter des serveurs tiers dans des conteneurs avec sandboxing.</p><p>Outils d'installation MCP malveillants</p><p>Les installateurs de ligne de commande et les scripts sont pratiques pour mettre en œuvre rapidement des serveurs ou des outils MCP, mais vous risquez d'installer du code non vérifié et compromis.</p><p>Installer dans des environnements "sandbox" et valider les signatures des paquets. Ne jamais effectuer de mise à jour automatique à partir de sources non vérifiées.</p><p>Pour lutter contre ce problème, Arjun suggère d'utiliser un registre MCP de confiance pour gérer toutes les vérifications (un sujet qui était au cœur de l'actualité - pour plus de détails, voir les deux premiers éléments de la liste de lecture ci-dessous), ainsi que d'utiliser cette <a href="https://github.com/slowmist/MCP-Security-Checklist">liste de contrôle de la sécurité</a>.</p><p>Lecture complémentaire :</p><ul><li><p><a href="https://modelcontextprotocol.io/specification/2025-06-18/basic/security_best_practices">Meilleures pratiques officielles de sécurité MCP</a></p></li><li><p><a href="https://owasp.org/www-project-top-10-for-large-language-model-applications/">Top 10 des candidatures au LLM de l'OWASP</a></p></li><li><p><a href="https://hiddenlayer.com/innovation-hub/">Recherche sur les menaces HiddenLayer</a></p></li><li><p><a href="https://github.com/invariantlabs-ai/mcp-scan">Scan MCP</a></p></li><li><p><a href="https://genai.owasp.org/llmrisk/llm01-prompt-injection/">Guide OWASP sur l'injection d'invites</a></p></li></ul><h2>Prochaines étapes : Registres, gouvernance et écosystème</h2><p>Un registre centralisé des MCP est en cours d'élaboration et a été l'un des sujets les plus discutés lors du sommet. L'écosystème actuel des serveurs souffre de fragmentation, d'un manque de confiance et d'accessibilité. Il est difficile pour les développeurs de trouver des serveurs MCP, de vérifier ce qu'ils font et de les installer en toute sécurité, en particulier dans un écosystème décentralisé où les métadonnées peuvent être incomplètes ou usurpées.</p><p>Un registre centralisé répond directement à ces problèmes en agissant comme une source de vérité fiable, en améliorant la découvrabilité, en garantissant l'intégrité des métadonnées du serveur et en réduisant le risque d'installation d'outils malveillants.</p><p>Les objectifs du registre MCP sont les suivants :</p><ul><li><p>Offrir une source unique de vérité pour les métadonnées des serveurs (ce que fait un serveur, comment s'authentifier, l'installer et l'appeler)</p></li><li><p>Se débarrasser des registres tiers incomplets et de la fragmentation, de sorte que lorsqu'un serveur veut être enregistré, il n'a pas à mettre à jour tous les autres registres sur l'internet.</p></li><li><p>Fournir un flux d'enregistrement de serveur qui comprend un outil CLI et un fichier server.json qui contient les métadonnées mentionnées précédemment.</p></li></ul><p>L'espoir le plus large est qu'un registre fiable aidera à développer l'écosystème en toute sécurité, en permettant aux développeurs de créer et de partager de nouveaux outils en toute confiance.</p><p>La gouvernance est une autre question prioritaire pour Anthropic. Ils ont clairement indiqué que le projet MCP devait rester ouvert et dirigé par la communauté, mais la mise à l'échelle de ce modèle de gouvernance est encore un travail en cours. Ils recherchent actuellement de l'aide dans ce domaine et demandent à toute personne ayant de l'expérience en matière de gouvernance dans les protocoles à source ouverte de les contacter. Cela nous amène à l'autre sujet que je voulais aborder. Tout au long de l'événement, les intervenants ont insisté sur le fait que l'écosystème ne peut se développer qu'avec les contributions des développeurs. Un effort concentré est nécessaire pour faire de MCP la nouvelle norme web et se démarquer des autres protocoles d'agents populaires.</p><h2>MCP dans le monde réel : Études de cas et démonstrations</h2><p>Plusieurs organisations ont expliqué comment le MCP est déjà utilisé dans des applications pratiques :</p><ul><li><p><strong>PayPal - Serveur MCP pour le commerce agentique : </strong>PayPal a présenté son nouvel <a href="https://github.com/paypal/agent-toolkit/">agent-toolkit</a> et son serveur MCP, qui peuvent changer fondamentalement l'expérience d'achat d'un utilisateur. Au lieu de parcourir les médias sociaux pour trouver des articles, comparer les prix et passer à la caisse, les utilisateurs peuvent discuter avec un agent qui se connecte au serveur MCP de PayPal pour gérer toutes ces actions.
</p></li><li><p><strong>EpicAI.pro - Jarvis :</strong> Les développements de MCP nous rapprochent de plus en plus d'un véritable assistant de type Jarvis. Pour ceux qui ne connaissent pas les films d'Iron Man, Jarvis est un assistant IA qui utilise le langage naturel, répond aux entrées multimodales, n'a aucune latence lorsqu'il répond, est proactif en anticipant les besoins de l'utilisateur, gère automatiquement les intégrations et peut passer d'un appareil à l'autre et d'un lieu à l'autre en fonction du contexte. Si nous imaginons Jarvis comme un assistant robotique physique, MCP lui donne des "mains" ou la capacité de gérer des tâches complexes.
</p></li><li><p><strong>Postman - </strong><a href="https://www.postman.com/explore/mcp-generator"><strong>Générateur de serveur MCP</strong></a><strong>: </strong>Fournit une expérience de panier pour les demandes d'API où vous pouvez choisir différentes demandes d'API, les mettre dans un panier et télécharger l'ensemble du panier en tant que serveur MCP.
</p></li><li><p>Bloomberg <strong>- </strong>Bloomberg a résolu un goulot d'étranglement important dans le développement de la GenAI en entreprise. Avec près de 10 000 ingénieurs, la société avait besoin d'un moyen standardisé pour intégrer les outils et les agents au sein des équipes. Avec MCP, ils ont transformé leurs outils internes en composants modulaires à distance que les agents peuvent facilement appeler sur une interface unifiée. Cela a permis à leurs ingénieurs de fournir des outils à l'ensemble de l'organisation, tandis que les équipes d'intelligence artificielle se sont concentrées sur la création d'agents plutôt que sur des intégrations personnalisées. Bloomberg prend désormais en charge des flux de travail d'agents évolutifs et sécurisés qui débloquent une interopérabilité totale avec l'écosystème MCP. Bloomberg n'a lié aucune ressource publique, mais c'est ce qu'ils ont présenté en public lors du sommet.
</p></li><li><p><strong>Block - </strong>Block utilise MCP pour alimenter <a href="https://github.com/block/goose?tab=readme-ov-file">Goose</a>, un agent d'intelligence artificielle interne qui permet aux employés d'automatiser des tâches dans les domaines de l'ingénierie, des ventes, du marketing, etc. Ils ont construit plus de 60 serveurs MCP pour des outils tels que Git, Snowflake, Jira et Google Workspace afin de permettre une interaction en langage naturel avec les systèmes qu'ils utilisent tous les jours. Les employés de Block utilisent désormais Goose pour interroger les données, détecter les fraudes, gérer les incidents, naviguer dans les processus internes et bien plus encore, le tout sans avoir à écrire de code. MCP a aidé Block à adopter l'IA dans de nombreuses fonctions en seulement 2 mois.
</p></li><li><p><strong>AWS - </strong><a href="https://github.com/awslabs/mcp"><strong>Serveurs MCP AWS</strong></a><strong>: </strong>AWS a présenté un serveur MCP amusant sur le thème de Donjons et Dragons qui simule un lancer de dés, suit les lancers passés et renvoie les résultats à l'aide de HTTP en continu. Cet exemple léger a mis en évidence la facilité avec laquelle il est possible de construire et de déployer des serveurs MCP à l'aide d'outils et d'infrastructures AWS comme Lambda et Fargate. Ils ont également présenté <a href="https://aws.amazon.com/blogs/opensource/introducing-strands-agents-an-open-source-ai-agents-sdk/">Strands SDK</a>, un kit d'outils open-source pour la création d'agents multimodaux qui interagissent avec les serveurs MCP.</p></li></ul><h2>Support MCP dans Elastic Agent Builder</h2><p>Vous pouvez commencer à expérimenter MCP dès aujourd'hui en utilisant <a href="https://www.elastic.co/fr/search-labs/blog/elastic-ai-agent-builder-context-engineering-introduction">Elastic Agent Builder,</a> qui est le moyen le plus simple de créer des agents directement à partir de vos données. Agent Builder vous permet d'exposer des outils basés sur Elasticsearch à des agents compatibles avec MCP et il est déjà livré avec quelques outils intégrés puissants, notamment :</p><ul><li><p><code>platform.core.search</code> - Exécute des recherches à l'aide du DSL Elasticsearch Query complet.</p></li><li><p><code>platform.core.list_indices</code> - Liste de tous les index disponibles dans Elasticsearch (aide les agents à découvrir les données existantes)</p></li><li><p><code>platform.core.get_index_mapping</code> - Récupère les correspondances de champs pour un index spécifique (aide les agents à comprendre la forme et les types de vos données).</p></li><li><p><code>platform.core.get_document_by_id</code> - Recherche d'un document spécifique par son numéro d'identification (pour une recherche précise)</p></li></ul><p>Grâce à ces outils, vous pouvez doter votre agent d'une fonction de recherche et de pertinence au niveau de l'entreprise, ce qui est essentiel pour créer des agents d'intelligence artificielle fiables.</p><p>Ce qui rend Agent Builder encore plus puissant, c'est la possibilité de définir et d'exposer vos propres outils personnalisés, adaptés aux besoins de votre application. Ceci est particulièrement utile pour les flux de travail basés sur l'opinion ou répétables lorsque vous souhaitez que l'agent effectue un type de recherche spécifique sur un index spécifique sans avoir à redécouvrir cette logique à chaque fois. Au lieu de dépenser des jetons en planification et en raisonnement pour arriver à la même conclusion, vous pouvez encoder cette intention directement dans un outil, ce qui rend vos agents plus rapides, plus fiables et plus rentables.</p><p>Dans l'interface utilisateur de l'Agent Builder, voici un exemple de définition d'outil personnalisé qui utilise ES|QL :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltca9e3d0a4e7031c0/6a17e803faa913d8c393c897/c1f6405a374b707e8e6fa36b9e21db5f3c7cd127-1376x864.png" alt="Interface utilisateur de l'Agent Builder" /><p>Une fois que vous avez défini vos outils personnalisés, vous pouvez les exposer (ainsi que les outils natifs intégrés) à l'aide de MCP en cliquant sur la liste déroulante <code>Manage MCP</code> et en copiant l'URL du serveur MCP.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf7d8b29b06c08f94/6a17e805033c8d07f06bb1b6/9f39588525ca2643475de557ea54a6bcf5c150f6-1282x616.png" alt="Outils MCP" /><p>Vous pouvez maintenant importer ce point de terminaison MCP dans n'importe quel client qui consomme MCP pour le connecter à Agent Builder et lui donner accès à tous les outils disponibles. Pour plus d'informations, lisez cette introduction à <a href="https://www.elastic.co/fr/search-labs/blog/elastic-ai-agent-builder-context-engineering-introduction">Agent Builder</a>.</p><h2>Conclusion</h2><p>Le MCP Dev Summit a clairement montré que le MCP façonne la manière dont ces agents d'intelligence artificielle interagissent entre eux et avec le monde des données qui les entoure. Qu'il s'agisse de connecter un agent aux données de l'entreprise ou de concevoir des agents entièrement autonomes, MCP offre une méthode d'intégration normalisée et composable qui devient rapidement utile à grande échelle. Des protocoles de transport et des modèles de sécurité aux registres et à la gouvernance, l'écosystème MCP mûrit rapidement. Le programme MCP restera ouvert et axé sur la communauté, de sorte que les développeurs d'aujourd'hui ont la possibilité de façonner son évolution.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/mcp-current-state</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/mcp-current-state</guid>
    <category><![CDATA[IA]]></category>
    <dc:creator><![CDATA[JD Armada]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2f63f23bbecd2a18/6a17e8066317302039585aa7/02b8c8672ffa129e0ed91a92d6cab612a01d27f2-1200x628.png" length="0" type="image/png"/>
    <pubDate>Thu, 12 Jun 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Spring AI et Elasticsearch comme base de données vectorielle]]></title>
    <description><![CDATA[Apprenez à créer une application RAG prête pour la production en utilisant Spring IA et Elasticsearch et à intégrer des LLM à vos données propriétaires à l’aide d’une Base vectorielle.
]]></description>
    <content:encoded><![CDATA[<p><strong>Spring AI</strong> est maintenant disponible, avec sa première <a href="https://spring.io/blog/2025/05/20/spring-ai-1-0-GA-released">version stable 1.0</a> prête à être téléchargée sur <a href="https://mvnrepository.com/artifact/org.springframework.ai/spring-ai-core">Maven Central.</a> Utilisons-le immédiatement pour construire une application d'IA complète, en utilisant votre <a href="https://www.elastic.co/what-is/large-language-models">LLM</a> favori et notre <a href="https://www.elastic.co/elasticsearch/vector-database">base de données vectorielle</a> préférée. Ou plongez directement dans le <a href="https://github.com/xeraa/rag-with-java-springai-elasticsearch">référentiel</a> avec l'application finale.</p><h2>Qu'est-ce que l'IA de printemps ?</h2><p><strong>Spring AI 1.0</strong>, une solution complète pour l'ingénierie de l'IA en Java, est maintenant disponible après une période de développement significative influencée par les progrès rapides dans le domaine de l'IA. Cette version comprend de nombreuses nouvelles fonctionnalités essentielles pour les ingénieurs en IA.</p><p>Java et Spring sont les mieux placés pour profiter de la vague de l'IA. Des tonnes d'entreprises utilisent Spring Boot, ce qui facilite grandement l'intégration de l'IA dans ce qu'elles font déjà. Vous pouvez relier votre logique d'entreprise et vos données à ces modèles d'IA sans trop de difficultés.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltee1f144eb9e74866/6a17e379dbb4ffdf4bfb5647/328d7c51e1c145e94ea1e73ee9ff91836d3b180e-1600x773.png" alt="Comment utiliser Spring IA avec Elasticsearch" /><p>Spring AI prend en charge <a href="https://docs.spring.io/spring-ai/reference/api/index.html">différents modèles et technologies d'IA</a>, tels que :</p><ul><li><p><strong>Modèles d'images</strong>: générer des images à partir de textes.</p></li><li><p><strong>Modèles de transcription</strong>: prendre des sources audio et les convertir en texte.</p></li><li><p><strong>Modèles d'intégration : </strong>convertissent des données arbitraires en <a href="https://www.elastic.co/what-is/vector-embedding">vecteurs</a>, qui sont des types de données optimisés pour la recherche de similarités sémantiques.</p></li><li><p><strong>Modèles de chat : </strong>cesdevraient vous être familiers ! Vous avez sans doute déjà eu une brève conversation avec l'un d'entre eux.</p></li></ul><p>Les modèles de dialogue en ligne sont ceux qui semblent le plus faire parler d'eux dans le domaine de l'IA, et à juste titre, ils sont géniaux ! Ils peuvent vous aider à corriger un document ou à écrire un poème. (Ne leur demandez pas de raconter une blague... pour l'instant). Ils sont géniaux, mais ils ont quelques problèmes.</p><h2>Des solutions d'IA de printemps pour relever les défis de l'IA</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbd2d062ded38cf83/6a17e37adbb4ff69d7fb564b/2ebd68a90ebc73847df6ef7325936d4d06b35c8c-1600x900.jpg" alt="Solutions Spring IA pour les défis de l’IA" /><p>Examinons quelques-uns de ces problèmes et leurs solutions dans Spring AI.</p><p></p><p>Problème</p><p>Solution</p><p>Cohérence</p><p>Les modèles de chat sont ouverts d'esprit et enclins à la distraction.</p><p>Vous pouvez les doter d'un système d'incitation pour régir leur forme et leur structure générales</p><p>Mémoire</p><p>Les modèles d'IA n'ont pas de mémoire et ne peuvent donc pas établir de corrélation entre les messages d'un utilisateur donné et ceux d'un autre utilisateur.</p><p>Vous pouvez leur donner un système de mémoire pour stocker les parties pertinentes de la conversation</p><p>L'isolement</p><p>Les modèles d'IA vivent dans de petits bacs à sable isolés, mais ils peuvent faire des choses vraiment étonnantes si vous leur donnez accès à des outils - des fonctions qu'ils peuvent invoquer lorsqu'ils le jugent nécessaire</p><p>Spring AI prend en charge l'appel d'outils, ce qui vous permet d'indiquer au modèle d'IA les outils présents dans son environnement, qu'il peut ensuite vous demander d'invoquer. Cette interaction à plusieurs tours est gérée de manière transparente pour vous</p><p>Données privées</p><p>Les modèles d'IA sont intelligents, mais ils ne sont pas omniscients ! Ils ne savent pas ce que contiennent vos bases de données propriétaires - et nous pensons que vous ne voudriez pas qu'ils le sachent !</p><p>Vous devez informer leurs réponses en remplissant les invites, c'est-à-dire en utilisant le puissant opérateur de concaténation de chaînes pour insérer du texte dans la demande avant que le modèle n'examine la question posée. Des informations de base, si vous voulez. Comment décidez-vous de ce qui doit être envoyé et de ce qui ne doit pas l'être ? Utilisez un magasin de vecteurs pour sélectionner uniquement les données pertinentes et les envoyer ensuite. C'est ce que l'on appelle la génération augmentée par récupération (RAG).</p><p>Hallucination</p><p>Les modèles de chat de l'IA aiment discuter ! Et parfois, ils le font avec tant d'assurance qu'ils peuvent inventer des choses</p><p>Vous devez recourir à l'évaluation - en utilisant un modèle pour valider les résultats d'un autre - pour confirmer des résultats raisonnables.</p><p></p><p>Bien entendu, aucune application d'IA n'est isolée. Aujourd'hui, les systèmes et services d'IA modernes fonctionnent mieux lorsqu'ils sont intégrés à d'autres systèmes et services. <a href="https://modelcontextprotocol.io/introduction"><strong>Model Context Protocol</strong></a>(MCP) permet de connecter vos applications d'intelligence artificielle à d'autres services basés sur MCP, quel que soit le langage dans lequel elles sont écrites. Vous pouvez assembler tous ces éléments dans des flux de travail <strong>agentiques </strong>qui mènent à un objectif plus large.</p><p>Le plus beau ? Vous pouvez faire tout cela en vous appuyant sur les idiomes et abstractions familiers auxquels tout développeur Spring Boot est habitué : des dépendances de départ pratiques pour pratiquement tout sont disponibles sur <a href="https://start.spring.io"><strong>Spring Initializr</strong></a><strong>.</strong></p><p>Spring AI fournit des autoconfigurations Spring Boot pratiques qui vous donnent la configuration conventionnelle sur la configuration que vous connaissez et attendez. Spring AI prend en charge l'observabilité avec l'Actuator de Spring Boot et le projet Micrometer. Il est également compatible avec GraalVM et les threads virtuels, ce qui vous permet de créer des applications d'IA super rapides et efficaces qui évoluent.</p><h2>Pourquoi Elasticsearch ?</h2><p>Elasticsearch est un moteur de recherche plein texte, vous le savez probablement. Alors pourquoi l'utiliser pour ce projet ? C'est <em>aussi</em> un magasin de vecteurs ! Il s'agit d'un excellent outil, dans lequel les données sont placées à côté du texte intégral. Autres avantages notables :</p><ul><li><p>Très facile à mettre en place</p></li><li><p>Opensource</p></li><li><p>Évolutivité horizontale</p></li><li><p>La plupart des données libres de votre organisation se trouvent probablement déjà dans un cluster Elasticsearch.</p></li><li><p>Fonctionnalité de moteur de recherche complète</p></li><li><p>Entièrement <a href="https://docs.spring.io/spring-ai/reference/api/vectordbs/elasticsearch.html">intégré à Spring AI</a>!</p></li></ul><p>Si l'on tient compte de tous ces éléments, Elasticsearch remplit toutes les conditions d'un excellent magasin de vecteurs, alors configurons-le et commençons à construire notre application !</p><h2>Prise en main d'Elasticsearch</h2><p>Nous allons avoir besoin d'Elasticsearch et de Kibana, la console d'interface utilisateur que vous utiliserez pour interagir avec les données hébergées dans la base de données.</p><p>Vous pouvez tout essayer sur votre machine locale grâce aux images Docker et à la <a href="http://elastic.co">page d'accueil d'Elastic.co.</a> Allez-y, faites défiler vers le bas pour trouver la commande <code>curl</code>, exécutez-la et envoyez-la directement dans votre shell :</p> curl -fsSL https://elastic.co/start-local | sh 
  ______                     
 |  ____| |         | | (_)     
 | |__  | | __  ___| |_   ___ 
 |  __| | |/ _` / __| __| |/ __|
 | |____| | (_| \__ \ |_| | (__ 
 |______|_|\__,_|___/\__|_|\___|
-------------------------------------------------
🚀 Run Elasticsearch and Kibana for local testing
-------------------------------------------------
ℹ️  Do not use this script in a production environment
⌛️ Setting up Elasticsearch and Kibana v9.0.0...
- Generated random passwords
- Created the elastic-start-local folder containing the files:
  - .env, with settings
  - docker-compose.yml, for Docker services
  - start/stop/uninstall commands
- Running docker compose up --wait
[+] Running 25/26
 ✔ kibana_settings Pulled                                                 16.7s 
 ✔ kibana Pulled                                                          26.8s 
 ✔ elasticsearch Pulled                                                   17.4s                                                                     
[+] Running 6/6
 ✔ Network elastic-start-local_default             Created                 0.0s 
 ✔ Volume "elastic-start-local_dev-elasticsearch"  Created                 0.0s 
 ✔ Volume "elastic-start-local_dev-kibana"         Created                 0.0s 
 ✔ Container es-local-dev                          Healthy                12.9s 
 ✔ Container kibana_settings                       Exited                 11.9s 
 ✔ Container kibana-local-dev                      Healthy                21.8s 
🎉 Congrats, Elasticsearch and Kibana are installed and running in Docker!
🌐 Open your browser at http://localhost:5601
   Username: elastic
   Password: w1GB15uQ
🔌 Elasticsearch API endpoint: http://localhost:9200
🔑 API key: SERqaGlKWUJLNVJDODc1UGxjLWE6WFdxSTNvMU5SbVc5NDlKMEhpMzJmZw==
Learn more at https://github.com/elastic/start-local
➜  ~ <p>Il suffit de tirer et de configurer des images Docker pour Elasticsearch et Kibana, et après quelques minutes, elles seront opérationnelles sur votre machine locale, avec les informations d'identification de connexion.</p><p>Vous avez également deux urls différentes que vous pouvez utiliser pour interagir avec votre instance Elasticsearch. Suivez les instructions et dirigez votre navigateur vers <a href="http://localhost:5601">http://localhost:5601</a>.</p><p>Notez également le nom d'utilisateur <code>elastic</code> et le mot de passe affichés sur la console : vous en aurez besoin pour vous connecter (dans l'exemple ci-dessus, il s'agit respectivement de <code>elastic</code> et <code>w1GB15uQ</code>).</p><p></p><h2>L'intégration de l'application</h2><p>Allez sur la page <a href="https://start.spring.io">Spring Initializr</a> et générez un nouveau projet Spring AI avec les dépendances suivantes :</p><ul><li><p><code>Elasticsearch Vector Store</code></p></li><li><p><code>Spring Boot Actuator</code></p></li><li><p><code>GraalVM</code></p></li><li><p><code>OpenAI</code></p></li><li><p><code>Web</code></p></li></ul><p>Veillez à choisir la dernière et la meilleure version de Java (idéalement Java 24 - au moment de la rédaction de ce document - ou une version plus récente) et l'outil de construction de votre choix. Nous utilisons Apache Maven dans cet exemple.</p><p>Cliquez sur <code>Generate</code>, puis décompressez le projet et importez-le dans l'IDE de votre choix. (Nous utilisons IntelliJ IDEA.)</p><p>Commençons par le commencement : spécifions les détails de la connexion pour votre application Spring Boot. Sur <code>application.properties,</code>, écrivez ce qui suit :</p>spring.elasticsearch.uris=http://localhost:9200
spring.elasticsearch.username=elastic
spring.elasticsearch.password=w1GB15uQ<p>Nous allons également utiliser la capacité de stockage vectoriel de Spring AI pour initialiser tout ce qui est nécessaire du côté d'Elasticsearch en termes de structures de données, donc spécifier :</p>spring.ai.vectorstore.elasticsearch.initialize-schema=true<p>Nous allons utiliser <strong>OpenAI</strong> dans cette démo, en particulier le <strong>modèle d'intégration</strong> et le <strong>modèle de conversation </strong>(n'hésitez pas à utiliser le service que vous préférez, tant que <a href="https://docs.spring.io/spring-ai/reference/api/embeddings.html#available-implementations">Spring AI le prend en charge</a>).</p><p>Le modèle d'intégration est nécessaire pour créer des intégrations des données avant de les stocker dans Elasticsearch. Pour qu'OpenAI fonctionne, nous devons spécifier l'adresse <code>API key</code>:</p>spring.ai.openai.api-key=...<p>Vous pouvez la définir comme une variable d'environnement telle que <code>SPRING_AI_OPENAI_API_KEY</code> pour éviter d'avoir à la stocker dans votre code source.</p><p>Nous allons télécharger des fichiers, donc assurez-vous de personnaliser la quantité de données pouvant être téléchargées vers le conteneur de servlet :</p>spring.servlet.multipart.max-file-size=20MB
spring.servlet.multipart.max-request-size=20MB<p>Nous y sommes presque ! Avant de nous plonger dans l'écriture du code, donnons un aperçu de la manière dont cela va fonctionner.</p><p>Sur notre machine, nous avons téléchargé le <a href="https://images-cdn.fantasyflightgames.com/filer_public/9f/aa/9faa23a3-9f71-4c77-865f-bba4aac8a258/runewars-revised-_rulebook.pdf">fichier suivant</a> (une liste de règles pour un jeu de société), l'avons renommé en <code>test.pdf</code> et l'avons placé dans <code>~/Downloads/test.pdf</code>.</p><p>Le fichier sera envoyé au point de terminaison <code>/rag/ingest</code> (remplacez le chemin d'accès en fonction de votre configuration locale) :</p>http --form POST http://localhost:8080/rag/ingest path@/Users/jlong/Downloads/test.pdf<p>Cela peut prendre quelques secondes...</p><p>En coulisses, les données sont envoyées à OpenAI, qui crée des enchâssements de données ; ces données sont ensuite écrites dans Elasticsearch, à la fois les vecteurs et le texte d'origine.</p><p>C'est à partir de ces données, et de tous les enchâssements qu'elles contiennent, que la magie opère. Nous pouvons ensuite interroger Elasticsearch à l'aide de l'interface <code>VectorStore</code>.</p><p>Le flux complet se présente comme suit :</p><ul><li><p>Le client HTTP télécharge le PDF de votre choix vers l'application Spring.</p></li><li><p>Spring AI se charge de l'extraction du texte de notre PDF et découpe chaque page en morceaux de 800 caractères.</p></li><li><p>OpenAI génère la représentation vectorielle pour chaque morceau.</p></li><li><p>Le texte fragmenté et l'intégration sont ensuite stockés dans Elasticsearch.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt93f4a64b634e9cce/6a17e37cb1e113215879f216/9734adb2d7128e61c515d5855dfad6d3a326a4a1-1454x706.png" alt="Workflow complet : extraction PDF (Spring AI), représentation vectorielle (OpenAI) et découpage de texte (Elasticsearch) pour générer des embeddings." /><p>Enfin, nous émettrons une requête :</p>http :8080/rag/query question=="where do you place the reward card after obtaining it?" <p>Et nous obtiendrons une réponse pertinente :</p>After obtaining a Reward card, you place it facedown under the Hero card of the hero who received it.
Found at page: 28 of the manual<p>C'est bien ! Comment cela fonctionne-t-il ?</p><ul><li><p>Le client HTTP soumet la question à l'application Spring.</p></li><li><p>Spring AI obtient la représentation vectorielle de la question auprès d'OpenAI.</p></li><li><p>Cette intégration permet de rechercher des documents similaires dans les morceaux Elasticsearch stockés et d'extraire les documents les plus similaires.</p></li><li><p>Spring AI envoie ensuite la question et le contexte récupéré à OpenAI pour générer une réponse LLM.</p></li><li><p>Enfin, il renvoie la réponse générée et une référence au contexte récupéré.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfab41731851104f3/6a17e37e445de90e924d00aa/3799de6e8cb13ce49b9e136cfe593263030231a8-1464x1050.png" alt="Workflow complet :Spring IA et Open IA pour générer la réponse LLM à une question." /><p>Plongeons dans le code Java pour voir comment cela fonctionne réellement.</p><p>Tout d'abord, la classe <strong>Main</strong>: il s'agit d'une classe principale standard pour n'importe quelle application Spring Boot.</p>@SpringBootApplication
public class DemoApplication {
 	public static void main(String[] args) { 
     		SpringApplication.run(DemoApplication.class, args);
 	}
}<p>Il n'y a rien à voir. Continuons...</p><p>Ensuite, un contrôleur HTTP de base :</p>@RestController
class RagController {

   private final RagService rag;

   RagController(RagService rag) {
       this.rag = rag;
   }

   @PostMapping("/rag/ingest")
   ResponseEntity&lt;?&gt; ingestPDF(@RequestBody MultipartFile path) {
       rag.ingest(path.getResource());
       return ResponseEntity.ok().body("Done!");
   }

   @GetMapping("/rag/query")
   ResponseEntity&lt;?&gt; query(@RequestParam String question) {
       String response = rag.directRag(question);
       return ResponseEntity.ok().body(response);
   }
}<p>Le contrôleur appelle simplement un service que nous avons construit pour gérer l'ingestion de fichiers et leur écriture dans la base de données vectorielles Elasticsearch, puis pour faciliter les requêtes sur cette même base de données vectorielles.</p><p>Examinons le service :</p>@Service
class RagService {

   private final ElasticsearchVectorStore vectorStore;

   private final ChatClient ai;

   RagService(ElasticsearchVectorStore vectorStore, ChatClient.Builder clientBuilder) {
       this.vectorStore = vectorStore;
       this.ai = clientBuilder.build();
   }

   void ingest(Resource path) {
       PagePdfDocumentReader pdfReader = new PagePdfDocumentReader(path);
       List&lt;Document&gt; batch = new TokenTextSplitter().apply(pdfReader.read());
       vectorStore.add(batch);
   }

  // TBD
}<p>Ce code gère l'ensemble de l'ingestion : étant donné un Spring Framework <code>Resource</code>, qui est un conteneur d'octets, nous lisons les données PDF (présumées être un fichier <code>.PDF</code> - assurez-vous de le valider avant d'accepter des entrées arbitraires !) à l'aide de Spring AI <code>PagePdfDocumentReader</code>, puis nous les tokenisons à l'aide de Spring AI <code>TokenTextSplitter</code>, et enfin nous ajoutons les <code>List&lt;Document&gt;</code>s résultants à l'implémentation de <code>VectorStore</code>, <code>ElasticsearchVectorStore</code>.</p><p>Vous pouvez le confirmer en utilisant Kibana : après avoir envoyé un fichier au point de terminaison <code>/rag/ingest</code>, ouvrez votre navigateur sur <code>localhost:5601</code> et, dans le menu latéral de gauche, naviguez vers <code>Dev Tools</code>. Vous pouvez y émettre des requêtes pour interagir avec les données de l'instance Elasticsearch.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt45805a5b2da5e336/6a17e3803e03d70a584f2bda/c85e522f02f8b2da7462cd428dc7e952c9692542-1600x1040.png" alt="Comment créer une requête dans l’instance Elasticsearch." /><p>Emettez une requête comme celle-ci :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt21d79210fe213b1e/6a17e382e3179163492d5767/00974a176cbce11e70fcab24fb4b3f9c6e205982-1600x1040.png" alt="Envoyer une requête dans la console Elasticsearch." /><p>Passons maintenant aux choses sérieuses : comment récupérer ces données en réponse à des requêtes d'utilisateurs ?</p><p>Voici un premier aperçu de la mise en œuvre de la requête, dans une méthode appelée <code>directRag</code>.</p>String directRag(String question) {
   // Query the vector store for documents related to the question
   List&lt;Document&gt; vectorStoreResult =
           vectorStore.doSimilaritySearch(SearchRequest.builder().query(question).topK(5)
                   .similarityThreshold(0.7).build());

   // Merging the documents into a single string
   String documents = vectorStoreResult.stream()
           .map(Document::getText)
           .collect(Collectors.joining(System.lineSeparator()));

   // Exit if the vector search didn't find any results
   if (documents.isEmpty()) {
       return "No relevant context found. Please change your question.";
   }

   // Setting the prompt with the context
   String prompt = """
           You're assisting with providing the rules of the tabletop game Runewars.
           Use the information from the DOCUMENTS section to provide accurate answers to the
           question in the QUESTION section.
           If unsure, simply state that you don't know.
          
           DOCUMENTS:
           """ + documents
           + """
           QUESTION:
           """ + question;


   // Calling the chat model with the question
   String response = ai
           .prompt()
           .user(prompt)
           .call()
           .content();

   return response +
           System.lineSeparator() +
           "Found at page: " +
           // Retrieving the first ranked page number from the document metadata
           vectorStoreResult.getFirst().getMetadata().get(PagePdfDocumentReader.METADATA_START_PAGE_NUMBER) +
           " of the manual";

}<p>Le code est assez simple, mais nous allons le décomposer en plusieurs étapes :</p><ol><li><p>Utilisez le site <code>VectorStore</code> pour effectuer une recherche de similitude.</p></li><li><p>Étant donné tous les résultats, récupérer les <code>Document</code>s sous-jacents de Spring AI et extraire leur texte, en les concaténant tous en un seul résultat.</p></li><li><p>Envoyer les résultats du site <code>VectorStore</code> au modèle, accompagnés d'une invite indiquant au modèle ce qu'il doit en faire et de la question de l'utilisateur. Attendez la réponse et renvoyez-la.</p></li></ol><p></p><p>Il s'agit de la <strong>RAG</strong> (retrieval augmented generation). Il s'agit de l'idée que nous utilisons des données provenant d'un magasin de vecteurs pour informer le traitement et l'analyse effectués par le modèle. Maintenant que vous savez comment faire, espérons que vous n'aurez jamais à le faire ! En tout cas, pas de cette manière : Les <a href="https://docs.spring.io/spring-ai/reference/api/advisors.html">conseillers de</a> Spring AI sont là pour simplifier encore davantage ce processus.</p><p>Advisors vous permet de pré- et post-traiter une requête vers un modèle donné, en plus de fournir une couche d'abstraction entre votre application et le magasin de vecteurs. Ajoutez la dépendance suivante à votre construction :
</p>&lt;dependency&gt;
   &lt;groupId&gt;org.springframework.ai&lt;/groupId&gt;
   &lt;artifactId&gt;spring-ai-advisors-vector-store&lt;/artifactId&gt;
&lt;/dependency&gt;<p>Ajoutez une autre méthode appelée <code>advisedRag(String question)</code> à la classe :</p>String advisedRag(String question) {
   return this.ai
           .prompt()
           .user(question)
           .advisors(new QuestionAnswerAdvisor(vectorStore))
           .call()
           .content();
}<p>Toute la logique du modèle RAG est encapsulée dans le site <code>QuestionAnswerAdvisor</code>. Pour le reste, tout se passe comme n'importe quelle demande adressée à <code>ChatModel</code>! C'est bien !</p><p>Vous pouvez <a href="https://github.com/xeraa/rag-with-java-springai-elasticsearch">obtenir le code complet sur GitHub</a>.</p><h2>Conclusion</h2><p>Dans cette démo, nous avons utilisé des images Docker et tout fait sur notre machine locale, mais l'objectif ici est de construire des systèmes et des services d'IA dignes d'une production. Il y a plusieurs choses que vous pouvez faire pour que cela devienne une réalité.</p><p>Tout d'abord, vous pouvez ajouter <a href="https://docs.spring.io/spring-boot/reference/actuator/index.html#actuator">Spring Boot Actuator</a> pour surveiller la consommation de jetons. Les jetons sont une approximation du coût de la complexité (et parfois du coût en dollars) d'une demande donnée au modèle.</p><p>Vous avez déjà l'Actuator Spring Boot sur le classpath, il suffit donc de spécifier les propriétés suivantes pour afficher toutes les métriques (capturées par le magnifique projet <a href="http://micrometer.io">Micrometer.io</a> ) :</p>management.endpoints.web.exposure.include=*<p>Redémarrez votre application. Effectuez une recherche, puis rendez-vous sur <a href="http://localhost:8080/actuator/metrics">:</a> http://localhost:8080/actuator/metrics. Recherchez "<code>token</code>" et vous verrez des informations sur les jetons utilisés par l'application. Veillez à garder un œil sur ce point. Vous pouvez bien sûr utiliser <a href="https://docs.micrometer.io/micrometer/reference/implementations/elastic.html">l' intégration de Micrometer pour Elasticsearch</a> pour pousser ces métriques et faire en sorte qu'Elasticsearch agisse comme votre base de données de séries temporelles de choix, aussi !</p><p>Il faut alors considérer que chaque fois que nous faisons une requête à un entrepôt de données comme Elasticsearch, ou à OpenAI, ou à d'autres services réseau, nous faisons de l'IO et - souvent - cette IO bloque les threads sur lesquels elle s'exécute. Java 21 et les versions ultérieures intègrent des <strong>threads virtuels</strong> non bloquants qui améliorent considérablement l'évolutivité. L'activer avec :
</p>spring.threads.virtual.enabled=true<p>Enfin, vous voudrez héberger votre application et vos données dans un endroit où elles pourront prospérer et évoluer. Nous sommes certains que vous avez déjà réfléchi à l'endroit où vous allez exécuter votre application, mais où allez-vous héberger vos données ? Pouvons-nous vous recommander l'<a href="https://cloud.elastic.co/">Elastic Cloud</a>? Il est sécurisé, privé, évolutif et doté de nombreuses fonctionnalités. Ce que nous préférons ? Si vous le souhaitez, vous pouvez obtenir l'édition Serverless dans laquelle Elastic porte le pager, pas vous !</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/spring-ai-elasticsearch-application</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/spring-ai-elasticsearch-application</guid>
    <category><![CDATA[IA]]></category>
    <dc:creator><![CDATA[Josh Long,Philipp Krenn,Laura Trotta]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt26b868ef618164c6/6a17e3830b0bedd68cdd3515/0771fb5b3d9234697cb868cd7d9d1b840000bf29-1280x720.png" length="0" type="image/png"/>
    <pubDate>Tue, 20 May 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[​​Construire un flux de travail RAG en utilisant LangGraph et Elasticsearch]]></title>
    <description><![CDATA[Apprenez à configurer et à personnaliser un modèle d'agent de récupération LangGraph avec Elasticsearch afin de créer un flux de travail RAG pour une récupération efficace des données et des réponses basées sur l'IA.]]></description>
    <content:encoded><![CDATA[<p>Le <a href="https://github.com/langchain-ai/retrieval-agent-template">modèle d'agent de recherche LangGraph</a> est un projet de démarrage développé par LangChain pour faciliter la création de systèmes de réponse aux questions basés sur la recherche en utilisant LangGraph dans LangGraph Studio. Ce modèle est préconfiguré pour s'intégrer de manière transparente à Elasticsearch, ce qui permet aux développeurs de créer rapidement des agents capables d'indexer et d'extraire des documents de manière efficace.</p><p>Ce blog se concentre sur l'exécution et la personnalisation du modèle d'agent de récupération LangChain en utilisant LangGraph Studio et LangGraph CLI. Le modèle fournit un cadre pour la construction d'applications de génération augmentée de recherche (RAG), en tirant parti de divers backends de recherche tels qu'Elasticsearch.</p><p>Nous vous guiderons dans la mise en place, la configuration de l'environnement et l'exécution efficace du modèle avec Elastic tout en personnalisant le flux de l'agent.</p><h2>Produits requis</h2><p>Avant de poursuivre, assurez-vous que les éléments suivants sont installés :</p><ul><li><p>Déploiement d'Elasticsearch Cloud ou déploiement d'Elasticsearch sur site (ou créer un <a href="https://www.elastic.co/fr/cloud/cloud-trial-overview">essai gratuit de </a>14 jours sur Elastic Cloud) - Version 8.0.0 ou supérieure</p></li><li><p>Python 3.9+</p></li><li><p>Accès à un fournisseur de LLM tel que <a href="https://cohere.com/">Cohere</a> (utilisé dans ce guide), <a href="https://openai.com/">OpenAI</a>, ou <a href="https://www.anthropic.com/claude">Anthropic/Claude</a></p></li></ul><h2>Création de l'application LangGraph</h2><h3>1. Installer l'interface de programmation LangGraph</h3>pip install --upgrade "langgraph-cli[inmem]"<h3>2. Créer une application LangGraph à partir de retrieval-agent-template</h3>mkdir lg-agent-demo
cd lg-agent-demo
langgraph new lg-agent-demo <p><em>Un menu interactif vous permettra de choisir parmi une liste de modèles disponibles. </em>Sélectionnez 4 pour Retrieval Agent et 1 pour Python, comme indiqué ci-dessous :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd44177037ea46d45/6a17f86b3e9e45265fba1663/6a41a41f95c2477c67810adc7be46d91faf06878-1600x407.png" alt="Modèle de recherche interactive." /><ul><li><p><strong>Dépannage</strong>: Si vous rencontrez l'erreur "urllib.error.URLError : &lt;urlopen error [SSL : CERTIFICATE_VERIFY_FAILED] certificate verify failed : unable to get local issuer certificate (_ssl.c:1000)&gt; "</p></li></ul><p>Veuillez exécuter la commande Install Certificate de Python pour résoudre le problème, comme indiqué ci-dessous.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbbe2d1d3a1af75b1/6a17f86d445de97c9c4d02e7/83ec238136c41738457299fd42c83aff32eb5b97-1407x75.png" alt="Exécution de la commande Python Install Certificate." /><h3>3. Installer les dépendances</h3><p>À la racine de votre nouvelle application LangGraph, créez un environnement virtuel et installez les dépendances en mode <code>edit</code> afin que vos modifications locales soient utilisées par le serveur :</p>#For Mac
python3 -m venv lg-demo
source lg-demo/bin/activate 
pip install -e .

#For Windows
python3 -m venv lg-demo
lg-demo\Scripts\activate 
pip install -e .<h2>Mise en place de l'environnement</h2><h3>1. Créez un environnement . fichier</h3><p>Le fichier <code>.env</code> contient les clés API et les configurations permettant à l'application de se connecter au fournisseur de LLM et de récupération que vous avez choisi. Générer un nouveau fichier <code>.env</code> en dupliquant l'exemple de configuration :</p>cp .env.example .env<h3>2. Configurez le fichier .env fichier</h3><p>Le fichier <code>.env</code> est livré avec un ensemble de configurations par défaut. Vous pouvez le mettre à jour en ajoutant les clés et valeurs API nécessaires en fonction de votre configuration. Les clés qui ne sont pas pertinentes pour votre cas d'utilisation peuvent être laissées inchangées ou supprimées.</p># To separate your traces from other applications
LANGSMITH_PROJECT=retrieval-agent

# LLM choice (set the API key for your selected provider):
ANTHROPIC_API_KEY=your_anthropic_api_key
FIREWORKS_API_KEY=your_fireworks_api_key
OPENAI_API_KEY=your_openai_api_key

# Retrieval provider (configure based on your chosen service):

## Elastic Cloud:
ELASTICSEARCH_URL=https://your_elastic_cloud_url
ELASTICSEARCH_API_KEY=your_elastic_api_key

## Elastic Local:
ELASTICSEARCH_URL=http://host.docker.internal:9200
ELASTICSEARCH_USER=elastic
ELASTICSEARCH_PASSWORD=changeme

## Pinecone:
PINECONE_API_KEY=your_pinecone_api_key
PINECONE_INDEX_NAME=your_pinecone_index_name

## MongoDB Atlas:
MONGODB_URI=your_mongodb_connection_string

# Cohere API key:
COHERE_API_KEY=your_cohere_api_key<ul><li><p>Exemple de fichier <code>.env</code> (avec Elastic Cloud et Cohere)</p></li></ul><p>Vous trouverez ci-dessous un exemple de configuration <code>.env</code> pour l'utilisation d'<strong>Elastic Cloud</strong> en tant que fournisseur d'extraction et de <strong>Cohere</strong> en tant que LLM, comme démontré dans ce blog :</p># To separate your traces from other applications
LANGSMITH_PROJECT=retrieval-agent
#Retrieval Provider
# Elasticsearch configuration
ELASTICSEARCH_URL=elastic-url:443
ELASTICSEARCH_API_KEY=elastic_api_key
# Cohere API key
COHERE_API_KEY=cohere_api_key<p><em>Note : Bien que ce guide utilise Cohere pour la génération de réponses et l'intégration, vous êtes libre d'utiliser d'autres fournisseurs LLM tels que </em><em><strong>OpenAI</strong></em><em>, </em><em><strong>Claude</strong></em><em>, ou même un modèle LLM local en fonction de votre cas d'utilisation. Assurez-vous que chaque clé que vous avez l'intention d'utiliser est présente et correctement définie dans le </em><em> fichier</em><em><code>.env</code></em>.</p><h3>3. Mise à jour du fichier de configuration -configuration.py </h3><p>Après avoir configuré votre fichier <code>.env</code> avec les clés API appropriées, l'étape suivante consiste à mettre à jour la configuration du modèle par défaut de votre application. La mise à jour de la configuration garantit que le système utilise les services et les modèles que vous avez spécifiés dans votre fichier <code>.env</code>.</p><p>Accédez au fichier de configuration :</p> cd src/retrieval_graph<p>Le fichier <code>configuration.py</code> contient les paramètres du modèle par défaut utilisés par l'agent de recherche pour trois tâches principales :</p><ul><li><p><strong>Modèle d'intégration</strong> - convertit les documents en représentations vectorielles</p></li><li><p><strong>Modèle de requête</strong> - traite la requête de l'utilisateur en un vecteur</p></li><li><p><strong>Modèle de réponse</strong> - génère la réponse finale</p></li></ul><p>Par défaut, le code utilise les modèles d'<strong>OpenAI</strong> (par exemple, <code>openai/text-embedding-3-small</code>) et d'<strong>Anthropic</strong> (par exemple, <code>anthropic/claude-3-5-sonnet-20240620 and anthropic/claude-3-haiku-20240307</code>).

Dans ce blog, nous passerons à l'utilisation des modèles Cohere. Si vous utilisez déjà OpenAI ou Anthropic, aucun changement n'est nécessaire.</p><h4>Exemple de modifications (en utilisant Cohere) :</h4><p>Ouvrez <code>configuration.py</code> et modifiez les valeurs par défaut du modèle comme indiqué ci-dessous :</p>…
 embedding_model: Annotated[
       str,
       {"__template_metadata__": {"kind": "embeddings"}},
   ] = field(
       default="cohere/embed-english-v3.0",
…
response_model: Annotated[str, {"__template_metadata__": {"kind": "llm"}}] = field(
       default="cohere/command-r-08-2024",
…
query_model: Annotated[str, {"__template_metadata__": {"kind": "llm"}}] = field(
       default="cohere/command-r-08-2024",
       metadata={<h2>Exécution de l'agent de recherche avec LangGraph CLI</h2><h3>1. Lancer le serveur LangGraph</h3>cd lg-agent-demo
langgraph dev<p>Cette opération permet de démarrer localement le serveur LangGraph API. Si l'opération se déroule correctement, vous devriez voir apparaître quelque chose comme :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt46c7a703e715ef66/6a17f86eb1e113272d79f42e/e3c3344b24651067e2d0892d870feca505b3be35-1494x542.png" alt=" Le serveur LangGraph API fonctionne correctement." /><p>URL de l'interface utilisateur d'Open Studio.</p><p>Deux graphiques sont disponibles :</p><ul><li><p><strong>Graphique de récupération</strong>: Récupère les données d'Elasticsearch et répond à la requête à l'aide d'un LLM.</p></li><li><p><strong>Graphique d'indexation</strong>: Indexe les documents dans Elasticsearch et génère des embeddings à l'aide d'un LLM.</p></li></ul><h3>2. Configuration du graphe de l'indexeur</h3><ul><li><p>Ouvrez le graphique de l'indexeur.</p></li><li><p>Cliquez sur gérer les assistants.</p><ul><li><p>Cliquez sur <strong>"Ajouter un nouvel assistant</strong>", entrez les détails de l'utilisateur comme spécifié, puis fermez la fenêtre.</p></li></ul></li></ul>{"user_id": "101"}<h3>3. Indexation de documents types</h3><ul><li><p>Indexez les documents types suivants, qui représentent un rapport trimestriel hypothétique pour l'organisation NoveTech :</p></li></ul>[
  {    "page_content": "NoveTech Solutions Q1 2025 Report - Revenue: $120.5M, Net Profit: $18.2M, EPS: $2.15. Strong AI software launch and $50M government contract secured."
  },
  {
    "page_content": "NoveTech Solutions Business Highlights - AI-driven analytics software gained 15% market share. Expansion into Southeast Asia with two new offices. Cloud security contract secured."
  },
  {
    "page_content": "NoveTech Solutions Financial Overview - Operating expenses at $85.3M, Gross Margin 29.3%. Stock price rose from $72.5 to $78.3. Market Cap reached $5.2B."
  },
  {
    "page_content": "NoveTech Solutions Challenges - Rising supply chain costs impacting hardware production. Regulatory delays slowing European expansion. Competitive pressure in cybersecurity sector."
  },
  {
    "page_content": "NoveTech Solutions Future Outlook - Expected revenue for Q2 2025: $135M. New AI chatbot and blockchain security platform launch planned. Expansion into Latin America."
  },
  {
    "page_content": "NoveTech Solutions Market Performance - Year-over-Year growth at 12.7%. Stock price increase reflects investor confidence. Cybersecurity and AI sectors remain competitive."
  },
  {
    "page_content": "NoveTech Solutions Strategic Moves - Investing in R&amp;D to enhance AI-driven automation. Strengthening partnerships with enterprise cloud providers. Focusing on data privacy solutions."
  },
  {
    "page_content": "NoveTech Solutions CEO Statement - 'NoveTech Solutions continues to innovate in AI and cybersecurity. Our growth strategy remains strong, and we foresee steady expansion in the coming quarters.'"
  }
]<p>Une fois les documents indexés, un message de suppression apparaît dans le fil de discussion, comme indiqué ci-dessous.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt38715eadffcb62f0/6a17f877faa9135f7393ca4c/fd3a1efd64cb54d54ea56ef5055249dd066d5708-1600x854.png" alt="LangGraph et Elasticsearch Indexation des documents de flux de travail RAG." /><h3>4. Exécution du graphe de recherche</h3><ul><li><p>Passez au graphique de recherche.</p></li><li><p>Saisissez la requête de recherche suivante :</p></li></ul>What was NovaTech Solutions total revenue in Q1 2025?<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5c0d070fca52e512/6a17f879505ac36d37ad8d12/eb4d8ddfe0effd7e1868fba921b8ef13f7baf27a-1600x755.png" alt="Exécution du graphe de recherche LangGraph et Elasticsearch" /><p>Le système renvoie les documents pertinents et fournit une réponse exacte sur la base des données indexées.</p><h2>Personnaliser l'agent de recherche</h2><p>Pour améliorer l'expérience de l'utilisateur, nous introduisons une étape de personnalisation dans le graphe de recherche afin de prédire les trois prochaines questions que l'utilisateur pourrait poser. Cette prédiction est basée sur :</p><ul><li><p>Contexte des documents extraits</p></li><li><p>Interactions avec les utilisateurs précédents</p></li><li><p>Dernière requête de l'utilisateur</p></li></ul><p>Les modifications de code suivantes sont nécessaires pour mettre en œuvre la fonction de prédiction des requêtes :</p><h3>1. Mettez à jour graph.py</h3><ul><li><p>Ajouter la fonction <code>predict_query</code>:</p></li></ul>async def predict_query(
   state: State, *, config: RunnableConfig
) -&gt; dict[str, list[BaseMessage]]:
   logger.info(f"predict_query predict_querypredict_query predict_query predict_query predict_query")  # Log the query

   configuration = Configuration.from_runnable_config(config)
   prompt = ChatPromptTemplate.from_messages(
       [
           ("system", configuration.predict_next_question_prompt),
           ("placeholder", "{messages}"),
       ]
   )
   model = load_chat_model(configuration.response_model)
   user_query = state.queries[-1] if state.queries else "No prior query available"
   logger.info(f"user_query: {user_query}")
   logger.info(f"statemessage: {state.messages}")
   #human_messages = [msg for msg in state.message if isinstance(msg, HumanMessage)]

   message_value = await prompt.ainvoke(
       {
           "messages": state.messages,
           "user_query": user_query,  # Use the most recent query as primary input
           "system_time": datetime.now(tz=timezone.utc).isoformat(),
       },
       config,
   )

   next_question = await model.ainvoke(message_value, config)
   return {"next_question": [next_question]}<ul><li><p>Modifier la fonction <code>respond</code> pour qu'elle renvoie l'objet <strong><code>response</code></strong> au lieu du message :</p></li></ul>async def respond(
   state: State, *, config: RunnableConfig
) -&gt; dict[str, list[BaseMessage]]:
   """Call the LLM powering our "agent"."""
   configuration = Configuration.from_runnable_config(config)
   # Feel free to customize the prompt, model, and other logic!
   prompt = ChatPromptTemplate.from_messages(
       [
           ("system", configuration.response_system_prompt),
           ("placeholder", "{messages}"),
       ]
   )
   model = load_chat_model(configuration.response_model)

   retrieved_docs = format_docs(state.retrieved_docs)
   message_value = await prompt.ainvoke(
       {
           "messages": state.messages,
           "retrieved_docs": retrieved_docs,
           "system_time": datetime.now(tz=timezone.utc).isoformat(),
       },
       config,
   )
   response = await model.ainvoke(message_value, config)
   # We return a list, because this will get added to the existing list
   return {"response": [response]}<ul><li><p>Mise à jour de la structure du graphe afin d'ajouter un nouveau nœud et une nouvelle arête pour predict_query :</p></li></ul>builder.add_node(generate_query)
builder.add_node(retrieve)
builder.add_node(respond)
builder.add_node(predict_query)
builder.add_edge("__start__", "generate_query")
builder.add_edge("generate_query", "retrieve")
builder.add_edge("retrieve", "respond")
builder.add_edge("respond", "predict_query")<h3>2. Mettez à jour prompts.py</h3><ul><li><p>Demande de devis pour la prédiction de guery dans <code>prompts.py</code>:</p></li></ul>PREDICT_NEXT_QUESTION_PROMPT = """Given the user query and the retrieved documents, suggest the most likely next question the user might ask.

**Context:**
- Previous Queries:
{previous_queries}

- Latest User Query: {user_query}

- Retrieved Documents:
{retrieved_docs}

**Guidelines:**
1. Do not suggest a question that has already been asked in previous queries.
2. Consider the retrieved documents when predicting the next logical question.
3. If the user's query is already fully answered, suggest a relevant follow-up question.
4. Keep the suggested question natural and conversational.
5. Suggest at least 3 question

System time: {system_time}"""<h3>3. Mettez à jour configuration.py</h3><ul><li><p>Ajouter <code>predict_next_question_prompt</code>:</p></li></ul>predict_next_question_prompt: str = field(
       default=prompts.PREDICT_NEXT_QUESTION_PROMPT,
       metadata={"description": "The system prompt used for generating responses."},
   )<h3>4. Mettez à jour state.py</h3><ul><li><p>Ajouter les attributs suivants :</p></li></ul>response: Annotated[Sequence[AnyMessage], add_messages]
next_question : Annotated[Sequence[AnyMessage], add_messages]<h3>5. Réexécutez le graphique de recherche</h3><ul><li><p>Saisissez à nouveau la requête de recherche suivante :</p></li></ul>What was NovaTech Solutions total revenue in Q1 2025?<p>Le système traitera les données et prévoira trois questions connexes que les utilisateurs pourraient poser, comme indiqué ci-dessous.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd8d4de9396ca4853/6a17f87be31791dc742d59c7/70e855a2e4edc0ba5a147588df0de30eb081d053-1600x777.png" alt="Exécution du graphe de recherche avec 3 questions d'utilisateurs à l'aide de LangGraph et Elasticsearch" /><h2>Conclusion</h2><p>L'intégration du modèle Retrieval Agent dans LangGraph Studio et CLI offre plusieurs avantages :</p><ul><li><p><strong>Développement accéléré</strong>: Les modèles et les outils de visualisation rationalisent la création et le débogage des flux de recherche, réduisant ainsi le temps de développement.</p></li><li><p><strong>Déploiement transparent</strong>: La prise en charge intégrée des API et de la mise à l'échelle automatique garantit un déploiement sans heurts dans tous les environnements.</p></li><li><p><strong>Des mises à jour faciles :</strong> La modification des flux de travail, l'ajout de nouvelles fonctionnalités et l'intégration de nœuds supplémentaires sont simples, ce qui facilite l'évolution et l'amélioration du processus de recherche.</p></li><li><p><strong>Mémoire persistante</strong>: Le système conserve les états et les connaissances des agents, ce qui améliore la cohérence et la fiabilité.</p></li><li><p><strong>Modélisation flexible des flux de travail</strong>: Les développeurs peuvent personnaliser la logique de recherche et les règles de communication pour des cas d'utilisation spécifiques.</p></li><li><p><strong>Interaction et débogage en temps réel</strong>: La possibilité d'interagir avec les agents en cours d'exécution permet de tester et de résoudre les problèmes de manière efficace.</p></li></ul><p>En tirant parti de ces fonctionnalités, les organisations peuvent construire des systèmes de recherche puissants, efficaces et évolutifs qui améliorent l'accessibilité des données et l'expérience de l'utilisateur.</p><p>Le code source complet de ce projet est disponible sur <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/langraph-retrieval-agent-template-demo">GitHub</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/build-rag-workflow-langgraph-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/build-rag-workflow-langgraph-elasticsearch</guid>
    <category><![CDATA[IA]]></category>
    <dc:creator><![CDATA[Neha Saini]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0c9f03d2c1a9cb4c/6a17f87d0b0bed3781dd377c/17b7e7b336f73e232375d1add582ae5f6c52a279-1440x840.png" length="0" type="image/png"/>
    <pubDate>Fri, 25 Apr 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Utilisation des modèles Amazon Nova dans Elasticsearch]]></title>
    <description><![CDATA[Découvrez comment utiliser les modèles Amazon Nova dans Elasticsearch pour extraire automatiquement le sentiment, l'authenticité, les résumés et les mots-clés des avis produits dans Elasticsearch.]]></description>
    <content:encoded><![CDATA[<p>Dans cet article, nous aborderons la famille de modèles d'IA d'Amazon, Amazon Nova, et apprendrons à l'utiliser avec Elasticsearch.</p><h2>À propos d'Amazon Nova</h2><p>Amazon Nova est une famille de modèles d'intelligence artificielle d'Amazon, disponibles sur Amazon Bedrock et conçus pour offrir de hautes performances et un bon rapport coût-efficacité. Ces modèles fonctionnent avec des entrées texte, image et vidéo, génèrent des sorties textuelles et sont optimisés pour répondre à différents besoins en termes de précision, de vitesse et de coût.</p><h3>Principaux modèles de l'Amazon Nova</h3><ul><li><p>Amazon Nova Micro : Axé exclusivement sur le texte, ce modèle rapide et économique est idéal pour la traduction, le raisonnement, la complétion de codes et la résolution de problèmes mathématiques. Sa production dépasse les 200 jetons par seconde, ce qui en fait un outil idéal pour les applications nécessitant des réponses instantanées.</p></li><li><p>Amazon Nova Lite : un modèle multimodal économique capable de traiter rapidement des images, des vidéos et du texte. Il se distingue par sa rapidité et sa précision, et convient plus particulièrement aux applications interactives et à fort volume où le coût est un facteur déterminant.</p></li><li><p>Amazon Nova Pro : L'option la plus avancée, combinant haute précision, vitesse et rentabilité. Idéal pour les tâches complexes telles que le résumé de vidéos, les questions et réponses, le développement de logiciels et les agents d'intelligence artificielle. Les avis d'experts attestent de son excellence en matière de compréhension textuelle et visuelle, ainsi que de sa capacité à suivre des instructions et à exécuter des flux de travail automatisés.</p></li></ul><p>Les modèles Amazon Nova sont adaptés à une variété d'applications, de la création de contenu et de l'analyse de données au développement de logiciels et à l'automatisation des processus alimentés par l'IA.</p><p>Ci-dessous, nous allons montrer comment utiliser les modèles Amazon Nova en conjonction avec Elasticsearch pour l'analyse automatisée des avis sur les produits.</p><p>Ce que nous ferons :</p><ol><li><p>Créer un point de terminaison via Inference API, intégrant Amazon Bedrock avec Elasticsearch.</p></li><li><p>Créer un pipeline à l'aide du processeur d'inférence, qui fera des appels au point d'extrémité de l'API d'inférence.</p></li><li><p>Indexer les avis sur les produits et générer automatiquement une analyse des avis à l'aide du pipeline.</p></li><li><p>Analyser les résultats de l'intégration.</p></li></ol><h2>Création d'un point de terminaison dans l'API d'inférence avec Amazon Nova Lite</h2><p>Tout d'abord, nous configurons l'API Inference pour intégrer Amazon Bedrock à Elasticsearch. Nous définissons Amazon Nova Lite, id <strong>amazon.nova-lite-v1:0,</strong> comme le modèle à utiliser, car il offre un équilibre entre la vitesse, la précision et le coût.</p><p><strong>Remarque :</strong> vous devez disposer d'informations d'identification valides pour utiliser Amazon Bedrock. Vous pouvez consulter la documentation relative à l'obtention des clés d'accès <a href="https://docs.aws.amazon.com/keyspaces/latest/devguide/create.keypair.html">ici :</a></p>PUT _inference/completion/bedrock_completion_amazon_nova-lite
{
   "service": "amazonbedrock",
   "service_settings": {
       "access_key": "#access_key#",
       "secret_key": "#secret_key#",
       "region": "us-east-1",
       "provider": "amazontitan",
       "model": "amazon.nova-lite-v1:0"
   }
}<h2>Création du pipeline d'analyse de l'examen</h2><p>Nous allons maintenant créer un pipeline de traitement qui utilisera le processeur d'inférence pour exécuter une invite d'analyse de révision. Cette invite enverra les données d'évaluation à Amazon Nova Lite, qui les exécutera :</p><ul><li><p>Classification du sentiment (positif, négatif ou neutre).</p></li><li><p>Résumé de l'examen.</p></li><li><p>Génération de mots-clés.</p></li><li><p>Mesure de l'authenticité (authentique | suspecte | générique).</p></li></ul>PUT /_ingest/pipeline/review_analyzer_ai
{
      "processors": [
      {
        "script": 
            {
            "source": """ctx.prompt = "Analyze the following product review and return a structured JSON. Task: - Summarize the review concisely. - Detect and classify the sentiment as positive, neutral, or negative.- Generate relevant tags (keywords) based on the review content and detected sentiment. - Evaluate the authenticity of the review (authentic, suspicious, or generic). Review: " + ctx.review + " Respond in JSON format with the following fields: \"review_analyze\": {\"sentiment\": \"&lt;positive | neutral | negative&gt;\", \"authenticity\": \"&lt;authentic | suspicious | generic&gt;\",\"summary\": \"&lt;short review summary&gt;\", \"keywords\": [\"&lt;keyword 1&gt;\", \"&lt;keyword 2&gt;\", \"...\"]}}}"
            """
            }
      },
      {
        "inference": {
          "model_id": "bedrock_completion_amazon_nova-lite",
          "input_output": {
            "input_field": "prompt",
            "output_field": "result"
          }
        }
      },
      {
        "gsub": {
          "field": "result",
          "pattern": "```json",
          "replacement": ""
        } 
      },
      {
        "json" : {
          "field" : "result",
          "strict_json_parsing": false,
          "add_to_root" : true
        }
      },
      {
        "remove": {
          "field": "result"
        }
      },
      {
        "remove": {
          "field": "prompt"
        }
      }
    ]
}<h2>Révision de l'indexation</h2><p>Nous indexons maintenant les avis sur les produits à l'aide de l'API Bulk. Le pipeline créé précédemment sera automatiquement appliqué, ajoutant l'analyse générée par le modèle Nova aux documents indexés.</p>POST bulk/
{ "index": { "_index" : "products", "_id": 1, "pipeline":"review_analyzer_ai" } }
{ "product": "Pampers Pants Premium Care Fralda", "review": "Best diaper ever! Great material, lots of cotton, without all that plastic. Doesn't leak! My baby is a boy and every diaper leaked around the waist, this model solved the problem. Even on a small baby it's worth the effort of putting on the short diaper. I put it on my baby at 9 pm and only take it off in the morning, without any leaks." }
{ "index": { "_index" : "products", "_id": 2, "pipeline":"review_analyzer_ai" } }
{ "product": "Portable Electric Body Massager", "review": "It broke in three months for no apparent reason, thank goodness I didn't review it before. I don't recommend buying it because it has a short lifespan." }
{ "index": { "_index" : "products", "_id": 3, "pipeline":"review_analyzer_ai" } }
{ "product": "Havit Fuxi-H3 Black Quad-Mode Wired and Wireless Gaming Headset", "review": "The sound is good for the price, but the connectivity is horrible. You always need to be playing audio, otherwise it loses connection (I work from home, and this is very annoying). Sometimes it loses connection and you have to turn it off and on again to get it back on. The microphone is very sensitive, so it loses connection frequently and you have to turn the headset off and on for the microphone to work again. The flexibility of the stem is useless, because if you move it, the microphone can turn off. Sometimes I need to use Linux and the headset simply doesn't work. It's light and comfortable, the sound is adequate, but the connectivity is terrible." }
{ "index": { "_index" : "products", "_id": 4, "pipeline":"review_analyzer_ai" } }
{ "product": "Air Fryer 4L Oil Free Fryer Mondial", "review": "For those looking for value for money, it's a good option, but the tray (which is underneath the perforated basket) is already peeling a lot. My mother has one just like it and said that hers is even rusting, in other words, the material is MUCH inferior. There's also something that bothers me, because it looks like a microwave, it doesn't fry evenly, it's weaker in the middle and stronger on the sides. Buy at your own risk." }<h2>Interrogation et analyse des résultats</h2><p>Enfin, nous lançons une requête pour voir comment le modèle Amazon Nova Lite analyse et classe les avis. En exécutant GET products/_search, nous obtenons les documents déjà enrichis des champs générés à partir du contenu de la revue.</p><p>Le modèle identifie le sentiment prédominant (positif, neutre ou négatif), génère des résumés concis, extrait les mots-clés pertinents et estime l'authenticité de chaque avis. Ces champs permettent de comprendre l'opinion du client sans avoir à lire le texte complet.</p><p>Pour interpréter les résultats, nous examinons</p><ul><li><p>Le sentiment, qui indique la perception globale du produit par le consommateur.</p></li><li><p>Le résumé, qui met en évidence les principaux points mentionnés.</p></li><li><p>Les mots-clés, qui peuvent être utilisés pour regrouper des avis similaires ou identifier des modèles de retour d'information.</p></li><li><p>L'authenticité, qui indique si l'avis semble digne de confiance. Cette fonction est utile pour la curation ou la modération.</p></li></ul>   "hits": [
      {
        "_index": "products",
        "_id": "1",
        "_score": 1,
        "_ignored": [
          "review.keyword"
        ],
        "_source": {
          "product": "Pampers Pants Premium Care Fralda",
          "model_id": "bedrock_completion_amazon_nova-lite",
          "review_analyze": {
            "summary": "The reviewer praises the diaper for its great material, high cotton content, and leak-proof design, especially highlighting its effectiveness for their baby.",
            "sentiment": "positive",
            "keywords": [
              "best diaper",
              "great material",
              "cotton",
              "no plastic",
              "leak-proof",
              "baby",
              "effective"
            ],
            "authenticity": "authentic"
          },
          "review": "Best diaper ever! Great material, lots of cotton, without all that plastic. Doesn't leak! My baby is a boy and every diaper leaked around the waist, this model solved the problem. Even on a small baby it's worth the effort of putting on the short diaper. I put it on my baby at 9 pm and only take it off in the morning, without any leaks."
        }
      },
      {
        "_index": "products",
        "_id": "2",
        "_score": 1,
        "_source": {
          "product": "Portable Electric Body Massager",
          "model_id": "bedrock_completion_amazon_nova-lite",
          "review_analyze": {
            "summary": "The product broke in three months for no apparent reason and the reviewer does not recommend it due to its short lifespan.",
            "sentiment": "negative",
            "keywords": [
              "broke",
              "short lifespan",
              "not recommend"
            ],
            "authenticity": "authentic"
          },
          "review": "It broke in three months for no apparent reason, thank goodness I didn't review it before. I don't recommend buying it because it has a short lifespan."
        }
      },
      {
        "_index": "products",
        "_id": "3",
        "_score": 1,
        "_ignored": [
          "review.keyword"
        ],
        "_source": {
          "product": "Havit Fuxi-H3 Black Quad-Mode Wired and Wireless Gaming Headset",
          "model_id": "bedrock_completion_amazon_nova-lite",
          "review_analyze": {
            "summary": "The headset has good sound quality for the price but suffers from poor connectivity, especially when using the microphone or moving the headset. It also has compatibility issues with Linux.",
            "sentiment": "negative",
            "keywords": [
              "sound",
              "connectivity",
              "microphone",
              "compatibility",
              "annoying",
              "turn off and on",
              "Linux",
              "flexible stem",
              "work from home"
            ],
            "authenticity": "authentic"
          },
          "review": "The sound is good for the price, but the connectivity is horrible. You always need to be playing audio, otherwise it loses connection (I work from home, and this is very annoying). Sometimes it loses connection and you have to turn it off and on again to get it back on. The microphone is very sensitive, so it loses connection frequently and you have to turn the headset off and on for the microphone to work again. The flexibility of the stem is useless, because if you move it, the microphone can turn off. Sometimes I need to use Linux and the headset simply doesn't work. It's light and comfortable, the sound is adequate, but the connectivity is terrible."
        }
      },
      {
        "_index": "products",
        "_id": "4",
        "_score": 1,
        "_ignored": [
          "review.keyword"
        ],
        "_source": {
          "product": "Air Fryer 4L Oil Free Fryer Mondial",
          "model_id": "bedrock_completion_amazon_nova-lite",
          "review_analyze": {
            "summary": "The product offers value for money but has issues with peeling, rusting, and uneven frying.",
            "sentiment": "negative",
            "keywords": [
              "value for money",
              "peeling",
              "rusting",
              "uneven frying",
              "weaker in the middle"
            ],
            "authenticity": "authentic"
          },
          "review": "For those looking for value for money, it's a good option, but the tray (which is underneath the perforated basket) is already peeling a lot. My mother has one just like it and said that hers is even rusting, in other words, the material is MUCH inferior. There's also something that bothers me, because it looks like a microwave, it doesn't fry evenly, it's weaker in the middle and stronger on the sides. Buy at your own risk."
        }
      }
    ]<h2>Conclusions</h2><p>L'intégration entre Amazon Nova Lite et Elasticsearch a démontré comment les modèles de langage peuvent transformer des avis bruts en informations structurées et précieuses. En traitant les avis par le biais d'un pipeline, nous avons pu extraire le sentiment, l'authenticité, les résumés et les mots-clés de manière automatique et cohérente.</p><p>Les résultats montrent que le modèle peut comprendre le contexte des avis, classer les opinions des utilisateurs et mettre en évidence les points les plus pertinents de chaque expérience. Cela crée un ensemble de données beaucoup plus riche qui peut être exploité pour améliorer les capacités de recherche.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/amazon-nova-models-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/amazon-nova-models-elasticsearch</guid>
    <category><![CDATA[IA]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdbbf13eb690294f1/6a17fddd6df73195190a115a/304713c48b568e17d0bb56b19edb28769f7801b3-721x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 02 Apr 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Connecter des agents à Elasticsearch avec Model Context Protocol]]></title>
    <description><![CDATA[Utilisons le serveur Model Context Protocol pour dialoguer avec vos données dans Elasticsearch.]]></description>
    <content:encoded><![CDATA[<p>Et si l'interaction avec vos données était aussi facile que de discuter avec un collègue ? Imaginez que vous demandiez simplement : "Montrez-moi toutes les commandes de plus de 500 $ du mois dernier" ou "Quels sont les produits qui ont reçu le plus d'avis 5 étoiles ?" et que vous obteniez des réponses instantanées et précises, sans qu'aucune requête ne soit nécessaire.</p><p>Le protocole de contexte de modèle (MCP) rend cela possible. Il connecte de manière transparente l'IA conversationnelle avec vos bases de données et vos API externes, transformant les demandes complexes en conversations naturelles. Si les LLM modernes sont excellents pour comprendre le langage, leur véritable potentiel se révèle lorsqu'ils sont intégrés à des systèmes du monde réel. MCP comble le fossé qui les sépare, en rendant l'interaction des données plus intuitive et plus efficace.</p><p>Dans ce billet, nous allons explorer :</p><ul><li><p>Architecture MCP - Comment cela fonctionne-t-il sous le capot ?</p></li><li><p>Avantages d'un serveur MCP connecté à Elasticsearch</p></li><li><p>Construire un <a href="https://github.com/elastic/mcp-server-elasticsearch">serveur MCP alimenté par Elasticsearch</a></p></li></ul><p>Des moments passionnants nous attendent ! L'intégration de MCP avec votre pile Elastic transforme la façon dont vous interagissez avec l'information, rendant les requêtes complexes aussi intuitives qu'une conversation de tous les jours.</p><h2>Modèle Contexte Protocole</h2><p><a href="https://modelcontextprotocol.io/introduction">Model Context Protocol</a> (MCP), développé par Anthropic, est une norme ouverte qui connecte les modèles d'intelligence artificielle à des sources de données externes par le biais de canaux sécurisés et bidirectionnels. Il résout un problème majeur de l'IA : l'accès en temps réel à des systèmes externes tout en préservant le contexte de la conversation.</p><h3>Architecture MCP</h3><p>Modèle Contexte L'architecture du protocole se compose de deux éléments clés :</p><ul><li><p><strong>Clients MCP</strong> - Assistants IA et chatbots qui demandent des informations ou exécutent des tâches au nom des utilisateurs.</p></li><li><p><strong>Serveurs MCP</strong> - Référentiels de données, moteurs de recherche et API qui récupèrent les informations pertinentes ou effectuent les actions demandées (par exemple, appel d'API externes).</p></li></ul><p>Les serveurs MCP exposent quatre capacités principales aux clients :</p><ul><li><p><strong>Ressources</strong> - Données structurées, documents et contenu qui peuvent être récupérés et utilisés comme contexte pour les interactions LLM. Cela permet aux assistants d'IA d'accéder à des informations pertinentes à partir de bases de données, d'index de recherche ou d'autres sources.</p></li><li><p><strong>Outils</strong> - Fonctions exécutables qui permettent aux LLM d'interagir avec des systèmes externes, d'effectuer des calculs ou de prendre des mesures dans le monde réel. Ces outils étendent les capacités de l'IA au-delà de la génération de texte, en permettant aux assistants de déclencher des flux de travail, d'appeler des API ou de manipuler des données de manière dynamique.</p></li><li><p><strong>Invitations</strong> - Modèles d'invitations et flux de travail réutilisables pour normaliser et partager les interactions LLM communes.</p></li><li><p><strong>Échantillonnage</strong> - Demander des compléments LLM par l'intermédiaire du client pour permettre des comportements agentiques sophistiqués tout en maintenant la sécurité et la confidentialité.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfe82754551bb187a/6a17f7ec6864a43e71b6895d/bef5178133391e96e3d66ae634e41a85712a33a9-2345x1620.png" alt="Architecture du protocole de contexte de modèle (MCP)" /><h2>Serveur MCP + Elasticsearch</h2><p></p><p>Les systèmes traditionnels de récupération et de génération augmentée (RAG) récupèrent des documents sur la base des requêtes de l'utilisateur, mais le MCP va plus loin : il permet aux agents d'intelligence artificielle de construire et d'exécuter des tâches de manière dynamique et en temps réel. Cela permet aux utilisateurs de poser des questions en langage naturel, comme par exemple :</p><p></p><ul><li><p>"Affichez toutes les commandes de plus de 500 $ passées le mois dernier."</p></li><li><p>"Quels sont les produits qui ont reçu le plus d'avis 5 étoiles ?"</p></li></ul><p></p><p>Et obtenir des réponses instantanées et précises, sans avoir à rédiger la moindre requête.</p><p></p><p>MCP atteint cet objectif grâce à</p><ul><li><p>Sélection dynamique des outils - Les agents choisissent intelligemment les bons outils exposés via les serveurs MCP en fonction de l'intention de l'utilisateur. Les gestionnaires de programmes d'éducation et de formation tout au long de la vie "plus intelligents" sont généralement plus aptes à sélectionner les bons outils avec les arguments appropriés en fonction du contexte.</p></li><li><p>Communication bidirectionnelle - Les agents et les sources de données échangent des informations de manière fluide, en affinant les requêtes si nécessaire (par ex. d'abord le mappage de l'index de consultation, puis seulement la construction de la requête ES).</p></li><li><p>Orchestration multi-outils - Les flux de travail peuvent exploiter simultanément les outils de plusieurs serveurs MCP.</p></li><li><p>Contexte persistant - Les agents se souviennent des interactions précédentes, ce qui permet de maintenir une continuité entre les conversations.</p></li></ul><p>Un serveur MCP connecté à Elasticsearch débloque une puissante architecture de recherche en temps réel. Les agents d'intelligence artificielle peuvent explorer, interroger et analyser les données Elasticsearch à la demande. Vos données peuvent être recherchées par le biais d'une interface de chat simple.</p><p>Au-delà de la simple récupération de données, MCP permet d'agir. Il s'intègre à d'autres outils pour déclencher des flux de travail, automatiser des processus et alimenter des systèmes d'analyse. En séparant la recherche de l'exécution, MCP permet aux applications alimentées par l'IA de rester flexibles, à jour et intégrées de manière transparente dans les flux de travail des agents.</p><h2>Pratique : Serveur MCP pour dialoguer avec vos données Elasticsearch</h2><p>Pour interagir avec Elasticsearch via un serveur MCP, nous avons au moins besoin des fonctions suivantes :</p><ul><li><p>Récupérer les indices</p></li><li><p>Obtenir des correspondances</p></li><li><p>Effectuer des recherches à l'aide du DSL de requête d'Elasticsearch</p></li></ul><p>Notre serveur est écrit en TypeScript, et nous utiliserons le <a href="https://github.com/modelcontextprotocol/typescript-sdk">SDK TypeScript officiel</a> du MCP. Pour l'installation, nous recommandons d'installer l'application Claude Desktop (la version gratuite est suffisante) car elle comprend un client MCP intégré. Notre serveur MCP expose essentiellement le <a href="https://www.elastic.co/fr/guide/en/elasticsearch/client/javascript-api/current/index.html">client Elasticsearch JavaScript</a> officiel à travers les outils MCP.</p><p>Commençons par définir le client Elasticsearch et le serveur MCP :</p> const esClient = new Client({
    node: url,
    auth: {
      apiKey: apiKey,
    },
  });

  const server = new McpServer({
    name: "elasticsearch-mcp-server",
    version: "0.1.0",
  });<p>Nous utiliserons les outils de serveur MCP suivants qui peuvent interagir avec Elasticsearch :</p><ul><li><p><strong>Liste des index</strong> <a href="https://github.com/elastic/mcp-server-elasticsearch/blob/main/index.ts#L46">(list_indices</a>) : Cet outil récupère tous les index Elasticsearch disponibles, en fournissant des détails tels que le nom de l'index, l'état de santé et le nombre de documents.</p></li><li><p><strong>Obtenir des correspondances</strong> <a href="https://github.com/elastic/mcp-server-elasticsearch/blob/main/index.ts#L94">(get_mappings</a>) : Cet outil récupère les correspondances de champs pour un index Elasticsearch spécifié, aidant les utilisateurs à comprendre la structure et les types de données des documents stockés.</p></li><li><p><strong>Search</strong> <a href="https://github.com/elastic/mcp-server-elasticsearch/blob/main/index.ts#L147">(recherche</a>) : Cet outil exécute une recherche Elasticsearch à l'aide d'un DSL de requête fourni. Il active automatiquement les surlignages pour les champs de texte, ce qui facilite l'identification des résultats de recherche pertinents.</p></li></ul><p>L'implémentation complète du serveur MCP Elasticsearch est disponible dans le repo <a href="https://github.com/elastic/mcp-server-elasticsearch">elastic/mcp-server-elasticsearch</a>.</p><h4>Chat avec votre index</h4><p>Voyons comment configurer le serveur Elasticsearch MCP pour pouvoir poser des questions en langage naturel sur vos données, telles que "Trouver toutes les commandes de plus de 500 $ du mois dernier."</p><p><strong>Configurez votre application Claude Desktop</strong></p><ul><li><p>Ouvrir l'application Claude Desktop</p></li><li><p>Naviguer vers Settings &gt; Developer &gt; MCP Servers</p></li><li><p>Cliquez sur "Edit Config" et ajoutez cette configuration à votre <code>claude_desktop_config.json</code>:</p></li></ul>{
  "mcpServers": {
    "Elasticsearch MCP Server": {
      "command": "npx",
      "args": [
        "-y",
        "@elastic/mcp-server-elasticsearch"
      ],
      "env": {
        "ES_URL": "",
        "ES_API_KEY": ""
      }
    }
  }
}<p>Note : Cette configuration utilise le paquet <a href="https://www.npmjs.com/package/@elastic/mcp-server-elasticsearch">@elastic/mcp-server-elasticsearch</a> npm publié par Elastic. Si vous souhaitez développer localement, vous trouverez plus de détails sur l'installation du serveur Elasticsearch MCP <a href="https://github.com/elastic/mcp-server-elasticsearch/blob/main/README.md">ici.</a></p><p><strong>Remplir votre index Elasticseach</strong></p><ul><li><p>Vous pouvez utiliser nos <a href="https://gist.github.com/jedrazb/60e9400cbe40addfd9e4337749c28431">données d'exemple</a> pour remplir l'index "orders" pour cette démo.</p></li><li><p>Cela vous permettra d'essayer des requêtes telles que "Trouver toutes les commandes de plus de 500 $ du mois dernier."</p></li></ul><p><strong>Commencez à l'utiliser</strong></p><ul><li><p>Ouvrir une nouvelle conversation dans l'application Claude Desktop</p></li><li><p>Le serveur MCP se connecte automatiquement</p></li><li><p>Commencez à poser des questions sur vos données Elasticsearch !</p></li></ul><p>Regardez cette démo pour voir à quel point il est facile d'interroger vos données Elasticsearch en utilisant le langage naturel :</p><h4>Comment fonctionne-t-elle ?</h4><p>À la question "Trouver toutes les commandes de plus de 500 $ du mois dernier", le LLM reconnaît l'intention d'effectuer une recherche dans l'index Elasticsearch avec les contraintes spécifiées. Pour effectuer une recherche efficace, l'agent doit.. :</p><ul><li><p>Déterminez le nom de l'index : <code>orders</code></p></li><li><p>Comprendre les mappings de l'index <code>orders</code></p></li><li><p>Construire le DSL de requête compatible avec les mappages d'index et enfin exécuter la requête de recherche.</p></li></ul><p>Cette interaction peut être représentée comme suit :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt152f41bc8c3e9752/6a17f7ee6df73152df0a10cc/8875bc75745124be87deac0be666509446887de2-2345x1620.png" alt="Comment fonctionne le serveur MCP + Elasticsearch ?" /><h2>Conclusion</h2><p>Model Context Protocol améliore la façon dont vous interagissez avec les données Elasticsearch, en permettant des conversations en langage naturel au lieu de requêtes complexes. En associant les capacités de l'IA à vos données, MCP crée un flux de travail plus intuitif et plus efficace qui maintient le contexte tout au long de vos interactions.</p><p>Le serveur Elasticsearch MCP est disponible sous forme de paquetage npm public<a href="https://www.npmjs.com/package/@elastic/mcp-server-elasticsearch">(@elastic/mcp-server-elasticsearch</a>), ce qui facilite l'intégration pour les développeurs. Avec une configuration minimale, votre équipe peut commencer à explorer les données, à déclencher des flux de travail et à obtenir des informations par le biais de simples conversations.</p><p>Prêt à en faire l'expérience ? Essayez le <a href="https://github.com/elastic/mcp-server-elasticsearch">serveur Elasticsearch MCP</a> dès aujourd'hui et commencez à discuter avec vos données.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/model-context-protocol-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/model-context-protocol-elasticsearch</guid>
    <category><![CDATA[IA agentique]]></category>
    <category><![CDATA[IA]]></category>
    <dc:creator><![CDATA[Jedr Blaszyk,Joe McElroy]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltce68a95c633809ae/6a17f7f0148009fa28b48915/65b378f644bd13e3edf2f108d48186f1889f546c-1200x628.png" length="0" type="image/png"/>
    <pubDate>Fri, 28 Mar 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Construire un système RAG multimodal avec Elasticsearch : L'histoire de Gotham City]]></title>
    <description><![CDATA[Apprenez à construire un système multimodal Retrieval-Augmented Generation (RAG) qui intègre des données textuelles, audio, vidéo et des images afin de fournir une recherche d'informations plus riche et contextualisée.]]></description>
    <content:encoded><![CDATA[<p>Dans ce blog, vous apprendrez à construire un pipeline RAG (Retrieval-Augmented Generation) multimodal à l'aide d'Elasticsearch. Nous verrons comment exploiter ImageBind pour générer des embeddings pour différents types de données, notamment le texte, les images, l'audio et les cartes de profondeur. Vous découvrirez également comment stocker et récupérer efficacement ces embeddings dans Elasticsearch à l'aide de dense_vector et de k-NN search. Enfin, nous intégrerons un modèle de langage étendu (LLM) pour analyser les preuves extraites et générer un rapport final complet.</p><h3>Comment fonctionne la canalisation multimodale RAG ?</h3><ol><li><p><strong>Collecte d'indices</strong> → Images, sons, textes et cartes de profondeur provenant de la scène de crime de Gotham.</p></li><li><p><strong>Génération d'embeddings</strong> → Chaque fichier est converti en vecteur à l'aide du modèle multimodal ImageBind.</p></li><li><p><strong>Indexation dans Elasticsearch</strong> → Les vecteurs sont stockés pour une récupération efficace.</p></li><li><p><strong>Recherche par similarité</strong> → Étant donné un nouvel indice, les vecteurs les plus similaires sont récupérés.</p></li><li><p><strong>Le LLM analyse les preuves</strong> → Un modèle GPT-4 synthétise la réponse et identifie le suspect !</p></li></ol><h3>Technologies utilisées</h3><ul><li><p><strong>ImageBind</strong> → Génère des encastrements unifiés pour différentes modalités.</p></li><li><p><strong>Elasticsearch</strong> → Permet une recherche vectorielle rapide et efficace.</p></li><li><p><strong>LLM (GPT-4, OpenAI)</strong> → Analyse les preuves et produit un rapport final.</p></li></ul><h3>À qui s'adresse ce blog ?</h3><ul><li><p>Utilisateurs d'Elastic intéressés par la recherche vectorielle multimodale.</p></li><li><p>Les développeurs qui cherchent à comprendre le RAG multimodal dans la pratique.</p></li><li><p>Toute personne à la recherche de solutions évolutives pour l'analyse de données provenant de sources multiples.</p></li></ul><h2>Conditions préalables à la mise en place d'un RAG multimodal : mise en place de l'environnement</h2><p>Pour résoudre les crimes de Gotham City, vous devez mettre en place votre environnement technologique. Suivez ce guide étape par étape :</p><h3>1. Exigences techniques</h3><p>Composant</p><p>Spécifications</p><p>Système OS</p><p>Linux, macOS ou Windows</p><p>Python</p><p>3.10 ou plus récent</p><p>RAM</p><p>8 Go minimum (16 Go recommandés)</p><p>GPU</p><p>Facultatif mais recommandé pour ImageBind</p><h3><strong>2. Mise en place du projet</strong></h3><p>Tout le matériel d'enquête est disponible sur GitHub, et nous utiliserons Jupyter Notebook (Google Colab) pour cette expérience interactive de résolution de crimes. Suivez les étapes suivantes pour commencer :</p><h4>Configuration avec Jupyter Notebook (Google Colab)</h4><p><strong>1. Accéder à l'ordinateur portable</strong></p><ul><li><p>Ouvrez notre carnet Google Colab prêt à l'emploi : <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/building-multimodal-rag-with-elasticsearch-gotham/notebook/01-mmrag-blog-quick-start.ipynb">RAG multimodal avec Elasticsearch</a><u>.</u></p></li><li><p>Ce carnet contient tout le code et les explications dont vous avez besoin pour suivre le cours.</p></li></ul><p><strong>2. Cloner le référentiel</strong></p># Clone the repository with the multimodal RAG code
!git clone -b https://github.com/elastic/elasticsearch-labs.git

# Navigate to the project directory
cd elasticsearch-labs/supporting-blog-content/building-multimodal-rag-with-elasticsearch-gotham<p><strong>3. Installer les dépendances</strong></p> # Install PyTorch and related libraries
!pip install torch&gt;=2.1.0 torchvision&gt;=0.16.0 torchaudio&gt;=2.1.0

# Install vision processing libraries
!pip install opencv-python-headless pillow numpy

# Install the specific ImageBind fork
!pip install git+https://github.com/hkchengrex/ImageBind.git

# Install Elasticsearch and environment management
!pip install elasticsearch python-dotenv<p><strong>4. Configurer les informations d'identification</strong></p># Input your credentials securely
import getpass

ELASTICSEARCH_URL = input("Enter the Elasticsearch endpoint url: ")
ELASTICSEARCH_API_KEY = getpass.getpass("Enter the Elasticsearch API key: ")
OPENAI_API_KEY = getpass.getpass("Enter the OpenAI API key: ")

# Configure environment variables
import os
os.environ["ELASTICSEARCH_API_KEY"] = ELASTICSEARCH_API_KEY
os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
os.environ["ELASTICSEARCH_URL"] = ELASTICSEARCH_URL<p>Note : Le modèle ImageBind (~2GB) sera téléchargé automatiquement lors de la première exécution.</p><p>Maintenant que tout est prêt, plongeons dans les détails et résolvons le crime !</p><h2>Introduction : Le crime à Gotham City</h2><p>Par une nuit pluvieuse à Gotham City, un crime choquant secoue la ville. Le commissaire Gordon a besoin de votre aide pour élucider ce mystère. Les indices sont dispersés dans différents formats : images floues, sons mystérieux, textes cryptés et même cartes de profondeur. Êtes-vous prêt à utiliser la technologie d'IA la plus avancée pour résoudre l'affaire ?</p><p>Dans ce blog, vous serez guidé pas à pas dans la construction d'un <strong>système RAG (Retrieval-Augmented Generation) multimodal</strong> qui unifie différents types de données<strong>(images, audio, textes et cartes de profondeur</strong>) dans un espace de recherche unique. Nous utiliserons <strong>ImageBind</strong> pour générer des encastrements multimodaux, <strong>Elasticsearch</strong> pour stocker et récupérer ces encastrements, et un <strong>Grand Modèle de Langage (LLM)</strong> pour analyser les preuves et générer un rapport final.</p><h2>Principes fondamentaux : Architecture multimodale RAG</h2><h3>Qu'est-ce qu'un RAG multimodal ?</h3><p>L'essor du <strong>multimodal RAG (Retrieval-Augmented Generation)</strong> révolutionne la façon dont nous interagissons avec les modèles d'IA. Traditionnellement, les systèmes RAG travaillent exclusivement avec du texte, récupérant les informations pertinentes dans des bases de données avant de générer des réponses. Toutefois, le monde ne se limite pas au texte : les images<strong>, les vidéos et les sons sont également porteurs de connaissances précieuses</strong>. C'est pourquoi les architectures multimodales gagnent en importance, permettant aux systèmes d'IA de <strong>combiner des informations provenant de différents formats pour obtenir des réponses plus riches et plus précises</strong>.</p><h3><strong>Trois approches principales pour le RAG multimodal</strong></h3><p>Trois stratégies sont couramment utilisées pour mettre en œuvre un RAG multimodal. Chaque approche présente ses propres avantages et limites, en fonction du cas d'utilisation :</p><h4>1. Espace vectoriel partagé</h4><p>Les données provenant de différentes modalités sont mises en correspondance dans un espace vectoriel commun à l'aide de modèles multimodaux tels qu'ImageBind. Cela permet aux requêtes textuelles de récupérer des images, des vidéos et des fichiers audio sans conversion explicite de format.</p><p><strong>Avantages :</strong></p><ul><li><p>Permet la <strong>recherche multimodale</strong> sans nécessiter de conversion de format explicite.</p></li><li><p>Il assure une <strong>intégration fluide</strong> entre les différentes modalités, permettant une recherche directe dans le texte, l'image, l'audio et la vidéo.</p></li><li><p>Évolutif pour divers types de données, ce qui le rend utile pour les <strong>applications de recherche à grande échelle.</strong></p></li></ul><p><strong>Inconvénients :</strong></p><ul><li><p><strong>La formation nécessite de grands ensembles de données multimodales</strong>, qui ne sont pas toujours disponibles.</p></li><li><p>L'espace d'intégration partagé peut introduire une <strong>dérive sémantique</strong>, lorsque les relations entre les modalités ne sont pas parfaitement préservées.</p></li><li><p><strong>Les biais dans les modèles multimodaux</strong> peuvent avoir un impact sur la précision de la recherche, en fonction de la distribution de l'ensemble des données.</p></li></ul><h4>2. Une seule modalité ancrée</h4><p>Toutes les modalités sont converties dans un <strong>format unique</strong>, généralement du <strong>texte</strong>, avant d'être extraites. Par exemple, les images sont décrites par des <strong>légendes générées automatiquement</strong>, et le son est transcrit en texte.</p><p><strong>Avantages :</strong></p><ul><li><p><strong>Simplifie la recherche</strong>, car tout est converti en une <strong>représentation textuelle uniforme</strong>.</p></li><li><p>Fonctionne bien avec les <strong>moteurs de recherche textuels existants</strong>, ce qui élimine la nécessité d'une infrastructure multimodale spécialisée.</p></li><li><p>Peut améliorer l'<strong>interprétabilité</strong> puisque les résultats obtenus sont dans un format lisible par l'homme.</p></li></ul><p><strong>Inconvénients :</strong></p><ul><li><p><strong>Perte d'informations</strong>: Certains détails (par exemple, les relations spatiales dans les images, la tonalité dans le son) peuvent ne pas être entièrement pris en compte dans les descriptions textuelles.</p></li><li><p><strong>Dépend de la qualité du sous-titrage/de la transcription</strong>: Les erreurs dans les annotations automatiques peuvent réduire l'efficacité de la recherche.</p></li><li><p>Elle <strong>n'est pas optimale pour les requêtes purement visuelles ou auditives</strong>, car le processus de conversion risque de supprimer le contexte essentiel.</p></li></ul><h4>3. Récupération séparée</h4><p>Maintient des <strong>modèles distincts</strong> pour chaque modalité. Le système effectue des <strong>recherches distinctes</strong> pour chaque type de données et <strong>fusionne</strong> ensuite les résultats.</p><p><strong>Avantages :</strong></p><ul><li><p>Permet une <strong>optimisation personnalisée par modalité</strong>, améliorant la précision de la recherche pour chaque type de données.</p></li><li><p>Moins de dépendance à l'égard de <strong>modèles multimodaux complexes</strong>, ce qui facilite l'intégration des systèmes de recherche existants.</p></li><li><p>Permet de <strong>contrôler finement le classement et le reclassement</strong>, les résultats provenant de différentes modalités pouvant être combinés de manière dynamique.</p></li></ul><p><strong>Inconvénients :</strong></p><ul><li><p><strong>Nécessite la fusion des résultats</strong>, ce qui rend le processus de recherche et de classement plus complexe.</p></li><li><p>Peut générer des <strong>réponses incohérentes</strong> si différentes modalités renvoient des informations contradictoires.</p></li><li><p><strong>Coût de calcul plus élevé</strong> car des recherches indépendantes sont effectuées pour chaque modalité, ce qui augmente le temps de traitement.</p></li></ul><h3>Notre choix : Espace vectoriel partagé avec ImageBind</h3><p>Parmi ces approches, nous avons choisi l'<strong>espace vectoriel partagé</strong>, une stratégie qui s'aligne parfaitement avec le besoin de <strong>recherches multimodales efficaces.</strong> Notre implémentation est basée sur <strong>ImageBind</strong>, un modèle capable de représenter plusieurs modalités<strong>(texte, image, audio et vidéo</strong>) dans un <strong>espace vectoriel commun.</strong> Cela nous permet de</p><ul><li><p>Effectuer des <strong>recherches multimodales</strong> entre différents formats de médias sans avoir à tout convertir en texte.</p></li><li><p>Utiliser des <strong>encastrements très expressifs</strong> pour capturer les relations entre les différentes modalités.</p></li><li><p>Garantir l'<strong>évolutivité et l'efficacité</strong>, en stockant des encastrements optimisés pour une recherche rapide dans Elasticsearch.</p></li></ul><p>En adoptant cette approche, nous avons construit un <strong>pipeline de recherche multimodale robuste</strong>, dans lequel une requête textuelle peut <strong>directement récupérer des images ou du son</strong> sans prétraitement supplémentaire. Cette méthode permet d'élargir les applications pratiques, de la <strong>recherche intelligente dans les grands référentiels</strong> aux <strong>systèmes de recommandation multimodaux avancés</strong>.</p><p>La figure suivante illustre le flux de données dans le pipeline RAG multimodal, en mettant en évidence le processus d'indexation, d'extraction et de génération de réponses sur la base de données multimodales :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt77bce4aa5216bcf3/6a17eef263173069e0585b57/a4ffdb44582738991813c045be37312dacb0d4f3-1488x1436.png" alt="Flux de données multimodales sur les chiffons" /><h3>Comment fonctionne l'espace d'intégration ?</h3><p>Traditionnellement, les enchâssements de texte proviennent de modèles de langage (par exemple, BERT, GPT). Aujourd'hui, avec des modèles multimodaux natifs comme <strong>ImageBind</strong> de Meta AI, nous disposons d'une colonne vertébrale qui génère des vecteurs pour de multiples modalités :</p><ul><li><p>Le <strong>texte</strong>: Les phrases et les paragraphes sont transformés en vecteurs de même dimension.</p></li><li><p><strong>Images (vision)</strong>: Les pixels sont placés dans le même espace dimensionnel que celui utilisé pour le texte.</p></li><li><p><strong>Audio</strong>: Les signaux sonores sont convertis en enregistrements comparables à des images et à du texte.</p></li><li><p><strong>Cartes de profondeur</strong>: Les données relatives à la profondeur sont traitées et donnent lieu à des vecteurs.</p></li></ul><p>Ainsi, tout indice<strong>(texte, image, son, profondeur</strong>) peut être comparé à un autre en utilisant des mesures de similarité vectorielle comme la <strong>similarité cosinusoïdale</strong>. Si un <strong>échantillon audio de rires</strong> et une <strong>image du visage d'un suspect</strong> sont "proches" dans cet espace, nous pouvons en déduire une certaine corrélation (par exemple, la même identité).</p><h2>Étape 1 - Collecte d'indices sur la scène de crime</h2><p>Avant d'analyser les preuves, il faut les collecter. Le crime commis à Gotham a laissé des traces qui peuvent être cachées dans des images, des sons, des textes et même des données de profondeur. Organisons ces indices pour qu'ils alimentent notre système.</p><h3>Qu'avons-nous ?</h3><p>Le commissaire Gordon nous a envoyé les fichiers suivants, qui contiennent des preuves recueillies sur la scène de crime selon quatre modalités différentes :</p><p><strong>Description de la piste et modalité</strong></p><p><strong>a) Images (2 photos)</strong></p><ul><li><p><code>crime_scene1.jpg, crime_scene2.jpg</code> → Photos prises sur la scène de crime. Montre des traces suspectes sur le sol.</p></li><li><p><code>suspect_spotted.jpg</code> → Image d'une caméra de sécurité montrant une silhouette s'enfuyant de la scène.</p></li></ul><p><strong>b)</strong> <strong>Audio (1 enregistrement)</strong></p><ul><li><p><code>joker_laugh.wav </code>→ Un microphone situé à proximité de la scène de crime a capté un rire sinistre.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte7690380dc545367/6a17eef8be6086886a00483b/3457bab38aa4a3caf61ca2a5e0a8b477ddd15cb1-86x45.png" alt="" /><p><strong>c) Texte (1 message)</strong></p><ul><li><p><code>Riddle.txt, note2.txt</code> → Des notes mystérieuses ont été trouvées sur place, peut-être laissées par le criminel.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8e5b8f4bc4124708/6a17eefa1d1b83850c93e50c/8963228dc3b4b7e2106cb6ddffbe2154020eb735-77x79.png" alt="" /><p><strong>d) Profondeur (1 carte de profondeur)</strong></p><ul><li><p><code>depth_suspect.png</code> → Une caméra de sécurité équipée d'un capteur de profondeur a filmé un suspect dans une ruelle voisine.</p></li><li><p><code>jdancing-depth.png</code> → Une caméra de sécurité équipée d'un capteur de profondeur a filmé un suspect descendant la station de métro.</p></li></ul><p>Ces éléments de preuve se présentent sous des formes différentes et ne peuvent être analysés directement de la même manière. Nous devons les transformer en encastrements, c'est-à-dire en vecteurs numériques qui permettront d'effectuer des comparaisons intermodales.</p><h3><strong>Organisation des dossiers</strong></h3><p>Avant de commencer le traitement, nous devons nous assurer que tous les indices sont correctement organisés dans le répertoire data/ afin que le pipeline fonctionne correctement.</p><p><strong>Structure de répertoire attendue :</strong></p>data/
├── images/
│   ├── crime_scene1.jpg
│   ├── suspect_spotted.jpg
│   ...
├── audios/
│   ├── joker_laugh.wav
│   ...
├── texts/
│   ├── riddle.txt
│   ... 
├── depths/
│   ├── depth_suspect.png<h3>Code pour vérifier l'organisation de l'indice</h3><p>Avant de poursuivre, vérifions que tous les fichiers nécessaires se trouvent au bon endroit.</p>import os

# Base directory for clues
data_dir = "data"

# List of expected files
evidences = {
    "images": ["crime_scene1.jpg","crime_scene1.jpg", "joker_alley.jpg"],
    "audios": ["joker_laugh.wav"],
    "texts": ["riddle.txt", "note2.txt”],
    "depths": ["depth_suspect.png", "jdancing-depth.png"]
}

# Create directories if they don't exist
for category, files in evidences.items():
    category_path = os.path.join(data_dir, category)
    os.makedirs(category_path, exist_ok=True)

    for file in files:
        file_path = os.path.join(category_path, file)
        if not os.path.exists(file_path):
            print(f"Warning: {file} not found in {category_path}.")

print("All files are correctly organized!")<p><strong>Exécution du fichier</strong></p>python  stages/01-stage/files_check.py<p><strong>Résultat attendu (si tous les fichiers sont corrects) :</strong></p>All files are correctly organized!<p><strong>Résultat attendu (si un fichier est manquant) :</strong></p>Warning: joker_laugh.wav not found in data/audios/
Warning: depth_suspect.png not found in data/depths/<p>Ce script permet d'éviter les erreurs avant de commencer à générer des embeddings et à les indexer dans Elasticsearch.</p><h2>Étape 2 - Organiser les preuves</h2><h3>Générer des embeddings avec ImageBind</h3><p>Pour unifier les indices, nous devons les transformer en représentations vectorielles (embeddings) qui capturent la signification de chaque modalité. Nous utiliserons <strong>ImageBind</strong>, un modèle de Meta AI qui génère des embeddings pour différents types de données<strong>(images, audio, texte et cartes de profondeur</strong>) dans un espace vectoriel partagé.
</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltff4aefffbfb5bf00/6a17eefe6864a45aecb68860/b19a1c32cc7a0b4c00fa5b247b18cce71f9693cb-1580x918.png" alt="Générer des embeddings avec ImageBind" /><h3><strong>Comment fonctionne ImageBind ?</strong></h3><p>Pour comparer différents types de preuves<strong>(images, audio, texte et cartes de profondeur</strong>), nous devons les transformer en vecteurs numériques à l'aide d'<strong>ImageBind</strong>. Ce modèle permet de convertir n'importe quel type d'entrée dans le même format d'intégration, ce qui permet d'<strong>effectuer des recherches intermodales</strong> entre les différentes modalités.</p><p>Vous trouverez ci-dessous un code optimisé (<code>src/embedding_generator.py</code>) permettant de générer des embeddings pour tout type d'entrée en utilisant les processeurs appropriés pour chaque modalité :</p>class EmbeddingGenerator:
    """Class for generating multimodal embeddings using ImageBind."""
    
    def __init__(self):
        self.device = "cuda" if torch.cuda.is_available() else "cpu"
        self.model = self._load_model()

    def _load_model(self):
        """Loads the ImageBind model and sets it to inference mode."""
        model = imagebind_model.imagebind_huge(pretrained=True)
        model.eval()
        model.to(self.device)
        return model

    def generate_embedding(self, input_data, modality):
        """Generates embedding for different modalities"""
        processors = {
            "vision": lambda x: data.load_and_transform_vision_data(x, self.device),
            "audio": lambda x: data.load_and_transform_audio_data(x, self.device),
            "text": lambda x: data.load_and_transform_text(x, self.device),
            "depth": self.process_depth
        }
        
        try:
            # Input type verification
            if not isinstance(input_data, list):
                raise ValueError(f"Input data must be a list. Received: {type(input_data)}")
                
            # Convert input data to a tensor format that the model can process
            # For images: [batch_size, channels, height, width] 
            # For audio: [batch_size, channels, time] 
            # For text: [batch_size, sequence_length]
            inputs = {modality: processors[modality](input_data)}
            with torch.no_grad():
                embedding = self.model(inputs)[modality]
            return embedding.squeeze(0).cpu().numpy()
        except Exception as e:
            logger.error(f"Error generating {modality} embedding: {str(e)}", exc_info=True)
            raise<p>Un tenseur est une structure de données fondamentale dans l'apprentissage automatique et l'apprentissage profond, en particulier lorsque l'on travaille avec des modèles comme ImageBind. Dans notre contexte :</p>input_tensor = processors[modality]([input_data], self.device)<p>Le tenseur représente les données d'entrée (image, son ou texte) converties dans un format mathématique que le modèle peut traiter. En particulier :</p><ul><li><p><strong>Pour les images</strong>: Le tenseur représente l'image sous la forme d'une matrice multidimensionnelle de valeurs numériques (pixels organisés par hauteur, largeur et canaux de couleur).</p></li><li><p><strong>Pour l'audio</strong>: Le tenseur représente les ondes sonores comme une séquence d'amplitudes dans le temps.</p></li><li><p><strong>Pour le texte</strong>: Le tenseur représente les mots ou les tokens sous forme de vecteurs numériques.</p></li></ul><h3>Test de la génération d'encastrement :</h3><p>Testons notre génération d'intégration avec le code suivant. Sauvegardez-le dans 02-stage/test_embedding_generation.py et exécutez-le avec cette commande :</p>python stages/02-stage/test_embedding_generation.py generator = EmbeddingGenerator()
image_embedding = generator.generate_embedding("data/images/crime_scene1.jpg","vision")

print(image_embedding.shape)<h3>Résultat attendu :</h3>(1024,)<p>L'image a été transformée en un <strong>vecteur à 1024 dimensions</strong>.</p><h2>Étape 3 - Stockage et recherche dans Elasticsearch</h2><p>À présent que nous avons généré les encastrements pour les éléments de preuve, nous devons les stocker dans une base de données vectorielle afin de permettre des recherches efficaces. Pour ce faire, nous utiliserons <strong>Elasticsearch</strong>, qui prend en charge les vecteurs denses (<code>dense_vector</code>) et permet d'effectuer des recherches par similarité.</p><p>Cette étape consiste en deux processus principaux :</p><ul><li><p><strong>Indexation des encastrements</strong> → Stockage des vecteurs générés dans Elasticsearch.</p></li><li><p><strong>Recherche de similarité</strong> → Récupère les documents les plus similaires à un nouvel élément de preuve.</p></li></ul><h3>Indexer les preuves dans Elasticsearch</h3><p>Chaque élément de preuve traité par <strong>ImageBind</strong> (image, son, texte ou profondeur) est converti en un <strong>vecteur à 1024 dimensions.</strong> Nous devons stocker ces vecteurs dans <strong>Elasticsearch</strong> pour permettre des recherches ultérieures.</p><p>Le code suivant (<code>src/elastic_manager.py</code>) crée un <strong>index</strong> dans Elasticsearch et configure le mapping pour stocker les embeddings.</p>from elasticsearch import Elasticsearch, helpers
...

class ElasticsearchManager:
    """Manages multimodal operations in Elasticsearch"""
    
    def __init__(self):
        load_dotenv()  # Load variables from .env
        self.es = self._connect_elastic()
        self.index_name = "multimodal_content"
        self._setup_index()
    
    def _connect_elastic(self):
        """Connects to Elasticsearch"""
        return Elasticsearch(
            os.getenv("ELASTICSEARCH_URL"),  # Elasticsearch endpoint
            api_key=os.getenv("ELASTICSEARCH_API_KEY")
        )
    
    def _setup_index(self):
        """Sets up the index if it doesn't exist"""
        if not self.es.indices.exists(index=self.index_name):
            mapping = {
                "mappings": {
                    "properties": {
                        "embedding": {
                            "type": "dense_vector",
                            "dims": 1024,
                            "index": True,
                            "similarity": "cosine"
                        },
                        "modality": {"type": "keyword"},
                        "content": {"type": "binary"},
                        "description": {"type": "text"},
                        "metadata": {"type": "object"},
                        "content_path": {"type": "text"}
                    }
                }
            }
            self.es.indices.create(index=self.index_name, body=mapping)
    
    def index_content(self, embedding, modality, content=None, description="", metadata=None, content_path=None):
        """Indexes multimodal content"""
        doc = {
            "embedding": embedding.tolist(),
            "modality": modality,
            "description": description,
            "metadata": metadata or {},
            "content_path": content_path
        }
        
        if content:
            doc["content"] = base64.b64encode(content).decode() if isinstance(content, bytes) else content
        
        return self.es.index(index=self.index_name, document=doc)
    
    def search_similar(self, query_embedding, modality=None, k=5):
        """Searches for similar contents"""
        query = {
            "knn": {
                "field": "embedding",
                "query_vector": query_embedding.tolist(),
                "k": k,
                "num_candidates": 100,
                "filter": [{"term": {"modality": modality}}] if modality else []
            }
        }
        
        try:
            response = self.es.search(
                index=self.index_name,
                query=query,
                size=k            
            )
            
            # Return both source data and score for each hit
            return [{
                **hit["_source"],
                "score": hit["_score"]
            } for hit in response["hits"]["hits"]]
        
        except Exception as e:
            print(f"Error: processing search_evidence: {str(e)}")
            return "Error generating search evidence"<h3>Exécution de l'indexation</h3><p>Maintenant, indexons un élément de preuve pour tester le processus.</p># Example: Indexing an image from the crime scene
generator = EmbeddingGenerator()
es_manager = ElasticsearchManager(cloud_id="YOUR_CLOUD_ID", api_key="YOUR_API_KEY")

image_embedding = generator.generate_embedding("data/images/crime_scene1.jpg", "vision")

response = es_manager.index_content(
    embedding=image_embedding,
    modality="vision",
    description="Photo of the crime scene with suspicious traces",
    content_path="data/images/crime_scene1.jpg"
)
print(json.dumps(response, indent=2))<p><strong>Résultat attendu dans Elasticsearch (résumé du document indexé) :</strong></p>{
    "embedding": [0.12, -0.53, 0.89, ...],  
    "modality": "vision",  
    "description": "Photo of the crime scene with suspicious traces",  
    "content_path": "data/images/crime_scene1.jpg"  
}<p>Pour indexer toutes les preuves multimodales, veuillez exécuter la commande Python suivante :</p>python stages/03-stage/index_all_modalities.py<p>Les preuves sont désormais stockées dans <strong>Elasticsearch</strong> et prêtes à être récupérées en cas de besoin.</p><h3>Vérification du processus d'indexation</h3><p>Après avoir exécuté le script d'indexation, vérifions si toutes nos preuves ont été correctement stockées dans Elasticsearch. Vous pouvez utiliser <strong>les outils de développement de Kibana</strong> pour exécuter des requêtes de vérification :</p><p>1. Vérifiez d'abord si l'index a été créé :</p>GET _cat/indices/multimodal_content?v<p>2. Ensuite, vérifiez le nombre de documents par modalité :</p>GET multimodal_content/_search
{
  "size": 0,
  "aggs": {
    "modalities": {
      "terms": {
        "field": "modality.keyword"
      }
    }
  }
}<p>3. Enfin, examiner la structure du document indexé :</p>GET multimodal_content/_search
{
  "size": 1,
  "query": {
    "match_all": {}
  }
}<h4>Résultats attendus :</h4><ul><li><p>Un index nommé `multimodal_content` devrait exister.</p></li><li><p>Environ 7 documents répartis entre différentes modalités (vision, audio, texte, profondeur).</p></li><li><p>Chaque document doit contenir les champs suivants : embedding, modality, description, metadata et content_path.</p></li></ul><p>Cette étape de vérification permet de s'assurer que notre base de données d'éléments de preuve est correctement constituée avant de procéder aux recherches de similitudes.</p><h3>Recherche de preuves similaires dans Elasticsearch</h3><p>Maintenant que les preuves ont été indexées, nous pouvons effectuer des recherches pour trouver les enregistrements les plus similaires à un nouvel indice. Cette recherche utilise la <strong>similarité vectorielle</strong> pour renvoyer les enregistrements les plus proches dans l'<strong>espace d'intégration</strong>.</p><p>Le code suivant effectue cette recherche.</p>def search_similar_evidence(self, query_embedding, k=5, modality=None):
    """Performs a kNN search to find the most similar clues."""
    
    knn_query = {
        "field": "embedding",
        "query_vector": query_embedding.tolist(),
        "k": k,
        "num_candidates": 100
    }

    query_body = {"knn": knn_query}
    if modality:
        query_body = {
            "bool": {
                "must": [
                    query_body, 
                    {"term": {"modality": modality}}
                ]
            }
        }

    try:
      results = self.es.search(
        index=self.index_name,
        query=query_body,
        _source_includes=["description", "modality", "content_path"],
        size=k
      )
    except Exception as e:
            print(f"Error processing search_evidence: {str(e)}")
            return "Error generating search evidence”

    return results["hits"]["hits"]<h3>Tester la recherche - Utiliser l'audio comme requête pour des résultats multimodaux</h3><p>Testons maintenant la recherche de preuves à l'aide d'un <strong>fichier audio suspect.</strong> Nous devons générer de la même manière un encapsulage pour le fichier et rechercher des encapsulages similaires :</p>python stages/03-stage/search_by_audio.py# Initialize classes
generator = EmbeddingGenerator()
es_manager = ElasticsearchManager(cloud_id="YOUR_CLOUD_ID", api_key="YOUR_API_KEY")

# Generate embedding for a suspicious audio
audio_embedding = generator.generate_embedding("data/audios/mysterious_laugh.wav", "audio")

# Search for similar evidence in Elasticsearch
similar_evidences = es_manager.search_similar_evidence(audio_embedding, k=3)

# Display the retrieved results
print("\n🔎 Similar evidence found:\n")
for i, evidence in enumerate(similar_evidences, start=1):
    description = evidence['_source']['description']
    modality = evidence['_source']['modality']
    score = evidence['_score']
    content_path = evidence['_source'].get('content_path', 'N/A')

    print(f"{i}. {description} ({modality})")
    print(f"   Similarity: {score:.4f}")
    print(f"   File path: {content_path}\n")<p><strong>Résultats attendus dans le terminal :</strong></p>🔎 Similar evidence found:

1. A sinister laugh captured near the crime scene (audio)
   Similarity: 0.9985
   File path: data/audios/joker_laugh.wav

2. The Joker with green hair, white face paint, and a sinister smile in an urban night setting. (vision)
   Similarity: 0.6068
   File path: data/images/joker_laughing.png

3. Suspect dancing (vision)
   Similarity: 0.5591
   File path: data/images/jdancing.png<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt73f8f56b0f2b8493/6a17ef01faa91381ca93c94e/24067ea40f7958e171149221f83cbd9bcfccc53f-1582x1208.png" alt="" /><p>Nous pouvons maintenant <strong>analyser les éléments de preuve retrouvés</strong> et déterminer leur pertinence dans le cadre de l'affaire.</p><h3>Au-delà de l'audio - Explorer les recherches multimodales</h3><h4>Inverser les rôles : Toute modalité peut être une question ""</h4><p>Dans notre système <strong>RAG multimodal</strong>, <strong>chaque modalité</strong> est une <strong>requête de recherche</strong> potentielle. Dépassons l'exemple de l'audio et explorons comment d'autres types de données peuvent <strong>déclencher des enquêtes</strong>.</p><h4>1. Recherche par texte (déchiffrer la note du criminel)</h4><p>Scénario : Vous avez trouvé un <strong>message texte crypté</strong> et vous souhaitez trouver des preuves connexes.</p>python stages/03-stage/search_by_text.py# Generate embedding from text
text = "Why so serious?"
embedding_text = generator.generate_embedding([text], "text")

# Search for related evidence
similar_evidences = es_manager.search_similar(
    query_embedding=embedding_text,
    k=3
)<p><strong>Résultats attendus :</strong></p>🔎 Similar evidence found:

1. Mysterious note found at the location (text)
   Similarity: 0.7639
   File path: data/texts/riddle.txt

2. The Joker with green hair, white face paint, and a sinister smile in an urban night setting. (vision)
   Similarity: 0.7161
   File path: data/images/joker_laughing.png

3. Why so serious (text)
   Similarity: 0.7132
   File path: data/texts/note2.txt<h4>2. Recherche d'images (suivi de la scène de crime suspecte)</h4><p><strong>Scénario :</strong> Une <strong>nouvelle scène de crime</strong> (<code>crime_scene2.jpg</code>) doit être comparée à d'autres éléments de preuve.
</p>python stages/03-stage/search_by_image.py# Generate embedding for a suspicious image
vision_embedding = generator.generate_embedding(["data/images/crime_scene2.jpg"], "vision")

# Search for similar evidence in Elasticsearch
similar_evidences = es_manager.search_similar(
    query_embedding=vision_embedding,
    k=3
)<p><strong>Sortie :</strong></p>🔎 Similar evidence found:

1. Photo of the crime scene: A dark, rain-soaked alley is filled with playing cards, while a sinister graffiti of the Joker laughing stands out on the brick wall. (vision)
   Similarity: 0.8258
   File path: data/images/crime_scene1.jpg

2. The Joker with green hair, white face paint, and a sinister smile in an urban night setting. (vision)
   Similarity: 0.6897
   File path: data/images/joker_laughing.png

3. Suspect dancing (vision)
   Similarity: 0.6588
   File path: data/images/jdancing.png<h4>3. Recherche d'une carte de profondeur (poursuite 3D)</h4><p><strong>Scénario :</strong> Une <strong>carte de profondeur</strong> <code>jdancing-depth.png</code>() révèle  <strong>les motifs d'évasion de l'</strong> image.</p>python stages/03-stage/search_by_depth.py# Generate embedding for a suspicious depth map
vision_embedding = generator.generate_embedding(["data/depths/jdancing-depth.png"], "depth")

# Search for similar evidence in Elasticsearch
similar_evidences = es_manager.search_similar(
    query_embedding=vision_embedding,
    modality="vision",
    k=3
)<p><strong>Sortie</strong></p>🔎 Similar evidence found:

1. The Joker with green hair, white face paint, and a sinister smile in an urban night setting. (vision)
   Similarity: 0.5329
   File path: data/images/joker_laughing.png<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt224eaf1be75a8b6b/6a17ef03e8fbceada23a1a0e/985f9536db95c772c696dfac996822fb00f199ee-1594x1160.png" alt="" /><p></p>2. Photo of the crime scene: A dark, rain-soaked alley is filled with playing cards, while a sinister graffiti of the Joker laughing stands out on the brick wall. (vision)
   Similarity: 0.5053
   File path: data/images/crime_scene1.jpg<p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9d75bdf986e1bbdd/6a17eef32f4a5c25d5fa89b1/7023ee786ccc760689257abbde2f759ca3cf5c59-1024x768.jpg" alt="" />3. Suspect dancing (vision)
   Similarity: 0.4859
   File path: data/images/jdancing.png<p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt73f8f56b0f2b8493/6a17ef01faa91381ca93c94e/24067ea40f7958e171149221f83cbd9bcfccc53f-1582x1208.png" alt="" /><h3><strong>Pourquoi cela est-il important ?</strong></h3><p>Chaque modalité révèle des <strong>connexions uniques</strong>:</p><ul><li><p><strong>Texte</strong> → Modèles linguistiques du suspect.</p></li><li><p><strong>Images</strong> → Reconnaissance de <strong>lieux et d'objets.</strong></p></li><li><p><strong>Profondeur</strong> → <strong>reconstruction de</strong>la scène en 3D.</p></li></ul><p>Nous disposons désormais d'une <strong>base de données de preuves structurée</strong> dans <strong>Elasticsearch</strong>, qui nous permet de <strong>stocker et d'extraire efficacement des preuves multimodales</strong>.</p><h3><strong>Résumé de ce que nous avons fait :</strong></h3><ul><li><p><strong>Stockage d'encastrements multimodaux</strong> dans Elasticsearch.</p></li><li><p><strong>Effectuer des recherches de similitudes</strong>, trouver des preuves liées à de nouveaux indices.</p></li><li><p><strong>Test de la recherche à l'aide d'un fichier audio suspect</strong>, afin de s'assurer que le système fonctionne correctement.</p></li></ul><p><strong>Prochaine étape :</strong> Nous utiliserons un <strong>LLM</strong> (Large Language Model) pour <strong>analyser les données extraites</strong> et produire un <strong>rapport final.</strong></p><h2>Étape 4 - Faire le lien avec le LLM</h2><p>Maintenant que les <strong>preuves ont été indexées</strong> dans <strong>Elasticsearch</strong> et peuvent être retrouvées par similarité, nous avons besoin d'un <strong>LLM (Large Language Model)</strong> pour les <strong>analyser</strong> et générer un <strong>rapport final</strong> à envoyer au commissaire Gordon. Le <strong>LLM</strong> sera chargé d'<strong>identifier des modèles, de relier des indices et de suggérer un suspect possible</strong> sur la base des éléments de preuve récupérés.</p><p>Pour cette tâche, nous utiliserons le <strong>GPT-4 Turbo</strong>, en formulant une <strong>demande détaillée</strong> afin que le modèle puisse <strong>interpréter les</strong> résultats de manière efficace.</p><h3><strong>Intégration du LLM</strong></h3><p>Pour intégrer le <strong>LLM</strong> dans notre système, nous avons créé la classe <strong>LLMAnalyzer</strong> (<code>src/llm_analyzer.py</code>), qui reçoit les <strong>preuves extraites</strong> d'<strong>Elasticsearch</strong> et génère un <strong>rapport médico-légal</strong> en utilisant ces preuves comme contexte d'invite.</p>import os
from openai import OpenAI
import logging
from dotenv import load_dotenv

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class LLMAnalyzer:
    """Evidence analyzer using GPT-4"""
    
    def __init__(self):
        load_dotenv()
        self.client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
    
    def analyze_evidence(self, evidence_results):
        """
        Analyzes multimodal search results and generates a report
        
        Args:
            evidence_results: Dict with results by modality
            {
                'vision': [...],
                'audio': [...],
                'text': [...],
                'depth': [...]
            }
        """
        # Format evidence for the prompt
        evidence_summary = self._format_evidence(evidence_results)

        # final prompt
        prompt = f"""
You are a highly experienced forensic detective specializing in multimodal evidence analysis. Your task is to analyze the collected evidence (audio, images, text, depth maps) and conclusively determine the **prime suspect** responsible for the Gotham Central Bank case.

---

### **Collected Evidence:**
{evidence_summary}

### **Task:**
1. **Analyze all the evidence** and identify cross-modal connections.
2. **Determine the exact identity of the criminal** based on behavioral patterns, visual/auditory/textual clues, and symbolic markers.
3. **Justify your conclusion** by explaining why this suspect is definitively responsible.
4. **Assign a confidence score (0-100%)** to your conclusion.

---

### **Final Output Format (Strictly Follow This Format):**
- **Prime Suspect:** [Full Name or Alias]
- **Evidence Supporting Conclusion:** [Detailed breakdown of visual, auditory, textual, and behavioral evidence]
- **Behavioral Patterns:** [Key actions, motives, and criminal signature]
- **Confidence Level:** [0-100%]
- **Next Steps (if any):** [What additional evidence would further confirm the identity? If none, state "No further evidence required."]

If there is **insufficient evidence**, specify exactly what is missing and suggest what additional data would be needed for a conclusive identification.

This report must be **direct and definitive**--avoid speculation and provide a final, actionable determination of the suspect's identity.
"""
        try:
            response = self.client.chat.completions.create(
                model="gpt-4-turbo-preview",
                messages=[
                    {
                        "role": "system",
                        "content": "You are a forensic detective specialized in multimodal evidence analysis."
                    },
                    {"role": "user", "content": prompt_01}
                ],
                temperature=0.5,
                max_tokens=1000
            )
            
            report = response.choices[0].message.content
            logger.info("\n📋 Forensic Report Generated:")
            logger.info("=" * 50)
            logger.info(report)
            logger.info("=" * 50)
            
            return report
            
        except Exception as e:
            logger.error(f"Error generating report: {str(e)}")
            return None<h4>Réglage de la température dans l'analyse LLM :</h4><p>Pour notre système d'analyse criminelle, nous utilisons une température modérée de 0,5. Ce cadre équilibré a été choisi pour les raisons suivantes :</p><ul><li><p>Il s'agit d'une solution intermédiaire entre les résultats déterministes (trop rigides) et les résultats hautement aléatoires ;</p></li><li><p>À 0,5, le modèle conserve une structure suffisante pour fournir des conclusions médico-légales logiques et justifiables ;</p></li><li><p>Ce paramètre permet au modèle d'identifier des modèles et d'établir des connexions tout en restant dans des paramètres raisonnables d'analyse médico-légale ;</p></li><li><p>Il permet de concilier le besoin de résultats cohérents et fiables avec la capacité de générer des analyses perspicaces.</p></li></ul><p>Cette température modérée permet de garantir que notre analyse médico-légale est à la fois fiable et perspicace, en évitant les conclusions trop rigides et trop spéculatives.</p><h3>Effectuer l'analyse des données probantes</h3><p>Maintenant que nous avons l'<strong>intégration LLM</strong>, nous avons besoin d'un <strong>script</strong> qui relie tous les composants du système. Ce script va :</p><ul><li><p><strong>Rechercher des preuves similaires</strong> dans <strong>Elasticsearch</strong>.</p></li><li><p><strong>Analyser les preuves récupérées</strong> à l'aide du <strong>LLM</strong> pour produire un <strong>rapport final</strong>.</p></li></ul><h4>Code : Script d'analyse des preuves</h4>python stages/04-stage/rag_crime_analyze.pyimport sys
import os
sys.path.append(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'src'))

from embedding_generator import EmbeddingGenerator
from elastic_manager import ElasticsearchManager
from llm_analyzer import LLMAnalyzer

import json
import logging
from dotenv import load_dotenv

# Setup logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Load environment variables
load_dotenv()

# Initialize classes
generator = EmbeddingGenerator()
es_manager = ElasticsearchManager()

llm = LLMAnalyzer()
logger.info("✅ All components initialized successfully")
    
try:
    evidence_data = {}
    
    # Get data for each modality
    test_files = {
        'vision': 'data/images/crime_scene2.jpg',
        'audio': 'data/audios/joker_laugh.wav',
        'text': 'Why so serious?',
        'depth': 'data/depths/jdancing-depth.png'
    }
    
    logger.info("🔍 Collecting evidence...")
    for modality, test_input in test_files.items():
        try:
            if modality == 'text':
                embedding = generator.generate_embedding([test_input], modality)
            else:
                embedding = generator.generate_embedding([str(test_input)], modality)
            
            results = es_manager.search_similar(embedding, k=2)
            if results:
                evidence_data[modality] = results
                logger.info(f"✅ Data retrieved for {modality}: {len(results)} results")
            else:
                logger.warning(f"⚠️ No results found for {modality}")
                
        except Exception as e:
            logger.error(f"❌ Error retrieving {modality} data: {str(e)}")
    
    if not evidence_data:
        raise ValueError("No evidence data found in Elasticsearch!")
    
    # Test forensic report generation
    logger.info("\n📝 Generating forensic report...")
    report = llm.analyze_evidence(evidence_data)
    
    if report:
        logger.info("✅ Forensic report generated successfully")
        logger.info("\n📊 Report Preview:")
        logger.info("+" * 50)
        logger.info(report)
        logger.info("+" * 50)
    else:
        raise ValueError("Failed to generate forensic report")
        
except Exception as e:
    logger.error(f"❌ Error in analysis : {str(e)}")<h4>Résultats attendus du LLM</h4>**Prime Suspect:** The Joker

**Evidence Supporting Conclusion:**

- **Visual Evidence:**
  - The photo of the crime scene with playing cards scattered around and the graffiti of the Joker laughing matches the Joker's known calling cards and thematic elements. The similarity score of 0.83 indicates a high likelihood that these elements are directly associated with the Joker.
  - The image of the Joker with green hair, white face paint, and a sinister smile in an urban night setting, although with a lower similarity score of 0.69, still supports the presence or recent activity of the Joker in areas consistent with the crime scene's characteristics.

- **Auditory Evidence:**
  - The captured sinister laugh with a similarity score of 1.00 perfectly matches known audio profiles of the Joker, making it a direct auditory signature of his presence at or near the crime scene.
  - Despite the lower similarity score of 0.61, the second audio piece further corroborates the Joker's involvement through thematic consistency.

- **Textual Evidence:**
  - The mysterious note found at the location, with a similarity score of 0.76, likely contains thematic or direct references to the Joker's modus operandi or signature phrases, further implicating him in the crime.
  - The similarity score of 0.72 for the Joker's description in textual evidence reinforces the thematic connection to the crime scene.

- **Depth Evidence:**
  - Depth sensor capture of the suspect with a similarity score of 0.77 suggests a physical presence matching the Joker's known dimensions or characteristic movements.
  - The lower similarity score of 0.53 in the second depth evidence still contributes to the overall pattern of evidence pointing towards the Joker, albeit with less certainty.

**Behavioral Patterns:**
- The Joker is known for his theatrical crimes, often leaving behind a signature trail of chaos, including playing cards, sinister laughter, and thematic graffiti. These elements are not only consistent with his known criminal signature but also directly observed at the crime scene.
- His motives often include creating chaos, drawing attention to his acts, and challenging his arch-nemesis, Batman, making a high-profile bank heist fitting within his behavioral patterns.

**Confidence Level:** 95%

**Next Steps:** No further evidence required.

The combination of visual, auditory, textual, and depth evidence strongly points to the Joker as the prime suspect. The thematic consistency across multiple modes of evidence, combined with known behavioral patterns and criminal signature, leaves little doubt regarding his involvement. While there is always a small margin of uncertainty in forensic analysis, the evidence at hand provides a compelling case against the Joker with a high degree of confidence.<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt05526b73530f46ed/6a17ef043e03d782434f2d30/132ee0880b7fb1e64b5b2d886ab76b58baa6de37-1024x768.jpg" alt="" /><h2>Conclusion : Affaire résolue</h2><p>Après <strong>avoir recueilli et analysé</strong> tous les indices, le <strong>système RAG multimodal</strong> a identifié un suspect : <strong>Le Joker</strong>.</p><p>En combinant des <strong>images, du son, du texte et des cartes de profondeur</strong> dans un <strong>espace vectoriel partagé</strong> à l'aide d'<strong>ImageBind</strong>, le système a pu <strong>détecter des connexions</strong> qu'il aurait été impossible d'identifier manuellement. <strong>Elasticsearch</strong> a permis d'<strong>effectuer des recherches rapides et efficaces</strong>, tandis que le <strong>LLM</strong> a synthétisé les données dans un <strong>rapport clair et concluant</strong>.</p><p>Cependant, le <strong>véritable pouvoir de</strong> ce système <strong>va au-delà de Gotham City.</strong> L'<strong>architecture multimodale RAG</strong> ouvre la voie à de <strong>nombreuses applications dans le monde réel</strong>:</p><ul><li><p><strong>Surveillance urbaine :</strong> Identification des suspects à partir d'<strong>images, de sons et de données de capteurs</strong>.</p></li><li><p><strong>L'analyse médico-légale :</strong> Corrélation d'<strong>éléments de preuve provenant de sources multiples</strong> pour résoudre des <strong>crimes complexes</strong>.</p></li><li><p><strong>Recommandation multimédia :</strong> Création de <strong>systèmes de recommandation</strong> qui comprennent les <strong>contextes multimodaux</strong> (par exemple, suggestion de <strong>musique</strong> sur la base d'images ou de textes).</p></li><li><p><strong>Tendances des médias sociaux :</strong> Détection des <strong>sujets en vogue</strong> dans différents formats de données.</p></li></ul><p>Maintenant que vous avez appris à <strong>construire un système RAG multimodal</strong>, pourquoi ne pas <strong>le tester avec vos propres indices</strong>?</p><p><strong>Partagez vos découvertes</strong> avec nous et aidez la <strong>communauté</strong> à progresser dans le domaine de l'<strong>IA multimodale</strong>!</p><h2>Remerciements particuliers</h2><p>Je tiens à remercier Adrian Cole pour sa précieuse contribution et son examen au cours du processus de définition de l'architecture de déploiement de ce code.</p><h2>Références</h2><ul><li><p><a href="https://www.elastic.co/fr/search-labs/blog/multimodal-image-retrieval-with-roboflow">Construire un système de recherche d'images multimodales en utilisant la recherche KNN et les encastrements CLIP</a></p></li><li><p><a href="https://www.elastic.co/fr/search-labs/tutorials/search-tutorial/vector-search/nearest-neighbor-search">Recherche par k-Nearest Neighbor (kNN)</a></p></li><li><p><a href="https://pytorch.org/docs/stable/tensors.html">Documentation officielle de PyTorch sur les tenseurs</a></p></li><li><p><a href="https://imagebind.metademolab.com/">ImageBind : une nouvelle façon de "relier" l'IA à travers les sens</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/building-multimodal-rag-system</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/building-multimodal-rag-system</guid>
    <category><![CDATA[IA]]></category>
    <dc:creator><![CDATA[Alex Salgado]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt75ee2f922dacb7a8/6a17ef067b54f9775a8b39a3/47635eb4dadb8481854862668231eaa3a005ebee-1600x900.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 11 Mar 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Utiliser Ollama avec l'API d'inférence]]></title>
    <description><![CDATA[Apprenez à intégrer Ollama avec Elasticsearch en utilisant l'API Inference.]]></description>
    <content:encoded><![CDATA[<p>Dans cet article, nous allons apprendre à connecter des modèles locaux au modèle d'inférence d'Elasticsearch à l'aide d'Ollama, puis à poser des questions à vos documents à l'aide de Playground.</p><p>Elasticsearch permet aux utilisateurs de se connecter aux LLM à l'aide de l'<a href="https://www.elastic.co/fr/guide/en/elasticsearch/reference/current/inference-apis.html">API Open</a> Inference, qui prend en charge des fournisseurs tels qu'Amazon Bedrock, Cohere, Google AI, Azure AI Studio, HuggingFace - en tant que service, entre autres.</p><p><a href="https://ollama.com">Ollama</a> est un outil qui vous permet de télécharger et d'exécuter des modèles LLM en utilisant votre propre infrastructure (votre machine/serveur local). Vous trouverez <a href="https://ollama.com/library">ici</a> une liste des modèles disponibles qui sont compatibles avec Ollama.</p><p>Ollama est une excellente option si vous souhaitez héberger et tester différents modèles open source sans avoir à vous soucier des différentes façons dont chacun des modèles pourrait être configuré, ou de la façon de créer une API pour accéder aux fonctions du modèle, car Ollama s'occupe de tout.</p><p>L'API d'Ollama étant compatible avec l'API d'OpenAI, nous pouvons facilement intégrer le modèle d'inférence et créer une application RAG à l'aide de Playground.</p><h2>Produits requis</h2><ol><li><p>Elasticsearch 8.17</p></li><li><p>Kibana 8.17</p></li><li><p>Python</p></li></ol><h2>Étapes</h2><ol><li><p><a href="https://www.elastic.co/fr/search-labs/blog/ollama-with-inference-api#setting-up-ollama-llm-server">Mise en place du serveur Ollama LLM</a></p></li><li><p><a href="https://www.elastic.co/fr/search-labs/blog/ollama-with-inference-api#creating-mappings">Création de mappings</a></p></li><li><p><a href="https://www.elastic.co/fr/search-labs/blog/ollama-with-inference-api#indexing-data">Indexation des données</a></p></li><li><p><a href="https://www.elastic.co/fr/search-labs/blog/ollama-with-inference-api#asking-questions-using-playground">Poser des questions à l'aide de l'aire de jeu</a></p></li></ol><h2>Mise en place du serveur Ollama LLM</h2><p>Nous allons mettre en place un serveur LLM pour le connecter à notre instance Playground en utilisant Ollama. Nous en aurons besoin :</p><ul><li><p>Téléchargez et exécutez Ollama.</p></li><li><p>Utilisez ngrok pour accéder à votre serveur web local qui héberge Ollama sur Internet.</p></li></ul><h3>Télécharger et lancer Ollama</h3><p>Pour utiliser Ollama, il faut d'abord <a href="https://ollama.com/download">le télécharger.</a> Ollama est compatible avec Linux, Windows et macOS. Il vous suffit donc de télécharger la version d'Ollama compatible avec votre système d'exploitation <a href="https://ollama.com/download">ici.</a> Une fois Ollama installé, nous pouvons choisir un modèle dans cette <a href="https://ollama.com/library">liste</a> de LLMs supportés. Dans cet exemple, nous utiliserons le modèle <a href="https://ollama.com/library/llama3.2">llama3.2</a>, un modèle général multilingue. Dans le processus d'installation, vous activerez l'outil de ligne de commande pour Ollama. Une fois qu'il est téléchargé, vous pouvez exécuter la ligne suivante :</p>ollama pull llama3.2<p>Ce qui produira un résultat :</p>pulling manifest
pulling dde5aa3fc5ff... 100% ▕█████████████████████████████████████████████████████████████████████████████████████████▏ 2.0 GB
pulling 966de95ca8a6... 100% ▕█████████████████████████████████████████████████████████████████████████████████████████▏ 1.4 KB
pulling fcc5a6bec9da... 100% ▕█████████████████████████████████████████████████████████████████████████████████████████▏ 7.7 KB
pulling a70ff7e570d9... 100% ▕█████████████████████████████████████████████████████████████████████████████████████████▏ 6.0 KB
pulling 56bb8bd477a5... 100% ▕█████████████████████████████████████████████████████████████████████████████████████████▏   96 B
pulling 34bb5ab01051... 100% ▕█████████████████████████████████████████████████████████████████████████████████████████▏  561 B
verifying sha256 digest
writing manifest
success<p>Une fois installé, vous pouvez le tester avec cette commande :</p>ollama run llama3.2<p>Posons une question :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc12f240920e897f5/6a17f39425daab32a508a367/ad1eff81c1b04d2a747c3afd0ecbc215e5bd96fd-800x501.gif" alt="Lancez Ollama et posez-lui une question" /><p>Une fois le modèle en cours d'exécution, Ollama active une API qui s'exécute par défaut sur le port "11434". Faisons une demande à cette API, en suivant la <a href="https://github.com/ollama/ollama/blob/main/docs/api.md">documentation officielle :</a></p>curl http://localhost:11434/api/generate -d '{                                          
  "model": "llama3.2",               
  "prompt": "What is the capital of France?"
}' <p>Voici la réponse que nous avons reçue :</p>{"model":"llama3.2","created_at":"2024-11-28T21:48:42.152817532Z","response":"The","done":false}
{"model":"llama3.2","created_at":"2024-11-28T21:48:42.251884485Z","response":" capital","done":false}
{"model":"llama3.2","created_at":"2024-11-28T21:48:42.347365913Z","response":" of","done":false}
{"model":"llama3.2","created_at":"2024-11-28T21:48:42.446837322Z","response":" France","done":false}
{"model":"llama3.2","created_at":"2024-11-28T21:48:42.542367394Z","response":" is","done":false}
{"model":"llama3.2","created_at":"2024-11-28T21:48:42.644580384Z","response":" Paris","done":false}
{"model":"llama3.2","created_at":"2024-11-28T21:48:42.739865362Z","response":".","done":false}
{"model":"llama3.2","created_at":"2024-11-28T21:48:42.834347518Z","response":"","done":true,"done_reason":"stop","context":[128006,9125,128007,271,38766,1303,33025,2696,25,6790,220,2366,18,271,128009,128006,882,128007,271,3923,374,279,6864,315,9822,30,128009,128006,78191,128007,271,791,6864,315,9822,374,12366,13],"total_duration":6948567145,"load_duration":4386106503,"prompt_eval_count":32,"prompt_eval_duration":1872000000,"eval_count":8,"eval_duration":684000000}<p><em>Notez que la réponse spécifique pour ce point d'accès est un flux.</em></p><h3>Exposer un point de terminaison à l'internet en utilisant ngrok</h3><p>Comme notre point d'extrémité fonctionne dans un environnement local, il n'est pas possible d'y accéder à partir d'un autre point, comme notre instance Elastic Cloud, via l'internet. <a href="https://ngrok.com">ngrok</a> nous permet d'exposer un port en offrant une IP publique. Créez un compte dans ngrok et suivez le <a href="https://dashboard.ngrok.com/get-started/setup">guide d'installation</a> officiel.</p><p>Une fois l'agent ngrok installé et configuré, nous pouvons exposer le port utilisé par Ollama :</p>ngrok http 11434 --host-header="localhost:11434"<p><em>Remarque : l'en-tête </em><em><code>--host-header="localhost:11434"</code></em><em> garantit que l'en-tête "Host" dans les demandes correspond à "localhost:11434."</em></p><p>L'exécution de cette commande renverra un lien public qui fonctionnera tant que le ngrok et le serveur Ollama fonctionneront localement.</p>Session Status                online                                                                                                                                                                              
Account                       xxxx@yourEmailProvider.com (Plan: Free)                                                                                                                                             
Version                       3.18.4                                                                                                                                                                              
Region                        United States (us)                                                                                                                                                                  
Latency                       561ms                                                                                                                                                                               
Web Interface                 http://127.0.0.1:4040                                                                                                                                                               
Forwarding                    https://your-ngrok-url.ngrok-free.app -&gt; http://localhost:11434                                                                                                                   


Connections                   ttl     opn     rt1     rt5     p50     p90                                                                                                                                         
                              0       0       0.00    0.00    0.00    0.00                                                ```<p>Dans "Forwarding", nous pouvons voir que ngrok a généré une URL. Gardez-le pour plus tard.</p><p>Essayons à nouveau de faire une requête HTTP vers le point d'accès, en utilisant maintenant l'URL générée par le moteur de recherche :</p>curl https://your-ngrok-endpoint.ngrok-free.app/api/generate -d '{                                          
  "model": "llama3.2",               
  "prompt": "What is the capital of France?"
}'<p>La réponse devrait être similaire à la précédente.</p><h2>Création de mappings</h2><h3>Point final ELSER</h3><p>Pour cet exemple, nous allons <a href="https://www.elastic.co/fr/guide/en/elasticsearch/reference/current/put-inference-api.html">créer un point de terminaison d'inférence à l'aide de l'API d'inférence d'Elasticsearch</a>. En outre, nous utiliserons <a href="https://www.elastic.co/fr/guide/en/machine-learning/current/ml-nlp-elser.html">ELSER</a> pour générer les enchâssements.</p>PUT _inference/sparse_embedding/medicines-inference
{
  "service": "elasticsearch",
  "service_settings": {
    "num_allocations": 1,
    "num_threads": 1,
    "model_id": ".elser_model_2_linux-x86_64"
  }
}<p>Pour cet exemple, imaginons que vous ayez une pharmacie qui vend deux types de médicaments :</p><ul><li><p>Médicaments nécessitant une ordonnance.</p></li><li><p>Médicaments qui ne nécessitent pas d'ordonnance.</p></li></ul><p>Cette information serait incluse dans le champ de description de chaque médicament.</p><p>Le LLM doit interpréter ce champ, c'est donc le mappage des données que nous utiliserons :</p>PUT medicines
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "copy_to": "semantic_field"
      },
      "semantic_field": {
        "type": "semantic_text",
        "inference_id": "medicines-inference"
      },
      "text_description": {
        "type": "text",
        "copy_to": "semantic_field"
      }
    }
  }
}<p>Le champ <code>text_description</code> stockera le texte brut des descriptions tandis que <code>semantic_field</code>, qui est un champ de type <a href="https://www.elastic.co/fr/guide/en/elasticsearch/reference/current/semantic-text.html">semantic_text</a>, stockera les enchâssements générés par ELSER.</p><p>La propriété <a href="https://www.elastic.co/fr/guide/en/elasticsearch/reference/current/copy-to.html">copy_to</a> copiera le contenu des champs name et <code>text_description</code> dans le champ sémantique afin de générer les embeddings pour ces champs.</p><h2>Indexation des données</h2><p>Maintenant, indexons les données à l'aide de l'<a href="https://www.elastic.co/fr/guide/en/elasticsearch/reference/current/docs-bulk.html">API _bulk</a>.</p>POST _bulk
{"index":{"_index":"medicines"}}
{"id":1,"name":"Paracetamol","text_description":"An analgesic and antipyretic that does NOT require a prescription."}
{"index":{"_index":"medicines"}}
{"id":2,"name":"Ibuprofen","text_description":"A nonsteroidal anti-inflammatory drug (NSAID) available WITHOUT a prescription."}
{"index":{"_index":"medicines"}}
{"id":3,"name":"Amoxicillin","text_description":"An antibiotic that requires a prescription."}
{"index":{"_index":"medicines"}}
{"id":4,"name":"Lorazepam","text_description":"An anxiolytic medication that strictly requires a prescription."}
{"index":{"_index":"medicines"}}
{"id":5,"name":"Omeprazole","text_description":"A medication for stomach acidity that does NOT require a prescription."}
{"index":{"_index":"medicines"}}
{"id":6,"name":"Insulin","text_description":"A hormone used in diabetes treatment that requires a prescription."}
{"index":{"_index":"medicines"}}
{"id":7,"name":"Cold Medicine","text_description":"A compound formula to relieve flu symptoms available WITHOUT a prescription."}
{"index":{"_index":"medicines"}}
{"id":8,"name":"Clonazepam","text_description":"An antiepileptic medication that requires a prescription."}
{"index":{"_index":"medicines"}}
{"id":9,"name":"Vitamin C","text_description":"A dietary supplement that does NOT require a prescription."}
{"index":{"_index":"medicines"}}
{"id":10,"name":"Metformin","text_description":"A medication used for type 2 diabetes that requires a prescription."}<p>Réponse :</p>{
   "errors": false,
   "took": 34732020848,
   "items": [
 	{
   	"index": {
     	"_index": "medicines",
     	"_id": "mYoeMpQBF7lnCNFTfdn2",
     	"_version": 1,
     	"result": "created",
     	"_shards": {
       	"total": 2,
       	"successful": 2,
       	"failed": 0
     	},
     	"_seq_no": 0,
     	"_primary_term": 1,
     	"status": 201
   	}
 	},
 	{
   	"index": {
     	"_index": "medicines",
     	"_id": "mooeMpQBF7lnCNFTfdn2",
     	"_version": 1,
     	"result": "created",
     	"_shards": {
       	"total": 2,
       	"successful": 2,
       	"failed": 0
     	},
     	"_seq_no": 1,
     	"_primary_term": 1,
     	"status": 201
   	}
 	},
 	{
   	"index": {
     	"_index": "medicines",
     	"_id": "m4oeMpQBF7lnCNFTfdn2",
     	"_version": 1,
     	"result": "created",
     	"_shards": {
       	"total": 2,
       	"successful": 2,
       	"failed": 0
     	},
     	"_seq_no": 2,
     	"_primary_term": 1,
     	"status": 201
   	}
 	},
 	{
   	"index": {
     	"_index": "medicines",
     	"_id": "nIoeMpQBF7lnCNFTfdn2",
     	"_version": 1,
     	"result": "created",
     	"_shards": {
       	"total": 2,
       	"successful": 2,
       	"failed": 0
     	},
     	"_seq_no": 3,
     	"_primary_term": 1,
     	"status": 201
   	}
 	},
 	{
   	"index": {
     	"_index": "medicines",
     	"_id": "nYoeMpQBF7lnCNFTfdn2",
     	"_version": 1,
     	"result": "created",
     	"_shards": {
       	"total": 2,
       	"successful": 2,
       	"failed": 0
     	},
     	"_seq_no": 4,
     	"_primary_term": 1,
     	"status": 201
   	}
 	},
 	{
   	"index": {
     	"_index": "medicines",
     	"_id": "nooeMpQBF7lnCNFTfdn2",
     	"_version": 1,
     	"result": "created",
     	"_shards": {
       	"total": 2,
       	"successful": 2,
       	"failed": 0
     	},
     	"_seq_no": 5,
     	"_primary_term": 1,
     	"status": 201
   	}
 	},
 	{
   	"index": {
     	"_index": "medicines",
     	"_id": "n4oeMpQBF7lnCNFTfdn2",
     	"_version": 1,
     	"result": "created",
     	"_shards": {
       	"total": 2,
       	"successful": 2,
       	"failed": 0
     	},
     	"_seq_no": 6,
     	"_primary_term": 1,
     	"status": 201
   	}
 	},
 	{
   	"index": {
     	"_index": "medicines",
     	"_id": "oIoeMpQBF7lnCNFTfdn2",
     	"_version": 1,
     	"result": "created",
     	"_shards": {
       	"total": 2,
       	"successful": 2,
       	"failed": 0
     	},
     	"_seq_no": 7,
     	"_primary_term": 1,
     	"status": 201
   	}
 	},
 	{
   	"index": {
     	"_index": "medicines",
     	"_id": "oYoeMpQBF7lnCNFTfdn2",
     	"_version": 1,
     	"result": "created",
     	"_shards": {
       	"total": 2,
       	"successful": 2,
       	"failed": 0
     	},
     	"_seq_no": 8,
     	"_primary_term": 1,
     	"status": 201
   	}
 	},
 	{
   	"index": {
     	"_index": "medicines",
     	"_id": "oooeMpQBF7lnCNFTfdn2",
     	"_version": 1,
     	"result": "created",
     	"_shards": {
       	"total": 2,
       	"successful": 2,
       	"failed": 0
     	},
     	"_seq_no": 9,
     	"_primary_term": 1,
     	"status": 201
   	}
 	}
   ]
 }<h2>Poser des questions à l'aide de l'aire de jeu</h2><p><a href="https://www.elastic.co/fr/guide/en/kibana/current/playground.html">Playground</a> est un outil Kibana qui vous permet de créer rapidement un système RAG en utilisant des index Elasticsearch et un fournisseur LLM. Vous pouvez lire cet <a href="https://www.elastic.co/fr/search-labs/blog/playground-connectors-data-chat">article</a> pour en savoir plus.</p><h3>Connecter le programme local d'éducation et de formation tout au long de la vie à l'aire de jeux</h3><p>Nous devons d'abord créer un connecteur qui utilise l'URL publique que nous venons de créer. Dans Kibana, allez sur <strong>Search&gt;Playground</strong> et cliquez sur "Connect to an LLM".</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt22148eabfabf6d3f/6a17f3963e9e459f97ba15c6/1854f0808f8150e359fe62ba5d901d32a88d477c-1600x867.png" alt="Connecter le mécanisme local d'apprentissage tout au long de la vie au terrain de jeu d'Ollama" /><p>Cette action fait apparaître un menu sur le côté gauche de l'interface Kibana. Cliquez ensuite sur "OpenAI".</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6e28194d9012f141/6a17f39725daab500a08a36b/c83d3c4d7035a518124ad7d22b38764db57b6800-933x1007.png" alt="Sélectionnez un connecteur : Open AI Ollama" /><p>Nous pouvons maintenant commencer à configurer le connecteur OpenAI.</p><p>Allez sur "Connector settings" et pour le fournisseur OpenAI, sélectionnez "Other (OpenAI Compatible Service)":</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9984dce6f78a7c08/6a17f3990b0bed0b7add36c4/ecfcdc4b575c309bd55b4e61ca0ddb348aa84f64-917x268.png" alt="Définir les paramètres du connecteur pour l'utilisation d'Ollama avec l'API Inference" /><p>Configurons maintenant les autres champs. Pour cet exemple, nous nommerons notre modèle "medicines-llm". Dans le champ URL, utilisez celle générée par ngrok (/v1/chat/completions). Dans le champ "Modèle par défaut", sélectionnez "llama3.2". Nous n'utiliserons pas de clé API, vous pouvez donc saisir n'importe quel texte au hasard :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8a24b93a39d380fb/6a17f39b96142a15c7eb1c3c/5d3b5027c8096cbe49fb740d70aa24e849611a9d-916x688.png" alt="Ajouter des paramètres" /><p>Cliquez sur "Sauvegarder" et ajoutez les médicaments de l'index en cliquant sur "Ajouter des sources de données":</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4f107a54d5be25f9/6a17f39d4b055deb9d432338/525113da59e902c8235f62bde8fb62371a63e11b-1579x753.png" alt="Ajouter des sources de données pour poser des questions à vos documents à l'aide de Playground" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt03fb36fe05dcdbf5/6a17f39ebe608602f40048be/96138de0bbe2c2ac619f64889d3487df62739ca4-466x805.png" alt="Ajouter des données d'interrogation" /><p>Excellent ! Nous avons maintenant accès à Playground en utilisant le LLM que nous exécutons localement en tant que moteur RAG.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb1b27580107259b3/6a17f3a096142abefceb1c40/cfb48b33c70f4534ab77eb01f58008237f65e6f4-1600x851.png" alt="Sélectionner les paramètres du modèle dans l'aire de jeu" /><p>Avant de le tester, ajoutons des instructions plus spécifiques à l'agent et augmentons le nombre de documents envoyés au modèle à 10, afin que la réponse dispose du plus grand nombre possible de documents. Le champ contextuel sera <code>semantic_field</code>, qui comprend le nom et la description des médicaments, grâce à la propriété copy_to.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4fbc97dc87c6fc62/6a17f3a1e8fbce052c3a1aa0/0c57c9c0e1a0e7b58fffdd3ef81d67d41e2990c4-580x806.png" alt="Paramètres de Moel dans Elastic Playground" /><p>Posons maintenant la question : <em><strong>Puis-je acheter du Clonazepam sans ordonnance ?</strong></em> et voir ce qui se passe :</p><p>Comme prévu, nous avons obtenu la bonne réponse.</p><h3>Étapes suivantes</h3><p>L'étape suivante consiste à créer votre propre application ! Playground fournit un code script en Python que vous pouvez exécuter sur votre machine et adapter à vos besoins. Par exemple, en le plaçant derrière un serveur <a href="https://fastapi.tiangolo.com/">FastAPI</a> pour créer un chatbot d'assurance qualité consommé par votre interface utilisateur.</p><p>Vous pouvez trouver ce code en cliquant sur le bouton <em><strong>Voir le code</strong></em> dans la partie supérieure droite de l'aire de jeux :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt42fe193b8aa08830/6a17f3a33e9e4569e8ba15ca/816bfd0e5f936ad65dbe719d5df10714e550a40b-380x121.png" alt="Bouton de visualisation du code" /><p>Et vous utilisez les <em><strong>clés de l'API Endpoints &amp; </strong></em> pour générer la variable d'environnement <code>ES_API_KEY</code> requise dans le code.</p><p>Pour cet exemple particulier, le code est le suivant :</p>## Install the required packages
## pip install -qU elasticsearch openai
import os
from elasticsearch import Elasticsearch
from openai import OpenAI
es_client = Elasticsearch(
    "https://your-deployment.us-central1.gcp.cloud.es.io:443",
    api_key=os.environ["ES_API_KEY"]
)
openai_client = OpenAI(
    api_key=os.environ["OPENAI_API_KEY"],
)
index_source_fields = {
    "medicines": [
        "semantic_field"
    ]
}
def get_elasticsearch_results():
    es_query = {
        "retriever": {
            "standard": {
                "query": {
                    "nested": {
                        "path": "semantic_field.inference.chunks",
                        "query": {
                            "sparse_vector": {
                                "inference_id": "medicines-inference",
                                "field": "semantic_field.inference.chunks.embeddings",
                                "query": query
                            }
                        },
                        "inner_hits": {
                            "size": 2,
                            "name": "medicines.semantic_field",
                            "_source": [
                                "semantic_field.inference.chunks.text"
                            ]
                        }
                    }
                }
            }
        },
        "size": 3
    }
    result = es_client.search(index="medicines", body=es_query)
    return result["hits"]["hits"]
def create_openai_prompt(results):
    context = ""
    for hit in results:
        inner_hit_path = f"{hit['_index']}.{index_source_fields.get(hit['_index'])[0]}"
        ## For semantic_text matches, we need to extract the text from the inner_hits
        if 'inner_hits' in hit and inner_hit_path in hit['inner_hits']:
            context += '\n --- \n'.join(inner_hit['_source']['text'] for inner_hit in hit['inner_hits'][inner_hit_path]['hits']['hits'])
        else:
            source_field = index_source_fields.get(hit["_index"])[0]
            hit_context = hit["_source"][source_field]
            context += f"{hit_context}\n"
    prompt = f"""
  Instructions:
  - You are an assistant specializing in answering questions about the sale of medicines.
  - Answer questions truthfully and factually using only the context presented.
  - If you don't know the answer, just say that you don't know, don't make up an answer.
  - You must always cite the document where the answer was extracted using inline academic citation style [], using the position.
  - Use markdown format for code examples.
  - You are correct, factual, precise, and reliable.
  Context:
  {context}
  """
    return prompt
def generate_openai_completion(user_prompt, question):
    response = openai_client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[
            {"role": "system", "content": user_prompt},
            {"role": "user", "content": question},
        ]
    )
    return response.choices[0].message.content
if __name__ == "__main__":
    question = "my question"
    elasticsearch_results = get_elasticsearch_results()
    context_prompt = create_openai_prompt(elasticsearch_results)
    openai_completion = generate_openai_completion(context_prompt, question)
    print(openai_completion)<p>Pour que cela fonctionne avec Ollama, vous devez modifier le client OpenAI pour qu'il se connecte au serveur Ollama au lieu du serveur OpenAI. Vous pouvez trouver la liste complète des exemples OpenAI et des points de terminaison compatibles ici.</p>openai_client = OpenAI(
    # you can use http://localhost:11434/v1/ if running this code locally.
    base_url='https://your-ngrok-url.ngrok-free.app/v1/',
    # required but ignored
    api_key='ollama',
)<p>Il faut également changer le modèle en llama3.2 lors de l'appel de la méthode d'achèvement :</p>def generate_openai_completion(user_prompt, question):
    response = openai_client.chat.completions.create(
        model="llama3.2",
        messages=[
            {"role": "system", "content": user_prompt},
            {"role": "user", "content": question},
        ]
    )
    return response.choices[0].message.content<p>Ajoutons notre question : <em><strong>Puis-je acheter du Clonazepam sans ordonnance ? </strong></em>Pour la requête Elasticsearch :</p>def get_elasticsearch_results():
    es_query = {
        "retriever": {
            "standard": {
                "query": {
                    "nested": {
                        "path": "semantic_field.inference.chunks",
                        "query": {
                            "sparse_vector": {
                                "inference_id": "medicines-inference",
                                "field": "semantic_field.inference.chunks.embeddings",
                                "query": "Can I buy Clonazepam without a prescription?"
                            }
                        },
                        "inner_hits": {
                            "size": 2,
                            "name": "medicines.semantic_field",
                            "_source": [
                                "semantic_field.inference.chunks.text"
                            ]
                        }
                    }
                }
            }
        },
        "size": 3
    }
    result = es_client.search(index="medicines", body=es_query)
    return result["hits"]["hits"]<p>Et aussi à l'appel d'achèvement avec quelques impressions, pour que nous puissions confirmer que nous envoyons les résultats d'Elasticsearch dans le cadre du contexte de la question :</p>if __name__ == "__main__":
    question = "Can I buy Clonazepam without a prescription?"
    elasticsearch_results = get_elasticsearch_results()
    context_prompt = create_openai_prompt(elasticsearch_results)
    print("========== Context Prompt START ==========")
    print(context_prompt)
    print("========== Context Prompt END ==========")
    print("========== Ollama Completion START ==========")
    openai_completion = generate_openai_completion(context_prompt, question)
    print(openai_completion)
    print("========== Ollama Completion END ==========")<p>Exécutons maintenant la commande</p><p><code>pip install -qU elasticsearch openai</code></p><p><code>python main.py</code></p><p>Vous devriez voir quelque chose comme ceci :</p>========== Context Prompt START ==========
  Instructions:
  - You are an assistant specializing in answering questions about the sale of medicines.
  - Answer questions truthfully and factually using only the context presented.
  - If you don't know the answer, just say that you don't know, don't make up an answer.
  - You must always cite the document where the answer was extracted using inline academic citation style [], using the position.
  - Use markdown format for code examples.
  - You are correct, factual, precise, and reliable.
  Context:
  Clonazepam
 ---
An antiepileptic medication that requires a prescription.A nonsteroidal anti-inflammatory drug (NSAID) available WITHOUT a prescription.
 ---
IbuprofenAn anxiolytic medication that strictly requires a prescription.
 ---
Lorazepam


========== Context Prompt END ==========
========== Ollama Completion START ==========
No, you cannot buy Clonazepam over-the-counter (OTC) without a prescription [1]. It is classified as a controlled substance in the United States due to its potential for dependence and abuse. Therefore, it can only be obtained from a licensed healthcare provider who will issue a prescription for this medication.
========== Ollama Completion END ==========<h2>Conclusion</h2><p>Dans cet article, nous pouvons voir la puissance et la polyvalence d'outils comme Ollama lorsque nous les utilisons avec l'API d'inférence Elasticsearch et Playground.</p><p>Après quelques étapes simples, nous avions une application RAG fonctionnelle avec un chat qui utilisait un LLM fonctionnant dans notre propre infrastructure à un coût nul. Cela nous permet également de mieux contrôler les ressources et les informations sensibles, tout en nous donnant accès à une variété de modèles pour différentes tâches.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ollama-with-inference-api</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ollama-with-inference-api</guid>
    <category><![CDATA[IA]]></category>
    <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd9c8eb0fc946920e/6a17f3a46864a4b2fbb688f0/399b9ef527be633845fb6505b68132cc03bc9e09-1150x628.png" length="0" type="image/png"/>
    <pubDate>Fri, 14 Feb 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Tester DeepSeek R1 localement pour RAG avec Ollama et Kibana]]></title>
    <description><![CDATA[Apprenez à exécuter une instance locale de DeepSeek et à vous y connecter depuis Kibana.]]></description>
    <content:encoded><![CDATA[<p>Le nouveau grand modèle de langage DeepSeek R1, développé par le fonds d’investissement chinois High-Flyer, fait beaucoup parler de lui. On spécule beaucoup dans les médias sur les implications pour l’industrie depuis qu’ils ont présenté un grand modèle de langage capable de raisonnement par chaîne de pensée avec des poids ouverts. Si vous êtes curieux d’utiliser ce nouveau modèle avec la RAG et toutes les capacités de base de données vectorielle d’Elasticsearch, ce tutoriel rapide vous montrera comment commencer avec DeepSeek R1 en utilisant l’inférence locale. Au fil du processus, nous allons utiliser l’outil Playground d’Elastic. Nous découvrirons également les propriétés, bonnes ou mauvaises, de Deepseek R1 en matière de RAG.</p><p>Voici un diagramme de ce que nous allons configurer dans ce tutoriel :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3214cc505e3d4d06/6a17df98dbb4ff12bafb55da/8aafec9011e986cd85b10958544a4d77be81e518-739x419.png" alt="Configuration de Deepseek avec Elasticsearch et Ollama" /><h2>Configuration de l'inférence locale avec Ollama</h2><p><a href="https://ollama.com/">Ollama</a> est une excellente façon de tester rapidement un ensemble de modèles open source pour l’inférence locale. C'est un outil très apprécié des développeurs en IA.</p><h3>Lancer Ollama en natif</h3><p>Une <a href="https://github.com/ollama/ollama/tree/main?tab=readme-ov-file#ollama">installation locale</a> sur Mac, Linux ou Windows est le moyen le plus facile d’utiliser les capacités GPU que vous pourriez avoir localement, en particulier pour ceux qui possèdent des puces Apple de la série M. Après avoir installé Ollama, vous pouvez télécharger et exécuter Deepseek R1 avec la commande ci-dessous.</p><p>Vous pourriez avoir besoin d’ajuster la taille des paramètres pour qu’elle corresponde à votre matériel. Vous trouverez les tailles disponibles <a href="https://ollama.com/library/deepseek-r1">ici</a>.</p>ollama run deepseek-r1:7b<p>Il est possible de discuter avec le modèle dans le terminal, mais il continue de fonctionner même lorsque vous quittez la commande avec CTRL+d ou que vous tapez « /bye ». Pour voir que le modèle continue de fonctionner, entrez :</p>ollama ps<h3>Exécution d'Ollama dans un conteneur</h3><p>Sinon, la façon la plus rapide de faire fonctionner Ollama est d’utiliser un moteur de conteneurs comme Docker. Si l’utilisation du GPU de votre machine locale n’est pas toujours simple, selon l’environnement, la mise en place d'une configuration de test rapide est simple tant que votre conteneur a assez de RAM et d’espace de stockage pour les modèles de plusieurs Go.</p><p>Pour faire fonctionner Ollama dans Docker, il suffit d'exécuter :</p>mkdir ollama_deepseek
cd ollama_deepseek
mkdir ollama
docker run -d -v ./ollama:/root/.ollama -p 11434:11434 \
--name ollama ollama/ollama
<p>Un répertoire « ollama » sera créé dans le dossier actuel, puis monté à l’intérieur du conteneur pour y conserver la configuration d’Ollama et les modèles. En fonction du nombre de paramètres employés, leur taille peut aller de quelques Go à plusieurs dizaines de Go, donc veillez à choisir un volume disposant d’un espace libre suffisant.</p><p>Remarque : si votre machine est équipée d'un GPU Nvidia, veillez à installer le <a href="https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html#installation">kit d’outils de conteneur Nvidia</a> et à ajouter « --gpus=all » à la commande « docker run » ci-dessus.</p><p>Dès que le conteneur Ollama fonctionne sur votre machine, vous pouvez télécharger un modèle comme deepseek-r1 en utilisant :</p>docker exec -it ollama ollama pull deepseek-r1:7b<p>À l'instar de l'approche bare metal, il est possible que vous souhaitiez ajuster la taille des paramètres pour qu'elle corresponde à votre matériel. Les tailles disponibles sont indiquées sur le site <a href="https://ollama.com/library/deepseek-r1">https://ollama.com/library/deepseek-r1</a>.</p><p>Une fois que l'extraction du modèle est terminée, il vous suffit de taper « /bye » pour quitter le prompt. Pour vérifier que le modèle est toujours en cours d’exécution :</p>docker exec -it ollama ollama ps<h2>Tester notre inférence locale avec une commande curl</h2><p>Pour tester l'inférence locale avec curl, vous pouvez exécuter la commande suivante. Nous avons recours à stream:false pour faciliter la lecture de la réponse narrative au format JSON :</p>curl http://localhost:11434/api/generate -d '{
  "model": "deepseek-r1:7b",
  "stream": false,
  "prompt":"Why is Elastic so cool?"
}'<h2>Test d’Ollama compatible OpenAI et d’une invite de RAG</h2><p>Pour plus de commodité, Ollama offre également un point de terminaison REST qui imite l'interface d’OpenAI, ce qui le rend compatible avec un grand nombre d'outils, notamment Kibana.</p>curl http://localhost:11434/v1/chat/completions -d '{
  "model": "deepseek-r1:7b",
  "stream": false,
  "messages": [
    { 
      "role": "system", 
      "content": "You are a helpful AI Assistant that uses the following context to answer questions only use the following context. \n\nContext:  The color of the sky today is purple. "},
    { "role": "user", 
      "content": "What does the sky look like today?" 
    }
  ]
}'<p>Le test de cette invite plus complexe produit un contenu qui comporte une partie dans laquelle le modèle a été entraîné pour trouver la solution au problème.</p>&lt;think&gt; 
Okay, so I need to figure out what the user is asking for here. They provided a context where the sky is described as purple today and then asked about how the sky looks. At first glance, it seems straightforward—maybe they just want confirmation or more details on why the sky is that color.
Wait, but maybe there's something deeper. Purple skies aren't something I encounter every day. It usually happens at certain times of the year, like during sunrise or sunset with the sun setting in pink or orange. Could this be a hint about the time of day? Or perhaps it's just an unusual natural phenomenon? 
I should consider if \"purple\" is a typo. Maybe they meant something else like blue or gray. But since they specifically said purple, I'll go with that. Purple skies can happen when there are atmospheric conditions that scatter light differently, maybe due to pollution or cloud cover affecting the sunset.

So, putting it all together, the user might be looking for an explanation of why today's sky is purple and what that implies about the weather or time of day. Alternatively, they could just want a simple statement confirming that the sky looks purple today.
&lt;/think&gt;

The color of the sky today is described as purple. This unusual shade can occur due to atmospheric conditions affecting light scattering, such as during sunrise/sunset with pollution or cloud cover influencing the sunset's hues.<h2>Connexion d'Ollama à Kibana</h2><p>Une excellente façon d’utiliser Elasticsearch est le script de développement « <a href="https://github.com/elastic/start-local?tab=readme-ov-file#-try-elasticsearch-and-kibana-locally">start-local</a> ».</p><p>Veillez à ce que votre Kibana et votre Elasticsearch soient en mesure d'atteindre votre Ollama sur le réseau. Si vous avez un environnement Elastic en conteneur local, vous devrez peut-être remplacer « localhost » par « host.docker.internal ». ou « host.conteneurs.internal » afin d’avoir un chemin réseau jusqu'à la machine hôte.</p><p>Dans Kibana, accédez à Stack Management &gt; Alertes et informations &gt; Connecteurs.</p><h3>Ce qu’il faut faire si cette alerte de configuration courante s'affiche</h3><p>Vous devrez vous assurer que xpack.encryptedSavedObjects.encryptionKey <a href="https://www.elastic.co/guide/en/kibana/current/xpack-security-secure-saved-objects.html">est correctement défini</a>. C'est une erreur courante lors de l'exécution d'une installation locale de Kibana sous Docker, c'est pourquoi je vais énumérer les étapes à suivre dans la syntaxe de Docker pour la corriger.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta4f7b7be2e04afae/6a17df9a1d1b8391e293e393/b70b4b810bcac1d1599b07da90a98c5c744a38de-497x223.png" alt="" /><p>Pour que les modifications soient conservées à l’arrêt du conteneur, il faut que le répertoire kibana/config soit persistant. Mes volumes de conteneurs Kibana ressemblent à ceci dans docker-compose.yml :</p>services:
  kibana:
...
   volumes:
      - certs:/usr/share/kibana/config/certs
      - kibanadata:/usr/share/kibana/data
      - kibanaconfig:/usr/share/kibana/config
...
volumes:
  certs:
    driver: local
  esdata01:
    driver: local
  kibanadata:
    driver: local
  kibanaconfig:
    driver: local<p>Vous pouvez à présent créer le keystore et y attribuer une valeur afin que les clés des connecteurs ne soient plus stockées en clair.</p>## generate some new keys for me and print them to the terminal
docker exec -it kibana_1 bin/kibana-encryption-keys generate

## create a new keystrore
docker exec -it kibana_1 bin/kibana-keystore create
docker exec -it kibana_1 bin/kibana-keystore add xpack.encryptedSavedObjects.encryptionKey

## You'll be prompted to paste in a value<p>Afin que les modifications soient appliquées, redémarrez l'ensemble de votre cluster.</p><h3>Création du connecteur</h3><p>Depuis l’écran de configuration du connecteur (dans Kibana, accédez à Stack Management &gt; Alertes et informations &gt; Connecteurs), créez un connecteur et sélectionnez le type « OpenAI ».</p><p>Configurez le connecteur avec les paramètres suivants</p><ul><li><p>Nom du connecteur : Deepseek (Ollama)</p></li><li><p>Sélectionnez un fournisseur OpenAI : autre (service compatible OpenAI)</p></li><li><p>URL : <a href="http://localhost:11434/v1/chat/completions">http://localhost:11434/v1/chat/completions</a></p><ul><li><p>Modifiez pour indiquer le bon chemin d'accès à votre ollama. Pensez à remplacer par host.docker.internal ou son équivalent si vous appelez depuis un conteneur.</p></li></ul></li><li><p>Modèle par défaut : deepseek-r1:7b</p></li><li><p>Clé API : inventez quelque chose, une entrée est nécessaire mais la valeur n'a pas d'importance</p></li></ul><p>Notez que le test d'un connecteur personnalisé à Ollama dans la configuration du connecteur est actuellement défectueux dans la version 8.17, mais a été corrigé dans la future version 8.18 de Kibana.</p><p>Notre connecteur ressemble à ceci :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt774e0793eb110f9d/6a17df9c445de981014d004d/4ce214aa953b4090ed112fbde40b01c01fb8f5c7-786x836.png" alt="" /><h2>Intégration des données vectorielles dans Elasticsearch</h2><p>Si vous connaissez déjà Playground et que vous y avez des données, vous pouvez passer à l'étape Playground ci-dessous. En revanche, si vous avez besoin de données de test rapides, assurez-vous que nos API d'inférence sont configurées. Depuis la version 8.17, les allocations de machine learning sont dynamiques. Pour télécharger et activer le vecteur dense multilingue e5, il suffit d’exécuter la commande suivante dans les Outils de développement de Kibana.</p>GET /_inference


POST /_inference/text_embedding/.multilingual-e5-small-elasticsearch
{
   "input": "are internet memes about deepseek sound investment advice?"
}<p>Si ce n'est pas déjà fait, cette opération lancera le téléchargement du modèle e5 depuis les dépôts de modèles d'Elastic.</p><p>Chargeons à présent un livre du domaine public comme contexte pour notre RAG. Voici où télécharger « Alice au pays des merveilles » depuis le Projet Gutenberg : <a href="https://www.gutenberg.org/cache/epub/11/pg11.txt">lien</a>. Enregistrez-le sous la forme d’un fichier  .txt.</p><p>Accédez à Elasticsearch &gt; Accueil &gt; Télécharger un fichier</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt45c594487844ecb4/6a17df9dfaa9137edb93c786/649042271f34a5e66789b17c39bfe95971c7f4ce-1360x629.png" alt="" /><p>Sélectionnez ou glissez-déposez votre fichier texte, puis cliquez sur le bouton « Importer ».</p><p>Sur l'écran « Importer des données », sélectionnez l'onglet « Avancé », puis définissez le nom de l'index sur « book_alice ».</p><p>Sélectionnez l’option « Ajouter un champ supplémentaire », elle se trouve juste en dessous de « Champs créés automatiquement ». Sélectionnez « Ajouter un champ de texte sémantique » et changez le point de terminaison d’inférence en « .multilingual-e5-small-elasticsearch ». Sélectionnez Ajouter, puis Importer.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9d9c22ccdaee8590/6a17df9f3e03d731f94f2b8e/e58d5c9a2d406d8e62eb96cab9ac98ca89414346-507x602.png" alt="" /><p></p><p>Une fois le chargement et l’inférence terminés, nous pouvons nous rendre sur Playground.</p><h2>Test de RAG dans Playground</h2><p>Accédez à Elasticsearch &gt; Playground dans Kibana.</p><p>Sur l’écran du Playground, vous devriez voir une coche verte et le message « LLM Connected » (LLM connecté) qui indique qu’il existe un connecteur. Il s'agit du connecteur Ollama que nous venons de créer ci-dessus. Un guide plus complet pour Playground est accessible <a href="https://www.elastic.co/guide/en/kibana/current/playground.html">ici</a>.</p><p>Cliquez sur le bouton bleu « Ajouter des sources de données » et sélectionnez l'index book_alice que nous avions fait, ou un autre index que vous avez préalablement configuré qui utilise les API d'inférence pour les plongements.</p><p>Deepseek est un modèle de chaîne de pensée présentant de fortes caractéristiques d'alignement. C'est à la fois un avantage et un inconvénient du point de vue de la RAG. L'entraînement de type « chaîne de pensée » peut aider Deepseek à rationaliser des énoncés qui semblent contradictoires dans les citations, mais le fort alignement avec les connaissances acquises lors de l'entraînement peut lui faire préférer sa propre version des faits plutôt que notre contexte. Bien qu'il parte d'une bonne intention, ce fort alignement rend les LLM difficiles à guider quand on aborde des sujets où nos connaissances privées sont en contradiction avec les données d'entraînement, ou n'y sont pas bien représentées.</p><p>Dans la configuration de notre Playground, nous avons saisi l'invite système « Vous êtes un assistant pour les tâches de questions-réponses en utilisant les passages pertinents du livre Alice au pays des merveilles » et accepté les autres paramètres par défaut.</p><p>À la question « Qui était au goûter ? » nous obtenons la réponse : « Réponse : le lièvre de mars, le chapelier et le loir étaient au goûter. [Citation : positions 1 et 2] », ce qui est exact.
</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0ce6a0facd972fdd/6a17dfa03e03d79aaa4f2b92/e8af3ff93a72e1f02de8e73f6c2606cbc19970e5-1296x813.png" alt="" /><p>Nous pouvons voir d'après les tags que Deepseek a clairement réfléchi au contenu des citations pour répondre aux questions.</p><h2>Test des limites d'alignement</h2><p>Pour tester Deepseek, créons un scénario qui soit un défi intellectuel. Nous allons créer un index de théories du complot que les données d'entraînement de Deepseek connaissent et qui ne sont pas vraies.</p><p>Dans les outils de développement de Kibana, créons l'index et les données suivants :</p>PUT /classic_conspiracies
{
   "mappings": {
       "properties": {
           "content": {
               "type": "text",
               "copy_to": "content_semantic"
           },
           "content_semantic": {
               "type": "semantic_text",
               "inference_id": ".multilingual-e5-small-elasticsearch"
           }
       }
   }
}




POST /classic_conspiracies/_doc/1
{
   "content": "birds aren't real, the government replaced them with drones a long time ago"
}
POST /classic_conspiracies/_doc/2
{
   "content": "tinfoil hats are necessary to prevent our brains from being read"
}
POST /classic_conspiracies/_doc/3
{
   "content": "ancient aliens influenced early human civilizations, this explains why things made out of stone are marginally similar on different continents"
}<p>
Ces théories du complot seront les données de base pour notre LLM. Même en utilisant une invite système agressive, Deepseek n’accepte pas notre version des faits. Si nos données privées étaient plus fiables, mieux ancrées ou mieux alignées sur les besoins de notre organisation, ce scénario ne serait pas acceptable</p><p>À la question test « Les oiseaux sont-ils réels ? » (explication : <a href="https://knowyourmeme.com/memes/birds-arent-real">know your meme</a>), nous obtenons la réponse suivante : « Dans le contexte fourni, les oiseaux ne sont pas considérés comme réels, mais en réalité, ce sont des animaux réels. » [Contexte : position 1]. Ce test prouve que DeepSeek R1 est puissant, même avec le niveau de paramètre 7B… mais ce n'est peut-être pas le meilleur choix pour la RAG, selon notre jeu de données.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5e8dabf65ea200e1/6a17dfa2ec0f8982135a6541/67d5f6cdb97bfd3adb926cbd588c768f9d6730ae-1277x737.png" alt="" /><h2>Quels sont les enseignements à retenir ?</h2><p>En résumé :</p><ul><li><p>L'exécution de modèles en local à l'aide d'outils comme Ollama est une excellente option pour avoir un aperçu de leur comportement.</p></li><li><p>Le Deepseek R1 est un modèle de raisonnement qui comporte des avantages et des inconvénients pour les cas d'utilisation comme la RAG.</p></li><li><p>Playground est en mesure de se connecter à des infrastructures d'hébergement d'inférence comme Ollama par le biais d'une API REST de type OpenAI, qui devient de facto un standard en cette période de lancement de l'hébergement de l'IA.</p></li></ul><p>Globalement, nous sommes impressionnés par les progrès de la RAG locale, « air gapped ». Les outils d’Elasticsearch, de Kibana et des modèles de poids ouverts disponibles ont considérablement progressé depuis que nous avons écrit pour la première fois sur <a href="https://www.elastic.co/search-labs/blog/privacy-first-ai-search-langchain-elasticsearch">la recherche IA axée sur la confidentialité</a> en 2023.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/deepseek-rag-ollama-playground</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/deepseek-rag-ollama-playground</guid>
    <category><![CDATA[IA]]></category>
    <category><![CDATA[Kibana]]></category>
    <dc:creator><![CDATA[Dave Erickson,Jakob Reiter]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2a4b2ae6bd97850b/6a17dfa4be6086558f00464c/1bd853bfdfa2710e44cc4c08dede6bd21b35c4b8-1542x860.png" length="0" type="image/png"/>
    <pubDate>Thu, 30 Jan 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Recherche à facettes : Utiliser l'IA pour améliorer la portée et les résultats de la recherche]]></title>
    <description><![CDATA[Découvrez comment utiliser la recherche par facettes dans Elasticsearch pour restreindre rapidement les options au sein des catégories.]]></description>
    <content:encoded><![CDATA[<p>Dans cet article, nous verrons comment l'intelligence artificielle (IA), et plus particulièrement l'utilisation de modèles linguistiques avancés tels que GPT-4, peut contribuer à créer des facettes plus contextuelles, les rendant encore plus pertinentes et utiles pour les utilisateurs.</p><p>La recherche par facettes est un outil puissant dans les plateformes de commerce électronique. Il permet d'organiser et d'affiner les résultats de la recherche en fonction des caractéristiques des éléments affichés. Souvent confondues avec les filtres, les facettes fonctionnent différemment. Les filtres sont des attributs fixes, définis par des informations toujours présentes dans l'index, telles que la catégorie ou le format d'un produit. Les facettes, quant à elles, sont dynamiques et générées à partir des résultats renvoyés par la recherche exécutée.</p><p>Imaginez un catalogue de vêtements : des champs tels que "category" (par exemple, t-shirts, pantalons) ou "gender" (par exemple, homme, femme) sont des filtres qui permettent d'affiner les résultats. Les facettes, quant à elles, reflètent des caractéristiques spécifiques des produits apparaissant dans les résultats, telles que les couleurs communes, les tailles disponibles ou les matériaux. Cela permet une expérience de recherche plus adaptable et contextuelle.</p><p>Voici une image dans laquelle nous interagissons avec une facette et pouvons voir les résultats de la recherche filtrés par celle-ci.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc4ce8ca7e5b62ad7/6a17f8ba6864a4534bb68979/74d2159706ab7248ebe5efddc74c882f0693db71-600x420.gif" alt="Exemple de recherche à facettes" /><h2>Comment l'IA peut améliorer la génération de facettes</h2><p>L'intelligence artificielle est souvent associée à la recherche sémantique et aux enchâssements, mais qu'en est-il des facettes ? Comment l'IA peut-elle être exploitée pour rendre les facettes plus utiles et plus adaptées au contexte de chaque recherche ?</p><p>Une possibilité intéressante consiste à utiliser l'IA pour créer de nouvelles catégorisations qui vont au-delà des classifications traditionnelles de l'index. En analysant les caractéristiques spécifiques du contenu, ces nouvelles catégories peuvent fournir une contextualisation plus riche et plus précise, rendant les facettes plus pertinentes et mieux adaptées aux besoins des utilisateurs. Cela permet d'affiner les résultats de manière plus significative par rapport aux catégories de documents originales.</p><h2>Comment l'IA peut affiner les classifications de films pour de meilleures recherches</h2><p>Analysons les films suivants, tous classés actuellement dans le genre dramatique :</p><ul><li><p>Requiem for a Dream
Résumé : Les utopies induites par la drogue de quatre habitants de Coney Island sont brisées lorsque leurs addictions deviennent profondes.</p></li><li><p>American Beauty
CV : Un père de famille de banlieue, sexuellement frustré, traverse une crise de la quarantaine après s'être entiché de la meilleure amie de sa fille.</p></li><li><p>Good Will Hunting
CV : Will Hunting, concierge au M.I.T., est doué pour les mathématiques, mais a besoin de l'aide d'un psychologue pour trouver un sens à sa vie.</p></li></ul><p>Cette classification des genres ne rend pas compte des différences subtiles ou des contextes uniques de chaque film. En exploitant l'IA pour analyser les synopsis et les thèmes centraux, nous pouvons créer de nouvelles catégories qui reflètent mieux le véritable contexte de chaque film. Par exemple :</p><ul><li><p>Requiem for a Dream - Nouvelle catégorie : "Addiction et dépendance"</p></li><li><p>American Beauty - Nouvelle catégorie : "Crise de la quarantaine"</p></li><li><p>Good Will Hunting - Nouvelle catégorie : "Lutte intellectuelle"</p></li></ul><p>Ces nouvelles catégories rendent les recherches beaucoup plus précises tout en offrant aux utilisateurs des filtres plus significatifs pour affiner leurs résultats. Cette approche est particulièrement efficace lorsque les catégories d'origine sont trop génériques, ce qui permet aux utilisateurs de trouver plus facilement ce qu'ils recherchent.</p><h2>Créer de nouvelles catégories avec GPT-4 : exemple de recherche à facettes</h2><p>Dans cet exemple, nous montrerons comment un modèle d'IA peut être utilisé pour créer de nouvelles catégories de films qui sont plus précises et alignées sur le contexte de chaque œuvre. Pour démontrer ce processus, nous utiliserons le pipeline de simulation Elastic avec le service d'inférence OpenAI. Un pipeline comprenant plusieurs processeurs sera créé, dont le processeur de script, qui sera chargé de créer l'invite à exécuter dans le processeur d'inférence, capable de déterminer les nouvelles catégories. Les autres processeurs seront utilisés pour manipuler les données et les champs auxiliaires générés pendant l'exécution du pipeline. Il convient de mentionner que cette logique peut également être appliquée à d'autres outils ou modèles similaires.</p><p>Tout d'abord, nous devons créer le point de terminaison de l'inférence, où nous définissons le service comme OpenAI, le jeton requis pour accéder au service et le modèle. Dans cet exemple, j'utilise gpt-4o-mini. Pour plus de détails sur le service d'inférence OpenAI, cliquez <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/infer-service-openai.html">ici.</a></p>PUT _inference/completion/generate_topics_ia
{
    "service": "openai",
    "service_settings": {
        "api_key": "your-token",
        "model_id": "gpt-4o-mini"
    }
}<p>Le point final étant créé, nous sommes maintenant prêts à l'utiliser pour créer les nouvelles catégories. Vous trouverez ci-dessous un pipeline qui prend en charge l'ensemble du processus de manipulation des données des documents et de génération des messages. Je vais expliquer en détail la fonction de chaque processeur.</p><p>Le premier processeur sera chargé de construire l'invite. Il est très important de détailler clairement les instructions afin que l'IA puisse analyser et identifier correctement les sujets. Dans ce questionnaire, je demande 2 sujets basés sur l'analyse du titre, de la description et des genres des films.</p>{
        "script": {
          "source": """
            ctx.prompt = "You are an expert in semantic analysis and audiovisual content categorization. Your task is to generate only subcategories (max 2 topics) that describe specific aspects of movies based on their genres and descriptions. The output should be like: 'n1, n2, ...n'. Here is a movie info to analyze: Title: " + ctx.title  + "Genres: " + ctx.genres  + "Description: " + ctx.description;
          """
        }<p>Le pipeline suivant est le pipeline d'inférence, il recevra l'invite et l'enverra à notre point de terminaison <strong>generate_topics_ia</strong>. La réponse générée par le modèle sera stockée dans le champ résultat.</p>{
        "inference": {
          "model_id": "generate_topics_ia",
          "input_output": {
            "input_field": "prompt",
            "output_field": "result"
          }
        }
      }<p>Ensuite, nous avons trois processeurs utilisés pour manipuler la réponse et la placer dans le champ des sujets, en plus de supprimer les champs temporaires que j'ai créés.</p><p>En exécutant ce pipeline, nous obtiendrons les résultats ci-dessous :</p>{
  "docs": [
    {
      "doc": {
        "_index": "index",
        "_version": "-3",
        "_id": "1",
        "_source": {
          "description": "While Frodo and Sam edge closer to Mordor with the help of the shifty Gollum, the divided fellowship makes a stand against Sauron's new ally, Saruman, and his hordes of Isengard.",
          "model_id": "generate_topics_ia",
          "title": "The Lord of the Rings: The Fellowship of the Ring",
          "genres": [
            "Action",
            "Adventure",
            "Drama"
          ],
          "topics": [
            "Fantasy",
            "Quest"
          ]
        },
        "_ingest": {
          "timestamp": "2024-11-22T17:51:51.340010257Z"
        }
      }
    },
    {
      "doc": {
        "_index": "index",
        "_version": "-3",
        "_id": "2",
        "_source": {
          "description": "A team of explorers travel through a wormhole in space in an attempt to ensure humanity's survival.",
          "model_id": "generate_topics_ia",
          "title": "Interstellar",
          "genres": [
            "Adventure",
            "Drama",
            "Sci-Fi"
          ],
          "topics": [
            "space exploration",
            "human survival"
          ]
        },
        "_ingest": {
          "timestamp": "2024-11-22T17:51:51.340413173Z"
        }
      }
    },
    {
      "doc": {
        "_index": "index",
        "_version": "-3",
        "_id": "3",
        "_source": {
          "description": "An astronaut becomes stranded on Mars after his team assume him dead, and must rely on his ingenuity to find a way to signal to Earth that he is alive.",
          "model_id": "generate_topics_ia",
          "title": "The Martian",
          "genres": [
            "Adventure",
            "Drama",
            "Sci-Fi"
          ],
          "topics": [
            "survival",
            "ingenuity"
          ]
        },
        "_ingest": {
          "timestamp": "2024-11-22T17:51:51.340427965Z"
        }
      }
    }
  ]
}<p>Il est à noter que nous avons de nouvelles catégories qui sont plus en rapport avec le contexte des films, même si certains d'entre eux sont initialement du même genre.</p><p>Nous pouvons maintenant utiliser ces nouvelles catégories et les indexer avec le document. Ainsi, lors de la génération des facettes, en plus de la catégorie principale, nous avons des sous-catégories plus spécifiques qui sont alignées sur le contexte des films.</p><p>En outre, il est possible de vectoriser ces nouvelles catégories et de les utiliser dans des recherches vectorielles. Cela signifie que les nouvelles catégories ne servent pas seulement de filtres, mais qu'elles peuvent également être utilisées pour calculer les similitudes sémantiques avec les termes de recherche, ce qui augmente encore la pertinence des résultats présentés.</p><p>Pipeline complet :</p>POST /_ingest/pipeline/_simulate
{
  "pipeline": {
    "processors": [
      {
        "script": {
          "source": """
            ctx.prompt = "You are an expert in semantic analysis and audiovisual content categorization. Your task is to generate only subcategories (max 2 topics) that describe specific aspects of movies based on their genres and descriptions. The output should be like string: 'n1, n2m ...n'. Here is a movies info to analyze: Title: " + ctx.title  + "Genres: " + ctx.genres  + "Description: " + ctx.description;
          """
        }
      },
      {
        "inference": {
          "model_id": "generate_topics_ia",
          "input_output": {
            "input_field": "prompt",
            "output_field": "result"
          }
        }
      },
      {
        "split": {
          "field": "result",
          "target_field": "topics",
          "separator": ", "
        }
      },
      {
        "remove": {
          "field": "result"
        }
      },
      {
        "remove": {
          "field": "prompt"
        }
      }
    ]
  },
  "docs": [
    {
      "_index": "index",
      "_id": "1",
      "_source": {
        "title": "The Lord of the Rings: The Fellowship of the Ring",
        "description": "While Frodo and Sam edge closer to Mordor with the help of the shifty Gollum, the divided fellowship makes a stand against Sauron's new ally, Saruman, and his hordes of Isengard.",
        "genres": [
          "Action",
          "Adventure",
          "Drama"
        ]
      }
    },
    {
      "_index": "index",
      "_id": "2",
      "_source": {
        "title": "Interstellar",
        "description": "A team of explorers travel through a wormhole in space in an attempt to ensure humanity's survival.",
        "genres": [
          "Adventure", "Drama", "Sci-Fi"
        ]
      }
    },
    {
      "_index": "index",
      "_id": "3",
      "_source": {
        "title": "The Martian",
        "description": "An astronaut becomes stranded on Mars after his team assume him dead, and must rely on his ingenuity to find a way to signal to Earth that he is alive.",
        "genres": [
          "Adventure", "Drama", "Sci-Fi"
        ]
      }
    }
  ]
}<h2>Conclusion</h2><p>L'utilisation de l'IA pour améliorer les facettes peut transformer l'expérience de recherche en rendant les résultats plus spécifiques et contextuels. Contrairement aux catégories fixes, qui sont souvent larges, les catégories générées par l'IA peuvent mieux refléter le contexte. Par exemple, lors de la reclassification des films, nous pouvons saisir le contexte qui échappe aux catégories primaires, ce qui permet d'obtenir des regroupements beaucoup plus pertinents.</p><p>Ces nouvelles catégories peuvent être ajoutées à l'index non seulement pour améliorer les facettes, mais aussi pour permettre des recherches vectorielles. Il en résulte une expérience de recherche plus efficace, avec des filtres mieux adaptés au contexte.</p><h2>Références</h2><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/infer-service-openai.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/infer-service-openai.html</a></p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/simulate-pipeline-api.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/simulate-pipeline-api.html</a></p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/script-processor.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/script-processor.html</a></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/faceted-search-examples-ai</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/faceted-search-examples-ai</guid>
    <category><![CDATA[IA]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd2838185214162c8/6a17f8bedbb4ff04affb58a5/25c9f9baa2326b5189ce0b1cc6240475781c755d-721x421.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 28 Jan 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Comment utiliser le connecteur Elasticsearch Vector Store pour Microsoft Semantic Kernel pour le développement d'agents d'intelligence artificielle ?]]></title>
    <description><![CDATA[Microsoft Semantic Kernel est un kit de développement léger et open-source qui vous permet de créer facilement des agents d'intelligence artificielle et d'intégrer les derniers modèles d'intelligence artificielle dans votre base de code C#, Python ou Java. Avec la sortie du connecteur Semantic Kernel Elasticsearch Vector Store, les développeurs qui utilisent Semantic Kernel pour créer des agents d'intelligence artificielle peuvent désormais utiliser Elasticsearch comme un magasin vectoriel d'entreprise évolutif tout en continuant à utiliser les abstractions de Semantic Kernel.]]></description>
    <content:encoded><![CDATA[<p>En collaboration avec l'équipe <a href="https://learn.microsoft.com/en-us/semantic-kernel/overview/">de Microsoft Semantic Kernel,</a> nous annonçons la disponibilité du <a href="https://github.com/elastic/semantic-kernel-net/">connecteur Semantic Kernel Elasticsearch Vector</a> Store, pour les utilisateurs <a href="https://learn.microsoft.com/en-us/semantic-kernel/overview/">de Microsoft Semantic Kernel (.NET).</a> Semantic Kernel simplifie la création d'agents d'intelligence artificielle de niveau professionnel, notamment en permettant d'améliorer les grands modèles de langage (LLM) grâce à des réponses plus pertinentes et fondées sur des données provenant d'un magasin de vecteurs. Semantic Kernel fournit une couche d'abstraction transparente pour interagir avec les magasins vectoriels tels qu'Elasticsearch, offrant des fonctionnalités essentielles telles que la création, l'énumération et la suppression de collections d'enregistrements et le téléchargement, l'extraction et la suppression d'enregistrements individuels.</p><p>Le <a href="https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors/out-of-the-box-connectors/elasticsearch-connector?pivots=programming-language-csharp">connecteur Semantic Kernel Elasticsearch Vector Store, prêt à l'emploi, prend</a> en charge les <a href="https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors/?pivots=programming-language-csharp#the-vector-store-abstraction">abstractions du Semantic Kernel</a> vector store, ce qui permet aux développeurs de brancher très facilement Elasticsearch en tant que magasin vectoriel lors de la création d'agents d'intelligence artificielle.</p><p>Elasticsearch est solidement ancré dans la communauté des logiciels libres et a récemment adopté la <a href="https://www.elastic.co/blog/elasticsearch-is-open-source-again">licence AGPL</a>. Associés au noyau sémantique Microsoft open-source, ces outils offrent une solution puissante, prête pour l'entreprise. Vous pouvez commencer localement en démarrant Elasticsearch en quelques minutes en exécutant cette commande <code>curl -fsSL https://elastic.co/start-local | sh </code>(voir <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/run-elasticsearch-locally.html">start-local</a> pour plus de détails) et évoluer vers des versions <a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;utm_source=semantickernel&amp;utm_content=documentation">hébergées dans le nuage</a> ou <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.16/install-elasticsearch.html">auto-hébergées</a> tout en mettant en production vos agents d'IA.</p><p>Dans ce blog, nous verrons comment utiliser le <a href="https://github.com/elastic/semantic-kernel-net/">connecteur Semantic Kernel Elasticsearch Vector Store</a> lors de l'utilisation de Semantic Kernel. Une version Python du connecteur sera disponible à l'avenir.</p><h2>Scénario de haut niveau : Construire une application RAG avec Semantic Kernel &amp; Elasticsearch</h2><p>Dans la section suivante, nous présentons un exemple. À un niveau élevé, nous construisons une application RAG (Retrieval Augmented Generation) qui prend la question d'un utilisateur en entrée et renvoie une réponse. Nous utiliserons Azure OpenAI<a href="https://devblogs.microsoft.com/semantic-kernel/introducing-new-ollama-connector-for-local-models/">(un LLM local</a> peut également être utilisé) comme LLM, Elasticsearch comme magasin de vecteurs et Semantic Kernel (.net) comme cadre pour relier tous les composants ensemble.</p><p>Si vous n'êtes pas familier avec les architectures RAG, vous pouvez vous familiariser rapidement avec cet article <a href="https://www.elastic.co/search-labs/blog/retrieval-augmented-generation-rag">: https://www.elastic.co/search-labs/blog/retrieval-augmented-generation-rag</a>.</p><p>La réponse est générée par le LLM qui est alimenté par le contexte, pertinent pour la question, récupéré à partir du vectorstore Elasticsearch. La réponse inclut également la source qui a été utilisée comme contexte par le LLM.</p><h3>Exemple de GCR</h3><p>Dans cet exemple spécifique, nous créons une application qui permet aux utilisateurs de poser des questions sur les hôtels stockés dans une base de données interne. L'utilisateur peut par exemple rechercher un hôtel spécifique, en fonction de différents critères, ou demander une liste d'hôtels.</p><p>Pour la base de données d'exemple, nous avons généré une <a href="https://github.com/elastic/semantic-kernel-net/blob/main/Elastic.SemanticKernel.Playground/hotels.csv">liste d'hôtels</a> contenant 100 entrées. La taille de l'échantillon est volontairement réduite pour vous permettre d'essayer la démo du connecteur aussi facilement que possible. Dans une application réelle, le connecteur Elasticsearch montrerait ses avantages par rapport à d'autres options, telles que l'implémentation du magasin vectoriel `InMemory`, en particulier lorsque l'on travaille avec de très grandes quantités de données.</p><p>L'application de démonstration complète se trouve dans le <a href="https://github.com/elastic/semantic-kernel-net/tree/main/Elastic.SemanticKernel.Playground">référentiel du</a> connecteur Elasticsearch vector store.</p><p>Commençons par ajouter les paquets NuGet et les directives nécessaires à notre projet :</p>dotnet add package "Elastic.Clients.Elasticsearch" -v 8.16.2
dotnet add package "Elastic.SemanticKernel.Connectors.Elasticsearch" -v 0.1.2
dotnet add package "Microsoft.Extensions.Hosting" -v 9.0.0
dotnet add package "Microsoft.SemanticKernel.Connectors.AzureOpenAI" -v 1.30.0
dotnet add package "Microsoft.SemanticKernel.PromptTemplates.Handlebars" -v 1.30.0using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

using Elastic.Clients.Elasticsearch;
using Elastic.Transport;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.Embeddings;
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;<p>Nous pouvons maintenant créer notre modèle de données et le doter d'attributs spécifiques au Semantic Kernel pour définir le schéma du modèle de stockage et quelques indications pour la recherche textuelle :</p>/// &lt;summary&gt;
/// Data model for storing a "hotel" with a name, a description, a  description embedding and an optional reference link.
/// &lt;/summary&gt;
public sealed record Hotel
{
	[VectorStoreRecordKey]
	public required string HotelId { get; set; }

	[TextSearchResultName]
	[VectorStoreRecordData(IsFilterable = true)]
	public required string HotelName { get; set; }

	[TextSearchResultValue]
	[VectorStoreRecordData(IsFullTextSearchable = true)]
	public required string Description { get; set; }

	[VectorStoreRecordVector(Dimensions: 1536, DistanceFunction.CosineSimilarity, IndexKind.Hnsw)]
	public ReadOnlyMemory&lt;float&gt;? DescriptionEmbedding { get; set; }

	[TextSearchResultLink]
	[VectorStoreRecordData]
	public string? ReferenceLink { get; set; }
}<p>Les attributs du schéma du modèle de stockage (`VectorStore*`) sont les plus pertinents pour l'utilisation réelle du connecteur Elasticsearch Vector Store, à savoir :</p><p></p><ul><li><p><code>VectorStoreRecordKey</code> pour marquer une propriété d'une classe d'enregistrement comme étant la clé sous laquelle l'enregistrement est stocké dans un magasin vectoriel.</p></li><li><p><code>VectorStoreRecordData</code> pour marquer une propriété d'une classe d'enregistrement comme "data".</p></li><li><p><code>VectorStoreRecordVector</code> pour marquer une propriété d'une classe d'enregistrement en tant que vecteur.</p></li></ul><p>Tous ces attributs acceptent divers paramètres facultatifs qui peuvent être utilisés pour personnaliser davantage le modèle de stockage. Dans le cas de <code>VectorStoreRecordKey </code>, par exemple, il est possible de spécifier une fonction de distance différente ou un type d'indice différent.</p><p>Les attributs de recherche de texte (<code>TextSearch*</code>) seront importants dans la dernière étape de cet exemple. Nous y reviendrons plus tard.</p><p>Dans l'étape suivante, nous initialisons le moteur du noyau sémantique et obtenons des références aux services de base. Dans une application réelle, l' <a href="https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection">injection de dépendances</a> devrait être utilisée au lieu d'accéder directement à la collection de services. La même chose s'applique à la configuration et aux secrets codés en dur, qui doivent être lus à l'aide d'un <a href="https://learn.microsoft.com/en-us/dotnet/core/extensions/configuration">fournisseur de configuration</a>:</p>var builder = Host.CreateApplicationBuilder(args);

// Register AI services.
var kernelBuilder = builder.Services.AddKernel();

kernelBuilder.AddAzureOpenAIChatCompletion("gpt-4o", "https://my-service.openai.azure.com", "my_token");

kernelBuilder.AddAzureOpenAITextEmbeddingGeneration("ada-002", "https://my-service.openai.azure.com", "my_token");

// Register text search service.
kernelBuilder.AddVectorStoreTextSearch&lt;Hotel&gt;();

// Register Elasticsearch vector store.
var elasticsearchClientSettings = new ElasticsearchClientSettings(new Uri("https://my-elasticsearch-instance.cloud"))
    .Authentication(new BasicAuthentication("elastic", "my_password"));

kernelBuilder.AddElasticsearchVectorStoreRecordCollection&lt;string, Hotel&gt;("skhotels", elasticsearchClientSettings);

// Build the host.
using var host = builder.Build();

// For demo purposes, we access the services directly without using a DI context.

var kernel = host.Services.GetService&lt;Kernel&gt;()!;
var embeddings = host.Services.GetService&lt;ITextEmbeddingGenerationService&gt;()!;
var vectorStoreCollection = host.Services.GetService&lt;IVectorStoreRecordCollection&lt;string, Hotel&gt;&gt;()!;

// Register search plugin.
var textSearch = host.Services.GetService&lt;VectorStoreTextSearch&lt;Hotel&gt;&gt;()!;
kernel.Plugins.Add(textSearch.CreateWithGetTextSearchResults("SearchPlugin"));<p>Le service <code>vectorStoreCollection</code> peut maintenant être utilisé pour créer la collection et ingérer quelques <a href="https://github.com/elastic/semantic-kernel-net/blob/main/Elastic.SemanticKernel.Playground/hotels.csv">enregistrements de démonstration :</a></p>await vectorStoreCollection.CreateCollectionIfNotExistsAsync();

// CSV format: ID;Hotel Name;Description;Reference Link
var hotels = (await File.ReadAllLinesAsync("hotels.csv"))
    .Select(x =&gt; x.Split(';'));

foreach (var chunk in hotels.Chunk(25))
{
    var descriptionEmbeddings = await embeddings.GenerateEmbeddingsAsync(chunk.Select(x =&gt; x[2]).ToArray());
    
    for (var i = 0; i &lt; chunk.Length; ++i)
    {
        var hotel = chunk[i];
        await vectorStoreCollection.UpsertAsync(new Hotel
        {
            HotelId = hotel[0],
            HotelName = hotel[1],
            Description = hotel[2],
            DescriptionEmbedding = descriptionEmbeddings[i],
            ReferenceLink = hotel[3]
        });
    }
}<p>Cela montre comment Semantic Kernel réduit l'utilisation d'un magasin de vecteurs, avec toute sa complexité, à quelques simples appels de méthodes.</p><p>Sous le capot, un nouvel index est créé dans Elasticsearch et tous les mappages de propriétés nécessaires sont créés. Notre ensemble de données est ensuite intégré de manière totalement transparente dans le modèle de stockage et finalement stocké dans l'index. Voici à quoi ressemblent les mappings dans Elasticsearch.</p>{
  "mappings": {
    "properties": {
      "descriptionEmbedding": {
        "dims": 1536,
        "index": true,
        "index_options": {
          "type": "hnsw"
        },
        "similarity": "cosine",
        "type": "dense_vector"
      },
      "hotelName": {
        "type": "keyword"
      },
      "description": {
        "type": "text"
      }
    }
  }
}<p>Le site <code>embeddings.GenerateEmbeddingsAsync()</code> appelle de manière transparente le service Azure AI Embeddings Generation configuré.</p><p>La dernière étape de cette démonstration est encore plus magique.</p><p>Avec un seul appel à <code>InvokePromptAsync</code>, toutes les opérations suivantes sont effectuées lorsque l'utilisateur pose une question sur les données :</p><p>1. L'intégration de la question de l'utilisateur est générée.</p><p>2. Le magasin de vecteurs est parcouru à la recherche d'entrées pertinentes</p><p>3. Les résultats de la requête sont insérés dans un modèle d'invite</p><p>4. La requête proprement dite, sous la forme de l'invite finale, est envoyée au service d'achèvement de chat de l'IA.</p>// Invoke the LLM with a template that uses the search plugin to
// 1. get related information to the user query from the vector store
// 2. add the information to the LLM prompt.
var response = await kernel.InvokePromptAsync(
    promptTemplate: """
                    Please use this information to answer the question:
                    {{#with (SearchPlugin-GetTextSearchResults question)}}
                      {{#each this}}
                        Name: {{Name}}
                        Value: {{Value}}
                        Source: {{Link}}
                        -----------------
                      {{/each}}
                    {{/with}}
                    
                    Include the source of relevant information in the response.

                    Question: {{question}}
                    """,
    arguments: new KernelArguments
    {
        { "question", "Please show me all hotels that have a rooftop bar." },
    },
    templateFormat: "handlebars",
    promptTemplateFactory: new HandlebarsPromptTemplateFactory());<p>Vous vous souvenez des attributs <code>TextSearch*</code> que nous avons définis précédemment dans notre modèle de données ? Ces attributs nous permettent d'utiliser les espaces réservés correspondants dans notre modèle d'invite, qui sont automatiquement remplis avec les informations provenant de nos entrées dans le magasin de vecteurs.</p><p>La réponse finale à notre question "Veuillez m'indiquer tous les hôtels qui disposent d'un bar sur le toit." est la suivante :</p>Console.WriteLine(response.ToString());

// &gt; The hotel that has a rooftop bar is Skyline Suites. You can find more information about this hotel [here](https://example.com/yz567).<p>La réponse se réfère correctement à l'entrée suivante dans notre fichier hotels.csv</p>9;
Skyline Suites;
Offering panoramic city views from every suite, this hotel is perfect for those who love the urban landscape. Enjoy luxurious amenities, a rooftop bar, and close proximity to attractions. Luxurious and contemporary.;
https://example.com/yz567<p>Cet exemple montre très bien comment l'utilisation de Microsoft Semantic Kernel permet une réduction significative de la complexité grâce à ses abstractions bien pensées, tout en permettant un très haut niveau de flexibilité. En changeant une seule ligne de code, par exemple, le magasin de vecteurs ou les services d'intelligence artificielle utilisés peuvent être remplacés sans qu'il soit nécessaire de remanier une autre partie du code.</p><p>Dans le même temps, le cadre fournit un ensemble considérable de fonctionnalités de haut niveau, telles que la fonction `InvokePrompt`, ou le système de plugin de modèle ou de recherche.</p><p>L'application de démonstration complète se trouve dans le référentiel du connecteur Elasticsearch vector store.</p><h2>Quelles sont les autres possibilités offertes par Elasticsearch ?</h2><ul><li><p><a href="https://www.elastic.co/search-labs/blog/semantic-search-simplified-semantic-text">Nouveau mappage semantic_text d'Elasticsearch : Simplifier la recherche sémantique</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/semantic-reranking-with-retrievers">Classement sémantique dans Elasticsearch à l'aide d'extracteurs</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1">Techniques avancées de RAG partie 1 : Traitement des données</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2">Techniques avancées de RAG, partie 2 : Requêtes et tests</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/elasticsearch-rag-with-llama3-opensource-and-elastic">Construire RAG avec Llama 3 open-source et Elastic</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/local-rag-agent-elasticsearch-langgraph-llama3">Un tutoriel sur la construction d'un agent local utilisant LangGraph, LLaMA3 et Elasticsearch vector store à partir de zéro</a></p></li></ul><h2>Elasticsearch &amp; Semantic Kernel : Quelle est la prochaine étape ?</h2><ul><li><p>Nous avons montré comment le magasin vectoriel Elasticsearch peut être facilement intégré au Semantic Kernel lors de la construction d'applications GenAI en .NET. Restez à l'écoute pour une prochaine intégration de Python.</p></li><li><p>Comme Semantic Kernel construit des abstractions pour des fonctions de recherche avancées telles que la <a href="https://www.elastic.co/search-labs/tutorials/search-tutorial/vector-search/hybrid-search">recherche hybride</a>, la connexion Elasticsearch permettra aux développeurs .NET de les mettre en œuvre facilement tout en utilisant Semantic Kernel.</p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-connector-microsoft-semantic-kernel</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-connector-microsoft-semantic-kernel</guid>
    <category><![CDATA[IA]]></category>
    <category><![CDATA[.NET]]></category>
    <category><![CDATA[Base vectorielle]]></category>
    <dc:creator><![CDATA[Florian Bernd,Srikanth Manvi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2d8725035e86f8a8/6a17fe447f6f1564f8c09d74/0564fe794e4c66d0507317822d7aa71826183d20-1311x762.jpg" length="0" type="image/jpeg"/>
    <pubDate>Fri, 06 Dec 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[LangChain4j avec Elasticsearch comme magasin de plongements]]></title>
    <description><![CDATA[LangChain4j (LangChain pour Java) a Elasticsearch comme magasin intégré. Découvrez comment l'utiliser pour construire votre application RAG en Java simple.]]></description>
    <content:encoded><![CDATA[<p>
Dans l'<a href="https://www.elastic.co/search-labs/blog/langchain4j-llm-integration-introduction">article précédent</a>, nous avons découvert ce qu'est LangChain4j et comment l'utiliser :</p><ul><li><p>Discutez avec les LLM en mettant en place un <code>ChatLanguageModel</code> et un <code>ChatMemory</code></p></li><li><p>Conserver l'historique du chat en mémoire pour se rappeler le contexte d'une discussion précédente avec un LLM</p></li></ul><p>Cet article de blog traite de la manière de procéder :</p><ul><li><p>Création d'encastrements vectoriels à partir d'exemples de textes</p></li><li><p>Stocker les embeddings vectoriels dans le magasin d'embedding d'Elasticsearch </p></li><li><p>Recherche de vecteurs similaires</p></li></ul><h2>Créer des embeddings</h2><p>Pour créer des embeddings, nous devons définir un <code>EmbeddingModel</code> à utiliser. Par exemple, nous pouvons utiliser le même modèle de mistral que celui utilisé dans le <a href="https://www.elastic.co/search-labs/blog/langchain4j-llm-integration-introduction">billet précédent.</a> Il s'agissait de courir avec l'ollama :</p>EmbeddingModel model = OllamaEmbeddingModel.builder()
  .baseUrl(ollama.getEndpoint())
  .modelName(MODEL_NAME)
  .build();<p>Un modèle est capable de générer des vecteurs à partir d'un texte. Nous pouvons ici vérifier le nombre de dimensions générées par le modèle :</p>Logger.info("Embedding model has {} dimensions.", model.dimension());
// This gives: Embedding model has 4096 dimensions.<p>Pour générer des vecteurs à partir d'un texte, nous pouvons utiliser :</p>Response&lt;Embedding&gt; response = model.embed("A text here");<p>Si nous voulons également fournir des métadonnées pour nous permettre de filtrer des éléments tels que le texte, le prix, la date de sortie ou autre, nous pouvons utiliser <code>Metadata.from()</code>. Par exemple, nous ajoutons ici le nom du jeu comme champ de métadonnées :</p>TextSegment game1 = TextSegment.from("""
    The game starts off with the main character Guybrush Threepwood stating "I want to be a pirate!"
    To do so, he must prove himself to three old pirate captains. During the perilous pirate trials, 
    he meets the beautiful governor Elaine Marley, with whom he falls in love, unaware that the ghost pirate 
    LeChuck also has his eyes on her. When Elaine is kidnapped, Guybrush procures crew and ship to track 
    LeChuck down, defeat him and rescue his love.
""", Metadata.from("gameName", "The Secret of Monkey Island"));
Response&lt;Embedding&gt; response1 = model.embed(game1);
TextSegment game2 = TextSegment.from("""
    Out Run is a pseudo-3D driving video game in which the player controls a Ferrari Testarossa 
    convertible from a third-person rear perspective. The camera is placed near the ground, simulating 
    a Ferrari driver's position and limiting the player's view into the distance. The road curves, 
    crests, and dips, which increases the challenge by obscuring upcoming obstacles such as traffic 
    that the player must avoid. The object of the game is to reach the finish line against a timer.
    The game world is divided into multiple stages that each end in a checkpoint, and reaching the end 
    of a stage provides more time. Near the end of each stage, the track forks to give the player a 
    choice of routes leading to five final destinations. The destinations represent different 
    difficulty levels and each conclude with their own ending scene, among them the Ferrari breaking 
    down or being presented a trophy.
""", Metadata.from("gameName", "Out Run"));
Response&lt;Embedding&gt; response2 = model.embed(game2);<p>Si vous souhaitez exécuter ce code, veuillez consulter la classe <a href="https://github.com/dadoonet/langchain4j-demo/blob/main/src/test/java/fr/pilato/demo/Step5EmbedddingsTest.java">Step5EmbedddingsTest.java</a>.</p><h2>Ajouter Elasticsearch pour stocker nos vecteurs</h2><p>LangChain4j fournit un magasin d'intégration en mémoire. Cette fonction est utile pour exécuter des tests simples :</p>EmbeddingStore&lt;TextSegment&gt; embeddingStore = new InMemoryEmbeddingStore&lt;&gt;();
embeddingStore.add(response1.content(), game1);
embeddingStore.add(response2.content(), game2);<p>Mais il est évident que cela ne pourrait pas fonctionner avec un ensemble de données beaucoup plus important car ce datastore stocke tout en mémoire et nous ne disposons pas d'une mémoire infinie sur nos serveurs. Ainsi, nous pourrions plutôt stocker nos enregistrements dans Elasticsearch, qui est par définition "élastique" et peut évoluer avec vos données. Pour cela, ajoutons Elasticsearch à notre projet :</p>&lt;dependency&gt;
  &lt;groupId&gt;dev.langchain4j&lt;/groupId&gt;
  &lt;artifactId&gt;langchain4j-elasticsearch&lt;/artifactId&gt;
  &lt;version&gt;${langchain4j.version}&lt;/version&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
  &lt;groupId&gt;org.testcontainers&lt;/groupId&gt;
  &lt;artifactId&gt;elasticsearch&lt;/artifactId&gt;
  &lt;version&gt;1.20.1&lt;/version&gt;
  &lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;<p>Comme vous l'avez remarqué, nous avons également ajouté le module Elasticsearch TestContainers au projet, afin de pouvoir démarrer une instance Elasticsearch à partir de nos tests :</p>// Create the elasticsearch container
ElasticsearchContainer container =
  new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.15.0")
    .withPassword("changeme");

// Start the container. This step might take some time...
container.start();

// As we don't want to make our TestContainers code more complex than
// needed, we will use login / password for authentication.
// But note that you can also use API keys which is preferred.
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("elastic", "changeme"));

// Create a low level Rest client which connects to the elasticsearch container.
client = RestClient.builder(HttpHost.create("https://" + container.getHttpHostAddress()))
  .setHttpClientConfigCallback(httpClientBuilder -&gt; {
    httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
    httpClientBuilder.setSSLContext(container.createSslContextFromCa());
    return httpClientBuilder;
  })
  .build();

// Check the cluster is running
client.performRequest(new Request("GET", "/"));<p>Pour utiliser Elasticsearch comme magasin d'intégration, il suffit de "" passer du datastore LangChain4j en mémoire au datastore Elasticsearch :</p>EmbeddingStore&lt;TextSegment&gt; embeddingStore =
  ElasticsearchEmbeddingStore.builder()
    .restClient(client)
    .build();
embeddingStore.add(response1.content(), game1);
embeddingStore.add(response2.content(), game2);<p>Cela permettra de stocker vos vecteurs dans Elasticsearch dans un index <code>default</code>. Vous pouvez également changer le nom de l'index en quelque chose de plus significatif :</p>EmbeddingStore&lt;TextSegment&gt; embeddingStore =
  ElasticsearchEmbeddingStore.builder()
    .indexName("games")
    .restClient(client)
    .build();
embeddingStore.add(response1.content(), game1);
embeddingStore.add(response2.content(), game2);<p>Si vous souhaitez exécuter ce code, veuillez consulter la classe <a href="https://github.com/dadoonet/langchain4j-demo/blob/main/src/test/java/fr/pilato/demo/Step6ElasticsearchEmbedddingsTest.java">Step6ElasticsearchEmbedddingsTest.java</a>.</p><h2>Recherche de vecteurs similaires</h2><p>Pour rechercher des vecteurs similaires, nous devons d'abord transformer notre question en une représentation vectorielle en utilisant le même modèle que celui utilisé précédemment. Nous l'avons déjà fait, il n'est donc pas difficile de le faire à nouveau. Notez que nous n'avons pas besoin des métadonnées dans ce cas :</p>String question = "I want to pilot a car";
Embedding questionAsVector = model.embed(question).content();<p>Nous pouvons construire une requête de recherche avec cette représentation de notre question et demander au magasin d'intégration de trouver les premiers vecteurs :</p>EmbeddingSearchResult&lt;TextSegment&gt; result = embeddingStore.search(
  EmbeddingSearchRequest.builder()
    .queryEmbedding(questionAsVector)
    .build());<p>Nous pouvons maintenant itérer sur les résultats et imprimer certaines informations, comme le nom du jeu qui provient des métadonnées et le score :</p>result.matches().forEach(m -&gt; Logger.info("{} - score [{}]",
  m.embedded().metadata().getString("gameName"), m.score()));<p>Comme on pouvait s'y attendre, cela nous donne "Out Run" comme premier résultat :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7ca0dcfdb1a9c94f/6a170291cf4f256938b2d017/140b6a962e5edbb4870419250e30bfb815b0d73e-640x480.gif" alt="Sortie de route" />Out Run - score [0.86672974]
The Secret of Monkey Island - score [0.85569763]<p>Si vous souhaitez exécuter ce code, consultez la classe <a href="https://github.com/dadoonet/langchain4j-demo/blob/9ec4b1d4c7c69821f143ddf272bbfed273c67b14/src/test/java/fr/pilato/demo/Step7SearchForVectorsTest.java#L110-L129">Step7SearchForVectorsTest.java</a>. </p><h2>L'envers du décor</h2><p>La configuration par défaut du magasin Elasticsearch Embedding utilise la <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.15/query-dsl-knn-query.html">requête kNN approximative</a> en arrière-plan.</p>POST games/_search
{
  "query" : {
    "knn": {
      "field": "vector",
      "query_vector": [-0.019137882, /* ... */, -0.0148779955]
    }
  }
}<p>Mais cela peut être modifié en fournissant une autre configuration (<code>ElasticsearchConfigurationScript</code>) que celle par défaut (<code>ElasticsearchConfigurationKnn</code>) au magasin d'intégration :</p>EmbeddingStore&lt;TextSegment&gt; embeddingStore =
  ElasticsearchEmbeddingStore.builder()
    .configuration(ElasticsearchConfigurationScript.builder().build())
    .indexName("games")
    .restClient(client)
    .build();<p>L'implémentation de <code>ElasticsearchConfigurationScript</code> exécute en coulisse une <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.15/query-dsl-script-score-query.html">requête</a> <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.15/query-dsl-script-score-query.html"><code>script_score</code></a> à l'aide d'une <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.15/query-dsl-script-score-query.html#vector-functions-cosine">fonction</a> <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.15/query-dsl-script-score-query.html#vector-functions-cosine"><code>cosineSimilarity</code></a>.</p><p>En principe, lors d'un appel :</p>EmbeddingSearchResult&lt;TextSegment&gt; result = embeddingStore.search(
  EmbeddingSearchRequest.builder()
    .queryEmbedding(questionAsVector)
    .build());<p>Il s'agit maintenant d'un appel :</p>POST games/_search
{
  "query": {
    "script_score": {
      "script": {
        "source": "(cosineSimilarity(params.query_vector, 'vector') + 1.0) / 2",
        "params": {
          "queryVector": [-0.019137882, /* ... */, -0.0148779955]
        }
      }
    }
  }
}<p>Dans ce cas, le résultat ne change pas en termes d'ordre "" mais le score est simplement ajusté parce que l'appel <code>cosineSimilarity</code> n'utilise pas d'approximation mais calcule le cosinus pour chacun des vecteurs correspondants :</p>Out Run - score [0.871952]
The Secret of Monkey Island - score [0.86380446]<p>Si vous souhaitez exécuter ce code, consultez la classe <a href="https://github.com/dadoonet/langchain4j-demo/blob/9ec4b1d4c7c69821f143ddf272bbfed273c67b14/src/test/java/fr/pilato/demo/Step7SearchForVectorsTest.java#L132-L155">Step7SearchForVectorsTest.java</a>.</p><h2>Conclusion</h2><p>Nous avons vu comment vous pouvez facilement générer des embeddings à partir de votre texte et comment vous pouvez stocker et rechercher les voisins les plus proches dans Elasticsearch en utilisant deux approches différentes :</p><ul><li><p>Utilisation de la requête approximative et rapide <code>knn</code> avec l'option par défaut <code>ElasticsearchConfigurationKnn</code></p></li><li><p>Utilisation de la requête exacte mais plus lente <code>script_score</code> avec l'option <code>ElasticsearchConfigurationScript</code></p></li></ul><p>La prochaine étape consistera à créer une application RAG complète, sur la base de ce que nous avons appris ici.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/langchain4j-elasticsearch-embedding-store</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/langchain4j-elasticsearch-embedding-store</guid>
    <category><![CDATA[Java]]></category>
    <category><![CDATA[IA]]></category>
    <category><![CDATA[Base vectorielle]]></category>
    <dc:creator><![CDATA[David Pilato]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfc873b86c76d1798/6a170293acf088f666be99b3/abd8a4a809064101c037af66b87f28e5ecde03b0-1474x645.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 08 Oct 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Présentation de LangChain4j pour simplifier l'intégration de LLM dans les applications Java]]></title>
    <description><![CDATA[LangChain4j (LangChain pour Java) est un ensemble d'outils puissants pour construire votre application RAG en Java simple.]]></description>
    <content:encoded><![CDATA[<p>Le <a href="https://docs.langchain4j.dev/">cadre LangChain4j</a> a été créé en 2023 avec <a href="https://github.com/langchain4j/langchain4j/blob/main/README.md#introduction">cet objectif</a>:</p>L'objectif de LangChain4j est de simplifier l'intégration des LLM dans les applications Java.<p>LangChain4j fournit une méthode standard pour :</p><ul><li><p>créer des embeddings (vecteurs) à partir d'un contenu donné, par exemple un texte</p></li><li><p>stocker les embeddings dans un magasin d'embeddings</p></li><li><p>recherche de vecteurs similaires dans le magasin d'intégration</p></li><li><p>discuter avec les LLM</p></li><li><p>utiliser une mémoire de chat pour se souvenir du contexte d'une discussion avec un LLM</p></li></ul><p>Cette liste n'est pas exhaustive et la communauté LangChain4j est toujours en train d'implémenter de nouvelles fonctionnalités.</p><p>Ce billet couvrira les premières parties principales du cadre.</p><h2>Ajouter LangChain4j OpenAI à notre projet</h2><p>Comme dans tous les projets Java, c'est juste une question de dépendances. Nous utiliserons ici Maven, mais la même chose pourrait être réalisée avec n'importe quel autre gestionnaire de dépendances.</p><p>Comme première étape du projet que nous voulons construire ici, nous utiliserons OpenAI, il nous suffit donc d'ajouter l'artefact <code>langchain4j-open-ai</code>:</p>&lt;properties&gt;
  &lt;langchain4j.version&gt;0.34.0&lt;/langchain4j.version&gt;
&lt;/properties&gt;

&lt;dependencies&gt;
  &lt;dependency&gt;
    &lt;groupId&gt;dev.langchain4j&lt;/groupId&gt;
    &lt;artifactId&gt;langchain4j-open-ai&lt;/artifactId&gt;
    &lt;version&gt;${langchain4j.version}&lt;/version&gt;
  &lt;/dependency&gt;
&lt;/dependencies&gt;
<p>Pour le reste du code, nous utiliserons soit notre propre clé API, que vous pouvez obtenir en créant un compte auprès d'<a href="https://platform.openai.com/signup/">OpenAI</a>, soit celle fournie par le projet LangChain4j à des fins de démonstration uniquement :</p>static String getOpenAiApiKey() {
  String apiKey = System.getenv(API_KEY_ENV_NAME);
  if (apiKey == null || apiKey.isEmpty()) {
    Logger.warn("Please provide your own key instead using [{}] env variable", API_KEY_ENV_NAME);
    return "demo";
  }
  return apiKey;
}
<p>Nous pouvons maintenant créer une instance de notre ChatLanguageModel :</p>ChatLanguageModel model = OpenAiChatModel.withApiKey(getOpenAiApiKey());
<p>Enfin, nous pouvons poser une question simple et obtenir une réponse :</p>String answer = model.generate("Who is Thomas Pesquet?");
Logger.info("Answer is: {}", answer);
<p>La réponse donnée pourrait être quelque chose comme :</p>Thomas Pesquet is a French aerospace engineer, pilot, and European Space Agency astronaut.
He was selected as a member of the European Astronaut Corps in 2009 and has since completed 
two space missions to the International Space Station, including serving as a flight engineer 
for Expedition 50/51 in 2016-2017. Pesquet is known for his contributions to scientific 
research and outreach activities during his time in space.
<p>Si vous souhaitez exécuter ce code, consultez la classe <a href="https://github.com/dadoonet/langchain4j-demo/blob/main/src/test/java/fr/pilato/demo/Step1AiChatTest.java">Step1AiChatTest.java</a>.</p><h2>Fournir plus de contexte avec langchain4j</h2><p>Ajoutons l'artefact <code>langchain4j</code>:</p>&lt;dependency&gt;
  &lt;groupId&gt;dev.langchain4j&lt;/groupId&gt;
  &lt;artifactId&gt;langchain4j&lt;/artifactId&gt;
  &lt;version&gt;${langchain4j.version}&lt;/version&gt;
&lt;/dependency&gt;
<p>Celui-ci fournit un ensemble d'outils qui peuvent nous aider à construire une intégration LLM plus avancée pour construire notre assistant. Ici, nous allons simplement créer une interface <code>Assistant</code> qui fournit la méthode <code>chat</code> qui appellera automatiquement la méthode <code>ChatLanguageModel</code> que nous avons définie plus tôt :</p>interface Assistant {
  String chat(String userMessage);
}
<p>Il suffit de demander à la classe LangChain4j <code>AiServices</code> de construire une instance pour nous :</p>Assistant assistant = AiServices.create(Assistant.class, model);
<p>Puis appeler la méthode <code>chat(String)</code>:</p>String answer = assistant.chat("Who is Thomas Pesquet?");
Logger.info("Answer is: {}", answer);
<p>Le comportement est le même qu'auparavant. Pourquoi avons-nous donc modifié le code ? Tout d'abord, c'est plus élégant, mais en plus, vous pouvez maintenant donner des instructions au LLM à l'aide de simples annotations :</p>interface Assistant {
  @SystemMessage("Please answer in a funny way.")
  String chat(String userMessage);
}
<p>C'est maintenant chose faite :</p>Ah, Thomas Pesquet is actually a super secret spy disguised as an astronaut! 
He's out there in space fighting aliens and saving the world one spacewalk at a time. 
Or maybe he's just a really cool French astronaut who has been to the International 
Space Station. But my spy theory is much more exciting, don't you think?
<p>Si vous souhaitez exécuter ce code, consultez la classe <a href="https://github.com/dadoonet/langchain4j-demo/blob/main/src/test/java/fr/pilato/demo/Step2AssistantTest.java">Step2AssistantTest.java</a>.</p><h2>Passer à un autre LLM : langchain4j-ollama</h2><p>Nous pouvons utiliser le grand <a href="https://ollama.com/">projet Ollama</a>. Il est utile d'exécuter un LLM localement sur votre machine.</p><p>Ajoutons l'artefact <code>langchain4j-ollama</code>:</p>&lt;dependency&gt;
  &lt;groupId&gt;dev.langchain4j&lt;/groupId&gt;
  &lt;artifactId&gt;langchain4j-ollama&lt;/artifactId&gt;
  &lt;version&gt;${langchain4j.version}&lt;/version&gt;
&lt;/dependency&gt;
<p>Comme nous exécutons l'exemple de code à l'aide de tests, ajoutons des <a href="https://java.testcontainers.org/">conteneurs de test</a> à notre projet :</p>&lt;dependency&gt;
  &lt;groupId&gt;org.testcontainers&lt;/groupId&gt;
  &lt;artifactId&gt;ollama&lt;/artifactId&gt;
  &lt;version&gt;1.20.1&lt;/version&gt;
  &lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
<p>Nous pouvons maintenant démarrer/arrêter les conteneurs Docker :</p>static String MODEL_NAME = "mistral";
static String DOCKER_IMAGE_NAME = "langchain4j/ollama-" + MODEL_NAME + ":latest";

static OllamaContainer ollama = new OllamaContainer(
  DockerImageName.parse(DOCKER_IMAGE_NAME).asCompatibleSubstituteFor("ollama/ollama"));

@BeforeAll
public static void setup() {
  ollama.start();
}

@AfterAll
public static void teardown() {
  ollama.stop();
}
<p>Nous "devons simplement" modifier l'objet <code>model</code> pour qu'il devienne un <code>OllamaChatModel</code> au lieu du <code>OpenAiChatModel</code> que nous utilisions précédemment :</p>OllamaChatModel model = OllamaChatModel.builder()
  .baseUrl(ollama.getEndpoint())
  .modelName(MODEL_NAME)
  .build();
<p>Notez que l'extraction de l'image avec son modèle peut prendre un certain temps, mais au bout d'un moment, vous pouvez obtenir la réponse :</p>Oh, Thomas Pesquet, the man who single-handedly keeps the French space program running 
while sipping on his crisp rosé and munching on a baguette! He's our beloved astronaut 
with an irresistible accent that makes us all want to learn French just so we can 
understand him better. When he's not floating in space, he's probably practicing his 
best "je ne sais quoi" face for the next family photo. Vive le Thomas Pesquet! 
🚀🌍🇫🇷 #FrenchSpaceHero
<h2>Meilleure mémoire</h2><p>Si nous posons plusieurs questions, par défaut le système ne se souviendra pas des questions et réponses précédentes. Ainsi, si nous posons la question suivante : "Quand est-il né ?", notre application répondra :</p>Oh, you're asking about this legendary figure from history, huh? Well, let me tell 
you a hilarious tale! He was actually born on Leap Year's Day, but only every 400 
years! So, do the math... if we count backwards from 2020 (which is also a leap year), 
then he was born in... *drumroll please* ...1600! Isn't that a hoot? But remember 
folks, this is just a joke, and historical records may vary.
<p>Ce qui est absurde. Nous devrions plutôt utiliser la <a href="https://docs.langchain4j.dev/tutorials/chat-memory">mémoire de chat :</a></p>ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
Assistant assistant = AiServices.builder(Assistant.class)
  .chatLanguageModel(model)
  .chatMemory(chatMemory)
  .build();
<p>En posant les mêmes questions maintenant, on obtient une réponse significative :</p>Oh, Thomas Pesquet, the man who was probably born before sliced bread but after dinosaurs! 
You know, around the time when people started putting wheels on suitcases and calling it 
a revolution. So, roughly speaking, he came into this world somewhere in the late 70s or 
early 80s, give or take a year or two - just enough time for him to grow up, become an 
astronaut, and make us all laugh with his space-aged antics! Isn't that a hoot? 
*laughs maniacally*
<h2>Conclusion</h2><p>Dans <a href="https://www.elastic.co/search-labs/blog/langchain4j-elasticsearch-embedding-store">le prochain article</a>, nous découvrirons comment poser des questions à notre ensemble de données privé en utilisant Elasticsearch comme magasin d'intégration. Cela nous permettra de passer à la vitesse supérieure en matière de recherche de candidatures.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/langchain4j-llm-integration-introduction</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/langchain4j-llm-integration-introduction</guid>
    <category><![CDATA[Java]]></category>
    <category><![CDATA[IA]]></category>
    <dc:creator><![CDATA[David Pilato]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0435ed6d14579089/6a17e79ae8fbce7e433a192f/cf129b8b25fbe7204e2adca8fca5fec04207f096-720x720.png" length="0" type="image/png"/>
    <pubDate>Mon, 23 Sep 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Techniques avancées de RAG, partie 2 : Requêtes et tests]]></title>
    <description><![CDATA[Discuter et mettre en œuvre des techniques susceptibles d'améliorer les performances du RAG. Partie 2 sur 2, axée sur l'interrogation et le test d'un pipeline RAG avancé.]]></description>
    <content:encoded><![CDATA[<p><em>Tout le code peut être trouvé </em><a href="https://github.com/elastic/elasticsearch-labs/tree/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques"><em>dans le repo de Searchlabs, dans la branche advanced-rag-techniques</em></a><em>.</em></p><p>Bienvenue dans la deuxième partie de notre article sur les techniques avancées de RAG ! Dans la <a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1">première partie de cette série</a>, nous avons mis en place, discuté et implémenté les composants de traitement des données du pipeline RAG avancé :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9a4691874a19d8da/6a170b3f47d49c99f22d8a24/72b51ba2ae5e5977b56e5b915674753d6cfd0e56-1440x840.jpg" alt="Pipeline RAG avancé" /><p>Dans cette partie, nous allons procéder à l'interrogation et au test de notre mise en œuvre. Allons droit au but !</p><h3>Table des matières</h3><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#searching-and-retrieving,-generating-answers">Recherche et récupération, génération de réponses</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#enriching-queries-with-synonyms">Enrichir les requêtes avec des synonymes</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#hyde-hypothetical-document-embedding">HyDE (Hypothetical Document Embedding)</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#hybrid-search">Recherche hybride</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#experiments">Expériences</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#summary-of-results">Synthèse des résultats</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#test-1-who-audits-elastic">Test 1 : Qui vérifie Elastic ?</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#advancedrag">AdvancedRAG</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#simplerag">SimpleRAG</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#test-2--total-revenue-2023">Test 2 : recettes totales en 2023</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#advancedrag-1">AdvancedRAG</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#simplerag-1">SimpleRAG</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#test-3-what-product-does-growth-primarily-depend-on-how-much">Test 3 : De quel produit la croissance dépend-elle principalement ? Combien ?</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#advancedrag-2">AdvancedRAG</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#simplerag-2">SimpleRAG</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#test-4-describe-employee-benefit-plan">Test 4 : Décrire le régime d'avantages sociaux des salariés</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#advancedrag-3">AdvancedRAG</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#simplerag-3">SimpleRAG</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#test-5-which-companies-did-elastic-acquire">Test 5 : Quelles sont les entreprises acquises par Elastic ?</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#advancedrag-4">AdvancedRAG</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#simplerag-4">SimpleRAG</a></p></li></ul></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#conclusion">Conclusion</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#appendix">Annexe</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#prompts">Prompts</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#rag-question-answering-prompt">Question du RAG - invite à répondre</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#elastic-query-generator-prompt">Générateur de requêtes élastiques</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#potential-questions-generator-prompt">Questions potentielles : invite à la création d'un générateur</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#hyde-generator-prompt">Invite du générateur HyDE</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#sample-hybrid-search-query">Exemple de requête de recherche hybride</a></p></li></ul></li></ul><h2>Recherche et récupération, génération de réponses</h2><p>Posons notre première question, idéalement une information trouvée principalement dans le rapport annuel. Que diriez-vous de.. :</p>Who audits Elastic?"
<p>Appliquons maintenant quelques-unes de nos techniques pour améliorer la requête.</p><h3>Enrichir les requêtes avec des synonymes</h3><p>Tout d'abord, améliorons la diversité de la formulation de la requête et transformons-la en une forme qui peut être facilement traitée dans une requête Elasticsearch. Nous ferons appel à GPT-4o pour convertir la requête en une liste de clauses OR. Écrivons ce message :</p>
ELASTIC_SEARCH_QUERY_GENERATOR_PROMPT = '''
You are an AI assistant specialized in generating Elasticsearch query strings. Your task is to create the most effective query string for the given user question. This query string will be used to search for relevant documents in an Elasticsearch index.

Guidelines:
1. Analyze the user's question carefully.
2. Generate ONLY a query string suitable for Elasticsearch's match query.
3. Focus on key terms and concepts from the question.
4. Include synonyms or related terms that might be in relevant documents.
5. Use simple Elasticsearch query string syntax if helpful (e.g., OR, AND).
6. Do not use advanced Elasticsearch features or syntax.
7. Do not include any explanations, comments, or additional text.
8. Provide only the query string, nothing else.

For the question "What is Clickthrough Data?", we would expect a response like:
clickthrough data OR click-through data OR click through rate OR CTR OR user clicks OR ad clicks OR search engine results OR web analytics

AND operator is not allowed. Use only OR.

User Question:
[The user's question will be inserted here]

Generate the Elasticsearch query string:
'''
<p>Appliqué à notre requête, GPT-4o génère des synonymes de la requête de base et du vocabulaire connexe.</p>'audits elastic OR 
elasticsearch audits OR 
elastic auditor OR 
elasticsearch auditor OR 
elastic audit firm OR 
elastic audit company OR 
elastic audit organization OR 
elastic audit service'
<p>Dans la classe <code>ESQueryMaker</code>, j'ai défini une fonction pour diviser la requête :</p>def parse_or_query(self, query_text: str) -&gt; List[str]:
    # Split the query by 'OR' and strip whitespace from each term
    # This converts a string like "term1 OR term2 OR term3" into a list ["term1", "term2", "term3"]
    return [term.strip() for term in query_text.split(' OR ')]
<p>Son rôle est de prendre cette chaîne de clauses OR et de les diviser en une liste de termes, ce qui nous permet d'effectuer une correspondance multiple sur les champs clés du document :</p>["original_text", 'keyphrases', 'potential_questions', 'entities']
<p>Finalement, nous avons abouti à cette requête :</p> 'query': {
    'bool': {
        'must': [
            {
                'multi_match': {
                'query': 'audits Elastic Elastic auditing Elastic audit process Elastic compliance Elastic security audit Elasticsearch auditing Elasticsearch compliance Elasticsearch security audit',
                'fields': [
                    'original_text',
                'keyphrases',
                'potential_questions',
                'entities'
                ],
                'type': 'best_fields',
                'operator': 'or'
                }
            }
      ]
<p>Cela permet de couvrir beaucoup plus de bases que la requête initiale, réduisant ainsi le risque de manquer un résultat de recherche en raison de l'oubli d'un synonyme. Mais nous pouvons faire plus.</p><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#table-of-contents">Haut de page</a></p><h3>HyDE (Hypothetical Document Embedding)</h3><p>Faisons à nouveau appel à GPT-4o, cette fois pour mettre en œuvre <a href="https://arxiv.org/abs/2212.10496">HyDE</a>.</p><p>Le principe de base de HyDE est de générer un document hypothétique - le type de document qui contiendrait probablement la réponse à la requête initiale. Le caractère factuel ou l'exactitude du document n'est pas en cause. En gardant cela à l'esprit, rédigeons l'invite suivante :</p>HYDE_DOCUMENT_GENERATOR_PROMPT = '''
You are an AI assistant specialized in generating hypothetical documents based on user queries. Your task is to create a detailed, factual document that would likely contain the answer to the user's question. This hypothetical document will be used to enhance the retrieval process in a Retrieval-Augmented Generation (RAG) system.

Guidelines:
1. Carefully analyze the user's query to understand the topic and the type of information being sought.
2. Generate a hypothetical document that:
   a. Is directly relevant to the query
   b. Contains factual information that would answer the query
   c. Includes additional context and related information
   d. Uses a formal, informative tone similar to an encyclopedia or textbook entry
3. Structure the document with clear paragraphs, covering different aspects of the topic.
4. Include specific details, examples, or data points that would be relevant to the query.
5. Aim for a document length of 200-300 words.
6. Do not use citations or references, as this is a hypothetical document.
7. Avoid using phrases like "In this document" or "This text discusses" - write as if it's a real, standalone document.
8. Do not mention or refer to the original query in the generated document.
9. Ensure the content is factual and objective, avoiding opinions or speculative information.
10. Output only the generated document, without any additional explanations or meta-text.

User Question:
[The user's question will be inserted here]

Generate a hypothetical document that would likely contain the answer to this query:
'''
<p>La recherche vectorielle s'appuyant généralement sur la similarité vectorielle cosinusoïdale, HyDE part du principe que l'on peut obtenir de meilleurs résultats en faisant correspondre des documents à des documents plutôt que des requêtes à des documents.</p><p>Ce qui nous importe, c'est la structure, le flux et la terminologie. Ce n'est pas tant le cas pour les faits. Le GPT-4o produit un document HyDE comme celui-ci :</p>'Elastic N.V., the parent company of Elastic, the organization known for developing Elasticsearch, is subject to audits to ensure financial accuracy, 
regulatory compliance, and the integrity of its financial statements. The auditing of Elastic N.V. is typically conducted by an external, 
independent auditing firm. This is common practice for publicly traded companies to provide stakeholders with assurance regarding the company\'s 
financial position and operations.\n\nThe primary external auditor for Elastic is the audit firm Ernst &amp; Young LLP (EY). Ernst &amp; Young is one of the 
four largest professional services networks in the world, commonly referred to as the "Big Four" audit firms. These firms handle a substantial number 
of audits for major corporations around the globe, ensuring adherence to generally accepted accounting principles (GAAP) and international financial 
reporting standards (IFRS).\n\nThe audit process conducted by EY involves several steps. Initially, the auditors perform a risk assessment to identify 
areas where misstatements due to error or fraud could occur. They then design audit procedures to test the accuracy and completeness of financial statements,
 which include examining financial transactions, assessing internal controls, and reviewing compliance with relevant laws and regulations. Upon completion of 
 the audit, Ernst &amp; Young issues an audit report, which includes the auditor’s opinion on whether the financial statements are free from material misstatement 
 and are presented fairly in accordance with the applicable financial reporting framework.\n\nIn addition to external audits by firms like Ernst &amp; Young, 
 Elastic may also be subject to internal audits. Internal audits are performed by the company’s own internal auditors to evaluate the effectiveness of internal 
 controls, risk management, and governance processes.\n\nOverall, the auditing process plays a crucial role in maintaining the transparency and reliability of 
 Elastic\'s financial information, providing confidence to investors, regulators, and other stakeholders.'
<p>Il semble assez crédible, comme le candidat idéal pour les types de documents que nous aimerions indexer. Nous allons l'intégrer et l'utiliser pour la recherche hybride.</p><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#table-of-contents">Haut de page</a></p><h3>Recherche hybride</h3><p>C'est le cœur de notre logique de recherche. Notre composante de recherche lexicale sera les chaînes de clauses OR générées. Notre composante vectorielle dense sera le document HyDE intégré (également appelé vecteur de recherche). Nous utilisons KNN pour identifier efficacement les documents candidats les plus proches de notre vecteur de recherche. Nous appelons notre composant de recherche lexicale <em>Scoring with TF-IDF and BM25</em> par défaut. Enfin, les scores des vecteurs lexicaux et denses seront combinés en utilisant le ratio 30/70 recommandé par <a href="https://arxiv.org/abs/2407.01219">Wang et al.</a></p>def hybrid_vector_search(self, index_name: str, query_text: str, query_vector: List[float], 
                         text_fields: List[str], vector_field: str, 
                         num_candidates: int = 100, num_results: int = 10) -&gt; Dict:
    """
    Perform a hybrid search combining text-based and vector-based similarity.

    Args:
        index_name (str): The name of the Elasticsearch index to search.
        query_text (str): The text query string, which may contain 'OR' separated terms.
        query_vector (List[float]): The query vector for semantic similarity search.
        text_fields (List[str]): List of text fields to search in the index.
        vector_field (str): The name of the field containing document vectors.
        num_candidates (int): Number of candidates to consider in the initial KNN search.
        num_results (int): Number of final results to return.

    Returns:
        Dict: A tuple containing the Elasticsearch response and the search body used.
    """
    try:
        # Parse the query_text into a list of individual search terms
        # This splits terms separated by 'OR' and removes any leading/trailing whitespace
        query_terms = self.parse_or_query(query_text)

        # Construct the search body for Elasticsearch
        search_body = {
            # KNN search component for vector similarity
            "knn": {
                "field": vector_field,  # The field containing document vectors
                "query_vector": query_vector,  # The query vector to compare against
                "k": num_candidates,  # Number of nearest neighbors to retrieve
                "num_candidates": num_candidates  # Number of candidates to consider in the KNN search
            },
            "query": {
                "bool": {
                    # The 'must' clause ensures that matching documents must satisfy this condition
                    # Documents that don't match this clause are excluded from the results
                    "must": [
                        {
                            # Multi-match query to search across multiple text fields
                            "multi_match": {
                                "query": " ".join(query_terms),  # Join all query terms into a single space-separated string
                                "fields": text_fields,  # List of fields to search in
                                "type": "best_fields",  # Use the best matching field for scoring
                                "operator": "or"  # Match any of the terms (equivalent to the original OR query)
                            }
                        }
                    ],
                    # The 'should' clause boosts relevance but doesn't exclude documents
                    # It's used here to combine vector similarity with text relevance
                    "should": [
                        {
                            # Custom scoring using a script to combine vector and text scores
                            "script_score": {
                                "query": {"match_all": {}},  # Apply this scoring to all documents that matched the 'must' clause
                                "script": {
                                    # Script to combine vector similarity and text relevance
                                    "source": """
                                    # Calculate vector similarity (cosine similarity + 1)
                                    # Adding 1 ensures the score is always positive
                                    double vector_score = cosineSimilarity(params.query_vector, params.vector_field) + 1.0;
                                    # Get the text-based relevance score from the multi_match query
                                    double text_score = _score;
                                    # Combine scores: 70% vector similarity, 30% text relevance
                                    # This weighting can be adjusted based on the importance of semantic vs keyword matching
                                    return 0.7 * vector_score + 0.3 * text_score;
                                    """,
                                    # Parameters passed to the script
                                    "params": {
                                        "query_vector": query_vector,  # Query vector for similarity calculation
                                        "vector_field": vector_field  # Field containing document vectors
                                    }
                                }
                            }
                        }
                    ]
                }
            }
        }

        # Execute the search request against the Elasticsearch index
        response = self.conn.search(index=index_name, body=search_body, size=num_results)
        # Log the successful execution of the search for monitoring and debugging
        logger.info(f"Hybrid search executed on index: {index_name} with text query: {query_text}")
        # Return both the response and the search body (useful for debugging and result analysis)
        return response, search_body
    except Exception as e:
        # Log any errors that occur during the search process
        logger.error(f"Error executing hybrid search on index: {index_name}. Error: {e}")
        # Re-raise the exception for further handling in the calling code
        raise e
<p>Enfin, nous pouvons reconstituer une fonction RAG. Notre RAG, de la question à la réponse, suivra ce flux :</p><ol><li><p>Convertir la requête en clauses OR.</p></li><li><p>Générer un document HyDE et l'intégrer.</p></li><li><p>Transmettre ces deux informations à la recherche hybride.</p></li><li><p>Récupérer les n premiers résultats, les inverser de façon à ce que le score le plus pertinent soit le "plus récent" dans la mémoire contextuelle du LLM (Reverse Packing) Reverse Packing Exemple : Requête : "Techniques d'optimisation des requêtes Elasticsearch" Documents récupérés (ordonnés par pertinence) :  Ordre inversé pour le contexte LLM :  En inversant l'ordre, l'information la plus pertinente (1) apparaît en dernier dans le contexte, recevant potentiellement plus d'attention de la part du LLM pendant la génération de la réponse.</p><ol><li><p>"Utilisez les requêtes bool pour combiner efficacement plusieurs critères de recherche."</p></li><li><p>"Mettre en œuvre des stratégies de mise en cache pour améliorer les temps de réponse des requêtes."</p></li><li><p>"Optimiser les mappages d'index pour accélérer les performances de recherche."</p></li><li><p>"Optimiser les mappages d'index pour accélérer les performances de recherche."</p></li><li><p>"Mettre en œuvre des stratégies de mise en cache pour améliorer les temps de réponse des requêtes."</p></li><li><p>"Utilisez les requêtes bool pour combiner efficacement plusieurs critères de recherche."</p></li></ol></li><li><p>Transmettre le contexte au LLM pour qu'il le génère.</p></li></ol>def get_context(index_name, 
                match_query, 
                text_query, 
                fields, 
                num_candidates=100, 
                num_results=20, 
                text_fields=["original_text", 'keyphrases', 'potential_questions', 'entities'], 
                embedding_field="primary_embedding"):

    embedding=embedder.get_embeddings_from_text(text_query)

    results, search_body = es_query_maker.hybrid_vector_search(
        index_name=index_name,
        query_text=match_query,
        query_vector=embedding[0][0],
        text_fields=text_fields,
        vector_field=embedding_field,
        num_candidates=num_candidates,
        num_results=num_results
    )

    # Concatenates the text in each 'field' key of the search result objects into a single block of text.
    context_docs=['\n\n'.join([field+":\n\n"+j['_source'][field] for field in fields]) for j in results['hits']['hits']]

    # Reverse Packing to ensure that the highest ranking document is seen first by the LLM.
    context_docs.reverse()
    return context_docs, search_body

def retrieval_augmented_generation(query_text):
    match_query= gpt4o.generate_query(query_text)
    fields=['original_text']

    hyde_document=gpt4o.generate_HyDE(query_text)

    context, search_body=get_context(index_name, match_query, hyde_document, fields)

    answer= gpt4o.basic_qa(query=query_text, context=context)
    return answer, match_query, hyde_document, context, search_body

<p>Exécutons notre requête et obtenons notre réponse :</p>According to the context, Elastic N.V. is audited by an independent registered public accounting firm, PricewaterhouseCoopers (PwC). 
This information is found in the section titled "report of independent registered public accounting firm," which states:

"We have audited the accompanying consolidated balance sheets of Elastic N.V. [...] / s / pricewaterhouseco."
<p>C'est une bonne chose. C'est exact.</p><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#table-of-contents">Haut de page</a></p><h2>Expériences</h2><p>Il faut maintenant répondre à une question importante. Qu'avons-nous obtenu en investissant tant d'efforts et de complexité supplémentaire dans ces mises en œuvre ?</p><p>Faisons une petite comparaison. Le pipeline RAG que nous avons mis en œuvre par rapport à la recherche hybride de base, sans aucune des améliorations que nous avons apportées. Nous allons effectuer une petite série de tests et voir si nous constatons des différences substantielles. Nous appellerons le RAG que nous venons de mettre en œuvre AdvancedRAG et le pipeline de base SimpleRAG.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf605c8246989df32/6a1711178b73cbc61d18a11d/8da40067835ab8b4dc12fe52a51a6c26858ad32f-1440x1095.jpg" alt="Pipeline RAG simple" /><h4>Synthèse des résultats</h4><p>Ce tableau résume les résultats de cinq tests effectués sur les deux pipelines RAG. J'ai évalué la supériorité relative de chaque méthode sur la base du détail et de la qualité des réponses, mais il s'agit d'un jugement totalement subjectif. Les réponses réelles sont reproduites sous ce tableau pour votre considération. Ceci étant dit, jetons un coup d'œil sur leurs résultats !</p><p>SimpleRAG n'a pas pu répondre aux questions 1 &amp; 5. AdvancedRAG a également répondu de manière beaucoup plus détaillée aux questions 2, 3 et 4. Sur la base de ces détails, j'ai jugé la qualité des réponses d'AdvancedRAG meilleure.</p><p>Test</p><p>Question</p><p>AdvancedRAG Performance</p><p>Performance de SimpleRAG</p><p>AdvancedRAG Latence</p><p>Latence de SimpleRAG</p><p>Gagnant</p><p>1</p><p>Qui audite Elastic ?</p><p>A correctement identifié PwC comme étant l'auditeur.</p><p>L'auditeur n'a pas été identifié.</p><p>11.6s</p><p>4.4s</p><p>AdvancedRAG</p><p>2</p><p>Quel a été le revenu total en 2023 ?</p><p>A fourni le chiffre d'affaires correct. Inclusion d'un contexte supplémentaire avec les recettes des années précédentes.</p><p>A fourni le chiffre d'affaires correct.</p><p>13.3s</p><p>2.8s</p><p>AdvancedRAG</p><p>3</p><p>De quel produit la croissance dépend-elle principalement ? Combien ?</p><p>A correctement identifié l'Elastic Cloud comme étant le facteur clé. Le contexte général des recettes a été inclus &amp; de manière plus détaillée.</p><p>A correctement identifié l'Elastic Cloud comme étant le facteur clé.</p><p>14.1s</p><p>12.8s</p><p>AdvancedRAG</p><p>4</p><p>Décrire le régime d'avantages sociaux des employés</p><p>Description complète des régimes de retraite, des programmes de santé et des autres avantages. Incluait des montants de cotisation spécifiques pour différentes années.</p><p>A donné un bon aperçu des avantages, y compris la rémunération, les plans de retraite, l'environnement de travail et le programme Elastic Cares.</p><p>26.6s</p><p>11.6s</p><p>AdvancedRAG</p><p>5</p><p>Quelles sont les entreprises acquises par Elastic ?</p><p>A corrigé la liste des acquisitions récentes mentionnées dans le rapport (CmdWatch, Build Security, Optimyze). Il a fourni quelques dates d'acquisition et des prix d'achat.</p><p>Échec de la recherche d'informations pertinentes dans le contexte fourni.</p><p>11.9s</p><p>2.7s</p><p>AdvancedRAG</p><h4>Test 1 : Qui vérifie Elastic ?</h4><p>AdvancedRAG</p>According to the context, Elastic N.V. is audited by an independent 
registered public accounting firm, PricewaterhouseCoopers (PwC). 
This information is found in the section titled "report of 
independent registered public accounting firm," which states:
"We have audited the accompanying consolidated balance sheets 
of Elastic N.V. [...] / s / pricewaterhouseco."
<p>SimpleRAG</p>According to the provided context, no specific auditing firm 
or company is mentioned as the auditor for Elastic. 
The context provided talks about various features, strengths, 
deployment options, and other details related to Elastic and 
its operations but does not specify which auditing firm is 
responsible for auditing Elastic's financial statements or 
internal controls.
<p><strong>Résumé</strong>: SimpleRAG n'a pas identifié PWC comme étant l'auditeur.</p><p>D'accord, c'est assez surprenant. Cela ressemble à un échec de recherche de la part de SimpleRAG. Aucun document relatif à l'audit n'a été retrouvé. Réduisons un peu la difficulté avec le test suivant.</p><h4>Test 2 : recettes totales en 2023</h4><p>AdvancedRAG</p>### Total Revenue for 2023

According to the provided context, the total revenue for the 
year ended April 30, 2023, was **$1,068,989,000**. This 
information can be found several times in the provided text. 
Specifically, one part states:

"The following table sets forth our results of operations 
for the periods presented in dollars and as a percentage of 
our total revenue. ... year ended April 30, 2023, ... 
total revenue 1,068,989".

Additionally, it is mentioned here as well:

"For the years ended April 30, 2023, 2022, and 2021, 
representing year-over-year growth of 24% for the year 
ended April 30, 2023... total revenue $1,068,989".

Therefore, the total revenue for 2023 is $1.069 billion.
<p>SimpleRAG</p>According to the context provided, the total revenue for the fiscal year ended April 30, 2023, is:

$1,068,989,000 (or approximately $1.1 billion).
<p><strong>Résumé</strong>: les deux RAG ont obtenu la bonne réponse : 1 068 989 000 dollars de recettes totales en 2023.</p><p>Ils étaient tous les deux ici. Il semble qu'AdvancedRAG ait acquis un plus grand nombre de documents ? La réponse est certainement plus détaillée et intègre des informations des années précédentes. On peut s'y attendre compte tenu des améliorations que nous avons apportées, mais il est encore trop tôt pour se prononcer.</p><p>Augmentons la difficulté.</p><h4>Test 3 : De quel produit la croissance dépend-elle principalement ? Combien ?</h4><p>AdvancedRAG</p>### Product Growth Primarily Depends On:
According to the context, "our revenue is derived primarily from 
subscriptions of search, observability and security built into 
the elastic stack." The document mentions various strategies and 
factors contributing to the company’s growth which include investment 
in "elastic cloud," "elastic stack," and the conversion of free 
users to paid subscribers.

### Quantitative Data:
- **Revenue**: The document states, "Our total revenue was $1.1 billion, 
$862.4 million, and $608.5 million for the years ended April 30, 2023, 
2022, and 2021, respectively."
- **Subscription Revenue**: "Subscriptions accounted for 92%, 93%, 
and 93% of our total revenue for the years ended April 30, 2023, 
2022, and 2021, respectively."
- **Elastic Cloud Contribution**: "For the years ended April 30, 2023, 
2022, and 2021, Elastic Cloud contributed 40%, 35%, and 27% of our 
total revenue, respectively."
- **Customer Growth**: The number of customers increased from 
"over 15,000 as of April 30, 2021," to "approximately 20,200 as of 
April 30, 2023."

### Summary:
Product growth for the company primarily depends on the "Elastic Stack" 
(which includes search, observability, and security solutions) and 
particularly the "Elastic Cloud." The Elastic Cloud's growing contribution 
to revenue highlights its significance, which grew from 27% in 2021 to 40% in 2023.
<p>SimpleRAG</p>According to the provided context, growth primarily depends on **Elastic Cloud**, 
which is described as the company's "family of cloud-based offerings." 
The context also emphasizes the significance of Elastic Cloud in achieving 
the company's long-term growth potential and its role in increasing subscription revenue.

From the context:

- "Increasing adoption of Elastic Cloud... is an important growth opportunity 
for our business. Organizations are increasingly looking for hosted 
deployment alternatives with reduced administrative burdens."
- "We believe that offering Elastic Cloud is important for achieving 
our long-term growth potential, and we expect Elastic Cloud’s contribution 
to our subscription revenue to continue to increase."

In terms of the contribution to total revenue, here are the specific figures provided:

- Elastic Cloud contributed 40% of the total revenue for the year ended April 30, 2023.
- Elastic Cloud contributed 35% of the total revenue for the year ended April 30, 2022.
- Elastic Cloud contributed 27% of the total revenue for the year ended April 30, 2021.

This progression shows a clear upward trend in the importance and 
contribution of Elastic Cloud to the company's overall revenue.
<p><strong>Résumé</strong>: les deux groupes d'experts ont correctement identifié Elastic Cloud comme le principal moteur de croissance. Cependant, AdvancedRAG fournit plus de détails, en tenant compte des revenus d'abonnement et de la croissance de la clientèle, et mentionne explicitement d'autres offres d'Elastic.</p><h4>Test 4 : Décrire le régime d'avantages sociaux des salariés</h4><p>AdvancedRAG</p>### Employee Benefit Plan Overview

The provided context outlines various elements of the employee benefit plan at 
Elastic, often referred to as "Elasticians." Here are the key components 
described in the texts:

1. **401(k) Plan**:
   - **Coverage**: The 401(k) plan is available to substantially all U.S. 
   employees who meet minimum age and service requirements.
   - **Contributions**: Elastic makes contributions to the 401(k) plan up to 
   6% of the participating employee’s W-2 earnings and wages.
   - **Expenses**: For the fiscal years ended April 30, Elastic recorded 
   expenses of $17.9 million (2023), $15.2 million (2022), and $11.4 million (2021) 
   related to the 401(k) plan.
   - **Defined-Contribution Plans in Other Countries**: Elastic has 
   defined-contribution plans in various other countries and recorded respective 
   expenses of $9.4 million (2023), $7.2 million (2022), and $5.1 million (2021).

2. **Stock-Based Compensation**:
   - **Types of Awards**: Stock options, restricted stock units (RSUs), 
   and shares under the Employee Stock Purchase Plan (ESPP).
   - **Fair Value Measurement**: Fair value of these stock awards is 
   measured using models like Black-Scholes.
   - **Employee Stock Purchase Plan (2022 ESPP)**: 
     - Started in 2022, it allows employees to acquire ordinary 
     shares at a discount (85% of the market value at the beginning 
     or end of the offering period).
     - Offering periods are approximately six months long.

3. **Total Rewards Compensation**:
   - **Components**: Includes cash compensation as well as equity awards, 
   reflecting a comprehensive interest in the well-being of the employees.
   - **Programs**: Market competitive medical and dental programs, 
   mental health, and holistic well-being programs.
   - **Paid Time Off**: Features 16 weeks of paid leave for new 
   parents and life-planning benefits.

4. **Retirement and Income Protection Plans**:
   - **401(k) Match**: Elastic provides a dollar-for-dollar 
   match up to 6% of eligible earnings up to a plan-limit 
   maximum for U.S.-based employees.
   - **Similar Plans Outside the U.S.**: Competitive retirement 
   plans available for employees based outside the United States.

5. **Flexible Work Environment**: 
   - **Distributed Workforce**: Elastic operates with a global 
   distributed workforce model, allowing employees to work remotely 
   and providing a broad candidate pool.
   
6. **Learning and Development**:
   - **Programs**: Access to on-demand video-based learning, 
   leadership development programs, and other specific training 
   to develop managers and leaders.

7. **Employee Engagement and Surveys**:
   - **Mechanisms**: Annual employee engagement survey and 
   mid-year pulse survey.
   - **Objectives**: To monitor morale, develop action plans, 
   and maintain high engagement scores.

8. **Onboarding Program**:
   - **X-School**: An extensive new-hire orientation program to 
   help new employees meet peers and learn about the company's 
   products and solutions.

In summary, Elastic's employee benefit plan encompasses a broad 
spectrum of financial, health, well-being, and professional development 
benefits designed to support and enhance the lives of its employees. 
The plan is both competitive and comprehensive, aiming to attract,
retain, and engage its global workforce.
<p>SimpleRAG</p>The employee benefit plan at the Company includes several key 
components aimed at enhancing the overall well-being and 
professional development of employees, referred to as "Elasticians." 

1. **Compensation and Benefits:**
   - The Company provides market-competitive compensation 
   packages which typically include cash compensation and equity awards.
   - Comprehensive medical and dental programs are offered, 
   emphasizing mental health and holistic well-being.
   - Paid time off programs feature:
     - 16 weeks of paid leave for new parents.
     - Life-planning benefits.
     - Travel reimbursements for certain healthcare services.

2. **Retirement and Income Protection Plans:**
   - In the United States, employees are eligible for a 401k 
   plan with a dollar-for-dollar match by the Company up to 6% 
   of eligible earnings, subject to a plan limit.
   - Similar competitive retirement and income protection plans 
   are available for employees outside the United States.

3. **Work Environment:**
   - The Company offers a flexible work environment, allowing most 
   employees to work from anywhere as often as they would like.
   - Physical offices around the world are available for employees 
   who prefer to work on-site or need a space for face-to-face interactions.

4. **Community Involvement:**
   - The Elastic Cares Program supports charitable organizations 
   that matter to employees on both local and global levels.
   - The program includes:
     - Donation matching.
     - Providing the Company's technology for free to certain 
     nonprofit organizations.
     - Volunteer time off initiative.
<p><strong>Résumé</strong>: AdvancedRAG va beaucoup plus en profondeur et en détail, en mentionnant le plan 401K pour les employés basés aux États-Unis, ainsi qu'en définissant les plans de contribution en dehors des États-Unis. Il mentionne également les régimes de santé et de bien-être, mais ne mentionne pas le programme Elastic Cares, que SimpleRAG mentionne.</p><h4>Test 5 : Quelles sont les entreprises acquises par Elastic ?</h4><p>AdvancedRAG</p>Elastic acquired several companies over the years to enhance its 
technology and market presence. According to the provided context, 
the specific acquisitions include:

1. **CmdWatch Security Inc.**: Acquired on September 17, 2021. 
The total purchase consideration was $77.8 million.
2. **Build Security Ltd.**: Acquired on September 2, 2021. The 
combined purchase price for Build Security Ltd. and another acquisition was $57.2 million.
3. **Optimyze Cloud Inc.**: Acquired on November 1, 2021. Part 
of the combined purchase price mentioned above with Build Security Ltd.
4. **Endgame, Inc.**: Although the context mentions an amended 
and restated stock incentive plan, it indicates that Endgame, Inc. was 
associated with Elastic, suggesting it could have been acquired at some point.

These acquisitions are part of Elastic's strategy to 
enhance the technology underlying its security and 
observability offerings.
<p>SimpleRAG</p>The provided context does not specify any companies that Elastic has acquired. 
Therefore, based on the context, there is no information available about the companies acquired by Elastic.
<p><strong>Résumé</strong>: SimpleRAG ne récupère aucune information pertinente sur les acquisitions, ce qui entraîne un échec de la réponse. AdvancedRAG répertorie correctement CmdWatch, Build Security et Optimyze, qui sont les principales acquisitions mentionnées dans le rapport.</p><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#table-of-contents">Haut de page</a></p><h2>Conclusion</h2><p>D'après nos tests, nos techniques avancées semblent augmenter l'étendue et la profondeur des informations présentées, ce qui pourrait améliorer la qualité des réponses RAG.</p><p>En outre, il est possible que la fiabilité soit améliorée, car les questions formulées de manière ambiguë, telles que <code>Which companies did Elastic acquire?</code> et <code>Who audits Elastic</code>, ont été correctement répondues par AdvancedRAG, mais pas par SimpleRAG.</p><p>Toutefois, il convient de garder à l'esprit que dans 3 cas sur 5, le pipeline RAG de base, intégrant la recherche hybride mais aucune autre technique, a réussi à produire des réponses qui capturaient la plupart des informations clés.</p><p>Il convient de noter qu'en raison de l'incorporation des LLM dans les phases de préparation des données et d'interrogation, la latence d'AdvancedRAG est généralement de 2 à 5 fois supérieure à celle de SimpleRAG. Il s'agit d'un coût important qui pourrait faire en sorte qu'AdvancedRAG ne convienne qu'aux situations où la qualité de la réponse est prioritaire par rapport à la latence.</p><p>Les coûts de latence importants peuvent être réduits en utilisant un LLM plus petit et moins cher comme Claude Haiku ou GPT-4o-mini à l'étape de préparation des données. Conservez les modèles avancés pour la génération de réponses.</p><p>Cela correspond aux conclusions de Wang et al. Comme le montrent leurs résultats, les améliorations apportées sont relativement progressives. En bref, un simple RAG de base vous permet d'obtenir un produit final décent, tout en étant moins cher et plus rapide. Pour moi, c'est une conclusion intéressante. Pour les cas d'utilisation où la vitesse et l'efficacité sont essentielles, SimpleRAG est un choix judicieux. Pour les cas d'utilisation où chaque goutte de performance doit être extraite, les techniques incorporées dans AdvancedRAG peuvent offrir une solution.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt56b7067a9d41d5a8/6a171119acf0886fb4be9c45/ea811706b6adc4731d90b925a9fefa0ac15901b4-1440x1060.jpg" alt="Pipeline Wang" /><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#table-of-contents">Haut de page</a></p><h2>Annexe</h2><h3>Prompts</h3><h4>Question du RAG - invite à répondre</h4><p>Invitation à faire en sorte que le LLM génère des réponses basées sur la requête et le contexte.</p>BASIC_RAG_PROMPT = '''
You are an AI assistant tasked with answering questions based primarily on the provided context, while also drawing on your own knowledge when appropriate. Your role is to accurately and comprehensively respond to queries, prioritizing the information given in the context but supplementing it with your own understanding when beneficial. Follow these guidelines:

1. Carefully read and analyze the entire context provided.
2. Primarily focus on the information present in the context to formulate your answer.
3. If the context doesn't contain sufficient information to fully answer the query, state this clearly and then supplement with your own knowledge if possible.
4. Use your own knowledge to provide additional context, explanations, or examples that enhance the answer.
5. Clearly distinguish between information from the provided context and your own knowledge. Use phrases like "According to the context..." or "The provided information states..." for context-based information, and "Based on my knowledge..." or "Drawing from my understanding..." for your own knowledge.
6. Provide comprehensive answers that address the query specifically, balancing conciseness with thoroughness.
7. When using information from the context, cite or quote relevant parts using quotation marks.
8. Maintain objectivity and clearly identify any opinions or interpretations as such.
9. If the context contains conflicting information, acknowledge this and use your knowledge to provide clarity if possible.
10. Make reasonable inferences based on the context and your knowledge, but clearly identify these as inferences.
11. If asked about the source of information, distinguish between the provided context and your own knowledge base.
12. If the query is ambiguous, ask for clarification before attempting to answer.
13. Use your judgment to determine when additional information from your knowledge base would be helpful or necessary to provide a complete and accurate answer.

Remember, your goal is to provide accurate, context-based responses, supplemented by your own knowledge when it adds value to the answer. Always prioritize the provided context, but don't hesitate to enhance it with your broader understanding when appropriate. Clearly differentiate between the two sources of information in your response.

Context:
[The concatenated documents will be inserted here]

Query:
[The user's question will be inserted here]

Please provide your answer based on the above guidelines, the given context, and your own knowledge where appropriate, clearly distinguishing between the two:
'''
<h4>Générateur de requêtes élastiques</h4><p>Invite à enrichir les requêtes avec des synonymes et à les convertir au format OR.</p>ELASTIC_SEARCH_QUERY_GENERATOR_PROMPT = '''
You are an AI assistant specialized in generating Elasticsearch query strings. Your task is to create the most effective query string for the given user question. This query string will be used to search for relevant documents in an Elasticsearch index.

Guidelines:
1. Analyze the user's question carefully.
2. Generate ONLY a query string suitable for Elasticsearch's match query.
3. Focus on key terms and concepts from the question.
4. Include synonyms or related terms that might be in relevant documents.
5. Use simple Elasticsearch query string syntax if helpful (e.g., OR, AND).
6. Do not use advanced Elasticsearch features or syntax.
7. Do not include any explanations, comments, or additional text.
8. Provide only the query string, nothing else.

For the question "What is Clickthrough Data?", we would expect a response like:
clickthrough data OR click-through data OR click through rate OR CTR OR user clicks OR ad clicks OR search engine results OR web analytics

AND operator is not allowed. Use only OR.

User Question:
[The user's question will be inserted here]

Generate the Elasticsearch query string:
'''
<h4>Questions potentielles : invite à la création d'un générateur</h4><p>Invite à générer des questions potentielles, à enrichir les métadonnées des documents.</p>RAG_QUESTION_GENERATOR_PROMPT = '''
You are an AI assistant specialized in generating questions for Retrieval-Augmented Generation (RAG) systems. Your task is to analyze a given document and create 10 diverse questions that would effectively test a RAG system's ability to retrieve and synthesize information from this document.

Guidelines:
1. Thoroughly analyze the entire document.
2. Generate exactly 10 questions that cover various aspects and levels of complexity within the document's content.
3. Create questions that specifically target:
   a. Key facts and information
   b. Main concepts and ideas
   c. Relationships between different parts of the content
   d. Potential applications or implications of the information
   e. Comparisons or contrasts within the document
4. Ensure questions require answers of varying lengths and complexity, from simple retrieval to more complex synthesis.
5. Include questions that might require combining information from different parts of the document.
6. Frame questions to test both literal comprehension and inferential understanding.
7. Avoid yes/no questions; focus on open-ended questions that promote comprehensive answers.
8. Consider including questions that might require additional context or knowledge to fully answer, to test the RAG system's ability to combine retrieved information with broader knowledge.
9. Number the questions from 1 to 10.
10. Output only the ten questions, without any additional text, explanations, or answers.

Document:
[The document content will be inserted here]

Generate 10 questions optimized for testing a RAG system based on this document:
'''
<h4>Invite du générateur HyDE</h4><p>Invite à générer des documents hypothétiques à l'aide de HyDE</p>HYDE_DOCUMENT_GENERATOR_PROMPT = '''
You are an AI assistant specialized in generating hypothetical documents based on user queries. Your task is to create a detailed, factual document that would likely contain the answer to the user's question. This hypothetical document will be used to enhance the retrieval process in a Retrieval-Augmented Generation (RAG) system.

Guidelines:
1. Carefully analyze the user's query to understand the topic and the type of information being sought.
2. Generate a hypothetical document that:
   a. Is directly relevant to the query
   b. Contains factual information that would answer the query
   c. Includes additional context and related information
   d. Uses a formal, informative tone similar to an encyclopedia or textbook entry
3. Structure the document with clear paragraphs, covering different aspects of the topic.
4. Include specific details, examples, or data points that would be relevant to the query.
5. Aim for a document length of 200-300 words.
6. Do not use citations or references, as this is a hypothetical document.
7. Avoid using phrases like "In this document" or "This text discusses" - write as if it's a real, standalone document.
8. Do not mention or refer to the original query in the generated document.
9. Ensure the content is factual and objective, avoiding opinions or speculative information.
10. Output only the generated document, without any additional explanations or meta-text.

User Question:
[The user's question will be inserted here]

Generate a hypothetical document that would likely contain the answer to this query:
'''
<h3>Exemple de requête de recherche hybride</h3>{'knn': {'field': 'primary_embedding',
  'query_vector': [0.4265527129173279,
   -0.1712949573993683,
   -0.042020395398139954,
   ...],
  'k': 100,
  'num_candidates': 100},
 'query': {'bool': {'must': [{'multi_match': {'query': 'audits Elastic Elastic auditing Elastic audit process Elastic compliance Elastic security audit Elasticsearch auditing Elasticsearch compliance Elasticsearch security audit',
      'fields': ['original_text',
       'keyphrases',
       'potential_questions',
       'entities'],
      'type': 'best_fields',
      'operator': 'or'}}],
   'should': [{'script_score': {'query': {'match_all': {}},
      'script': {'source': '\n                                        double vector_score = cosineSimilarity(params.query_vector, params.vector_field) + 1.0;\n                                        double text_score = _score;\n                                        return 0.7 * vector_score + 0.3 * text_score;\n                                        ',
       'params': {'query_vector': [0.4265527129173279,
         -0.1712949573993683,
         -0.042020395398139954,
        ...],
        'vector_field': 'primary_embedding'}}}}]}},
 'size': 10}
]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2</guid>
    <category><![CDATA[Base vectorielle]]></category>
    <category><![CDATA[IA]]></category>
    <dc:creator><![CDATA[Han Xiang Choong]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf605c8246989df32/6a1711178b73cbc61d18a11d/8da40067835ab8b4dc12fe52a51a6c26858ad32f-1440x1095.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 15 Aug 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Techniques avancées de RAG partie 1 : Traitement des données]]></title>
    <description><![CDATA[Discuter et mettre en œuvre des techniques susceptibles d'améliorer les performances du RAG. Partie 1 sur 2, se concentrant sur le traitement et l'ingestion des données d'un pipeline RAG avancé.]]></description>
    <content:encoded><![CDATA[<p><em>Voici la première partie de notre exploration des techniques avancées de RAG. </em><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2"><em>Cliquez ici pour la deuxième partie !</em></a></p><p>L'article récent intitulé <a href="https://arxiv.org/abs/2407.01219">Searching for Best Practices in Retrieval-Augmented Generation</a> évalue de manière empirique l'efficacité de diverses techniques d'amélioration des RAG, dans le but de converger vers un ensemble de meilleures pratiques pour les RAG.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt671704ff06a4011d/6a170b3ea929cf2d19ae09d8/dafa7250e7c4ead4d9b4aed7c407509131929749-1440x572.png" alt="RAG pipeline recommandé par Wang" /><p>Nous mettrons en œuvre quelques-unes des meilleures pratiques proposées, notamment celles qui visent à améliorer la qualité de la recherche <strong>(Sentence Chunking, HyDE, Reverse Packing)</strong>.</p><p>Par souci de concision, nous omettons les techniques axées sur l'amélioration de l'efficacité <strong>(classification des requêtes et résumé).</strong></p><p>Nous mettrons également en œuvre quelques techniques qui n'ont pas été abordées, mais que je trouve personnellement utiles et intéressantes <strong>(Metadata Inclusion, Composite Multi-Field Embeddings, Query Enrichment).</strong></p><p>Enfin, nous effectuerons un petit test pour voir si la qualité de nos résultats de recherche et des réponses générées s'est améliorée par rapport à la base de référence. C'est parti !</p><h2>Vue d'ensemble du RAG</h2><p>RAG vise à améliorer les LLM en récupérant des informations dans des bases de connaissances externes afin d'enrichir les réponses générées. En fournissant des informations spécifiques au domaine, les LLM peuvent être rapidement adaptés à des cas d'utilisation qui sortent du cadre de leurs données de formation ; cela coûte beaucoup moins cher qu'un réglage fin et il est plus facile de les tenir à jour.</p><p>Les mesures visant à améliorer la qualité du RAG se concentrent généralement sur deux axes :</p><ol><li><p>Améliorer la qualité et la clarté de la base de connaissances.</p></li><li><p>Améliorer la couverture et la spécificité des requêtes de recherche.</p></li></ol><p>Ces deux mesures permettront d'améliorer les chances que le LLM ait accès à des faits et des informations pertinents, et qu'il soit donc moins susceptible d'halluciner ou de s'appuyer sur ses propres connaissances - qui peuvent être dépassées ou non pertinentes.</p><p>La diversité des méthodes est difficile à expliquer en quelques phrases. Pour plus de clarté, passons directement à la mise en œuvre.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9a4691874a19d8da/6a170b3f47d49c99f22d8a24/72b51ba2ae5e5977b56e5b915674753d6cfd0e56-1440x840.jpg" alt="Pipeline RAG avancé" /><h3>Table des matières</h3><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#overview">Aperçu</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#table-of-contents">Table des matières</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#set-up">Mise en place</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#ingesting-processing-and-embedding-documents">Acquisition, traitement et intégration de documents</a>  </p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#data-ingestion">Ingestion des données</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#sentence-level-token-wise-chunking">Chunking au niveau de la phrase, par jetons</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#metadata-inclusion-and-generation">Inclusion et génération de métadonnées</a> </p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#keyphrases-extracted-by-textrank">Phrases clés extraites par TextRank</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#potential-questions-generated-by-gpt-4o">Questions potentielles générées par le GPT-4o</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#entities-extracted-by-spacy">Entités extraites par Spacy</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#composite-multi-field-embeddings">Enchâssement composite de champs multiples</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#indexing-to-elastic">Indexation vers Elastic</a></p></li></ul></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#cat-break">Pause-catalogues</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#appendix">Annexe</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#definitions">Définitions</a></p></li></ul></li></ul><h2>Mise en place</h2><p><em>Tout le code peut être trouvé </em><a href="https://github.com/elastic/elasticsearch-labs/tree/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques"><em>dans le repo de Searchlabs</em></a><em>.</em></p><p>Commençons par le commencement. Vous aurez besoin des éléments suivants :</p><ol><li><p>Déploiement d'un nuage élastique</p></li><li><p>Une API LLM - Nous utilisons un déploiement GPT-4o sur Azure OpenAI dans ce carnet.</p></li><li><p>Python version 3.12.4 ou ultérieure</p></li></ol><p>Nous allons exécuter tout le code du <a href="https://github.com/elastic/elasticsearch-labs/blob/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques/main.ipynb">cahier main.ipynb</a>.</p><p>Allez-y et clonez git le repo, naviguez vers supporting-blog-content/advanced-rag-techniques, puis exécutez les commandes suivantes :</p># Create a new virtual environment named 'rag_env'
python -m venv rag_env

# Activate the virtual environment (for Unix-based systems)
source rag_env/bin/activate

# (For Windows)
.\rag_env\Scripts\activate

# Install packages listed in requirements.txt
pip install -r requirements.txt
<p>Une fois que c'est fait, créez un fichier <em>.env</em> et remplissez les champs suivants (référencés dans l'<a href="https://github.com/elastic/elasticsearch-labs/blob/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques/.env.example"><em>exemple .env</em></a>). Remerciements à mon co-auteur, Claude-3.5, pour ses commentaires utiles.</p># Elastic Cloud: Found in the 'Deployment' page of your Elastic Cloud 
# console
ELASTIC_CLOUD_ENDPOINT=""
ELASTIC_CLOUD_ID=""

# Elastic Cloud: Created during deployment setup or in 'Security' 
# settings
ELASTIC_USERNAME=""
ELASTIC_PASSWORD=""

# Elastic Cloud: The name of the index you created in Kibana or via API
ELASTIC_INDEX_NAME=""

# Azure AI Studio: Found in 'Keys and Endpoint' section of your Azure 
# OpenAI resource
AZURE_OPENAI_KEY_1=""
AZURE_OPENAI_KEY_2=""
AZURE_OPENAI_REGION=""
AZURE_OPENAI_ENDPOINT=""

# Azure AI Studio: Found in 'Deployments' section of your Azure OpenAI 
# resource
AZURE_OPENAI_DEPLOYMENT_NAME=""

# Using BAAI/bge-small-en-v1.5 because I think it is a good balance of 
# resource efficiency and performance. 
HUGGINGFACE_EMBEDDING_MODEL="BAAI/bge-small-en-v1.5"
<p>Ensuite, nous allons choisir le document à ingérer et le placer dans le dossier documents. Pour cet article, nous utiliserons le <a href="https://s201.q4cdn.com/217177842/files/doc_downloads/OtherDocuments/2023/AnnualMeeting/Annual-Report-Fiscal-Year-2023.pdf">rapport annuel 2023 d'Elastic N.V.</a> Il s'agit d'un document assez difficile et dense, parfait pour tester nos techniques RAG.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte292dc6030d496cc/6a170b40dc55de9b03e00dfc/e513b9d67adac43da794c25a5969b893127bbbe3-1440x395.jpg" alt="Rapport annuel d'Elastic 2023" /><p>Maintenant que tout est prêt, passons à l'ingestion. Ouvrez <em>main.ipynb</em> et exécutez les deux premières cellules pour importer tous les paquets et initialiser tous les services.</p><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#table-of-contents">Haut de page</a></p><h2>Acquisition, traitement et intégration de documents</h2><h3>Ingestion des données</h3><ul><li><p><em>Note personnelle : je suis stupéfait par la commodité de LlamaIndex. Avant les LLM et LlamaIndex, l'ingestion de documents de différents formats était un processus pénible de collecte de paquets ésotériques provenant d'un peu partout. Aujourd'hui, il se réduit à un seul appel de fonction. Sauvage.</em></p></li></ul><p>La commande <code>SimpleDirectoryReader</code> chargera chaque document contenu dans les fichiers <code>directory_path.</code> et <code>.pdf</code>. Elle renvoie une liste d'objets document, que je convertis en dictionnaires Python parce que je trouve qu'ils sont plus faciles à manipuler.</p># llamaindex_processor.py
from llama_index.core import SimpleDirectoryReader

class LlamaIndexProcessor:
   def __init__(self):
       pass 
   
   def load_documents(self, directory_path):
       ''' 
       Load all documents in directory
       '''
       reader = SimpleDirectoryReader(input_dir=directory_path)
       return reader.load_data()

# main.ipynb
llamaindex_processor=LlamaIndexProcessor()
documents=llamaindex_processor.load_documents('./documents/')
documents=[dict(doc_obj) for doc_obj in documents]
<p>Chaque dictionnaire contient le contenu clé du champ <code>text</code>. Il contient également des métadonnées utiles telles que le numéro de page, le nom du fichier, sa taille et son type.</p>{
  'id_': '5f76f0b3-22d8-49a8-9942-c2bbab14f63f',
  'metadata': {'page_label': '5',
   'file_name': 'Elastic_NV_Annual-Report-Fiscal-Year-2023.pdf',
   'file_path': '/Users/han/Desktop/Projects/truckasaurus/documents/Elastic_NV_Annual-Report-Fiscal-Year-2023.pdf',
   'file_type': 'application/pdf',
   'file_size': 3724426,
   'creation_date': '2024-07-27',
   'last_modified_date': '2024-07-27'},
   'text': 'Table of Contents\nPage\nPART I\nItem 1. Business 3\n15 Item 1A. Risk Factors\nItem 1B. Unresolved Staff Comments 48\nItem 2. Properties 48\nItem 3. Legal Proceedings 48\nItem 4. Mine Safety Disclosures 48\nPART II\nItem 5. Market for Registrant's Common Equity, Related Stockholder Matters and Issuer Purchases of \nEquity Securities49\nItem 6. [Reserved] 49\nItem 7. Management's Discussion and Analysis of Financial Condition and Results of Operations 50\nItem 7A. Quantitative and Qualitative Disclosures About Market Risk 64\nItem 8. Financial Statements and Supplementary Data 66\nItem 9. Changes in and Disagreements With Accountants on Accounting and Financial Disclosure 100\n100\n101Item 9A. Controls and Procedures\nItem 9B. Other Information\nItem 9C. Disclosure Regarding Foreign Jurisdictions That Prevent Inspections 101\nPART III\n102\n102\n102\n102Item 10. Directors, Executive Officers and Corporate Governance\nItem 11. Executive Compensation\nItem 12. Security Ownership of Certain Beneficial Owners and Management, and Related Stockholder Matters  \nItem 13. Certain Relationships and Related Transactions, and Director Independence\nItem 14. Principal Accountant Fees and Services 102\nPART IV\n103\n105Item 15. Exhibits and Financial Statement Schedules  \nItem 16. Form 10-K Summary\nSignatures 106\ni',
   ...
}
<p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#table-of-contents">Haut de page</a></p><h3>Chunking au niveau de la phrase, par jetons</h3><p>La première chose à faire est de réduire nos documents en morceaux d'une longueur standard (pour assurer la cohérence et la maniabilité). Les modèles d'intégration ont des limites de jetons uniques (taille d'entrée maximale qu'ils peuvent traiter). Les jetons sont les unités de base du texte que les modèles traitent. Pour éviter la perte d'informations (tronquage ou omission de contenu), nous devrions fournir un texte qui ne dépasse pas ces limites (en divisant les textes plus longs en segments plus petits).</p><p>Le découpage a un impact significatif sur les performances. Idéalement, chaque morceau devrait représenter un élément d'information autonome, capturant des informations contextuelles sur un seul sujet. Les méthodes de découpage comprennent le découpage au niveau des mots, où les documents sont divisés en fonction du nombre de mots, et le découpage sémantique qui utilise un LLM pour identifier les points de rupture logiques.</p><p>Le découpage au niveau des mots est bon marché, rapide et facile, mais il risque de diviser les phrases et donc de briser le contexte. Le découpage sémantique devient lent et coûteux, surtout s'il s'agit de documents tels que le rapport annuel Elastic de 116 pages.</p><p>Choisissons une approche intermédiaire. Le découpage en phrases est encore simple, mais il permet de préserver le contexte plus efficacement que le découpage en mots, tout en étant nettement moins coûteux et plus rapide. En outre, nous mettrons en œuvre une fenêtre coulissante pour capturer une partie du contexte environnant et atténuer l'impact du découpage des paragraphes.</p># chunker.py 

import uuid
import re


class Chunker: 
    def __init__(self, tokenizer):
        self.tokenizer = tokenizer 
    
    def split_into_sentences(self, text):
        """Split text into sentences."""
        return re.split(r'(?&lt;=[.!?])\s+', text)
 
    def sentence_wise_tokenized_chunk_documents(self, documents, chunk_size=512, overlap=20, min_chunk_size=50):
        '''
        1. Split text into sentences.
        2. Tokenize using the provided tokenizer method.
        3. Build chunks up to the chunk_size limit.
        4. Create an overlap based on tokens - to preserve context.
        5. Only keep chunks that meet the minimum token size requirement.
        '''
        chunked_documents = []

        for doc in documents:
            sentences = self.split_into_sentences(doc['text'])
            tokens = []
            sentence_boundaries = [0]

            # Tokenize all sentences and keep track of sentence boundaries
            for sentence in sentences:
                sentence_tokens = self.tokenizer.encode(sentence, add_special_tokens=True)
                tokens.extend(sentence_tokens)
                sentence_boundaries.append(len(tokens))

            # Create chunks
            chunk_start = 0
            while chunk_start &lt; len(tokens):
                chunk_end = chunk_start + chunk_size

                # Find the last complete sentence that fits in the chunk
                sentence_end = next((i for i in sentence_boundaries if i &gt; chunk_end), len(tokens))
                chunk_end = min(chunk_end, sentence_end)

                # Create the chunk
                chunk_tokens = tokens[chunk_start:chunk_end]

                # Check if the chunk meets the minimum size requirement
                if len(chunk_tokens) &gt;= min_chunk_size:
                    # Create a new document object for this chunk
                    chunk_doc = {
                        'id_': str(uuid.uuid4()),
                        'chunk': chunk_tokens,
                        'original_text': self.tokenizer.decode(chunk_tokens),
                        'chunk_index': len(chunked_documents),
                        'parent_id': doc['id_'],
                        'chunk_token_count': len(chunk_tokens)
                    }

                    # Copy all other fields from the original document
                    for key, value in doc.items():
                        if key != 'text' and key not in chunk_doc:
                            chunk_doc[key] = value

                    chunked_documents.append(chunk_doc)

                # Move to the next chunk start, considering overlap
                chunk_start = max(chunk_start + chunk_size - overlap, chunk_end - overlap)

        return chunked_documents

# main.ipynb 
# Initialize Embedding Model
HUGGINGFACE_EMBEDDING_MODEL = os.environ.get('HUGGINGFACE_EMBEDDING_MODEL')
embedder=EmbeddingModel(model_name=HUGGINGFACE_EMBEDDING_MODEL)

# Initialize Chunker
chunker=Chunker(embedder.tokenizer)
<p>La classe <code>Chunker</code> utilise le tokenizer du modèle d'intégration pour encoder et décoder le texte. Nous allons maintenant construire des blocs de 512 jetons chacun, avec un chevauchement de 20 jetons. Pour ce faire, nous divisons le texte en phrases, nous tokenisons ces phrases, puis nous ajoutons les phrases tokenisées à notre morceau actuel jusqu'à ce que nous ne puissions plus en ajouter sans dépasser notre limite de tokens.</p><p>Enfin, nous décodons les phrases pour les ramener au texte d'origine afin de les intégrer, en les stockant dans un champ appelé <code>original_text</code>. Les morceaux sont stockés dans un champ appelé <code>chunk</code>. Pour réduire le bruit (c'est-à-dire les documents inutiles), nous éliminons tous les documents dont la longueur est inférieure à 50 tokens.</p><p>Passons-le en revue nos documents :</p>chunked_documents=chunker.sentence_wise_tokenized_chunk_documents(documents, chunk_size=512)
<p>Et vous obtenez des morceaux de texte qui ressemblent à ceci :</p>print(chunked_documents[4]['original_text'])

[CLS] the aggregate market value of the ordinary shares held by non - affiliates of the registrant, 
based on the closing price of the shares of ordinary shares on the new york stock exchange on 
october 31, 2022 ( the last business day of the registrant 's second fiscal quarter ), was 
approximately $ 6. 1 billion. [SEP] [CLS] as of may 31, 2023, the registrant had 97, 390, 886 
ordinary shares, par value €0. 01 per share, outstanding. [SEP] [CLS] documents incorporated by 
reference portions of the registrant 's definitive proxy statement relating to the registrant 's 2
023 annual general meeting of shareholders are incorporated by reference into part iii of this annual 
...
...
<p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#table-of-contents">Haut de page</a></p><h3>Inclusion et génération de métadonnées</h3><p>Nous avons découpé nos documents en morceaux. Il est maintenant temps d'enrichir les données. Je souhaite générer ou extraire des métadonnées supplémentaires. Ces métadonnées supplémentaires peuvent être utilisées pour influencer et améliorer les performances de recherche.</p><p>Nous allons définir une classe <code>DocumentEnricher</code>, dont le rôle est de recevoir une liste de documents (dictionnaires Python) et une liste de fonctions du processeur. Ces fonctions s'exécutent sur la colonne <code>original_text</code> des documents et stockent leurs résultats dans de nouveaux champs.</p><p>Tout d'abord, nous extrayons les phrases clés à l'aide de <a href="https://github.com/elastic/elasticsearch-labs/blob/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques/nltk_processor.py">TextRank</a>. TextRank est un algorithme basé sur un graphe qui permet d'extraire des phrases et des expressions clés d'un texte en classant leur importance sur la base des relations entre les mots.</p><p>Ensuite, nous allons <a href="https://github.com/elastic/elasticsearch-labs/blob/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques/llm.py">générer des questions potentielles à l'aide de GPT-4o</a>.</p><p>Enfin, nous <a href="https://github.com/elastic/elasticsearch-labs/blob/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques/entity_extractor.py">extrairons les entités</a> à l'aide de <a href="https://spacy.io/">Spacy</a>.</p><p>Le code de chacun d'entre eux étant assez long et complexe, je m'abstiendrai de le reproduire ici. Si vous êtes intéressé, les fichiers sont indiqués dans les exemples de code ci-dessous.</p><p>Lançons l'enrichissement des données :</p># documentenricher.py
from tqdm import tqdm

class DocumentEnricher:

    def __init__(self):
        pass 

    def enrich_document(self, documents, processors, text_col='text'):
        for doc in tqdm(documents, desc="Enriching documents using processors: "+str(processors)): 
            for (processor, field) in processors: 
                metadata=processor(doc[text_col])
                if isinstance(metadata, list):
                    metadata='\n'.join(metadata)
                doc.update({field: metadata})
 
# main.ipynb
# Initialize processor classes 
nltkprocessor=NLTKProcessor() // nltk_processor.py
entity_extractor=EntityExtractor() // entity_extractor.py
gpt4o = LLMProcessor(model='gpt-4o') // llm.py

# Initialize LLM
documentenricher=DocumentEnricher()

# Create new fields in the documents - These are the outputs of the processor functions.
processors=[
    (nltkprocessor.textrank_phrases, "keyphrases"),
    (gpt4o.generate_questions, "potential_questions"),
    (entity_extractor.extract_entities, "entities")
    ]

# .enrich_document() will modify chunked_docs in place. 
# To view the results, we'll print chunked_docs in the next few cells!
documentenricher.enrich_document(chunked_docs, text_col='original_text', processors=processors)
<p>Et regardez les résultats :</p><h4>Phrases clés extraites par TextRank</h4><p>Ces phrases clés sont des substituts des thèmes centraux de la rubrique. Si une requête a trait à la cybersécurité, le score de ce morceau sera augmenté.</p>print(chunked_documents[25]['keyphrases'])

'elastic agent stop', 'agent stop malware', 
'stop malware ransomware', 'malware ransomware environment', 
'ransomware environment wide', 'environment wide visibility', 
'wide visibility threat', 'visibility threat detection', 
'sep cl key', 'cl key feature'
<h4>Questions potentielles générées par le GPT-4o</h4><p>Ces questions potentielles peuvent correspondre directement aux requêtes des utilisateurs, ce qui permet d'améliorer le score. Nous demandons à GPT-4o de générer des questions auxquelles il est possible de répondre en utilisant les informations trouvées dans le morceau actuel.</p>print(chunked_documents[25]['potential_questions'])

1. What are the primary functions that Elastic Agent provides in terms of cybersecurity?
2. Describe how Logstash contributes to data management within an IT environment.
3. List and explain any key features of Logstash mentioned in the document.
4. How does Elastic Agent enhance environment-wide visibility in threat detection?
5. What capabilities does Logstash offer for handling data beyond simple collection?
6. In what ways does the document suggest that Elastic Agent stops malware and ransomware?
7. Can you identify any relationships between the functionalities of Elastic Agent and Logstash in an integrated environment?
8. What implications might the advanced threat detection capabilities of Elastic Agent have for organizational security policies?
9. Compare and contrast the roles of Elastic Agent and Logstash based on their described functions.
10. How might the centralized collection ability of Logstash support the threat detection capabilities of Elastic Agent?
<h4>Entités extraites par Spacy</h4><p>Ces entités ont un objectif similaire à celui des phrases clés, mais elles capturent les noms des organisations et des individus, ce que l'extraction des phrases clés peut ne pas faire.</p>print(chunked_documents[29]['entities'])

'appdynamics', 'apm data', 'azure sentinel', 
'microsoft', 'mcafee', 'broadcom', 'cisco', 
'dynatrace', 'coveo', 'lucidworks'
<p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#table-of-contents">Haut de page</a></p><h3>Enchâssement composite de champs multiples</h3><p>Maintenant que nous avons enrichi nos documents avec des métadonnées supplémentaires, nous pouvons exploiter ces informations pour créer des encastrements plus robustes et tenant compte du contexte.</p><p>Faisons le point sur l'état actuel du processus. Nous avons quatre champs d'intérêt dans chaque document.</p>{
    "chunk": "...",
    "keyphrases": "...", 
    "potential_questions": "...", 
    "entities": "..." 
}
<p>Chaque champ représente une perspective différente sur le contexte du document, mettant potentiellement en évidence un domaine clé sur lequel le LLM devrait se concentrer.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt84cb328fce6aae23/6a170b42964cea3e4408bbc4/aea1f513009a0c7c8545a79fad8f072a5bcae24c-1440x1067.jpg" alt="Pipeline d'enrichissement des métadonnées dans RAG" /><p>Il s'agit d'intégrer chacun de ces champs, puis de créer une somme pondérée des intégrations, appelée intégration composite.</p><p>Avec un peu de chance, cette intégration composite permettra au système de mieux tenir compte du contexte, tout en introduisant un autre hyperparamètre réglable pour contrôler le comportement de recherche.</p><p>Tout d'abord, intégrons chaque champ et mettons à jour chaque document en place, en utilisant notre modèle d'intégration défini localement et importé au début du bloc-notes main.ipynb.</p># EmbeddingModel defined in embedding_model.py
embedder=EmbeddingModel(model_name=HUGGINGFACE_EMBEDDING_MODEL)

cols_to_embed=['keyphrases', 'potential_questions', 'entities']

embedding_cols=[]
for col in cols_to_embed:
    # Works on text input
    embedding_col=embedder.embed_documents_text_wise(chunked_documents, text_field=col)
    embedding_cols.append(embedding_col)
# Works on token input
embedding_col=embedder.embed_documents_token_wise(chunked_documents, token_field="chunk")
embedding_cols.append(embedding_col)
<p>Chaque fonction d'intégration renvoie le champ de l'intégration, qui est simplement le champ d'entrée original avec un postfixe <code>_embedding</code>.</p><p>Définissons maintenant les pondérations de notre encastrement composite :</p>embedding_cols=[
                'keyphrases_embedding',
                'potential_questions_embedding',
                'entities_embedding',
                'chunk_embedding']
combination_weights=[
                    0.1,
                    0.15,
                    0.05,
                    0.7
                ]
<p>Les pondérations vous permettent d'attribuer des priorités à chaque composant, en fonction de votre cas d'utilisation et de la qualité de vos données. Intuitivement, la taille de ces pondérations dépend de la valeur sémantique de chaque composant. Étant donné que le morceau de texte lui-même est de loin le plus riche, je lui attribue une pondération de 70%. Les entités étant les plus petites, puisqu'il s'agit simplement d'une liste de noms d'organisations ou de personnes, je leur attribue une pondération de 5%. Le réglage précis de ces valeurs doit être déterminé de manière empirique, au cas par cas.</p><p>Enfin, écrivons une fonction pour appliquer les pondérations et créer notre intégration composite. Pour gagner de la place, nous supprimerons également tous les composants intégrés.</p>from tqdm import tqdm 
def combine_embeddings(objects, embedding_cols, combination_weights, primary_embedding='primary_embedding'):
    # Ensure the number of weights matches the number of embedding columns
    assert len(embedding_cols) == len(combination_weights), "Number of embedding columns must match number of weights"
    
    # Normalize weights to sum to 1
    weights = np.array(combination_weights) / np.sum(combination_weights)
    
    for obj in tqdm(objects, desc="Combining embeddings"):
        # Initialize the combined embedding
        combined = np.zeros_like(obj[embedding_cols[0]])
        
        # Compute the weighted sum
        for col, weight in zip(embedding_cols, weights):
            combined += weight * np.array(obj[col])
        
        # Add the new combined embedding to the object
        obj.update({primary_embedding:combined.tolist()})
        
        # Remove the original embedding columns
        for col in embedding_cols:
            obj.pop(col, None)

combine_embeddings(chunked_documents, embedding_cols, combination_weights)
<p>Nous avons ainsi terminé le traitement des documents. Nous disposons à présent d'une liste d'objets documents qui se présente comme suit :</p>{ 'id_': '7fe71686-5cd0-4831-9e79-998c6dbeae0c', 'chunk': [2312, 14613, ...], 'original_text': 'if an emerging growth company, indicate by check mark if the registrant has elected not to use the extended ...', 'chunk_index': 3, 'chunk_token_count': 399, 'metadata': {'page_label': '3', 'file_name': 'Elastic_NV_Annual-Report-Fiscal-Year-2023.pdf', ... 'keyphrases': 'sep cl unk\ncheck mark registrant\ncl unk indicate\nunk indicate check\nindicate check mark\nprincipal executive office\naccelerate filer unk\ncompany unk emerge\nunk emerge growth\nemerge growth company', 'potential_questions': '1. What are the different types of registrant statuses mentioned in the document?\n2. Under what section of the Sarbanes-Oxley Act must registrants file a report on the effectiveness of their internal ...', 'entities': 'the effe ctiveness of\nsection 13\nSEP\nUNK\nsection 21e\n1934\n1933\nu. s. c.\nsection 404\nsection 12\nal', 'primary_embedding': [-0.3946287803351879, -0.17586839850991964, ...] }
<h4>Indexation vers Elastic</h4><p>Chargeons nos documents en vrac dans Elastic Search. À cette fin, j'ai défini il y a longtemps un ensemble de fonctions Elastic Helper dans <a href="https://github.com/elastic/elasticsearch-labs/blob/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques/elastic_helpers.py"><code>elastic_helpers.py</code></a>. Il s'agit d'un code très long, nous allons donc nous contenter d'examiner les appels de fonction.</p><p><code>es_bulk_indexer.bulk_upload_documents</code> fonctionne avec n'importe quelle liste d'objets dictionnaires, en tirant parti des mappages dynamiques pratiques d'Elasticsearch.</p># Initialize Elasticsearch
ELASTIC_CLOUD_ID = os.environ.get('ELASTIC_CLOUD_ID')
ELASTIC_USERNAME = os.environ.get('ELASTIC_USERNAME')
ELASTIC_PASSWORD = os.environ.get('ELASTIC_PASSWORD')
ELASTIC_CLOUD_AUTH = (ELASTIC_USERNAME, ELASTIC_PASSWORD)
es_bulk_indexer = ESBulkIndexer(cloud_id=ELASTIC_CLOUD_ID, credentials=ELASTIC_CLOUD_AUTH)
es_query_maker = ESQueryMaker(cloud_id=ELASTIC_CLOUD_ID, credentials=ELASTIC_CLOUD_AUTH)

# Define Index Name
index_name=os.environ.get('ELASTIC_INDEX_NAME')


# Create index and bulk upload 
index_exists = es_bulk_indexer.check_index_existence(index_name=index_name)
if not index_exists:
    logger.info(f"Creating new index: {index_name}")
    es_bulk_indexer.create_es_index(es_configuration=BASIC_CONFIG, index_name=index_name)

success_count = es_bulk_indexer.bulk_upload_documents(
    index_name=index_name, 
    documents=chunked_documents, 
    id_col='id_',
    batch_size=32
)
<p>Rendez-vous sur Kibana et vérifiez que tous les documents ont été indexés. Il devrait y en avoir 224. Pas mal pour un document aussi volumineux !</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8efeface6effe01d/6a170b447d8d67652870e72a/1b3b07f6b98ceb65f6594ce4be83c5b0ed7e7cf9-1440x1380.jpg" alt="Index Kibana" /><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#table-of-contents">Haut de page</a></p><h2>Pause-catalogues</h2><p>Faisons une pause, l'article est un peu lourd, je sais. Jetez un coup d'œil à mon chat :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc1db5595f71c12ff/6a170b450e2e49940241a0fe/baca4eb52b801b21ced97352cc55462f0a12d6b0-969x996.jpg" alt="Pipeline de Han" /><p>Adorable. Le chapeau a disparu et je soupçonne à moitié qu'elle l'a volé et caché quelque part :(</p><p>Félicitations pour avoir réussi à aller aussi loin :)</p><p>Rejoignez-moi dans la <a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2">deuxième partie</a> pour tester et évaluer notre pipeline RAG !</p><h2>Annexe</h2><h3>Définitions</h3><p><strong>1. Découpage des phrases</strong></p><ul><li><p>Technique de prétraitement utilisée dans les systèmes RAG pour diviser le texte en unités plus petites et significatives.</p></li><li><p><em>Processus :</em> </p><ol><li><p>Entrée : Grand bloc de texte (par exemple, document, paragraphe)</p></li><li><p>Sortie : Segments de texte plus petits (généralement des phrases ou des petits groupes de phrases)</p></li></ol></li><li><p><em>Objet :</em> </p><ul><li><p>Création de segments de texte granulaires et spécifiques au contexte</p></li><li><p>Permet une indexation et une recherche plus précises</p></li><li><p>Améliore la pertinence des informations recherchées dans les systèmes RAG</p></li></ul></li><li><p><em>Caractéristiques :</em> </p><ul><li><p>Les segments sont sémantiquement significatifs</p></li><li><p>Peuvent être indexés et récupérés de manière indépendante</p></li><li><p>Souvent, le contexte est préservé afin de garantir la compréhensibilité autonome.</p></li></ul></li><li><p><em>Avantages :</em> </p><ul><li><p>Améliore la précision de la recherche</p></li><li><p>Permet une augmentation plus ciblée des pipelines RAG</p></li></ul></li></ul><p><strong>2. HyDE (Hypothetical Document Embedding)</strong></p><ul><li><p>Une technique qui utilise un LLM pour générer un document hypothétique pour l'expansion des requêtes dans les systèmes RAG.</p></li><li><p><em>Processus :</em>  </p><ol><li><p>Requête d'entrée à un LLM</p></li><li><p>LLM génère un document hypothétique répondant à la requête</p></li><li><p>Intégrer le document généré</p></li><li><p>Utiliser l'intégration pour la recherche vectorielle</p></li></ol></li><li><p><em>Différence essentielle :</em> </p><ul><li><p>RAG traditionnel : Correspondance entre la requête et les documents</p></li><li><p>HyDE : fait correspondre des documents à d'autres documents</p></li></ul></li><li><p><em>Objet :</em> </p><ul><li><p>Améliorer les performances de recherche, en particulier pour les requêtes complexes ou ambiguës</p></li><li><p>Saisir un contexte sémantique plus riche qu'une requête courte</p></li></ul></li><li><p><em>Avantages :</em> </p><ul><li><p>Exploite les connaissances du LLM pour élargir les requêtes</p></li><li><p>Peut potentiellement améliorer la pertinence des documents retrouvés</p></li></ul></li><li><p><em>Défis :</em> </p><ul><li><p>Nécessite une inférence LLM supplémentaire, ce qui augmente le temps de latence et le coût.</p></li><li><p>La performance dépend de la qualité du document hypothétique généré</p></li></ul></li></ul><p><strong>3. Emballage inversé</strong></p><ul><li><p>Technique utilisée dans les systèmes RAG pour réorganiser les résultats de la recherche avant de les transmettre au LLM.</p></li><li><p><em>Processus :</em> </p><ol><li><p>Le moteur de recherche (par exemple, Elasticsearch) renvoie les documents par ordre décroissant de pertinence.</p></li><li><p>L'ordre est inversé, le document le plus pertinent étant placé en dernier.</p></li></ol></li><li><p><em>Objet :</em> </p><ul><li><p>Exploite le biais de récence des LLM, qui ont tendance à se concentrer sur les informations les plus récentes dans leur contexte.</p></li><li><p>Veille à ce que les informations les plus pertinentes soient "les plus récentes" dans la fenêtre contextuelle du LLM.</p></li></ul></li><li><p><em>Exemple :</em> Ordre original : [Plus pertinent, Deuxième plus important, Troisième plus important, ...] Ordre inversé : [..., Troisième plus important, Deuxième plus important, Plus important]</p></li></ul><p><strong>4. Classification des requêtes</strong></p><ul><li><p>Technique permettant d'optimiser l'efficacité du système RAG en déterminant si une requête nécessite un RAG ou si elle peut être traitée directement par le LLM.</p></li><li><p><em>Processus :</em> </p><ol><li><p>Développer un ensemble de données personnalisé spécifique au programme d'éducation et de formation tout au long de la vie utilisé</p></li><li><p>Former un modèle de classification spécialisé</p></li><li><p>Utiliser le modèle pour catégoriser les requêtes entrantes</p></li></ol></li><li><p><em>Objet :</em> </p><ul><li><p>Améliorer l'efficacité du système en évitant le traitement inutile des RAG</p></li><li><p>Diriger les demandes vers le mécanisme de réponse le plus approprié</p></li></ul></li><li><p><em>Exigences :</em> </p><ul><li><p>Ensemble de données et modèle spécifiques au LLM</p></li><li><p>Amélioration continue pour maintenir la précision</p></li></ul></li><li><p><em>Avantages :</em> </p><ul><li><p>Réduction de la charge de calcul pour les requêtes simples</p></li><li><p>Amélioration potentielle du temps de réponse pour les requêtes non RAG</p></li></ul></li></ul><p><strong>5. Résumé</strong></p><ul><li><p>Une technique pour condenser les documents récupérés dans les systèmes RAG.</p></li><li><p><em>Processus :</em> </p><ol><li><p>Récupérer les documents pertinents</p></li><li><p>Générer des résumés concis de chaque document</p></li><li><p>Utiliser des résumés plutôt que des documents complets dans le pipeline RAG</p></li></ol></li><li><p><em>Objet :</em> </p><ul><li><p>Améliorer la performance du RAG en se concentrant sur les informations essentielles</p></li><li><p>Réduire le bruit et les interférences provenant de contenus moins pertinents</p></li></ul></li><li><p><em>Avantages :</em> </p><ul><li><p>Amélioration potentielle de la pertinence des réponses au programme d'éducation et de formation tout au long de la vie</p></li><li><p>Permet d'inclure un plus grand nombre de documents dans les limites du contexte</p></li></ul></li><li><p><em>Défis :</em> </p><ul><li><p>Risque de perdre des détails importants dans le résumé</p></li><li><p>Frais de calcul supplémentaires pour la génération du résumé</p></li></ul></li></ul><p><strong>6. Inclusion de métadonnées</strong></p><ul><li><p>Une technique pour enrichir les documents avec des informations contextuelles supplémentaires.</p></li><li><p><em>Types de métadonnées :</em>  </p><ul><li><p>Mots clés</p></li><li><p>Titres</p></li><li><p>Dates</p></li><li><p>Coordonnées de l'auteur</p></li><li><p>Les commentaires</p></li></ul></li><li><p><em>Objet :</em> </p><ul><li><p>Augmenter les informations contextuelles disponibles pour le système RAG</p></li><li><p>Fournir aux gestionnaires du droit d'auteur une meilleure compréhension du contenu et de la pertinence des documents</p></li></ul></li><li><p><em>Avantages :</em> </p><ul><li><p>Amélioration potentielle de la précision de la recherche</p></li><li><p>Améliore la capacité du LLM à évaluer l'utilité des documents</p></li></ul></li><li><p><em>Mise en œuvre :</em> </p><ul><li><p>Peut être effectué lors du prétraitement des documents</p></li><li><p>Peut nécessiter des étapes supplémentaires d'extraction ou de génération de données</p></li></ul></li></ul><p><strong>7. Intégrations composites multi-champs</strong></p><ul><li><p>Une technique d'intégration avancée pour les systèmes RAG qui crée des intégrations distinctes pour les différents composants du document.</p></li><li><p><em>Processus :</em> </p><ol><li><p>Identifier les champs pertinents (par exemple, le titre, les phrases clés, le résumé, le contenu principal)</p></li><li><p>Générer des embeddings distincts pour chaque champ</p></li><li><p>Combiner ou stocker ces encastrements pour les utiliser lors de la recherche.</p></li></ol></li><li><p><em>Différence par rapport à l'approche standard :</em> </p><ul><li><p>Traditionnel : Intégration unique pour l'ensemble du document</p></li><li><p>Composite : Plusieurs encastrements pour différents aspects du document</p></li></ul></li><li><p><em>Objet :</em> </p><ul><li><p>Créer des représentations de documents plus nuancées et tenant compte du contexte</p></li><li><p>Saisir des informations provenant d'une plus grande variété de sources dans un document</p></li></ul></li><li><p><em>Avantages :</em> </p><ul><li><p>Amélioration potentielle des performances sur les requêtes ambiguës ou à multiples facettes</p></li><li><p>Permet une pondération plus souple des différents aspects du document dans la recherche.</p></li></ul></li><li><p><em>Défis :</em> </p><ul><li><p>Complexité accrue de l'intégration des processus de stockage et d'extraction</p></li><li><p>Peut nécessiter des algorithmes d'appariement plus sophistiqués</p></li></ul></li></ul><p><strong>8. Enrichissement des requêtes</strong></p><ul><li><p>Une technique qui consiste à ajouter des termes connexes à la requête initiale afin d'améliorer la couverture de la recherche.</p></li><li><p><em>Processus :</em> </p><ol><li><p>Analyser la requête originale</p></li><li><p>Générer des synonymes et des phrases sémantiquement proches</p></li><li><p>Complétez la requête avec ces termes supplémentaires</p></li></ol></li><li><p><em>Objet :</em> </p><ul><li><p>Augmenter l'éventail des correspondances potentielles dans le corpus de documents</p></li><li><p>Améliorer les performances de recherche pour les requêtes formulées dans un langage spécifique ou technique</p></li></ul></li><li><p><em>Avantages :</em> </p><ul><li><p>Possibilité de retrouver des documents pertinents qui ne correspondent pas exactement aux termes de la requête initiale.</p></li><li><p>Peut aider à surmonter l'inadéquation du vocabulaire entre les requêtes et les documents</p></li></ul></li><li><p><em>Défis :</em> </p><ul><li><p>Risque de dérive des requêtes en l'absence d'une mise en œuvre rigoureuse</p></li><li><p>Peut augmenter la charge de calcul dans le processus de recherche.</p></li></ul></li></ul><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#table-of-contents">Haut de page</a></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1</guid>
    <category><![CDATA[Base vectorielle]]></category>
    <category><![CDATA[IA]]></category>
    <dc:creator><![CDATA[Han Xiang Choong]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9a4691874a19d8da/6a170b3f47d49c99f22d8a24/72b51ba2ae5e5977b56e5b915674753d6cfd0e56-1440x840.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 14 Aug 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Protéger les informations sensibles et PII dans RAG avec Elasticsearch et LlamaIndex]]></title>
    <description><![CDATA[Comment protéger les données sensibles et PII dans une application RAG avec Elasticsearch et LlamaIndex.]]></description>
    <content:encoded><![CDATA[<p></p><p></p><p>Dans ce billet, nous examinerons les moyens de protéger les informations personnelles identifiables (PII) et les données sensibles lors de l'utilisation de LLM publics dans un flux RAG (Retrieval Augmented Generation). Nous explorerons le masquage des IIP et des données sensibles à l'aide de bibliothèques open source et d'expressions régulières, ainsi que l'utilisation de LLM locaux pour masquer les données avant d'invoquer un LLM public.</p><p>Avant de commencer, rappelons la terminologie utilisée dans ce billet.</p><h2>Terminologie</h2><p><a href="https://www.llamaindex.ai/">LlamaIndex</a> est un cadre de données de premier plan pour la création d'applications LLM (Large Language Model). LlamaIndex fournit des abstractions pour les différentes étapes de la construction d'une application RAG (Retrieval Augmented Generation). Des structures telles que LlamaIndex et LangChain fournissent des abstractions afin que les applications ne soient pas étroitement liées aux API d'un LLM spécifique.</p><p><a href="https://www.elastic.co/enterprise-search">Elasticsearch</a> est proposé par <a href="https://elastic.co/">Elastic</a>. Elastic est un leader de l'industrie avec Elasticsearch, un magasin de données évolutif et une base de données vectorielle qui prend en charge la recherche en texte intégral pour la précision, la recherche vectorielle pour la compréhension sémantique, et la recherche hybride pour le meilleur des deux mondes. Elasticsearch est un moteur de recherche et d'analyse RESTful distribué, un magasin de données évolutif et une base de données vectorielle. Les fonctionnalités d'Elasticsearch que nous utilisons dans ce blog sont disponibles dans la version gratuite et ouverte d'Elasticsearch.</p><p>La <a href="https://www.promptingguide.ai/techniques/rag">génération assistée par récupération (RAG)</a> est une technique ou un modèle d'IA dans lequel les LLM sont dotés de connaissances externes pour générer des réponses aux questions des utilisateurs. Cela permet d'adapter les réponses du programme d'éducation et de formation tout au long de la vie à un contexte spécifique et de les rendre moins génériques.</p><p>Les <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.13/semantic-search.html">encastrements</a> sont des représentations numériques de la signification d'un texte ou d'un média. Il s'agit de représentations de dimensions inférieures d'informations de dimensions supérieures.</p><h2>RAG et protection des données</h2><p>En règle générale, les grands modèles linguistiques (LLM) permettent de générer des réponses sur la base des informations disponibles dans le modèle, qui peut être formé à partir de données Internet. Toutefois, pour les questions pour lesquelles les informations ne sont pas disponibles dans le modèle, les MFR doivent être alimentés par des connaissances externes ou des détails spécifiques qui ne sont pas contenus dans le modèle. Ces informations peuvent se trouver dans votre base de données ou dans votre système de connaissances interne. La génération améliorée par récupération (RAG) est une technique dans laquelle, pour une requête d'utilisateur donnée, vous récupérez d'abord le contexte/l'information pertinente des systèmes externes (au LLM) (par exemple votre base de données) et envoyez ce contexte avec la requête d'utilisateur au LLM pour générer une réponse plus spécifique et plus pertinente.</p><p>Cela rend la technique RAG très efficace pour les applications de réponse aux questions, de création de contenu et partout où une compréhension approfondie du contexte et des détails est bénéfique.</p><p>Par conséquent, dans une filière RAG, vous courez le risque d'exposer des informations internes telles que des PII (informations personnelles identifiables) et des informations sensibles (par exemple des noms, des dates de naissance, des numéros de compte, etc.</p><p>Bien que vos données soient sécurisées lorsque vous utilisez une base de données vectorielle comme Elasticsearch (grâce à divers leviers tels que le <a href="https://www.elastic.co/guide/en/cloud-enterprise/current/ece-configure-rbac.html">contrôle d'accès basé sur les rôles</a>, la <a href="https://www.elastic.co/search-labs/blog/dls-internal-knowledge-search">sécurité au niveau du document</a>, etc.), il convient d'être prudent lorsque vous envoyez des données à l'extérieur vers un LLM public.</p><p>La protection des informations d'identification personnelle (PII) et des données sensibles est cruciale lors de l'utilisation de grands modèles de langage (LLM), et ce pour plusieurs raisons :</p><ul><li><p><strong>Conformité en matière de protection de la vie privée</strong>: De nombreuses régions disposent de réglementations strictes, telles que le règlement général sur la protection des données (RGPD) en Europe ou le California Consumer Privacy Act (CCPA) aux États-Unis, qui imposent la protection des données personnelles. Le respect de ces lois est nécessaire pour éviter les conséquences juridiques et les amendes.</p></li><li><p><strong>Confiance des utilisateurs</strong>: Garantir la confidentialité et l'intégrité des informations sensibles permet de renforcer la confiance des utilisateurs. Les utilisateurs sont plus enclins à utiliser et à interagir avec des systèmes dont ils pensent qu'ils protègent leur vie privée.</p></li><li><p><strong>Sécurité des données</strong>: La protection contre les violations de données est essentielle. Les données sensibles exposées aux MLD sans garanties adéquates peuvent faire l'objet d'un vol ou d'une utilisation abusive, entraînant des dommages potentiels tels que l'usurpation d'identité ou la fraude financière.</p></li><li><p><strong>Considérations éthiques</strong>: D'un point de vue éthique, il est important de respecter la vie privée des utilisateurs et de traiter leurs données de manière responsable. Un mauvais traitement des IPI peut conduire à la discrimination, à la stigmatisation ou à d'autres conséquences négatives pour la société.</p></li><li><p><strong>Réputation de l'entreprise</strong>: Les entreprises qui ne protègent pas les données sensibles peuvent voir leur réputation entachée, ce qui peut avoir des effets négatifs à long terme sur leurs activités, notamment la perte de clients et de revenus.</p></li><li><p><strong>Réduction des risques d'abus</strong>: Le traitement sécurisé des données sensibles permet d'éviter l'utilisation malveillante des données ou du modèle, comme l'entraînement des modèles sur des données biaisées ou l'utilisation des données pour manipuler ou nuire à des personnes.</p></li></ul><p>Dans l'ensemble, une protection solide des IPI et des données sensibles est nécessaire pour garantir la conformité légale, maintenir la confiance des utilisateurs, assurer la sécurité des données, respecter les normes éthiques, protéger la réputation de l'entreprise et réduire le risque d'abus.</p><h2>Récapitulatif rapide</h2><p>Dans l'<a href="https://www.elastic.co/search-labs/blog/rag-with-llamaIndex-and-elasticsearch">article précédent</a>, nous avons expliqué comment mettre en œuvre l'expérience Q&amp;A en utilisant une technique RAG avec Elasticsearch comme base de données vectorielle tout en utilisant LlamaIndex et un Mistral LLM fonctionnant localement. Ici, nous nous appuyons sur cette base.</p><p>La lecture de l'article précédent est facultative car nous allons maintenant discuter/récapituler rapidement ce que nous avons fait dans l'article précédent.</p><p>Nous disposions d'un échantillon de données de conversations de centre d'appel entre des agents et des clients d'une compagnie d'assurance habitation fictive. Nous avons créé une application RAG simple qui répond à des questions telles que "Quels sont les problèmes liés à l'eau pour lesquels les clients déposent des réclamations ?</p><p>Voici comment se présentait le flux à un niveau élevé.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9d3882da43bfdac0/6a17050867045bd5fe45c0e9/9d51295472f8bcca3d1973248acb724f8b94767e-1054x555.png" alt="Flux RAG" /><p>Pendant la phase d'indexation, nous avons chargé et indexé des documents en utilisant le pipeline LlamaIndex. Les documents ont été regroupés et stockés dans la base de données vectorielles Elasticsearch avec leur intégration.</p><p>Pendant la phase d'interrogation, lorsque l'utilisateur pose une question, LlamaIndex récupère les documents similaires les plus pertinents par rapport à l'interrogation. Ces documents les plus pertinents, accompagnés de la requête, ont été envoyés au Mistral LLM local, qui a ensuite généré la réponse à renvoyer à l'utilisateur. N'hésitez pas à consulter l'article précédent ou à <a href="https://github.com/srikanthmanvi/RAG-InsuranceCompany/tree/main">explorer le code</a>.</p><p>Dans l'article précédent, le LLM fonctionnait localement. Cependant, en production, vous pouvez vouloir utiliser un LLM externe fourni par diverses entreprises comme <a href="https://openai.com/">OpenAI</a>, <a href="https://mistral.ai/">Mistral</a>, <a href="https://www.anthropic.com/claude">Anthropic</a>, etc. Cela peut être dû au fait que votre cas d'utilisation nécessite un modèle de base plus important ou que l'exécution locale n'est pas une option en raison des besoins de production de l'entreprise tels que l'évolutivité, la disponibilité, les performances, etc.</p><p>L'introduction d'un LLM externe dans votre pipeline RAG vous expose à un risque de fuite involontaire de données sensibles et d'informations confidentielles vers les LLM. Dans cet article, nous allons explorer les options permettant de masquer les informations PII dans le cadre de votre processus RAG avant d'envoyer des documents à un LLM externe.</p><h2>RAG avec un LLM public</h2><p>Avant d'aborder la question de la protection des informations confidentielles et sensibles dans un pipeline RAG, nous allons d'abord construire une application RAG simple utilisant LlamaIndex, la base de données vectorielle Elasticsearch et OpenAI LLM.</p><h3>Produits requis</h3><p>Nous aurons besoin des éléments suivants.</p><ul><li><p><strong>Elasticsearch</strong> est opérationnel en tant que base de données vectorielle pour le stockage des embeddings. Suivez les instructions de l'article précédent sur l'<a href="https://www.elastic.co/search-labs/blog/rag-with-llamaIndex-and-elasticsearch#install-elasticsearch">installation d'Elasticsearch</a>.</p></li><li><p>Clés d'API ouvertes pour l'IA.</p></li></ul><h3>Application simple du RAG</h3><p>Pour référence, le code entier peut être trouvé dans ce <a href="https://github.com/srikanthmanvi/RAG-InsuranceCompany/tree/protecting-pii">dépôt Github</a>(branch:protecting-pii). Le clonage du dépôt est facultatif car nous allons parcourir le code ci-dessous.</p><p>Dans votre IDE préféré, créez une nouvelle application Python avec les 3 fichiers ci-dessous.</p><ul><li><p><code>index.py</code> où se trouve le code lié à l'indexation des données.</p></li><li><p><code>query.py</code> où se trouve le code lié à l'interrogation et à l'interaction avec le LLM.</p></li><li><p><code>.env</code> où se trouvent les propriétés de configuration telles que les clés d'API.</p></li></ul><p>Nous devons installer quelques paquets. Nous commençons par créer un nouvel <a href="https://docs.python.org/3/library/venv.html">environnement virtuel</a> python dans le dossier racine de votre application.</p>python3 -m venv .venv
<p>Activez l'environnement virtuel et installez les paquets requis ci-dessous.</p>source .venv/bin/activate
pip install llama-index 
pip install llama-index-embeddings-openai
pip install llama-index-vector-stores-elasticsearch
pip install sentence-transformers
pip install python-dotenv
pip install openai
<p>Configurer les propriétés de connexion d'OpenAI et d'Elasticsearch dans le fichier .env fichier.</p>OPENAI_API_KEY="REPLACEME"
ELASTIC_CLOUD_ID="REPLACEME"
ELASTIC_API_KEY="REPLACEME"
<h4>Indexation des données</h4><p>Téléchargez le fichier <a href="https://github.com/srikanthmanvi/RAG-InsuranceCompany/blob/main/conversations.json">conversations.json</a> qui contient les <em>conversations</em> entre les clients et les agents du centre d'appel de notre compagnie d'assurance habitation fictive. Placez le fichier dans le répertoire racine de l'application avec les 2 fichiers python et le fichier .env. que vous avez créé précédemment. Vous trouverez ci-dessous un exemple du contenu du fichier.</p>{
"conversation_id": 103,
"customer_name": "Sophia Jones",
"agent_name": "Emily Wilson",
"policy_number": "JKL0123",
"conversation": "Customer: Hi, I'm Sophia Jones. My Date of Birth is November 15th, 1985, Address is 303 Cedar St, Miami, FL 33101, and my Policy Number is JKL0123.\nAgent: Hello, Sophia. How may I assist you today?\nCustomer: Hello, Emily. I have a question about my policy.\nCustomer: There's been a break-in at my home, and some valuable items are missing. Are they covered?\nAgent: Let me check your policy for coverage related to theft.\nAgent: Yes, theft of personal belongings is covered under your policy.\nCustomer: That's a relief. I'll need to file a claim for the stolen items.\nAgent: We'll assist you with the claim process, Sophia. Is there anything else I can help you with?\nCustomer: No, that's all for now. Thank you for your assistance, Emily.\nAgent: You're welcome, Sophia. Please feel free to reach out if you have any further questions or concerns.\nCustomer: I will. Have a great day!\nAgent: You too, Sophia. Take care.",
"summary": "A customer inquires about coverage for stolen items after a break-in at home, and the agent confirms that theft of personal belongings is covered under the policy. The agent offers assistance with the claim process, resulting in the customer expressing relief and gratitude."
}
<p>Passez le code ci-dessous dans <code>index.py</code> qui se charge de l'indexation des données.</p># index.py
# pip install sentence-transformers
# pip install llama-index-embeddings-openai
# pip install llama-index-embeddings-huggingface

import json
import os
from dotenv import load_dotenv
from llama_index.core import Document
from llama_index.core import Settings
from llama_index.core.ingestion import IngestionPipeline
from llama_index.core.node_parser import SentenceSplitter
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.vector_stores.elasticsearch import ElasticsearchStore


def get_documents_from_file(file):
   """Reads a json file and returns list of Documents"""

   with open(file=file, mode='rt') as f:
       conversations_dict = json.loads(f.read())

   # Build Document objects using fields of interest.
   documents = [Document(text=item['conversation'],
                         metadata={"conversation_id": item['conversation_id']})
                for
                item in conversations_dict]
   return documents

# Load .env file contents into env
load_dotenv('.env')
Settings.embed_model = HuggingFaceEmbedding(
   model_name="BAAI/bge-small-en-v1.5"
)

def main():
   # ElasticsearchStore is a VectorStore that
   # takes care of Elasticsearch Index and Data management.
   es_vector_store = ElasticsearchStore(index_name="convo_index",
                                        vector_field='conversation_vector',
                                        text_field='conversation',
                                        es_cloud_id=os.getenv("ELASTIC_CLOUD_ID"),
                                        es_api_key=os.getenv("ELASTIC_API_KEY"))

   # LlamaIndex Pipeline configured to take care of chunking, embedding
   # and storing the embeddings in the vector store.
   llamaindex_pipeline = IngestionPipeline(
       transformations=[
           SentenceSplitter(chunk_size=350, chunk_overlap=50),
           Settings.embed_model
       ],
       vector_store=es_vector_store
   )

   # Load data from a json file into a list of LlamaIndex Documents
   documents = get_documents_from_file(file="conversations.json")
   llamaindex_pipeline.run(documents=documents)
   print(".....Indexing Data Completed.....\n")

if __name__ == "__main__":
   main()
<p>L'exécution du code ci-dessus crée un index dans Elasticsearch, stocke les embeddings dans l'index Elasticsearch nommé <code>convo_index</code>.</p><p>Si vous avez besoin d'explications sur la LlamaIndex IngestionPipeline, veuillez vous référer à l'article précédent dans la section <a href="https://www.elastic.co/search-labs/blog/rag-with-llamaIndex-and-elasticsearch#indexing-data">Create IngestionPipeline</a>.</p><h4>Interrogation</h4><p>Dans l'article précédent, nous avons utilisé un LLM local pour les <a href="https://www.elastic.co/search-labs/blog/rag-with-llamaIndex-and-elasticsearch#querying">requêtes</a>.</p><p>Dans ce billet, nous utilisons le LLM public, OpenAI, comme indiqué ci-dessous.</p># query.py
from llama_index.core import VectorStoreIndex, QueryBundle, Settings
from llama_index.llms.openai import OpenAI
from index import es_vector_store

# Public LLM where we send user query and Related Documents
llm = OpenAI()

index = VectorStoreIndex.from_vector_store(es_vector_store)

# This query_engine, for a given user query retrieves top 10 similar documents from
# Elasticsearch vector database and sends the documents along with the user query to the LLM.
# Note that documents are sent as-is. So any PII/Sensitive data is sent to the LLM.
query_engine = index.as_query_engine(llm, similarity_top_k=10)

query="Give me summary of water related claims that customers raised."
bundle = QueryBundle(query, embedding=Settings.embed_model.get_query_embedding(query))
result = query_engine.query(bundle)
print(result)
<p>Le code ci-dessus affiche la réponse d'OpenAI comme suit.</p><p>Les clients ont fait part de diverses réclamations liées à l'eau, notamment des dégâts des eaux dans les sous-sols, des éclatements de canalisations, des dommages causés aux toits par la grêle, et des refus de demandes d'indemnisation pour des raisons telles que l'absence de notification en temps utile, des problèmes d'entretien, l'usure progressive et les dommages préexistants. Dans chaque cas, les clients ont exprimé leur frustration face aux refus de demandes d'indemnisation et ont demandé des évaluations et des décisions équitables concernant leurs demandes d'indemnisation.</p><h2>Masquage des IIP dans le RAG</h2><p>Ce que nous avons couvert jusqu'à présent consiste à envoyer des documents tels quels à OpenAI avec la requête de l'utilisateur.</p><p>Dans le pipeline RAG, une fois que le contexte pertinent est extrait d'un magasin vectoriel, nous avons la possibilité de masquer les IIP et les informations sensibles avant d'envoyer la requête et le contexte au mécanisme d'apprentissage tout au long de la vie.</p><p>Il existe plusieurs façons de masquer les informations PII avant de les envoyer à un MLD externe, chacune d'entre elles ayant ses propres mérites. Nous examinons ci-dessous quelques-unes des options possibles</p><ol><li><p>Utilisation de bibliothèques NLP comme spacy.io ou <a href="https://microsoft.github.io/presidio/">Presidio</a> (bibliothèque open source gérée par Microsoft).</p></li><li><p>Utiliser LlamaIndex prêt à l'emploi <code>NERPIINodePostprocessor.</code></p></li><li><p>Utilisation de LLM locaux via <code>PIINodePostprocessor</code></p></li></ol><p>Une fois que vous avez implémenté la logique de masquage en utilisant l'une des méthodes ci-dessus, vous pouvez configurer la LlamaIndex IngestionPipeline avec un PostProcessor (votre propre PostProcessor personnalisé ou l'un des PostProcessors prêts à l'emploi de LlamaIndex).</p><h3>Utilisation des bibliothèques NLP</h3><p>Dans le cadre du pipeline RAG, nous pouvons masquer les données sensibles à l'aide de bibliothèques NLP. Nous utiliserons le paquet spacy.io dans cette démonstration.</p><p>Créez un nouveau fichier <code>query_masking_nlp.py</code> et ajoutez le code ci-dessous.</p># query_masking_nlp.py

# pip install spacy
# python3 - m spacy download en_core_web_sm
import re
from typing import List, Optional

import spacy
from llama_index.core import VectorStoreIndex, QueryBundle, Settings
from llama_index.core.postprocessor.types import BaseNodePostprocessor
from llama_index.core.schema import NodeWithScore
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.llms.openai import OpenAI
from index import es_vector_store

# Load the spaCy model
nlp = spacy.load("en_core_web_sm")

# Compile regex patterns for performance
phone_pattern = re.compile(r'\b\d{3}[-.]?\d{3}[-.]?\d{4}\b')
email_pattern = re.compile(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b')
date_pattern = re.compile(r'\b(\d{1,2}[-/]\d{1,2}[-/]\d{2,4}|\d{2,4}[-/]\d{1,2}[-/]\d{1,2})\b')
dob_pattern = re.compile(
r"(January|February|March|April|May|June|July|August|September|October|November|December)\s(\d{1,2})(st|nd|rd|th),\s(\d{4})")
address_pattern = re.compile(r'\d+\s+[\w\s]+\,\s+[A-Za-z]+\,\s+[A-Z]{2}\s+\d{5}(-\d{4})?')
zip_code_pattern =  re.compile(r'\b\d{5}(?:-\d{4})?\b')
policy_number_pattern = re.compile(r"[A-Z]{3}\d{4}\.$")  # 3 characters followed by 4 digits, in our case e.g XYZ9876

Settings.embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5")

# match = re.match(policy_number_pattern, "XYZ9876")
# print(match)


def mask_pii(text):
   """
   Masks Personally Identifiable Information (PII) in the given
   text using pre-defined regex patterns and spaCy's named entity recognition.
   Args:
       text (str): The input text containing potential PII.
   Returns:
       str: The text with PII masked.
   """

   # Process the text with spaCy for NER
   doc = nlp(text)

   # Mask entities identified by spaCy NER (e.g First/Last Names etc)
   for ent in doc.ents:
       if ent.label_ in ["PERSON", "ORG", "GPE"]:
           text = text.replace(ent.text, '[MASKED]')

   # Apply regex patterns after NER to avoid overlapping issues
   text = phone_pattern.sub('[PHONE MASKED]', text)
   text = email_pattern.sub('[EMAIL MASKED]', text)
   text = date_pattern.sub('[DATE MASKED]', text)
   text = address_pattern.sub('[ADDRESS MASKED]', text)
   text = dob_pattern.sub('[DOB MASKED]', text)
   text = zip_code_pattern.sub('[ZIP MASKED]', text)
   text = policy_number_pattern.sub('[POLICY MASKED]', text)

   return text


class CustomPostProcessor(BaseNodePostprocessor):
   """
   Custom Postprocessor which masks Personally Identifiable Information (PII).
   PostProcessor is called on the Documents before they are sent to the LLM.
   """
   def _postprocess_nodes(
           self, nodes: List[NodeWithScore], query_bundle: Optional[QueryBundle]
   ) -&gt; List[NodeWithScore]:
       # Masks PII
       for n in nodes:
          n.node.set_content(mask_pii(n.text))
       return nodes

   
# Use Public LLM to send user query and Related Documents
llm = OpenAI()
index = VectorStoreIndex.from_vector_store(es_vector_store)

# This query_engine, for a given user query retrieves top 10 similar documents from
# Elasticsearch vector database and sends the documents along with the user query to the LLM.
# Note that documents are masked based on custom logic defined in CustomPostProcessor._postprocess_nodes.
query_engine = index.as_query_engine(llm, similarity_top_k=10, node_postprocessors=[CustomPostProcessor()])



query = "Give me summary of water related claims that customers raised."
bundle = QueryBundle(query, embedding=Settings.embed_model.get_query_embedding(query))
response = query_engine.query(bundle)
print(response)

<p>La réponse du LLM est présentée ci-dessous.</p>Les clients ont fait part de diverses réclamations liées à l'eau, notamment des dégâts d'eau dans les sous-sols, des éclatements de canalisations, des dommages causés aux toits par la grêle et des inondations en cas de fortes pluies. Ces demandes ont engendré des frustrations en raison de refus de demandes fondés sur des motifs tels que l'absence de notification en temps utile, les problèmes d'entretien, l'usure progressive et les dommages préexistants. Les clients ont fait part de leur déception, de leur stress et de leur charge financière à la suite de ces refus, et ont demandé des évaluations équitables et des examens approfondis de leurs demandes d'indemnisation. Certains clients ont également été confrontés à des retards dans le traitement des demandes d'indemnisation, ce qui a aggravé leur mécontentement à l'égard du service fourni par la compagnie d'assurance.<p>Dans le code ci-dessus, lors de la création du moteur de requête de l'index Llama, nous fournissons un CustomPostProcessor.</p><p>La logique invoquée par le QueryEngine est définie dans la méthode <code>_postprocess_nodes</code> de <code>CustomPostProcessor</code>. Nous utilisons la bibliothèque SpaCy.io pour détecter les entités nommées dans nos documents et nous utilisons ensuite des expressions régulières pour remplacer ces noms ainsi que les informations sensibles avant d'envoyer les documents au LLM.</p><p>À titre d'exemple, voici des parties de conversations originales et de la conversation masquée créée par le CustomPostProcessor.</p><p>Texte original :</p>Client : Bonjour, je m'appelle Matthew Lopez, je suis né le 12 octobre 1984 et j'habite au 456 Cedar St, Smalltown, NY 34567. Mon numéro de police est TUV8901. Agent : Bonjour, Matthew. Comment puis-je vous aider aujourd'hui ? Le client : Bonjour, je suis extrêmement déçu de la décision de votre société de refuser ma demande d'indemnisation.<p>Texte masqué par le CustomPostProcessor.</p>Client : Bonjour, je m'appelle [MASKED], [MASKED] est [DOB MASKED], et j'habite au 456 Cedar St, [MASKED], [MASKED] 34567. Mon numéro de police est [MASKED]. Agent : Bonjour, [MASQUÉ]. Comment puis-je vous aider aujourd'hui ? Le client : Bonjour, je suis extrêmement déçu de la décision de votre société de refuser ma demande d'indemnisation.<p>Remarque :</p><p><em>Identifier et masquer les IPI et les informations sensibles n'est pas une tâche aisée. Couvrir les différents formats et la sémantique des informations sensibles nécessite une bonne compréhension de votre domaine et de vos données. Bien que le code présenté ci-dessus puisse fonctionner pour certains cas d'utilisation, il se peut que vous deviez le modifier en fonction de vos besoins et de vos tests.</em></p><h3>Utiliser le LlamaIndex prêt à l'emploi <code>NERPIINodePostprocessor</code></h3><p>LlamaIndex a facilité la protection des informations PII dans un pipeline RAG en introduisant les éléments suivants <code>NERPIINodePostprocessor.</code></p>from llama_index.core import VectorStoreIndex, QueryBundle, Settings
from llama_index.core.postprocessor import NERPIINodePostprocessor
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.llms.openai import OpenAI
from index import es_vector_store

Settings.embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5")

# Use Public LLM to send user query and Related Documents
llm = OpenAI()

ner_processor = NERPIINodePostprocessor()
index = VectorStoreIndex.from_vector_store(es_vector_store)

# This query_engine, for a given user query retrieves top 10 similar documents from
# Elasticsearch vector database and sends the documents along with the user query to the LLM.
# Note that documents masked using the NERPIINodePostprocessor so that PII/Sensitive data is not sent to the LLM.
query_engine = index.as_query_engine(llm, similarity_top_k=10, node_postprocessors=[ner_processor])

query = "Give me summary of fire related claims that customers raised."
bundle = QueryBundle(query, embedding=Settings.embed_model.get_query_embedding(query))
response = query_engine.query(bundle)
print(response)
<p>La réponse est la suivante</p>Des clients ont déposé des demandes d'indemnisation pour des dommages causés par des incendies à leurs propriétés. Dans un cas, une demande d'indemnisation pour des dommages causés par l'incendie d'un garage a été refusée parce que l'incendie criminel était exclu de la couverture. Un autre client a déposé une demande d'indemnisation pour des dommages causés par un incendie à sa maison, qui étaient couverts par sa police. En outre, un client a signalé un incendie dans sa cuisine et a reçu l'assurance que les dommages causés par l'incendie étaient couverts.<h3>Utilisation de LLM locaux via <code>PIINodePostprocessor</code></h3><p>Nous pourrions également utiliser un LLM fonctionnant localement ou dans votre réseau privé pour effectuer le travail de masquage avant d'envoyer les données à un LLM public.</p><p>Nous utiliserons Mistral fonctionnant sur Ollama sur votre machine locale pour effectuer le masquage.</p><h4>Exécuter Mistral localement</h4><p>Téléchargez et installez <a href="https://ollama.com/">Ollama</a>. Après avoir installé Ollama, lancez la commande suivante pour télécharger et exécuter <a href="https://ollama.com/library/mistral">mistral</a></p>ollama run mistral
<p>Le téléchargement et l'exécution locale du modèle pour la première fois peuvent prendre quelques minutes. Vérifiez si le mistral fonctionne en posant une question telle que la suivante : "Ecrivez un poème sur les nuages" et vérifiez si le poème vous plaît. Gardez ollama en marche car nous aurons besoin d'interagir avec le modèle mistral plus tard par le biais du code.</p><p>Créez un nouveau fichier appelé <code>query_masking_local_LLM.py</code> et ajoutez le code ci-dessous.</p># pip install llama-index-llms-ollama
from llama_index.core import VectorStoreIndex, QueryBundle, Settings
from llama_index.core.postprocessor import PIINodePostprocessor
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.llms.ollama import Ollama
from llama_index.llms.openai import OpenAI
from index import es_vector_store

Settings.embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5")

# Use Public LLM to send user query and Related Documents and Local LLM to mask
public_llm = OpenAI()
local_llm = Ollama(model="mistral")

pii_processor = PIINodePostprocessor(llm=local_llm)
index = VectorStoreIndex.from_vector_store(es_vector_store)

# This query_engine, for a given user query retrieves top 10 similar documents from
# Elasticsearch vector database and sends the documents along with the user query to the public LLM.
# Note that documents are masked using the local llm via PIINodePostprocessor
# so that PII/Sensitive data is not sent to the public LLM.
query_engine = index.as_query_engine(public_llm, similarity_top_k=10, node_postprocessors=[pii_processor])


query = "Give me summary of fire related claims that customers raised."
bundle = QueryBundle(query, embedding=Settings.embed_model.get_query_embedding(query))
result = query_engine.query(bundle)
print(result)
<p>La réponse ressemble à ce qui est indiqué ci-dessous</p>Des clients ont déposé des demandes d'indemnisation pour des dommages causés par des incendies à leurs propriétés. Dans un cas, une demande d'indemnisation pour des dommages causés par l'incendie d'un garage a été refusée parce que l'incendie criminel était exclu de la couverture. Un autre client a déposé une demande d'indemnisation pour des dommages causés par un incendie à sa maison, qui étaient couverts par sa police. En outre, un client a signalé un incendie dans sa cuisine et a reçu l'assurance que les dommages causés par l'incendie étaient couverts.<h3>Conclusion</h3><p>Dans cet article, nous avons montré comment protéger les informations confidentielles et les données sensibles lors de l'utilisation de LLM publics dans un flux RAG. Nous avons démontré qu'il y avait plusieurs façons d'y parvenir. Il est fortement recommandé de tester ces approches en fonction de votre cas d'utilisation et de vos besoins avant de les adopter.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/rag-security-masking-pii</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/rag-security-masking-pii</guid>
    <category><![CDATA[IA]]></category>
    <dc:creator><![CDATA[Srikanth Manvi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9d3882da43bfdac0/6a17050867045bd5fe45c0e9/9d51295472f8bcca3d1973248acb724f8b94767e-1054x555.png" length="0" type="image/png"/>
    <pubDate>Thu, 25 Jul 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[RAG (Retrieval Augmented Generation) avec LlamaIndex, Elasticsearch et Mistral]]></title>
    <description><![CDATA[Apprenez à mettre en œuvre un système RAG (Retrieval Augmented Generation) en utilisant LlamaIndex, Elasticsearch et Mistral en local.]]></description>
    <content:encoded><![CDATA[<p>Dans ce blog, nous verrons comment mettre en œuvre l'expérience Q&amp;A en utilisant une technique RAG (Retrieval Augmented Generation) avec Elasticsearch comme base de données vectorielle. Nous utiliserons LlamaIndex et un LLM Mistral fonctionnant localement.</p><p>Avant de commencer, nous allons examiner quelques termes.</p><h3>Terminologie</h3><p><a href="https://www.llamaindex.ai/">LlamaIndex</a> est un cadre de données de premier plan pour la création d'applications LLM (Large Language Model). LlamaIndex fournit des abstractions pour les différentes étapes de la construction d'une application RAG (Retrieval Augmented Generation). Des structures telles que LlamaIndex et LangChain fournissent des abstractions afin que les applications ne soient pas étroitement liées aux API d'un LLM spécifique.</p><p><a href="https://www.elastic.co/enterprise-search">Elasticsearch</a> est proposé par <a href="https://elastic.co/">Elastic</a>. Elastic est un leader de l'industrie avec Elasticsearch, un moteur de recherche et d'analyse qui prend en charge la recherche en texte intégral pour la précision, la recherche vectorielle pour la compréhension sémantique, et la recherche hybride pour le meilleur des deux mondes. Elasticsearch est un magasin de données évolutif et une base de données vectorielle. Les fonctionnalités d'Elasticsearch que nous utilisons dans ce blog sont disponibles dans la version gratuite et ouverte d'Elasticsearch.</p><p><a href="https://www.promptingguide.ai/techniques/rag">Retrieval Augment Generation (RAG)</a> est une technique/un modèle d'IA dans lequel les LLM sont dotés de connaissances externes pour générer des réponses aux requêtes des utilisateurs. Cela permet d'adapter les réponses du programme d'éducation et de formation tout au long de la vie à un contexte spécifique et les réponses sont plus spécifiques.</p><p><a href="https://docs.mistral.ai/">Mistral</a> propose des modèles LLM open-source et optimisés pour les entreprises. Dans ce tutoriel, nous utiliserons leur modèle open source <a href="https://docs.mistral.ai/models/#mistral-7b">mistral-7b</a> qui fonctionne sur votre ordinateur portable. Si vous ne souhaitez pas exécuter le modèle sur votre ordinateur portable, vous pouvez utiliser la version en nuage, auquel cas vous devrez modifier le code de ce blog afin d'utiliser les clés et les paquets API appropriés.</p><p><a href="https://ollama.com/">Ollama</a> permet d'exécuter des LLM localement sur votre ordinateur portable. Nous utiliserons Ollama pour exécuter localement le modèle open source Mistral-7b.</p><p>Les <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.13/semantic-search.html">encastrements</a> sont des représentations numériques de la signification d'un texte ou d'un média. Il s'agit de représentations de dimensions inférieures d'informations de dimensions supérieures.</p><h3>Construire une application RAG avec LlamaIndex, Elasticsearch &amp; Mistral : Aperçu du scénario</h3><p><strong>Scénario :</strong></p><p>Nous disposons d'un échantillon de données (sous forme de fichier JSON) de conversations entre des agents et des clients d'une compagnie d'assurance habitation fictive. Nous allons créer une application RAG simple qui peut répondre à des questions telles que</p><p><code>Give me summary of water related issues.</code></p><h3>Débit élevé</h3><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9d3882da43bfdac0/6a17050867045bd5fe45c0e9/9d51295472f8bcca3d1973248acb724f8b94767e-1054x555.png" alt="Flux RAG" /><p>Mistral LLM fonctionne localement à l'aide d'Ollama.</p><p>Ensuite, nous chargeons les <em>conversations</em> du fichier JSON en tant que <code>Documents</code> dans le <a href="https://developers.llamaindex.ai/python/examples/vector_stores/elasticsearchindexdemo/">magasin ElasticsearchStore</a> (qui est un magasin VectorStore soutenu par Elasticsearch). Lors du chargement des documents, nous créons des enchâssements à l'aide du modèle Mistral exécuté localement. Nous stockons ces encastrements ainsi que les <em>conversations</em> dans le magasin vectoriel Elasticsearch de LlamaIndex<a href="https://developers.llamaindex.ai/python/examples/vector_stores/elasticsearchindexdemo/">(ElasticsearchStore)</a>.</p><p>Nous configurons une ligne d'ingestion LlamaIndex et lui fournissons le LLM local que nous utilisons, dans ce cas Mistral fonctionnant via Ollama.</p><p>Lorsque nous posons une question telle que "Donnez-moi un résumé des problèmes liés à l'eau", Elasticsearch effectue une recherche sémantique et renvoie les <em>conversations</em> liées aux problèmes de l'eau. Ces <em>conversations</em>, ainsi que la question initiale, sont envoyées au LLM local pour générer une réponse.</p><h3>Étapes de la création de l'application RAG</h3><h4>Exécuter Mistral localement</h4><p>Téléchargez et installez <a href="https://ollama.com/">Ollama</a>. Après avoir installé Ollama, lancez la commande suivante pour télécharger et exécuter <a href="https://ollama.com/library/mistral">mistral</a></p>ollama run mistral
<p>Le téléchargement et l'exécution locale du modèle pour la première fois peuvent prendre quelques minutes. Vérifiez si le mistral fonctionne en posant une question telle que la suivante : "Ecrivez un poème sur les nuages" et vérifiez si le poème vous plaît. Gardez ollama en marche car nous aurons besoin d'interagir avec le modèle mistral plus tard par le biais du code.</p><h4>Installer Elasticsearch</h4><p>Faites fonctionner Elasticsearch en créant un déploiement dans le nuage<a href="https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud">(instructions ici</a>) ou en l'exécutant dans Docker<a href="https://www.elastic.co/search-labs/tutorials/install-elasticsearch/docker">(instructions ici).</a> Vous pouvez également créer un déploiement autonome d'Elasticsearch au niveau de la production en commençant <a href="https://www.elastic.co/search-labs/tutorials/install-elasticsearch/docker#self-hosted-production-deployments">ici.</a></p><p>Si vous utilisez le déploiement dans le nuage, saisissez la clé API et l'ID du nuage pour le déploiement, comme indiqué dans les instructions. Nous les utiliserons plus tard.</p><h4>Application RAG</h4><p>Pour référence, le code complet peut être trouvé dans ce <a href="https://github.com/srikanthmanvi/RAG-InsuranceCompany">dépôt Github</a>. Le clonage de la base de données est facultatif car nous allons parcourir le code ci-dessous.</p><p>Dans votre IDE préféré, créez une nouvelle application Python avec les 3 fichiers ci-dessous.</p><ul><li><p><code>index.py</code> où se trouve le code lié à l'indexation des données.</p></li><li><p><code>query.py</code> où se trouve le code lié à l'interrogation et à l'interaction avec le LLM.</p></li><li><p><code>.env</code> où se trouvent les propriétés de configuration telles que les clés d'API.</p></li></ul><p>Nous devons installer quelques paquets. Nous commençons par créer un nouvel <a href="https://docs.python.org/3/library/venv.html">environnement virtuel</a> python dans le dossier racine de votre application.</p>python3 -m venv .venv
<p>Activez l'environnement virtuel et installez les paquets requis ci-dessous.</p>source .venv/bin/activate
pip install llama-index 
pip install llama-index-embeddings-ollama
pip install llama-index-llms-ollama
pip install llama-index-vector-stores-elasticsearch
pip install sentence-transformers
pip install python-dotenv
<h4>Indexation des données</h4><p>Téléchargez le fichier <a href="https://github.com/srikanthmanvi/RAG-InsuranceCompany/blob/main/conversations.json">conversations.json</a> qui contient les <em>conversations</em> entre les clients et les agents du centre d'appel de notre compagnie d'assurance habitation fictive. Placez le fichier dans le répertoire racine de l'application avec les 2 fichiers python et le fichier .env. que vous avez créé précédemment. Vous trouverez ci-dessous un exemple du contenu du fichier.</p>{
    "conversation_id": 103,
    "customer_name": "Sophia Jones",
    "agent_name": "Emily Wilson",
    "policy_number": "JKL0123",
    "conversation": "Customer: Hi, I'm Sophia Jones. My Date of Birth is November 15th, 1985, Address is 303 Cedar St, Miami, FL 33101, and my Policy Number is JKL0123.\nAgent: Hello, Sophia. How may I assist you today?\nCustomer: Hello, Emily. I have a question about my policy.\nCustomer: There's been a break-in at my home, and some valuable items are missing. Are they covered?\nAgent: Let me check your policy for coverage related to theft.\nAgent: Yes, theft of personal belongings is covered under your policy.\nCustomer: That's a relief. I'll need to file a claim for the stolen items.\nAgent: We'll assist you with the claim process, Sophia. Is there anything else I can help you with?\nCustomer: No, that's all for now. Thank you for your assistance, Emily.\nAgent: You're welcome, Sophia. Please feel free to reach out if you have any further questions or concerns.\nCustomer: I will. Have a great day!\nAgent: You too, Sophia. Take care.",
    "summary": "A customer inquires about coverage for stolen items after a break-in at home, and the agent confirms that theft of personal belongings is covered under the policy. The agent offers assistance with the claim process, resulting in the customer expressing relief and gratitude."
}
<p>Nous définissons une fonction appelée <code>get_documents_from_file</code> dans <code>index.py</code> qui lit le fichier json et crée une liste de documents. Les objets <a href="https://docs.llamaindex.ai/en/stable/module_guides/loading/documents_and_nodes/">document</a> sont l'unité de base de l'information avec laquelle LlamaIndex travaille.</p># index.py
import json, os
from llama_index.core import Document, Settings
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core.ingestion import IngestionPipeline
from llama_index.embeddings.ollama import OllamaEmbedding
from llama_index.vector_stores.elasticsearch import ElasticsearchStore
from dotenv import load_dotenv

def get_documents_from_file(file):
   """Reads a json file and returns list of Documents"""

   with open(file=file, mode='rt') as f:
       conversations_dict = json.loads(f.read())
      
   # Build Document objects using fields of interest.
   documents = [Document(text=item['conversation'],
                         metadata={"conversation_id": item['conversation_id']})
                for
                item in conversations_dict]
   return documents
<p>Créer un pipeline d'ingestion</p><p>Tout d'abord, ajoutez au fichier <code>.env</code> l'Elasticsearch CloudID et les clés API que vous avez obtenues dans la section <code>Install Elasticsearch</code>. Votre fichier <code>.env</code> devrait ressembler à ce qui suit (avec des valeurs réelles).</p>ELASTIC_CLOUD_ID=&lt;REPLACE WITH YOUR CLOUD ID&gt;
ELASTIC_API_KEY=&lt;REPLACE WITH YOUR API_KEY&gt;
<p>LlamaIndex <a href="https://docs.llamaindex.ai/en/stable/module_guides/loading/ingestion_pipeline/">IngestionPipeline</a> vous permet de composer un pipeline à l'aide de plusieurs composants. Ajoutez le code ci-dessous au fichier <code>index.py</code>.</p># index.py

# Load .env file contents into env
# ELASTIC_CLOUD_ID and ELASTIC_API_KEY are expected to be in the .env file.
load_dotenv('.env')

# ElasticsearchStore is a VectorStore that
# takes care of ES Index and Data management.
es_vector_store = ElasticsearchStore(index_name="calls",
                                     vector_field='conversation_vector',
                                     text_field='conversation',
                                     es_cloud_id=os.getenv("ELASTIC_CLOUD_ID"),
                                     es_api_key=os.getenv("ELASTIC_API_KEY"))


def main():
    # Embedding Model to do local embedding using Ollama.
    ollama_embedding = OllamaEmbedding("mistral")

    # LlamaIndex Pipeline configured to take care of chunking, embedding
    # and storing the embeddings in the vector store.
    pipeline = IngestionPipeline(
        transformations=[
            SentenceSplitter(chunk_size=350, chunk_overlap=50),
            ollama_embedding,
        ],
        vector_store=es_vector_store
    )

    # Load data from a json file into a list of LlamaIndex Documents
    documents = get_documents_from_file(file="conversations.json")

    pipeline.run(documents=documents)
    print(".....Done running pipeline.....\n")


if __name__ == "__main__":
    main()

<p>Comme nous l'avons déjà mentionné, la LlamaIndex IngestPipeline peut être composée de plusieurs éléments. Nous ajoutons 3 composants au pipeline dans la ligne <code>pipeline = IngestionPipeline(...</code>.</p><ul><li><p><a href="https://docs.llamaindex.ai/en/stable/module_guides/loading/node_parsers/modules/?h=sentencesp#sentencesplitter">SentenceSplitter</a>: Comme on peut le voir dans la définition de <code>get_documents_from_file()</code>, chaque document possède un champ texte qui contient la conversation trouvée dans le fichier json. Ce champ de texte est un long texte. Pour que la recherche sémantique fonctionne bien, elle doit être décomposée en morceaux de textes plus petits. La classe <a href="https://docs.llamaindex.ai/en/stable/module_guides/loading/node_parsers/modules/?h=sentencesp#sentencesplitter">SentenceSplitter</a> s'en charge pour nous. Ces morceaux sont appelés nœuds dans la terminologie de LlamaIndex. Les nœuds contiennent des métadonnées qui renvoient au document auquel ils appartiennent. Vous pouvez également utiliser Elasticsearch Ingestpipeline pour le découpage, comme indiqué dans ce <a href="https://www.elastic.co/search-labs/blog/chunking-via-ingest-pipelines">blog.</a></p></li><li><p><a href="https://docs.llamaindex.ai/en/stable/module_guides/models/embeddings/">OllamaEmbedding</a>: Les modèles d'incorporation convertissent un texte en nombres (également appelés vecteurs). La représentation numérique nous permet d'effectuer une <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-search.html">recherche sémantique</a> où les résultats de la recherche correspondent à la signification du mot plutôt que d'effectuer une simple recherche textuelle. Nous fournissons à l'IngestionPipeline le site <code>OllamaEmbedding("mistral")</code>. Les morceaux que nous découpons à l'aide de SentenceSplitter sont envoyés au modèle Mistral qui s'exécute sur votre machine locale via Ollama, mistral crée alors des embeddings pour les morceaux.</p></li><li><p><a href="https://developers.llamaindex.ai/python/examples/vector_stores/elasticsearchindexdemo/">ElasticsearchStore</a>: Le magasin vectoriel ElasticsearchStore de LlamaIndex sauvegarde les embeddings créés dans un index Elasticsearch. ElasticsearchStore se charge de créer et d'alimenter le contenu de l'index Elasticsearch spécifié. Lors de la création de l'ElasticsearchStore (référencé par <code>es_vector_store</code>), nous fournissons le nom de l'index Elasticsearch que nous voulons créer (<code>calls</code> dans notre cas), le champ de l'index dans lequel nous voulons stocker les embeddings (<code>conversation_vector</code> dans notre cas) et le champ dans lequel nous voulons stocker le texte (<code>conversation</code> dans notre cas). En résumé, selon notre configuration, <code>ElasticsearchStore</code> crée un nouvel index dans Elasticsearch avec <code>conversation_vector</code> et <code>conversation</code> comme champs (parmi d'autres champs créés automatiquement).</p></li></ul><p>Pour relier le tout, nous exécutons le pipeline en appelant <code>pipeline.run(documents=documents)</code>.</p><p>Lancez le script index.py pour exécuter le pipeline d'ingestion :</p>python index.py
<p>Une fois l'exécution du pipeline terminée, nous devrions voir un nouvel index dans Elasticsearch appelé <code>calls</code>. En exécutant une simple requête elasticsearch à l'aide de la Dev Console, vous devriez être en mesure de voir les données chargées avec les embeddings.</p>GET calls/_search?size=1
<p>Pour résumer ce que nous avons fait jusqu'à présent, nous avons créé des documents à partir d'un fichier JSON, nous les avons divisés en morceaux, nous avons créé des embeddings pour ces morceaux et nous avons stocké les embeddings (et la conversation textuelle) dans un magasin vectoriel (ElasticsearchStore).</p><h4>Interrogation</h4><p>Le <a href="https://docs.llamaindex.ai/en/stable/module_guides/indexing/vector_store_guide/">VectorStoreIndex</a> llamaIndex vous permet de retrouver des documents pertinents et d'interroger des données. Par défaut, VectorStoreIndex stocke les intégrations en mémoire dans un <a href="https://docs.llamaindex.ai/en/stable/module_guides/indexing/vector_store_guide/">SimpleVectorStore</a>. Cependant, des entrepôts de vecteurs externes (comme <a href="https://developers.llamaindex.ai/python/examples/vector_stores/elasticsearchindexdemo/">ElasticsearchStore</a>) peuvent être utilisés à la place pour rendre les enregistrements persistants.</p><p>Ouvrez le site <code>query.py</code> et collez le code ci-dessous</p># query.py
from llama_index.core import VectorStoreIndex, QueryBundle, Response, Settings
from llama_index.embeddings.ollama import OllamaEmbedding
from llama_index.llms.ollama import Ollama
from index import es_vector_store

# Local LLM to send user query to
local_llm = Ollama(model="mistral")
Settings.embed_model= OllamaEmbedding("mistral")

index = VectorStoreIndex.from_vector_store(es_vector_store)
query_engine = index.as_query_engine(local_llm, similarity_top_k=10)

query="Give me summary of water related issues"
bundle = QueryBundle(query, embedding=Settings.embed_model.get_query_embedding(query))
result = query_engine.query(bundle)
print(result)
<p>Nous définissons un LLM local (<code>local_llm</code>) pour indiquer le modèle Mistral fonctionnant sur Ollama. Ensuite, nous créons un VectorStoreIndex (<code>index</code>) à partir du magasin de vecteurs ElasticssearchStore que nous avons créé précédemment, puis nous obtenons un moteur de requête à partir de l'index. Lors de la création du moteur de requête, nous référençons le LLM local qui doit être utilisé pour répondre, nous fournissons également (<code>similarity_top_k=10</code>) pour configurer le nombre de documents qui doivent être récupérés dans le magasin vectoriel et envoyés au LLM pour obtenir une réponse.</p><p>Lancez le script <code>query.py</code> pour exécuter le flux RAG :</p>python query.py
<p>Nous envoyons la demande à <code>Give me summary of water related issues</code> (n'hésitez pas à personnaliser <code>query</code>) et la réponse du LLM, qui est accompagnée des documents correspondants, devrait ressembler à ce qui suit.</p>Dans le contexte fourni, nous voyons plusieurs cas où les clients se sont renseignés sur la couverture des dommages liés à l'eau. Dans deux cas, les inondations ont causé des dommages aux sous-sols et les fuites de toiture ont été à l'origine d'un autre cas. Les agents ont confirmé que les deux types de dégâts des eaux sont couverts par leurs polices respectives. Par conséquent, les problèmes liés à l'eau, tels que les inondations et les fuites de toit, sont généralement couverts par les polices d'assurance habitation.<h4>Quelques mises en garde :</h4><p>Cet article de blog est une introduction pour débutants à la technique RAG avec Elasticsearch et omet donc la configuration des fonctionnalités qui vous permettront de passer de ce point de départ à la production. Lorsque vous construisez pour des cas d'utilisation en production, vous voudrez prendre en compte des aspects plus sophistiqués comme la possibilité de protéger vos données avec <a href="https://www.elastic.co/search-labs/blog/dls-internal-knowledge-search">Document Level Security</a>, le découpage de vos données dans le cadre d'un <a href="https://www.elastic.co/search-labs/blog/chunking-via-ingest-pipelines">pipeline d'ingestion</a> Elasticsearch ou même l'exécution d'autres <a href="https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-overview.html">tâches de ML</a> sur les mêmes données que celles utilisées pour les cas d'utilisation GenAI/Chat/Q&amp;A.</p><p>Vous pouvez également envisager d'obtenir des données et de créer des embeddings à partir de diverses sources externes (par exemple Azure Blob Storage, Dropbox, Gmail, etc.) à l'aide d'<a href="https://www.elastic.co/guide/en/enterprise-search/current/connectors.html">Elastic Connectors.</a></p><p>Elastic rend possible tout ce qui précède et plus encore, et fournit une solution d'entreprise complète pour les cas d'utilisation de la GenAI et au-delà.</p><h4>Quelle est la suite ?</h4><ul><li><p>Vous avez peut-être remarqué que nous envoyons 10 conversations connexes avec la question de l'utilisateur au LLM pour formuler une réponse. Ces conversations peuvent contenir des IPI (informations personnelles identifiables) telles que le nom, la date de naissance, l'adresse, etc. Dans notre cas, le LLM est local, la fuite de données n'est donc pas un problème. Cependant, lorsque vous souhaitez utiliser un LLM fonctionnant dans le nuage (par exemple OpenAI), il n'est pas souhaitable d'envoyer des textes contenant des informations PII. Dans un blog ultérieur, nous verrons comment masquer les informations PII avant de les envoyer aux LLM externes dans le flux RAG.</p></li><li><p>Dans ce billet, nous avons utilisé un LLM local. Dans le prochain billet sur le masquage des données PII dans RAG, nous verrons comment passer facilement d'un LLM local à un LLM public.</p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/rag-with-llamaIndex-and-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/rag-with-llamaIndex-and-elasticsearch</guid>
    <category><![CDATA[IA]]></category>
    <dc:creator><![CDATA[Srikanth Manvi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9d3882da43bfdac0/6a17050867045bd5fe45c0e9/9d51295472f8bcca3d1973248acb724f8b94767e-1054x555.png" length="0" type="image/png"/>
    <pubDate>Fri, 12 Apr 2024 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>