<?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[Florian Bernd - 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[Florian Bernd - 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/author/florian-bernd</link>
    </image>
    <link>https://www.elastic.co/fr/search-labs/author/florian-bernd</link>
    <atom:link href="https://www.elastic.co/fr/search-labs/rss/author/florian-bernd.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[fr]]></language>
    <lastBuildDate>Mon, 28 Sep 2026 03:58:28 GMT</lastBuildDate>
  <item>
    <title><![CDATA[LINQ to Elasticsearch ES|QL : écrire en C#, interroger Elasticsearch]]></title>
    <description><![CDATA[Découverte du nouveau fournisseur LINQ to Elasticsearch ES|QL dans le client Elasticsearch .NET, qui vous permet d'écrire du code C# qui est automatiquement converti en requêtes ES|QL.]]></description>
    <content:encoded><![CDATA[<p>À partir des versions <strong>9.3.4</strong> et <strong>8.19.18</strong>, le client .NET Elasticsearch inclut un fournisseur <a href="https://learn.microsoft.com/en-us/dotnet/csharp/linq/">LINQ (Language Integrated Query) </a>qui traduit les expressions LINQ C# en <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql.html">requêtes ES|QL (Elasticsearch Query Language)</a> à l'exécution. Au lieu d'écrire manuellement des chaînes ES|QL, vous composez vos requêtes à l'aide des fonctions <code>Where</code>, <code>Select</code>, <code>OrderBy</code>, <code>GroupBy</code> et d'autres opérateurs standard. Le fournisseur se charge de la traduction, du paramétrage et de la désérialisation des résultats, y compris le flux par ligne qui maintient l'utilisation de la mémoire constante, quelle que soit la taille de l'ensemble des résultats.</p><h2>Votre première requête</h2><p>Commencez par définir un objet CLR (POCO) classique qui correspond à votre index Elasticsearch. Les noms de propriétés sont résolus en noms de colonnes ES|QL via des attributs standard <code>System.Text.Json</code>, comme <code>[JsonPropertyName]</code>, ou via un <code>JsonNamingPolicy</code> configuré. Les mêmes règles de <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/dotnet/source-serialization">sérialisation des sources</a> que celles qui s'appliquent au reste du client s'appliquent également ici.</p>using System.Text.Json.Serialization;

public class Product
{
    [JsonPropertyName("product_id")]
    public string Id { get; set; }

    public string Name { get; set; }

    public string Brand { get; set; }

    [JsonPropertyName("price_usd")]
    public double Price { get; set; }

    [JsonPropertyName("in_stock")]
    public bool InStock { get; set; }
}<p>Une fois le type défini, une requête ressemble à ceci :</p>var minPrice = 100.0;
var brand = "TechCorp";

await foreach (var product in client.Esql.QueryAsync&lt;Product&gt;(q =&gt; q
    .From("products")
    .Where(p =&gt; p.InStock &amp;&amp; p.Price &gt;= minPrice &amp;&amp; p.Brand == brand)
    .OrderByDescending(p =&gt; p.Price)
    .Take(10)))
{
    Console.WriteLine($"{product.Name}: ${product.Price}");
}<p>Le fournisseur la traduit en ES|QL comme ceci :</p><p>Quelques détails à noter :</p><ul><li><p><strong>Résolution des noms de propriété</strong> : <code>p.Price</code> devient <code>price_usd</code> en raison de l'attribut <code>[JsonPropertyName]</code>, et <code>p.Brand</code> devient <code>brand</code> conformément à la politique de dénomination camelCase par défaut.</p></li><li><p><strong>Capture des paramètres</strong> : les variables C# <code>minPrice</code> et <code>brand</code> sont capturées comme paramètres nommés (<code>?minPrice</code>, <code>?brand</code>). Elles sont envoyées séparément de la chaîne de requête dans la charge utile JSON, ce qui empêche l'injection et permet la mise en cache du plan de requête côté serveur.</p></li><li><p><strong>Flux en continu</strong> : <code>QueryAsync&lt;T&gt;</code> renvoie <code>IAsyncEnumerable&lt;T&gt;</code>. Les lignes se matérialisent une à une à mesure de leur arrivée depuis Elasticsearch.</p></li></ul><p>Vous pouvez également inspecter la requête générée et ses paramètres sans l'exécuter :</p>var query = client.Esql.CreateQuery&lt;Product&gt;()
    .Where(p =&gt; p.InStock &amp;&amp; p.Price &gt;= minPrice &amp;&amp; p.Brand == brand)
    .OrderByDescending(p =&gt; p.Price)
    .Take(10);

Console.WriteLine(query.ToEsqlString());
// FROM products | WHERE (in_stock == true AND price_usd &gt;= 100) | SORT price_usd DESC | LIMIT 10

Console.WriteLine(query.ToEsqlString(inlineParameters: false));
// FROM products | WHERE (in_stock == true AND price_usd &gt;= ?minPrice AND brand == ?brand) | SORT price_usd DESC | LIMIT 10

var parameters = query.GetParameters();
// { "minPrice": 100.0, "brand": "TechCorp" }<h2>Comment ça marche ? Petit rappel sur LINQ</h2><p>Le mécanisme qui rend possibles les fournisseurs LINQ est la distinction entre <code>IEnumerable&lt;T&gt;</code> et <code>IQueryable&lt;T&gt;</code>.</p><p>Lorsque vous appelez <code>.Where(p =&gt; p.Price &gt; 100)</code> sur un <code>IEnumerable&lt;T&gt;</code>, la lambda est compilée en un <code>Func&lt;Product, bool&gt;</code>, un délégué standard que le runtime exécute en interne. C'est le principe du LINQ-to-Objects.</p><p>Lorsque vous appelez la même méthode sur un <code>IQueryable&lt;T&gt;</code>, le compilateur C# enveloppe la lambda dans un <code>Expression&lt;Func&lt;Product, bool&gt;&gt;</code> à la place. Il s'agit d'une structure de données qui représente la <em>structure</em> du code plutôt que sa forme exécutable. L'arbre d'expression peut être inspecté, analysé et traduit dans un autre langage au moment de l'exécution.</p>// IEnumerable: the lambda is a compiled delegate
IEnumerable&lt;Product&gt; local = products.Where(p =&gt; p.Price &gt; 100);

// IQueryable: the lambda is an expression tree, a data structure
IQueryable&lt;Product&gt; remote = queryable.Where(p =&gt; p.Price &gt; 100);<p>L’interface <code>IQueryProvider</code> est le point d’extension. Tout fournisseur peut implémenter <code>CreateQuery&lt;T&gt;</code> et <code>Execute&lt;T&gt;</code> pour traduire ces arbres d’expressions dans une langue cible. Entity Framework utilise ceci pour émettre du SQL. Le fournisseur LINQ to ES|QL l'utilise pour émettre ES|QL.</p><p>L'arbre d'expression de la requête ci-dessus ressemble à ceci :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt521838e8b9c36649/6a1705b1839dfa5f40dcfdfe/f864cd18a390831f8d28503a29b5835efb1842f7-1000x720.png" alt="Arbre d'expression de l'exemple de requête." /><p><em>Arbre d'expression de l'exemple de requête.</em></p><p>L'arbre est imbriqué de l'intérieur vers l'extérieur : <code>Take</code> englobe <code>OrderByDescending</code>, qui englobe <code>Where</code>, qui englobe <code>From</code>, qui englobe la racine constante <code>EsqlQueryable&lt;Product&gt;</code>. Le prédicat <code>Where</code> est lui-même un sous-arbre des nœuds <code>BinaryExpression</code> pour les opérateurs <code>&amp;&amp;</code>, <code>&gt;=</code>, et les opérateurs <code>==</code>, avec des feuilles <code>MemberExpression</code> pour les accès aux propriétés et des captures de fermeture pour les variables <code>minPrice</code> et <code>brand</code>. C'est cette structure de données que le fournisseur parcourt pour produire le code ES|QL final.</p><h2>Sous le capot : le pipeline de traduction</h2><p>Le chemin d'une expression LINQ vers les résultats de la requête suit un pipeline en six étapes :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt930670a505dd61ea/6a1705b3b339d58a54769ecf/2a2c772b63d720f61fc9a28b2f85668fa2db8d38-1999x1036.png" alt="Aperçu du pipeline de traduction." /><p><em>Aperçu du pipeline de traduction.</em></p><h3>1. Capture de l'arbre d'expressions</h3><p>Lorsque vous chaînez <code>.Where()</code>, <code>.OrderBy()</code>, <code>.Take()</code> et d’autres opérateurs sur un <code>IQueryable&lt;T&gt;</code>, l’infrastructure standard de LINQ construit un arbre d’expressions. <code>EsqlQueryable&lt;T&gt;</code> met en œuvre <code>IQueryable&lt;T&gt;</code> et délègue à <code>EsqlQueryProvider</code>.</p><h3>2. Traduction</h3><p>Lors de l'exécution de la requête (par énumération, appel de <code>ToList()</code> ou utilisation de <code>await foreach)</code>), le <code>EsqlExpressionVisitor</code> parcourt l'arbre d'expressions de l'intérieur vers l'extérieur. Il envoie chaque appel de méthode LINQ à un visiteur spécialisé :</p><p>Visiteur</p><p>Est traduit</p><p>En</p><p>WhereClauseVisitor</p><p>.Where(predicate)</p><p>Condition WHERE</p><p>SelectProjectionVisitor</p><p>.Select(selector)</p><p>EVAL + KEEP + RENAME</p><p>GroupByVisitor</p><p>.GroupBy().Select()</p><p>STATS ... BY</p><p>OrderByVisitor</p><p>.OrderBy() / .ThenBy()</p><p>Champ SORT [ASC\|DESC]</p><p>EsqlFunctionTranslator</p><p>EsqlFunctions.*, Math.*, méthodes string</p><p>Plus de 80 fonctions ES|QL</p><p>Lors de la traduction, les variables C# référencées dans les expressions sont capturées comme des paramètres nommés.</p><h3>3. Modèle de requête</h3><p>Les visiteurs ne produisent pas directement des chaînes de caractères. À la place, ils produisent des objets <code>QueryCommand</code> , une représentation intermédiaire immuable. Un objet <code>FromCommand</code>, un objet <code>WhereCommand</code>, un objet <code>SortCommand</code> et un objet <code>LimitCommand</code>, chacun représentant une commande de traitement ES|QL. Ces objets sont ensuite regroupés dans un modèle <code>EsqlQuery</code>.</p><p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt788c9936976f2f62/6a1705b50e2e4910da419ff0/2adc349b6cf655b96b7b3e826a134e8a17fe42fd-1999x1036.png" alt="Modèle de requête et schéma de commande." /><p><em>Modèle de requête et schéma de commande.</em></p><p>Ce modèle intermédiaire est découplé de l'arbre d'expression et du format de sortie. Il peut être inspecté, intercepté (via <code>IEsqlQueryInterceptor</code>) ou modifié avant d'être formaté.</p><h3>4. Formatage</h3><p><code>EsqlFormatter</code> parcourt chaque <code>QueryCommand</code> dans l'ordre et produit la chaîne ES|QL finale. Chaque commande devient une ligne, séparée par l'opérateur pipe (|) utilisé par ES|QL pour chaîner les commandes de traitement. Les identificateurs contenant des caractères spéciaux sont automatiquement échappés par des guillemets inversés.</p><h3>5. Exécution</h3><p>La chaîne ES|QL formatée et les paramètres capturés sont envoyés au point de terminaison <code>/_query</code> d'Elasticsearch sous forme de charge utile JSON. L'interface <code>IEsqlQueryExecutor</code> masque la couche transport, où l'architecture de packages en couches prend tout son sens.</p><h3>6. Matérialisation</h3><p><code>EsqlResponseReader</code> transmet la réponse JSON sans mettre en mémoire tampon l'ensemble des résultats. Un arbre <code>ColumnLayout</code>, précalculé une fois par requête, mappe les noms de colonnes ES|QL plats (comme <code>address.street</code>, <code>address.city</code>) aux propriétés POCO imbriquées. Chaque ligne est assemblée dans une instance <code>T</code> et renvoyée une par une via <code>IEnumerable&lt;T&gt;</code> ou <code>IAsyncEnumerable&lt;T&gt;</code>.</p><h2>L'architecture en couches</h2><p>La fonctionnalité LINQ to ES|QL est répartie sur trois packages :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt662bd0dd8861b6b6/6a1705b7a929cf7086ae08a2/41b8aae860ecdc2480edcb1c1d4cc9b03cfb78c9-1999x1036.png" alt="Architecture du package." /><p><em>Architecture des packages.</em>
<a href="https://www.nuget.org/packages/Elastic.Esql"><strong><code>Elastic.Esql</code></strong></a> est le moteur de traduction pur. Il ne dépend d'aucun HTTP et intègre les visiteurs d'expressions, le modèle de requêtes, le formateur et le lecteur de réponses. Vous pouvez l'utiliser de manière autonome pour créer et analyser des requêtes ES|QL sans connexion à Elasticsearch, ce qui est utile pour les tests, la journalisation des requêtes ou la création de votre propre couche d'exécution.</p>// Translation-only: no Elasticsearch connection needed
var provider = new EsqlQueryProvider();
var query = new EsqlQueryable&lt;Product&gt;(provider)
    .From("products")
    .Where(p =&gt; p.InStock)
    .OrderByDescending(p =&gt; p.Price);

Console.WriteLine(query.ToEsqlString());
// FROM products | WHERE in_stock == true | SORT price_usd DESC<p><a href="https://www.nuget.org/packages/Elastic.Clients.Esql"><strong><code>Elastic.Clients.Esql</code></strong></a> est un client ES|QL léger et autonome. Il ajoute l'exécution HTTP en plus de <code>Elastic.Esql</code> via <code>Elastic.Transport</code>. Si votre application n'a besoin que d'ES|QL et d'aucune autre API Elasticsearch, il s'agit de l'option de dépendance minimale.</p><p><a href="https://www.nuget.org/packages/Elastic.Clients.Elasticsearch"><strong><code>Elastic.Clients.Elasticsearch</code></strong></a> est le client complet Elasticsearch .NET. Il s'appuie également sur <code>Elastic.Esql</code> et expose le fournisseur LINQ via l'espace de noms <code>client.Esql</code>. C'est le point d'entrée recommandé pour la plupart des applications.</p><p>Les deux packages de la couche d'exécution fournissent leur propre implémentation de <code>IEsqlQueryExecutor</code>, l'interface de stratégie qui fait le lien entre la traduction et le transport.</p><p>Les trois packages sont compatibles avec Native AOT lorsqu'ils sont utilisés avec un <code>JsonSerializerContext</code> généré par la source. Pour le client complet, consultez la <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/dotnet/source-serialization#native-aot">documentation Native AOT</a>.</p><h2>Au-delà des bases</h2><p>L'exemple ci-dessus traitait du filtrage, du tri et de la pagination. Le fournisseur prend en charge un ensemble d'opérations plus étendu.</p><h3>Agrégations</h3><p><code>GroupBy</code>, associé aux fonctions d'agrégation dans <code>Select</code>, se traduit en ES|QL <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/stats-by"><code>STATS ... BY</code></a>par :</p>var stats = client.Esql.Query&lt;Product, object&gt;(q =&gt; q
    .GroupBy(p =&gt; p.Brand)
    .Select(g =&gt; new
    {
        Brand = g.Key,
        Count = g.Count(),
        AvgPrice = g.Average(p =&gt; p.Price),
        MaxPrice = g.Max(p =&gt; p.Price)
    }));

// -&gt; FROM products | STATS COUNT(*), AVG(price_usd), MAX(price_usd) BY brand<h3>Projections</h3><p><code>Select</code>, avec des types anonymes, génère les commandes <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/eval"><code>EVAL</code></a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/keep"><code>KEEP</code></a> et <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/rename"><code>RENAME</code></a> :</p>var query = client.Esql.CreateQuery&lt;Product&gt;()
    .Select(p =&gt; new { ProductName = p.Name, p.Price, p.InStock });

// -&gt; FROM products | KEEP name, price_usd, in_stock | RENAME name AS ProductName<h3>Bibliothèque riche en fonctions</h3><p>Plus de 80 fonctions ES|QL sont disponibles via la classe <code>EsqlFunctions</code>, couvrant la gestion des dates et heures, des chaînes de caractères, des opérations mathématiques, des adresses IP, la correspondance de modèles et le calcul de scores. Les méthodes standard <code>Math.*</code> et <code>string.*</code> se traduisent également par :</p>.Where(p =&gt; p.Name.Contains("Pro"))       // -&gt; WHERE name LIKE "*Pro*"
.Where(p =&gt; EsqlFunctions.CidrMatch(      // -&gt; WHERE CIDR_MATCH(ip, "10.0.0.0/8")
    p.IpAddress, "10.0.0.0/8"))<h3>LOOKUP JOIN</h3><p>Les recherches par index croisé se traduisent en ES|QL <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/lookup-join"><code>LOOKUP JOIN</code></a>par :</p>var enriched = client.Esql.Query&lt;Product, object&gt;(q =&gt; q
    .LookupJoin&lt;Product, CategoryLookup, string, object&gt;(
        "category-lookup-index",
        product =&gt; product.Id,
        category =&gt; category.CategoryId,
        (product, category) =&gt; new { product.Name, category!.CategoryLabel }));<h3>Séquence d'échappement pour ES|QL brut</h3><p>Pour les fonctionnalités ES|QL non encore prises en charge par le fournisseur LINQ, vous pouvez ajouter des fragments bruts :</p>var results = client.Esql.Query&lt;Product&gt;(q =&gt; q
    .Where(p =&gt; p.InStock)
    .RawEsql("| EVAL discounted = price_usd * 0.9"));<h3>Requêtes asynchrones côté serveur</h3><p>Pour les requêtes de longue durée, soumettez-les pour un traitement en arrière-plan sur le serveur :</p>await using var asyncQuery = await client.Esql.SubmitAsyncQueryAsync&lt;Product&gt;(
    q =&gt; q.Where(p =&gt; p.InStock),
    asyncQueryOptions: new EsqlAsyncQueryOptions
    {
        WaitForCompletionTimeout = TimeSpan.FromSeconds(5),
        KeepAlive = TimeSpan.FromMinutes(10)
    });

await asyncQuery.WaitForCompletionAsync();
await foreach (var product in asyncQuery.AsAsyncEnumerable())
    Console.WriteLine(product.Name);<p>Les requêtes asynchrones côté serveur sont particulièrement utiles pour les requêtes analytiques de longue durée/le traitement de grands ensembles de données, qui peuvent dépasser les seuils de délai d'expiration habituels, ou dans les environnements sensibles aux délais d'expiration avec équilibreurs de charge, passerelles API ou proxys qui imposent des délais d'expiration HTTP stricts. Les requêtes asynchrones évitent les interruptions de connexion en découplant la soumission et la récupération des résultats.</p><h2>Premiers pas</h2><p>LINQ to ES|QL est disponible à partir de :</p><ul><li><p><strong>Elastic.Clients.Elasticsearch v9.3.4</strong> (branche 9.x)</p></li><li><p><strong>Elastic.Clients.Elasticsearch v8.19.18</strong> (branche 8.x)</p></li></ul><p>Installation depuis NuGet :</p><p><code>dotnet add package Elastic.Clients.Elasticsearch</code></p><p>Les points d’entrée sont sur <code>client.Esql</code>:</p><p>Méthode</p><p>Retours</p><p>Cas d'utilisation</p><p>Query&lt;T&gt;(...)</p><p>IEnumerable&lt;T&gt;</p><p>Exécution synchrone</p><p>QueryAsync&lt;T&gt;(...)</p><p>IAsyncEnumerable&lt;T&gt;</p><p>Streaming asynchrone</p><p>CreateQuery&lt;T&gt;()</p><p>IEsqlQueryable&lt;T&gt;</p><p>Composition et inspection avancées</p><p>SubmitAsyncQueryAsync&lt;T&gt;(...)</p><p>EsqlAsyncQuery&lt;T&gt;</p><p>Requêtes de longue durée côté serveur</p><p>Pour une description complète des fonctionnalités, notamment les options de requête, l'accès à plusieurs champs, les objets imbriqués et la gestion des champs à valeurs multiples, consultez la <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/dotnet/linq-to-esql">documentation LINQ to ES|QL</a>.</p><h2>Conclusion</h2><p>LINQ to ES|QL apporte toute la puissance d'expression de LINQ to C# au langage de requêtes ES|QL d'Elasticsearch, vous permettant d'écrire des requêtes fortement typées et composables sans avoir à les concevoir manuellement. Grâce à la capture automatique des paramètres, la matérialisation en flux continu et une architecture de packages modulaire scalable, allant d'une simple traduction à un client Elasticsearch complet, il s'intègre naturellement aux applications .NET de toute taille. applications .NET de toute taille. Installez le client le plus récent, configurez vos expressions LINQ pour qu'elles pointent vers un index, et laissez le fournisseur gérer le reste.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/linq-esql-c-elasticsearch-net-client</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/linq-esql-c-elasticsearch-net-client</guid>
    <category><![CDATA[ES|QL]]></category>
    <category><![CDATA[Base vectorielle]]></category>
    <dc:creator><![CDATA[Florian Bernd,Martijn Laarman]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdfa35fbcbbf4959f/6a1705b9dc55de19a4e00d07/e54132e915217063e9ed0ec45059c6cfc38e31dd-1280x720.png" length="0" type="image/png"/>
    <pubDate>Wed, 01 Apr 2026 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[Evolution du client Elasticsearch .NET : De NEST à Elastic.Clients.Elasticsearch]]></title>
    <description><![CDATA[Découvrez l'évolution du client Elasticsearch .NET et la transition de NEST à Elastic.Clients.Elasticsearch.]]></description>
    <content:encoded><![CDATA[<h2>Introduction au client .NET et à NEST</h2><p>Dans le monde .NET, l'intégration avec Elasticsearch est depuis longtemps facilitée par la bibliothèque <code>NEST</code>, qui sert d'interface robuste aux développeurs pour interagir avec les puissantes capacités de recherche et d'analyse d'Elasticsearch. <code>NEST</code>née de la nécessité de disposer d'un client .NET natif pour Elasticsearch, a rapidement gagné en popularité auprès des développeurs en raison de la richesse de ses fonctionnalités et de ses capacités d'intégration transparentes.</p><p>Depuis près de <a href="https://github.com/elastic/elasticsearch-net/commit/724f932ba598915c8c3c35a19827fdfa4f782c1d">14 ans</a> et seulement 8 mois après le <a href="https://github.com/elastic/elasticsearch/commit/ec72ca8b7a115f9b2eea3c76c518062b99a1d015">premier commit d'Elasticsearch</a>, NEST suit fidèlement les versions d'Elasticsearch.</p><h2>Passage de NEST à Elastic.Clients.Elasticsearch</h2><p>Avec l'évolution d'Elasticsearch, la maintenance de la base de code complexe de <code>NEST</code> est devenue de plus en plus difficile. Nous avons reconnu la nécessité d'une approche plus durable du développement des clients et nous avons entrepris de redéfinir le client .NET à partir de zéro. Il nous a fallu près d'un an pour publier une première version bêta et un an de plus pour nous rapprocher de la prise en charge de tous les points d'extrémité du serveur. L'une des décisions les plus difficiles à prendre a été de réduire la portée de la bibliothèque afin de donner la priorité à la maintenabilité.</p><p>Compte tenu de la taille de la surface de l'API Elasticsearch aujourd'hui, il n'est plus pratique de maintenir plus de 450 points d'extrémité et près de 3000 types (demandes, réponses, requêtes, agrégations, etc.) à la main. Pour garantir un alignement cohérent, précis et opportun entre les clients linguistiques et Elasticsearch, les clients 8.x et de nombreux types associés sont désormais automatiquement générés par le code à partir d'une <a href="https://github.com/elastic/elasticsearch-specification">spécification partagée.</a> Il s'agit d'une solution courante pour maintenir l'alignement entre le client et le serveur parmi les SDK et les bibliothèques, tels que ceux pour Azure, AWS et Google Cloud Platform.</p><p>La spécification Elasticsearch a été créée il y a plus de 8 ans en exportant les mappages de types de <code>NEST</code> et grâce au travail acharné de l'équipe clients, nous pouvons maintenant utiliser la même spécification pour créer un nouveau client .NET (et des clients pour de nombreux autres langages tels que Java, Go, etc.)</p><p>Avec la sortie de la version 8.13, l'abandon de <code>NEST</code> a été officiellement annoncé. Avec la transition d'Elasticsearch vers <code>Elastic.Clients.Elasticsearch</code>, <code>NEST</code> sera progressivement supprimé et atteindra sa fin de vie à la fin de l'année. Les développeurs sont fortement encouragés à commencer les efforts de migration dès le début afin d'assurer une transition en douceur et d'atténuer les perturbations potentielles. L'adoption du site <code>Elastic.Clients.Elasticsearch</code> permet non seulement d'assurer la compatibilité avec les dernières fonctionnalités du serveur, mais aussi de protéger les applications contre les fonctionnalités obsolètes.</p><h2>Elastic.Clients.Elasticsearch : aperçu des fonctionnalités et des changements</h2><p>Le passage au client v8 <code>Elastic.Clients.Elasticsearch</code> permet d'accéder à toutes les nouvelles fonctionnalités d'Elasticsearch 8 et apporte également de nombreuses modernisations à la bibliothèque elle-même, mais implique également une réduction des fonctionnalités de commodité par rapport à son prédécesseur. Parmi les nouvelles fonctionnalités de base, citons le langage de requête <code>ES|QL</code>, des capacités modernes d'apprentissage automatique (ML) et des diagnostics améliorés sous la forme d'activités compatibles avec OpenTelemetry. À partir de la version 8.13, <code>Elastic.Clients.Elasticsearch</code> prend en charge presque toutes les fonctionnalités serveur d'Elasticsearch 8.</p><p>Une rupture importante, par exemple, est liée aux agrégations. Dans <code>NEST</code>, l'utilisation de l'API fluide se présente comme suit :</p>s =&gt; s
.Aggregations(aggs =&gt; aggs
    .Children&lt;CommitActivity&gt;("name_of_child_agg", child =&gt; child
        .Aggregations(childAggs =&gt; childAggs
            .Average("average_per_child", avg =&gt; avg.Field(p =&gt; p.ConfidenceFactor))
            .Max("max_per_child", max =&gt; max.Field(p =&gt; p.ConfidenceFactor))
            .Min("min_per_child", min =&gt; min.Field(p =&gt; p.ConfidenceFactor))
        )
    )
)
<p>tandis que le client v8 requiert la syntaxe suivante :</p>s =&gt; s
.Aggregations(aggs =&gt; aggs
	.Add("name_of_child_agg", agg =&gt; agg
		.Children(_ =&gt; {})
		.Aggregations(childAggs =&gt; childAggs
			.Add("average_per_child", agg =&gt; agg.Avg(avg =&gt; avg.Field(p =&gt; p.ConfidenceFactor)))
			.Add("max_per_child", agg =&gt; agg.Max(max =&gt; max.Field(p =&gt; p.ConfidenceFactor)))
			.Add("min_per_child", agg =&gt; agg.Min(min =&gt; min.Field(p =&gt; p.ConfidenceFactor)))
		)
	)
)
<h2>Migration de NEST v7 vers le client .NET v8</h2><p>Un guide de migration complet est disponible ici : <a href="https://www.elastic.co/guide/en/elasticsearch/client/net-api/8.18/migration-guide.html">Guide de migration : De NEST v7 à .NET Client v8</a>.</p><h2>Ressources supplémentaires</h2><ul><li><p><a href="https://github.com/elastic/elasticsearch-net">Elastic.Clients.Elasticsearch v8 Client sur GitHub</a></p></li><li><p><a href="https://www.nuget.org/packages/Elastic.Clients.Elasticsearch">Elastic.Clients.Elasticsearch v8 Client sur NuGet</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/net-client-evolution</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/net-client-evolution</guid>
    <category><![CDATA[.NET]]></category>
    <dc:creator><![CDATA[Florian Bernd]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltac0adcdaa85d703f/6a17f5816df731db170a1079/d09f7f5cb468d5e84f7f4636d92b3476e6604e11-1024x1024.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 16 Apr 2024 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>