<?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[Elasticsearch Labs]]></title>
        <description><![CDATA[Artikel und Tutorials vom Search-Team bei Elastic]]></description>
        <link>https://www.elastic.co/search-labs/de</link>
        <image>
            <url>https://www.elastic.co/search-labs/favicon.svg</url>
            <title>Elasticsearch Labs</title>
            <link>https://www.elastic.co/search-labs/de</link>
        </image>
        <generator>Elasticsearch Labs</generator>
        <lastBuildDate>Sun, 19 Apr 2026 21:34:06 GMT</lastBuildDate>
        <atom:link href="https://www.elastic.co/search-labs/de/rss/feed" rel="self" type="application/rss+xml"/>
        <copyright><![CDATA[© 2026. Elasticsearch B.V. Alle Rechte vorbehalten]]></copyright>
        <language><![CDATA[de]]></language>
        <item>
            <title><![CDATA[Warum die E-Commerce-Suche Governance benötigt]]></title>
            <description><![CDATA[Erfahren Sie, warum E-Commerce-Suche ohne Governance unzureichend ist und wie eine Kontrollschicht vorhersehbare und absichtsgesteuerte Ergebnisse gewährleistet und damit die Suche verbessert.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/ecommerce-search-governance-improve-retrieval</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/ecommerce-search-governance-improve-retrieval</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Alexander Marquardt, Honza Král, Taylor Roy]]></dc:creator>
            <pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3793259b01a5653a7520393a2f006610de0d21e7-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>E-Commerce-Händler müssen verschiedene grundlegend unterschiedliche Abfragetypen innerhalb desselben Systems handhaben. Ein Kunde, der nach „Orangen“ sucht, erwartet die Frucht selbst, nicht Produkte, die das Wort „Orange“ enthalten, wie Orangensaft oder Orangenmarmelade, und auch nicht semantisch verwandte Zitrusprodukte. Ein Käufer, der nach einem „Geschenk für Opa, der gerne Süßes mag“ sucht, benötigt semantische Erkennung, nicht die wörtliche Übereinstimmung mit Schlüsselwörtern.</p><p>Die <em>lexikalische Suche</em> (Textabgleich), die <em>semantische Suche</em> (Abgleich von Konzepten) und die <em>hybride Suche</em> (Kombination lexikalischer und semantischer Signale) lösen diese Probleme für sich genommen nicht. Bei der lexikalischen Suche werden möglicherweise alle Ergebnisse angezeigt, die das Wort „Orangen“ enthalten, während sich die rein semantische Suche bei einer Suchanfrage mit hoher Intentionsstärke wie „Orangen“ auf verwandte Begriffe wie Zitronen oder Grapefruits ausweiten kann. Die hybride Abfrage kombiniert diese lexikalischen und semantischen Signale, kann jedoch nach wie vor nicht entscheiden, ob diese Abfrage als navigatorisch zu behandeln ist, welche Einschränkungen durchgesetzt werden sollten oder welche geschäftlichen Richtlinien gelten sollten. Das Problem liegt nicht in der Abruftechnologie selbst, sondern im Fehlen einer Steuerungsebene, die erkennt, um welche Art von Abfrage es sich handelt und welche Einschränkungen vor Beginn des Abrufs durchgesetzt werden müssen.</p><p>In diesem Blogbeitrag befassen wir uns mit der Steuerung der E-Commerce-Suche, ihrer Bedeutung und der Frage, wie eine Kontrollschicht für vorhersehbare und präzise Suchergebnisse sorgt.</p><h2>Was Governance in der E-Commerce-Suche bedeutet</h2><p><em>Governance</em> bedeutet in diesem Zusammenhang, eine Entscheidungsebene zwischen der Abfrage des Nutzers und der Abruf-Engine einzuführen. Diese Ebene erfüllt die folgenden Funktionen:</p><ul><li>Klassifiziert die Suchabsicht: Handelt es sich um eine Navigation („Orangen“) oder um eine Suche („Geschenk für Opa“)?</li><li>Wendet geschäftliche Vorgaben an: Welche Kategoriegrenzen, Zulassungsregeln, Verfügbarkeitsbeschränkungen oder Merchandising-Richtlinien gelten?</li><li>Wege zur geeigneten Strategie: Sollte hierbei lexikalisches Abrufen, semantisches Abrufen oder ein hybrider Ansatz zum Einsatz kommen?</li></ul><p>Eine Governance-Ebene legt fest, welcher Abrufansatz für jede Abfrage verwendet werden soll, welche Einschränkungen durchgesetzt werden müssen und welche Geschäftsrichtlinien vor Beginn des Abrufs gelten sollen. Es ist wichtig, Governance nicht mit dem hybriden Abruf zu verwechseln: Der hybride Abruf ist eine Abrufstrategie, die lexikalische und semantische Signale kombiniert, während Governance die vorgelagerte Entscheidungsebene ist, die bestimmt, ob lexikalische, semantische oder hybride Signale verwendet werden sollen.</p><h2>Der Status quo: Die „Spaghetti“-Implementierung auf Anwendungsebene</h2><p>Derzeit versuchen viele Händler, dieses Problem zu lösen, indem sie die Logik direkt in die Anwendungsschicht integrieren. Dies führt oft zu <em>Spaghetti-Code</em>, also zu Tausenden von Zeilen fest codierter if-then-Anweisungen, Regex und komplexen Suchvorlagen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f532b099ee103458e15563a711dae92952f8df02-1024x765.png" style="width: 100%;" /><p>Dieser Ansatz kann die oben gezeigten gewünschten Suchergebnisse liefern; jedoch verursacht er erhebliche betriebliche Reibungsverluste:</p><ul><li><strong>Abhängigkeit von der Entwicklungsabteilung:</strong> Geschäfts-Nutzer und Merchandiser können das Suchverhalten nicht ohne Entwicklungs-Tickets und lange Deployment-Zyklen ändern, die oft mehrere Wochen dauern.</li><li><strong>Fragmentierung:</strong> Die Suchlogik verteilt sich auf den Anwendungscode und die Suchvorlagen, ist schwer zu erklären oder zu überprüfen und birgt daher Risiken bei der Weiterentwicklung.</li></ul><p>Selbst wenn Teams die Notwendigkeit des Routings erkennen, konzentriert sich die Debatte oft auf die falsche Frage: welche Abrufmethode gewählt werden soll.</p><h2>Die falsche Wahl: Lexikalisch vs. semantisch vs. hybrid</h2><p>Suchteams betrachten diese Herausforderung häufig als eine Frage der Wahl der Abrufstrategie: lexikalisch/BM25 versus semantisch/Vektoren versus hybrid. Diese Sichtweise ist zwar nachvollziehbar (die Abrufmethoden spielen eine Rolle), lässt jedoch die häufigste Fehlerquelle in realen Implementierungen außer Acht: Die Verwendung eines einzigen Abrufansatzes für alle Abfragen führt zu suboptimalen Ergebnissen.</p><p>Commerce Search ist eine Mischung aus grundlegend verschiedenen Absichten:</p><ul><li><strong>Deterministische Navigation mit hoher Absicht</strong> („Orangen“, „Milch“, „Schokolade ohne Erdnüsse“, „billiges Olivenöl“).</li><li><strong>Explorative Suche</strong> („Jacke zum Wandern in den Bergen“, „Geschenk für ein 12‑jähriges Kind, das Robotik mag“).</li><li><strong>Betriebliche Einschränkungen</strong> (Verfügbarkeit, Größe, Preis, Farbe).</li><li><strong>Merchandising und Kampagnen</strong> (Boost, Begraben, saisonale Kampagnen).</li></ul><p>Wenn das System all diese Vorgänge über dieselbe Abrufstrategie abwickelt, sind die Ergebnisse oft auf vorhersehbare Weise systematisch fehlerhaft, da es dem Betriebsmodell an Governance mangelt. Wenn Teams dies nicht als Lücke in der Unternehmensführung erkennen, reagieren sie mit dem einzigen Mittel, das ihnen zur Verfügung steht: noch mehr Feinabstimmung.</p><h2>Warum „Relevanzoptimierung“ zyklisch werden kann.</h2><p>Ohne eine Routing-Schicht verwandelt sich „Relevanz“ oft in einen nie endenden Rückstand:</p><ul><li>Warum zeigt diese Abfrage Zubehör über dem Kernprodukt an?</li><li>Warum hat diese Kopfabfrage plötzlich verwandte Ergebnisse angezeigt?</li><li>Warum änderten sich die Ergebnisse, nachdem wir Synonyme hinzugefügt, Analysatoren angepasst oder Hybrid aktiviert hatten?</li><li>Warum benötigt das Business-Team ein Engineering-Release, um eine einzelne Abfrage zu beheben?</li></ul><p>Die Teams reagieren mit weiteren Optimierungen: mehr Synonyme, mehr Boosts, mehr Experimente zur Neugewichtung, mehr Ausnahmen im Anwendungscode. Dies kann eine Zeit lang funktionieren, führt jedoch häufig zu instabilem Verhalten, da dem System nach wie vor eine explizite Entscheidungsebene fehlt, um den Abfragetyp zu bestimmen und die richtigen Einschränkungen vor dem Abruf durchzusetzen.</p><h2>Die Anatomie der E-Commerce-Absicht: „Head“ und „Tail“</h2><p>In diesem Abschnitt verwenden wir „Head“ und „Tail“ als praktische Kurzform für gängige Navigations- und Suchmuster im E-Commerce. In der realen Welt enthalten viele Anfragen Aspekte von beidem:</p><h3>„Head“-Abfragen (deterministische Absicht)</h3><p>Dies sind direkte, navigationsbezogene Abfragen, bei denen der Nutzer genau weiß, was er möchte:</p><ul><li>Absicht in Bezug auf einen einzelnen Artikel (&quot;Orangen&quot;, &quot;Milch&quot;, &quot;Brot&quot;).</li><li>Genaue Marken oder Produktfamilien („iPhone 15 Pro“, „Diet Coke“).</li><li>Artikelnummern, Modellnummern, Größen (&quot;ABC123&quot;, &quot;air max 270&quot;).</li></ul><p>Für diese Abfragen kann die lexikalische Suche die Token-Korrespondenz (Wortübereinstimmung) abdecken, aber das Unternehmen erwartet außerdem, dass Constraints eingehalten werden, vorhersehbare Rankings zurückgegeben werden und die Ergebnisse steuerbar sind. Ein Merchandiser muss sicherstellen, dass eine Abfrage innerhalb der richtigen Kategoriegrenzen aufgelöst wird, die Eligibility-Kriterien einhält und bestimmte geschäftliche Prioritäten sichtbar macht.</p><p>Governance ist erforderlich, um die beabsichtigte Lösung durchzusetzen. Zum Beispiel sollten „Orangen“ der Kategorie Frischwaren zugeordnet werden, nicht Orangensaft, Orangenmarmelade oder Orangenlimonade.</p><h3>„Tail“-Abfragen (explorative Datensuche)</h3><p>Dies sind beschreibende, absichtsvolle Suchanfragen, mit denen Käufer recherchieren:</p><ul><li>„Geschenk für Opa mit einer Vorliebe für Süßes“</li><li>„Jacke zum Wandern in den Bergen“</li><li>„Schuhe, um den ganzen Tag zu stehen“</li></ul><p>Der lexikalische Abruf hat hier oft Schwierigkeiten. Der semantische Abruf ist überlegen, weil er das Abfragekonzept mit dem Produkt verknüpfen kann, selbst wenn die Formulierungen nicht übereinstimmen. Aber auch der semantische Abruf allein reicht selten aus. Abfragen aus der Praxis erfordern oft, dass Constraints durchgesetzt werden – unabhängig davon, welche Abrufmethode verwendet wird.</p><h2>Die Einschränkungen sind orthogonal zur Abrufmethode.</h2><p>Das Anwenden von Einschränkungen auf semantische Abrufe bedeutet nicht <em>hybride Suche</em>. Dies sind orthogonale Konzepte. Einschränkungen wie Filter und Boosts in Elasticsearch können auf jede lexikalische, semantische oder hybride Suche angewendet werden. Die Herausforderung besteht darin, zu entscheiden, wie die Abfrage interpretiert werden soll, welche Einschränkungen durchgesetzt werden müssen und welche Abfragemethode verwendet werden soll.</p><p>Im Folgenden finden Sie einige Beispiele für Abfragen, die die Datensuche mit festen Einschränkungen kombinieren:</p><ul><li><strong>Orangen:</strong> Lexikalische Suche nach „Orangen“ unter Hinzunahme einer Kategoriebeschränkung, wie beispielsweise „Obst“ oder „Frischwaren“, wodurch Orangenmarmelade, Orangensaft und Orangenlimonade ausgeschlossen werden.</li><li><strong>Früchte mit hohem Vitamin-C-Gehalt unter 4 $:</strong> Semantische Suche nach dem Nährwert plus Einschränkungen, die die Ergebnisse auf die Kategorie Obst und Produkte unter 4 $ beschränken.</li><li><strong>Bequeme Schuhe für die Arbeit:</strong> Semantischer Informationsabruf für kontextuelle Absicht plus eine Kategoriebeschränkung, die die Ergebnisse auf Schuhe begrenzt.</li></ul><p>Diese Abfragen können nicht mit einem einzigen Ansatz bearbeitet werden:</p><ul><li>Ein <strong>rein lexikalischer Abruf</strong> ist hier oft unzureichend, da Phrasen wie „reich an Vitamin C“ oder „komfortabel“ möglicherweise nicht als saubere, strukturierte Attribute existieren. Sie müssen möglicherweise aus Produktbeschreibungen, Bewertungen oder Spezifikationen abgeleitet werden.</li><li>Auch eine <strong>rein semantische Suche</strong> reicht nicht immer aus, da eine Suchanfrage wie „vitamin-C-reiche Früchte“ ohne explizite Einschränkungen auf Vitaminpräparate, Getränke mit Fruchtgeschmack oder vitaminreiches Gemüse außerhalb der beabsichtigten Kategorie und Preisklasse ausgeweitet werden könnte.</li></ul><p>Eine Steuerungsebene legt fest, ob eine Abfrage eine lexikalische Suche, ein semantisches Verständnis, die Durchsetzung von Einschränkungen oder eine Kombination dieser Elemente erfordert. Ohne diese Ebene könnten E-Commerce-Teams in folgende Situation geraten:</p><ul><li><strong>Übermäßige Einschränkung:</strong> Verwendung lexikalischer Abfragen für semantische Anfragen (zum Beispiel „Geschenk für Opa“).</li><li><strong>Unterbeschränkung: </strong>Verwendung semantischer Abfragen für Head-Abfragen mit hoher Intention (z. B. „Orangen“).</li></ul><p>Die Herausforderung im Bereich der Governance besteht darin, ein System zu entwickeln, das für jede Art von Anfrage die richtige Entscheidung treffen kann.</p><h2>Was passiert ohne Governance?</h2><p>Die häufigste Fehlerquelle ist ganz einfach: Teams nehmen die rohe Benutzeranfrage und leiten sie direkt an eine einzige Abrufstrategie weiter (lexikalisch, semantisch oder hybrid), ohne eine zwischengeschaltete Governance-Ebene.</p><h3>Die lexikalische Suche verfehlt die beabsichtigte Auflösung.</h3><p>Wenn ein Nutzer nach &quot;Orangen&quot; sucht, kann eine lexikalische Suchstrategie alles zurückgeben, was dieses Token enthält: Orangensaft, Orangenmarmelade oder Orangenlimonade. Das System hat den Begriff korrekt zugeordnet, aber ohne Governance kann es den beabsichtigten Einkaufskontext (die Frucht) nicht auflösen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/99abc7a46f9c56a26a68d0a089d7ab830b9b5568-1560x814.png" style="width: 100%;" /><h3>Der semantische Abruf weitet sich über die beabsichtigten Einschränkungen hinaus aus</h3><p>Wenn ein Nutzer nach „Orangen“ sucht, kann ein semantisches System konzeptionell verwandte Elemente aus nahegelegenen Produktkonzepten abrufen. Das System versteht zwar den übergeordneten Bereich (Obst oder Gemüse), aber ohne explizite Steuerung kann es dennoch über die vom Nutzer beabsichtigte Beschränkung (speziell Orangen) hinausgehen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c9de86363ecbed499fe48259f47b3c5b2c26bc43-1568x796.png" style="width: 100%;" /><h3>Die Lücke ist Governance</h3><p>Erforderlich ist eine vorgelagerte Entscheidungsebene, die die Abfrageabsicht bestimmt und die richtigen Einschränkungen durchsetzt, bevor der Abruf beginnt. Dadurch werden Probleme wie die folgenden behoben:</p><ul><li>Ähnliche oder verwandte Artikel, die neben dem angezeigt werden, was der Nutzer eigentlich gesucht hat.</li><li>Verschwimmende Kategoriegrenzen („Getränke“ vs. „Frischwaren“).</li><li>Unfähigkeit, saisonale Boosts oder Kampagnen umzusetzen.</li><li>Unvorhersehbare und unerklärliche Ergebnisse.</li></ul><h2>Absichtserkennung und Weiterleitung: Die notwendige Steuerungsebene</h2><p>Ein gesteuertes Suchsystem führt eine schlanke Steuerungsebene vor dem Abruf ein (vor der Ausführung einer Abfrage in Elasticsearch). Auf dieses Steuerelement wird in den Teilen 3 und 4 dieser Blogreihe ausführlich eingegangen; vorerst beschränken wir uns darauf, zu erläutern, was es leisten kann, ohne jedoch auf seine Funktionsweise einzugehen:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/88c3d0f9731a128d73a765dcdffed897308110a6-2680x766.png" style="width: 100%;" /><p>Eine Steuerungsebene kann Absichten erkennen, Geschäftsrichtlinien anwenden und die entsprechende Abrufstrategie wie folgt sicherstellen:</p><p><strong>1. Absichtssignale erkennen</strong></p><ul><li>Ist diese Abfrage wahrscheinlich Navigation oder Entdeckung?</li><li>Handelt es sich um eine bekannte Head-Abfrage (Milch, Brot, Bananen)?</li><li>Gibt es eine bekannte Interpretation für ein Produkt, eine Marke oder eine Kategorie (beispielsweise sollte „Orangen“ zu „Frischwaren“ führen)?</li><li>Ist die Abfrage ein SKU-ähnliches Muster?</li><li>Fällt die Abfrage unter eine aktive Kampagne oder eine saisonale Richtlinie (beispielsweise während der Weihnachtszeit, um Suchergebnisse zum Thema Truthahn stärker hervorzuheben)?</li><li>Impliziert die Abfrage Einschränkungen (Kategorie, Attribute, Ausschlüsse, Preis/Größe/Farbe)?</li></ul><p><strong>2. Setzen Sie Governance- und Geschäftsrichtlinien um.</strong></p><ul><li>Wenden Sie zunächst deterministische Einschränkungen an (Kategorie/Attribut/Negation/Verfügbarkeit).</li><li>Wenden Sie aktive Merchandising-Maßnahmen an (hervorheben/verbergen/fixieren/überschreiben).</li><li>Lösen Sie Konflikte mithilfe von Prioritätsregeln (z. B. Kampagnen-Überschreibungen gegenüber globalen Richtlinien).</li></ul><p><strong>3. Weg zur geeigneten Abrufstrategie</strong></p><ul><li>Lexikalisch (schnell, deterministisch) für navigationsorientierte Suchanfragen mit hoher Kaufabsicht.</li><li>Semantische Suche für echte Entdeckungsanfragen.</li><li>Ein hybrider Ansatz, bei dem kombinierte lexikalische und semantische Signale unter expliziten geschäftlichen Vorgaben einen Mehrwert schaffen.</li></ul><p>In der Praxis ist der Ausgang der Steuerungsebene nicht einfach „Hybrid verwenden“ oder „Semantik verwenden“. Es handelt sich um einen geregelten Abrufplan: eine Interpretation der Absicht des Kunden, der geltenden Einschränkungen und Richtlinien sowie der auszuführenden Abrufstrategie. Ein paar einfache Beispiele verdeutlichen dies:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Kundenabfrage</td><td style="border: 1px solid #ccc; padding: 4px;">Gesteuerte Interpretation</td><td style="border: 1px solid #ccc; padding: 4px;">Beispiel-Abrufplan</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">„Schokolade ohne Erdnüsse“</td><td style="border: 1px solid #ccc; padding: 4px;">Produktorientierte Abfrage mit einer strengen Ausschlussbedingung</td><td style="border: 1px solid #ccc; padding: 4px;">Lexikalische Suche nach „Schokolade“ sowie ein Ausschlussfilter für Produkte, die Erdnüsse enthalten</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">„billiges Olivenöl“</td><td style="border: 1px solid #ccc; padding: 4px;">Produkt-/Kategorie-Abfrage mit einer Preisbeschränkung</td><td style="border: 1px solid #ccc; padding: 4px;">Lexikalischer Abruf für Olivenöl plus ein Preisfilter, der auf den Schwellenwert des Einzelhändlers für „günstig“ begrenzt ist</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">„Obst reich an Vitamin C unter 4 $“</td><td style="border: 1px solid #ccc; padding: 4px;">Discovery-Abfrage, die semantisches Verständnis sowie strikte Einschränkungen erfordert</td><td style="border: 1px solid #ccc; padding: 4px;">Semantische Suche nach Produkten für Ernährungszwecke, beschränkt auf die Kategorie „Obst“ und gefiltert nach Produkten mit einem Preis unter 4 $</td>
                    </tr>
              </tbody>
            </table>
          <p>Eine Steuerungsebene wählt für jede Abfrage konsistent, vorhersehbar und in großem Maßstab die richtige Richtlinie und Abrufstrategie aus. Dies macht fortgeschrittene Abrufmethoden in der Produktion vorhersehbarer, da absichtsbasierte Einschränkungen zuerst durchgesetzt werden und Routing-Entscheidungen explizit statt implizit getroffen werden.</p><h2>Wie dies mit anderen Ansätzen zusammenhängt</h2><p>Einige Teams nutzen verbesserte Einbettungsmodelle, um die Produktsemantik besser zu erfassen, was die Qualität der semantischen Suche erheblich verbessern kann. Andere nutzen Ansätze zur Neureihenfolge, wie beispielsweise <a href="https://www.elastic.co/docs/solutions/search/ranking/learning-to-rank-ltr">Learning To Rank (LTR)</a>, um die Reihenfolge der Ergebnisse nach der Abfrage auf der Grundlage von Interaktions- oder geschäftsbezogenen Signalen zu optimieren. Beide sind wertvoll und ergänzen sich oft. Bessere Einbettungen verbessern den Ähnlichkeitsabgleich. Durch die Neubewertung wird die Reihenfolge der abgerufenen Kandidaten verbessert.</p><p>Governance befasst sich mit einer anderen Ebene des Problems: Sie ist dem Abrufprozess vorgelagert. Es entscheidet, welche Abrufstrategie verwendet wird (zum Beispiel lexikalisch, semantisch oder hybrid), welche deterministischen Einschränkungen erforderlich sind und welche Abfragen mehrere Geschäftsrichtlinien kombinieren sollten.</p><h2>Was eine gesteuerte Steuerungsebene ermöglicht</h2><p>Sobald eine Governance-Ebene eingerichtet ist, verändert sich das Betriebsmodell grundlegend. Umsatzrelevante Abfragen lassen sich vorhersagen. Geschäftsteams können das Suchverhalten anpassen, ohne auf die Release-Zyklen der Entwickler warten zu müssen. Und fortgeschrittene Abrufmethoden, wie semantische und hybride Verfahren, können schrittweise eingeführt werden – im Rahmen von Routing-Regeln und Sicherheitsvorkehrungen – anstatt als globaler Ein-/Aus-Schalter.</p><p>Der nächste Beitrag dieser Reihe befasst sich damit, wie dieses Betriebsmodell in der Praxis aussieht und warum es möglicherweise genauso wichtig ist wie die ihm zugrunde liegende Suchtechnologie.</p><p>Wenn ein Merchandiser ein Jira-Ticket erstellen und auf eine Bereitstellung warten muss, um eine umsatzkritische Abfrage zu beheben, liegt der Engpass nicht in der Engine, sondern im Betriebsmodell. Eine moderne E-Commerce-Suche muss in der Lage sein, geschäftliche Absichten schnell und sicher in kontrolliertes, überprüfbares Suchverhalten umzusetzen, dabei aber weiterhin auf erweiterte Suchfunktionen zurückzugreifen, wo diese einen messbaren Mehrwert bieten.</p><h2>Wie geht es weiter in dieser Serie?</h2><p>Die in dieser Reihe behandelten Muster greifen bereits vor der Abfrage an: Sie dienen dazu, geschäftliche Absichten in die richtige Abfragestrategie umzusetzen, noch bevor die Abfrage generiert wird. Im nächsten Beitrag wechseln wir vom technischen zum operativen Problem: Was passiert, wenn Geschäftsteams das Suchverhalten ohne ein technisches Deployment ändern können, und warum Governance das sicher macht?</p><h2>Setzen Sie die reglementierte E-Commerce-Suche in die Praxis um</h2><p>Technische Engpässe, instabile Logik auf Anwendungsebene und unvorhersehbare Suchergebnisse sind Probleme, bei deren Lösung Ihnen Elastic Services im Rahmen von E-Commerce-Projekten für Unternehmen behilflich sein kann. Die in dieser Reihe beschriebene Architektur der verwalteten Steuerungsebene wurde von Elastic Services Engineering entwickelt.</p><p>Wenn Ihr Team Entwicklungsressourcen darauf verwendet, Merchandising-Anforderungen in Codeänderungen umzusetzen, oder wenn Ihr Rückstand bei der Suchrelevanz scheinbar nie abnimmt, können wir Ihnen dabei helfen, Ihre aktuelle Architektur zu bewerten und einen Weg zu einer kontrollierten, vom Geschäftsteam editierbaren Suche zu ebnen. Kontaktieren Sie <a href="https://www.elastic.co/consulting">Elastic Services</a>.</p><h2>Nehmen Sie an der Diskussion teil</h2><p>Haben Sie Fragen zur Suchsteuerung, zu Abrufstrategien oder zur Sucharchitektur im E-Commerce? Nehmen Sie an der <a href="https://discuss.elastic.co/">Diskussion der Elastic-Community</a> teil.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[So erstellen Sie agentische KI-Anwendungen mit Mastra und Elasticsearch]]></title>
            <description><![CDATA[Lernen Sie anhand eines praktischen Beispiels, wie Sie agentische KI-Anwendungen mit Mastra und Elasticsearch erstellen.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/build-agentic-ai-applications-mastra-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/build-agentic-ai-applications-mastra-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Enrico Zimuel]]></dc:creator>
            <pubDate>Wed, 08 Apr 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ab30baf2f908534840c5d71a46705773807baf54-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>In diesem Artikel zeigen wir Ihnen, wie Sie mit dem TypeScript-Framework von <a href="https://mastra.ai/">Mastra</a> agentische Anwendungen erstellen, die mit <a href="https://www.elastic.co/elasticsearch">Elasticsearch</a> interagieren.</p><p>Wir haben kürzlich zum Open-Source-Projekt <a href="https://github.com/mastra-ai/mastra">mastra-ai/mastra</a> beigetragen, indem wir Unterstützung für Elasticsearch als Vektordatenbank hinzugefügt haben. Mit diesem neuen Feature können Sie Elasticsearch nativ in Mastra verwenden, um Einbettungen zu speichern. Zusätzlich zu Vektoren bietet Elasticsearch eine Reihe erweiterter Features, die alle Ihre Anforderungen an das Kontext-Engineering erfüllen. (zum Beispiel <a href="https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-evolution-agentic-ai">hybride Suche und Reranking</a>).</p><p>Dieser Artikel beschreibt die Erstellung eines Agenten zur Implementierung einer RAG-Architektur (Retrieval-Augmented Generation) mit Elasticsearch. Wir stellen ein Demoprojekt vor, bei dem ein agentischer Ansatz verwendet wird, um mit einem in Elasticsearch gespeicherten Korpus von Science-Fiction-Filmdaten zu interagieren. Das Projekt ist unter <a href="https://github.com/elastic/mastra-elasticsearch-example">elastic/mastra-elasticsearch-example</a> verfügbar.</p><h2>Mastra</h2><p>Mastra ist ein TypeScript-Framework zur Erstellung agentischer KI-Anwendungen.</p><p>Eine Projektstruktur in Mastra sieht wie folgt aus:</p><pre><code>src/
├── mastra/
│   ├── agents/
│   │   └── weather-agent.ts
│   ├── tools/
│   │   └── weather-tool.ts
│   ├── workflows/
│   │   └── weather-workflow.ts
│   ├── scorers/
│   │   └── weather-scorer.ts
│   └── index.ts
├── .env.example
├── package.json
└── tsconfig.json</code></pre><p>In Mastra können Sie <a href="https://mastra.ai/docs/agents/overview">Agenten</a>, <a href="https://mastra.ai/docs/agents/using-tools">Tools</a>, <a href="https://mastra.ai/docs/workflows/overview">Workflows</a> und <a href="https://mastra.ai/docs/evals/overview">Scores</a> erstellen.</p><p>Ein <strong>Agent</strong> ist eine Klasse, die eine Nachricht als Eingabe akzeptiert und eine Antwort als Ausgabe erzeugt. Ein Agent kann Tools, Large Language Models (LLMs) und einem Speicher verwenden (Abbildung 1).</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f6aca2dcc7fcc45d25e06681649be1b2b7eb6781-706x721.png" style="width: 100%;" /><p>Die <strong>Tools</strong> eines Agenten ermöglichen ihm die Interaktion mit der „Außenwelt“, beispielsweise die Kommunikation mit einer Web-API oder die Durchführung einer internen Operation, wie etwa das Abfragen von Elasticsearch. Die <strong>Speicherkomponente</strong> ist entscheidend für die Speicherung des Konversationsverlaufs, einschließlich früherer Ein- und Ausgaben. Dank dieser gespeicherten Kontextinformationen kann der Agent anhand früherer Interaktionen fundiertere und relevantere Antworten auf künftige Fragen geben.</p><p><strong>Workflows</strong> ermöglichen Ihnen die Definition komplexer Aufgabenabläufe anhand klarer, strukturierter Schritte, anstatt sich auf die Argumentation eines einzelnen Agenten zu verlassen (Abbildung 2). Sie geben Ihnen die volle Kontrolle über die Aufteilung von Aufgaben, den Datenaustausch zwischen ihnen und den Zeitpunkt der Ausführung. Workflows werden standardmäßig über die integrierte Ausführungs-Engine ausgeführt oder können auf <a href="https://mastra.ai/docs/deployment/workflow-runners">Workflow-Runner</a> bereitgestellt werden.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0dce161cf7891207015dc87532b5b90df1822432-880x252.png" style="width: 100%;" /><p>In Mastra können Sie außerdem Scores definieren. Dabei handelt es sich um automatisierte Tests, die die Ausgaben der Agenten mithilfe modellbasierter, regelbasierter und statistischer Methoden bewerten. Scorer geben <em>Scores</em> zurück: numerische Werte (in der Regel zwischen 0 und 1), die messen, wie gut eine Ausgabe Ihre Bewertungskriterien erfüllt. Diese Scores ermöglichen Ihnen eine objektive Leistungsüberwachung, den Vergleich verschiedener Ansätze und die Ermittlung von Verbesserungsmöglichkeiten in Ihren KI-Systemen. Scorer können mit Ihren eigenen Prompts und Scoring-Funktionen angepasst werden.</p><h2>Elasticsearch</h2><p>Für die Ausführung des Demoprojekts benötigen wir eine laufende Elasticsearch-Instanz. Sie können eine kostenlose Testversion von <a href="https://www.elastic.co/cloud">Elastic Cloud</a> aktivieren oder Elasticsearch lokal mithilfe des Skripts <a href="https://github.com/elastic/start-local"><code>start-local</code></a> installieren:</p><pre><code>curl -fsSL https://elastic.co/start-local | sh</code></pre><p>Dadurch werden Elasticsearch und Kibana auf Ihrem Computer installiert und ein API-Schlüssel generiert, der zur Konfiguration der Mastra-Integration verwendet wird.</p><p>Der API-Schlüssel wird als Ausgabe des vorherigen Befehls angezeigt und in einer <strong>.env</strong>-Datei im Ordner elastic-start-local gespeichert.</p><h2>Demo installieren und konfigurieren</h2><p>Wir haben ein <a href="https://github.com/elastic/mastra-elasticsearch-example">Elastic/mastra-elasticsearch-example-Repository</a> erstellt, das den Quellcode des Demoprojekts enthält. Das im Repository bereitgestellte Beispiel veranschaulicht, wie Sie in Mastra einen Agenten erstellen, der eine RAG-Architektur zum Abrufen von Dokumenten aus Elasticsearch implementiert.</p><p>Wir haben für die Demo einen Datensatz zu Science-Fiction-Filmen bereitgestellt. Wir haben 500 Filme aus dem IMDb-Datensatz auf <a href="https://www.kaggle.com/datasets/rajugc/imdb-movies-dataset-based-on-genre/versions/2?select=scifi.csv">Kaggle</a> extrahiert.</p><p>Der erste Schritt besteht in der Installation der Abhängigkeiten des Projekts mit npm. Verwenden Sie dazu den folgenden Befehl:</p><pre><code>npm install</code></pre><p>Anschließend müssen wir die <strong>.env</strong>-Datei konfigurieren, die die Einstellungen enthalten wird. Wir können diese Datei generieren, indem wir die Struktur aus der <strong>.env.example-Datei</strong> mit folgendem Befehl kopieren:</p><pre><code>cp .env.example .env</code></pre><p>Nun können wir die .env-Datei bearbeiten, indem wir die fehlenden Informationen hinzufügen:</p><pre><code>OPENAI_API_KEY=
ELASTICSEARCH_URL=
ELASTICSEARCH_API_KEY=
ELASTICSEARCH_INDEX_NAME=scifi-movies</code></pre><p>Der Name des Elasticsearch-Index lautet <strong><code>scifi-movies</code></strong>. Auf Wunsch können Sie ihn mithilfe der .env-Variablen <code>ELASTICSEARCH_INDEX_NAME</code> ändern.</p><p>Wir haben OpenAI als Einbettungsdienst verwendet, was bedeutet, dass Sie einen API-Schlüssel für OpenAI in der .env-Variablen <code>OPENAI_API_KEY</code> angeben müssen.</p><p>Das im Beispiel verwendete Einbettungsmodell ist <a href="https://developers.openai.com/api/docs/models/text-embedding-3-small">openai/text-embedding-3-small</a>, mit einer Einbettungsdimension von 1536.</p><p>Zur Generierung der endgültigen Antwort haben wir das Modell <a href="https://developers.openai.com/api/docs/models/gpt-5-nano">openai/gpt-5-nano</a> verwendet, um die Kosten zu senken.</p><p>Die RAG-Architektur ermöglicht die Verwendung eines weniger leistungsfähigen (und in der Regel kostengünstigeren) finalen LLM-Modells, da die Hauptarbeit der Beantwortung durch die Retrieval-Komponente (in diesem Fall Elasticsearch) erledigt wird.</p><p>Das kleinere LLM ist nur für zwei Hauptaufgaben zuständig:</p><ul><li><strong>Umformulierung/Einbettung der Abfrage:</strong> Umwandlung der in natürlicher Sprache formulierten Frage des Nutzers in eine Vektoreinbettung für die semantische Suche.</li><li><strong>Antwortsynthese:</strong> Die relevanten, abgerufenen Kontextfragmente (Dokumente/Filme) werden gemäß den vorgegebenen Anweisungen zu einer kohärenten, endgültigen und für Menschen lesbaren Antwort zusammengeführt.</li></ul><p>Da der RAG-Prozess <strong>den genauen sachlichen Kontext</strong> liefert, der für die Antwort benötigt wird, muss das endgültige LLM weder umfangreich noch hochkomplex sein und es muss nicht das gesamte erforderliche Wissen innerhalb seiner eigenen Parameter besitzen (was die Stärke großer, teurer Modelle ist). Es fungiert im Wesentlichen als ausgeklügelter Textzusammenfasser und -formatierer für den von Elasticsearch bereitgestellten Kontext und nicht als vollwertige Wissensdatenbank an sich. Auf diese Weise lassen sich Modelle wie <code>gpt-5-nano</code> zur Kosten- und Latenzoptimierung verwenden.</p><p>Nach der Konfiguration der .env-Datei können Sie die Filme mithilfe des folgenden Befehls in Elasticsearch ingestieren:</p><pre><code>npx tsx src/utility/store.ts</code></pre><p>Der Ausgabe sollte wie folgt aussehen:</p><pre><code>🚀 Starting ingestion of 500 movies from 500_scifi_movies.jsonl...
Ingesting ░░░░░░░░░░░░░░░░░░░░░░░░ 1/500 (0%) | ok:1 | fail:0 | chunks:1 | eta:19m 33s | current:Capricorn One
Ingesting ░░░░░░░░░░░░░░░░░░░░░░░░ 2/500 (0%) | ok:2 | fail:0 | chunks:2 | eta:10m 32s | current:Doghouse
Ingesting ░░░░░░░░░░░░░░░░░░░░░░░░ 3/500 (1%) | ok:3 | fail:0 | chunks:3 | eta:7m 33s | current:Dinocroc
Ingesting ░░░░░░░░░░░░░░░░░░░░░░░░ 4/500 (1%) | ok:4 | fail:0 | chunks:7 | eta:6m 10s | current:Back to the Future           
Ingesting ░░░░░░░░░░░░░░░░░░░░░░░░ 5/500 (1%) | ok:5 | fail:0 | chunks:9 | eta:5m 14s | current:The Projected Man            
Ingesting ░░░░░░░░░░░░░░░░░░░░░░░░ 6/500 (1%) | ok:6 | fail:0 | chunks:11 | eta:4m 41s | current:I, Robot
...
✅ Ingestion complete in 1m 46s. Success: 500, Failed: 0, Chunks: 693.</code></pre><p>Das Mapping des Science-Fiction-Filmindex enthält die folgenden Felder:</p><ul><li><strong>Einbettung</strong>, dense_vector mit 1536 Dimensionen, Kosinus-Ähnlichkeit.</li><li><strong>Beschreibung</strong>, Text mit der Beschreibung des Films.</li><li><strong>Regisseur</strong>, Text mit dem Namen des Regisseurs.</li><li><strong>Titel</strong> Text mit dem Titel des Films.</li></ul><p>Wir haben die Einbettungen anhand des Titels und der Beschreibung generiert. Da Titel und Beschreibung zwei separate Felder sind, stellt die Verknüpfung beider sicher, dass der resultierende Einbettungsvektor sowohl die spezifische, einzigartige Identität (Titel) als auch den reichhaltigen, beschreibenden Kontext (Beschreibung) des Films erfasst, was zu genaueren und umfassenderen semantischen Suchergebnissen führt. Durch diese kombinierte Eingabe erhält das Einbettungsmodell eine bessere einheitliche Darstellung des Dokumentinhalts für den Ähnlichkeitsabgleich.</p><h2>Demo ausführen</h2><p>Sie können die Demo mit folgendem Befehl ausführen:</p><pre><code>npm run dev</code></pre><p>Dieser Befehl startet eine Webanwendung unter <strong>localhost:4111</strong>, um auf Mastra Studio zuzugreifen (Abbildung 3).</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1567e309df21a12bcf1dfef4429f82342549956c-1705x1079.png" style="width: 100%;" /><p><a href="https://mastra.ai/docs/getting-started/studio">Mastra Studio</a> bietet eine interaktive Benutzeroberfläche zum Erstellen und Testen Ihrer Agenten sowie eine REST-API, die Ihre Mastra-Anwendung als lokalen Dienst bereitstellt. So können Sie sofort mit der Entwicklung beginnen, ohne sich Gedanken über die Integration machen zu müssen.</p><p>Wir haben einen <strong>Elasticsearch-Agenten</strong> bereitgestellt, der das <a href="https://mastra.ai/reference/tools/vector-query-tool">createVectorQueryTool</a> von Mastra als Tool zur Ausführung semantischer Suchen mit Elasticsearch verwendet. Dieser Agent verwendet den RAG-Ansatz, um relevante Dokumente (d. h. Filme) zu suchen, die die Frage des Nutzers beantworten.</p><p>Dieser Agent verwendet folgenden Prompt:</p><pre><code>You are a helpful assistant that answers questions based on the provided context.
Follow these steps for each response:

1. First, carefully analyze the retrieved context chunks and identify key information.
2. Break down your thinking process about how the retrieved information relates to the query.
3. Draw conclusions based only on the evidence in the retrieved context.
4. If the retrieved chunks don't contain enough information, explicitly state what's missing.

Format your response as:
THOUGHT PROCESS:
- Step 1: [Initial analysis of retrieved chunks]
- Step 2: [Reasoning based on chunks]

FINAL ANSWER:
[Your concise answer based on the retrieved context]

Important: When asked to answer a question, please base your answer only on the context provided in the tool. 
If the context doesn't contain enough information to fully answer the question, please state that explicitly and stop it.
Do not add more information than what is present in the retrieved chunks.
Remember: Explain how you're using the retrieved information to reach your conclusions.</code></pre><p>Wenn Sie auf das Menü <code>Mastra Studio &gt; Agents</code> klicken und <strong>Elasticsearch-Agent</strong> auswählen, können Sie den Agenten über ein Chatsystem testen. Zum Beispiel können Sie Informationen zu Science-Fiction-Filmen mit folgender Frage stellen:</p><p><em>Finden Sie 5 Filme oder Fernsehserien über UFOs</em>.</p><p>Sie werden bemerken, dass der Agent das vectorQueryTool ausführen wird. Sie können auf das aufgerufene Tool klicken, um sich die Eingabe und die Ausgabe anzusehen. Am Ende der Ausführung beantwortet das LLM Ihre Frage unter Berücksichtigung des Kontexts aus dem „scifi-movies“-Index von Elasticsearch (Abbildung 4).</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d9998d4f687984de98845dae52d1288166abf448-1344x1071.png" style="width: 100%;" /><p>Mastra führt intern folgende Schritte aus:</p><ol><li><strong>Vektorumwandlung:</strong> Die Frage des Nutzers, <em>Finde 5 Filme oder TV-Serien über UFOs,</em> wird mithilfe des Modells <code>openai/text-embedding-3-small</code> von OpenAI in eine Vektoreinbettung umgewandelt.</li><li><strong>Vektorsuche:</strong> Diese Einbettung wird dann verwendet, um Elasticsearch über eine Vektorsuche abzufragen.</li><li><strong>Ergebnisabruf:</strong> Elasticsearch gibt eine Liste von 10 Filmen zurück, die für die Suchanfrage besonders relevant sind (d. h. die Filme, deren Vektoren dem Suchvektor des Nutzers am nächsten liegen).</li><li><strong>Antwortgenerierung:</strong> Die abgerufenen Filme und die ursprüngliche Nutzerfrage werden an das LLM gesendet, speziell <code>openai/gpt-5-nano</code>. Das LLM verarbeitet diese Informationen und generiert eine endgültige Antwort, um sicherzustellen, dass die Anfrage des Nutzers nach fünf Ergebnissen erfüllt wird.</li></ol><h2>Der Elasticsearch-Agent</h2><p>Hier haben wir den Quellcode des Elasticsearch-Agenten veröffentlicht.</p><pre><code>import { Agent } from "@mastra/core/agent";
import { ElasticSearchVector } from '@mastra/elasticsearch';
import { createVectorQueryTool } from '@mastra/rag';
import { ModelRouterEmbeddingModel } from "@mastra/core/llm";
import { Memory } from "@mastra/memory";

const es_url = process.env.ELASTICSEARCH_URL;
const es_apikey = process.env.ELASTICSEARCH_API_KEY;
const es_index_name = process.env.ELASTICSEARCH_INDEX_NAME;
const prompt = 'insert here the previous prompt';

const esVector = new ElasticSearchVector({
  id: 'elasticsearch-vector',
  url: es_url,
  auth: {
    apiKey : es_apikey
  }
});

const vectorQueryTool = createVectorQueryTool({
  vectorStore: esVector,
  indexName: es_index_name,
  model: new ModelRouterEmbeddingModel("openai/text-embedding-3-small")
});

export const elasticsearchAgent = new Agent({
  id: "elasticsearch-agent",
  name: "Elasticsearch Agent",
  instructions: prompt,
  model: 'openai/gpt-5-nano',
  tools: { vectorQueryTool },
  memory: new Memory(),
});</code></pre><p>Das <strong>vectorQueryTool</strong> ist das Tool, das zur Implementierung des Abrufteils des RAG-Beispiels aufgerufen wird. Es verwendet die <a href="https://mastra.ai/reference/vectors/elasticsearch">ElasticSearchVector</a>-Implementierung, die Elastic zu Mastra beigetragen hat.</p><p>Der Agent ist ein Objekt der Agentenklasse, das das vectorQueryTool, den Prompt und einen Speicher verbraucht. Wie Sie sehen können, ist der Code, den wir für die Verbindung von Elasticsearch mit einem Agenten implementieren müssen, sehr minimal.</p><h2>Fazit</h2><p>Dieser Artikel verdeutlichte die Einfachheit und Leistungsfähigkeit der Integration von Elasticsearch in das Mastra-Framework zur Entwicklung komplexer agentischer KI-Anwendungen. Konkret haben wir gezeigt, wie ein RAG-Agent erstellt wird, der eine semantische Suche in einem Korpus von Science-Fiction-Filmdaten durchführen kann, die in Elasticsearch indiziert sind.</p><p>Ein wichtiger Aspekt ist der direkte Beitrag von Elastic zum Open-Source-Projekt Mastra, der native Unterstützung für Elasticsearch als Vektorspeicher bietet. Diese Integration senkt die Einstiegshürde erheblich, wie der Quellcode des <strong>Elasticsearch-Agenten</strong> zeigt. Mit der Verwendung von <code>ElasticSearchVector</code> und <code>createVectorQueryTool</code> benötigt die vollständige Einrichtung für die Verbindung von Elasticsearch mit Ihrem Agenten nur wenige Zeilen Konfigurationscode.</p><p>Elasticsearch bietet verschiedene erweiterte Features zur Verbesserung der Ergebnisrelevanz. Zum Beispiel steigert die <a href="https://www.elastic.co/elasticsearch/hybrid-search">hybride Suche</a> die Genauigkeit erheblich, indem die lexikalische Suche mit Vektorsuche kombiniert wird. Ein weiteres interessantes Feature ist das Reranking mit den neuesten <a href="https://www.elastic.co/search-labs/tutorials/jina-tutorial/jina-reranker-v3">Jina-Modellen</a>, die am Ende der hybriden Suche angewendet werden können. Weitere Informationen zu diesen Technologien finden Sie in den folgenden Artikeln von Elasticsearch Labs:</p><ul><li><a href="https://www.elastic.co/search-labs/blog/hybrid-search-elasticsearch">Elasticsearch Hybrides Suchen</a> von Valentin Crettaz</li><li><a href="https://www.elastic.co/search-labs/blog/jina-models-elasticsearch-guide">Einführung in Jina-Modelle, ihre Funktionen und ihre Verwendung in Elasticsearch</a> von Scott Martens</li></ul><p>Wir empfehlen Ihnen außerdem, sich das bereitgestellte Beispiel anzusehen und damit zu beginnen, Ihre eigenen datengestützten Agenten mit Mastra und Elasticsearch zu entwickeln. Weitere Informationen zu Mastra finden Sie <a href="https://mastra.ai/docs">hier</a> in der offiziellen Dokumentation.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Überwachung der Kibana-Dashboard-Ansichten mit Elastic Workflows]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie mit Elastic Workflows alle 30 Minuten Kibana-Dashboard-Ansichtsmetriken erfassen und in Elasticsearch indexieren können, um benutzerdefinierte Analysen und Visualisierungen auf Basis Ihrer eigenen Daten zu erstellen.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/monitor-kibana-dashboard-views-elastic-workflows</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/monitor-kibana-dashboard-views-elastic-workflows</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Gustavo Llermaly]]></dc:creator>
            <pubDate>Fri, 03 Apr 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0ed94ce00962287b5507f45c92ecb60fdcbf2718-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p><a href="https://www.elastic.co/kibana">Kibana</a> erfasst, wie oft jedes Dashboard angezeigt wird, aber diese Daten werden in keinem integrierten Dashboard nativ angezeigt. In diesem Artikel werden wir <strong>Elastic Workflows</strong> nutzen, um diese Daten alle 30 Minuten automatisch zu sammeln und in Elasticsearch zu indexieren, damit wir darauf unsere eigenen Analysen aufbauen können.</p><p><a href="https://www.elastic.co/docs/explore-analyze/workflows">Elastic Workflows</a> ist eine in Kibana integrierte Automatisierungs-Engine, mit der Sie mehrstufige Prozesse mithilfe einer einfachen YAML-Konfiguration definieren können. Jeder Workflow kann über einen Zeitplan, ein Ereignis oder als Tool in <a href="https://www.elastic.co/docs/explore-analyze/ai-features/elastic-agent-builder">Elastic Agent Builder</a> ausgelöst werden, und jeder Schritt kann Kibana-APIs aufrufen, Elasticsearch abfragen oder Daten umwandeln.</p><p>Wir verwenden die Anzahl der Dashboard-Ansichten als konkretes Beispiel, aber das gleiche Muster gilt für jede Metrik, die über die Kibana-API für gespeicherte Objekte bereitgestellt wird.</p><h2>Voraussetzungen</h2><ul><li><a href="https://www.elastic.co/cloud">Elastic Cloud</a> oder <a href="https://www.elastic.co/docs/deploy-manage/deploy/self-managed">selbstverwalteter </a>Cluster (Version 9.3)</li><li><a href="https://www.elastic.co/docs/explore-analyze/workflows/get-started#workflows-prerequisites">Workflows aktiviert</a> (Erweiterte Einstellungen)</li></ul><div style="display:none">Unknown block type "block", specify a component for it in the `components.types` option</div><p>Bevor wir etwas bauen, sollten wir uns erst einmal darüber klar werden, welche Daten wir haben. Kibana speichert den Großteil seiner Konfiguration und Metadaten als <a href="https://www.elastic.co/docs/api/doc/kibana/group/endpoint-saved-objects">gespeicherte Objekte</a> in einem separaten internen Index. Kibana erfasst auf diese Weise unter anderem die Anzahl der Dashboard-Aufrufe, indem ein spezieller Typ gespeicherter Objekte namens „Nutzungszähler“ verwendet wird. Sie können sie direkt in Dev Tools abfragen:</p><pre><code>GET kbn:/api/saved_objects/_find?type=usage-counter&filter=usage-counter.attributes.domainId:"dashboard"%20and%20usage-counter.attributes.counterType:"viewed"&per_page=10000</code></pre><p>Die Reaktion sieht in etwa wie folgt aus:</p><pre><code>{
  "page": 1,
  "per_page": 10000,
  "total": 1,
  "saved_objects": [
    {
      "type": "usage-counter",
      "id": "dashboard:346f3c64-ebca-484d-9d57-ec600067d596:viewed:server:20260310",
      "attributes": {
        "domainId": "dashboard",
        "counterName": "346f3c64-ebca-484d-9d57-ec600067d596",
        "counterType": "viewed",
        "source": "server",
        "count": 1
      },
      ...
    }
  ]</code></pre><p>Das Feld <code>counterName</code> ist die Dashboard-ID und <code>count</code> ist die kumulierte Anzahl der Aufrufe für dieses Dashboard an diesem bestimmten Tag. Kibana erstellt ein Zählerobjekt pro Dashboard pro Tag; Sie können das Datumssuffix in der Objekt-ID sehen (...viewed:server:20260310). Die Anzahl wächst im Laufe des Tages, wenn Nutzer das Dashboard öffnen.</p><p>Anstatt dieses tägliche Dokumentmodell in unserem Index zu replizieren, erstellen wir pro Workflow-Ausführung ein Dokument. Jedes Dokument zeichnet auf, wie viele Aufrufe dieses Dashboard für den Tag zum Zeitpunkt der Erfassung angesammelt hatte.</p><h2>Schritt 2: Den Zielindex erstellen</h2><p>Wir benötigen einen Index, um unsere Snapshots der Dashboard-Ansicht zu speichern. Der folgende Befehl erstellt sie mit expliziten Mappings, sodass wir später aggregieren und visualisieren können. Führen Sie dazu in Dev Tools den folgenden Befehl aus:</p><pre><code>PUT dashboard-views
{
  "mappings": {
    "properties": {
      "captured_at": {
        "type": "date"
      },
      "dashboard_id": {
        "type": "keyword"
      },
      "dashboard_name": {
        "type": "keyword"
      },
      "view_count": {
        "type": "integer"
      }
    }
  }
}</code></pre><p>Die Verwendung von <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/keyword"><code>keyword</code></a> Mappings für IDs und Namen ermöglicht <a href="https://www.elastic.co/docs/explore-analyze/query-filter/aggregations">Aggregationen</a>. Die Verwendung von <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/number"><code>integer</code></a> für <code>view_count</code> ist ein sicherer Standardwert, da Kibana den Zähler täglich zurücksetzt und das Erreichen des 32-Bit-Limits (mehr als 2 Milliarden Aufrufe an einem einzigen Tag) daher keine realistische Sorge darstellt. Es unterstützt weiterhin numerische Operationen wie <a href="https://www.elastic.co/docs/reference/aggregations/search-aggregations-metrics-max-aggregation"><code>max</code></a>, <a href="https://www.elastic.co/docs/reference/aggregations/search-aggregations-metrics-avg-aggregation"><code>avg</code></a>und <a href="https://www.elastic.co/docs/reference/aggregations/search-aggregations-metrics-min-aggregation"><code>min</code></a> und andere.</p><h2>Schritt 3: Den Workflow erstellen</h2><p>Gehen Sie zu <strong>Stack Management &gt; Workflows &gt; Neuer Workflow</strong>, und fügen Sie die folgende Workflow-YAML-Konfiguration ein:</p><pre><code>name: dashboard-views-ingestion
triggers:
  - type: scheduled
    with:
      every: 30m

steps:
  - name: fetch_dashboard_views
    type: kibana.request
    with:
      method: GET
      path: >-
        /api/saved_objects/_find?type=usage-counter&per_page=10000&filter=usage-counter.attributes.domainId:"dashboard"%20and%20usage-counter.attributes.counterType:"viewed"

  - name: index_each_dashboard
    type: foreach
    foreach: "{{ steps.fetch_dashboard_views.output.saved_objects }}"
    steps:
      - name: fetch_dashboard_name
        type: kibana.request
        with:
          method: GET
          path: /api/saved_objects/dashboard/{{ foreach.item.attributes.counterName }}
        on-failure:
          continue: true

      - name: index_doc
        type: elasticsearch.request
        with:
          method: POST
          path: /dashboard-views/_doc
          body:
            dashboard_id: "{{ foreach.item.attributes.counterName }}"
            dashboard_name: "{{ steps.fetch_dashboard_name.output.attributes.title }}"
            view_count: "${{ foreach.item.attributes.count | plus: 0 }}"
            captured_at: "{{ execution.startedAt | date: '%Y-%m-%dT%H:%M:%SZ' }}"</code></pre><p>Im folgenden Abschnitt werfen wir Schritt für Schritt einen Blick auf den Workflow.</p><h3>So funktioniert der Workflow</h3><h4>Auslöser</h4><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5670991d65c64ee833924225c2d375a1be868b13-325x162.png" style="width: 100%;" /><p>Der Workflow wird alle 30 Minuten mit einem geplanten Auslöser ausgeführt. Dadurch erhalten wir Zeitreihendaten, ohne die API zu überlasten.</p><h4>fetch_dashboard_views</h4><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/66eaec147c3d01c524c67cf1c7f663ac56a3259d-812x215.png" style="width: 100%;" /><p>Verwendet <code>kibana.request</code> , um die Kibana-API für gespeicherte Objekte aufzurufen. Es ist keine Authentifizierungskonfiguration erforderlich: Die Workflow-Engine fügt automatisch die korrekten Header basierend auf dem Ausführungskontext hinzu.</p><h4>index_each_dashboard (foreach)</h4><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/aad45e8aed8dc81ded6260cd6199ff78dcffe3b4-1892x290.png" style="width: 100%;" /><p>Iteriert über das Array <a href="https://www.elastic.co/docs/api/doc/kibana/group/endpoint-saved-objects"><code>saved_objects</code></a>, das vom vorherigen Schritt zurückgegeben wurde. Das aktuelle Element in jeder Iteration ist als <code>foreach.item</code> verfügbar. Innerhalb der Schleife führen wir zwei verschachtelte Schritte für jedes Dashboard aus.</p><p><strong>1. <code>fetch_dashboard_name</code>:</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/db21ec5094b743018b9cd66c5052681f14c7d7e3-1999x431.png" style="width: 100%;" /><p>Ermittelt den für Menschen lesbaren Dashboard-Titel durch Aufruf von <code>GET /api/saved_objects/dashboard/{id}</code>. Wir fügen <code>on-failure: continue: true</code> hinzu, damit die Schleife fortgesetzt wird, anstatt dass die gesamte Ausführung fehlschlägt, falls ein Dashboard gelöscht wurde, aber noch Aufrufzähler vorhanden sind.</p><p><strong>2. <code>index_doc</code>:</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f49dd0c9f0817bb1e1e5d9f4a2b05d13ef331054-1999x626.png" style="width: 100%;" /><p>Indexiert jedes Dokument mit <code>POST /dashboard-views/_doc</code> (ohne eine explizite ID), wodurch Elasticsearch IDs automatisch generieren kann. Dadurch wird bei jedem Durchlauf ein neues Dokument erstellt, wodurch eine Historie der Aufrufzahlen im Laufe der Zeit aufgebaut wird, anstatt den vorherigen Snapshot zu überschreiben.</p><p>Zwei Dinge, die es zu beachten gilt:</p><ul><li>Das Feld <code>captured_at</code> verwendet den Datumsfilter, um den Zeitstempel als <a href="https://www.iso.org/iso-8601-date-and-time-format.html">ISO 8601</a> zu formatieren. Ohne ihn erscheint der Wert als JavaScript-Datumszeichenfolge, wie <code>Tue Mar 10 2026 05:03:47 GMT+0000</code>, die Elasticsearch nicht als Datum abbildet.</li><li>Der Ausdruck <code>view_count</code> verwendet die Syntax <code>${{ }}</code> mit <code>| plus: 0</code>, um den numerischen Typ beizubehalten. Die Verwendung von <code>{{ }}</code> würde es als Zeichenfolge darstellen, was mathematische Operationen im Dashboard verhindern würde.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6d48c8784d5df6192e8b5175e69dbab5098194bc-919x774.png" style="width: 100%;" /><p><em>Die Benutzeroberfläche ermöglicht eine komfortable Fehlerbehebung jedes einzelnen Workflow-Schritts.</em></p><h2>Schritt 4: Das Statistik-Dashboard erstellen</h2><p>Sobald der Workflow einige Male ausgeführt wurde und Daten gesammelt wurden, erstellen Sie in Kibana ein neues Dashboard mithilfe der Data View „Dashboard-Ansichten“.</p><p>Einige Panels für den Anfang:</p><ul><li><strong>Top-Dashboards nach Ansichten:</strong> Verwenden Sie ein <a href="https://www.elastic.co/docs/explore-analyze/visualize/charts/bar-charts"><strong>Balkendiagramm</strong></a> mit <code>dashboard_name</code> auf der X-Achse und <code>last_value(view_count)</code> auf der Y-Achse. Hier wird die aktuelle tägliche Anzahl der Aufrufe pro Dashboard angezeigt.</li><li><strong>Ansichten im Zeitverlauf:</strong> Verwenden Sie ein <a href="https://www.elastic.co/docs/explore-analyze/visualize/charts/line-charts"><strong>Liniendiagramm</strong></a> mit <code>captured_at</code> auf der X-Achse und <code>last_value(view_count)</code> auf der Y-Achse, unterteilt nach <code>dashboard_name</code>. Da jede Ausführung ein neues Dokument anhängt, verwenden Sie den letzten Wert, um die Spitzenanzahl pro Zeit-Buckets zu erhalten, anstatt Duplikate zu summieren.</li><li><strong>Aktueller Snapshot:</strong> Verwenden Sie eine <a href="https://www.elastic.co/docs/explore-analyze/visualize/charts/tables"><strong>Datentabelle</strong></a> mit den neuesten <code>captured_at</code>, um die aktuellsten Aufrufzahlen aller Dashboards anzuzeigen.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e245b95f67daf76a2aaf4cb9df2c75ef4cfef582-1462x747.png" style="width: 100%;" /><p>Da jeder Workflow ein neues Dokument erstellt, können Sie nach Zeitbereich filtern, um die Aktivität in bestimmten Zeiträumen zu analysieren, Vergleiche von Woche zu Woche anzustellen oder Alerts zu erstellen, wenn ein Dashboard unter einen Ansichtsschwellenwert fällt.</p><h2><strong>Fazit</strong></h2><p>Elastic Workflows eignet sich gut für diese Art von periodischer Datenerhebung, da sowohl die Quelle (Kibana API) als auch das Ziel (Elasticsearch) native sind, was bedeutet, dass keine Zugangsdaten verwaltet werden. Die Workflow-Engine verwaltet die Authentifizierung für die Schritte <code>kibana.request</code> und <code>elasticsearch.request</code> automatisch, sodass Sie nur die Logik schreiben müssen.</p><h2><strong>Ressourcen</strong></h2><ul><li><a href="https://www.elastic.co/docs/explore-analyze/workflows">Elastic-Workflows</a></li><li><a href="https://www.elastic.co/docs/api/doc/kibana/">Kibana-API</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Wenn TSDS auf ILM trifft: Gestaltung von Zeitreihendatenströmen, die verspätete Daten nicht ablehnen]]></title>
            <description><![CDATA[Interaktion zwischen den Zeitgrenzen von TSDS und den ILM-Phasen und Erstellung von Richtlinien, die verspätet eintreffende Metriken tolerieren.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/tsds-ilm-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/tsds-ilm-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Bret Wortman]]></dc:creator>
            <pubDate>Thu, 02 Apr 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e4c46e4a6f746d9c845857e80de036f5d51cd4e7-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Kürzlich habe ich den Metrik-Cluster eines Kunden von „alles in der Hot-Tier-Ebene“ auf eine Heiß-Kalt-Eingefroren-Architektur migriert. Es war eine Veränderung, die ich schon dutzende Male zuvor vorgenommen hatte. Innerhalb weniger Minuten stellte Logstash die Datenübertragung vollständig ein.</p><p>Elasticsearch lehnte verspätet eintreffende Metriken ab. Dies führte zu Verzögerungen in der Pipeline. Dadurch wurden wiederum weitere verspätete Daten generiert, was noch mehr Ablehnungen zur Folge hatte. Schließlich kam die Pipeline vollständig zum Stillstand.</p><p>Wir mussten die Daten aus einem Snapshot wiederherstellen, die Daten neu indizieren und die Ingestion-Pipeline neu gestalten, um die Wiederherstellung zu ermöglichen.</p><p>Die eigentliche Ursache lag nicht in der Verwaltung des Indexlebenszyklus (ILM) selbst. Es ging um Zeitreihendatenströme (TSDS) und wie diese zeitgebundene Sicherungsindizes erzwingen.</p><p>TSDS kann die Speicherplatzanforderungen für Metriken um 40–70 % reduzieren, aber die architektonischen Änderungen, die TSDS effizient machen, verändern auch das Verhalten von Indizes im Laufe der Zeit. Diese Änderungen sind wichtig bei der Gestaltung von ILM-Richtlinien oder wenn Ihre Ingestion-Pipelines möglicherweise verspätet eintreffende Daten erzeugen.</p><h2>TL;DR</h2><p>Bei Verwendung von TSDS:</p><ul><li>Sicherungsindizes akzeptieren nur Dokumente innerhalb eines bestimmten Zeitfensters.</li><li>Wenn verspätete Daten eintreffen, nachdem ein Index in den Status „kalt“ oder „eingefroren“ versetzt wurde, weist Elasticsearch diese Dokumente zurück oder leitet sie, falls konfiguriert, an den Fehlerspeicher weiter.</li></ul><p>Designregel:</p><pre><code>warm_min_age > rollover_max_age + maximum_expected_lateness</code></pre><h2>Was ist ein Zeitreihendatenstrom?</h2><p>Ein<em> Zeitreihendatenstrom</em> (TSDS) ist ein spezieller Datenstrom, der für Metrikdaten optimiert ist. Die Daten werden so weitergeleitet, dass zugehörige Dokumente innerhalb derselben Shards liegen, wodurch sie für Abfrage und Abruf optimiert werden. So funktioniert dies in Elasticsearch:</p><p>Jedes Dokument enthält:</p><ul><li>Ein Zeitstempel.</li><li>Dimensionsfelder, die die Zeitreihen identifizieren.</li><li>Metrische Felder, die Messwerte darstellen.</li></ul><p>Dazu ein paar Beispiele:</p><ul><li>CPU-Auslastung pro Host.</li><li>Anfragenlatenzen pro Dienst.</li><li>Temperaturmessungen pro Sensor.</li></ul><p><em>Dimensionen </em>geben an, was wir messen möchten, während <em>Metriken </em>Werte darstellen, die sich im Laufe der Zeit ändern.</p><h3>Abmessungen</h3><p>Die Abmessungen beschreiben das gemessene Objekt.</p><p>Beispiele:</p><pre><code>host.name
service.name
container.id</code></pre><p>Wir definieren sie in den Mappings mit:</p><pre><code>time_series_dimension: true</code></pre><h3>Metriken</h3><p>Metriken stellen numerische Werte dar und werden wie folgt definiert:</p><pre><code>time_series_metric</code></pre><p>Gängige Arten von Metriken:</p><ul><li>Messanzeigen: Werte, die steigen und fallen.</li><li>Zähler: Werte, die bis zum Zurücksetzen ansteigen.</li></ul><p>Elastic Agent sammelt in erster Linie Metriken und Protokolldaten. Selbst wenn Sie also keine TSDS-Indizes manuell aktiviert haben, können diese dennoch in Ihrem Cluster vorhanden sein.</p><h3>Das _tsid-Feld</h3><p>Elasticsearch generiert intern einen <code>_tsid</code>-Wert aus Dimensionsfeldern. Dadurch können Dokumente mit identischen Abmessungen an denselben Shard weitergeleitet werden, was folgende Vorteile mit sich bringt:</p><ul><li>Kompression.</li><li>Abfrageort.</li><li>Aggregationsleistung.</li></ul><h2>Der wesentliche Unterschied: zeitgebundene Sicherungsindizes</h2><p>Herkömmliche Datenströme schreiben immer in den aktuellsten Sicherungsindex, der als <em>Schreibindex</em> bezeichnet wird, aber TSDS verhält sich anders.</p><p>Jeder TSDS-Sicherungsindex hat ein definiertes Zeitfenster und akzeptiert nur Dokumente mit <code>@timestamp</code>-Werten, die in dieses Zeitfenster fallen:</p><pre><code>GET _data_stream/my-metrics-data-stream


     "index_mode": "time_series",
     "time_series": {
       "temporal_ranges": [
         {
           "start": "2026-01-15T14:35:50.000Z",
           "end": "2026-03-16T11:34:40.000Z"
         }
       ]
     }</code></pre><p>Wenn ein Dokument indexiert wird, leitet Elasticsearch es an den für diesen Zeitstempel zuständigen Sicherungsindex weiter. Das bedeutet, dass ein TSDS im Gegensatz zu herkömmlichen Indizes gleichzeitig auf mehrere Sicherungsindizes schreiben kann.</p><p>Zum Beispiel:</p><ul><li>Echtzeit-Daten → aktuellster Index.</li><li>Verspätete Daten → ein früherer Index, der diesen Zeitbereich abdeckt.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/31af2bb3b3dc24db8342e791e1db77a44659ba7a-1589x502.png" style="width: 100%;" /><h2>Gestaltung für verspätet eintreffende Daten</h2><p>Echte Ingestion-Pipelines liefern Metriken nur selten perfekt und pünktlich. Die Metriken können sich aufgrund von Netzwerkausfällen, Rückständen auf dem Übertragungsweg, Batch-Ingestion und dem Ausfall von Edge-Geräten verzögern, die sich dann wieder verbinden und den Rückstand aufholen.</p><p>Herkömmliche Indizes gleichen diese Verzögerungen stillschweigend aus. TSDS tut dies nicht.</p><p>Wenn der Zeitstempel eines Dokuments außerhalb des Bereichs der beschreibbaren Sicherungsindizes liegt, wird es von Elasticsearch abgelehnt. Das bedeutet, dass Ihre ILM-Richtlinie verspätete Daten berücksichtigen muss.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/32a103b95b20e31615c214271e27811a7ee315ae-1999x691.png" style="width: 100%;" /><h2>Die kritische Einschränkung</h2><p>Unterstützende Indizes müssen lange genug beschreibbar bleiben, um verzögerte Daten zu akzeptieren.</p><p>Konkret bedeutet dies:</p><pre><code>time_until_readonly > maximum_expected_lateness</code></pre><p>Da ILM das Alter ab dem Rollover misst, lautet die operative Regel:</p><pre><code>warm_or_cold_min_age > rollover_max_age + maximum_expected_lateness</code></pre><p></p><p>Wenn beispielsweise Metriken bis zu sechs Stunden verspätet eintreffen können, müssen Indizes mindestens sechs Stunden nach dem Rollover beschreibbar bleiben.</p><p></p><p>Dass diese Einschränkung nicht berücksichtigt wurde, war genau die Ursache für den zuvor beschriebenen Ingestion-Fehler. Verspätet eintreffende Daten wurden an einen früheren Index weitergeleitet, der sich bereits im Cold Tier befand und daher für Schreibvorgänge gesperrt war.</p><p></p><h2>Umgang mit abgelehnten Dokumenten</h2><p>Wenn TSDS ein Dokument ablehnt, gibt Elasticsearch eine Fehlermeldung aus, die darauf hinweist, dass der Zeitstempel nicht in den Bereich der beschreibbaren Indizes fällt. Die Art und Weise, wie Ihre Ingestion-Pipeline mit diesem Fehler umgeht, bestimmt, ob Sie Daten verlieren oder die Ingestion unterbrechen.</p><p>Der primäre Mechanismus für den Umgang mit abgelehnten Dokumenten ist der Fehlerspeicher.</p><h3>Fehlerspeicher (empfohlen in Elasticsearch 9.1+)</h3><p>Mit Elasticsearch 9.1 wurde der Fehlerspeicher eingeführt, der automatisch abgelehnte Dokumente erfasst. Anstatt Fehler an Clients zurückzuleiten, schreibt Elasticsearch fehlgeschlagene Dokumente in einen dedizierten Fehlerindex innerhalb des Datenstroms.</p><p>Sie können Fehler inspizieren, indem Sie:</p><pre><code>GET metrics-myapp::failures/_search</code></pre><p>Die Verwendung des Fehlerspeichers verhindert, dass Ingestion-Pipelines aufgrund von Ablehnungsfehlern überlastet werden, während fehlgeschlagene Daten für Analysen oder <a href="https://www.elastic.co/docs/manage-data/data-store/data-streams/reindex-tsds">erneute Indexierung</a> aufbewahrt werden.</p><h2>Probleme bei der Ablehnung überwachen</h2><p>Bei spät auftretenden Problemen zeigen sich zunächst meist Ingestion-Anomalien. Zunächst fallen sie Ihnen möglicherweise auf als:</p><ul><li>Plötzliche Rückgänge der Indexierungsrate.</li><li>Sprunghafte Anstiege bei abgelehnten Dokumenten.</li><li>Eine wachsende Anzahl von Einträgen im Fehlerspeicher.</li><li>Abweichungen zwischen den Eingangs- und Ausgangswerten der Pipeline.</li></ul><p>Durch die Warnmeldung bei diesen Signalen können die Betreiber Probleme erkennen, bevor es zu einem Stillstand der Pipelines kommt. Workflows, Machine Learning-Jobs und andere Mechanismen können verwendet werden, um die Erkennung und Benachrichtigung zu automatisieren.</p><h2>Migrationscheckliste für TSDS + ILM</h2><p>Wenn Sie einen Metrik-Cluster auf TSDS migrieren, ILM-Tiering einsetzen oder auf eine Elasticsearch-Version upgraden, bei der die Metriken standardmäßig als TSDS erfasst sind, überprüfen Sie diese Punkte zuerst.</p><h3><strong>1. Ingestion-Latenz messen</strong></h3><p>Vor der Änderung der ILM-Richtlinien ist Folgendes festzulegen:</p><ul><li>Normale Ingestion-Verzögerung.</li><li>Maximale Verzögerung bei Vorfällen.</li><li>Verzögerungen durch Batch-Pipelines.</li></ul><p>Ihr ILM-Design muss die maximale realistische Verzögerung berücksichtigen.</p><h3><strong>2. Indexzeitfenster überprüfen</strong></h3><p>Inspizieren Sie Ihre TSDS-Sicherungsindizes:</p><pre><code>GET _data_stream/<your-stream></code></pre><p>Suchen Sie nach:</p><ul><li><code>time_series.start_time</code></li><li><code>time_series.end_time</code></li></ul><p>Diese Grenzen legen fest, welche Indizes Dokumente aufnehmen können. Das Verständnis dieser Zeitfenster kann Ihnen dabei helfen, zu ermitteln, wie spät Daten eintreffen dürfen, bevor sie abgelehnt werden.</p><h3><strong>3. Die Dimension der „heißen“ Ebene für verspätet eintreffende Daten bestimmen</strong></h3><p>Stellen Sie sicher, dass die unterstützenden Indizes lange genug beschreibbar bleiben, um verzögerte Daten zu verarbeiten.</p><p>Betriebsregel:</p><ul><li><code>warm_min_age &gt; rollover_max_age + maximum_expected_lateness</code></li></ul><p>Denken Sie daran, dass Indizes mindestens sechs Stunden beschreibbar bleiben müssen, wenn Metriken sechs Stunden zu spät eintreffen.</p><h3><strong>4. Entscheiden Sie, wie mit abgelehnten Dokumenten umgegangen werden soll</strong></h3><p>Wählen Sie eine Strategie, bevor Sie TSDS aktivieren:</p><ul><li>Fehlerspeicher (empfohlen in Elasticsearch 9.1+).</li><li>Warteschlange für unzustellbare Nachrichten in Logstash.</li><li>Fallback-Index für verspätete Eingänge.</li><li>Akzeptanz von begrenztem Datenverlust.</li></ul><h3><strong>5. Ingestion-Status überwachen</strong></h3><p>Warnmeldungen hinzufügen für:</p><ul><li>Die Indexierungsrate sinkt.</li><li>Abgelehnte Dokumente.</li><li>Das Fehlerspeicherwachstum.</li><li>Diskrepanzen zwischen Pipeline-Eingabe und -Ausgabe.</li></ul><p>Verspätete Datenprobleme treten oft zuerst als Ingestion-Anomalien auf.</p><h2>Zusammenfassung</h2><p>Zeitreihendatenströme bieten erhebliche Speicher- und Leistungsverbesserungen für Metrik-Workloads, bringen aber eine wichtige architektonische Änderung mit sich: Sicherungsindizes sind zeitgebunden, was das Verhalten von ILM beeinflusst.</p><p>Bei Verwendung von TSDS:</p><ul><li>Indizes müssen lange genug beschreibbar bleiben, um verzögerte Daten zu akzeptieren.</li><li>Ingestion-Pipelines sollten abgelehnte Dokumente sicher verarbeiten.</li></ul><p>Die wichtigste Regel lautet:</p><pre><code>warm_min_age > rollover_max_age + maximum_expected_lateness</code></pre><p>Wenn Sie Ihre ILM-Richtlinien unter Berücksichtigung dieser Einschränkung gestalten, eignet sich TSDS hervorragend für Metrik-Workloads.</p><p>Wenn Sie dies jedoch ignorieren, könnte Ihre Ingestion-Pipeline diese Zeitgrenzen auf die harte Tour erfahren.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[LINQ zu ES|QL: C# schreiben, Elasticsearch abfragen]]></title>
            <description><![CDATA[Erkundung des neuen LINQ to ES|QL-Providers im Elasticsearch .NET Client, mit dem Sie C#-Code schreiben können, der automatisch in ES|QL-Abfragen übersetzt wird.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/linq-esql-c-elasticsearch-net-client</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/linq-esql-c-elasticsearch-net-client</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Florian Bernd, Martijn Laarman]]></dc:creator>
            <pubDate>Wed, 01 Apr 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e54132e915217063e9ed0ec45059c6cfc38e31dd-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Ab <strong>v9.3.4</strong> und <strong>v8.19.18</strong> enthält der Elasticsearch .NET-Client einen <a href="https://learn.microsoft.com/en-us/dotnet/csharp/linq/">Language Integrated Query (LINQ)</a>-Provider, der C# LINQ-Ausdrücke in die <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql.html">Elasticsearch Query Language (ES|QL)</a>-Abfragen zur Laufzeit übersetzt. Anstatt ES|QL-Zeichenfolgen von Hand zu schreiben, stellen Sie Abfragen mit <code>Where</code>, <code>Select</code>, <code>OrderBy</code>, <code>GroupBy</code> und anderen Standardoperatoren zusammen. Der Anbieter kümmert sich um die Übersetzung, Parametrisierung und Deserialisierung der Ergebnisse, einschließlich des zeilenweisen Streamings, das die Speichernutzung unabhängig von der Größe der Ergebnismenge konstant hält.</p><h2>Ihre erste Abfrage</h2><p>Definieren Sie zunächst ein einfaches altes CLR-Objekt (POCO), das auf Ihren Elasticsearch-Index abgebildet wird. Eigenschaftsnamen werden über Standardattribute <code>System.Text.Json</code> , wie z. B. <code>[JsonPropertyName]</code>, oder über ein konfiguriertes <code>JsonNamingPolicy</code> in ES|QL-Spaltennamen aufgelöst. Die gleichen Regeln für <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/dotnet/source-serialization">die Quellenserialisierung</a>, die für den Rest des Clients gelten, gelten auch hier.</p><pre><code>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; }
}</code></pre><p>Nachdem der Typ festgelegt wurde, sieht eine Abfrage folgendermaßen aus:</p><pre><code>var minPrice = 100.0;
var brand = "TechCorp";

await foreach (var product in client.Esql.QueryAsync<Product>(q => q
    .From("products")
    .Where(p => p.InStock && p.Price >= minPrice && p.Brand == brand)
    .OrderByDescending(p => p.Price)
    .Take(10)))
{
    Console.WriteLine($"{product.Name}: ${product.Price}");
}</code></pre><p>Der Anbieter übersetzt dies in folgende ES|QL:</p><pre><code>FROM products
| WHERE (in_stock == true AND price_usd >= ?minPrice AND brand == ?brand)
| SORT price_usd DESC
| LIMIT 10</code></pre><p>Ein paar Details, die zu beachten sind:</p><ul><li><strong>Namensauflösung der Eigenschaften:</strong> <code>p.Price</code> wird aufgrund des Attributs <code>[JsonPropertyName]</code> zu <code>price_usd</code> , und <code>p.Brand</code> wird gemäß der Standard-CamelCase-Namenskonvention zu <code>brand</code> .</li><li><strong>Parametererfassung:</strong> Die C#-Variablen <code>minPrice</code> und <code>brand</code> werden als benannte Parameter erfasst (<code>?minPrice</code>, <code>?brand</code>). Sie werden separat von der Abfragezeichenfolge im JSON-Payload gesendet, was Injektionen verhindert und serverseitiges Abfrageplan-Caching ermöglicht.</li><li><strong>Streaming:</strong> <code>QueryAsync&lt;T&gt;</code> gibt<code>IAsyncEnumerable&lt;T&gt;</code> zurück. Zeilen werden einzeln materialisiert, sobald sie von Elasticsearch eintreffen.</li></ul><p>Sie können auch die generierte Abfrage und ihre Parameter inspizieren, ohne sie auszuführen:</p><pre><code>var query = client.Esql.CreateQuery<Product>()
    .Where(p => p.InStock && p.Price >= minPrice && p.Brand == brand)
    .OrderByDescending(p => p.Price)
    .Take(10);

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

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

var parameters = query.GetParameters();
// { "minPrice": 100.0, "brand": "TechCorp" }</code></pre><h2>Wie funktioniert das? Eine kurze LINQ-Auffrischung</h2><p>Der Mechanismus, der LINQ-Anbieter ermöglicht, ist die Unterscheidung zwischen <code>IEnumerable&lt;T&gt;</code> und <code>IQueryable&lt;T&gt;</code>.</p><p>Wenn Sie<code>.Where(p =&gt; p.Price &gt; 100)</code> auf einem <code>IEnumerable&lt;T&gt;</code>aufrufen, kompiliert die Lambda zu einem <code>Func&lt;Product, bool&gt;</code>, einem regulären Delegierten, den die Laufzeit während des Prozesses ausführt. Dies ist LINQ-to-Objects.</p><p>Wenn Sie dieselbe Methode auf einem <code>IQueryable&lt;T&gt;</code>aufrufen, umwickelt der C#-Compiler stattdessen das Lambda in einem <code>Expression&lt;Func&lt;Product, bool&gt;&gt;</code> . Dies ist eine Datenstruktur, die die <em>Struktur</em> des Codes repräsentiert und nicht seine ausführbare Form. Der Ausdrucksbaum kann zur Laufzeit inspiziert, analysiert und in eine andere Sprache übersetzt werden.</p><pre><code>// IEnumerable: the lambda is a compiled delegate
IEnumerable<Product> local = products.Where(p => p.Price > 100);

// IQueryable: the lambda is an expression tree, a data structure
IQueryable<Product> remote = queryable.Where(p => p.Price > 100);</code></pre><p>Die <code>IQueryProvider</code> Schnittstelle ist der Erweiterungspunkt. Jeder Anbieter kann <code>CreateQuery&lt;T&gt;</code> und <code>Execute&lt;T&gt;</code> implementieren, um diese Ausdrucksbäume in eine Zielsprache zu übersetzen. Entity Framework verwendet dies, um SQL auszugeben. Der LINQ to ES|QL-Anbieter verwendet es, um ES|QL zu emittieren.</p><p>Der Ausdrucksbaum für die obige Abfrage sieht wie folgt aus:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f864cd18a390831f8d28503a29b5835efb1842f7-1000x720.png" style="width: 100%;" /><p><em>Ausdrucksbaum für die Beispielabfrage.</em></p><p>Der Baum ist von innen nach außen verschachtelt: <code>Take</code> umschließt <code>OrderByDescending</code>, welches <code>Where</code> umschließt, welches <code>From</code> umschließt, welches die Wurzel <code>EsqlQueryable&lt;Product&gt;</code> Konstante umschließt. Das <code>Where</code> -Prädikat ist selbst ein Teilbaum von <code>BinaryExpression</code> Knoten für die Operatoren <code>&amp;&amp;</code>, <code>&gt;=</code>, und <code>==</code> , mit <code>MemberExpression</code> Blättern für Eigenschaften-Zugriffe und Abschluss-Erfassungen für die Variablen <code>minPrice</code> und <code>brand</code> . Dies ist die Datenstruktur, die der Provider durchläuft, um das endgültige ES|QL zu erzeugen.</p><h2>Unter der Haube: Die Übersetzungspipeline</h2><p>Der Weg von einem LINQ-Ausdruck zu den Abfrageergebnissen folgt einer sechsstufigen Pipeline:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2a2c772b63d720f61fc9a28b2f85668fa2db8d38-1999x1036.png" style="width: 100%;" /><p><em>Überblick über die Übersetzungspipeline.</em></p><h3>1. Erfassung des Ausdrucksbaums</h3><p>Wenn man <code>.Where()</code>, <code>.OrderBy()</code>, <code>.Take()</code> und andere Operatoren auf einem <code>IQueryable&lt;T&gt;</code> verkettet, erstellt die Standard-LINQ-Infrastruktur einen Ausdrucksbaum. <code>EsqlQueryable&lt;T&gt;</code> implementiert <code>IQueryable&lt;T&gt;</code> und delegiert an <code>EsqlQueryProvider</code>.</p><h3>2. Übersetzung</h3><p>Wenn die Abfrage ausgeführt wird (durch Enumerieren, Aufrufen von <code>ToList()</code> oder Verwenden von <code>await foreach)</code>), durchläuft <code>EsqlExpressionVisitor</code> den Ausdrucksbaum von innen nach außen. Es sendet jeden LINQ-Methodenaufruf an einen spezialisierten Besucher:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Besucher</td><td style="border: 1px solid #ccc; padding: 4px;">Übersetzt</td><td style="border: 1px solid #ccc; padding: 4px;">In</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">WhereClauseVisitor</td><td style="border: 1px solid #ccc; padding: 4px;">.Where(predicate)</td><td style="border: 1px solid #ccc; padding: 4px;">WHERE-Bedingung</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">SelectProjectionVisitor</td><td style="border: 1px solid #ccc; padding: 4px;">.Select(selector)</td><td style="border: 1px solid #ccc; padding: 4px;">EVAL + KEEP + RENAME</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">GroupByVisitor</td><td style="border: 1px solid #ccc; padding: 4px;">.GroupBy().Select()</td><td style="border: 1px solid #ccc; padding: 4px;">STATISTIKEN ... NACH</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OrderByVisitor</td><td style="border: 1px solid #ccc; padding: 4px;">.OrderBy() / .ThenBy()</td><td style="border: 1px solid #ccc; padding: 4px;">SORT field [ASC\|DESC]</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">EsqlFunctionTranslator</td><td style="border: 1px solid #ccc; padding: 4px;">EsqlFunctions.*, Math.*, string methods</td><td style="border: 1px solid #ccc; padding: 4px;">80+ ES|QL functions</td>
                    </tr>
              </tbody>
            </table>
          <p>Bei der Übersetzung werden in Ausdrücken referenzierte C#-Variablen als benannte Parameter erfasst.</p><h3>3. Abfragemodell</h3><p>Die Besucher produzieren nicht direkt Zeichenfolgen. Stattdessen produzieren sie <code>QueryCommand</code> Objekte, eine unveränderliche Zwischenrepräsentation. Ein <code>FromCommand</code>, ein <code>WhereCommand</code>, ein <code>SortCommand</code> und ein <code>LimitCommand</code>, jeweils einen ES|QL-Verarbeitungsbefehl repräsentierend. Diese werden in einem <code>EsqlQuery</code> Modell gesammelt.</p><p></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2adc349b6cf655b96b7b3e826a134e8a17fe42fd-1999x1036.png" style="width: 100%;" /><p><em>Abfragemodell und Befehlsmuster.</em></p><p>Dieses Zwischenmodell ist vom Ausdrucksbaum und vom Ausgangsformat entkoppelt. Es kann inspiziert, abgefangen (über <code>IEsqlQueryInterceptor</code>) oder vor der Formatierung modifiziert werden.</p><h3>4. Formatierung</h3><p><code>EsqlFormatter</code> besucht jede <code>QueryCommand</code> in der richtigen Reihenfolge und erstellt die finale ES|QL-Zeichenfolge. Jeder Befehl wird zu einer Zeile, getrennt durch den Pipe-Operator (|), den ES|QL zur Verkettung von Verarbeitungsbefehlen verwendet. Bezeichner, die Sonderzeichen enthalten, werden automatisch mit Backticks versehen.</p><h3>5. Ausführung</h3><p>Die formatierte ES|QL-Zeichenfolge und erfasste Parameter werden als JSON-Payload an den <code>/_query</code> -Endpunkt von Elasticsearch gesendet. Die Schnittstelle <code>IEsqlQueryExecutor</code> abstrahiert die Transportschicht, an der die geschichtete Paketarchitektur zum Tragen kommt.</p><h3>6. Materialisierung</h3><p><code>EsqlResponseReader</code> streamt die JSON-Reaktion, ohne das gesamte Ergebnis-Set in den Speicher zu puffern. Ein <code>ColumnLayout</code> -Baum, der einmal pro Abfrage vorab berechnet wird, ordnet flache ES|QL-Spaltennamen (wie <code>address.street</code>, <code>address.city</code>) verschachtelten POCO-Eigenschaften zu. Jede Zeile wird zu einer <code>T</code> -Instanz zusammengestellt und einzeln über <code>IEnumerable&lt;T&gt;</code> oder <code>IAsyncEnumerable&lt;T&gt;</code> zurückgegeben.</p><h2>Die mehrschichtige Architektur</h2><p>Die LINQ-to-ES|QL-Funktionalität ist auf drei Pakete aufgeteilt:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/41b8aae860ecdc2480edcb1c1d4cc9b03cfb78c9-1999x1036.png" style="width: 100%;" /><p><em>Paketarchitektur.</em><br/><a href="https://www.nuget.org/packages/Elastic.Esql"><strong><code>Elastic.Esql</code></strong></a> ist die reine Übersetzungsmaschine. Es hat keine HTTP-Abhängigkeiten und enthält die Expression Visitors, das Abfragemodell, den Formatter und den Response Reader. Sie können es eigenständig verwenden, um ES|QL-Abfragen ohne Elasticsearch-Verbindung zu erstellen und zu inspizieren, was für Tests, Abfrageprotokollierung oder den Aufbau einer eigenen Ausführungsschicht nützlich ist.</p><pre><code>// Translation-only: no Elasticsearch connection needed
var provider = new EsqlQueryProvider();
var query = new EsqlQueryable<Product>(provider)
    .From("products")
    .Where(p => p.InStock)
    .OrderByDescending(p => p.Price);

Console.WriteLine(query.ToEsqlString());
// FROM products | WHERE in_stock == true | SORT price_usd DESC</code></pre><p><a href="https://www.nuget.org/packages/Elastic.Clients.Esql"><strong><code>Elastic.Clients.Esql</code></strong></a> ist ein leichter, eigenständiger ES|QL-Client. Es fügt die HTTP-Ausführung über <code>Elastic.Esql</code> mittels <code>Elastic.Transport</code> hinzu. Wenn Ihre Anwendung nur ES|QL und keine der anderen Elasticsearch-APIs benötigt, ist dies die Option mit den geringsten Abhängigkeiten.</p><p><a href="https://www.nuget.org/packages/Elastic.Clients.Elasticsearch"><strong><code>Elastic.Clients.Elasticsearch</code></strong></a> ist der vollständige Elasticsearch.NET-Client. Es baut außerdem auf <code>Elastic.Esql</code> auf und stellt den LINQ-Provider über den Namespace <code>client.Esql</code> bereit. Dies ist der empfohlene Einstieg für die meisten Anwendungen.</p><p>Beide Ausführungsschicht-Pakete bieten ihre eigene Implementierung von <code>IEsqlQueryExecutor</code>, der Strategieschnittstelle, die Übersetzung und Transport miteinander verbindet.</p><p>Alle drei Pakete sind mit Native AOT kompatibel, wenn sie mit einem quellgenerierten <code>JsonSerializerContext</code> verwendet werden. Für den vollständigen Client siehe die <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/dotnet/source-serialization#native-aot">Native AOT-Dokumentation</a>.</p><h2>Über die Grundlagen hinaus</h2><p>Das obige Beispiel umfasste das Filtern, Sortieren und Paginieren. Der Anbieter unterstützt ein breiteres Spektrum an Operationen.</p><h3>Aggregationen</h3><p><code>GroupBy</code>, kombiniert mit Aggregatfunktionen in <code>Select</code>, übersetzt in ES|QL <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/stats-by"><code>STATS ... BY</code></a>:</p><pre><code>var stats = client.Esql.Query<Product, object>(q => q
    .GroupBy(p => p.Brand)
    .Select(g => new
    {
        Brand = g.Key,
        Count = g.Count(),
        AvgPrice = g.Average(p => p.Price),
        MaxPrice = g.Max(p => p.Price)
    }));

// -> FROM products | STATS COUNT(*), AVG(price_usd), MAX(price_usd) BY brand</code></pre><h3>Projektionen</h3><p><code>Select</code>, mit anonymen Typen erzeugt die Befehle <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> und <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/rename"><code>RENAME</code></a>:</p><pre><code>var query = client.Esql.CreateQuery<Product>()
    .Select(p => new { ProductName = p.Name, p.Price, p.InStock });

// -> FROM products | KEEP name, price_usd, in_stock | RENAME name AS ProductName</code></pre><h3>Umfassende Funktionsbibliothek</h3><p>Über 80 ES|QL-Funktionen sind über die <code>EsqlFunctions</code> -Klasse verfügbar und decken Datum/Uhrzeit, Zeichenfolgen, Mathematik, IP, Musterabstimmung und Wertung ab. Die Standardmethoden <code>Math.*</code> und <code>string.*</code> werden ebenfalls übersetzt:</p><pre><code>.Where(p => p.Name.Contains("Pro"))       // -> WHERE name LIKE "*Pro*"
.Where(p => EsqlFunctions.CidrMatch(      // -> WHERE CIDR_MATCH(ip, "10.0.0.0/8")
    p.IpAddress, "10.0.0.0/8"))</code></pre><h3>LOOKUP JOIN</h3><p>Indexübergreifende Suchvorgänge werden in ES|QL <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/lookup-join"><code>LOOKUP JOIN</code></a> übersetzt:</p><pre><code>var enriched = client.Esql.Query<Product, object>(q => q
    .LookupJoin<Product, CategoryLookup, string, object>(
        "category-lookup-index",
        product => product.Id,
        category => category.CategoryId,
        (product, category) => new { product.Name, category!.CategoryLabel }));</code></pre><h3>Raw ES|QL Umgehungsmöglichkeit</h3><p>Für ES|QL-Features, die vom LINQ-Anbieter noch nicht unterstützt werden, können Sie Rohfragmente anfügen:</p><pre><code>var results = client.Esql.Query<Product>(q => q
    .Where(p => p.InStock)
    .RawEsql("| EVAL discounted = price_usd * 0.9"));</code></pre><h3>Serverseitige asynchrone Abfragen</h3><p>Bei langlaufenden Abfragen sollten diese zur Hintergrundverarbeitung auf dem Server eingereicht werden:</p><pre><code>await using var asyncQuery = await client.Esql.SubmitAsyncQueryAsync<Product>(
    q => q.Where(p => 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);</code></pre><p>Serverseitige asynchrone Abfragen sind besonders nützlich für langlaufende analytische Abfragen / die Verarbeitung großer Datensätze, die die typischen Timeout-Schwellenwerte überschreiten könnten, oder in Timeout-sensiblen Umgebungen mit Load-Balancern, API-Gateways oder Proxys, die strikte HTTP-Timeouts durchsetzen. Asynchrone Abfragen vermeiden Verbindungsabbrüche, indem die Übermittlung von dem Abruf der Ergebnisse entkoppelt wird.</p><h2>Erste Schritte</h2><p>LINQ to ES|QL ist verfügbar ab:</p><ul><li><strong>Elastic.Clients.Elasticsearch v9.3.4</strong> (9.x branch)</li><li><strong>Elastic.Clients.Elasticsearch v8.19.18</strong> (8.x Branch)</li></ul><p>Installation über NuGet:</p><p><code>dotnet add package Elastic.Clients.Elasticsearch</code></p><p>Die Einstiegspunkte befinden sich auf <code>client.Esql</code>:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Methode</td><td style="border: 1px solid #ccc; padding: 4px;">Rückgaben</td><td style="border: 1px solid #ccc; padding: 4px;">Anwendungsfall</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Query<T>(...)</td><td style="border: 1px solid #ccc; padding: 4px;">IEnumerable<T></td><td style="border: 1px solid #ccc; padding: 4px;">Synchrone Ausführung</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">QueryAsync<T>(...)</td><td style="border: 1px solid #ccc; padding: 4px;">IAsyncEnumerable<T></td><td style="border: 1px solid #ccc; padding: 4px;">Asynchrones Streaming</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">CreateQuery<T>()</td><td style="border: 1px solid #ccc; padding: 4px;">IEsqlQueryable<T></td><td style="border: 1px solid #ccc; padding: 4px;">Erweiterte Zusammensetzung und Inspektion</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">SubmitAsyncQueryAsync<T>(...)</td><td style="border: 1px solid #ccc; padding: 4px;">EsqlAsyncQuery<T></td><td style="border: 1px solid #ccc; padding: 4px;">Langlaufende serverseitige Abfragen</td>
                    </tr>
              </tbody>
            </table>
          <p>Eine vollständige Feature-Referenz – einschließlich Abfrageoptionen, Zugriff auf mehrere Felder, verschachtelter Objekte und der Verarbeitung mehrwertiger Felder – finden Sie in der <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/dotnet/linq-to-esql">LINQ to ES|QL-Dokumentation</a>.</p><h2>Fazit</h2><p>LINQ to ES|QL bringt die volle Ausdruckskraft von C# LINQ in die ES|QL-Abfragesprache von Elasticsearch, sodass Sie stark typisierte, kombinierbare Abfragen schreiben können, ohne Abfragezeichenfolgen von Hand erstellen zu müssen. Mit automatischer Parametererfassung, Streaming-Materialisierung und einer geschichteten Paketarchitektur, die von eigenständiger Übersetzung zum vollständigen Elasticsearch-Client skaliert, fügt es sich natürlich in .NET-Anwendungen jeder Größe ein. Installieren Sie den neuesten Client, verweisen Sie Ihre LINQ-Ausdrücke auf einen Index und überlassen Sie den Rest dem Provider.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Erstellung eines Elasticsearch MCP-Servers mit TypeScript]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie mit TypeScript und Claude Desktop einen Elasticsearch MCP-Server erstellen.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/elastic-mcp-server-typescript-claude</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/elastic-mcp-server-typescript-claude</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
            <pubDate>Fri, 27 Mar 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0bb24c05fff391f42070c2883182ea6fe9cb9680-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Bei der Arbeit mit großen Wissensdatenbanken in Elasticsearch ist das Finden von Informationen nur die halbe Miete. Entwickler müssen häufig Ergebnisse aus mehreren Dokumenten zusammenführen, Zusammenfassungen erstellen und Antworten bis zu ihren Quellen zurückverfolgen. Um dies zu erreichen, bietet das Modellkontextprotokoll (MCP) eine standardisierte Möglichkeit, Elasticsearch mit LLM-gestützten Anwendungen zu verbinden. Während Elastic offizielle Lösungen anbietet, wie den Elastic Agent Builder (der unter anderem einen <a href="https://www.elastic.co/docs/solutions/search/agent-builder/mcp-server">MCP-Endpoint</a> zu seinen Features zählt), ermöglicht die Entwicklung eines benutzerdefinierten MCP-Servers die volle Kontrolle über die Suchlogik, die Ergebnisformatierung und die Art und Weise, wie abgerufene Inhalte an ein LLM zur Synthese, Zusammenfassung und Zitierung weitergegeben werden.</p><p>In diesem Artikel untersuchen wir die Vorteile der Entwicklung eines benutzerdefinierten Elasticsearch MCP-Servers und zeigen, wie man einen in TypeScript erstellt, der Elasticsearch mit LLM-gestützten Anwendungen verbindet.</p><h2>Warum einen benutzerdefinierten Elasticsearch MCP-Server entwickeln?</h2><p>Elastic bietet einige Alternativen für <a href="https://www.elastic.co/docs/solutions/search/mcp">MCP-Server</a>:</p><ul><li><a href="https://www.elastic.co/docs/solutions/search/agent-builder/mcp-server">Elastic Agent Builder MCP-Server für Elasticsearch 9.2+</a></li><li><a href="https://github.com/elastic/mcp-server-elasticsearch?tab=readme-ov-file#elasticsearch-mcp-server">Elasticsearch MCP-Server für ältere Versionen (Python)</a></li></ul><p>Wenn Sie mehr Kontrolle darüber benötigen, wie Ihr MCP-Server mit Elasticsearch interagiert, bietet Ihnen die Entwicklung eines eigenen benutzerdefinierten Servers die nötige Flexibilität, um ihn genau an Ihre Bedürfnisse anzupassen. Zum Beispiel ist der MCP-Endpoint von Agent Builder auf Elasticsearch Query Language (ES|QL) Abfragen beschränkt, während ein benutzerdefinierter Server die Verwendung des vollständigen Abfrage-DSL ermöglicht. Sie erhalten außerdem die Kontrolle über die Formatierung der Ergebnisse, bevor sie an das LLM weitergeleitet werden, und können zusätzliche Verarbeitungsschritte integrieren, wie die OpenAI-gestützte Zusammenfassung, die wir in dieses Tutorial implementieren.</p><p>Am Ende dieses Artikels verfügen Sie über einen MCP-Server in TypeScript, der in einem Elasticsearch-Index gespeicherte Informationen durchsucht, zusammenfasst und Zitate bereitstellt. Wir verwenden Elasticsearch für den Abruf, das <code>gpt-4o-mini</code>-Modell von OpenAI zur Zusammenfassung und Erzeugung von Zitaten sowie Claude Desktop als MCP-Client und Benutzeroberfläche, um Nutzeranfragen zu empfangen und darauf zu reagieren. Das Endergebnis ist ein interner Wissensassistent, der Entwicklern dabei hilft, Best Practices in der technischen Dokumentation ihres Unternehmens zu entdecken und zu synthetisieren.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ec5771a874cf9740d4cac6888622cbe8cd6aede7-1999x1133.png" style="width: 100%;" /><h2>Voraussetzungen:</h2><ul><li>Node.js 20+</li><li>Elasticsearch</li><li>OpenAI-API-Schlüssel</li><li>Claude Desktop</li></ul><h3>Was ist MCP?</h3><p><a href="https://www.elastic.co/what-is/mcp">MCP</a> ist ein offener Standard, der von <a href="https://www.anthropic.com/news/model-context-protocol">Anthropic</a> entwickelt wurde und sichere, bidirektionale Verbindungen zwischen LLMs und externen Systemen wie Elasticsearch ermöglicht. Mehr über den aktuellen Stand von MCP können Sie in <a href="https://www.elastic.co/search-labs/blog/mcp-current-state">diesem Artikel</a> lesen.</p><p>Die MCP-Landschaft <a href="https://www.elastic.co/search-labs/blog/mcp-current-state#mcp-project-updates:-transport,-elicitation,-and-structured-tooling">entwickelt sich täglich weiter</a>, wobei Server für eine Vielzahl von Anwendungsfällen zur Verfügung stehen. In diesem Artikel zeigen wir Ihnen außerdem, wie einfach es ist, Ihren eigenen benutzerdefinierten MCP-Server zu entwickeln.</p><h3>MCP-Clients</h3><p>Es gibt eine lange <a href="https://modelcontextprotocol.io/clients">Liste verfügbarer MCP-Clients</a>, von denen jeder seine eigenen Eigenschaften und Einschränkungen hat. Aufgrund seiner Einfachheit und Beliebtheit verwenden wir <a href="https://claude.ai/download">Claude Desktop</a> als unseren MCP-Client. Er dient als Chat-Schnittstelle, auf der Nutzer Fragen in natürlicher Sprache stellen können, und ruft automatisch die von unserem MCP-Server bereitgestellten Tools auf, um Dokumente zu durchsuchen und Zusammenfassungen zu erstellen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/66eb0b11473347b6cf2d85718251eeac38d6249d-1999x1491.png" style="width: 100%;" /><h2>Erstellen eines Elasticsearch MCP-Servers</h2><p>Mit dem <a href="https://github.com/modelcontextprotocol/typescript-sdk">TypeScript SDK</a> können wir einfach einen Server erstellen, der versteht, wie er unsere Elasticsearch-Daten basierend auf einer Nutzereingabe abfragt.</p><p>In diesem Artikel werden die folgenden Schritte zur Integration des Elasticsearch MCP-Servers mit dem Claude Desktop-Client beschrieben:</p><ol><li><a href="https://www.elastic.co/search-labs/blog/elastic-mcp-server-typescript-claude#configure-mcp-server-for-elasticsearch">MCP-Server für Elasticsearch konfigurieren.</a></li><li><a href="https://www.elastic.co/search-labs/blog/elastic-mcp-server-typescript-claude#load-the-mcp-server-into-claude-desktop">MCP-Server in Claude Desktop laden.</a></li><li><a href="https://www.elastic.co/search-labs/blog/elastic-mcp-server-typescript-claude#test-it-out">Probieren Sie es aus.</a></li></ol><h3>MCP-Server für Elasticsearch konfigurieren</h3><p>Zunächst initialisieren wir eine Node-Anwendung:</p><pre><code>npm init -y</code></pre><p>Dadurch wird eine <code>package.json</code>-Datei erstellt, wodurch wir damit beginnen können, die notwendigen Abhängigkeiten für diese Anwendung zu installieren.</p><pre><code>npm install @elastic/elasticsearch @modelcontextprotocol/sdk openai zod && npm install --save-dev ts-node @types/node typescript</code></pre><ul><li><strong>@elastic/elasticsearch</strong> verschafft Ihnen Zugriff auf die Node.js-Bibliothek von Elasticsearch.</li><li><strong>@modelcontextprotocol/SDK</strong> stellt die Kerntools bereit, um einen MCP-Server zu erstellen und zu verwalten, Tools zu registrieren und die Kommunikation mit MCP-Clients zu übernehmen.</li><li><strong>OpenAI</strong> ermöglicht die Interaktion mit OpenAI-Modellen, um Zusammenfassungen oder Antworten in natürlicher Sprache zu generieren.</li><li><strong><a href="https://zod.dev/">ZOD</a> </strong>sorgt dafür, strukturierte Schemata für Eingangs- und Ausgangsdaten in jedem Tool zu definieren und zu validieren.</li></ul><p><code>ts-node</code>Während der Entwicklung werden <code>@types/node</code> und <code>typescript</code> verwendet, um den Code zu schreiben und die Skripte zu kompilieren.</p><h4>Datensatz einrichten</h4><p>Um die Daten bereitzustellen, die Claude Desktop über unseren MCP-Server abfragen kann, verwenden wir einen simulierten <a href="https://github.com/Delacrobix/typescript-elasticsearch-mcp/blob/main/dataset.json">internen Wissensdatenbank-Datensatz</a>. So sieht ein Dokument aus diesem Datensatz aus:</p><pre><code>{
    "id": 5,
    "title": "Logging Standards for Microservices",
    "content": "Consistent logging across microservices helps with debugging and tracing. Use structured JSON logs and include request IDs and timestamps. Avoid logging sensitive information. Centralize logs in Elasticsearch or a similar system. Configure log rotation to prevent storage issues and ensure logs are searchable for at least 30 days.",
    "tags": ["logging", "microservices", "standards"]
}</code></pre><p>Für die Aufnahme der Daten haben wir ein Skript vorbereitet, das einen Index in Elasticsearch erstellt und den Datensatz darin lädt. Sie finden es <a href="https://github.com/Delacrobix/typescript-elasticsearch-mcp/blob/main/setup.ts">hier</a>.</p><h4>MCP-Server</h4><p>Erstellen Sie eine Datei mit dem Namen <a href="https://github.com/Delacrobix/typescript-elasticsearch-mcp/blob/main/index.ts"><code>index.ts</code></a> und fügen Sie den folgenden Code hinzu, um die Abhängigkeiten zu importieren und Umgebungsvariablen zu verarbeiten:</p><pre><code>// index.ts
import { z } from "zod";
import { Client } from "@elastic/elasticsearch";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import OpenAI from "openai";

const ELASTICSEARCH_ENDPOINT =
  process.env.ELASTICSEARCH_ENDPOINT ?? "http://localhost:9200";
const ELASTICSEARCH_API_KEY = process.env.ELASTICSEARCH_API_KEY ?? "";
const OPENAI_API_KEY = process.env.OPENAI_API_KEY ?? "";
const INDEX = "documents";</code></pre><p>Außerdem initialisieren wir die Clients, um Elasticsearch- und OpenAI-Aufrufe zu bewältigen:</p><pre><code>const openai = new OpenAI({
  apiKey: OPENAI_API_KEY,
});

const _client = new Client({
  node: ELASTICSEARCH_ENDPOINT,
  auth: {
    apiKey: ELASTICSEARCH_API_KEY,
  },
});</code></pre><p>Um unsere Implementierung robuster zu machen und einen strukturierten Eingang und Ausgang zu gewährleisten, definieren wir Schemata mit <a href="https://zod.dev/"><code>zod</code></a>. Dadurch können wir Daten zur Laufzeit validieren, Fehler frühzeitig abfangen und die Tool-Reaktionen einfacher programmatisch verarbeiten.</p><pre><code>const DocumentSchema = z.object({
  id: z.number(),
  title: z.string(),
  content: z.string(),
  tags: z.array(z.string()),
});

const SearchResultSchema = z.object({
  id: z.number(),
  title: z.string(),
  content: z.string(),
  tags: z.array(z.string()),
  score: z.number(),
});

type Document = z.infer<typeof DocumentSchema>;
type SearchResult = z.infer<typeof SearchResultSchema>;</code></pre><p>Erfahren Sie <a href="https://www.elastic.co/search-labs/blog/structured-outputs-elasticsearch-guide">hier</a> mehr über strukturierte Ausgänge.</p><p>Nun initialisieren wir den MCP-Server:</p><pre><code>const server = new McpServer({
  name: "Elasticsearch RAG MCP",
  description:
    "A RAG server using Elasticsearch. Provides tools for document search, result summarization, and source citation.",
  version: "1.0.0",
});</code></pre><h4>Definition der MCP-Tools</h4><p>Wenn alles konfiguriert ist, können wir mit dem Schreiben der Tools beginnen, die von unserem MCP-Server bereitgestellt werden. Dieser Server stellt zwei Tools bereit:</p><ul><li><strong><code>search_docs</code>: </strong>Sucht nach Dokumenten in Elasticsearch mittels Volltextsuche.</li><li><strong><code>summarize_and_cite</code>:</strong> Fasst Informationen aus zuvor abgerufenen Dokumenten zusammen und synthetisiert sie, um eine Nutzerfrage zu beantworten. Dieses Tool fügt außerdem Zitate hinzu, die auf die Quelldokumente verweisen.</li></ul><p>Zusammen bilden diese Tools einen einfachen Workflow zum Abrufen und Zusammenfassen, bei dem ein Tool relevante Dokumente abruft und das andere diese Dokumente verwendet, um eine zusammengefasste, zitierte Reaktion zu generieren.</p><h4>Reaktionsformat des Tools</h4><p>Jedes Tool kann beliebige Eingangsparameter akzeptieren, muss aber mit folgender Struktur antworten:</p><ul><li><strong>Inhalt:</strong> Dies ist die Reaktion des Tools im unstrukturierten Format. Dieses Feld wird in der Regel verwendet, um Text, Bilder, Audio, Links oder Einbettungen zurückzugeben. In dieser Anwendung dient es dazu, formatierten Text mit den Informationen zurückzugeben, die von den Tools generiert wurden.</li><li><strong>structuredContent: </strong>Dies ist eine optionale Rückgabe, die verwendet wird, um die Ergebnisse der einzelnen Tools in einem strukturierten Format bereitzustellen. Dies ist für programmatische Zwecke nützlich. Zwar wird es in diesem MCP-Server nicht verwendet, es kann jedoch von Nutzen sein, wenn Sie andere Tools entwickeln oder die Ergebnisse programmatisch verarbeiten möchten.</li></ul><p>Vor diesem Hintergrund betrachten wir nun jedes Tool im Detail.</p><h4>Tool „Search_docs“</h4><p>Dieses Tool führt eine <a href="https://www.elastic.co/docs/solutions/search/full-text">Volltextsuche</a> im Elasticsearch-Index durch, um die relevantesten Dokumente basierend auf der Nutzeranfrage abzurufen. Es hebt die wichtigsten Treffer hervor und bietet einen schnellen Überblick mit Relevanzbewertungen.</p><pre><code>server.registerTool(
  "search_docs",
  {
    title: "Search Documents",
    description:
      "Search for documents in Elasticsearch using full-text search. Returns the most relevant documents with their content, title, tags, and relevance score.",
    inputSchema: {
      query: z
        .string()
        .describe("The search query terms to find relevant documents"),
      max_results: z
        .number()
        .optional()
        .default(5)
        .describe("Maximum number of results to return"),
    },
    outputSchema: {
      results: z.array(SearchResultSchema),
      total: z.number(),
    },
  },
  async ({ query, max_results }) => {
    if (!query) {
      return {
        content: [
          {
            type: "text",
            text: "Query parameter is required",
          },
        ],
        isError: true,
      };
    }

    try {
      const response = await _client.search({
        index: INDEX,
        size: max_results,
        query: {
          bool: {
            must: [
              {
                multi_match: {
                  query: query,
                  fields: ["title^2", "content", "tags"],
                  fuzziness: "AUTO",
                },
              },
            ],
            should: [
              {
                match_phrase: {
                  title: {
                    query: query,
                    boost: 2,
                  },
                },
              },
            ],
          },
        },
        highlight: {
          fields: {
            title: {},
            content: {},
          },
        },
      });

      const results: SearchResult[] = response.hits.hits.map((hit: any) => {
        const source = hit._source as Document;

        return {
          id: source.id,
          title: source.title,
          content: source.content,
          tags: source.tags,
          score: hit._score ?? 0,
        };
      });

      const contentText = results
        .map(
          (r, i) =>
            `[${i + 1}] ${r.title} (score: ${r.score.toFixed(
              2,
            )})\n${r.content.substring(0, 200)}...`,
        )
        .join("\n\n");

      const totalHits =
        typeof response.hits.total === "number"
          ? response.hits.total
          : (response.hits.total?.value ?? 0);

      return {
        content: [
          {
            type: "text",
            text: `Found ${results.length} relevant documents:\n\n${contentText}`,
          },
        ],
        structuredContent: {
          results: results,
          total: totalHits,
        },
      };
    } catch (error: any) {
      console.log("Error during search:", error);

      return {
        content: [
          {
            type: "text",
            text: `Error searching documents: ${error.message}`,
          },
        ],
        isError: true,
      };
    }
  }
);</code></pre><p><em>Wir konfigurieren <code><a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-fuzzy-query">fuzziness</a>: “AUTO”</code> so, dass eine variable Tippfehlertoleranz basierend auf der Länge des analysierten Tokens besteht. Wir legen außerdem <code>title^2</code> fest, um die Bewertung der Dokumente zu erhöhen, bei denen die Übereinstimmung im Feld „Titel“ erfolgt.</em></p><h4>Tool „summarize_and_cite“</h4><p>Dieses Tool erstellt eine Zusammenfassung auf Basis von Dokumenten, die bei der vorherigen Suche ermittelt wurden. Es verwendet das <code>gpt-4o-mini</code>-Modell von OpenAI, um die relevantesten Informationen zur Beantwortung der Nutzerfragen zu synthetisieren und Antworten zu liefern, die direkt aus den Suchergebnissen abgeleitet werden. Neben der Zusammenfassung werden auch Metadaten für Zitate zu den verwendeten Quelldokumenten zurückgegeben.</p><pre><code>server.registerTool(
  "summarize_and_cite",
  {
    title: "Summarize and Cite",
    description:
      "Summarize the provided search results to answer a question and return citation metadata for the sources used.",
    inputSchema: {
      results: z
        .array(SearchResultSchema)
        .describe("Array of search results from search_docs"),
      question: z.string().describe("The question to answer"),
      max_length: z
        .number()
        .optional()
        .default(500)
        .describe("Maximum length of the summary in characters"),
      max_docs: z
        .number()
        .optional()
        .default(5)
        .describe("Maximum number of documents to include in the context"),
    },
    outputSchema: {
      summary: z.string(),
      sources_used: z.number(),
      citations: z.array(
        z.object({
          id: z.number(),
          title: z.string(),
          tags: z.array(z.string()),
          relevance_score: z.number(),
        })
      ),
    },
  },
  async ({ results, question, max_length, max_docs }) => {
    if (!results || results.length === 0 || !question) {
      return {
        content: [
          {
            type: "text",
            text: "Both results and question parameters are required, and results must not be empty",
          },
        ],
        isError: true,
      };
    }

    try {
      const used = results.slice(0, max_docs);

      const context = used
        .map(
          (r: SearchResult, i: number) =>
            `[Document ${i + 1}: ${r.title}]\\n${r.content}`
        )
        .join("\n\n---\n\n");

      // Generate summary with OpenAI
      const completion = await openai.chat.completions.create({
        model: "gpt-4o-mini",
        messages: [
          {
            role: "system",
            content:
              "You are a helpful assistant that answers questions based on provided documents. Synthesize information from the documents to answer the user's question accurately and concisely. If the documents don't contain relevant information, say so.",
          },
          {
            role: "user",
            content: `Question: ${question}\\n\\nRelevant Documents:\\n${context}`,
          },
        ],
        max_tokens: Math.min(Math.ceil(max_length / 4), 1000),
        temperature: 0.3,
      });

      const summaryText =
        completion.choices[0]?.message?.content ?? "No summary generated.";

      const citations = used.map((r: SearchResult) => ({
        id: r.id,
        title: r.title,
        tags: r.tags,
        relevance_score: r.score,
      }));

      const citationText = citations
        .map(
          (c: any, i: number) =>
            `[${i + 1}] ID: ${c.id}, Title: "${c.title}", Tags: ${c.tags.join(
              ", ",
            )}, Score: ${c.relevance_score.toFixed(2)}`,
        )
        .join("\n");

      const combinedText = `Summary:\\n\\n${summaryText}\\n\\nSources used (${citations.length}):\\n\\n${citationText}`;

      return {
        content: [
          {
            type: "text",
            text: combinedText,
          },
        ],
        structuredContent: {
          summary: summaryText,
          sources_used: citations.length,
          citations: citations,
        },
      };
    } catch (error: any) {
      return {
        content: [
          {
            type: "text",
            text: `Error generating summary and citations: ${error.message}`,
          },
        ],
        isError: true,
      };
    }
  }
);</code></pre><p>Zu guter Letzt starten wir den Server mithilfe von <a href="https://github.com/modelcontextprotocol/typescript-sdk?tab=readme-ov-file#stdio">stdio</a>. Das bedeutet, dass der MCP-Client mit unserem Server kommuniziert, indem er seine standardmäßigen Eingangs- und Ausgangsstreams liest und schreibt. stdio ist die einfachste Transportoption und funktioniert gut für lokale MCP-Server, die vom Client als Unterprozesse gestartet werden. Fügen Sie den folgenden Code am Ende der Datei hinzu:</p><pre><code>const transport = new StdioServerTransport();
server.connect(transport);</code></pre><p>Kompilieren Sie das Projekt jetzt mit folgendem Befehl:</p><pre><code>npx tsc index.ts --target ES2022 --module node16 --moduleResolution node16 --outDir ./dist --strict --esModuleInterop</code></pre><p>Dadurch wird ein Ordner <code>dist</code> erstellt und darin eine Datei <code>index.js</code>.</p><h3>Laden Sie den MCP-Server in Claude Desktop</h3><p>Folgen Sie <a href="https://modelcontextprotocol.io/docs/develop/connect-local-servers">dieser Anleitung</a>, um den MCP-Server mit Claude Desktop zu konfigurieren. In der Konfigurationsdatei von Claude müssen die folgenden Werte festgelegt werden:</p><pre><code>{
  "mcpServers": {
    "elasticsearch-rag-mcp": {
      "command": "node",
      "args": [   "/Users/user-name/app-dir/dist/index.js"
      ],
      "env": {
        "ELASTICSEARCH_ENDPOINT": "your-endpoint-here",
        "ELASTICSEARCH_API_KEY": "your-api-key-here",
        "OPENAI_API_KEY": "your-openai-key-here"
      }
    }
  }
}</code></pre><p>Der Wert <code>args</code> sollte auf die kompilierte Datei im Ordner <code>dist</code> verweisen. Außerdem müssen Sie die Umgebungsvariablen in der Konfigurationsdatei mit genau den gleichen Namen festlegen, die im Code definiert sind.</p><h3>Probieren Sie es aus</h3><p>Klicken Sie vor der Ausführung jedes Tools auf <strong>Search and Tools</strong>, um sicherzustellen, dass die Tools aktiviert sind. Hier können Sie außerdem alle Funktionen aktivieren oder deaktivieren:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/172981c2a54adabc70d5819013c3007670935605-1999x1002.png" style="width: 100%;" /><p>Zum Schluss testen wir den MCP-Server über den Claude Desktop-Chat und beginnen, Fragen zu stellen:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/03654c0f8c53c714f801fba8b25747071179209b-1999x1353.png" style="width: 100%;" /><p>Für die Frage „<strong>Suche nach Dokumenten zu Authentifizierungsmethoden und RBAC</strong>“ wird das <code>search_docs</code>-Tool ausgeführt und liefert folgende Ergebnisse:</p><pre><code>Most Relevant Documents:
Access Control and Role Management (highest relevance) - This document covers role-based access control (RBAC) principles, including ensuring users only have necessary permissions, regular auditing of user roles, revoking inactive accounts, and implementing just-in-time access for sensitive operations.
User Authentication with OAuth 2.0 - This document explains OAuth 2.0 authentication, which enables secure delegated access without credential sharing. It covers configuring identity providers, token management with limited scope and lifetime, and secure storage of refresh tokens.
Container Security Guidelines - While primarily about container security, this document touches on access control aspects like running containers as non-root users and avoiding embedded credentials.
Incident Response Playbook - This mentions role assignment during incidents (incident commander, communications lead, etc.), which relates to access control in emergency scenarios.
Logging Standards for Microservices - This document includes guidance on avoiding logging sensitive information, which is relevant to authentication security.</code></pre><p>Die Reaktion lautet: „Großartig! Ich habe 5 relevante Dokumente über Authentifizierungsmethoden und RBAC gefunden. Folgendes wurde gefunden:“</p><p>Der Toolaufruf gibt die Quelldokumente als Teil seiner Reaktionnutzlast zurück, die später zur Erstellung von Zitaten verwendet werden.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/25ff4c7e9563d99752f95540dafdc7fd211a66e3-800x530.gif" style="width: 100%;" /><p>Es ist auch möglich, mehrere Tools in einer einzigen Interaktion zu verketten. In diesem Fall analysiert Claude Desktop die Frage des Nutzers und stellt fest, dass zunächst <code>search_docs</code> aufgerufen werden muss, um relevante Dokumente abzurufen, und dass diese Ergebnisse anschließend an <code>summarize_and_cite</code> übergeben werden müssen, um die endgültige Antwort zu generieren, ohne dass dafür separate Prompts vom Nutzer erforderlich sind:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e6a8a46acb4236e77058f18bcd2f0737b5882c05-1999x1101.png" style="width: 100%;" /><p>In diesem Fall haben wir für die Abfrage „<strong>Was sind die wichtigsten Empfehlungen zur Verbesserung der Authentifizierung und Zugriffssteuerung in unseren Systemen? Referenzen einbeziehen.</strong>“ folgende Ergebnisse erzielt:</p><pre><code>Based on the documentation, here are the main recommendations to improve authentication and access control across your systems:

Key Recommendations

1. Implement Role-Based Access Control (RBAC)
Ensure users have only the permissions necessary for their job functions. This principle of least privilege minimizes security risks by limiting access to what's truly needed. [1]

2. Regular Access Audits
Conduct regular audits of user roles and promptly revoke access for inactive accounts. This helps prevent unauthorized access from dormant or compromised accounts. [1]

3. Just-in-Time (JIT) Access
Implement just-in-time access for sensitive operations, where permissions are granted only when needed and for a limited duration. This reduces the attack surface by minimizing standing privileges. [1]

4. OAuth 2.0 for Secure Authentication
Use OAuth 2.0 to enable secure delegated access without sharing user credentials. This protocol allows third-party applications to access resources on behalf of users without exposing passwords. [2]

5. Token Security and Management
Configure your identity provider to issue tokens with limited scope and lifetime

Store refresh tokens securely
Validate access tokens consistently to maintain security [2]
References

Access Control and Role Management (Tags: security, access-control)
User Authentication with OAuth 2.0 (Tags: authentication, oauth)
These recommendations work together to create a defense-in-depth approach, where multiple security layers protect your systems from unauthorized access.</code></pre><p>Wie im vorherigen Schritt können wir die Reaktion jedes Tools auf diese Frage einsehen:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/cb606d356b2f7d5e4878a5eff71bc881869ac0ee-800x585.gif" style="width: 100%;" /><p><em>Hinweis: Wenn ein Untermenü erscheint, in dem gefragt wird, ob Sie die Nutzung jedes Tools genehmigen möchten, wählen Sie <strong>Immer erlauben</strong> oder <strong>Einmal erlauben</strong>.</em></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/aea942ba9b0037526ea215bec65690f1a5c3099c-1522x250.png" style="width: 100%;" /><h2>Fazit</h2><p>MCP-Server stellen einen bedeutenden Schritt zur Standardisierung von LLM-Tools für lokale und entfernte Anwendungen dar. Die vollständige Kompatibilität ist zwar noch in Bearbeitung, wir verzeichnen jedoch große Fortschritte in diese Richtung.</p><p>In diesem Artikel haben wir gelernt, wie man einen benutzerdefinierten MCP-Server in TypeScript entwickelt, der Elasticsearch mit LLM-gestützten Anwendungen verbindet. Unser Server stellt zwei Tools bereit: <code>search_docs</code> zum Abrufen relevanter Dokumente mittels Abfrage-DSL und <code>summarize_and_cite</code> zur Erstellung von Zusammenfassungen mit Zitaten über OpenAI-Modelle und Claude Desktop als Client-Benutzeroberfläche.</p><p>Die Zukunft der Kompatibilität zwischen verschiedenen Client- und Server-Anbietern sieht vielversprechend aus. Zu den nächsten Schritten gehört die Erweiterung des Funktionsumfangs und die Erhöhung der Flexibilität Ihres Agenten. Es gibt einen praktischen <a href="https://www.elastic.co/search-labs/blog/llm-functions-elasticsearch-intelligent-query">Artikel</a> dazu, wie Sie Ihre Abfragen mithilfe von Suchvorlagen für mehr Präzision und Flexibilität parametrisieren können.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Ankündigung von Leseberechtigungen für Kibana-Dashboards]]></title>
            <description><![CDATA[Einführung von schreibgeschützten Dashboards in Kibana, die den Erstellern von Dashboards granulare Freigabekontrollen bieten, um die Ergebnisse korrekt zu halten und vor unerwünschten Änderungen zu schützen.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/kibana-dashboards-read-only-permissions</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/kibana-dashboards-read-only-permissions</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Fabio Busatto, Teresa Alvarez Soler]]></dc:creator>
            <pubDate>Thu, 26 Mar 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/11e578bc317aea30d2e10ccc0334a532f6af2ef9-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Sie kennen das. Sie verbringen eine Stunde damit, das perfekte Dashboard zur Überwachung Ihrer Protokolle zu erstellen: jedes Diagramm, jeden Filter und jede Beschriftung. Sie teilen es mit Ihrem Team. Ein paar Tage später öffnen Sie es und etwas stimmt nicht. Ein Kollege hat eine Abfrage angepasst. Oder jemand hat den Datenbereich geändert. Vielleicht dachten sie, sie würden helfen. Jetzt wühlen Sie sich durch die Revisionen und hinterfragen jede Zahl. Klingt das vertraut?</p><p>Genau aus diesem Grund haben wir <strong>schreibgeschützte Dashboards</strong> entwickelt. Das ist die Kontrolle, die Sie sich gewünscht haben. Geben Sie Dashboards vertrauensvoll weiter, ohne befürchten zu müssen, dass die nächste Person mit Bearbeitungsrechten sie verändert oder zerstört.</p><p>Hinweis: Schreibgeschützte Berechtigungen sind in Elastic Cloud Serverless und ab Version 9.3 für Elastic Cloud Hosted und Elastic Self-Managed verfügbar.</p><h2>Wenn die Option „Jeder kann bearbeiten“ Probleme bereitet</h2><p>In Kibana hat das <em>Teilen </em>in der Regel Berechtigungen auf Space-Ebene bedeutet. Wenn jemand Dashboards in einem Bereich erstellen kann, kann er auch die Dashboards anderer Personen bearbeiten oder löschen. Das ist großartig für die Zusammenarbeit – bis es das nicht mehr ist. Eine einzige versehentliche Änderung kann zu Fehlentscheidungen, Vertrauensverlust und viel Aufräumarbeit führen.</p><p>Wir haben die Workarounds gehört: <strong>„Wir haben ‚read-only‘ in den Dashboard-Namen gesetzt und hoffen, dass die Leute es bemerken.“</strong> Oder: <strong>„Wir markieren sie und drücken die Daumen.“</strong> Hoffnung ist kein Genehmigungsmodell. Sie brauchten eine echte Möglichkeit, ein Dashboard zu sperren, ohne alle aus dem Bereich auszusperren.</p><h2>Was tatsächlich schiefgeht</h2><p>Deb und Kevin haben beide Bearbeitungszugriff auf das Log-Monitoring-Dashboard im Operationsbereich. Kevin nimmt einige Änderungen an den Charts vor. Als Deb zurückkommt, stimmen die Zahlen nicht mit den von ihr präsentierten überein. Sie muss herausfinden, was sich geändert hat (oft aus dem Gedächtnis), es beheben und sich fragen, wie viele Berichte mit falschen Daten verschickt wurden.</p><h2>Schreibgeschützte Dashboards: Verantwortung und Kontrolle, die sinnvoll sind</h2><p>Schreibgeschützte Dashboards lösen dieses Problem, indem sie Ihnen die Kontrolle darüber geben, ob andere Nutzer das Dashboard bearbeiten können. Wenn Sie ein Dashboard teilen, wählen Sie: <strong>Bearbeiten</strong> (Standard, wie heute) oder <strong>Ansehen</strong>. Im <strong>Ansichtsmodus </strong>können nur Sie (und die Kibana-Administratoren) es ändern oder löschen. Alle anderen können es öffnen, verwenden und darauf vertrauen, aber sie können es nicht verändern.</p><h3>Was Sie erhalten</h3><ul><li><strong>Dashboard-Integrität:</strong> Im <strong>Ansichtsmodus</strong> können andere Nutzer mit Bearbeitungszugriff das Dashboard nicht ändern oder löschen. Wenn sie es versuchen, wird ihnen mitgeteilt, dass es gesperrt ist. Ihre Diagramme und Logik bleiben so, wie Sie sie verlassen haben.</li><li><strong>Sie behalten die Kontrolle:</strong> Sie sind der Besitzer. Sie können jederzeit bearbeiten, verfeinern und aktualisieren. Das Teilen als Nur-Ansicht sperrt Sie nicht aus; es fixiert die Version, die alle anderen sehen.</li><li><strong>Flexibler Lebenszyklus:</strong> Sie können ein Dashboard jederzeit wieder auf „bearbeitbar“ umschalten. Und Kibana-Administratoren können weiterhin alle Dashboards verwalten (zum Beispiel, wenn der Eigentümer ausscheidet). Keine Sackgassen.</li></ul><p>Sie können finalisierte, missionskritische Dashboards weitreichend teilen und sicher sein, dass sie konsistent bleiben. Dies ist in <strong>allen Elastic-Stufen und -Angeboten</strong> verfügbar, einschließlich Serverless.</p><h3>Wer kann was tun?</h3><p>Schnelle Referenz nach Rolle:</p><ul><li><strong>Dashboard-Besitzer:</strong> Sie haben es erstellt; Sie haben vollen Bearbeitungszugriff.</li><li><strong>Kibana-Administrator:</strong> Kann alle Dashboards verwalten.</li><li><strong>Nutzer mit Bearbeitungsrechten:</strong> Kann eigene Dashboards erstellen und bearbeiten; kann schreibgeschützte Dashboards weder bearbeiten noch löschen.</li><li><strong>Nutzer mit Ansichtsrechten:</strong> Kann nur Dashboards anzeigen (und auflisten).</li></ul>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Aktion</td><td style="border: 1px solid #ccc; padding: 4px;">Dashboard-Inhaber</td><td style="border: 1px solid #ccc; padding: 4px;">Kibana-Administrator</td><td style="border: 1px solid #ccc; padding: 4px;">Nutzer mit Bearbeitungsrechten</td><td style="border: 1px solid #ccc; padding: 4px;">Nutzer mit Space-Ansicht</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Dashboards auflisten und anzeigen</td><td style="border: 1px solid #ccc; padding: 4px;">✔</td><td style="border: 1px solid #ccc; padding: 4px;">✔</td><td style="border: 1px solid #ccc; padding: 4px;">✔</td><td style="border: 1px solid #ccc; padding: 4px;">✔</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Neue Dashboards erstellen</td><td style="border: 1px solid #ccc; padding: 4px;">✔</td><td style="border: 1px solid #ccc; padding: 4px;">✔</td><td style="border: 1px solid #ccc; padding: 4px;">✔</td><td style="border: 1px solid #ccc; padding: 4px;">✘</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Bearbeitbare Dashboards ändern/löschen</td><td style="border: 1px solid #ccc; padding: 4px;">✔</td><td style="border: 1px solid #ccc; padding: 4px;">✔</td><td style="border: 1px solid #ccc; padding: 4px;">✔</td><td style="border: 1px solid #ccc; padding: 4px;">✘</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Dashboards mit Lesezugriff ändern/löschen</td><td style="border: 1px solid #ccc; padding: 4px;">✔</td><td style="border: 1px solid #ccc; padding: 4px;">✔</td><td style="border: 1px solid #ccc; padding: 4px;">✘</td><td style="border: 1px solid #ccc; padding: 4px;">✘</td>
                    </tr>
              </tbody>
            </table>
          <h2>So aktivieren Sie den Schreibschutz</h2><p>Sie können den schreibgeschützten Modus beim Speichern eines neuen Dashboards oder später über das Freigabemenü festlegen.</p><h3>Beim Speichern eines neuen Dashboards</h3><ul><li>Erstellen Sie Ihr Dashboard, und klicken Sie auf <strong>Speichern</strong>.</li><li>Suchen Sie im Modal „Als neues Dashboard speichern“ nach <strong>Berechtigungen</strong>.</li><li>Ändern Sie diese von <strong>Kann bearbeiten</strong> in <strong>Kann ansehen</strong>.</li><li>Klicken Sie auf <strong>Speichern</strong>. Das war&#x27;s. Für alle anderen ist es schreibgeschützt.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/42a71d1bb55f9d50bd079f53bf45a0e1999b27f7-1214x1306.png" style="width: 100%;" /><h2>Für ein Dashboard, das Sie bereits besitzen</h2><ul><li>Dashboard öffnen.</li><li>Öffnen Sie das Menü <strong>Dashboard teilen</strong>.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e8405938557c879b1d4c262b98cf5a7f66408c04-1246x264.png" style="width: 100%;" /><ul><li>Im Freigabemodal suchen Sie <strong>Berechtigungen</strong> und wechseln Sie zu <strong>Kann anzeigen</strong>. Die Änderung tritt sofort in Kraft; andere Nutzer im selben Bereich können sie nicht mehr bearbeiten oder löschen.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/849405bc32701f3ebe0def012d8ae3cf3813ea0a-996x750.png" style="width: 100%;" /><ul><li>Sie können mit der Maus über die Aktion <strong>Teilen</strong> fahren, um zu sehen, welche Art von Berechtigungen ein bestimmtes Dashboard hat.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/80541ddb1b1bc567b0aeff693944ea8b6871d6a7-1270x320.png" style="width: 100%;" /><h3>Sehen, welche Dashboards gesperrt sind</h3><p>In der Hauptliste der Dashboards haben Dashboards, die Sie nicht bearbeiten oder löschen können, ein deaktiviertes Auswahlkästchen. Dadurch lässt sich leicht erkennen, welche Inhalte nur zur Ansicht freigegeben sind.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/24b2eba08df86174db949c662e7886c5aea1b460-1999x876.png" style="width: 100%;" /><p>Im Dashboard werden Sie außerdem feststellen, dass die Aktion „Bearbeiten“ deaktiviert ist und ein Tooltip erscheint, der erklärt, dass das Dashboard als schreibgeschützt eingestellt wurde.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e0a2f9da6dc854e876fc6dc2a7c3ef8b313b52ef-1358x330.png" style="width: 100%;" /><h2>Ausprobieren</h2><p>Schreibgeschützte Dashboards sind jetzt verfügbar. Erstellen Sie ein Dashboard, setzen Sie es auf <strong>Ansehen</strong> und teilen Sie es. Ihr Team erhält eine einzige verlässliche Informationsquelle, und Sie erhalten Sicherheit. Kein „Bitte nicht bearbeiten“ mehr im Titel.</p><p>Wir würden gerne hören, wie Sie schreibgeschützte Dashboards verwenden. Teilen Sie Ihr Feedback in unserem <a href="https://discuss.elastic.co">Community-Forum</a> mit.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Das Shell-Tool ist kein Allheilmittel für Kontext-Engineering]]></title>
            <description><![CDATA[Erfahren Sie, welche Tools zur Kontextsuche für das Kontext-Engineering existieren, wie sie funktionieren und welche Nachteile sie mit sich bringen.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/search-tools-context-engineering</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/search-tools-context-engineering</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Leonie Monigatti]]></dc:creator>
            <pubDate>Wed, 25 Mar 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f91e4d07915ba7bf3b7abf15fac8fab3350f7df2-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Die wichtigsten Werkzeuge eines Agenten sind die Suchwerkzeuge, mit denen er seinen eigenen Kontext aufbauen kann. Die jüngsten Beiträge von <a href="https://www.llamaindex.ai/blog/files-are-all-you-need">LlamaIndex</a> und <a href="https://x.com/hwchase17/status/2011814697889316930">LangChain</a> haben eine Diskussion ausgelöst: <em>Sind ein Shell-Tool und ein Dateisystem alles, was ein Agent für Kontext-Engineering braucht? </em>Leider driftete die Diskussion schnell in die falsche Richtung ab: Dateisystem versus Datenbank.</p><p>Dieser Beitrag konzentriert sich erneut auf die Frage:<em>Welche Suchoberflächen braucht ein Agent, um seinen eigenen Kontext zu erstellen?</em> Zunächst werden die Vor- und Nachteile von Shell-Tools und dedizierten Datenbanktools erläutert. Von dort aus ergibt sich ein praktisches Framework, um die richtigen Schnittstellen für die Bedürfnisse Ihres Agenten zu finden.</p><h2>Was bedeutet „Kontextaufbau“ eigentlich für einen Agenten?</h2><p>In frühen <a href="https://www.elastic.co/what-is/retrieval-augmented-generation">Retrieval-Augmented Generation (RAG)-Pipelines</a> erzeugte der Entwickler eine feste Retrieval-Pipeline, und das große Sprachmodell (LLM) war passiver Empfänger des Kontexts. Das war eine grundlegende Einschränkung: Der Kontext wurde bei jeder Abfrage abgerufen, egal ob er benötigt wurde oder nicht, ohne zu prüfen, ob er tatsächlich half.</p><p>Mit der Umstellung auf agentenbasiertes RAG haben die Agenten nun Zugriff auf eine Reihe von Suchwerkzeugen, um ihren eigenen Kontext zu erstellen. Beispielsweise ermöglichen sowohl Claude Code [1] als auch Cursor [2] dem Agenten, zwischen verschiedenen Suchwerkzeugen zu wählen und diese sogar für verkettete Abfragen zu kombinieren, je nachdem, was die Aufgabe tatsächlich erfordert.</p><h2>Welche Suchoberflächen gibt es für das Kontext-Engineering?</h2><p>Kontext kann sich an verschiedenen Orten befinden, etwa im Web, in einem lokalen Dateisystem oder in einer Datenbank. Ein Agent kann mit jeder dieser kontextlosen Datenquellen über verschiedene Werkzeuge interagieren:</p><ul><li><strong>Shell-Tools</strong> können Shell-Befehle ausführen und haben Zugriff auf das lokale Dateisystem. Beispiele für integrierte Shell-Tools sind <a href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/bash-tool">das Bash-Tool von Claude API</a>, <a href="https://docs.openclaw.ai/tools/exec">das Exec-Tool von OpenClaw</a> und <a href="https://docs.langchain.com/oss/python/integrations/tools/bash">das Shell-Tool von LangChain</a>.</li><li><strong>Spezielle Datenbanktools</strong> wie zum Beispiel Tools eines Model Context Protocol (MCP)-Servers (z. B. der <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/mcp-server">Elastic Agent Builder MCP-Server)</a> oder benutzerdefinierte Tools (z. B. <code>run_esql(query)</code> oder <code>db_list_index()</code>) können Datenbanken abfragen.</li><li><strong>Dedizierte Dateisuchwerkzeuge</strong> können lokale (oder hochgeladene) Dateien suchen und lesen (ohne vollständigen Zugriff auf die Shell). Beispiele für integrierte Dateisuchwerkzeuge sind <a href="https://ai.google.dev/gemini-api/docs/file-search">das Dateisuchwerkzeug der Gemini API</a> oder <a href="https://developers.openai.com/api/docs/guides/tools-file-search">das Dateisuchwerkzeug von OpenAI</a>.</li><li><strong>Web-Suchwerkzeuge</strong> können Informationen aus dem Web abrufen.</li><li><strong>Gedächtniswerkzeuge</strong> speichern und rufen aus dem Langzeitgedächtnis ab (unabhängig davon, wie es gespeichert ist).</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/115f20c8ded259e508f51524b2c06bdc702d70ab-1999x1050.png" style="width: 100%;" /><p>Wie Sie sehen, ist das Shell-Tool vielseitig und kann verwendet werden, um Kontext aus verschiedenen Datenquellen abzurufen, darunter:</p><ul><li><strong>Dateisystem:</strong> Der Agent erkundet die Verzeichnisstruktur (ls, find), sucht nach relevanten Inhalten (grep, cat) und wiederholt dies, bis er genügend Kontext aufgebaut hat.</li><li><strong>Datenbank:</strong> Der Agent kann Befehlszeilen-Schnittstellen (CLI)-Tools für Datenbanken verwenden (z. B. <a href="https://www.elastic.co/docs/reference/query-languages/sql/sql-cli"><code>elasticsearch-sql-cli</code></a>), HTTP-APIs über curl aufrufen oder Skripte ausführen, was insbesondere in Kombination mit Elastic Agent Skills nützlich ist. Dabei handelt es sich um wiederverwendbare, dokumentierte Beispiele, die in den Kontext des Agenten eingefügt werden, um die korrekte Tool-Nutzung anzuleiten (z. B. <a href="https://github.com/elastic/agent-skills">Elastic Agent Skills für Elasticsearch</a>).</li><li><strong>Web: </strong>Der Agent kann Websuchen per curl-Befehl über die API eines Suchanbieters ausführen.</li></ul><p>Das Shell-Tool bietet jedoch direkten Systemzugriff und erfordert daher Sicherheitsmaßnahmen, zum Beispiel die Ausführung in einer isolierten Sandbox-Umgebung und das Protokollieren aller ausgeführten Befehle.</p><h2>Wann sollten welche Suchoberflächen verwendet werden?</h2><p>Die richtige Suchschnittstelle hängt von Ihren Daten, Ihren Abfragemustern und Ihrem Anwendungsfall ab. Dieser Abschnitt dient als praktischer Ausgangspunkt.</p><h3>Dateisysteme machen Datenbanken nicht überflüssig</h3><p>Bei der Diskussion um Dateisysteme versus Datenbanken geht es nicht um die Speicherschicht. LangChain erklärt zum Beispiel, dass <a href="https://x.com/hwchase17/status/2011814697889316930">sein Speichersystem</a> Daten nicht wirklich in einem echten Dateisystem speichert. Stattdessen speichert es sie in einer Datenbank und <em>stellt</em> sie dem Agenten als eine Reihe von Dateien dar [3].</p><p>Dateisysteme eignen sich hervorragend für dateiabhängige Anwendungsfälle, wie z. B. Codierungsagenten. Sie eignen sich auch gut als temporärer Notizblock oder Arbeitsspeicher und für Einzelnutzer- oder Einzelagenten-Szenarien, bei denen die Gleichzeitigkeit keine Rolle spielt. In diesen Fällen bietet Ihnen ein physisches Dateisystem oder die Darstellung der Daten als Dateisystem Flexibilität, bevor Sie sich auf eine speziell entwickelte Schnittstelle festlegen.</p><p>Aber Dateisystemspeicher haben echte Nachteile, wie etwa schwache Parallelität, manuelle Schema-Durchsetzung und atomare Transaktionen. Diese treten noch deutlicher zutage, wenn Ihre Anwendung skaliert werden muss oder auf ein Multi-Agenten-Szenario umgestellt werden soll. Wer diese Nachteile ignoriert, sieht sich zu einer <a href="https://dx.tips/oops-database">mühsamen Neuerstellung schlechterer Datenbanken</a> gezwungen, ohne die jahrzehntelange technische Erfahrung hinter Transaktionssicherheit oder Zugriffskontrolle nutzen zu können, die Produktionsdatenbanken bereits bieten. Außerdem wählt man in den meisten Unternehmenskontexten nicht, ob eine Datenbank verwenden werden soll, da sie bereits vorhanden ist und geschäftskritische Daten speichert.</p><h3>Shell-Tool + Dateisystem</h3><p>Ein Shell-Tool ist der natürliche Ausgangspunkt für die Dateisystemsuche. Derzeit treiben Codierungsagenten den Fortschritt auf diesem Feld maßgeblich voran. Da sie mit Code in lokalen Dateien arbeiten, sind es von Natur aus dateiintensive Anwendungsfälle. Deshalb werden LLMs in der Post-Training-Phase für Codierungsaufgaben feinjustiert. Viele LLMs beherrschen daher nicht nur das Schreiben von Code, sondern auch die Verwendung von Shell-Befehlen und die Navigation in Dateisystemen.</p><p>Die Verwendung eines Shell-Tools mit integrierten CLIs wie <code>ls</code> und <code>grep</code> zum Suchen von Dateien ist effektiv. Mit grep ist eine Abfrage wie „Finde alle Dateien, die <code>matplotlib</code>importieren“ schnell, präzise und kostengünstig. Wenn der Agent jedoch konzeptionelle Anfragen bearbeiten muss, wie zum Beispiel „Wie geht unsere App mit fehlgeschlagener Authentifizierung um?“, kann die Mustererkennung mit grep schnell an ihre Grenzen stoßen. Um diese Lücke zu schließen, sind mehrere Alternativen entstanden, die semantische Suchfunktionen in die Befehlszeile einfügen, darunter <a href="https://github.com/jina-ai/jina-grep-cli"><code>jina-grep</code></a>.</p><p>Grep und viele seiner semantischen Suchalternativen laufen jedoch in O(n) über dem Korpus. Für Anwendungsfälle in Codebasen mag das in Ordnung sein. Wenn Ihre Datenmenge jedoch zunimmt, wird sich Latenz bemerkbar machen. In diesem Fall ist ein indizierter Datenspeicher erforderlich, um die Leistung aufrechtzuerhalten.</p><h3>Shell-Tool + Datenbank</h3><p>Eine weitere Möglichkeit, zusätzliche Suchfunktionen wie semantische oder hybride Suche für Ihre Daten zu erstellen, besteht darin, diese in einer Datenbank zu speichern, wie es beispielsweise Cursor tut. Und wenn Daten komplexe relationale Verknüpfungen oder Aggregationen erfordern, benötigen Sie eine Datenbankschnittstelle.</p><p>Falls die Daten nicht im Dateisystem, sondern in einer Datenbank gespeichert sind, kann ein Shell-Tool für bestimmte Anwendungsfälle als leichtgewichtige Datenbankschnittstelle dienen. Sind Ihre Abfragen einfach genug für eine CLI oder einen curl-Aufruf, führt ein spezielles Datenbanktool eventuell zu unnötiger Komplexität.</p><p>Dieser Ansatz eignet sich auch für frühe Explorationsphasen, in denen Sie noch nicht wissen, welche Abfragemuster Ihr Agent tatsächlich entwickeln wird. In diesem Fall können Agent Skills dem Agenten genügend Struktur geben, um korrekte Abfragen durchzuführen, ohne dass ein speziell dafür entwickeltes Tool erforderlich ist. Wenn der Agent jedoch viele Iterationen benötigt, um die richtige Methode zum Abfragen der Datenbank für wiederkehrende Aufgaben herauszufinden, rechtfertigt der Token-Overhead der Verwendung eines Shell-Tools als Schnittstelle den Einfachheitsvorteil, ein zusätzliches Tool zu vermeiden, nicht mehr.</p><h3>Dedizierte Datenbank-Tools</h3><p>Insbesondere bei strukturierten oder analytischen wiederkehrenden Abfragemustern sind spezielle Datenbankwerkzeuge notwendig. In einem <a href="https://vercel.com/blog/testing-if-bash-is-all-you-need">Blogbeitrag von Vercel und Braintrust</a> wurden Agenten mit verschiedenen Suchwerkzeugen für reale Rechercheaufgaben in semistrukturierten Daten verglichen, wie z. B. Kundensupport-Tickets und Transkripte von Verkaufsgesprächen (z. B. „Wie viele offene Tickets erwähnen ‚Sicherheit‘?“ oder „Finden Sie Tickets, bei denen jemand einen Fehler gemeldet hat und später jemand einen PR eingereicht hat, der behauptet, ihn zu beheben?“). [4].</p><p>Agenten mit dedizierten Datenbank-Tools verbrauchten weniger Token, waren schneller und machten weniger Fehler als Agenten, die nur ein Shell-Tool und ein Dateisystem verwendeten. Wir ziehen daraus den Schluss, dass direkte Datenbank-Tools die richtige Wahl sind, wenn die Abfrage analytische Schlussfolgerungen über teilweise strukturierte Daten erfordert.</p><h3>Kombination von Suchschnittstellen</h3><p>Keine einzelne Suchschnittstelle kann jede Suchanfrage optimal verarbeiten. Cursor kombiniert beispielsweise Shell-Tools (für Suchen mit grep) und semantische Suchwerkzeuge und ermöglicht es dem Agenten, mit dem Prompt des Nutzers das richtige Werkzeug auszuwählen. Sie berichten, dass der Agent für das Auffinden bestimmter Symbole oder Zeichenfolgen grep, für konzeptuelle oder Verhaltensfragen die semantische Suche und beides für explorative Aufgaben auswählt.</p><p>Das Vercel-Experiment kommt zum selben Ergebnis: Sein Hybrid-Agent mit Zugriff sowohl auf ein Shell-Tool als auch auf ein dediziertes Datenbank-Tool erzielte die beste Leistung aller getesteten Agenten, indem er zuerst die dedizierten Datenbank-Tools nutzte und anschließend die Ergebnisse durch das Durchsuchen des Dateisystems mit grep überprüfte. Jedoch verwendet dieser Ansatz mehr Token und Zeit für Überlegungen zur Werkzeugwahl und Verifizierung.</p><p>Das Muster ist in beiden Beispielen dasselbe: Komposition ist jeder einzelnen Schnittstelle überlegen, aber Komposition hat auch den Nachteil zusätzlicher Kosten und Latenz.</p><h2>Praktische Empfehlungen zur Auswahl des richtigen Werkzeugsatzes</h2><p>Die richtigen Suchschnittstellen sind klein, zielgerichtet und speziell auf die tatsächlichen Suchmuster Ihres Agenten zugeschnitten. Derzeit gilt es als Best Practice, statt eines Agenten mit Hunderten von MCP-Tools einen Agenten mit so wenigen Tools wie möglich zu haben. Das liegt daran, dass die sofortige Offenlegung aller möglichen Tools ein Nachteil ist, weil sie das Kontextfenster aufbläht und den Agenten darüber verwirrt, welches Tool er tatsächlich verwenden soll. Claude Code verfügt beispielsweise Berichten zufolge nur über etwa 20 Tools.</p><p>Die Idee der progressiven Offenlegung besteht hingegen darin, mit einem minimalen Satz an Tools zu beginnen und es dem Agenten zu ermöglichen, zusätzliche Fähigkeiten nur bei Bedarf abzufragen. Forschungen von Anthropic [5] und Cursor [6] haben gezeigt, dass dieser Ansatz eine Token-Ersparnis von 47%–85% bringt. Claude Code implementiert dies zum Beispiel direkt und ermöglicht es dem Agenten, schrittweise herauszufinden, wie eine API oder eine Datenbank abgefragt werden kann, ohne dass dieses Wissen bei jedem LLM-Aufruf Kontext verbraucht.</p><p>Sobald Sie mit den Abfragemustern des Agenten vertraut sind, können Sie die Suchwerkzeuge, auf die der Agent standardmäßig Zugriff hat, erneut aufrufen. Eine hilfreiche Herangehensweise an diesen Zielkonflikt ist das <a href="https://www.elastic.co/search-labs/blog/database-retrieval-tools-context-engineering#building-the-right-database-retrieval-tools-%5C(%E2%80%9Clow-floor,-high-ceiling%E2%80%9D%5C">Prinzip „Low Floor, High Ceiling“</a> zur Auswahl der passenden Werkzeuge. High-Ceiling-Tools schränken das Potenzial des Agenten nicht ein. Ein vielseitiges Shell-Tool ermöglicht es dem Agenten beispielsweise, vollständige, auch mehrdeutige Datenbankabfragen zu schreiben, aber auf Kosten des Argumentationsaufwands, höherer Latenz und geringerer Zuverlässigkeit.</p><p>Bei Low-Floor-Tools ist es genau umgekehrt. Dabei handelt es sich um spezialisierte Tools, die bestimmte Abfragen kapseln und dem Agenten mit minimalem zusätzlichem Überlegungsaufwand sofort zur Verfügung stellen – bei geringeren Kosten und höherer Zuverlässigkeit. Sie erfordern jedoch eine Vorentwicklung, können nicht jede mögliche Anfrage abdecken und erschweren es dem Agenten, das richtige Werkzeug auszuwählen.</p><p>Betrachten Sie jedes Tool als Teil eines Spektrums: Low-Floor-Tools sind für den Agenten leicht korrekt zu verwenden, haben aber einen begrenzten Anwendungsbereich. High-Ceiling-Tools sind vielseitig, erfordern aber mehr Überlegung, um sie richtig einzusetzen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e6d1b973be4b0a0a25c99c74f02a47e98395a3f7-1200x630.png" style="width: 100%;" /><p>Die meisten Agenten benötigen eine Mischung aus verschiedenen Suchwerkzeugen. Jedes Werkzeug muss sich jedoch als nützlich erweisen. Wir empfehlen, mit einem universellen Such-Tool zu beginnen (zum Beispiel einem <code>search_database()</code> -Tool oder einem Shell-Tool). Nutzen Sie dann die Befehlsprotokolle, die Sie aus Sicherheitsgründen bereits führen, erneut, um nachzuverfolgen, was Ihr Agent tatsächlich tut, einschließlich der Toolaufrufe, Wiederholungen und der Anzahl der Aufrufe pro Nutzerabfrage. Und wenn Sie feststellen, dass sich ein Abfragemuster wiederholt oder fehlschlägt, dann sollten Sie für diese Aufgabe ein spezielles Werkzeug erstellen.</p><h2>Zusammenfassung</h2><p>Die Debatte „Dateisystem versus Datenbank“ lenkt von der eigentlichen Frage ab, die sich Entwickler stellen müssen: <em>Welche Suchoberflächen braucht ein Agent, um seinen eigenen Kontext zu erstellen?</em> Die Antwort ist sehr wahrscheinlich: <em>keine einzige</em>.</p><p>Ein Shell-Tool ist ein vielseitiges Tool für die Interaktion mit unterschiedlichen kontextfremden Quellen und damit ein guter Ausgangspunkt. Für Anwendungsfälle mit strukturierten analytischen Abfragen ist es jedoch weniger effizient und präzise als dedizierte Datenbank-Tools.</p><p>Ziel ist es, die minimale Menge an Suchwerkzeugen zu finden, die die tatsächlichen Abfragemuster Ihres Agenten gut verarbeitet. Beginnen Sie mit einem Shell-Tool und protokollieren Sie, was Ihr Agent tatsächlich tut. Wenn Sie feststellen, dass sich ein Abfragemuster wiederholt und fehlschlägt, ist es Zeit, spezialisierte Werkzeuge zu entwickeln.</p><h2>Referenzen</h2><p>1. Thariq (Anthropic). <a href="https://x.com/trq212/status/2027463795355095314">Lessons from Building Claude Code: Seeing like an Agent</a> (2026).</p><p>2. Cursor: Dokumentation. <a href="https://cursor.com/docs/agent/tools/search">Semantic &amp; agentic search</a> (2026).</p><p>3. Harrison Chase (LangChain). <a href="https://x.com/hwchase17/status/2011814697889316930">How we built Agent Builder’s memory system</a> (2026).</p><p>4. Ankur Goyal (Braintrust) und Andrew Qu (Vercel). <a href="https://vercel.com/blog/testing-if-bash-is-all-you-need">Testing if &quot;bash is all you need&quot;</a> (2026).</p><p>5. Anthropic. <a href="https://www.anthropic.com/engineering/advanced-tool-use">Introducing advanced tool use on the Claude Developer Platform</a> (2025).</p><p>6. Cursor. <a href="https://cursor.com/blog/dynamic-context-discovery">Dynamische Kontexterkennung</a> (2026).</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Elasticsearch-Replikate für den Lastausgleich in Serverless]]></title>
            <description><![CDATA[Erfahren Sie, wie Elastic Cloud Serverless die Indexreplikate automatisch an die Suchlast anpasst und so eine optimale Abfrageleistung ohne manuelle Konfiguration gewährleistet.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/elasticsearch-replicas-load-balancing-serverless</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/elasticsearch-replicas-load-balancing-serverless</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Andrei Dan]]></dc:creator>
            <pubDate>Tue, 24 Mar 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3c4c1e99b892f026b7aba098973593f8298e2ea6-1280x717.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>In Elastic Cloud Serverless passen wir die Anzahl der Replikate für Ihre Indizes automatisch basierend auf der Suchlast an, um eine optimale Abfrageleistung ohne manuelle Konfiguration zu gewährleisten. In diesem Blogbeitrag erklären wir, wie Replikate skaliert werden, wann das System sie hinzufügt oder entfernt, und was das für Ihre Indizes bedeutet.</p><h2>Es ist zu voll auf der Party</h2><p>Sie veranstalten eine Pizza-Party. Sie haben ein paar Freunde, die Ihnen beim Servieren helfen, jeder an verschiedenen Stellen im Raum stationiert. Sie geben jedem Freund eine Pizza und verteilen die Stücke an die hungrigen Gäste, sobald diese eintreffen.</p><p>Zunächst läuft alles reibungslos. Nach und nach treffen ein paar Gäste ein, Ihre Freunde servieren Snacks, alle sind zufrieden. Aber dann spricht sich herum, wie gut Ihre Sauerteigpizzen sind. Es klingelt ständig an der Tür. Die Gäste strömen herein. Bald formiert sich eine Menschenmenge um einen Ihrer Freunde, der eine Salamipizza in der Hand hält, die anscheinend jeder will.</p><p>Ihr Freund mit der Salamipizza ist überfordert. Die Gäste warten, werden ungeduldig und es hat sich eine lange Warteschlange gebildet. Währenddessen steht Ihr Freund mit einer Margherita-Pizza herum, und kaum jemand fragt nach einem Stück.</p><p>Und was nun?</p><p>Sie bestellen ein paar mehr Salamipizzen und geben sie an andere Freunde weiter. Jetzt haben drei Freunde Salamipizzen parat, nicht nur einer. Die Menge verteilt sich und plötzlich können dreimal so viele Gäste gleichzeitig bedient werden.</p><p>Ein paar Dinge werden deutlich, wenn Sie mehr Partys veranstalten:</p><ul><li><strong>Nicht alle Pizzen sind gleich beliebt.</strong> Einige sind sehr gefragt, andere finden weniger Abnehmer. Sie benötigen keine zusätzlichen „Kopien“ unbeliebter Pizzen. Sie benötigen noch weitere der stark gefragten.</li><li><strong>Bestellen Sie mehr Pizzen, bevor die Warteschlange zu lang wird.</strong> Wenn Sie warten, bis Ihr Freund völlig überfordert ist und die Gäste verärgert abreisen, haben Sie zu lange gewartet. Es ist besser, eine zusätzliche Pizza zu holen, wenn Sie sehen, wie sich eine Menschenmenge bildet.</li><li><strong>Werfen Sie die Pizzen nicht zu schnell weg.</strong> Nur weil der Andrang am Salamistand für fünf Minuten zurückgegangen ist, heißt das nicht, dass der Ansturm vorbei ist. Vielleicht füllen die Leute nur ihre Getränke nach oder unterhalten sich miteinander (gibt es sowas heutzutage überhaupt noch?). Halten Sie die zusätzlichen Pizzen bereit. Wenn die Flaute eine Weile anhält, können Sie sie wegräumen.</li><li><strong>Sie können nur so viele Pizzen verteilen, wie Sie Freunde haben, die mithelfen.</strong> Wenn Ihnen nur vier Freunde helfen, ändern zehn Pizzen nichts am Ergebnis. Es können nur vier Menschen gleichzeitig bedient werden. Passen Sie die Anzahl der Pizzen an Ihre verfügbaren Helfer an.</li><li><strong>Wenn ein Freund geht, räumen Sie seine Pizza weg.</strong> Wenn einer Ihrer Freunde gehen muss, räumen Sie sofort seine Pizza weg. Sie können keine Pizzen unbeaufsichtigt stehen lassen. Geben Sie sie jemand anderem oder räumen Sie sie weg.</li></ul><h2>Von Pizzen bis zu Repliken</h2><p>Ordnen wir dies wieder Elasticsearch zu.</p><p>In unserer Analogie sind Pizzen die Replikate (Kopien Ihrer Index-Shards), Ihre Freunde, die sie servieren, sind die Suchknoten, die hungrigen Gäste sind die Suchabfragen, und die beliebte Pizza, um die sich alle reißen, ist ein heißer Index mit hoher Suchlast.</p><p>Wenn der Suchverkehr auf einem bestimmten Index zunimmt, erstellen wir zusätzliche Replikate und verteilen diese auf Ihre Suchknoten. Jedes Replikat kann jede Abfrage für diesen Index beantworten, genau wie jeder Freund, der eine Salamipizza in der Hand hält, Pizzastücke verteilen kann. Mehr Replikate bedeuten einen höheren Durchsatz: Drei Replikate können dreimal so viele Abfragen pro Sekunde wie ein einzelnes Replikat verarbeiten.</p><h2>Den Hunger messen</h2><p>Bevor wir entscheiden, wie viele Pizzen wir bestellen, müssen wir wissen, wie hungrig das Publikum ist.</p><p>Elasticsearch verfolgt die <strong>Suchlast</strong> für jeden Shard. Es ist eine Metrik, die erfasst, wie viel Suchaktivität ein Shard verarbeitet. Wir aggregieren dies über alle Shards eines Indexes, um die gesamte Suchnachfrage zu verstehen.</p><p>Am wichtigsten ist die <strong>relative Suchlast</strong>: Welcher Anteil des gesamten Suchverkehrs Ihres Projekts trifft jeden Index? Wenn ein Index 60 % aller Suchanfragen erhält, während ein anderer 5 % bekommt, wissen wir, wo wir Kapazität hinzufügen müssen.</p><h2>Die Berechnung hinter den Pizzen</h2><p>Wir berechnen die optimale Anzahl der Replikate nach folgender Formel:</p><pre><code>desired_replicas = min(ceil(L × N / (S × X)), N)</code></pre><p>Wo:</p><ul><li><strong>L</strong> = die relative Suchlast des Index (zwischen 0 und 1).</li><li><strong>N</strong> = die Anzahl der gewünschten Suchknoten in Ihrem Projekt.</li><li><strong>S</strong> = die Anzahl der Shards im Index.</li><li><strong>X</strong> = eine Schwelle, um Hotspots zu vermeiden (Standard: 0,5).</li></ul><p>Ein Beispiel: vier Suchknoten, ein Index mit zwei primären Shards, die 80 % des Suchverkehrs erhalten:</p><pre><code>desired_replicas = min(ceil(0.8 × 4 / (2 × 0.5)), 4)
                 = min(4, 4)
                 = 4</code></pre><p>Dieser Hot-Index erhält vier Replikate, die auf die Suchknoten verteilt werden.</p><p>Der Schwellenwert X (standardmäßig 0,5) ist wichtig. Wir warten nicht, bis ein Replikat völlig überlastet ist, sondern skalieren, wenn es die halbe Kapazität erreicht hat. Teilen Sie die zusätzliche Pizza aus, wenn Sie sehen, dass sich eine Menschenmenge bildet, nicht wenn die Gäste bereits gehen.</p><h2>Schnell hochskalieren, langsam herunterskalieren</h2><p>Wenn die Suchlast steigt, fügen wir sofort Repliken hinzu. Es gibt keinen Grund, die Nutzer warten zu lassen.</p><p>Wenn die Suchlast abfällt, warten wir eine Weile, bevor wir etwas unternehmen. Wir müssen eine konstant niedrige Nachfrage über einen Zeitraum von etwa 30 Minuten beobachten, bevor wir die Anzahl der Replikate reduzieren. (Dies dient dazu, mit Spitzenverkehr umzugehen, wobei ein ruhiger Moment nicht bedeutet, dass die Party vorbei ist.)</p><p>Das ist wichtig, weil das Hinzufügen eines Replikats Kosten verursacht. Das neue Replikat kopiert Daten und wärmt seine Caches auf, bevor es Abfragen effizient verarbeitet. Replikate zu voreilig zu entfernen bedeutet, dass Sie diese Anlaufkosten ständig erneut zahlen, weil der Traffic naturgemäß schwankt.</p><h2>Berücksichtigung der Topologiegrenzen</h2><p>Replikate können niemals die Anzahl der Suchknoten überschreiten. Mehr Replikate als Knoten zu haben, bringt keinen Vorteil (Sie können nur so viele Pizzen servieren, wie Sie Freunde haben, die beim Servieren der Pizzastücke helfen).</p><p>Wenn Knoten aus Ihrem Projekt entfernt werden, reduzieren wir die Anzahl der Replikate sofort entsprechend. Es wird nicht erst aufs Abkühlen gewartet, da es keine nicht zugewiesenen Replikate geben kann. Sobald ein Freund geht, räumen wir seine Pizza weg.</p><h2>Das größere Serverless-Bild</h2><p>Replikate für den Suchlastausgleich arbeiten mit anderen Systemen zur automatischen Skalierung zusammen:</p><ul><li><strong>Automatische Suchskalierung</strong> passt die Anzahl der Suchknoten an (Anzahl der helfenden Freunde).</li><li><strong>Replikate für die Suchlastverteilung</strong> verteilen den Traffic durch Anpassung der Replikatzahlen pro Index (wie viele Pizzen jeder Sorte benötigt werden).</li><li><strong>Datenstrom-Autosharding</strong> optimiert die Shard-Anzahl für Schreibvorgänge (wie man jede Pizza aufteilt, beschrieben im <a href="https://www.elastic.co/search-labs/blog/datastream-autosharding-serverless">vorherigen Beitrag</a>).</li></ul><p>Ein wichtiges Gestaltungsprinzip: Replikate für den Lastausgleich lösen nicht direkt die automatische Skalierung der Suche aus. Durch die Verteilung von Suchanfragen auf mehrere Replikate können Sie stattdessen die Ressourcenauslastung Ihrer Suchknoten erhöhen. Diese höhere Auslastung löst dann unsere vorhandene automatische Skalierungslogik aus, um bei Bedarf für zusätzliche Kapazitäten zu sorgen. Replikate für den Lastausgleich ermöglichen die automatische Skalierung und stellen sicher, dass Ihre Suchknoten tatsächlich genutzt werden, anstatt dass der gesamte Datenverkehr auf einem einzigen Replikat blockiert wird, während andere Knoten untätig bleiben.</p><h2>Was das für Sie bedeutet</h2><p>Sie müssen nicht vorhersagen, welche Indizes beliebt sein werden. Sie müssen die Replikate nicht manuell anpassen, wenn sich die Verkehrsmuster ändern. Sie müssen nicht um 3 Uhr morgens aufwachen, weil ein Ansturm Ihren am stärksten belasteten Index überfordert hat.</p><p>Das System überwacht, wo sich Warteschlangen bilden, und bestellt für diese Stellen mehr Pizzen. Kalte Indizes verschwenden keine Ressourcen für unnötige Replikate. Heiße Indizes erhalten die benötigte Kapazität. Ihr Budget fließt dort hin, wo es wichtig ist.</p><h2>Fazit</h2><p>Im <a href="https://www.elastic.co/search-labs/blog/datastream-autosharding-serverless">Autosharding-Beitrag</a> haben wir dafür gesorgt, dass Ihre Pizzen richtig aufgeteilt werden. Jetzt sorgen wir mit Replikaten für die Suchlastverteilung dafür, dass Sie genug Pizzen bereit haben, wenn die hungrigen Massen eintreffen.</p><p>Probieren Sie <a href="https://www.elastic.co/cloud/serverless">Elastic Cloud Serverless</a> aus und lassen Sie uns die Pizza-Logistik übernehmen.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Die Verwendung der Elasticsearch Inference API zusammen mit Hugging Face-Modellen]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie Elasticsearch mithilfe von Inferenz-Endpoints mit Hugging Face Modellen verbinden und ein mehrsprachiges Blog-Empfehlungssystem mit semantischer Suche und Chat-Abschlüssen erstellen.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/hugging-face-elasticsearch-inference-api</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/hugging-face-elasticsearch-inference-api</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
            <pubDate>Mon, 23 Mar 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1417d6ff033712206c9bd4bcc22074ee3437ce96-1999x1125.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>In den letzten Aktualisierungen hat Elasticsearch eine native Integration eingeführt, um sich mit Modellen zu verbinden, die auf dem <a href="https://endpoints.huggingface.co/">Hugging Face Inference Service</a> gehostet werden. In diesem Beitrag erfahren Sie, wie Sie diese Integration konfigurieren und über einfache API-Aufrufe mithilfe eines großen Sprachmodells (LLM) Inferenzen durchführen können. Wir verwenden <a href="https://huggingface.co/HuggingFaceTB/SmolLM3-3B">SmolLM3-3B</a>, ein leichtes Allzweckmodell mit einem ausgewogenen Gleichgewicht zwischen Ressourcenverbrauch und Antwortqualität.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7ddadf1976421a860a7d62087239adb9150d808b-1999x1388.png" style="width: 100%;" /><h2>Voraussetzungen</h2><ul><li><strong>Elasticsearch 9.3 oder Elastic Cloud Serverless: </strong>Sie können ein Cloud-Deployment erstellen, indem Sie <a href="https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud">diese Anweisungen</a> befolgen, oder stattdessen den <a href="https://www.elastic.co/docs/deploy-manage/deploy/self-managed/local-development-installation-quickstart#local-dev-quick-start"><code>start-local</code></a> Quickstart verwenden.</li><li><strong>Python 3.12: </strong>Laden Sie Python <a href="https://www.python.org/">hier</a> herunter.</li><li><strong>Hugging Face </strong><a href="https://huggingface.co/docs/hub/en/security-tokens">Zugriffstoken</a>.</li></ul><h2>Chat-Abschlüsse unter Verwendung eines Inferenz-Endpoints von Hugging Face</h2><p>Zuerst erstellen wir ein praktisches Beispiel, das Elasticsearch mit einem Hugging Face <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put">Inferenz-Endpoint</a> verbindet, um KI-gestützte Empfehlungen aus einer Reihe von Blogbeiträgen zu generieren. Für die Wissensdatenbank der App verwenden wir einen Datensatz mit Blogartikeln des Unternehmens, der wertvolle, aber oft schwer zugängliche Informationen enthält.</p><p>Bei diesem Endpoint ruft die <a href="https://www.elastic.co/docs/solutions/search/semantic-search">semantische Suche</a> die relevantesten Artikel für eine gegebene Abfrage ab, und ein Hugging Face LLM generiert kurze, kontextuelle Empfehlungen basierend auf diesen Ergebnissen.</p><p>Verschaffen wir uns einen groben Überblick über den Informationsfluss, den wir entwickeln werden:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1dfbc2323438feaaa42e13ab242dd1f7166f74aa-1200x676.png" style="width: 100%;" /><p>In diesem Artikel testen wir die Fähigkeit von <strong>SmolLM3-3B</strong> und<strong> </strong>kombinieren seine kompakte Größe mit einer starken mehrsprachigen Argumentations- und Tool-Aufruffunktion. Basierend auf einer Suchabfrage senden wir alle passenden Inhalte (auf Englisch und Spanisch) an das LLM, um eine Liste empfohlener Artikel mit einer individuell erstellten Beschreibung basierend auf der Suchanfrage und den Ergebnissen zu erstellen.</p><p>So könnte die Benutzeroberfläche einer Artikelseite mit einem System zur Generierung von KI-Empfehlungen aussehen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8d3b86b212f28ff279f2da67a33e6134039f0e4e-1999x949.png" style="width: 100%;" /><p>Sie können die vollständige Implementierung dieser Anwendung im verlinkten <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/elasticsearch-inference-api-and-hugging-face/notebook.ipynb">Notizbuch</a> finden.</p><h3>Konfiguration der Elasticsearch Inferenz-Endpoints</h3><p>Um den Elasticsearch <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put-hugging-face">Hugging Face Inferenz-Endpoint</a> zu verwenden, benötigen wir zwei wichtige Elemente: einen Hugging Face API-Schlüssel und eine ausgeführte Hugging Face Endpoint-URL. Dies sollte so aussehen:</p><pre><code>PUT _inference/chat_completions/hugging-face-smollm3-3b
{
    "service": "hugging_face",
    "service_settings": {
        "api_key": "hugging-face-access-token", 
        "url": "url-endpoint" 
    }
}</code></pre><p>Der Hugging Face Inferenz-Endpoint in Elasticsearch unterstützt verschiedene Aufgabentypen: <code>text_embedding</code>, <code>completion</code>, <code>chat_completion</code> und <code>rerank</code>. In diesem Blogbeitrag verwenden wir <code>chat_completion</code>, weil das Modell Gesprächsempfehlungen basierend auf den Suchergebnissen und einem Systemprompt generieren kann. Dieser Endpoint ermöglicht es uns, Chatabschlüsse direkt von Elasticsearch aus auf einfache Weise mit der Elasticsearch-API durchzuführen:</p><pre><code>POST _inference/chat_completion/hugging-face-smollm3-3b/_stream
{
  "messages": [
      { "role": "user", "content": "<user prompt>" }
  ]
}</code></pre><p>Dies dient als Kern der Anwendung, der den Prompt und die Suchergebnisse empfängt, die durch das Modell laufen werden. Nachdem die Theorie geklärt ist, können wir mit der Implementierung der Anwendung beginnen.</p><h4>Einrichten des ​​Inferenz-Endpoints auf Hugging Face</h4><p>Um das Hugging Face Modell bereitzustellen, werden wir <a href="https://huggingface.co/inference-endpoints/dedicated">Hugging Face One-Click-Deployments</a> verwenden, einen einfachen und schnellen Service für die Bereitstellung von Modell-Endpoints. Beachten Sie bitte, dass es sich um einen kostenpflichtigen Service handelt, durch dessen Nutzung zusätzliche Kosten entstehen können. In diesem Schritt wird die Modellinstanz erstellt, die zur Generierung der Empfehlungen für die Artikel verwendet wird.</p><p>Sie können ein Modell aus dem Ein-Klick-Katalog aussuchen:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b816e9fba1fe172687bf58f5143fb1f838c1077f-549x331.png" style="width: 100%;" /><p>Wählen Sie das <strong>SmolLM3-3B</strong> Modell:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/610d3aba0429f3666c2df3616d513eb6a4397c0c-502x478.png" style="width: 100%;" /><p>Hier können Sie die URL des Hugging Face Endpoints abrufen:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/025094ddb2cfbd1f0f216a5ec4e119b0f4fa2c42-646x328.png" style="width: 100%;" /><p>Wie in der <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put-hugging-face">Dokumentation zu den Hugging Face Inferenz-Endpunkten</a> von Elasticsearch erwähnt, erfordert die Textgenerierung ein Modell, das mit der OpenAI-API kompatibel ist. Aus diesem Grund müssen wir den <code>/v1/chat/completions</code>-Subpfad an die Hugging Face Endpoint-URL anhängen. Das Ergebnis sieht wie folgt aus:</p><pre><code>https://j2g31h0futopfkli.us-east-1.aws.endpoints.huggingface.cloud/v1/chat/completions</code></pre><p>Mit dieser Voraussetzung können wir mit dem Codieren in einem Python-Notebook beginnen.</p><h4>API-Schlüssel für Hugging Face generieren</h4><p>Erstellen Sie ein <a href="https://huggingface.co/join">Hugging Face Konto</a> und erhalten Sie ein API-Token, indem Sie <a href="https://huggingface.co/docs/hub/en/security-tokens#user-access-tokens">diesen Anweisungen</a> folgen. Man kann zwischen drei Token-Typen wählen: <em>detailliert</em> (empfohlen für die Produktion, da es nur Zugriff auf bestimmte Ressourcen bietet); <em>Lesezugriff</em> (schreibgeschützt); oder <em>Schreibzugriff</em> (für Lese- und Schreibzugriff). Für dieses Tutorial ist ein Lesezugriffstoken ausreichend, da wir nur den Inferenz-Endpoint aufrufen müssen. Speichern Sie diesen Code für den nächsten Schritt.</p><h4>Einrichten des Elasticsearch Inferenz-Endpoints</h4><p>Zunächst legen wir einen Elasticsearch-Python-Client fest:</p><pre><code>os.environ["ELASTICSEARCH_API_KEY"] = "your-elasticsearch-api-key"
os.environ["ELASTICSEARCH_URL"] = "https://xxxx.us-central1.gcp.cloud.es.io:443"

es_client = Elasticsearch(
    os.environ["ELASTICSEARCH_URL"], api_key=os.environ["ELASTICSEARCH_API_KEY"]
)</code></pre><p>Danach erstellen wir einen Elasticsearch Inferenz-Endpoint, der das Hugging Face Modell verwendet. Dieser Endpoint ermöglicht es uns, Reaktionen zu generieren, die auf den Blogbeiträgen und dem an das Modell übergebenen Prompt basieren.</p><pre><code>INFERENCE_ENDPOINT_ID = "smollm3-3b-pnz"

os.environ["HUGGING_FACE_INFERENCE_ENDPOINT_URL"] = (
 "https://j2g31h0futopfkli.us-east-1.aws.endpoints.huggingface.cloud/v1/chat/completions"
)
os.environ["HUGGING_FACE_API_KEY"] = "hf_xxxxx"

resp = es_client.inference.put(
        task_type="chat_completion",
        inference_id=INFERENCE_ENDPOINT_ID,
        body={
            "service": "hugging_face",
            "service_settings": {
                "api_key": os.environ["HUGGING_FACE_API_KEY"],
                "url": os.environ["HUGGING_FACE_INFERENCE_ENDPOINT_URL"],
            },
        },
    )</code></pre><h3>Datensatz</h3><p>Der Datensatz enthält die <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/elasticsearch-inference-api-and-hugging-face/dataset.json">Blogbeiträge</a>, die abgefragt werden, und repräsentiert einen mehrsprachigen Inhaltssatz, der im gesamten Workflow verwendet wird:</p><pre><code>// Articles dataset document example: 
{
    "id": "6",
    "title": "Complete guide to the new API: Endpoints and examples",
    "author": "Tomas Hernandez",
    "date": "2025-11-06",
    "category": "tutorial",
    "content": "This guide describes in detail all endpoints of the new API v2. It includes code examples in Python, JavaScript, and cURL for each endpoint. We cover authentication, resource creation, queries, updates, and deletion. We also explain error handling, rate limiting, and best practices. Complete documentation is available on our developer portal."
  }</code></pre><h4>Elasticsearch-Mappings</h4><p>Mit dem definierten Datensatz müssen wir ein Datenschema erstellen, das der Struktur des Blogbeitrags entspricht. Die folgenden <a href="https://www.elastic.co/docs/manage-data/data-store/mapping">Index-Mappings</a> werden verwendet, um die Daten in Elasticsearch zu speichern:</p><pre><code>INDEX_NAME = "blog-posts"

mapping = {
    "mappings": {
        "properties": {
            "id": {"type": "keyword"},
            "title": {
                "type": "object",
                "properties": {
                    "original": {
                        "type": "text",
                        "copy_to": "semantic_field",
                        "fields": {"keyword": {"type": "keyword"}},
                    },
                    "translated_title": {
                        "type": "text",
                        "fields": {"keyword": {"type": "keyword"}},
                    },
                },
            },
            "author": {"type": "keyword", "copy_to": "semantic_field"},
            "category": {"type": "keyword", "copy_to": "semantic_field"},
            "content": {"type": "text", "copy_to": "semantic_field"},
            "date": {"type": "date"},
            "semantic_field": {"type": "semantic_text"},
        }
    }
}


es_client.indices.create(index=INDEX_NAME, body=mapping)</code></pre><p>Hier können wir klar erkennen, wie die Daten strukturiert sind. Wir werden die semantische Suche verwenden, um Ergebnisse basierend auf natürlicher Sprache abzurufen, zusammen mit der <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/copy-to"><code>copy_to</code></a>-Eigenschaft, um den Feldinhalt in das <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text"><code>semantic_text</code></a>-Feld zu kopieren. Zusätzlich enthält das <code>title</code>-Feld zwei Unterfelder: Das <code>original</code>-Unterfeld speichert den Titel entweder auf Englisch oder Spanisch, abhängig von der Originalsprache des Artikels, und das <code>translated_title</code>-Unterfeld ist nur für spanische Artikel vorhanden und enthält die englische Übersetzung des Originaltitels.</p><h3>Ingestieren von Daten</h3><p>Der folgende Code-Schnipsel überträgt den Datensatz des Blogbeitrags in Elasticsearch mithilfe der <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/javascript/bulk_examples">Bulk-API</a>:</p><pre><code>def build_data(json_file, index_name):
    with open(json_file, "r") as f:
        data = json.load(f)

    for doc in data:
        action = {"_index": index_name, "_source": doc}
        yield action


try:
    success, failed = helpers.bulk(
        es_client,
        build_data("dataset.json", INDEX_NAME),
    )
    print(f"{success} documents indexed successfully")

    if failed:
        print(f"Errors: {failed}")
except Exception as e:
    print(f"Error: {str(e)}")</code></pre><p>Nun, da wir die Artikel in Elasticsearch aufgenommen haben, müssen wir eine Funktion erstellen, die nach dem Feld <code>semantic_text</code> sucht:</p><pre><code>def perform_semantic_search(query_text, index_name=INDEX_NAME, size=5):
    try:
        query = {
            "query": {
                "match": {
                    "semantic_field": {
                        "query": query_text,
                    }
                }
            },
            "size": size,
        }

        response = es_client.search(index=index_name, body=query)
        hits = response["hits"]["hits"]

        return hits
    except Exception as e:
        print(f"Semantic search error: {str(e)}")
        return []</code></pre><p>Wir benötigen außerdem eine Funktion, die den Endpoint aufruft. In diesem Fall rufen wir den Endpoint mit dem <strong><code>chat_completion</code> </strong>Aufgabentyp auf, um Streaming-Antworten zu erhalten:</p><pre><code>def stream_chat_completion(messages: list, inference_id: str = INFERENCE_ENDPOINT_ID):
    url = f"{ELASTICSEARCH_URL}/_inference/chat_completion/{inference_id}/_stream"
    payload = {"messages": messages}
    headers = {
        "Authorization": f"ApiKey {ELASTICSEARCH_API_KEY}",
        "Content-Type": "application/json",
    }

    try:
        response = requests.post(url, json=payload, headers=headers, stream=True)
        response.raise_for_status()

        for line in response.iter_lines(decode_unicode=True):
            if line:
                line = line.strip()

                if line.startswith("event:"):
                    continue

                if line.startswith("data: "):
                    data_content = line[6:]

                    if not data_content.strip() or data_content.strip() == "[DONE]":
                        continue

                    try:
                        chunk_data = json.loads(data_content)

                        if "choices" in chunk_data and len(chunk_data["choices"]) > 0:
                            choice = chunk_data["choices"][0]
                            if "delta" in choice and "content" in choice["delta"]:
                                content = choice["delta"]["content"]
                                if content:
                                    yield content

                    except json.JSONDecodeError as json_err:
                        print(f"\nJSON decode error: {json_err}")
                        print(f"Problematic data: {data_content}")
                        continue

    except requests.exceptions.RequestException as e:
        yield f"Error: {str(e)}"</code></pre><p>Nun können wir eine Funktion schreiben, die die semantische Suchfunktion, den <code>chat_completions</code> Inferenz-Endpoint und den Empfehlungs-Endoint aufruft, um die Daten zu generieren, die in den Karten angezeigt werden:</p><pre><code>def recommend_articles(search_query, index_name=INDEX_NAME, max_articles=5):
    print(f"\n{'='*80}")
    print(f"🔍 Search Query: {search_query}")
    print(f"{'='*80}\n")

    articles = perform_semantic_search(search_query, index_name, size=max_articles)

    if not articles:
        print("❌ No relevant articles found.")
        return None, None

    print(f"✅ Found {len(articles)} relevant articles\n")

    # Build context with found articles
    context = "Available blog articles:\n\n"
    for i, article in enumerate(articles, 1):
        source = article.get("_source", article)
        context += f"Article {i}:\n"
        context += f"- Title: {source.get('title', 'N/A')}\n"
        context += f"- Author: {source.get('author', 'N/A')}\n"
        context += f"- Category: {source.get('category', 'N/A')}\n"
        context += f"- Date: {source.get('date', 'N/A')}\n"
        context += f"- Content: {source.get('content', 'N/A')}\n\n"

    system_prompt = """You are an expert content curator that recommends blog articles.

    Write recommendations in a conversational style starting with phrases like:
    - "If you're interested in [topic], this article..."
    - "This post complements your search with..."
    - "For those looking into [topic], this article provides..."


    FORMAT REQUIREMENTS:
    - Return ONLY a JSON array
    - Each element must have EXACTLY these three fields: "article_number", "title", "recommendation"
    - If the original title is in spanish, use the "translated_title" subfield in the "title" field

    Keep each recommendation concise (2-3 sentences max) and focused on VALUE to the reader.

    EXAMPLE OF CORRECT FORMAT:
    [
        {"article_number": 1, "title": "Article title in english", "recommendation": "If you are interested in [topic], this article provides..."},
        {"article_number": 2, "title": "Article title in english", "recommendation": " for those looking into [topic], this article provides..."}
    ]

    Return ONLY the JSON array following this exact structure."""

    user_prompt = f"""Search query: "{search_query}"

    Generate recommendations for the following articles: {context}
    """

    messages = [
        {"role": "system", "content": "/no_think"},
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": user_prompt},
    ]

    # LLM generation
    print(f"{'='*80}")
    print("🤖 Generating personalized recommendations...\n")

    full_response = ""

    for chunk in stream_chat_completion(messages):
        print(chunk, end="", flush=True)
        full_response += chunk

    return context, articles, full_response</code></pre><p>Abschließend müssen wir die Informationen extrahieren und formatieren, um sie zu drucken:</p><pre><code>def display_recommendation_cards(articles, recommendations_text):
    print("\n" + "=" * 100)
    print("📇 RECOMMENDED ARTICLES".center(100))
    print("=" * 100 + "\n")

    # Parse JSON recommendations - clean tags and extract JSON
    recommendations_list = []
    try:

        # Clean up <think> tags
        cleaned_text = re.sub(
            r"<think>.*?</think>", "", recommendations_text, flags=re.DOTALL
        )
        # Remove markdown code blocks ( ... ``` or ``` ... ```)
        cleaned_text = re.sub(r"```(?:json)?", "", cleaned_text)
        cleaned_text = cleaned_text.strip()

        parsed = json.loads(cleaned_text)

        # Extract recommendations from list format
        for item in parsed:
            article_number = item.get("article_number")
            title = item.get("title", "")
            rec_text = item.get("recommendation", "")

            if article_number and rec_text:
                recommendations_list.append(
                    {
                        "article_number": article_number,
                        "title": title,
                        "recommendation": rec_text,
                    }
                )
    except json.JSONDecodeError as e:
        print(f"⚠️  Could not parse recommendations as JSON: {e}")
        return

    for i, article in enumerate(articles, 1):
        source = article.get("_source", article)

        # Card border
        print("┌" + "─" * 98 + "┐")

        # Find recommendation and title for this article number
        recommendation = None
        title = None
        for rec in recommendations_list:
            if rec.get("article_number") == i:
                recommendation = rec.get("recommendation")
                title = rec.get("title")
                break

        # Print title
        title_lines = textwrap.wrap(f"📌 {title}", width=94)
        for line in title_lines:
            print(f"│  {line}".ljust(99) + "│")

        # Card border
        print("├" + "─" * 98 + "┤")

        # Print recommendation
        if recommendation:
            recommendation_lines = textwrap.wrap(recommendation, width=94)
            for line in recommendation_lines:
                print(f"│  {line}".ljust(99) + "│")

        # Card bottom
        print("└" + "─" * 98 + "┘")</code></pre><p>Wir führen einen Test durch, indem wir eine Frage zu den Sicherheitsblogbeiträgen stellen:</p><pre><code>search_query = "Security and vulnerabilities"

context, articles, recommendations = recommend_articles(search_query)

print("\nElasticsearch context:\n", context)

# Display visual cards
display_recommendation_cards(articles, recommendations)</code></pre><p>Hier sehen wir die vom Workflow in der Konsole generierten Karten:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/730d35212594bb3db30447c3ea7e2a92857287b7-1999x1515.png" style="width: 100%;" /><p>Die vollständigen Ergebnisse, einschließlich aller Treffer und der LLM-Reaktion, können Sie in <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/elasticsearch-inference-api-and-hugging-face/results.md">dieser Datei</a> sehen.</p><p>Wir bitten um Artikel zum Thema: „Sicherheit und Schwachstellen“. Diese Frage wird als Suchabfrage gegen die in Elasticsearch gespeicherten Dokumente verwendet. Die abgerufenen Ergebnisse werden dann an das Modell weitergegeben, das basierend auf ihrem Inhalt Empfehlungen generiert. Wie wir sehen können, hat das Modell gute Arbeit geleistet und einen ansprechenden kurzen Text erstellt, der den Leser zum Anklicken motivieren kann.</p><h2>Fazit</h2><p>Dieses Beispiel zeigt, wie Elasticsearch und Hugging Face kombiniert werden können, um ein schnelles und effizientes zentrales System für KI-Anwendungen zu schaffen. Dieser Ansatz reduziert den manuellen Aufwand und bietet dank des umfangreichen Modellkatalogs von Hugging Face mehr Flexibilität. Insbesondere die Verwendung von SmolLM3-3B zeigt, wie kompakte, mehrsprachige Modelle in Kombination mit semantischer Suche weiterhin sinnvolles Schlussfolgern und Content-Generierung liefern können. Zusammen bieten diese Tools eine skalierbare und effektive Grundlage für die Entwicklung intelligenter Inhaltsanalysen und mehrsprachiger Anwendungen.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Schnell vs. genau: Messung der Recall-Rate bei der quantisierten Vektorsuche]]></title>
            <description><![CDATA[Eine Erklärung, wie der Recall für die Vektorsuche in Elasticsearch mit minimalem Aufwand gemessen werden kann.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/recall-vector-search-quantization</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/recall-vector-search-quantization</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jeff Vestal]]></dc:creator>
            <pubDate>Fri, 20 Mar 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/09f03b9239d66c36763cdab3fafcdac207ff6d83-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Wir alle wünschen uns eine sofortige Vektorsuche. Jedoch sind hochdimensionale Vektoren sehr umfangreich. Ein einzelner 1.024-dimensionaler Float-32-Vektor beansprucht viel Speicherplatz, wobei der Vergleich mit Millionen anderer Vektoren einen hohen Rechenaufwand erfordert.</p><p>Zur Lösung dieses Problems wenden Suchmaschinen wie Elasticsearch zwei wesentliche Optimierungsstrategien an:</p><ol><li><strong>Ungefähre Suche (Hierarchical Navigable Small World [HNSW]):</strong> Anstatt jedes Dokument zu durchsuchen, erstellen wir einen Navigationsgraphen, um schnell in die wahrscheinliche Nachbarschaft der Antwort zu gelangen.</li><li><strong>Quantisierung:</strong> Wir komprimieren die Vektoren (beispielsweise von 32-Bit-Gleitkommazahlen auf 8-Bit-Ganzzahlen oder sogar auf 1-Bit-Binärwerte), um den Speicherbedarf zu verringern und die Berechnungen zu beschleunigen.</li></ol><p>Doch Optimierung hat oft ihren Preis: <strong>Genauigkeit</strong>.</p><p>Die Befürchtung ist berechtigt: „Wenn ich meine Daten komprimiere und bei der Suche Abstriche mache, verpasse ich dann die besten Ergebnisse?“ „Beeinträchtigt diese Optimierung die Relevanz meiner Suchmaschine?“</p><p>Für den Nachweis, dass die Quantisierung von Elastic die Ergebnisse nicht beeinträchtigt, haben wir einen wiederholbaren Testrahmen unter Verwendung des <a href="https://huggingface.co/datasets/fancyzhx/dbpedia_14"><strong>DBPedia-14</strong>-Datensatzes</a> entwickelt, um präzise zu berechnen, wie viel Genauigkeit (genauer gesagt <strong>Recall)</strong> wir bei der Verwendung der Standardoptimierungen in Elasticsearch zugunsten der Geschwindigkeit einbüßen.</p><p>Kurz gesagt: Viel weniger, als Sie denken. Sehen Sie sich das <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/fast_vs_accurate_measuring_the_recall_of_quantized_vector_search/vector_recall_notebook.ipynb">Notebook hier</a> an und testen Sie es selbst</p><h2><strong>Die Definitionen (für die Nicht-Experten)</strong></h2><p>Klären wir zunächst einige Begriffe, bevor wir uns den Code ansehen.</p><ul><li><strong>Relevanz vs. Recall:</strong> <strong>Relevanz</strong> ist subjektiv (habe ich etwas Gutes gefunden?). <strong>Recall</strong> ist mathematisch. Wenn sich in der Datenbank 10 Dokumente befinden, die <em>perfekt</em> zu Ihrer Abfrage passen, und die Suchmaschine neun davon findet, beträgt Ihr Recall 90 % (oder 0,9).</li><li><strong>Exakte Suche (flach):</strong> Wird auch als „Brute-Force“-Methode bezeichnet. Die Suchmaschine scannt jedes einzelne Dokument in einem Index und berechnet die Entfernung.<ul><li><em>Vorteile:</em> 100 % perfekter Recall.</li><li><em>Nachteile:</em> Rechenintensiv und bei großem Umfang langsam.</li></ul></li><li><strong>Näherungssuche (HNSW):</strong> Die „Abkürzungs“-Methode. Die Suchmaschine erstellt einen <a href="https://www.elastic.co/search-labs/blog/hnsw-graph">HNSW-Graphen</a>. Sie durchläuft den Graphen, um die nächsten Nachbarn zu finden.<ul><li><em>Vorteile:</em> Extrem schnell und skalierbar.</li><li><em>Nachteile:</em> Falls das Durchlaufen des Graphen zu früh beendet wird, könnte ein Nachbar übersehen werden.</li></ul></li></ul><h2><strong>Das Experiment: Exakt vs. ungefähr</strong></h2><p>Für den Recall-Test haben wir den <strong>DBPedia-14</strong>-Datensatz verwendet, einen umfangreichen Datensatz mit Titeln und Auszügen aus 14 Ontologieklassen, der häufig zum Trainieren und Bewerten von Modellen zur Textkategorisierung genutzt wird. Konkret konzentrieren wir uns auf die Kategorie „Film“. Wir wollten die optimierten Produktionseinstellungen mit einem mathematisch perfekten Referenzwert vergleichen.</p><p>Für dieses Experiment verwenden wir das Modell <a href="https://www.elastic.co/search-labs/blog/jina-embeddings-v5-text">jina-embeddings-v5-text-small</a>, ein fortschrittliches mehrsprachiges Modell, das bei den Branchen-Benchmarks für Textdarstellung führend ist. Wir haben uns für dieses Modell entschieden, da es den aktuellen Standard für leistungsstarke Einbettungen setzt. Durch die Kombination der herausragenden Genauigkeit von Jina v5 mit der nativen Quantisierung von Elasticsearch können wir eine Sucharchitektur präsentieren, die sowohl rechnerisch effizient ist als auch keine Kompromisse bei der Abrufqualität eingeht.</p><p>Wir haben einen Index mit doppeltem Mapping eingerichtet. Wir haben denselben Text gleichzeitig in zwei verschiedene Felder aufgenommen:</p><ol><li><strong><code>content.raw</code> </strong>mit Typ: <code>flat</code>. Dadurch wird Elasticsearch gezwungen, einen Brute-Force-Scan der gesamten Float32-Vektoren durchzuführen. Hierdurch werden exakte Übereinstimmungen geliefert, die als Ausgangsbasis dienen.</li><li><strong><code>content</code> </strong>mit Typ <code>semantic_text</code>. Standardmäßig werden HNSW und „Better Binary Quantization“ (BBQ) verwendet. Hierbei handelt es sich um die standardmäßige, optimierte Produktionseinstellung für die ungefähre Übereinstimmung.</li></ol><h3><strong>Der Recall @10-Test</strong></h3><p>Als Metrik verwendeten wir Recall@10.</p><p>Wir haben 50 zufällige Filme ausgewählt und dieselbe Abfrage für beide Felder durchgeführt.</p><ul><li>Wenn die <strong>exakte (flache)</strong> Suche ergibt, dass die ersten 10 Nachbarn IDs [1, 2, 3 ... 10] sind.</li><li>Und die <strong>ungefähre (HNSW)</strong> Suche liefert die IDs [1, 2, 3 ... 9, 99].</li><li>Wir haben neun der Top 10 korrekt gefunden. Der Score liegt bei <strong>0,9</strong>.</li></ul><p>Hier ist das von uns verwendete Mapping:</p><pre><code># The "Control Group": Forces exact brute-force scan
"raw": {
    "type": "semantic_text",
    "inference_id": ".jina-embeddings-v5-text-small",
    "index_options": {
        "dense_vector": {
            "type": "flat"
        }
    }
}</code></pre><p><strong>Das Ergebnis: Die „flache Kurve“ des Erfolgs</strong></p><p>Wir haben einen Skalierungstest durchgeführt, bei dem wir den gesamten Datensatz neu geladen und mit Indexgrößen von 1.000 bis 40.000 Dokumenten getestet haben.</p><p>So hat sich der Recall-Score entwickelt:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Dokumente</td><td style="border: 1px solid #ccc; padding: 4px;">Recall@10-Wert</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">1.000</td><td style="border: 1px solid #ccc; padding: 4px;">1.000 (100 %)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">5.000</td><td style="border: 1px solid #ccc; padding: 4px;">0,998 (100 %)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">10.000</td><td style="border: 1px solid #ccc; padding: 4px;">0,992 (99,4 %)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">20.000</td><td style="border: 1px solid #ccc; padding: 4px;">0,999 (99,0 %)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">40.000</td><td style="border: 1px solid #ccc; padding: 4px;">0,992 (98,8 %)</td>
                    </tr>
              </tbody>
            </table>
          <p>Die Ergebnisse waren erstaunlich stabil. Selbst als wir die Skalierung erhöhten, stimmte die ungefähre Suche in <strong>&gt;99 % der Fälle</strong> mit der exakten Brute-Force-Suche überein.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a4bfacb1d0cce6fdf6df0e1a9d4fc5d4007a66da-1999x1209.png" style="width: 100%;" /><h2><strong>Warum hat es so gut funktioniert?</strong></h2><p>Sie könnten erwarten, dass die Komprimierung von Vektoren zu Binärwerten die Genauigkeit stärker beeinträchtigen würde. Der Grund dafür liegt in der Art und Weise, wie Elasticsearch den Abruf handhabt.</p><p>Die meisten Einbettungsmodelle liefern heutzutage Float32-Vektoren als Ausgabe, die sehr groß sind. Für eine effiziente Suche nutzt Elasticsearch die Quantisierung für hochdimensionale Vektoren. Genauer gesagt wird seit Version 9.2 standardmäßig <a href="https://www.elastic.co/search-labs/blog/elasticsearch-9-1-bbq-acorn-vector-search">BBQ</a> verwendet.</p><p>BBQ verwendet einen <strong>Rescoring</strong>-Mechanismus:</p><ol><li><strong>Durchlaufen:</strong> Die Suchmaschine verwendet die komprimierten (quantisierten) Vektoren, um den HNSW-Graphen schnell zu durchlaufen. Da die Vektoren klein sind, kann das System effizient überabtasten und so eine größere Liste von Kandidaten (zum Beispiel die 100 am ehesten passenden Dokumente) ohne Leistungseinbußen zusammenstellen.</li><li><strong>Rescore:</strong> Sobald diese Kandidaten vorliegen, ruft das System die Werte in voller Genauigkeit nur für diese wenigen Dokumente ab, um das endgültige, genaue Ranking zu berechnen.</li></ol><p>So erhalten Sie das Beste aus beiden Welten: die Geschwindigkeit der Quantisierung für die rechenintensiven Aufgaben und die Präzision von Gleitkommazahlen für die abschließende Sortierung.</p><h2><strong>Können wir das besser machen?</strong></h2><p>An dieser Stelle ist anzumerken, dass die hier gezeigten Ergebnisse auf den Standardeinstellungen und einer zufälligen Stichprobe von Daten basieren. Betrachten Sie das als einen leistungsstarken Ausgangspunkt. Auch wenn Jina v5 ein wahres Kraftpaket ist, sind diese Recall-Werte keine allgemeingültige Garantie für jeden Datensatz. Jede Datenerhebung hat ihre Eigenheiten, und obwohl es durchaus möglich ist, die Leistung durch weitere Optimierungen weiter zu steigern, sollten Sie stets einen Vergleichstest mit Ihren eigenen spezifischen Daten durchführen, um Ihre Leistungsgrenze zu ermitteln.</p><h2><strong>Fazit</strong></h2><p>Es handelt sich hierbei um einen sehr kleinen Test. Der Zweck dieser Übung besteht jedoch nicht speziell in der Messung des Einbettungsmodells oder von BBQ, sondern vielmehr in der Veranschaulichung, wie Sie mit minimalem Aufwand den Recall Ihres Datensatzes messen können.</p><p>Wenn Sie diesen Test mit Ihren eigenen Daten durchführen möchten, können Sie sich das <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/fast_vs_accurate_measuring_the_recall_of_quantized_vector_search/vector_recall_notebook.ipynb">Notebook hier</a> ansehen und es selbst testen.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Die Gemini CLI-Erweiterung für Elasticsearch mit Tools und Fähigkeiten]]></title>
            <description><![CDATA[Wir stellen die Erweiterung von Elastic für Googles Gemini CLI vor, mit der Elasticsearch-Daten in Entwickler- und agentischen Workflows gesucht, abgerufen und analysiert werden können.
]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/gemini-cli-extension-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/gemini-cli-extension-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Walter Rafelsberger, Enrico Zimuel]]></dc:creator>
            <pubDate>Tue, 17 Mar 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6dfcec4a77b3dc83bf0d974417bf2e211abb1f4f-876x468.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Wir freuen uns, die Veröffentlichung unserer Elastic-Erweiterung für Googles Gemini CLI ankündigen zu können, mit der Sie die volle Leistungsfähigkeit von <a href="https://www.elastic.co/elasticsearch">Elasticsearch</a> und <a href="https://www.elastic.co/elasticsearch/agent-builder">Elastic Agent Builder</a> direkt in Ihren KI-Entwicklungsworkflow einbringen können. Diese Erweiterung bietet auch mehrere kürzlich entwickelte Agentenfähigkeiten für die Interaktion mit Elasticsearch.</p><p>Die Erweiterung ist <a href="https://github.com/elastic/gemini-cli-elasticsearch">hier</a> als Open-Source-Projekt verfügbar.</p><h2>Was ist Gemini CLI, und wie installieren Sie sie?</h2><p><a href="https://geminicli.com/">Gemini CLI</a> ist ein Open-Source-KI-Agent, der Googles Gemini-Modelle direkt in die Befehlszeile bringt. Er ermöglicht Entwicklern, über das Terminal mit KI zu interagieren, um Aufgaben wie das Generieren von Code, das Bearbeiten von Dateien, das Ausführen von Shell-Befehlen und das Abrufen von Informationen aus dem Web durchzuführen.</p><p>Im Gegensatz zu typischen Chat-Schnittstellen integriert sich die Gemini CLI in Ihre lokale Entwicklungsumgebung. Das bedeutet, dass sie den Projektkontext versteht, Dateien ändert, Builds oder Tests ausführt und Workflows direkt im Terminal automatisiert. Dies macht sie besonders nützlich für Entwickler, Site Reliability Engineers (SREs) und Engineers, die KI-gestütztes Codieren und Automatisierung wünschen, ohne ihren Befehlszeilen-Workflow zu verlassen.</p><p>Gemini CLI kann mit mehreren Paketmanagern installiert werden. Die gängigste Methode ist die Installation über npm:</p><pre><code>npm install -g @google/gemini-cli</code></pre><p>Wenn Sie sich über alternative Installationsmöglichkeiten informieren möchten, lesen Sie die <a href="https://geminicli.com/docs/get-started/installation/">offizielle Installationsseite</a>.</p><p>Starten Sie die CLI nach der Installation durch Ausführen des folgenden Befehls:</p><pre><code>gemini</code></pre><p>Sie sehen einen Bildschirm, wie in Abbildung 1 dargestellt:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6dfcec4a77b3dc83bf0d974417bf2e211abb1f4f-876x468.png" style="width: 100%;" /><h2>Elasticsearch konfigurieren</h2><p>Wir benötigen eine laufende Elasticsearch-Instanz. Wenn Sie den Model Context Protocol (MCP)-Server verwenden möchten, benötigen Sie zudem Kibana 9.3+. Für die Nutzung der unten beschriebenen Elasticsearch Query Language (ES|QL)-Fähigkeit (<code>esql</code>) ist Kibana nicht erforderlich.</p><p>Sie können eine kostenlose Testversion auf <a href="https://www.elastic.co/cloud">Elastic Cloud</a> aktivieren oder es lokal mit dem <a href="https://github.com/elastic/start-local"><code>start-local</code></a>-Skript installieren:</p><pre><code>curl -fsSL https://elastic.co/start-local | sh</code></pre><p>Dadurch werden Elasticsearch und Kibana auf Ihrem Computer installiert und ein API-Schlüssel generiert, den Sie für die Konfiguration von Gemini CLI verwenden können.</p><p>Der API-Schlüssel wird als Ausgabe des vorherigen Befehls angezeigt und in einer <strong>.env</strong>-Datei im Ordner <strong><code>elastic-start-local</code></strong> gespeichert.</p><p>Wenn Sie Elasticsearch lokal (zum Beispiel <code>start-local</code>), und Elastic Agent Builder mit MCP verwenden möchten, müssen Sie auch ein Large Language Model (LLM) verbinden. Lesen Sie <a href="https://www.elastic.co/docs/explore-analyze/ai-features/llm-guides/llm-connectors">diese Dokumentationsseite</a>, um sich über die verschiedenen Optionen zu informieren.</p><p>Wenn Sie Elastic Cloud (oder serverless) verwenden, verfügen Sie bereits über eine vorgefertigte LLM-Verbindung.</p><h2>Installieren Sie die Elasticsearch-Erweiterung</h2><p>Sie können die Elasticsearch-Erweiterung für Gemini CLI mit folgendem Befehl installieren:</p><pre><code>gemini extensions install https://github.com/elastic/gemini-cli-elasticsearch</code></pre><p>Sie können überprüfen, ob die Erweiterungen erfolgreich installiert wurden, indem Sie Gemini öffnen und den folgenden Befehl ausführen:</p><pre><code>/extensions list</code></pre><p>Die Elasticsearch-Erweiterung sollte verfügbar sein.</p><p>Wenn Sie die MCP-Integration verwenden möchten, müssen Sie die Elasticsearch-Version 9.3 oder höher installiert haben. Sie benötigen die URL Ihres MCP-Servers aus <a href="https://www.elastic.co/kibana">Kibana</a>:</p><ul><li>Sie erhalten Ihre MCP-Server-URL unter Agenten &gt; Alle Tools anzeigen &gt; MCP verwalten &gt; MCP-Server-URL kopieren.</li><li>Die URL wird so aussehen: https://your-kibana-instance/api/agent_builder/mcp</li></ul><p>Sie benötigen die URL des Elasticsearch-Endpoints. Dies wird üblicherweise oben auf der Kibana Elasticsearch-Seite angezeigt. Wenn Sie Elasticsearch mit <code>start-local</code> ausführen, ist der Endpoint bereits im Schlüssel <code>ES_LOCAL_URL</code> in der .env-Datei <code>start-local</code> hinterlegt.</p><p>Sie benötigen auch einen API-Schlüssel. Wenn Sie Elasticsearch mit <code>start-local</code> ausführen, ist der <code>ES_LOCAL_API_KEY</code> in der .env-Datei <code>start-local</code> hinterlegt. Andernfalls können Sie einen API-Schlüssel über die Kibana-Schnittstelle erstellen, wie <a href="https://www.elastic.co/docs/deploy-manage/api-keys/elasticsearch-api-keys">hier</a> beschrieben:</p><ul><li>In Kibana: Stack Management &gt; Sicherheit &gt; API-Schlüssel &gt; API-Schlüssel erstellen.</li><li>Wir empfehlen, nur die Leserechte für den API-Schlüssel festzulegen und so die <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/permissions#grant-access-with-roles">hier</a> beschriebene Berechtigung <code>feature_agentBuilder.read</code> zu aktivieren.</li><li>Kopieren Sie den codierten API-Schlüsselwert.</li></ul><p>Stellen Sie die erforderlichen Umgebungsvariablen in Ihrer Shell ein:</p><pre><code>export ELASTIC_URL="your-elasticsearch-url"
export ELASTIC_MCP_URL="your-elasticsearch-mcp-url"
export ELASTIC_API_KEY="your-encoded-api-key"</code></pre><h2>Installieren Sie den Beispieldatensatz</h2><p>Sie können den Datensatz für <strong>E-Commerce-Bestellungen </strong>, der von Kibana verfügbar ist, installieren. Sie enthält einen einzigen Index mit dem Namen <strong><code>kibana_sample_data_ecommerce</code></strong>, der Informationen für 4.675 Bestellungen von einer E-Commerce-Website enthält. Für jede Bestellung haben wir folgende Informationen:</p><ul><li>Kundeninformationen (Name, Ausweis, Geburtsdatum, E-Mail-Adresse und mehr)</li><li>Bestelldatum</li><li>Bestell-ID</li><li>Produkte (Liste aller Produkte mit Preis, Menge, ID, Kategorie, Rabatt und weiteren Details)</li><li>SKU</li><li>Gesamtpreis (ohne Steuern, mit Steuern)</li><li>Gesamtmenge</li><li>Geoinformationen (Stadt, Land, Kontinent, Ort, Region)</li></ul><p>Um die Beispieldaten zu installieren, öffnen Sie die Seite <strong>Integrationen</strong> in Kibana (suchen Sie in der Suchleiste oben nach „Integration&quot;) und installieren Sie die <strong>Beispieldaten</strong>. Weitere Einzelheiten finden Sie in der Dokumentation <a href="https://www.elastic.co/docs/explore-analyze/#gs-get-data-into-kibana">hier</a>.</p><p>Ziel dieses Artikels ist es, zu zeigen, wie einfach es ist, die Gemini CLI so zu konfigurieren, dass sie mit Elasticsearch verbunden ist und mit dem Index <strong><code>kibana_sample_data_ecommerce</code></strong> interagiert.</p><h2>Verwendung des Elasticsearch MCP</h2><p>Sie können die Verbindung mit folgendem Befehl in Gemini überprüfen:</p><pre><code>/mcp list</code></pre><p>Sie sollten sehen, dass <strong><code>elastic-agent-builder</code></strong> aktiviert ist, wie in Abbildung 2 dargestellt:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1508423bc1d1bc3c04a1cb01e2d59495a3516ed1-1465x844.png" style="width: 100%;" /><p>Elasticsearch bietet eine Reihe von Standardtools. Sehen Sie sich die Beschreibung <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/tools/builtin-tools-reference">hier</a> an.</p><p>Mithilfe dieser Tools können Sie mit Elasticsearch interagieren und Fragen stellen wie:</p><ul><li><code>Give me the list of all the indexes available in Elasticsearch.</code></li><li><code>How many customers are based in the USA in the kibana_sample_data_ecommerce index of Elasticsearch?</code></li></ul><p>Je nach Frage wird Gemini eines oder mehrere der verfügbaren Tools verwenden, um sie zu beantworten.</p><h2>Die /elastic-Befehle</h2><p>In der Elasticsearch-Erweiterung für Gemini CLI haben wir auch<strong><code>/elastic</code></strong>-Befehle<strong> </strong>hinzugefügt.</p><p>Wenn Sie den Befehl <strong><code>/help</code></strong> ausführen, werden Ihnen alle verfügbaren <code>/elastic</code>-Optionen angezeigt (Abbildung 3):</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5b2a0727ce7a04354878dd048253d3f4d062324b-1983x230.png" style="width: 100%;" /><p>Diese Befehle können nützlich sein, wenn Sie ein bestimmtes Tool des <code>elastic-agent-builder</code>-MCP-Servers direkt ausführen möchten. Beispielsweise können Sie mit dem folgenden Befehl das Mapping von <code>kibana_sample_data_ecommerce</code> abrufen:</p><pre><code>/elastic:get-mapping kibana_sample_data_ecommerce</code></pre><p>Diese Befehle sind im Wesentlichen Abkürzungen für das Ausführen spezifischer Tools, anstatt sich auf das Gemini-Modell zu verlassen, um zu bestimmen, welches Tool aufgerufen werden sollte.</p><h2>Verwendung der Elasticsearch-Fähigkeiten</h2><p>Diese Erweiterung beinhaltet außerdem eine <a href="https://github.com/elastic/gemini-cli-elasticsearch/tree/main/skills/esql">agentische Fähigkeit für ES|QL</a>, die in Elasticsearch verfügbare <a href="https://www.elastic.co/docs/explore-analyze/discover/try-esql">Elasticsearch Query Language</a>. <a href="https://agentskills.io/home">Agentische Fähigkeiten</a> stellen ein offenes Format dar, das KI-Coding-Agenten wie Gemini CLI individuelle Anweisungen für bestimmte Aufgaben gibt. Sie verwenden ein Konzept namens <em>Progressive Disclosure</em> (progressive Offenlegung), was bedeutet, dass der anfänglichen Systemaufforderung nur eine kurze Beschreibung der Fähigkeit hinzugefügt wird. Wenn Sie den Agenten bitten, eine Aufgabe auszuführen, wie etwa eine Abfrage an Elasticsearch, passt er die Anfrage an die relevante Fähigkeit an und lädt dynamisch die detaillierten Anweisungen. Dies ist eine effiziente Methode, um Token-Budgets zu verwalten und der KI genau den Kontext bereitzustellen, den sie benötigt.</p><p>Die <strong><code>esql</code></strong>-<strong>Fähigkeit</strong> ist so konzipiert, dass Gemini CLI ES|QL-Abfragen direkt in Ihrem Cluster schreiben und ausführen kann. ES|QL ist eine leistungsstarke Abfragesprache, die die Datenexploration, die Log-Analyse und die Aggregationen sehr intuitiv macht. Wenn diese Fähigkeit aktiviert ist, müssen Sie nicht nach der ES|QL-Syntax suchen; Sie können der Gemini CLI einfach Fragen zu Ihren Daten in natürlicher Sprache stellen, und der Agent kümmert sich um den Rest.</p><p>Die Ausführung erfolgt mit einfachen <a href="https://curl.se/">curl</a>-Befehlen, die in einem Terminal ausgeführt werden. Dies ist möglich, da Elasticsearch eine umfangreiche Sammlung von REST APIs bereitstellt, die sich problemlos in jede beliebige Architektur integrieren lassen.</p><p><strong>Was die <code>esql</code> Fähigkeit bietet:</strong></p><ul><li><strong>Erkennung von Indizes und Schemata:</strong> Der Agent kann die integrierten Tools der Fähigkeit nutzen, um verfügbare Indizes aufzulisten und Feld-Mappings abzurufen. Bevor der Agent beispielsweise eine Abfrage für die E-Commerce-Datensätze schreibt, kann er eine Schema-Prüfung für <strong><code>kibana_sample_data_ecommerce</code></strong> ausführen, um die verfügbaren Felder wie <strong><code>taxful_total_price</code></strong> oder <strong><code>category</code></strong> zu ermitteln.</li><li><strong>Nahtlose Übersetzung natürlicher Sprache:</strong> Diese Fähigkeit bietet dem Agenten mehr als nur ein einfaches Nachschlagewerk; sie liefert eine konkrete Anleitung zur Interpretation der Nutzerabsicht. Wenn Sie Anfragen in natürlicher Sprache eingeben, wie „Zeige durchschnittliche Reaktionszeit gruppiert nach Service“, nutzt der Agent die in der Fähigkeit integrierte Mustererkennung, um Ihre Worte sofort in die richtigen ES|QL-Aggregationen, Filter und Befehle umzuwandeln.</li><li><strong>Selbstkorrektur:</strong> Wenn eine Abfrage fehlschlägt (zum Beispiel wegen eines Typfehlers oder eines Syntaxfehlers), gibt die Fähigkeit die generierte Abfrage zusammen mit der genauen Elasticsearch-Fehlermeldung zurück, sodass der Agent die Abfrage sofort beheben und erneut ausführen kann, ohne dass Sie eingreifen müssen.</li></ul><p>Da die <code>esql</code>-Fähigkeit auch als Tool auf dem <code>elastic-agent-builder</code>-MCP-Server verfügbar ist, müssen wir diesen Server vorübergehend deaktivieren. Sie können sie mit dem folgendem Befehl deaktivieren:</p><pre><code>/mcp disable elastic-agent-builder</code></pre><p>Dann können Sie einfach einen Befehl wie diesen in Ihre Gemini CLI eingeben:</p><pre><code>Find the top 5 product categories by total sales revenue in the kibana_sample_data_ecommerce index</code></pre><p>Der Agent wird:</p><ul><li>Die Notwendigkeit der <code>esql</code>-Fähigkeit erkennen</li><li>Überprüfen Sie das Schema von <strong><code>kibana_sample_data_ecommerce</code></strong>.</li><li>Eine ES|QL-Abfrage erstellen wie: <code>FROM kibana_sample_data_ecommerce | STATS total_revenue = SUM(taxful_total_price) BY category.keyword | SORT total_revenue DESC | LIMIT 5</code></li><li>Die Abfrage über die Elasticsearch-API ausführen</li><li>Das Ergebnis Ihnen direkt im Terminal anzeigen</li></ul><p>Hier haben wir ein Beispiel für die Antwort von Gemini auf die vorherige Eingabe aufgeführt:</p><pre><code>───────────────────────────────────────────────────────────
> Find the top 5 product categories by total sales revenue in the kibana_sample_data_ecommerce index   ───────────────────────────────────────────────────────────
✓  Activate Skill "esql": Interact with Elasticsearch using ES|QL and curl. Use when querying, managing indices,  …
Skill esql activated. Resources loaded from /home/user/.gemini/extensions/elasticsearch/skills/esql: 
Showing up to 200 items (files + folders).                                                                     /home/user/.gemini/extensions/elasticsearch/skills/esql/                                                            ├───SKILL.md 
└───references/
├───esql-reference.md 
├───generation-tips.md 
└───query-patterns.md

✦ I will start by exploring the mapping of the kibana_sample_data_ecommerce index to identify the fields for product category and sales revenue. Then, I will construct and execute an ES|QL query to determine the top 5 product categories by total sales revenue.

✓  Shell curl -s "${ELASTIC_URL%/}/kibana_sample_data_ecommerce/_mapping" -H "Authorization: ApiKey $(printenv ELAS…                                                                                                                       {  
   "kibana_sample_data_ecommerce": { 
     "mappings": { 
       "properties": {
         "category": { 
           "type": "text", 
           "fields": {
             "keyword": {
               "type": "keyword"
             }
           }
         }, 
         "currency": {
           "type": "keyword"
         }, 

✦ Okay, I have the mapping. The category is products.category.keyword and sales revenue is taxful_total_price. I'll now execute the ES|QL query to get the top 5 categories by revenue.

✓  Shell curl -s -X POST "${ELASTIC_URL%/}/_query" \  -H "Authorization: ApiKey $(printenv ELASTIC_API_KEY)" \  -H …
{ 
   "took": 62,
   "is_partial": false, 
   "completion_time_in_millis": 1772805644164,
   "documents_found": 4675,
   "values_loaded": 12084, 
   "start_time_in_millis": 1772805644102, 
   "expiration_time_in_millis": 1773237644087,  
   "columns": [ 
     { 
       "name": "total_revenue",    
       "type": "double"  
     }, 
     {   
       "name": "products.category.keyword",

✦ The query for top 5 product categories by revenue was successful. I'll format the returned JSON values into a   user-friendly list.

 ✓  Shell 
                             
 Top 5 product categories by total sales revenue: 
 1. Men's Clothing: 149393.91
 2. Women's Clothing: 135099.91
 3. Women's Shoes: 105479.17
 4. Men's Shoes: 91797.92
 5. Women's Accessories: 60830.31 

✦ I've found the answer; now I'll format it for clarity.

 ✓  Shell 

 Here are the top 5 product categories by total sales revenue:                                                        
                                                                      
 | Rank | Category              | Total Revenue | 
 |------|-----------------------|---------------| 
 | 1    | Men's Clothing        | 149,393.91    |
 | 2    | Women's Clothing      | 135,099.91    |
 | 3    | Women's Shoes         | 105,479.17    |
 | 4    | Men's Shoes           | 91,797.92     | 
 | 5    | Women's Accessories   | 60,830.31     | 

✦ I've formatted and presented the answer. I'm finished.
 ───────────────────────────────────────────────────────────</code></pre><p>Es ist interessant festzustellen, wie das Gemini-Modell die endgültige Antwort generiert, indem es alle Schritte zeigt, die es dabei durchläuft. Hier können Sie deutlich den Einfluss der Fähigkeit auf den Denkprozess des Modells erkennen. Wenn das Modell zum ersten Mal erkennt, dass es eine Fähigkeit verwenden oder einen Shell-Befehl ausführen muss, erfragt es die Genehmigung mithilfe des Human-in-the-Loop-Ansatzes.</p><p>Durch die Übernahme der aufwendigen Aufgaben der Schemaerkennung, Abfragegenerierung und -ausführung ermöglicht Ihnen die <code>esql</code>-Fähigkeit, sich voll und ganz auf die Antworten zu konzentrieren, anstatt auf die Mechanismen ihrer Ermittlung. Sie erhalten die Daten, die Sie benötigen, richtig formatiert und direkt in Ihrem Terminal, ohne jemals eine einzige Zeile Syntax zu schreiben oder zu einer anderen Anwendung zu wechseln.</p><h2>Fazit</h2><p>In diesem Artikel haben wir die kürzlich veröffentlichte Elasticsearch-Erweiterung für die Gemini CLI vorgestellt. Mit dieser Erweiterung können Sie über Gemini und den von Elastic Agent Builder bereitgestellten Elasticsearch-MCP-Server, der ab Version 9.3.0 verfügbar ist, sowie über den Befehl <code>/elastic</code> mit Ihrer Elasticsearch-Instanz interagieren.</p><p>Darüber hinaus enthält die Erweiterung auch eine <code>esql</code>-Fähigkeit, die die Anfrage eines Nutzers von der natürlichen Sprache in eine ES|QL-Abfrage umwandelt. Diese Fähigkeit kann besonders nützlich sein, wenn der MCP-Server nicht genutzt werden kann, da die zugrundeliegende Kommunikation durch einfache Curl-Befehle in einem Terminal gesteuert wird. Elasticsearch bietet eine umfangreiche Auswahl an REST APIs, die leicht in jedes Projekt integriert werden können. Dies ist besonders nützlich bei der Entwicklung von agentischen KI-Anwendungen.</p><p>Weitere Informationen zu unserer Gemini CLI-Erweiterung finden Sie <a href="https://github.com/elastic/gemini-cli-elasticsearch">hier</a> im Projekt-Repository.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Agent Skills für Elastic: Verwandeln Sie Ihren KI-Agenten in einen Elastic-Experten]]></title>
            <description><![CDATA[Geben Sie Ihrem KI-Coding-Agenten mit Elastic Agent Skills das Wissen zum Abfragen, Visualisieren, Absichern und Automatisieren.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/agent-skills-elastic</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/agent-skills-elastic</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Graham Hudgins, Matt Ryan]]></dc:creator>
            <pubDate>Mon, 16 Mar 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/09e64953819083168a9ecef0888c7f8bde1a43bd-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Jeder Entwickler, Site Reliability Engineer (SRE) oder Analyst, der schon einmal versucht hat, einen KI-Codierungsagenten mit einer spezialisierten Plattform zu verwenden, stand vor derselben Wand. Sie bitten den Agenten, eine Abfrage zu schreiben, einen Alarm zu konfigurieren oder etwas zu untersuchen, und er kommt nah dran, liegt aber nicht ganz richtig. Elastic hat hier einen Vorteil: Mehr als ein Jahrzehnt an Dokumentation, Blogbeiträgen und Antworten aus der Community bedeutet, dass KI-Agenten Elastic bereits besser kennen als die meisten Datenplattformen. Doch diese Tiefe bringt auch Rauschen mit sich. Veraltete APIs arbeiten neben aktuellen. Überholte Muster werden genauso hoch bewertet wie Best Practices. Der Agent reproduziert zuversichtlich einen Ansatz, der drei Versionen vorher funktioniert hat, weil er dies in seinen Trainingsdaten tat. Das Ergebnis ist eine Art Korrektursteuer: Nutzer speisen manuell Dokumentation in den Kontext ein, korrigieren halluzinierte Syntax und arbeiten um den Agenten herum, statt mit ihm zusammen. Schlimmer noch: Neue, bessere Funktionen bleiben vollständig ungenutzt, nicht weil die Nutzer sie nicht benötigen, sondern weil der Agent nicht weiß, dass sie existieren.</p><p>Deshalb stellen wir <a href="https://github.com/elastic/agent-skills">Elastic Agent Skills</a> als Open Source zur Verfügung: native Plattform-Expertise für Elasticsearch, Kibana, Elastic Observability und Elastic Security. Fügen Sie sie in die Agenten-Runtime ein, die Sie bereits verwenden, und machen Sie Ihren Agenten aus einem „Generalisten“, der viel Syntax erraten kann, zu einem Experten, der in der Lage ist, viele der Architekturstandards genauso wie die Entwicklungsteams von Elastic selbst zu verwenden. Diese erste technische Vorschauversion konzentriert sich auf Fähigkeiten mit maximaler Kompatibilität für <a href="https://www.elastic.co/cloud/serverless">Elastic Cloud Serverless</a>, wird sich aber schnell weiterentwickeln, um auch eine verbesserte Unterstützung für ältere Stack-Versionen zu bieten.</p><p>Darüber hinaus löst Elastic dieses Problem von beiden Seiten. Für Agenten auf der Elastic-Plattform ermöglicht der <a href="https://www.elastic.co/search-labs/blog/agent-builder-elastic-ga">Elastic Agent Builder</a> (jetzt allgemein verfügbar) das Erstellen und Chatten mit KI-Agenten, die die Zugriffskontrollen Ihrer Daten erben, integrierte Such- und Analysetools verwenden und kontextbezogen neben Ihren Dashboards, Alerts und Untersuchungen arbeiten. Wir arbeiten intensiv daran, herausragende agentische Erlebnisse auf der Elastic-Plattform zu gewährleisten. Aber nicht jeder Agent ist in Elastic integriert. Ihr Team verwendet bereits Cursor, Claude Code oder andere Laufzeitumgebungen, und diese Agenten müssen Elastic ebenfalls richtig einsetzen. An dieser Stelle kommen Agent Skills ins Spiel.</p><h2>Warum Agenten mit spezialisierten Plattformen Schwierigkeiten haben</h2><p>Große Sprachmodelle (LLMs) sind bemerkenswert leistungsfähige Generalisten. Sie können Python schreiben, Kubernetes-Manifeste erklären und React-Komponenten refaktorisieren, weil ihre Trainingsdaten reich an Beispielen sind. Aber wenn es um plattformspezifische Arbeit geht, die proprietäre Abfragesprachen, tiefe API-Oberflächen und domänenspezifische Best Practices beinhaltet, stoßen sie auf vorhersehbare Weise an ihre Grenzen.</p><p>Bei Elasticsearch zeigt sich die Lücke ganz konkret:</p><ul><li><strong>Die Elasticsearch-Abfragesprache (ES|QL) ist Neuland.</strong> LLMs werden intensiv in SQL geschult, aber ES|QL ist eine Pipe-Abfragesprache mit anderer Syntax, anderen Funktionen und anderer Semantik. Agenten schreiben oft Abfragen, die plausibel aussehen, aber nicht parsen. Sie verwechseln <code>WHERE</code> mit <code>| WHERE</code>, erfinden Funktionen, die es nicht gibt, und übersehen das Pipe-basierte Kompositionsmodell völlig.</li><li><strong>API-Oberflächen sind breit und tief.</strong> Elasticsearch, Kibana und Elastic Security stellen Hunderte von APIs für Suche, Ingestion, Alerting, Erkennungsregeln, Ticketmanagement, Dashboard und mehr zur Verfügung. Ein Agent, der nur mit allgemeinen Trainingsdaten ausgestattet ist, muss erraten, welchen Endpoint er aufrufen soll, wie der Anfragetext aussieht und wie er mit der Reaktion umgehen soll. Er rät oft genug falsch und untergräbt damit das Vertrauen.</li><li><strong>Best Practices liegen nicht in den Trainingsdaten.</strong> Wann sollten Sie <code>semantic_text</code> im Vergleich zu einer benutzerdefinierten Einbettungs-Pipeline verwenden? Wie sollte man eine Ingest-Pipeline für eine 10-GB-CSV strukturieren? Was ist die richtige Erkennungsregelsyntax für eine <a href="https://www.elastic.co/docs/solutions/security/detect-and-alert/mitre-attandckr-coverage">MITRE ATT&amp;CK-Technik</a>? Allgemeine Agenten haben standardmäßig kein kuratiertes, zuverlässig strukturiertes Elastic-spezifisches Wissen geladen. Sie müssten es erst einmal finden, und selbst wenn sie es fänden, spiegeln Rohdokumente nicht immer die Beurteilungen und Best Practices wider, die erfahrene Fachleute anwenden.</li></ul><p>Das Ergebnis: Entwickler verbringen mehr Zeit damit, die Ausgabe des Agenten zu korrigieren, als sie für das Schreiben des Codes selbst benötigt hätten. Das ist nicht das, was man haben wollte.</p><h2>Agent Skills: Plattformwissen, speziell für Agenten aufbereitet</h2><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8d16ec00d16e70a916c5eef0aaa23fcc735b7186-1067x1280.png" style="width: 100%;" /><p>Agent Skills sind selbstständige Verzeichnisse mit Anweisungen, Skripten und Referenzmaterial, die in Agentenlaufzeiten dynamisch geladen werden können. Wenn ein Skill, also eine Fähigkeit, aktiv ist, hat der Agent zum richtigen Zeitpunkt Zugriff auf den richtigen Kontext: Abfragesyntax, API-Muster, Validierungslogik, ausgearbeitete Beispiele, sodass er Aufgaben schon beim ersten Versuch korrekt ausführen kann.</p><p>Jede Fähigkeit folgt der offenen <a href="https://agentskills.io">agentskills.io</a>-Spezifikation: einem Ordner mit einer <code>SKILL.md</code>-Datei, die Metadaten und strukturierte Anweisungen enthält. Kein proprietäres Format, keine Anbieterabhängigkeit. Fähigkeiten funktionieren über Agentenlaufzeiten hinweg, darunter Cursor, Claude Code, GitHub Copilot, Windsurf, Gemini CLI, Cline, Codex und <a href="https://agentskills.io">viele mehr</a>.</p><h3>Was enthält die erste Version 0.1.0?</h3><p>Die erste Gruppe von Fähigkeiten umfasst fünf Bereiche des Elastic Stack:</p><ul><li>Interaktion mit den Elasticsearch-APIs (Suche, Indizierung, Clusterverwaltung)</li><li>Erstellung und Verwaltung von Kibana-Inhalten wie Dashboards, Warnmeldungen, Konnektoren und mehr</li><li>Domänenexpertise für Elastic Observability</li><li>Domänenexpertise für Elastic Security</li><li>Erstellung effektiver Agenten in Agent Builder</li></ul><h3>Skills sind kombinierbar</h3><p>Skills sind nicht monolithisch. Sie sind von Natur aus modular. Ihr Agent lädt nur die Fähigkeiten, die für die jeweilige Aufgabe relevant sind. Arbeiten Sie an einer ES|QL-Abfrage? Die ES|QL-Fähigkeit wird aktiviert. Müssen Sie aus diesen Ergebnissen ein Dashboard erstellen? Die Dashboard-Fähigkeit wird aktiviert. Müssen Sie den Zustand Ihrer Anwendung beurteilen? Da kommt die Dienstzustandsfähigkeit ins Spiel. Sie untersuchen eine Sicherheitswarnung? Die Triage-Fähigkeiten werden im Verlauf der Untersuchung in Ticketmanagement- und Reaktionsfähigkeiten umgewandelt.</p><p>Diese Komponierbarkeit bedeutet, dass Sie keinen einzigen, massiven Prompt benötigen, der versucht, alles abzudecken. Jede Fähigkeit trägt genau den Kontext, den ihr Bereich erfordert, nichts mehr, nichts weniger.</p><h2>Für Entwickler, die Such- und KI-Anwendungen entwickeln</h2><p>Wenn Sie Daten in Elasticsearch laden, Abfragen schreiben oder Indizes migrieren, reduzieren Skills den Kreislauf aus Codegenerierung, Fehlersuche und der Suche nach der Ursache in der Dokumentation.</p><p>Bitten Sie Ihren Agenten, eine CSV-Datei zu laden und er verwendet ein Streaming-Ingestion-Tool, das Backpressure handhabt und aus den Daten Zuordnungen ableitet. Es handelt sich dabei nicht um eine handgesteuerte _bulk-Schleife, die beim Ausführen der ersten großen Datei den Speicher erschöpft. Bitten Sie ihn um eine Abfrage mit ES|QL und er entdeckt Ihre tatsächlichen Indexnamen und Feldschemata und schreibt dann gültige Pipe-Abfragen mit korrekter Syntax, geeigneten Aggregationen und versionsabhängiger Feature-Auswahl, nicht eine SQL-basierte Vermutung, die drei Runden Debugging erfordert. Fordern Sie ihn auf, die Neuindizierung Cluster-übergreifend durchzuführen und er wird dem gesamten operativen Workflow folgen: Er erstellt das Ziel mit expliziten Zuordnungen, optimiert die Einstellungen für den Durchsatz, führt den Job asynchron aus und stellt nach Abschluss die Produktionseinstellungen wieder her – das ist nicht nur ein einfacher _reindex-Aufruf, der die Hälfte der Schritte überspringt, die ein erfahrener Operator befolgen würde.</p><p>Statt eines Agenten, der Ihnen einen plausiblen Ausgangspunkt liefert, den Sie dann korrigieren müssen, erhalten Sie einen, der die operative Disziplin kodiert, die dafür sorgt, dass das Ergebnis tatsächlich funktioniert.</p><p><strong>Beispiele für die Wirkung von Elastic Agent Skills</strong></p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Eval</td><td style="border: 1px solid #ccc; padding: 4px;">Was durch die Fähigkeit verändert wurde</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">es-audit-query-failed-logins</td><td style="border: 1px solid #ccc; padding: 4px;">Anstelle einer generischen Suche wurden die Abfragemuster für das Auditprotokoll aus dem Skill verwendet</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">es-authz-role-mapping-ldap</td><td style="border: 1px solid #ccc; padding: 4px;">Die korrekte API-Aufrufstruktur für das Rollen-Mapping wurde ausgegeben</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">esql-basic-query</td><td style="border: 1px solid #ccc; padding: 4px;">Eine ES|QL Pipe-Syntax über Abfrage-DSL wurde geschrieben</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">esql-error-handling</td><td style="border: 1px solid #ccc; padding: 4px;">Betrachtet zuerst das Schema, statt Feldnamen zu erraten</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">esql-schema-discovery</td><td style="border: 1px solid #ccc; padding: 4px;">Indexnamen wurden nie erraten</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">es-ingest-csv-with-infer</td><td style="border: 1px solid #ccc; padding: 4px;">Nur --infer-mappings verwendet, Kombination mit --source-format-CSV vermieden, was zu einem leeren Index führt</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">es-ingest-json-file</td><td style="border: 1px solid #ccc; padding: 4px;">Verwendete eine robuste Ingestionsmethode, die auch große Dateien verarbeiten kann</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">es-reindex-local-async</td><td style="border: 1px solid #ccc; padding: 4px;">Zuerst wurde der Zielindex mit replicas: 0 und refresh_interval: "-1" erstellt, anschließend erfolgte eine asynchrone Neuindizierung. Baseline verzichtete auf jegliche Vorbereitung</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">es-security-403-privileges</td><td style="border: 1px solid #ccc; padding: 4px;">Befolgte den diagnostischen Workflow der Fähigkeit für Berechtigungsfehler anstelle allgemeiner Ratschläge</td>
                    </tr>
              </tbody>
            </table>
          <h2>Für Sicherheitsteams</h2><p>Security-Teams wiederholen täglich die gleichen operativen Workflows: Priorisierung von Warnmeldungen, Anpassung von Erkennungsregeln und Verwaltung von Tickets. Agent Skills kodieren dieses prozedurale Wissen, damit Ihr KI-Agent die Workflows korrekt ausführen kann, indem er die richtigen APIs in der richtigen Reihenfolge mit den richtigen Feldnamen aufruft. Eine praktische Anleitung, die Sie von null zu einer vollständig eingerichteten Elastic Security-Umgebung führt, ohne Ihre IDE zu verlassen, finden Sie unter <a href="https://www.elastic.co/security-labs/agent-skills-elastic-security">Erste Schritte mit Elastic Security von Ihrem KI-Agenten aus</a>.</p><h2>Für Observability- und operative Teams</h2><p>Die neuen Agent Skills für Elastic Observability reduzieren den betrieblichen Aufwand bei der Instrumentierung komplexer Systeme, der Verwaltung von SLOs, der Sichtung komplexer Daten und der Bewertung des Dienstzustands. Die direkte Einbettung nativer Elastic-Expertise in KI-Agenten ermöglicht es Teams, komplexe Beobachtbarkeits-Workflows mit einfacher natürlicher Sprache auszuführen. Dadurch können SREs und Ops-Teams Vorfälle schneller beheben und zuverlässige Systeme einfacher pflegen. Weitere Informationen finden Sie in <a href="https://www.elastic.co/observability-labs/blog/elastic-agent-skills-observability-workflows">diesem Blog</a>.</p><h2>Open Source, offene Spezifikationen, Community-gesteuert</h2><p>Wir veröffentlichen Agent Skills unter der Apache-2.0-Lizenz, weil wir glauben, dass Agentenwissen offen sein sollte. Die <a href="https://agentskills.io">agentskills.io</a>-Spezifikation, der die Skills folgen, ist ein offener Standard, kein proprietäres Elastic-Format. Wir möchten, dass Fähigkeiten eine Gemeinschaftsleistung sind, kein geschlossener Bereich.</p><h2>Teil eines größeren Ganzen</h2><p>Agent Skills ist Teil einer umfassenderen Initiative, Elasticsearch zur agentenfreundlichsten Datenplattform zu machen. Für Agenten, die in die Elasticsearch-Plattform integriert sind, geht <a href="https://www.elastic.co/search-labs/blog/agent-builder-elastic-ga">Agent Builder</a> noch einen Schritt weiter, indem es die Zugriffskontrollen und Berechtigungen Ihrer Daten übernimmt, integriert und individuelle Tools für Suche und Analyse bereitstellt und Nutzern erlaubt, mit Agenten im Kontext und ihren Dashboards, Warnungen und Untersuchungen zu interagieren. Schließlich wird in Kürze auch die Unterstützung für Skills in Agent Builder verfügbar sein und Entwicklern die Flexibilität geben, Elastic Agent Skills sowie Skills aus beliebigen anderen Quellen zu nutzen, um sichere, kontextbezogene Chats und Automatisierungen auf der Elasticsearch-Plattform zu ermöglichen.</p><p>Für Agenten an allen anderen Orten investieren wir in das offene Ökosystem:</p><ul><li><strong>Erweiterung des Model Context Protocol (MCP)-Servers:</strong> Die Erweiterung des <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/mcp-server">MCP Endpoints</a> in Agent Builder um weitere Tools über die aktuelle Suche, ES|QL und Indexoperationen hinaus.</li><li><strong>Verbesserungen bei der Authentifizierung:</strong> Dies macht es für Agenten einfacher, eine sichere Verbindung herzustellen, mit dem Ziel, manuelles Kopieren und Einfügen von API-Schlüsseln zu eliminieren.</li><li><strong>LLM-lesbare Dokumentation:</strong> Veröffentlichung der Dateien <code>llms.txt</code> und <code>AGENTS.md</code>, damit Agenten Elastic-APIs selbstständig finden und verstehen können.</li><li><strong>Eine Befehlszeilenschnittstelle (CLI) für Agenten-Workflows:</strong> Ein Befehlszeilentool, das die Verbindungsverwaltung und gängige Operationen agentenfreundlich gestaltet.</li></ul><p>Skills sind die Ebene, die Sie heute nutzen können. Der Rest folgt.</p><h2>Erste Schritte</h2><p><strong>Bevor Sie beginnen: </strong>KI-Programmieragenten arbeiten mit echten Anmeldeinformationen, echtem Shell-Zugriff und oft mit den vollen Berechtigungen des Nutzers, der sie ausführt. Wenn diese Agenten auf Sicherheitsworkflows ausgerichtet werden, steigen die Risiken: Sie geben einem automatisierten System Zugriff auf Erkennungslogik, Reaktionsmaßnahmen und sensible Telemetrie. Das Risikoprofil jeder Organisation ist unterschiedlich. Bevor Sie KI-gesteuerte Sicherheitsworkflows aktivieren, sollten Sie prüfen, <strong>auf welche Daten der Agent zugreifen kann, welche Maßnahmen er ausführen kann und was passiert, wenn er sich nicht so verhält, wie erwartet</strong>.</p><p>Installieren Sie Elastic Agent Skills in Ihrer Agenten-Runtime:</p><p><code>npx skills add elastic/agent-skills</code></p><p>Dadurch werden Ihre installierten Agenten-Runtimes automatisch erkannt und die Skills im richtigen Konfigurationsverzeichnis abgelegt. Von dort aus nimmt Ihr Agent sie automatisch auf.</p><p>Sie können den <a href="https://github.com/elastic/agent-skills">Fähigkeitenkatalog</a> auch direkt durchsuchen und einzelne Fähigkeiten manuell installieren, indem Sie den Fähigkeitenordner in das Konfigurationsverzeichnis Ihres Agenten kopieren.</p><p>Sie haben noch keinen Elasticsearch-Cluster? Starten Sie eine <a href="https://cloud.elastic.co/registration">kostenlose Elastic Cloud-Testversion</a>. Es dauert etwa eine Minute, bis eine vollständig konfigurierte Umgebung eingerichtet ist.</p><p><strong>Projekt erkunden:</strong></p><ul><li><a href="https://github.com/elastic/agent-skills">Agent Skills-Repository</a></li><li><a href="https://agentskills.io">agentskills.io Spezifikation</a></li><li><a href="https://www.elastic.co/docs">Elasticsearch-Dokumentation</a></li><li><a href="https://cloud.elastic.co/registration">Elastic Cloud kostenlos testen</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Entitätsauflösung mit Elasticsearch, Teil 4: Die ultimative Herausforderung]]></title>
            <description><![CDATA[Lösung und Bewertung von Herausforderungen bei der Entitätsauflösung in einem äußerst vielfältigen Datensatz zur „ultimativen Herausforderung“, der entwickelt wurde, um Abkürzungen zu verhindern.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/entity-resolution-elasticsearch-llm-challenges</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/entity-resolution-elasticsearch-llm-challenges</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jessica Moszkowicz]]></dc:creator>
            <pubDate>Fri, 13 Mar 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/70fb0ff949f6db9ac9b8a28ecb4329ab915ebf46-720x420.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Wir haben nun die Implementierung intelligenter Entitätsauflösung auf zwei Arten gesehen. Beide Ansätze beginnen auf dieselbe Weise: Aufbereitung und Extraktion der Entitäten, gefolgt vom Abruf der Kandidaten mit Elasticsearch. Anschließend bewerten wir diese Kandidaten mithilfe eines großen Sprachmodells (LLM), entweder durch promptbasierte JSON-Generierung oder durch Funktionsaufrufe, und fordern vom Modell eine transparente Begründung für seine Entscheidung.</p><p>Wie wir im <a href="https://www.elastic.co/search-labs/blog/elasticsearch-entity-resolution-llm-function-calling">vorherigen Beitrag</a> gesehen haben, ist die Konstanz, die durch Funktionsaufrufe ermöglicht wird, nicht nur eine praktische Optimierung, sondern essenziell. Nachdem wir strukturelle Fehler aus dem Evaluationskreislauf entfernt hatten, verbesserten sich die Ergebnisse in Standardszenarien (wie denen im Tier-4-Datensatz) dramatisch.</p><p>Doch eine offensichtliche Frage bleibt noch zu beantworten:</p><p><em>Funktioniert dieser Ansatz noch, wenn es kompliziert wird?</em></p><p>Die Entitätsauflösung in der realen Welt schlägt selten in einfachen Fällen fehl. Sie scheitert, wenn Namen Sprach-, Kultur-, Schrift-, Zeit- und Unternehmensgrenzen überschreiten. Sie scheitert, wenn auf Menschen mit Titeln anstelle von Namen Bezug genommen wird, wenn Unternehmen Namen ändern, wenn Transliterationen nicht konstant sind und wenn Kontext (nicht die Schreibweise) das Einzige ist, was eine Erwähnung mit einer realen Entität verbindet.</p><p>Für den letzten Beitrag dieser Serie haben wir das System einer sogenannten <strong>ultimativen Herausforderung</strong> unterzogen.</p><h2>Was macht dies zur ultimativen Herausforderung?</h2><p>In früheren Auswertungen haben wir das System mit zunehmend komplexeren Datensätzen getestet. Als wir die im vorherigen Beitrag besprochene Stufe 4 erreichten, hatten wir es bereits mit einer Mischung aus Spitznamen, Titeln, mehrsprachigen Namen und semantischen Bezügen zu tun. Diese Tests zeigten, dass die Architektur selbst solide war, aber dass Zuverlässigkeitsprobleme, insbesondere fehlerhaftes JSON, den Rückruf unterdrückten.</p><p>Mit dem implementierten Funktionsaufruf hatten wir endlich eine stabile Grundlage. So konnten wir eine interessantere Frage stellen:</p><p><em>Kann eine einheitliche Pipeline <strong>viele verschiedene Arten</strong> von Entitätsauflösungsproblemen gleichzeitig bewältigen?</em></p><p>Der ultimative herausfordernde Datensatz wurde darauf ausgelegt, genau diese Dimension zu erreichen.</p><p>Anstatt sich auf eine einzelne Schwierigkeit (wie Spitznamen oder Transliteration) zu konzentrieren, kombiniert dieser Datensatz <strong>mehr als 50 verschiedene Herausforderungstypen</strong>, darunter:</p><ul><li>Kulturelle Namenskonventionen.</li><li>Titelbasierte Referenzen.</li><li>Geschäftliche Beziehungen und historische Namensänderungen.</li><li>Mehrsprachige und skriptübergreifende Erwähnungen.</li><li>Zusammengesetzte Herausforderungen, die mehrere der oben genannten Punkte kombinieren.</li></ul><p>Entscheidend ist, dass es hier nicht um die Optimierung für einen einzigen, eng begrenzten Anwendungsfall geht. Es geht darum zu testen, ob das <em>Entwurfsmuster</em> Bestand hat, wenn sich die Regeln von Entität zu Entität ändern.</p><h2>Der Datensatz auf einen Blick</h2><p>Der ultimativ herausfordernde Datensatz besteht aus:</p><ul><li><strong>50 Entitäten</strong>, darunter Personen, Unternehmen und Institutionen.</li><li><strong>~60 Artikel</strong>, mit unterschiedlicher Struktur und sprachlicher Komplexität.</li><li><strong>51 verschiedene Herausforderungskategorien</strong>, grob unterteilt in:<ul><li>Kulturelle Namenskonventionen.</li><li>Titel und beruflichem Kontext.</li><li>Geschäfts- und Unternehmensbeziehungen.</li><li>Mehrsprachigkeit und Transliterationsherausforderungen.</li><li>Kombinierten und Grenzfall-Szenarien.</li></ul></li></ul><p>Zu Beginn der Serie haben wir gesehen, dass die Verwendung von generativer KI (GenKI) zur Erstellung von Datensätzen ein zweischneidiges Schwert sein kann. Ohne sie wäre es äußerst schwierig, ausreichend große und vielfältige Testdaten zusammenzustellen. Aber wenn das Modell nicht kontrolliert wird, neigt es dazu, die Dinge zu einfach zu machen.</p><p>Bei einer frühen Generationsüberprüfung stellten wir beispielsweise fest, dass das Modell Formulierungen wie „der russische Präsident“ als expliziten Aliasnamen für Wladimir Putin enthielt. Das mag heute vernünftig erscheinen, aber es widerspricht dem Zweck der Prüfung der Kontextauflösung. Was passiert, wenn der Artikel Russland in den 1990er Jahren behandelt? Das System sollte die richtige Entität aus dem Kontext ableiten und sich nicht auf einen fest codierten Alias verlassen.</p><p>Aus diesem Grund wurde dieser Datensatz bewusst so konzipiert, dass <strong>Abkürzungen nicht funktionieren</strong>. Pseudonyme werden nicht explizit aufgeführt, wenn das System die Bedeutung erschließen soll. Beschreibende Phrasen sind nicht vorab mit Entitäten verknüpft. Korrekte Treffer hängen oft vom Kontext auf Artikelebene ab, nicht nur vom lokalen Text.</p><p><strong>Wichtiger Hinweis:</strong> Obwohl wir die Fähigkeiten des Systems in verschiedenen Szenarien demonstrieren, ist dies dennoch ein Bildungsprototyp. Produktionssysteme, die die Überwachung von sanktionierten Entitäten in der realen Welt handhaben, würden zusätzliche Validierung, Compliance-Prüfungen, Audit-Trails und eine spezialisierte Handhabung für sensible Anwendungsfälle erfordern.</p><h2>Warum diese Szenarien schwierig sind</h2><p>Im ersten Beitrag dieser Reihe haben wir ein einfaches, aber mehrdeutiges Beispiel vorgestellt: „Das neue Swift-Update ist da!“ Die Herausforderung besteht darin, dass „Swift“ je nach Kontext auf mehrere reale Entitäten aufgelöst werden kann. Dieses Beispiel verdeutlicht eine grundlegendere Wahrheit: Natürliche Sprache ist von Natur aus mehrdeutig.</p><p>Die Entitätsauflösung ist daher nicht nur ein Problem des Zeichenfolgenabgleichs. Menschen verlassen sich routinemäßig auf gemeinsames Wissen, kulturelle Normen und situativen Kontext, um Referenzen zu lösen, und oft merken wir gar nicht, dass wir das tun.</p><p>Betrachten wir ein paar gängige Fälle:</p><ul><li>Ein Titel wie „der Präsident“ ist ohne geopolitischen und zeitlichen Kontext bedeutungslos.</li><li>Ein Firmenname kann sich je nach Zeitpunkt der Artikelveröffentlichung auf ein Mutterunternehmen, eine Tochtergesellschaft oder eine ehemalige Marke beziehen.</li><li>Der Name einer Person kann in verschiedenen Reihenfolgen, Schriften oder Transliterationen erscheinen, abhängig von Sprache und Kultur.</li><li>Die gleiche Phrase kann in verschiedenen Kontexten legitimerweise auf unterschiedliche Entitäten verweisen, und das System muss in der Lage sein, Matches genauso zuversichtlich <em>abzulehnen</em>, wie sie zu akzeptieren.</li></ul><p>Es gibt kein einzelnes Regelwerk, das all dies sauber abdeckt. Deshalb trennt dieser Prototyp die verschiedenen Bereiche so konsequent:</p><ul><li>Elasticsearch schränkt den Kandidatenraum effizient und transparent ein.</li><li>Das LLM wird nur dort verwendet, wo ein Urteil erforderlich ist, und ist gezwungen, sich selbst zu erklären.</li><li>Abruf und Schlussfolgerung bleiben getrennte Schritte.</li></ul><p>Diese Trennung wird umso wichtiger, je größer die Vielfalt der Herausforderungen ist.</p><h2>So geht das System mit Vielfalt ohne Spezialfälle um</h2><p>Eines der interessantesten Ergebnisse dieser Bewertung ist, was <em>sich nicht geändert hat</em>:</p><ul><li>Wir haben <strong>keine</strong> spezielle Logik für japanische Namen hinzugefügt.</li><li>Wir haben <strong>keine</strong> benutzerdefinierten Regeln für arabische Patronyme hinzugefügt.</li><li>Wir haben <strong>keine</strong> fest codierten Mappings für historische Firmennamen hinzugefügt.</li></ul><p>Stattdessen basierte das System auf denselben Kernzutaten, die früher in der Serie eingeführt wurden:</p><ul><li>Mit Kontext angereicherte Entitäten, die für semantische Suchen indexiert sind.</li><li>Hybrider Abruf (exakt, per Alias und semantisch) in Elasticsearch.</li><li>Eine kleine, gut definierte Gruppe von Kandidatenmatches.</li><li>LLM-Bewertung eingeschränkt durch Funktionsaufruf und Minimalschemata.</li></ul><p>Das deutet darauf hin, dass die Flexibilität des Systems von <strong>Repräsentation und Architektur</strong> herrührt, nicht von einer ständig wachsenden Sammlung von Regeln.</p><p>Wenn das System erfolgreich ist, liegt das daran, dass die richtigen Kandidaten ermittelt werden und das LLM ausreichend Kontext hat, um zu erklären, warum eine Referenz einer bestimmten Entität zugeordnet wird oder nicht.</p><h2>Ergebnisse: Wie hat es abgeschnitten?</h2><p>Im ultimativ herausfordernden Datensatz erzielte das System folgende Gesamtergebnisse:</p><ul><li><strong>Präzision:</strong> ~91 %</li><li><strong>Rückruf:</strong> ~86 %</li><li><strong>F1-Score:</strong> ~89 %</li><li><strong>LLM-Annahmequote:</strong> ~72 %</li></ul><h3>Leistung nach Herausforderungstyp</h3><p>Die Aufschlüsselung der Ergebnisse nach Herausforderungstyp zeigt Stärken und Schwächen:</p><p><strong>Die stärkste Leistung (100 % F1-Score)</strong> wurde in folgenden Bereichen beobachtet:</p><ul><li>Schriftübergreifender Abgleich (kyrillische, koreanische, chinesische Unternehmen).</li><li>Hebräische Szenarien (Patronyme, Berufstitel, religiöse Titel, Transliteration).</li><li>Unternehmenshierarchien (Luft- und Raumfahrt, diversifizierte Fertigungsunternehmen, Konzerne mit mehreren Geschäftsbereichen).</li><li>Berufsbezeichnungen (akademisch, militärisch, politisch, religiös).</li><li>Kombinierte japanische Szenarien mit mehreren Schriftsystemen.</li></ul><p><strong>Starke Leistung (80–99 % F1-Score)</strong> umfassten:</p><ul><li>Internationale politische Persönlichkeiten (98 %).</li><li>Historische Namensänderungen (90 %).</li><li>Komplexe Unternehmenshierarchien (89 %).</li><li>Japanische Firmennamen (93 %).</li><li>Cross-Script-Transliteration (86 %).</li><li>Arabische Patronyme (86 %).</li></ul><p><strong>Problematischere Bereiche</strong> waren:</p><ul><li>Erweiterte Transliteration (Chinesisch, Koreanisch): 0 % F1.</li><li>Bestimmte japanische Szenarien (Höflichkeitsformen, Namensreihenfolge, Variationen des Schriftsystems): ~67 % F1.</li><li>Einige arabische Szenarien (Unternehmensnamen, institutionelle Referenzen): ~40 % F1.</li></ul><p>Wichtig ist hier, <em>warum</em> das System in diesen Fällen Schwierigkeiten hatte. Die Fehler waren nicht auf das Scheitern des Gesamtansatzes zurückzuführen, sondern auf Einschränkungen in bestimmten Komponenten, insbesondere dem dichten Vektormodell, das für die semantische Suche in bestimmten mehrsprachigen Szenarien verwendet wird.</p><p>Da Abruf und Beurteilung klar getrennt sind, erfordert die Leistungssteigerung keine Neuprogrammierung des Systems. Der Austausch eines leistungsfähigeren mehrsprachigen Einbettungsmodells, die Anreicherung des Entitätskontextes oder die Verfeinerung der Suchstrategien würde die Ergebnisse in diesen Kategorien verbessern, ohne die Kernarchitektur zu verändern.</p><p>Aus architektonischer Sicht ist das der eigentliche Erfolgsmaßstab.</p><h2>Was uns das über das Design verrät</h2><p>Beim Rückblick auf die Serie lassen sich einige Muster erkennen:</p><ul><li><strong>Vorbereitung ist wichtiger als kluges Matching. </strong>Die Anreicherung von Entitäten mit Kontextinformationen im Vorfeld reduziert spätere Mehrdeutigkeiten erheblich.</li><li><strong>LLMs sind als Bewertungs- und nicht als Abrufsysteme am wertvollsten. </strong>Sie um eine Erklärung zu bitten, <em>warum</em> eine Übereinstimmung sinnvoll ist, ist weitaus wirkungsvoller als sie um eine Suche zu bitten.</li><li><strong>Zuverlässigkeit ermöglicht Genauigkeit. </strong>Der Funktionsaufruf hat nicht nur JSON bereinigt, sondern auch den Abruf von Informationen freigegeben, die bereits in der Abrufphase latent waren.</li><li><strong>Verallgemeinerung schlägt Spezialisierung. </strong>Eine kleine Anzahl gut gewählter Abstraktionen bewältigte Dutzende von Aufgabentypen ohne benutzerdefinierte Logik.</li></ul><p>Aus diesem Grund ist der Prototyp bewusst Elasticsearch-nativ und konservativ in der Verwendung von LLMs. Das Ziel besteht nicht darin, das Suchen zu ersetzen. Es geht darum, das Suchen in Situationen erklärbar zu machen, in denen die Bedeutung wichtig ist.</p><h2>Fazit</h2><p>Die ultimative Herausforderung bestand nicht darin, perfekte Metriken zu verfolgen; es ging darum, eine grundlegendere Frage zu beantworten:</p><p><em>Kann eine transparente, suchbasierte, LLM-gestützte Architektur mit der Mehrdeutigkeit realer Entitäten umgehen, ohne in Regeln oder Blackboxes zu zerfallen?</em></p><p>Für diesen Bildungsprototyp lautet die Antwort ja, mit klaren Vorbehalten in Bezug auf Produktionshärtung, Compliance, Überwachung und Datenqualität. Wenn Sie Systeme erstellen, die begründen müssen, <em>warum</em> ein Entitätsabgleich vorgenommen wurde, ist dieses Muster eine ernsthafte Überlegung wert. Ich hoffe, diese Serie hat gezeigt, dass die Entitätsauflösung kein Mysterium sein muss. Mit der richtigen Aufteilung der Anliegen wird sie zu etwas, worüber man nachdenken, was man messen und verbessern kann.</p><p>Diese Arbeit deutet auch auf ein breiteres architektonisches Muster hin. Daraus entsteht eine leichte, aber wichtige Weiterentwicklung der klassischen Retrieval-Augmented-Generation (RAG). Anstatt die Abfrage direkt in die Generierung einfließen zu lassen, führen wir einen expliziten Bewertungsschritt ein. Das LLM wird zunächst zur Beurteilung und Plausibilitätsprüfung der abgerufenen Kandidaten verwendet, und nur die als geeignet befundenen Ergebnisse dürfen die Generierung erweitern. Sie können sich das als Generation-Augmented Retrieval-Augmented Generation with Evaluation oder GARAGE vorstellen, denn wer weiß nicht ein gutes Akronym zu schätzen?</p><p>Welche anderen Anwendungsfälle könnten von diesem Muster profitieren? Systeme, die Vertrauen, Transparenz und nachvollziehbare Argumentation erfordern, sind natürliche Kandidaten. Die künftige Arbeit in diesem Bereich sollte sich als ebenso überzeugend erweisen wie die Ergebnisse, die wir hier gesehen haben, und ich bin gespannt, wie sich die Gemeinschaft weiter entwickelt.</p><h2>Nächste Schritte: Versuchen Sie es selbst</h2><p>Möchten Sie die ultimative Herausforderung in Aktion sehen? Schauen Sie sich das <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>Ultimate Challenge-Notebook</strong></a> für eine vollständige Anleitung mit realen Implementierungen, detaillierten Erklärungen und praktischen Beispielen an.</p><p>Die vollständige Pipeline zur Entitätsauflösung demonstriert die Kernkonzepte und die Architektur, die für den produktiven Einsatz erforderlich sind. Sie können es als Grundlage nutzen, um Systeme zu entwickeln, die Nachrichtenartikel überwachen, Erwähnungen von Entitäten verfolgen und Fragen beantworten, welche Entitäten in welchen Artikeln erscheinen – und das alles, während Transparenz und Erklärbarkeit erhalten bleiben.<br/></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Adaptive vorzeitige Beendigung für HNSW in Elasticsearch]]></title>
            <description><![CDATA[Einführung einer neuen adaptiven Strategie zur vorzeitigen Beendigung von HNSW in Elasticsearch.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/hnsw-elasticsearch-adaptive-early-termination</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/hnsw-elasticsearch-adaptive-early-termination</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Tommaso Teofili]]></dc:creator>
            <pubDate>Mon, 02 Mar 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e6d3186f609dd56dc5ffe33d70fa9e5cfa05b51f-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Elasticsearch verwendet den <a href="https://www.elastic.co/search-labs/blog/hnsw-graph">Hierarchical Navigable Small World</a> (HNSW)-Algorithmus, um eine Vektorsuche in einem Proximity-Graphen durchzuführen. HNSW ist bekannt dafür, einen guten Kompromiss zwischen der Qualität der k-Nearest-Neighbor (KNN) Ergebnisse und den damit verbundenen Kosten zu bieten.</p><p>In HNSW erfolgt die Suche durch das iterative Erweitern von Kandidatenknoten im Graphen, wobei eine begrenzte Menge der bisher entdeckten nächsten Nachbarn gepflegt wird. Jede Erweiterung hat Kosten zur Folge (Vektorabläufe, zufällige Suchaktionen zur Festplatte und mehr), wobei der marginale Vorteil dieser Kosten tendenziell abnimmt, je weiter die Suche voranschreitet.</p><p>Eine Möglichkeit, die Durchquerung von HNSW-Graphen zu optimieren, besteht darin, die Suche zu beenden, wenn die marginale Wahrscheinlichkeit, neue echte Nachbarn zu finden, nicht steigt. Aus diesem Grund haben wir in <a href="https://www.elastic.co/docs/reference/elasticsearch/index-settings/index-modules#index-dense-vector-hnsw-early-termination">Elasticsearch 9.2</a> einen neuen <a href="https://www.elastic.co/search-labs/blog/hnsw-knn-search-early-termination">Mechanismus zur vorzeitigen Beendigung</a> eingeführt. Das stoppt den Suchvorgang, wenn der Besuch von Graphknoten nicht genug neue nächste Nachbarn für eine feste Anzahl hintereinander liefert.</p><p>In diesem Artikel erfahren Sie, wie wir den erwähnten Mechanismus zur vorzeitigen Beendigung in HNSW verbessert haben, damit er sich für verschiedene Datensätze und Datenverteilungen besser eignet.</p><h2><strong>Vorzeitige Beendigung in HNSW</strong></h2><p>In HNSW erfolgt die Suche durch das iterative Erweitern von Kandidatenknoten im Proximity-Graphen, wobei eine begrenzte Menge der bisher entdeckten nächsten Nachbarn gepflegt wird, bis entweder der gesamte Graph besucht wurde oder ein frühes Abbruchkriterium erfüllt ist.</p><p>Die vorzeitige Beendigung ist daher nicht unbedingt immer eine Optimierung, sondern <strong>Teil des Suchalgorithmus selbst</strong>. Der Moment, in dem wir beschließen aufzuhören, bestimmt das Gleichgewicht zwischen Effizienz und Abruf. In Elasticsearch gibt es bereits eine Reihe von Möglichkeiten, um Abfragen auf HNSW vorzeitig zu beenden:</p><ul><li>Eine festgelegte maximale Anzahl von Knoten wird besucht.</li><li>Ein festgelegtes Zeitlimit ist erreicht.</li></ul><p>Diese Regeln sind zwar einfach und vorhersehbar, verhalten sich aber weitgehend <strong>unabhängig von dem, was die Suche tatsächlich bewirkt</strong>. Außerdem werden sie hauptsächlich verwendet, um sicherzustellen, dass die Abfrage für den Endnutzer in angemessener Zeit abgeschlossen wird.</p><p>In einem <a href="https://www.elastic.co/search-labs/blog/hnsw-knn-search-early-termination">früheren Blogbeitrag</a> haben wir das Konzept der Redundanz im HNSW vorgestellt. Kurz gesagt, redundante Berechnungen treten auf, wenn HNSW weiterhin neue Kandidatenknoten auswertet, ohne dabei weitere nächste Nachbarn zu finden.</p><h2><strong>Geduld: Fortschritt statt Anstrengung messen</strong></h2><p>Der Begriff der <em>Geduld</em> stellt die frühe Beendigung auf <strong>Fortschritt statt Anstrengung</strong> um.</p><p>Anstatt zu fragen:</p><p>„Wie viele Schritte haben wir unternommen?“</p><p>Die neue Frage lautet:</p><p>„Wie viel Rechenleistung sind wir bereit zu verschwenden, bis wir die Hoffnung verlieren?“</p><p>Während der HNSW-Suche führt eine frühe Exploration in der Regel zu Spitzenverbesserungen der Gruppe der Top-k-Kandidaten. Während der ersten Schritte der HNSW-Graph-Exploration wird die Menge der Nachbarn kontinuierlich aktualisiert, da der Algorithmus immer nähere Nachbarn zum Abfragevektor entdeckt. Mit der Zeit werden diese Verbesserungen immer seltener, da die Suche konvergiert. <a href="https://cs.uwaterloo.ca/~jimmylin/publications/Teofili_Lin_ECIR2025.pdf">Die auf Geduld basierende Beendigung</a> überwacht dieses Muster und beendet die Suche, sobald die Verbesserungen für längere Zeit weniger werden.</p><p>In der Praxis berechnen wir beim Besuchen des HNSW-Graphen auch das Sättigungsverhältnis der Warteschlange, während wir Kandidatenknoten durchgehen. So wird der Prozentsatz der nächstgelegenen Nachbarn gemessen, die beim Besuch des letzten Graphknoten unverändert blieben (oder der Kehrwert der Anzahl der während der letzten Iteration eingeführten neuen Nachbarn). Wenn ein solches Verhältnis für zu viele aufeinanderfolgende Iterationen zu groß wird, hören wir auf, den Graphen zu besuchen.</p><p>Konzeptionell betrachtet Geduld die HNSW-Suche als einen Prozess mit <strong>abnehmendem Ertrag</strong>. Wenn sich die Erträge einpendeln, bringt die weitere Exploration des Graphen wenig Nutzen.</p><p>Dieses Framing ist wirkungsvoll, weil es die Beendigung direkt an <em>beobachtbare Ergebnisse</em> bindet, anstatt an willkürlich festgelegte Grenzwerte.</p><p>Der Vorteil dieser intelligenten Technik zur vorzeitigen Beendigung liegt darin, dass HNSW-Graph-Explorationen tendenziell eine geringere Anzahl von Graphknoten besuchen und dabei eine nahezu perfekte relative Abrufquote beibehalten.</p><p>Um dies zu visualisieren, können wir die Abrufquote pro besuchtem Knoten aufzeichnen, die wir mit der geduldsbasierten frühzeitigen Beendigung (gekennzeichnet als <em><code>et=static</code></em>) im Vergleich zum standardmäßigen HNSW-Verhalten (gekennzeichnet als <em><code>et=no</code></em>) bei einigen Datensätzen, FinancialQA und Quora, und Modellen, JinaV3 und E5-small, erhalten haben.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a9d07c5153ea64a2426c82487c36846030692bb9-1600x945.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/dac56125c4b16d1b596c9876b6ca9ac7b2dc87fa-1600x944.png" style="width: 100%;" /><h2><strong>Statische Schwellenwerte und HNSW-Dynamik</strong></h2><p>In der Praxis wird dies in Elasticsearch mithilfe <strong>statischer Schwellenwerte</strong> umgesetzt. Ein Schwellenwert bezieht sich auf den <strong>Sättigungsschwellenwert</strong>, also das Sättigungsverhältnis, das wir für suboptimal halten. Der andere Schwellenwert bezieht sich auf die Anzahl aufeinanderfolgender Graphknoten, die wir zum Besuch zulassen, während wir dennoch eine suboptimale Warteschlangenauslastung erreichen, also der <strong>Geduldsschwellenwert</strong>.</p><p>Als wir diese Strategie zur vorzeitigen Beendigung in Elasticsearch 9.2 einführten, entschieden wir uns für konservative Standardeinstellungen, um den Abruf so weit wie möglich zu erhalten und gleichzeitig die Latenz und den Speicherverbrauch zu verbessern. Aus diesem Grund legten wir den Sättigungsschwellenwert auf 100 % und den Geduldsschwellenwert auf einen (begrenzten) Wert von 30 % des <a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-knn-query#knn-query-top-level-parameters:~:text=search%20request%20size.-,num_candidates,-(Optional%2C%20integer)%20The"><em><code>num_candidates</code></em></a> in der KNN-Abfrage fest.</p><p>In vielen Szenarien erwiesen sich diese Einstellungen als gut geeignet, es kann aber sein, dass zwei Anfragen, die die gleiche Anzahl von Nachbarn anfordern, ein völlig unterschiedliches Konvergenzverhalten aufweisen. Einige Abfragen stoßen auf dichte lokale Nachbarschaften und sind schnell gesättigt, andere müssen lange, spärliche Pfade durchqueren, bevor sie wettbewerbsfähige Kandidaten finden. Letztgenanntes erwies sich als besonders schwierig effektiv zu handhaben.</p><p>Dabei fiel uns folgendes auf:</p><ul><li>Übermäßige Exploration bei einfachen Abfragen.</li><li>Vorzeitige Beendigung bei schwierigen Abfragen.</li></ul><p>Daher kamen wir zu dem Schluss, dass feste Schwellenwerte globale Annahmen über die Konvergenz kodieren, während wir HNSW besser an unterschiedliche Dynamiken anpassen könnten.</p><h2><strong>Die vorzeitige Beendigung von HNSW adaptiv gestalten</strong></h2><p>Ein adaptiver Ansatz zur vorzeitigen Beendigung geht dieses Problem aus einem anderen Blickwinkel an. Anstatt vordefinierte Stoppschwellenwerte durchzusetzen, leitet der Algorithmus <strong>aus den Suchdynamiken selbst ab, wann gestoppt werden soll</strong>.</p><p>Anstatt also das Verhältnis der Warteschlangensättigung zwischen zwei aufeinanderfolgenden Kandidaten zu vergleichen, beschlossen wir, sowohl eine direkt ausgeglichene $Entdeckungsrate d_{q,i} $ (wie viele neue Nachbarn für eine Abfrage <em>q</em> beim letzten Besuch <em>i</em> eingeführt wurden) sowie den fortlaufenden Mittelwert $\mu_{q,i}$ und die Standardabweichung $\sigma_{q,i}$ dieser Entdeckungsrate während des Graphbesuchs (unter Verwendung von <a href="https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford&#x27;s_online_algorithm">Welfords Algorithmus</a>) einzuführen. Diese Statistiken zur Entdeckungsrate werden pro Abfrage berechnet, sodass diese Informationen genutzt werden können, um für jede Abfrage unterschiedliche Geduldsgrade festzulegen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1958be7ca4425ade66eaf621ada3533173183598-694x118.png" style="width: 100%;" /><p>Die zuvor statischen Schwellenwerte werden an die Statistik der Entdeckungsrate angepasst: Die Sättigungsschwelle wird zum gleitenden Mittelwert und mit der Standardabweichung addiert, während wir die Geduld anpassen und umgekehrt mit der Standardabweichung skalieren.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f7ee4a55c24853b657df26052b275e8bd76cf0f9-654x156.png" style="width: 100%;" /><p>Die Regeln für die vorzeitige Beendigung bleiben gleich: Die Sättigung tritt ein, wenn die sofortige Entdeckungsrate niedriger ist als der adaptive Sättigungsgrenzwert. Der Graph-Besuch stoppt, wenn die Sättigung für eine Anzahl aufeinanderfolgender Kandidatenbesuche anhält, die größer ist als die adaptive Geduld.</p><p>So erhalten wir ein Verhalten, das nicht vom <em><code>num_candidates</code></em>-Parameter in der KNN-Abfrage abhängt (der immer als Standard gesetzt oder belassen werden kann, unabhängig von der vorzeitigen Beendigung) und sich dynamisch besser an jede Abfrage und Vektorverteilung anpasst.</p><p>Die Abrufquote pro besuchtem Knoten auf FinancialQA und Quora mit der adaptiven Strategie (gekennzeichnet als <em><code>et=adaptive</code></em>) ist höher als bei der statischen Strategie (<em><code>et=static</code></em>) und dem Standardverhalten von HNSW (<em><code>et=no</code></em>).</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2a906997d9a25d74c7038bd9661bc97581e7258e-1600x938.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3a114911e232c351dbb814cea20e8b0f1415a717-1600x925.png" style="width: 100%;" /><p>Die adaptive vorzeitige Beendigung ist in Elasticsearch 9.3 standardmäßig für HNSW-dichte Vektorfelder aktiviert (und kann schließlich über die <a href="https://www.elastic.co/docs/reference/elasticsearch/index-settings/index-modules#index-dense-vector-hnsw-early-termination">gleiche Index-Level-Einstellung</a> deaktiviert werden).</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Common Expression Language (CEL): Wie CEL-Eingaben die Datenerfassung in Elastic Agent-Integrationen verbessern]]></title>
            <description><![CDATA[Erfahren Sie, wie sich die Common Expression Language von anderen Programmiersprachen unterscheidet, wie wir sie für CEL-Eingaben von Filebeat erweitert haben und welche Flexibilität sie Ihnen bietet, um Ihre Datenerfassungslogik in Elastic Agent-Integrationen auszudrücken.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/common-expression-language-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/common-expression-language-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Chris Berkhout]]></dc:creator>
            <pubDate>Fri, 27 Feb 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/985c50bfabee3348494eb4307f0b3375a97a0644-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Elastic <a href="https://www.elastic.co/integrations">Agent-Integrationen</a> ermöglichen es Nutzern, Daten aus einer Vielzahl von Quellen in Elasticsearch zu ingestieren. Sie kombinieren Erfassungslogik, Ingest-Pipelines, Dashboards und andere Artefakte in einem Paket, das über die Kibana-Web-Schnittstelle installiert und verwaltet werden kann.</p><p>Integrationen konfigurieren <a href="https://www.elastic.co/docs/reference/beats/filebeat/configuration-filebeat-options">Filebeat-Eingaben</a>, um die Datenerfassung durchzuführen. Um Daten aus HTTP-APIs zu sammeln, haben wir oft <a href="https://www.elastic.co/docs/reference/beats/filebeat/filebeat-input-httpjson">HTTP-JSON-Eingaben</a> verwendet. Jedoch können selbst grundlegende Listing-APIs in den Details stark voneinander abweichen, und das Modell der YAML-konfigurierten Transformationen der HTTP-JSON-Eingabe kann es umständlich und manchmal unmöglich machen, die erforderliche Sammlungslogik auszudrücken.</p><p><a href="https://www.elastic.co/docs/reference/beats/filebeat/filebeat-input-cel">Common Expression Language (CEL)-Eingaben</a> wurde eingeführt, um eine flexiblere Interaktion mit HTTP-APIs zu ermöglichen. <a href="https://cel.dev/">CEL</a> ist eine Sprache, die dafür entwickelt wurde, in Anwendungen eingebettet zu werden, die eine schnelle, sichere und erweiterbare Möglichkeit zur Darstellung von Bedingungen und Datentransformationen erfordern. CEL-Eingaben ermöglichen es einem Integration Builder, einen Ausdruck zu schreiben, der Einstellungen lesen, seinen eigenen Status verfolgen, Anfragen stellen, Reaktionen verarbeiten und letztendlich Ereignisse zurückgeben kann, die für den Ingest bereit sind.</p><p>In diesem Artikel werden wir uns ansehen, wie sich CEL von anderen Programmiersprachen unterscheidet, wie wir es für CEL-Eingaben erweitert haben und welche Flexibilität und Möglichkeiten es Ihnen bietet, um Ihre Datenerfassungslogik auszudrücken.</p><h2>CEL und seine Funktionsweise als Eingabe</h2><p>CEL ist eine Ausdruckssprache. Es enthält keine Anweisungen. Wenn Sie CEL schreiben, geben Sie dem Programm nicht durch Anweisungen vor, was es tun soll, sondern teilen ihm durch einen Ausdruck mit, welchen Wert es erzeugen soll. Jeder CEL-Ausdruck erzeugt einen Wert, und kleinere Ausdrücke können zu einem größeren Ausdruck kombiniert werden, um ein Ergebnis nach komplexeren Regeln zu erzeugen. Später werden wir sehen, wie man Ausdrücke für Dinge verwendet, die in anderen Sprachen möglicherweise mit Anweisungen geschrieben werden.</p><p>CEL ist bewusst eine nicht Turing-vollständige Sprache. Es erlaubt keine unbegrenzten Schleifen. Später werden wir sehen, wie man Listen und Maps mithilfe von Makros verarbeiten kann. Durch die Vermeidung unbegrenzter Schleifen garantiert die Sprache jedoch eine vorhersehbare und begrenzte Ausführungszeit für einzelne Ausdrücke.</p><p>Die CEL-Eingabe wird mit einem CEL-Programm (einem Ausdruck) und einem Anfangszustand konfiguriert. Der Zustand wird als Eingabe für das Programm bereitgestellt. Das Programm wird ausgewertet, um einen Ausgabezustand zu erzeugen. Wenn der Ausgabezustand eine Liste von Ereignissen enthält, werden diese entfernt und veröffentlicht. Der Rest des Ausgabezustands wird als Eingabe für die nächste Auswertung verwendet. Wenn der Ausgabezustand ein oder mehrere Ereignisse und den Flag <code>want_more: true</code>enthält, wird die nächste Auswertung sofort durchgeführt; ansonsten schläft er für den Rest der konfigurierten Intervallzeit, bevor er weiterfährt. Hier ist ein vereinfachtes Diagramm des Kontrollflusses der Eingabe:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/42671541f97e2dba808fd53969fe12f517917f9a-1600x529.png" style="width: 100%;" /><p>Die Ausgabe jeder Auswertung wird als Eingabe an die nächste Auswertung weitergeleitet, solange die Eingabe läuft. Ausgabedaten unter dem Schlüssel „<code>cursor</code>“ werden auf der Festplatte beibehalten und nach dem Neustart der Eingabe wieder geladen, aber der Rest des Zustands wird nicht über Neustarts hinweg beibehalten.</p><p>Die CEL-Sprache selbst hat begrenzte Funktionalität und vermeidet Nebenwirkungen, ist jedoch erweiterbar. Die <a href="https://github.com/google/cel-go">cel-go</a>-Implementierung fügt einige Funktionen hinzu, darunter optionale Syntax und Typen. Die <a href="https://github.com/elastic/mito">Mito</a>-Bibliothek baut auf cel-go auf und fügt weitere Funktionen hinzu, darunter die Möglichkeit, HTTP-Anfragen zu stellen. Die CEL-Eingabe verwendet die Mito-Version von CEL.</p><h2>Zusammenarbeit mit Mito</h2><p>Für den Aufbau oder die Fehlersuche einer Integration mit CEL-Eingaben ist es am wichtigsten zu verstehen, welchen Ausgangszustand Ihr CEL-Programm für einen gegebenen Eingabezustand erzeugt. Während der Entwicklung kann es umständlich sein, Ihr CEL-Programm über die Eingabe auszuführen, umgeben vom vollständigen Elastic Stack. Eine Möglichkeit, eine schnellere Feedback-Schleife zu erreichen, besteht darin, das Befehlszeilentool von Mito zu verwenden, mit dem Sie ein CEL-Programm direkt ausführen und den Ausgang sehen können, den es für eine bestimmte Eingabe erzeugt.</p><p>Mito ist in Go geschrieben und kann wie folgt installiert werden:</p><pre><code>go install github.com/elastic/mito/cmd/mito@latest</code></pre><p>Wenn Sie ein CEL-Programm mit Mito ausführen, geben Sie ihm in der Regel zwei Dateien: eine JSON-Datei mit dem anfänglichen Eingabezustand und eine weitere Datei mit dem Quellcode Ihres CEL-Programms.</p><pre><code>mito -data state.json src.cel</code></pre><p>Um das Kopieren und Einfügen zu erleichtern, sind die Beispiele in diesem Artikel als einzelne Befehle geschrieben, bei denen die Shell temporäre Dateien spontan erstellt, indem der Inhalt jeder Datei in <code>&lt;(echo &#x27;...content...&#x27;)</code>eingeschlossen wird. Bei Ihren eigenen Entwicklungsprojekten wird Ihnen die Arbeit mit realen Dateien leichter fallen.</p><h2>Problemdaten von GitHub abrufen</h2><p>Das folgende Beispiel enthält ein vollständiges CEL-Programm, das Daten zu Problemen aus der <a href="https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#list-repository-issues">GitHub-API</a> abruft. Der anfängliche Eingabezustand enthält eine URL für den API-Endpunkt sowie einige Informationen darüber, wie die Paginierung gehandhabt werden soll. Das CEL-Programm verwendet die Daten im Eingabezustand, um eine Anfrage zu generieren. Es entschlüsselt die Antwort, erzeugt daraus Ereignisse und gibt sie als Teil seines Ausgabezustands zurück.</p><pre><code>mito -data <(echo '
  {
    "url": "https://api.github.com/repos/elastic/integrations/issues",
    "per_page": 3,
    "max_pages": 3
  }
') <(echo '
  int(state.?cursor.page.orValue(1)).as(page,
    (
      state.url + "?" + {
        "state": ["all"],
        "sort": ["created"],
        "direction": ["asc"],
        "per_page": [string(state.per_page)],
        "page": [string(page)],
      }.format_query()
    ).as(full_url,
      request("GET", full_url).with({
        "Header": {
          "Accept": ["application/vnd.github+json"],
          "X-GitHub-Api-Version": ["2022-11-28"],
        }
      }).do_request().as(resp,
        resp.Body.decode_json().as(data,
          state.with({
            "events": data.map(i, {
              "html_url": i.html_url,
              "title": i.title,
              "created_at": i.created_at,
            }),
            "cursor": { "page": page + 1 },
            "want_more": size(data) == state.per_page && page < state.max_pages,
          })
        )
      )
    )
  )
')</code></pre><p>Die erste Auswertung liefert folgende Ausgabe:</p><pre><code>{
  "cursor": {
    "page": 2
  },
  "events": [
    {
      "created_at": "2018-09-14T09:47:35Z",
      "html_url": "https://github.com/elastic/integrations/issues/3250",
      "title": "Increase support of log formats in haproxy filebeat module"
    },
    {
      "created_at": "2019-02-06T12:37:37Z",
      "html_url": "https://github.com/elastic/integrations/issues/487",
      "title": "ETCD Metricbeat module needs polishing and grooming"
    },
    {
      "created_at": "2019-08-13T11:33:11Z",
      "html_url": "https://github.com/elastic/integrations/pull/1",
      "title": "Initial structure"
    }
  ],
  "max_pages": 3,
  "per_page": 3,
  "url": "https://api.github.com/repos/elastic/integrations/issues",
  "want_more": true
}</code></pre><p>Die Ereignisse werden entfernt, und wenn sie in der CEL-Eingabe ausgeführt werden, werden sie zur Ingestion veröffentlicht. Der Rest der Ausgabe wird dem nächsten CEL-Programmbewertungsvorgang als Eingabestand bereitgestellt.</p><p></p><p>Um zu verstehen, wie dieses CEL-Programm funktioniert, betrachten wir einige kleinere CEL-Beispiele und besprechen weitere Details zur Funktionsweise der CEL-Eingabe.</p><h2>CEL-Grundlagen</h2><p>In der CEL-Sprache gibt es keine Anweisungen, sondern nur Ausdrücke. Jeder erfolgreiche CEL-Ausdruck wird zu einem endgültigen Wert ausgewertet. Hier ist einer der kleinsten CEL-Ausdrücke, die Sie schreiben können, zusammen mit seiner Ausgabe:</p><pre><code>mito <(echo '
  "hello" + " " + "world"
')</code></pre><pre><code>"hello world"</code></pre><p>Viele einfache Ausdrücke sind intuitiv. Mathematische Operationen werden nur bei Werten desselben Typs unterstützt (zum Beispiel <code>int</code> mit <code>int</code>), konvertieren Sie also Typen nach Bedarf (hier von <code>int</code> zu <code>double</code>):</p><pre><code>mito <(echo '
  double((1 + 2) * (3 + 4)) / 2.0
')</code></pre><pre><code>10.5</code></pre><p>In der CEL-Sprache gibt es keine Variablen, aber ein Ausdruck kann mit Hilfe des <a href="https://pkg.go.dev/github.com/elastic/mito/lib#hdr-As__Macro_-Collections"><code>as</code></a>-Makros von Mito benannt und in einem größeren Ausdruck verwendet werden. In diesem Beispiel wird der Ausdruck <code>(1 + 1)</code> zum Wert <code>2</code> ausgewertet, und <code>.as(n, ...)</code> gibt diesem Wert den Namen <code>n</code> zur Verwendung im Ausdruck <code>&quot;one plus one is &quot;+string(n)</code>:</p><pre><code>mito <(echo '
  (1 + 1).as(n, "one plus one is "+string(n))
')</code></pre><pre><code>"one plus one is 2"</code></pre><p>Es ist auch möglich, Informationen in einer Map zu sammeln und sie später im Ausdruck zu verwenden, wie hier mit <a href="https://pkg.go.dev/github.com/elastic/mito/lib#hdr-With-Collections"><code>with</code></a> gezeigt wird:</p><pre><code>mito <(echo '
  { "key": "value" }.with({ "key2": "value2" }).as(data,
    {
      "data": data,
      "size": size(data),
    }
  )
')</code></pre><pre><code>{
  "data": {
    "key": "value",
    "key2": "value2"
  },
  "size": 2
}</code></pre><p>Sehen Sie sich dieses Beispiel erneut an. Beachten Sie, dass der verschachtelte Teil – <code>({ &quot;data&quot;: data, &quot;size&quot;: size(data), })</code> – uns die Form des Endwertes vorgibt. Es ist eine Map mit den Schlüsseln <code>&quot;data&quot;</code> und <code>&quot;size&quot;</code>. Die Werte für diese Schlüssel hängen von <code>data</code> ab, was durch den äußeren Teil des Ausdrucks definiert ist. Das Lesen von CEL-Ausdrücken von innen nach außen kann helfen, schnell zu erkennen, was sie zurückgeben.</p><p>CEL hat keine Kontrollflussanweisungen wie <code>if</code>, aber bedingte Verzweigungen können mit dem ternären Operator durchgeführt werden:</p><pre><code>mito <(echo '
  1 + 1 < 12 ? "few" : "many"
')</code></pre><pre><code>"few"</code></pre><p>Unbeschränkte Schleifen und Rekursion werden nicht unterstützt, da CEL keine Turing-vollständige Sprache ist. Das macht die Ausführungszeit vorhersehbar und proportional zur Größe der Eingabedaten und zur Komplexität des Ausdrucks.</p><p>Obwohl unbeschränkte Schleifen in einzelnen CEL-Ausdrücken nicht möglich sind, können Sie Listen und Maps mit Makros wie <a href="https://github.com/google/cel-spec/blob/master/doc/langdef.md#macros"><code>map</code></a>bearbeiten:</p><pre><code>mito <(echo '
  [1, 2, 3].map(x, x * 2)
')</code></pre><pre><code>[2, 4, 6]</code></pre><p>In diesem Abschnitt haben wir Folgendes behandelt:</p><ul><li>Zeichenfolgen, Zahlen, Listen und Maps.</li><li>Verkettung von Zeichenfolgen.</li><li>Mathematische Operationen.</li><li>Typumwandlung.</li><li>Bedingungen.</li><li>Benennung von Unterausdrücken.</li><li>Verarbeiten von Sammlungen.</li></ul><p>Als Nächstes sehen wir uns an, wie man HTTP-Anfragen stellt.</p><h2>Anfragen</h2><p>Mito erweitert CEL um die Möglichkeit, <a href="https://pkg.go.dev/github.com/elastic/mito/lib#HTTP">HTTP-Anfragen</a> zu stellen:</p><pre><code>mito <(echo '
  get("https://example.com").as(resp, string(resp.Body))
')</code></pre><pre><code>"<!doctype html><html lang=\"en\"><head><title>Example Domain</title>..."</code></pre><p>Anfragen können explizit erstellt werden, bevor sie ausgeführt werden. Dadurch ist es möglich, verschiedene HTTP-Methoden zu verwenden und Header sowie einen Body hinzuzufügen.</p><p>In diesem Beispiel erstellen wir eine URL mit Hilfe von <a href="https://pkg.go.dev/github.com/elastic/mito/lib#hdr-Format_Query-HTTP"><code>format_query</code></a>, fügen der Anfrage einen Header hinzu und parsen den Response Body mit <a href="https://pkg.go.dev/github.com/elastic/mito/lib#hdr-Decode_JSON-JSON"><code>decode_json</code></a>. Wenn die Option <code>-log_requests</code> angeboten wird, protokolliert Mito detaillierte Informationen im JSON-Format zu jeder Anfrage und Antwort.</p><pre><code>mito -log_requests <(echo '
  request("GET",
    "https://postman-echo.com/get?" + {
        "q": ["query value"]
     }.format_query()
  ).with({
    "Header": { "Accept": ["application/json"] }
  }).do_request().as(resp, {
    "status": resp.StatusCode,
    "data": resp.Body.decode_json(),
  })
')</code></pre><pre><code>{"time":"...","level":"INFO","msg":"HTTP request",...}
{"time":"...","level":"INFO","msg":"HTTP response",...}
{
  "data": {
    "args": {
      "q": "query value"
    },
    "headers": {
      "accept": "application/json",
      "accept-encoding": "gzip, br",
      "host": "postman-echo.com",
      "user-agent": "Go-http-client/2.0",
      "x-forwarded-proto": "https"
    },
    "url": "https://postman-echo.com/get?q=query+value"
  },
  "status": 200
}</code></pre><h2>Zustandsverwaltung und Evaluierungen</h2><p>Nachdem wir nun behandelt haben, wie man Anfragen stellt und welche CEL-Grundlagen erforderlich sind, um den gewünschten Ausgabezustand zu erzeugen, schauen wir uns genauer an, was wir in den Ausgabezustand einfügen sollten und wie wir dadurch die spätere Verarbeitung steuern können.</p><p>Das CEL-Programm einer Integration muss sicherstellen, dass sein Ausgabezustand als Eingabe für die nächste Auswertung geeignet ist. Die Konfiguration legt den Ausgabezustand fest, und dieser sollte in der Ausgabe mit allen entsprechenden Änderungen wiederholt werden. Eine einfache Möglichkeit ist, <code>state.with({ ... })</code>zu verwenden, um die Zustands-Map mit einigen Überschreibungen zu wiederholen. Ein gängiges Muster für kleine Programme ist es, das gesamte Programm in <code>state.with()</code>einzuschließen, sodass die Zustandspropagation nicht in jedem Branch wiederholt werden muss, der Ausgabedaten erzeugt (zum Beispiel Erfolg, Fehler).</p><p>Wenn es Statuswerte gibt, die durch eine Auswertung initialisiert werden und nicht fest im anfänglichen Eingabezustand kodiert sind, muss das Programm nach einem vorhandenen Wert suchen, bevor es den anfänglichen Wert setzt. Dabei kann die Unterstützung für <a href="https://pkg.go.dev/github.com/google/cel-go/cel#OptionalTypes">optionale Syntax und Typen</a> helfen. Durch die Verwendung eines Fragezeichens vor dem Feldnamen in einem Map-Schlüssel wird der Zugriff optional: Er kann zu einem Wert aufgelöst werden oder auch nicht, aber weitere optionale Zugriffe sind möglich, und es ist einfach, einen Standardwert bereitzustellen, wenn kein Wert vorhanden ist.<br/></p><pre><code>mito -data <(echo '{}') <(echo '
  int(state.?counter.orValue(0)).as(counter,
    state.with({
      "counter": counter + 1,
      "want_more": counter + 1 < 3,
    })
  )
')</code></pre><pre><code>{ "counter": 1, "want_more": true }
{ "counter": 2, "want_more": true }
{ "counter": 3, "want_more": false }</code></pre><p>In diesem Beispiel wird der aus dem Zustand gelesene Zählerwert in <code>int</code> umgewandelt, da alle Zahlen im Zustand als Gleitkommazahlen serialisiert werden, entsprechend den Konventionen von JSON und dem <code>Number</code>-Typ von JavaScript. Es sollte auch beachtet werden, dass <code>&quot;want_more&quot;: true</code> hier von Mito beachtet wird, aber wenn es in der CEL-Eingabe ausgeführt wird, wird die Auswertung nur wiederholt, wenn die Ausgabe auch Ereignisse enthält.</p><p>Es ist eine Voraussetzung für CEL-Programme, die über die CEL-Eingabe ausgeführt werden, dass sie in ihrer Ausgabezuordnung einen <code>&quot;events&quot;</code>-Schlüssel zurückgeben. Sein Wert kann eine Liste von Ereignis-Maps, eine leere Liste oder ein einzelnes Ereignis-Map sein. Der Fall mit dem einzelnen Ereignis wird üblicherweise für Fehler verwendet. Das Ereignis wird von der Eingabe veröffentlicht, aber auch sein Wert wird geloggt, und wenn ein <code>error.message</code> Wert gesetzt wird, wird dieser verwendet, um den Fleet-Gesundheitsstatus der Integration zu aktualisieren. Wenn Ihr Programm ein einzelnes Ereignis erzeugt, das keinen Fehler verursacht, ist es am besten, es in eine Liste einzuschließen.</p><p>Werfen Sie noch einmal einen Blick auf die Ausgabe unseres GitHub-Problem-Programms von vorhin:</p><pre><code>{
  "url": "https://api.github.com/repos/elastic/integrations/issues",
  "per_page": 3,
  "max_pages": 3,
  "cursor": {
    "page": 2
  },
  "events": [
    { ... },
    { ... },
    { ... }
  ],
  "want_more": true
}</code></pre><p>Das Programm hat seinen Zustand effektiv verwaltet, indem:</p><ul><li>Wiederholte Anfangszustandswerte in <code>url</code>, <code>per_page</code>, und <code>max_pages</code>.</li><li>Hinzufügen eines Zustands, der über Neustarts hinweg erhalten bleiben soll, in <code>cursor.page</code>.</li><li>Wiederkehrende Veranstaltungen, bereit zur Veröffentlichung in der <code>events</code>-Liste.</li><li>Sofortige Neubewertung mit <code>want_more: true</code> angefordert.</li></ul><p>Jetzt, da Sie den optionalen Zugriff und die Statusverwaltung sowie die CEL-Grundlagen und HTTP-Anfragen verstehen, sollte das vollständige GitHub-Problem-Programm lesbar sein. Versuchen Sie, es mit Mito auszuführen und mit einigen Änderungen zu experimentieren.</p><h2>Überprüfung und Ressourcen</h2><p>In diesem Artikel haben wir untersucht, was die CEL-Sprache ist und wie sie in der Mito-Bibliothek für die Nutzung in der CEL-Eingabe erweitert wurde. Wir haben die Flexibilität von CEL in einem Beispielprogramm gesehen, das Probleminformationen von der GitHub-API abruft, und sind alle Details durchgegangen, die zum Verständnis dieses Programms notwendig sind. Dazu gehören der Zugriff auf Einstellungen im Ausgabezustand, die Interaktion mit HTTP-APIs, die Rückgabe von Ereignissen zum Ingestieren und die Verwaltung des Zustands für spätere Programmausführungen.</p><p>Wenn Sie mehr erfahren und Integrationen mit CEL-Eingaben erstellen möchten, gibt es eine Reihe von Ressourcen, die Sie sich ansehen sollten:</p><ul><li><a href="https://www.elastic.co/docs/reference/beats/filebeat/filebeat-input-cel">CEL-Eingabe – Filebeat-Dokumentation</a></li><li><a href="https://pkg.go.dev/github.com/elastic/mito">Mito-Dokumentation</a></li><li><a href="https://cel.dev/">Common Expression Language - cel.dev Website</a></li><li><a href="https://www.elastic.co/docs/extend/integrations">Eine Integration erstellen – Elastic-Dokumentation</a></li></ul><p>Die vielleicht wertvollste Ressource für die Entwicklung von Integrationen mit CEL-Eingaben ist der CEL-Code bestehender Elastic-Integrationen, der auf GitHub zu finden ist:</p><p><a href="https://github.com/search?q=repo%3Aelastic%2Fintegrations+path%3A**%2Fcel.yml.hbs&amp;type=code"><code>cel.yml.hbs</code> Dateien im Elastic-Integrations-Repository – GitHub</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Entitätsauflösung mit Elasticsearch & LLMs, Teil 2: Abgleich von Entitäten mit LLM-Bewertung und semantischer Suche]]></title>
            <description><![CDATA[Verwendung semantischer Suche und transparenter LLM-Bewertung zur Entitätsauflösung in Elasticsearch.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/elasticsearch-entity-resolution-llm-semantic-search</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/elasticsearch-entity-resolution-llm-semantic-search</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jessica Moszkowicz]]></dc:creator>
            <pubDate>Thu, 26 Feb 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/473ca4357c7d60f690edbd2a844acda169aca9c3-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>In<a href="https://www.elastic.co/search-labs/blog/entity-resolution-llm-elasticsearch"> Teil 1</a> haben wir unsere Watchlist vorbereitet und Entitätserwähnungen extrahiert. Nun können wir die schwierige Frage beantworten: Auf welche Entität bezieht sich die Erwähnung eigentlich? Kehren wir zu dem Beispiel im ersten Blog dieser Serie zurück, das verdeutlicht, warum wir eine Entitätsauflösung benötigen: „Das Swift-Update ist da!“ Stellen Sie sich vor, diese Überschrift wird von etwas mehr Kontext begleitet:</p><ol><li>Das neue Swift-Update ist da! Entwickler sind gespannt darauf, die neuen Features auszuprobieren.</li><li>Das neue Swift-Update ist da! Das neue Album erscheint nächsten Monat.</li></ol><p>Mit diesem zusätzlichen Kontext sollten wir den Namen „Swift“ der richtigen Entität zuordnen können.</p><p>Im <a href="https://www.elastic.co/search-labs/blog/entity-resolution-llm-elasticsearch">vorherigen Beitrag</a> haben wir unsere Watchlist eingerichtet und die Entitäten mit zusätzlichem Kontext angereichert. Anhand unserer obigen Beispiele müssen wir mindestens die folgenden beiden Elemente in der Liste haben: Taylor Swift und Swift Programming Language. Wir haben auch besprochen, wie wir Entitätserwähnungen aus Text extrahieren. Beide Beispiele würden „Swift“ extrahieren. Mit diesen Zutaten, der angereicherten Watchlist und den extrahierten Entitäten, sind wir endlich bereit, den Star der Show vorzustellen: den Entitätsabgleich.</p><p><strong>Denken Sie daran:</strong> Dies ist ein pädagogischer Prototyp, der entwickelt wurde, um Konzepte zum Abgleich von Entitäten zu vermitteln. Produktionssysteme könnten verschiedene große Sprachmodelle (LLMs), benutzerdefinierte Abgleichsregeln, spezialisierte Bewertungspipelines oder Ensemble-Ansätze verwenden, die mehrere Abgleichsstrategien kombinieren.</p><h2>Das Problem: Warum der Abgleich schwierig ist</h2><p>Die menschliche Sprache ist eine bemerkenswerte Sache. Eine ihrer interessantesten Eigenschaften ist ihre unendliche Kreativität. Wir können eine unendliche Anzahl neuer Sätze erzeugen und verstehen. Ist es dann verwunderlich, dass exakte Übereinstimmungen bei der Entitätsauflösung selten sind? Autoren bemühen sich, kreativ zu sein, wenn sie können. Es wäre ziemlich mühsam, wenn wir immer die vollständigen Namen schreiben und lesen müssten, wenn eine Entität erwähnt wird. Exakte Übereinstimmungen sind zwar einfach, aber die Realität sieht so aus, dass wir einen ausgefeilteren Ansatz zur Entitätsauflösung benötigen: einen, der robust genug ist, um zumindest einen Teil der grenzenlosen Kreativität menschlicher Autoren zu bewältigen. Deshalb unterteilen wir das Problem in zwei Schritte: Mit Elasticsearch werden plausible Kandidaten skaliert abgerufen, und anschließend wird mit einem LLM beurteilt, ob sich diese Kandidaten tatsächlich auf dieselbe reale Entität beziehen.</p><h2>Die Lösung: Dreistufiger Abgleich mit transparenter LLM-Bewertung</h2><p>Wir befinden uns mitten in einem Paradigmenwechsel in der Art und Weise, wie wir Computer nutzen. Genauso wie der Aufstieg des Internets uns vom lokalen Computing zu einem global vernetzten Netzwerk geführt hat, verändert die generative KI grundlegend die Art und Weise, wie Inhalte, Code und Informationen erstellt werden. Tatsächlich wurde der pädagogische Prototyp, der diese Serie begleitet, fast ausschließlich „Vibe-codiert“ unter Verwendung eines LLMs mit sorgfältiger Eingabe durch den Autor. Das soll nicht heißen, dass LLMs die Produktivität der menschlichen Sprache erreicht haben oder erreichen werden, aber es bedeutet, dass wir jetzt eine leistungsstarke Ressource haben, die uns bei der Entitätsauflösung unterstützt.</p><p>Ein häufiges Muster, das wir mit GenAI verwenden, ist Retrieval-Augmented Generation (RAG). Hier bedeutet <em>Abrufen</em> das Abrufen von Entitätskandidaten (nicht das Generieren von Antworten), und das LLM wird ausschließlich für die Bewertung und Erklärung von Übereinstimmungen verwendet. Obwohl wir ein LLM um Unterstützung bei der End-to-End-Lösung von Entitäten bitten <em>könnten</em>, ist das sowohl zeitlich als auch finanziell kostspielig. RAG hilft LLMs bei ihrer Arbeit, indem es effizientere Wege nutzt, um dem LLM Kontext bereitzustellen, und ermöglicht es dem LLM so, effizient bei der Entitätsauflösung zu helfen.</p><p>Für den Abrufteil von RAG greifen wir erneut auf Elasticsearch zurück. Zunächst ermitteln wir potenzielle Übereinstimmungen mithilfe einer Kombination aus exaktem Abgleich, Abgleich mit Aliasen und hybrider Suche, die Stichwort- und semantische Suche kombiniert. Sobald wir diese potenziellen Übereinstimmungen gefunden haben, schicken wir sie an ein LLM zur Bewertung. Das LLM fungiert als der letzte Übereinstimmungsbewerter. Wir lassen das LLM außerdem seine Argumentation erläutern, was ein wichtiges Unterscheidungsmerkmal zu anderen Entitätsauflösungssystemen darstellt. Ohne diese Erklärungen ist die Entitätsauflösung eine Blackbox; mit ihnen können wir selbst sehen, warum eine Übereinstimmung Sinn ergibt.</p><h2>Schlüsselkonzepte: Drei-Schritte-Abgleich, hybride Suche und transparente LLM-Bewertung</h2><p><strong>Was ist der Drei-Schritte-Abgleich?</strong> Zu Beginn dieses Projekts haben wir die Hypothese aufgestellt, dass die semantische Suche ein entscheidender Bestandteil des Systems sein wird, aber nicht jeder Abgleich erfordert eine so ausgefeilte Suche. Um effizient Übereinstimmungen zu finden, gehen wir das Problem progressiv an. Zuerst überprüfen wir exakte Übereinstimmungen mit der Stichwortsuche. Wenn wir eine solche Übereinstimmung finden, ist unsere Arbeit getan und wir können weitermachen. Wenn der exakte Abgleich fehlschlägt, wenden wir uns dem Aliasabgleich zu. Im Prototyp wird der Einfachheit halber auch der Aliasabgleich mit Stichwörtern durchgeführt. In der Produktion können Sie diesen Schritt durch Normalisierung, Transliterationsregeln, Fuzzy Matching oder kuratierte Aliastabellen erweitern. Wenn wir in den ersten beiden Schritten immer noch keinen potenziellen Treffer gefunden haben, dann ist es an der Zeit, die semantische Suche über die hybride Suche von Elasticsearch mit Reciprocal Rank Fusion (RRF) einzuführen.</p><p><strong>Was ist die hybride Suche?</strong> In Elasticsearch können wir die semantische Suche nutzen, um bedeutungsvolle Übereinstimmungen zu finden, die Kontext berücksichtigen. Elasticsearch wird häufig für Vektorsuche und hybride Abfrageverfahren eingesetzt. Semantische Ähnlichkeit ist sehr aussagekräftig, aber sie ist kein Ersatz für strukturiertes Filtern (z. B. nach Zeitspannen, Orten oder Identifikatoren) und ist oft unnötig, wenn eine exakte Übereinstimmung verfügbar ist. Elasticsearch hat sich mit der lexikalischen Suche einen Namen gemacht, die sich hervorragend für Aufgaben eignet, bei denen die semantische Suche nicht ausreicht. Um beide Ansätze voll auszuschöpfen, verwenden wir die lexikalische Suche neben der semantischen Suche in einer einzigen hybriden Abfrage. Anschließend führen wir die Ergebnisse zusammen, um mithilfe von RRF die wahrscheinlichsten Übereinstimmungen zu finden. Im Prototyp werden die oberen zwei Ergebnisse zu potenziellen Übereinstimmungen, die zur LLM-Bewertung gesendet werden können.</p><p><strong>Warum die LLM-Bewertung?</strong> LLM-Bewertungen und -Erklärungen ermöglichen es unserem System, Ambiguität und Kontext transparent zu behandeln. Dies ist entscheidend für Fälle wie „der Präsident“, die sich auf mehrere Entitäten beziehen können, abhängig vom Kontext, aber es ermöglicht auch, dass Dinge wie Spitznamen und kulturelle Variationen gut im System funktionieren. Und schließlich müssen wir bei geschäftskritischen Aufgaben, wie der Identifizierung von Personen aus Sanktionslisten, wissen, warum ein Treffer akzeptiert wurde, um dem System vertrauen zu können. Entscheidend ist, dass das LLM nicht den gesamten Korpus durchsucht; es bewertet nur die kleine Anzahl von Kandidaten, die von Elasticsearch zurückgegeben werden.</p><h2>Reale Ergebnisse: Übereinstimmung mit der LLM-Argumentation</h2><p>Eine große Herausforderung bei jeder Aufgabe der natürlichen Sprachverarbeitung ist die Erstellung eines Referenzdokuments, eines „Lösungsschlüssels“, der uns mitteilt, was die zu erwartenden Ergebnisse sind. Ohne diese Grundlage ist es nahezu unmöglich zu beurteilen, wie gut ein System eine Aufgabe erfüllt. Doch die Erstellung eines solchen Dokuments kann ein mühsamer Prozess sein. Für den Prototyp zur Entitätsauflösung haben wir uns erneut an generative KI gewandt, um Unterstützung bei der Einrichtung von Testdaten zu erhalten.</p><p>Zunächst definierten wir mehrere Herausforderungstypen, wie Spitznamen und Transliteration, und baten dann das LLM, eine gestufte Sammlung von Datensätzen zu erstellen, die für das System zunehmend größer und anspruchsvoller werden sollte. Die Erstellung der Datensätze war weniger einfach, als man es sich erhoffen könnte. Das LLM hatte eine starke Neigung zum „Betrügen“, indem es zu einfach wurde, die richtige Antwort zu erhalten. Eine der Herausforderungen konzentrierte sich zum Beispiel auf den semantischen Kontext. Zu dieser Art gehörte beispielsweise die Auflösung von „russischer Autor“ zu „Leo Tolstoi“. Das LLM hat fälschlicherweise „russischer Autor“ als Alias für „Leo Tolstoi“ verwendet, was die Notwendigkeit einer Hybridsuche zum Finden der Übereinstimmung negierte.</p><p>Nach mehreren Refaktorierungen, um Probleme wie dieses zu beheben, hatten wir fünf Datensatzstufen, mit denen wir arbeiten konnten. Die Stufen 1–4 waren zunehmend größer und boten mehr Herausforderungstypen. Stufe 5 war der Datensatz der „ultimativen Herausforderung“, der aus den kniffligsten Beispielen aller Herausforderungstypen bestand. Sämtliche Testdaten sind im <a href="https://github.com/jesslm/entity-resolution-lab-public/tree/main/comprehensive_evaluation">umfassenden Auswertungsverzeichnis</a> verfügbar.</p><p>Zur Evaluierung unseres auf Eingabeaufforderungen basierenden Ansatzes zur Entitätsauflösung konzentrierten wir uns auf den Stufe-4-Datensatz. Ein wichtiger Hinweis ist, dass die Bewertung als kontrolliertes Experiment durchgeführt wurde, so dass wir uns auf die Qualität der Entitätsübereinstimmung konzentrieren konnten. Die Daten der Watchlist wurden vorab mit Kontext angereichert, und Entitäten wurden im Voraus aus dem Artikel extrahiert. Dadurch wurde sichergestellt, dass sich die Bewertung auf den Abgleich und nicht auf die Genauigkeit der Extraktion konzentrierte. Dies isoliert die Qualität der Übereinstimmungen; die Gesamtleistung hängt zusätzlich von der Trefferquote bei der Extraktion und der Qualität der Anreicherung ab.</p><h3>Evaluationsdatensatz</h3><p>Der Evaluierungsdatensatz der Stufe 4 bietet einen umfassenden Test der Leistungsfähigkeit des Systems:[1]</p><ul><li><strong>Watchlist-Entitäten:</strong> 66 Entitäten unterschiedlichster Art (Personen, Organisationen, Standorte).</li><li><strong>Testartikel:</strong> 69 Artikel über reale Szenarien zur Auflösung von Entitäten.</li><li><strong>Erwartete Übereinstimmungen:</strong> 206 erwartete Entitätsübereinstimmungen in allen Artikeln.</li><li><strong>Herausforderungstypen: </strong>15 verschiedene Herausforderungstypen, die verschiedene Aspekte der Entitätsauflösung prüfen.</li></ul><p>Die in den Datensätzen enthaltenen Herausforderungstypen sind:</p><ul><li><strong>Spitznamen:</strong> „Bob Smith“ → „Robert Smith“ (sieben Artikel).</li><li><strong>Titel und Ehrenbezeichnungen:</strong> „Dr. Sarah Williams“ → „Sarah Williams“ (fünf Artikel).</li><li><strong>Semantischer Kontext:</strong> „Russischer Autor“ → „Leo Tolstoi“ (acht Artikel).</li><li><strong>Mehrsprachige Namen:</strong> Umgang mit Namen in verschiedenen Skripten (sechs Artikel).</li><li><strong>Geschäftseinheiten:</strong> Variationen von Firmennamen (sieben Artikel).</li><li><strong>Referenzen von Führungskräften: </strong>„Microsoft CEO“ → „Satya Nadella“ (fünf Artikel).</li><li><strong>Politische Führungspersönlichkeiten:</strong> Titelbasierte Referenzen (fünf Artikel).</li><li><strong>Initialen:</strong> „J. Smith“ → „John Smith“ (drei Artikel).</li><li><strong>Varianten der Namensreihenfolge:</strong> Verschiedene Konventionen für die Namensreihenfolge (drei Artikel).</li><li><strong>Abgekürzte Namen:</strong> Teilweise Namensübereinstimmungen (drei Artikel).</li><li><strong>Namensaufteilung:</strong> Namen, die über Text verteilt sind (drei Artikel).</li><li><strong>Fehlende Leerzeichen/Bindestriche:</strong> Formatierungsabweichungen (zwei Artikel).</li><li><strong>Transliteration:</strong> Skriptübergreifender Namensabgleich (zwei Artikel).</li><li><strong>Kombinierte Herausforderungen:</strong> Mehrere Herausforderungen in einem Artikel (sechs Artikel).</li><li><strong>Komplexe Geschäftsbeziehungen:</strong> Hierarchische Geschäftsbeziehungen (fünf Artikel).</li></ul><p>Mal sehen, wie die auf Eingabeaufforderungen basierende Entitätsauflösung funktioniert hat.</p><h3>Gesamtleistung</h3><p>Die Ergebnisse zeigen, dass die LLM-gestützte Übereinstimmungsbewertung vielversprechend ist, aber sie offenbaren auch ein erhebliches Zuverlässigkeitsproblem. Da jedes Kandidatenpaar vom LLM bewertet werden muss, können Fehler im strukturierten Ausgang die Akzeptanz und das Erinnern unterdrücken, selbst wenn der Abruf gut funktioniert.</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Metrik</td><td style="border: 1px solid #ccc; padding: 4px;">Wert</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Präzision</td><td style="border: 1px solid #ccc; padding: 4px;">83,8 %</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Abruf</td><td style="border: 1px solid #ccc; padding: 4px;">62,6 %</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">F1-Score</td><td style="border: 1px solid #ccc; padding: 4px;">71,7 %</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Gesamtanzahl der Übereinstimmungen</td><td style="border: 1px solid #ccc; padding: 4px;">344</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">LLM-Annahmequote</td><td style="border: 1px solid #ccc; padding: 4px;">44,8 %</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Fehlerquote</td><td style="border: 1px solid #ccc; padding: 4px;">30,2 %</td>
                    </tr>
              </tbody>
            </table>
          <h3>Das Problem mit der Fehlerrate</h3><p>Zur Erinnerung: Der erste Schritt im Prototyp besteht darin, mithilfe von Elasticsearch potenzielle Übereinstimmungspaare zu erstellen. Jede dieser potenziellen Übereinstimmungen muss vom LLM bewertet werden. Um all diese Übereinstimmungen effizient zu verarbeiten, fassen wir die LLM-Aufrufe in Batches zusammen. Dies reduziert die API-Kosten und die Latenzzeit, aber es besteht auch ein erhöhtes Risiko, dass der Ausgang fehlerhaftes JSON enthält. Mit zunehmender Batchgröße wird das JSON länger und komplexer, wodurch die Wahrscheinlichkeit steigt, dass der LLM ungültiges JSON generiert. Hier liegt der Ursprung der Fehlerquote von 30 %. In der Bewertung haben wir eine Batch-Größe von fünf Übereinstimmungen pro Anfrage verwendet. Selbst bei dieser konservativen Batchgröße beobachten wir immer noch JSON-Parsing-Fehler, welche die Auswertungsergebnisse erheblich verfälschen.</p><h2>Nächstes Ziel: Optimierung der LLM-Integration</h2><p>Nachdem wir nun Entitäten mithilfe semantischer Suche und LLM-Bewertung abgeglichen haben, verfügen wir über eine vollständige Entitätsauflösungspipeline. Dieser Ansatz führt jedoch einen neuen Ausfallmodus ein, wenn die Einschätzung des Modells richtig ist, sein Ausgang jedoch nicht nutzbar ist. Wir können die LLM-Integration im Hinblick auf höhere Zuverlässigkeit und Kosteneffizienz optimieren. Im nächsten Beitrag werden wir untersuchen, wie Sie Funktionsaufrufe für einen strukturierten Ausgang verwenden können, der garantierte Struktur- und Typsicherheit bietet und gleichzeitig Fehler und Kosten reduziert.</p><h2>Probieren Sie es selbst aus</h2><p>Möchten Sie den Entitätsabgleich in Aktion sehen? Schauen Sie sich das <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">Entitätsabgleich-Notizbuch</a> für eine vollständige Anleitung mit realen Implementierungen, detaillierten Erklärungen und praktischen Beispielen an. Das Notizbuch zeigt Ihnen genau, wie Sie Entitäten mithilfe der dreistufigen Suche, der hybriden Suche mit RRF und der LLM-gestützten Bewertung mit Schlussfolgerungen abgleichen.</p><p><strong>Denken Sie daran:</strong> Dies ist ein pädagogischer Prototyp, der entwickelt wurde, um die Konzepte zu vermitteln. Bei der Entwicklung von Produktionssystemen sollten zusätzliche Faktoren wie Modellauswahl, Kostenoptimierung, Latenzanforderungen, Qualitätsvalidierung, Fehlerbehandlung und Überwachung berücksichtigt werden, die in diesem lernorientierten Prototyp nicht behandelt werden.</p><h2>Anmerkungen</h2><ol><li>Diese Datensätze sind synthetisch und für Bildungszwecke konzipiert; sie nähern sich realen Herausforderungen an, sind aber nicht repräsentativ für eine einzelne Produktionsdomäne.</li></ol>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Die Vektorsuche in Elasticsearch ist bis zu 8-mal schneller als in OpenSearch]]></title>
            <description><![CDATA[Analyse von Benchmarks zum gefilterten Vektorsuchen: OpenSearch und Elasticsearch im Vergleich – und warum die Performance des Vektorsuchens für kontextoptimierte Systeme entscheidend ist.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/opensearch-vs-elasticsearch-filtered-vector-search</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/opensearch-vs-elasticsearch-filtered-vector-search</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Sachin Frayne]]></dc:creator>
            <pubDate>Wed, 25 Feb 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/fb7ee623925ca6696d643e437ce8efe5fe749079-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<h2>Warum die Suchgeschwindigkeit für KI-Agenten und Kontext-Engineering wichtig ist</h2><p>Unsere Benchmarks auf einem 20-Millionen-Dokumentenkorpus zeigen, dass Elasticsearch bei der gefilterten Vektorsuche bis zu 8-mal mehr Durchsatz als OpenSearch liefert und gleichzeitig höhere Recall@100 in den getesteten Konfigurationen erzielt. Kontextgestaltung erfordert mehr als nur einen schnellen Vektorabruf. Teams benötigen außerdem starke Relevanzkontrollen, wie hybride Such- und Filterfunktionen, eine einfache Bedienung und eine vorhersehbare Leistung bei der Iteration von Workflows. Da die Agenten jedoch oft mehrmals pro Anfrage Abfrage-, Auswertungs- und erneute Abfrage-Schleifen durchlaufen, wirkt sich die Latenz bei der Datenabfrage multiplikativ aus, sodass Verbesserungen in diesem Bereich direkt zu einer besseren Reaktionsgeschwindigkeit über den gesamten Prozess hinweg und zu geringeren Kosten führen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d5a52a07773f0942c2baa732dacfe782aac0f415-1600x683.png" style="width: 100%;" /><p>Für das Kontext-Engineering ist der Abruf kein einmaliger Schritt. Agenten und Anwendungen führen wiederholt Schleifen aus, wie z. B. Abrufen → Begründung → Abrufen, um Abfragen zu verfeinern, Fakten zu überprüfen, einen fundierten Kontext zusammenzustellen und Aufgaben zu erledigen. Dieses Muster ist typisch für agentenbasierte Workflows und iterative Retrieval Augmented Generation (RAG). Da der Abruf pro Nutzeranfrage mehrfach aufgerufen werden kann, verzögert sich die Reaktion und/oder erhöht die Infrastrukturkosten.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/54ac476ff20a3cf93484298c9ae47612c12fc110-800x417.png" style="width: 100%;" /><h2>Warum ist die Leistung der Vektorsuche so wichtig?</h2><p></p><p>Stellen Sie sich vor, ein Verkäufer beantwortet die Frage: „Ich brauche einen Handgepäck-Rucksack unter 60 €, in den ein 15-Zoll-Laptop passt, der wasserabweisend ist und bis Freitag geliefert werden kann.“</p><p>In der Produktion gibt der Assistent nur selten eine Vektorabfrage aus und hält dann an. Es führt einen Abrufzyklus aus, um den richtigen Kontext aufzubauen, und jeder Schritt wird typischerweise durch Filter eingeschränkt, wie Verfügbarkeit, Region, Versandversprechen, Markenregeln und Richtlinienberechtigung.</p><p><strong>Schritt 1: Absicht interpretieren und in Einschränkungen übersetzen.</strong></p><p>Der Agent wandelt die Anfrage in strukturierte Filter und eine semantische Abfrage um, wie zum Beispiel:</p><ul><li>Filterkriterien: Auf Lager, lieferbar an die Postleitzahl des Nutzers, Lieferung bis Freitag, Preis unter 60 €, gültiges Angebot</li><li>Vektorabfrage: „Bordrucksack für 15-Zoll-Laptop, wasserabweisend“</li></ul><p><strong>Schritt 2: Kandidaten abrufen und anschließend verfeinern.</strong></p><p>Es wiederholt oft das Abrufen mit Variationen, um gute Übereinstimmungen nicht zu verpassen:</p><ul><li>„Reiserucksack in Kabinengröße mit Laptophülle“</li><li>„wasserabweisender Pendlerrucksack 15 Zoll“</li><li>„ leichtgewichtiger Kabinenrucksack “</li></ul><p>Für jede Abfrage werden die gleichen Berechtigungsfilter verwendet, da das Abrufen irrelevanter oder nicht verfügbarer Elemente eine Verschwendung von Kontext darstellt.</p><p><strong>Schritt 3: Erweitern, um Details zu bestätigen und das Risiko zu reduzieren.</strong></p><p>Der Agent ruft dann erneut ab, um wichtige Attribute zu überprüfen, die die endgültige Antwort beeinflussen:</p><ul><li>Angaben zu Material und Wasserfestigkeit</li><li>Abmessungen und Passform des Laptopfachs</li><li>Rückgabebestimmungen oder Garantiebeschränkungen</li><li>Alternative Optionen, wenn der Bestand gering ist</li></ul><p>Dies ist mehrstufiges Kontext-Engineering: Abrufen, schlussfolgern, abrufen, zusammenstellen.</p><h2>Warum Latenz und Recall für das Kontext-Engineering wichtig sind</h2><p>Diese Interaktionen können Dutzende gefilterter Abrufaufrufe pro Nutzersitzung umfassen. Das macht die Latenz pro Anruf zu einem direkten Multiplikator der End-to-End-Reaktionszeit, und eine niedrige Rückrufrate erzwingt zusätzliche Wiederholungsversuche oder führt dazu, dass der Agent geeignete Elemente übersieht, was die Antwortqualität verschlechtert.</p><p>Fazit: In kontextbasierten Systemen ist die gefilterte approximative Suche nach nächsten Nachbarn (ANN) keine einfache Nachschlageoperation. Sie ist ein wiederholter Vorgang unter Einschränkungen, sodass die Leistung der Vektorsuche sofort in Latenz, Durchsatz und Kosten angezeigt wird, selbst wenn das Large Language Model (LLM) die sichtbarste Komponente ist.</p><h2>Benchmarking</h2><h3>Ergebnisse</h3><p>In Diagramm 2 stellt jeder Punkt eine Testkonfiguration dar. Die besten Ergebnisse werden oben links angezeigt, was eine höhere Erinnerungsrate bei geringerer Latenz bedeutet. Die Ergebnisse von Elasticsearch liegen durchweg näher am oberen linken Rand als die von OpenSearch, was auf eine höhere Geschwindigkeit und Genauigkeit bei gleicher Arbeitslast hinweist.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c50d1df00968cac18149a2799e6242fbe49b66a0-1600x990.png" style="width: 100%;" /><h4>Einige wichtige Einblicke</h4><ul><li><code>s_n_r_value</code>: Kurzschrift für <code>size_numCandidates_rescoreOversample</code> (k und numCandidates in diesen Tests gleich numCandidates gesetzt), zum Beispiel <code>100_500_1</code> bedeutet size=100, numCandidates=500 und k=500, rescore oversample=1</li><li>Erinnerung: Gemessener Recall@100 für diese Konfiguration</li><li>Durchschnittslatenz (ms): Durchschnittliche End-to-End-Latenz pro Abfrage</li><li>Durchsatz: Abfragen pro Sekunde</li><li>Erinnerung %: Relative Verbesserung der Trefferquote von Elasticsearch gegenüber OpenSearch (Elasticsearch minus OpenSearch) / OpenSearch</li><li>Latenz Xs: Die durchschnittliche Latenz von OpenSearch dividiert durch die durchschnittliche Latenz von Elasticsearch</li><li>Durchsatz Xs: Elasticsearch-Durchsatz geteilt durch den OpenSearch-Durchsatz</li></ul>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Engine</td><td style="border: 1px solid #ccc; padding: 4px;">`s_n_r_value`</td><td style="border: 1px solid #ccc; padding: 4px;">Abruf</td><td style="border: 1px solid #ccc; padding: 4px;">Durchschnittliche Latenz (ms)</td><td style="border: 1px solid #ccc; padding: 4px;">Durchsatz</td><td style="border: 1px solid #ccc; padding: 4px;">Erinnerung %</td><td style="border: 1px solid #ccc; padding: 4px;">Latenz Xs</td><td style="border: 1px solid #ccc; padding: 4px;">Durchsatz Xs</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_250_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,7704</td><td style="border: 1px solid #ccc; padding: 4px;">25</td><td style="border: 1px solid #ccc; padding: 4px;">534,75</td><td style="border: 1px solid #ccc; padding: 4px;">9,70 %</td><td style="border: 1px solid #ccc; padding: 4px;">2,28</td><td style="border: 1px solid #ccc; padding: 4px;">1,91</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_250_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,7023</td><td style="border: 1px solid #ccc; padding: 4px;">57,08</td><td style="border: 1px solid #ccc; padding: 4px;">279,58</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_500_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,8577</td><td style="border: 1px solid #ccc; padding: 4px;">25,42</td><td style="border: 1px solid #ccc; padding: 4px;">524,14</td><td style="border: 1px solid #ccc; padding: 4px;">7,20 %</td><td style="border: 1px solid #ccc; padding: 4px;">2,4</td><td style="border: 1px solid #ccc; padding: 4px;">2</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_500_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,8001</td><td style="border: 1px solid #ccc; padding: 4px;">60,9</td><td style="border: 1px solid #ccc; padding: 4px;">262,12</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_750_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,8947</td><td style="border: 1px solid #ccc; padding: 4px;">29,67</td><td style="border: 1px solid #ccc; padding: 4px;">528,09</td><td style="border: 1px solid #ccc; padding: 4px;">5,72 %</td><td style="border: 1px solid #ccc; padding: 4px;">2,25</td><td style="border: 1px solid #ccc; padding: 4px;">2,21</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_750_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,8463</td><td style="border: 1px solid #ccc; padding: 4px;">66,76</td><td style="border: 1px solid #ccc; padding: 4px;">239,11</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_1000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9156</td><td style="border: 1px solid #ccc; padding: 4px;">29,65</td><td style="border: 1px solid #ccc; padding: 4px;">534,5</td><td style="border: 1px solid #ccc; padding: 4px;">4,66 %</td><td style="border: 1px solid #ccc; padding: 4px;">2,46</td><td style="border: 1px solid #ccc; padding: 4px;">2,44</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_1000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,8748</td><td style="border: 1px solid #ccc; padding: 4px;">72,88</td><td style="border: 1px solid #ccc; padding: 4px;">219,01</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_1500_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9386</td><td style="border: 1px solid #ccc; padding: 4px;">31,84</td><td style="border: 1px solid #ccc; padding: 4px;">497,3</td><td style="border: 1px solid #ccc; padding: 4px;">3,38 %</td><td style="border: 1px solid #ccc; padding: 4px;">2,71</td><td style="border: 1px solid #ccc; padding: 4px;">2,68</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_1500_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9079</td><td style="border: 1px solid #ccc; padding: 4px;">86,16</td><td style="border: 1px solid #ccc; padding: 4px;">185,4</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_2000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9507</td><td style="border: 1px solid #ccc; padding: 4px;">34,69</td><td style="border: 1px solid #ccc; padding: 4px;">457,2</td><td style="border: 1px solid #ccc; padding: 4px;">2,57 %</td><td style="border: 1px solid #ccc; padding: 4px;">2,98</td><td style="border: 1px solid #ccc; padding: 4px;">2,96</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_2000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9269</td><td style="border: 1px solid #ccc; padding: 4px;">103,36</td><td style="border: 1px solid #ccc; padding: 4px;">154,55</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_2500_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9582</td><td style="border: 1px solid #ccc; padding: 4px;">37,9</td><td style="border: 1px solid #ccc; padding: 4px;">418,43</td><td style="border: 1px solid #ccc; padding: 4px;">1,99 %</td><td style="border: 1px solid #ccc; padding: 4px;">3,28</td><td style="border: 1px solid #ccc; padding: 4px;">3,26</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_2500_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9395</td><td style="border: 1px solid #ccc; padding: 4px;">124,29</td><td style="border: 1px solid #ccc; padding: 4px;">128,53</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_3000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9636</td><td style="border: 1px solid #ccc; padding: 4px;">41,86</td><td style="border: 1px solid #ccc; padding: 4px;">379,4</td><td style="border: 1px solid #ccc; padding: 4px;">1,62 %</td><td style="border: 1px solid #ccc; padding: 4px;">3,46</td><td style="border: 1px solid #ccc; padding: 4px;">3,44</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_3000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9482</td><td style="border: 1px solid #ccc; padding: 4px;">144,67</td><td style="border: 1px solid #ccc; padding: 4px;">110,34</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_4000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9705</td><td style="border: 1px solid #ccc; padding: 4px;">50,28</td><td style="border: 1px solid #ccc; padding: 4px;">316,21</td><td style="border: 1px solid #ccc; padding: 4px;">1,06 %</td><td style="border: 1px solid #ccc; padding: 4px;">3,87</td><td style="border: 1px solid #ccc; padding: 4px;">3,85</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_4000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9603</td><td style="border: 1px solid #ccc; padding: 4px;">194,36</td><td style="border: 1px solid #ccc; padding: 4px;">82,22</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_5000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9749</td><td style="border: 1px solid #ccc; padding: 4px;">58,77</td><td style="border: 1px solid #ccc; padding: 4px;">270,91</td><td style="border: 1px solid #ccc; padding: 4px;">0,73 %</td><td style="border: 1px solid #ccc; padding: 4px;">4,43</td><td style="border: 1px solid #ccc; padding: 4px;">4,41</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_5000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9678</td><td style="border: 1px solid #ccc; padding: 4px;">260,33</td><td style="border: 1px solid #ccc; padding: 4px;">61,38</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_6000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9781</td><td style="border: 1px solid #ccc; padding: 4px;">66,75</td><td style="border: 1px solid #ccc; padding: 4px;">238,59</td><td style="border: 1px solid #ccc; padding: 4px;">0,52 %</td><td style="border: 1px solid #ccc; padding: 4px;">4,91</td><td style="border: 1px solid #ccc; padding: 4px;">4,89</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_6000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,973</td><td style="border: 1px solid #ccc; padding: 4px;">327,44</td><td style="border: 1px solid #ccc; padding: 4px;">48,81</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_7000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9804</td><td style="border: 1px solid #ccc; padding: 4px;">74,64</td><td style="border: 1px solid #ccc; padding: 4px;">213,49</td><td style="border: 1px solid #ccc; padding: 4px;">0,38 %</td><td style="border: 1px solid #ccc; padding: 4px;">5,28</td><td style="border: 1px solid #ccc; padding: 4px;">5,27</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_7000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9767</td><td style="border: 1px solid #ccc; padding: 4px;">394,24</td><td style="border: 1px solid #ccc; padding: 4px;">40,53</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_8000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9823</td><td style="border: 1px solid #ccc; padding: 4px;">82,28</td><td style="border: 1px solid #ccc; padding: 4px;">193,59</td><td style="border: 1px solid #ccc; padding: 4px;">0,27 %</td><td style="border: 1px solid #ccc; padding: 4px;">6,86</td><td style="border: 1px solid #ccc; padding: 4px;">6,83</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_8000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9797</td><td style="border: 1px solid #ccc; padding: 4px;">564,14</td><td style="border: 1px solid #ccc; padding: 4px;">28,33</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_9000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9837</td><td style="border: 1px solid #ccc; padding: 4px;">90,08</td><td style="border: 1px solid #ccc; padding: 4px;">176,96</td><td style="border: 1px solid #ccc; padding: 4px;">0,16 %</td><td style="border: 1px solid #ccc; padding: 4px;">7,63</td><td style="border: 1px solid #ccc; padding: 4px;">7,61</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_9000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9821</td><td style="border: 1px solid #ccc; padding: 4px;">687,25</td><td style="border: 1px solid #ccc; padding: 4px;">23,25</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_10000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9848</td><td style="border: 1px solid #ccc; padding: 4px;">97,64</td><td style="border: 1px solid #ccc; padding: 4px;">163,31</td><td style="border: 1px solid #ccc; padding: 4px;">0,08 %</td><td style="border: 1px solid #ccc; padding: 4px;">8,38</td><td style="border: 1px solid #ccc; padding: 4px;">8,36</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_10000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,984</td><td style="border: 1px solid #ccc; padding: 4px;">818,64</td><td style="border: 1px solid #ccc; padding: 4px;">19,53</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
              </tbody>
            </table>
          <p>Zum Beispiel beträgt OpenSearch bei <code>100_9000_1</code> im Durchschnitt 687 Millisekunden pro Abruf gegenüber 90 Millisekunden bei Elasticsearch, und in einer 10-Schritte-Abrufschleife entspricht das etwa 10 × (687 - 90) = sechs Sekunden zusätzlicher Wartezeit. </p><p><a href="https://github.com/elastic/competitive-benchmarking-studies/tree/main/es-9.3-vs-os-3.5-vector-search/jingra/results/20260220">Die vollständigen Ergebnisse</a> ansehen.</p><h3>Methodik</h3><p>Wir verwendeten Python, um die Anfragen zu senden und die Antwortzeiten sowie weitere Statistiken zu verfolgen. Wir haben die folgenden Anfragen an die Engines gesendet. Bedenken Sie, dass die Leistungsfähigkeit jeder Vektorsuchmaschine davon abhängt, wie Sie ihre Kernparameter einstellen: wie viele Kandidaten berücksichtigt werden sollen, wie aggressiv die Neubewertung erfolgen soll und wie viel Kontext zurückgegeben werden soll. Diese Einstellungen wirken sich direkt sowohl auf die Trefferquote (die Wahrscheinlichkeit, die richtige Antwort zu finden) als auch auf die Latenz (wie schnell Sie Ergebnisse erzielen) aus.</p><p>In unseren Benchmarks verwendeten wir die gleichen Kandidaten-, Rescore- und Ergebnisgrößeneinstellungen, die man typischerweise in einer agentenbasierten Abrufschleife anpasst, und wir maßen, wie Elasticsearch unter dieser Arbeitslast abschneidet. Anschließend führten wir OpenSearch mit denselben Einstellungen als Referenz durch.</p><p>OpenSearch</p><pre><code>GET <INDEX_NAME>/_search
{
  "query": {
    "knn": {
      "<DENSE_VECTOR_FIELD_NAME>": {
        "vector": [...],
        "k": <NUMBER_OF_CANDIDATES>,
        "method_parameters": {
          "ef_search": <NUMBER_OF_CANDIDATES>
        },
        "rescore": {
          "oversample_factor": <OVERSAMPLE>
        },
        "filter": {
          <SOME_FILTER>
        }
      }
    }
  },
  "size": <RESULT_SIZE>,
  "_source": {
    "excludes": [
      "<DENSE_VECTOR_FIELD_NAME>"
    ]
  }
}</code></pre><ul><li><code>&quot;size&quot;: &lt;RESULT_SIZE&gt;</code>: Anzahl der an den Client zurückgegebenen Treffer. In diesem Benchmark ist die Ergebnisgröße 100, um Recall@100 zu berechnen.</li><li><code>&quot;k&quot;: &lt;NUMBER_OF_CANDIDATES&gt;</code>: Die Anzahl der nächstgelegenen Nachbarkandidaten.</li><li><code>&quot;ef_search&quot;: &lt;NUMBER_OF_CANDIDATES&gt;</code>: Die Anzahl der zu untersuchenden Vektoren.</li><li><code>&quot;oversample_factor&quot;: &lt;OVERSAMPLE&gt;</code>: Wie viele Kandidatenvektoren werden abgerufen, bevor das Rescoring erfolgt.</li></ul><p>Elasticsearch</p><pre><code>GET <INDEX_NAME>/_search
{
  "query": {
    "knn": {
      "field": "<DENSE_VECTOR_FIELD_NAME>",
      "query_vector": [...],
      "k": <NUMBER_OF_CANDIDATES>,
      "num_candidates": <NUMBER_OF_CANDIDATES>,
      "rescore_vector": {
        "oversample": <OVERSAMPLE>
      },
      "filter": {
        <SOME_FILTER>
      }
    }
  },
  "size": <RESULT_SIZE>,
  "_source": {
    "excludes": [
      "<DENSE_VECTOR_FIELD_NAME>"
    ]
  }
}</code></pre><ul><li><code>&quot;size&quot;: &lt;RESULT_SIZE&gt;</code>: Anzahl der an den Client zurückgegebenen Treffer. In diesem Benchmark ist die Ergebnisgröße 100, um Recall@100 zu berechnen.</li><li><code>&quot;k&quot;: &lt;NUMBER_OF_CANDIDATES&gt;</code>: Anzahl der nächsten Nachbarn, die von jedem Shard zurückgegeben werden sollen.</li><li><code>&quot;num_candidates&quot;: &lt;NUMBER_OF_CANDIDATES&gt;</code>: Anzahl der zu berücksichtigenden nächsten Nachbarn pro Shard bei der <code>knn</code> -Suche.</li><li><code>&quot;oversample&quot;: &lt;OVERSAMPLE&gt;</code>: Wie viele Kandidatenvektoren werden abgerufen, bevor das Rescoring erfolgt.</li></ul><p>Beispiel</p><p><code>Knn</code> Die Abfrage (<code>100_500_1</code>) würde wie folgt lauten:</p><p>OpenSearch</p><pre><code>GET search_catalog_128/_search
{
  "query": {
    "knn": {
      "search_catalog_embedding": {
        "vector": [...],
        "k": 500,
        "method_parameters": {
          "ef_search": 500
        },
        "rescore": {
          "oversample_factor": 1
        },
        "filter": {
          "term": {
            "valid": true
          }
        }
      }
    }
  },
  "size": 100,
  "_source": {
    "excludes": [
      "search_catalog_embedding"
    ]
  }
}</code></pre><p>Elasticsearch</p><pre><code>GET search_catalog_128/_search
{
  "query": {
    "knn": {
      "field": "search_catalog_embedding",
      "query_vector": [...],
      "k": 500,
      "num_candidates": 500,
      "rescore_vector": {
        "oversample": 1
      },
      "filter": {
        "term": {
          "valid": true
        }
      }
    }
  },
  "size": 100,
  "_source": {
    "excludes": [
      "search_catalog_embedding"
    ]
  }
}</code></pre><p>Die vollständige Konfiguration, zusammen mit Terraform-Skripten, Kubernetes-Manifesten und dem Benchmarking-Code, ist in diesem <a href="https://github.com/elastic/competitive-benchmarking-studies">Repository</a> im Ordner <a href="https://github.com/elastic/competitive-benchmarking-studies/tree/main/es-9.3-vs-os-3.5-vector-search">es-9.3-vs-os-3.5-vector-search</a> verfügbar.</p><h3>Cluster-Einrichtung</h3><p>Wir führten unsere Tests auf sechs e2-Standard-16-Cloud-Servern durch, jeder mit 16 vCPUs und 64 GB RAM. Auf jedem Server haben wir 15 vCPUs und 56 GB RAM jedem Kubernetes-Pod zugewiesen, der den Suchmaschinen-Node ausführt, wobei 28 GB für den JVM-Heap reserviert waren.</p><p>Die Cluster liefen mit Elasticsearch 9.3.0 und OpenSearch 3.5.0 (Lucene 10.3.2). Da beide Systeme in diesem Benchmark die gleiche Lucene-Version verwenden, können die beobachteten Unterschiede im Durchsatz und in der Latenz nicht allein Lucene zugeschrieben werden, sondern spiegeln vielmehr Unterschiede in der Art und Weise wider, wie die einzelnen Engines die gefilterte k-nächste-Nachbarn-Suche (kNN) und das Rescoring integrieren und ausführen. Wir haben einen einzelnen Index mit drei Primäre Shards und einem Replikat verwendet (also insgesamt 6 Shards, einer pro Node).</p><p>Wir verwendeten außerdem einen separaten Server in derselben Region, um den Benchmark-Client auszuführen und Zeitstatistiken zu erfassen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f81002c9186e4c2d3e92f49d72418fee9860fc5e-761x401.png" style="width: 100%;" /><h3>Der Datensatz</h3><p></p><p>Für diesen Benchmark verwendeten wir einen umfangreichen Katalog-Embedding-Datensatz im E-Commerce-Stil mit 20 Millionen Dokumenten, der die reale gefilterte Vektorsuche in großem Umfang widerspiegeln soll.</p><p></p><p>Jedes Dokument stellt einen Katalogartikel dar und beinhaltet:</p><p></p><ul><li>Eine 128-dimensionale Dichtevektoreinbettung, die für das ungefähre kNN-Abrufverfahren verwendet wird.</li><li>Strukturierte Metadatenfelder, die zur Filterung verwendet werden (zum Beispiel die Gültigkeit und Verfügbarkeit von Artikeln sowie andere Katalogbeschränkungen), die das gemeinsame Produktionsmuster ermöglichen, die nächstgelegenen Nachbarn abzurufen, jedoch nur innerhalb einer zulässigen Teilmenge.</li></ul><p></p><p>Wir haben diesen Datensatz gewählt, weil er die Kernleistungsherausforderung widerspiegelt, die wir bei agentenbasierten und RAG-Systemen im Produktiveinsatz beobachten: Vektorähnlichkeit allein reicht nicht aus, die Suche wird häufig durch Filter eingeschränkt, und das System muss unter diesen Einschränkungen eine hohe Trefferquote bei gleichzeitig niedriger Latenz pflegen. Im Vergleich zu kleineren QA-Datensätzen spiegelt ein Korpus von 20 Millionen Dokumenten auch besser den Umfang und den Kandidatendruck wider, denen gefilterte ANN-Systeme in der Praxis ausgesetzt sind.</p><h2>Fazit</h2><p>In modernen KI-Architekturen, insbesondere solchen, die auf Kontext-Engineering aufbauen, ist die Geschwindigkeit der Vektorsuche kein unbedeutendes Implementierungsdetail. Sie ist ein Multiplikator. Wenn Agenten und Workflows durch Abrufen → Verarbeiten → Abrufen iterieren, beeinflusst die Abrufleistung direkt die End-to-End-Latenz, den Durchsatz und die Qualität des in das Modell eingespeisten Kontexts.</p><p>In unseren Benchmarks lieferte Elasticsearch konstant einen höheren Abruf bei geringerer Latenz als OpenSearch in Szenarien, in denen die Korrektheit vom Abruf des richtigen Dokuments und nicht nur eines ähnlichen Vektors abhängt. An einem kontrollierten Datensatz ist der Unterschied deutlich, und in der Produktion akkumulieren sich diese Gewinne über große Volumina von Abrufaufrufen, wodurch die Reaktionsfähigkeit verbessert, der Kapazitätsspielraum erhöht und die Infrastrukturkosten reduziert werden.</p><h3>Weitere Lektüre</h3><ol><li><a href="https://www.elastic.co/search-labs/blog/context-engineering-overview">Was ist Context Engineering?</a></li><li><a href="https://www.elastic.co/search-labs/blog/series/context-engineering-hybrid-search-evolution">Die Entwicklung der hybriden Suche und des Kontext-Engineerings</a></li><li><a href="https://www.elastic.co/search-labs/blog/context-engineering-relevance-ai-agents-elasticsearch">Der Einfluss der Relevanz auf das Kontext-Engineering für KI-Agenten</a></li></ol>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[jina-embeddings-v5-text: Kompakte, hochmoderne Texteinbettungen für Suchen und intelligente Anwendungen]]></title>
            <description><![CDATA[Einführung der jina-embeddings-v5-text-Modelle, darunter jina-embeddings-v5-text-small und jina-embeddings-v5-text-nano, sowie eine Erklärung zur Nutzung dieser mehrsprachigen Einbettungsmodelle über den Elastic Inference Service (EIS).]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/jina-embeddings-v5-text</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/jina-embeddings-v5-text</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Scott Martens, Sofia Vasileva]]></dc:creator>
            <pubDate>Mon, 23 Feb 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e98dd30ab925b4bb32830228d71a1a51d02a0917-1600x840.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Jina AI und Elastic bringen <code>jina-embeddings-v5-text</code> heraus, eine Familie neuer, leistungsstarker, kompakter Texteinbettungsmodelle mit modernster Leistung für Modelle vergleichbarer Größe über alle wichtigen Aufgabentypen hinweg.</p><p>Die Familie umfasst zwei Modelle:</p><ul><li><code>jina-embeddings-v5-text-small</code></li><li><code>jina-embeddings-v5-text-nano</code></li></ul><p>Diese Modelle sind das erfolgreiche Ergebnis eines innovativen neuen Trainingsverfahrens zum Einbetten von Modellen. Beide übertreffen um ein Vielfaches größere Modelle. Sie sparen Speicherplatz und Rechenressourcen und reagieren schneller auf Anfragen.</p><p>Das <code>jina-embeddings-v5-text-small</code>-Modell verfügt über 677 Millionen Parameter, unterstützt ein 32.768-Token-Eingangskontextfenster und erzeugt standardmäßig 1.024-Dimensionseinbettungen.</p><p><code>jina-embeddings-v5-text-nano</code> ist nur etwa ein Drittel so groß wie sein Pendant, mit 239 Mio. Parametern und einem Eingangskontextfenster mit 8192 Token, was schlanke Einbettungen mit 768 Dimensionen ergibt.</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Modellname</td><td style="border: 1px solid #ccc; padding: 4px;">Gesamtgröße</td><td style="border: 1px solid #ccc; padding: 4px;">Größe des Eingangskontextfensters</td><td style="border: 1px solid #ccc; padding: 4px;">Einbettungsgröße</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">jina-v5-text-small</td><td style="border: 1px solid #ccc; padding: 4px;">677 Mio. Parameter</td><td style="border: 1px solid #ccc; padding: 4px;">32.768 Token</td><td style="border: 1px solid #ccc; padding: 4px;">1024 Dimensionen</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">jina-v5-text-nano</td><td style="border: 1px solid #ccc; padding: 4px;">239 Mio. Parameter</td><td style="border: 1px solid #ccc; padding: 4px;">8192 Tokens</td><td style="border: 1px solid #ccc; padding: 4px;">768 Dimensionen</td>
                    </tr>
              </tbody>
            </table>
          <p>Diese beiden Modelle sind hinsichtlich der Gesamtleistung des MMTEB (<a href="https://huggingface.co/spaces/mteb/leaderboard">Multilingual MTEB</a>)-Benchmarks die besten verfügbaren. Unter den Modellen mit weniger als 500 Millionen Parametern ist <code>jina-embeddings-v5-text-nano</code> trotz weniger als 250 Millionen Parametern der beste Performer, und das <code>jina-embeddings-v5-text-small</code>-Modell ist führend unter mehrsprachigen Einbettungsmodellen mit weniger als 750 Millionen Parametern.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d97126285fa196c3045b1913b7754e4b186c1e4c-1300x1100.png" style="width: 100%;" /><p>Diese Modelle sind über den Elastic Inference Service (EIS), über eine Online-API und für lokales Hosting verfügbar. Anweisungen zum Zugriff auf <code>jina-embeddings-v5-text</code>-Modelle finden Sie im Abschnitt „<strong>Erste Schritte</strong>“ weiter unten.</p><p>Einbettungsmodelle und semantisches Indexieren erhöhen die Genauigkeit von Suchalgorithmen drastisch, haben aber auch eine Vielzahl anderer Einsatzmöglichkeiten für Aufgaben, die semantische Ähnlichkeit und Bedeutungsextraktion betreffen, zum Beispiel:</p><ul><li>Doppelte Texte finden.</li><li>Paraphrasen und Übersetzungen erkennen.</li><li>Themenfindung.</li><li>Empfehlungssysteme.</li><li>Stimmungs- und Absichtsanalyse.</li><li>Spamfilterung.</li><li>Und vieles mehr.</li></ul><h2><strong>Features</strong></h2><p>Diese neue Modellfamilie verfügt über eine Reihe von Features zur Verbesserung der Relevanz und Kostensenkung.</p><h3>Aufgabenoptimierung</h3><p>Wir haben die <code>jina-embeddings-v5-text</code>-Modelle für vier breitgefächerte Aufgabentypen optimiert:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Aufgabe</td><td style="border: 1px solid #ccc; padding: 4px;">Beispiel-Anwendungsfälle</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Abruf</td><td style="border: 1px solid #ccc; padding: 4px;">Suche mit natürlichsprachlichen Abfragen und Abrufen der relevantesten Treffer in einer Sammlung von Dokumenten.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Textabgleich</td><td style="border: 1px solid #ccc; padding: 4px;">Semantische Ähnlichkeiten, Deduplizierung, Paraphrasierungs- und Übersetzungsabgleich und mehr.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Clustering</td><td style="border: 1px solid #ccc; padding: 4px;">Themenfindung und automatische Organisation von Dokumentensammlungen.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Klassifizierung</td><td style="border: 1px solid #ccc; padding: 4px;">Kategorisierung von Dokumenten, Stimmungs- und Absichtsanalysen, ähnliche Aufgaben.</td>
                    </tr>
              </tbody>
            </table>
          <p>Die Optimierung für eine Aufgabe bedeutet in der Regel, dass man bei einer anderen Aufgabe Kompromisse eingehen muss. Daher bieten die meisten Einbettungsmodelle nur für eine Art von Aufgabe eine wettbewerbsfähige Leistung. <code>jina-embeddings-v5-text</code>-Modelle können sich hingegen auf alle vier Bereiche spezialisieren, ohne Kompromisse einzugehen, indem sie aufgabenspezifische <a href="https://arxiv.org/abs/2106.09685">Low-Rank Adaptation (LoRA)-Adapter</a> trainieren.</p><p>LoRA-Adapter sind gewissermaßen Plugins für KI-Modelle, die ihr Verhalten drastisch ändern, während sie die Gesamtgröße nur geringfügig erhöhen. Anstatt für jede Aufgabe ein komplettes Modell mit Hunderten Millionen Parametern zu verwenden, ermöglicht die <code>jina-embeddings-v5-text</code>-Modellfamilie die Nutzung eines einzigen Modells mit einem kompakten LoRA-Adapter für jede Aufgabe. Dadurch werden Speicher, Speicherplatz und Inferenzkosten gespart.</p><h3>Kürzen von Einbettungen</h3><p>Wir haben die <code>jina-embeddings-v5-text</code>-Modelle mit <a href="https://arxiv.org/abs/2205.13147">Matryoshka Representation Learning</a> trainiert, das es Ihnen ermöglicht, Ihre Einbettungen auf kleinere Größen zu reduzieren, ohne die Qualität wesentlich zu beeinträchtigen.</p><p>Standardmäßig erzeugt <code>jina-embeddings-v5-text-small</code> Einbettungsvektoren in 1.024 Dimensionen, die jeweils durch eine 16-Bit-Zahl dargestellt werden, sodass jede Einbettung 2 KB groß ist. Für eine große Sammlung von Dokumenten kann dies eine Menge Daten zum Speichern bedeuten, und die Suche in einer Vektordatenbank voller Einbettungen fällt proportional zur Größe der Datenbank sowie zur Anzahl der Dimensionen aus, die jeder gespeicherte Vektor enthält.</p><p>Man kann aber einfach die Größe der Einbettungen halbieren (512 der 1.024 Dimensionen weglassen) und so den Speicherplatz halbieren und gleichzeitig die Suchgeschwindigkeit verdoppeln. Dies hat Auswirkungen auf die Leistung. Das Entfernen von Informationen verringert die Präzision. Aber wie der Graph unten zeigt, verringert sich die Leistung selbst dann nur geringfügig, wenn Sie die Hälfte der Einbettung weglassen:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f11969682c81bcf22b5b29a6204ba6ec40471b3d-900x700.png" style="width: 100%;" /><p>Solange Ihre Einbettungen mindestens 256 Dimensionen haben, sollte der Präzisionsverlust relativ gering bleiben. Unterhalb dieses Niveaus nehmen Relevanz und Genauigkeit jedoch schnell ab.</p><p>Das Kürzen von Einbettungen auf diese Weise ermöglicht es Nutzern, ihre eigenen Kompromisse zwischen Genauigkeit und Rechenkosten zu bestimmen. Es bietet Ihnen die nötigen Tools, um große Effizienzgewinne und erhebliche Kosteneinsparungen aus Ihrer Such-KI zu erzielen.</p><h3>Robuste Quantisierung</h3><p><em>Quantisierung </em>ist eine weitere Möglichkeit, um die Größe von Einbettungen zu reduzieren. Anstatt einen Teil jeder Einbettung zu entfernen, reduziert die Quantisierung die Präzision der Zahlen in der Einbettung. Die <code>jina-embeddings-v5-text</code>-Modelle generieren Einbettungen mit 16-Bit-Zahlen, doch diese Zahlen können abgerundet werden, wodurch ihre Präzision und die Anzahl der Bits, die zu ihrer Speicherung nötig sind, reduziert werden. Im Extremfall können wir jede Zahl auf ein Bit (0 oder 1) reduzieren und die standardmäßigen 1.024-dimensionalen Einbettungen von <code>jina-embeddings-v5-text</code>von 2 Kilobyte auf 128 Byte komprimieren, was einer Reduzierung um 94 % durch alleinige binäre Quantisierung entspricht. Genau wie bei der Kürzung führt dies zu großen Einsparungen bei Speicherplatz und Rechenkosten. Jedoch sorgt die Quantisierung, ähnlich wie eine Kürzung, dafür, dass Einbettungen weniger genau ausfallen.</p><p>Wir haben die <code>jina-embeddings-v5-text</code> -Modelle darauf trainiert, mit <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/bbq">Elasticsearchs Better Binary Quantization</a> zu arbeiten, indem wir diesen Genauigkeitsverlust minimieren. Benchmark-Tests binarisierter Einbettungen aus diesen Modellen zeigen eine Leistung, die fast der ihrer nicht-binarisierten Äquivalente entspricht. Im <a href="https://arxiv.org/abs/2602.15547">technischen Bericht</a> finden Sie detaillierte Ablationsstudien zur Binarisierungsleistung.</p><h3>Mehrsprachige Leistung</h3><p>Viele Einbettungsmodelle sind mehrsprachig, weil sie auf Materialien trainiert wurden, die eine große Anzahl von Sprachen enthalten. Das bedeutet jedoch nicht, dass sie in allen unterstützten Sprachen gleich gut funktionieren.</p><p>Wir haben 211 Sprachen im MMTEB-Mehrsprachen-Benchmark identifiziert und sie getrennt, um unsere Modelle mit ähnlichen Modellen auf Sprachbasis vergleichen zu können. Die folgende Abbildung fasst unsere Ergebnisse als Heatmap zusammen. Jedes Feld stellt eine Sprache dar (identifiziert durch ihren ISO-639-Code). Je grüner es ist, desto besser hat das Modell im Vergleich zum Durchschnitt ähnlicher Modelle abgeschnitten:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/852ac5d0f1977bb0c1124d87f8863a9bb94eb7da-1600x765.png" style="width: 100%;" /><p>Obwohl die Genauigkeit zwischen Sprachen variiert, sind die <code>jina-embeddings-v5-text</code>-Modelle in den meisten Sprachen weltweit auf dem neuesten Stand der Technik oder nahezu so weit.</p><p>Details zur mehrsprachigen Leistung finden Sie im<a href="https://arxiv.org/abs/2602.15547"> technischen Bericht</a> <a href="https://arxiv.org/abs/2602.15547"><code>jina-embeddings-v5-text</code></a>.</p><h2><strong>Jina in Elastic: Hochmoderne native KI für die Suche</strong></h2><p>Mit <code>jina-embeddings-v5-text</code>-Modellen auf EIS können Sie leistungsstarke, mehrsprachige Einbettungsmodelle nativ in <a href="https://www.elastic.co/de/elasticsearch">Elasticsearch</a> ausführen, mit vollständig verwalteter, GPU-beschleunigter Inferenz und ohne Infrastruktur zur Bereitstellung oder Skalierung. <code>jina-embeddings-v5-text</code>-Modelle erweitern den wachsenden EIS-Modellkatalog mit kompakten, mehrsprachigen Modellen, die von den neuesten Entwicklungen im Bereich der KI angetrieben werden. Diese Modelle weisen eine herausragende Leistung bei der Informationswiedergewinnung und Standard-Datenanalyse-Benchmarks auf und bieten eine unübertroffene, weltweite Unterstützung in mehreren Sprachen.</p><p>Mit zwei Modellen in deutlich unterschiedlichen Größen können die Nutzer entscheiden, welches am besten zu ihren Anwendungsbereichen und ihrem Budget passt. Darüber hinaus bieten <code>jina-embeddings-v5-text </code>-Modelle mit robusten Einbettungen, die auch bei Verkleinerung der Größe oder Quantisierung auf eine geringere Genauigkeit leistungsfähig bleiben, Möglichkeiten für weitere konkrete Einsparungen bei Speicher- und Rechenkosten sowie bei der Verarbeitungslatenz.</p><p>Mit der <code>jina-embeddings-v5-text</code>-Familie, Jina Reranker und Elastics schneller Vektor- und BM25-Suche haben Nutzer nun Zugang zu einer <a href="https://www.elastic.co/docs/solutions/search/hybrid-search">End-to-End-Hybridsuche</a> von Elastic. Wenn Sie die relevantesten Ergebnisse benötigen – sei es für Retrieval Augmented Generation (RAG) Pipelines, Suchanwendungen oder Datenanalysen – bietet Elastic mit den Such-KI-Modellen von Jina solide und kosteneffiziente Qualität.</p><h2><strong>Erste Schritte</strong></h2><p>Die <code>jina-embeddings-v5-text</code>-Modelle sind vollständig in <a href="https://www.elastic.co/docs/explore-analyze/elastic-inference/eis">EIS</a> integriert und können durch Einstellen des <strong><code>type</code> </strong>Felds für<strong> <code>semantic_text</code></strong> verwendet werden, um Ihren Index zu erstellen und das Modell (<code>jina-embeddings-v5-text-small</code> oder <code>jina-embeddings-v5-text-nano</code>) im <code>inference_id</code><strong> </strong>Feld zu spezifizieren, wie an diesem Beispiel zu sehen ist:</p><pre><code>PUT multilingual-semantic-index
{
  "mappings": {
    "properties": {
      "content": {
        "type": "semantic_text",
        "inference_id": ".jina-embeddings-v5-text-small"
      }
    }
  }
}

# Ingest data about France
POST multilingual-semantic-index/_doc
{
  "content": "The capital of France is Paris"}

GET multilingual-semantic-index/_search
{
  "query": {
    "semantic": {
      "field": "content",
      "query": "What is the French capital?"
    }
  }
}</code></pre><p><a href="https://www.elastic.co/de/elasticsearch">Elasticsearch</a> wählt während des Indexierens und des Abrufs automatisch den entsprechenden LoRA-Adapter aus. Die Einbettungsdimensionen (siehe Abschnitt <strong>„Kürzen von Einbettungen“</strong> oben) können festgelegt werden, wenn <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put">ein benutzerdefinierter Inferenz-Endpoint erstellt wird</a>.</p><p>In der <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/dense-vector">Elasticsearch-Dokumentation</a> finden Sie weitere Informationen zur Nutzung von<strong><code>jina-embeddings-v5-text</code> </strong>Modellen.</p><h2><strong>Weitere Informationen</strong></h2><p>Um mehr über <code>jina-embeddings-v5-text</code>-Modelle zu erfahren, lesen Sie die <a href="https://jina.ai/news/jina-embeddings-v5-text-distilling-4b-quality-into-sub-1b-multilingual-embeddings/">Versionshinweise im Jina AI-Blog</a> und den <a href="https://arxiv.org/abs/2602.15547">technischen Bericht</a> mit detaillierteren technischen Informationen zur Leistung und zum innovativen neuen Trainingsverfahren von Jina AI. Informationen zum lokalen Herunterladen und Betrieb dieser Modelle finden Sie auf der Seite der <a href="https://huggingface.co/collections/jinaai/jina-embeddings-v5-text"><code>jina-embeddings-v5-text</code></a>-<a href="https://huggingface.co/collections/jinaai/jina-embeddings-v5-text">Sammlung</a> auf Hugging Face.</p><p>Die Jina AI-Modelle stehen unter einer <a href="https://spdx.org/licenses/CC-BY-NC-4.0">CC-BY-NC-4.0-Lizenz</a> zur Verfügung. Sie können sie also kostenlos herunterladen und ausprobieren. Für die kommerzielle Nutzung wenden Sie sich bitte an den <a href="https://www.elastic.co/contact?pg=global&amp;plcmt=nav&amp;cta=205352">Elastic-Vertrieb</a>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Sicherstellung semantischer Präzision mit Mindestscore]]></title>
            <description><![CDATA[Verbessern Sie die semantische Präzision durch die Verwendung von Schwellenwerten für die Mindestscore. Der Artikel enthält konkrete Beispiele für die semantische und hybride Suche. ]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/semantic-precision-minimum-score</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/semantic-precision-minimum-score</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Mattias Brunnert]]></dc:creator>
            <pubDate>Fri, 20 Feb 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8b3b5910abfe16d48d309341a0027008b16c4340-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Die semantische Suche hat eine Vielzahl von Möglichkeiten für die Suchrelevanz eröffnet. Hochwertige dünn und dicht besetzte Modelle wie ELSER, E5 und Jina Embedding v4 liefern relevante Ergebnisse, die auf der Bedeutung von Wörtern basieren und nicht auf der Übereinstimmung von Schlüsselwörtern. Allerdings liefert die semantische Suche gelegentlich irrelevante Ergebnisse am Ende der Liste oder bei Suchanfragen, für die es keine relevanten Ergebnisse im Index gibt. Diese Eigenschaft von spärlichen und dichten Modellen kann Nutzer verwirren oder wertvolle Token für große Sprachmodelle (LLMs) verschwenden.</p><p>In diesem Artikel erfahren Sie, wie Sie den Parameter „Mindestscore“ verwenden können, um die Genauigkeit Ihrer semantischen Suchergebnisse zu erhöhen. Wenn Sie die in diesem Blogbeitrag bereitgestellten Beispiele testen möchten, besuchen Sie <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/ensuring-semantic-precision-with-minimum-score/ensuring_semantic_precision_with_minimum_score.ipynb">das zugehörige Jupyter-Notizbuch</a>.</p><h2>Hintergrund: Präzision und Abruf</h2><p>In der Suchrelevanz sind <em>Präzision </em>und <em>Recall </em>Schlüsselkonzepte. Lesern, die noch nicht mit diesen Themen vertraut sind, wird dringend empfohlen, sich darüber zu informieren. Nachfolgend eine Zusammenfassung.</p><ul><li><strong>Genauigkeit: </strong>Der Anteil der zurückgegebenen Suchergebnisse, die für den Nutzer relevant sind.</li><li><strong>Recall: </strong>Der Anteil aller relevanten Dokumente im Korpus, die in den Suchergebnissen enthalten sind.</li></ul><p>Oder, mit anderen Worten, Präzision gibt <strong>nur </strong>relevante Ergebnisse zurück; und Recall gibt <strong>alle </strong>relevanten Ergebnisse zurück. Wie Sie sich vorstellen können, handelt es sich dabei oft um konkurrierende Anforderungen. Die semantische Suche weist tendenziell eine sehr hohe Trefferquote auf, hat aber mitunter Schwierigkeiten mit der Präzision. Lesen Sie weiter, um zu erfahren, wie Sie diese Eigenschaft umgehen können.</p><h2>Einführung des Mindestscore-Parameters</h2><p>Der ‘min_score’-Parameter ermöglicht es uns, die Präzision zu verbessern, indem ein Mindestscore festgelegt wird, der das Ergebnisset durch Entfernen aller Treffer mit einem Score unter dem definierten Schwellenwert kürzt. Nachfolgend ein einfaches Beispiel:</p><pre><code>GET search-movies/_search
{
  "retriever": {
    "linear": {
      "min_score": 4,
      "retrievers": [
        ...
      ]
    }
  }
}</code></pre><h2>Normalisierung des Scores</h2><p>Die Festlegung eines Mindestscores ist schön und gut, aber nicht alle semantischen Modelle liefern einen Score, die sich für einen statischen Schwellenwert eignet. ELSER gibt beispielsweise einen unbegrenzten Score zurück. <a href="https://huggingface.co/intfloat/e5-small#faq">Einige</a> Scores des dichten Modells sind eng gruppiert und nur im Zusammenhang mit der spezifischen Anfrage sinnvoll.</p><p>Für die meisten Fälle der semantischen Suche empfehlen wir, vor der Anwendung von „min_score“ einen Normalisierungsansatz zu verwenden. Durch die Normalisierung wird sichergestellt, dass der Dokumentenscore innerhalb eines definierten Intervalls liegt. Elasticsearch-Retriever bieten zwei solcher <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers/linear-retriever#linear-retriever-normalizers">Normalisierer</a>, ‘l2_norm’ und ‘minmax’. Am häufigsten wird die „minmax“-Methode verwendet, da sie leicht verständlich ist und in vielen Szenarien gut funktioniert. Wichtige Eigenschaften von ‘minmax’ umfassen:</p><ul><li>Die Dokumentenscores liegen im Bereich von 0 bis 1.</li><li>Das Dokument mit der höchsten Punktzahl erhält immer den Score 1.</li><li>Das Dokument mit der niedrigsten Punktzahl erhält immer den Score 0.<ul><li>Dies kann die Eignung für die Stichwortsuche beeinträchtigen. Weitere Informationen finden Sie im Abschnitt „Hybride Suche“.</li></ul></li></ul><p>Im Folgenden ein Beispiel für eine normalisierte semantische Abfrage mit <code>min_score</code>. Die Größe des Ranking-Fensters wurde auf 500 erhöht, damit wir eine längere Liste von Suchergebnissen zurückgeben können, angefangen bei 100.</p><pre><code>GET search-movies/_search
{
  "size": 100,
  "_source": [
    "title", "overview"
  ],
  "retriever": {
    "linear": {
      "rank_window_size": 500,
      "min_score": 0.25,
      "retrievers": [
        {
          "normalizer": "minmax",
          "retriever": {
            "standard": {
              "query": {
                "semantic": {
                  "field": "overview_vector",
                  "query": "superhero movie"
                }
              }
            }
          }
        }
      ]
    }
  }
}</code></pre><p>Die Größe wurde auf einen höheren Wert als in der Produktion üblich eingestellt. So können wir die Qualität der Suchergebnisse inspizieren und die Ergebnisse optimieren.</p><h2>Hybridsuche mit dem linearen Retriever</h2><p>Für die Hybridsuche ist der einfachste Ansatz, alle Scores zu normalisieren, Gewichte zuzuweisen und einen Mindestscore anzuwenden. Beachten Sie, dass Sie durch die Wahl von Gewichtungen mit einer Summe von 1 den Gesamtscore innerhalb eines Bereichs von 0 bis 1 halten. Dadurch lassen sich die Endergebnisse leicht nachvollziehen und die Melodie <code>min_score</code> stimmen. Nachfolgend ein Beispiel:</p><pre><code>GET search-movies/_search
{
  "size": 100,
  "_source": ["title", "overview","keywords"],
  "retriever": {
    "linear": {
      "rank_window_size": 500,
      "min_score": 0.25,
      "retrievers": [
        {
          "weight": 0.6,
          "normalizer": "minmax",
          "retriever": {
            "standard": {
              "query": {
                "semantic": {
                  "field": "overview_vector",
                  "query": "superhero movie"
                }
              }
            }
          }
        },
        {
          "weight": 0.4,
          "normalizer": "minmax",
          "retriever": {
            "standard": {
              "query": {
                "multi_match": {
                  "query": "superhero movie",
                  "fields": ["overview","keywords", "title"],
                  "type": "cross_fields",
                  "minimum_should_match": "2"
                }
              }
            }
          }
        }
      ]
    }
  }
}</code></pre><h2>Hybridsuche mit RRF</h2><p>Mit BM25 steuern wir die Präzision oft durch andere Mittel, wie die Verwendung des <code>AND</code>-Operators oder <code>minimum_should_match</code>. Darüber hinaus werden Abfragen, die aus einzelnen, präzisen und seltenen Begriffen bestehen, natürlicherweise zu Suchergebnissen mit wenigen Suchergebnissen führen, die oft alle hochrelevant sind. Dies kann zu Folgendem führen:</p><ul><li>Ergebnisse, die weiter hinten im Ergebnis stehen, erhalten im BM25-Retriever einen niedrigen normalisierten Score, selbst wenn der absolute BM25-Score nahe an den Treffern mit den höchsten Scores liegt.</li><li>Wenn ein sehr niedriger BM25-Score zum semantischen Score hinzugefügt wird, kann die Summe als semantischer Score approximiert werden.</li><li>Das Fehlen eines BM25-Score-Beitrags kann dazu führen, dass das Dokument von <code>min_score threshold</code> verworfen wird.</li></ul><p>Als Lösung können wir stattdessen die reziproke Rangfusion (RRF) verwenden, um BM25- und semantische Ergebnisse zu kombinieren. RRF umgeht die Herausforderung, Scores verschiedener Suchalgorithmen zu vergleichen, indem es sich stattdessen auf die Position in jedem Ergebnis auf konzentriert. In diesem Szenario wird die <code>min_score</code> nur auf den semantischen Retriever angewendet.</p><pre><code>GET search-movies/_search
{
  "_source": ["title", "overview","keywords"],
  "retriever": {
    "rrf": {
      "rank_window_size": 500,
      "retrievers": [
        {
          "linear": {
            "rank_window_size": 500,
            "min_score": 0.25,
            "retrievers": [
              {
                "normalizer": "minmax",
                "retriever": {
                  "standard": {
                    "query": {
                      "semantic": {
                        "field": "overview_vector",
                        "query": "superhero movie"
                      }
                    }
                  }
                }
              }
            ]
          }
        },
        {
          "standard": {
            "query": {
              "multi_match": {
                "query": "superhero movie",
                "fields": ["overview", "keywords","title"],
                "type": "cross_fields",
                "minimum_should_match": "2"
              }
            }
          }
        }
      ]
    }
  }
}</code></pre><h2>Fazit</h2><p>Mit <code>min_score</code> haben wir gezeigt, wie wir die Anzahl der Fehlalarme in unseren Ergebnissätzen reduzieren können, die durch den hohen Recall semantischer Suchalgorithmen verursacht werden. Um mehr über Retriever zu erfahren, siehe bitte diesen <a href="https://www.elastic.co/search-labs/blog/elasticsearch-retrievers">Blogbeitrag</a> und die <a href="https://www.elastic.co/docs/solutions/search/retrievers-overview">Elasticsearch-Dokumentation</a>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Abhängigkeitsmanagement auf Kubernetes]]></title>
            <description><![CDATA[Wie man das Abhängigkeitsmanagement auf Kubernetes mithilfe von Renovate CLI und Argo Workflows optimieren kann.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/dependency-management-kubernetes</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/dependency-management-kubernetes</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Nikos Fotiou]]></dc:creator>
            <pubDate>Thu, 19 Feb 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/00519840e6eec7101c1fb096afcae976ee0c454e-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>So haben wir mit Kubernetes, Argo Workflows, Argo Events und Renovate CLI eine selbstgehostete Plattform für das Abhängigkeitsmanagement aufgebaut, um Updates zu automatisieren, häufige Schwachstellen und Expositionen (CVEs) schnell zu beheben und neue Paketversionen effizient über Tausende von Repositorys zu verbreiten.</p><h2><strong>Abhängigkeitsmanagement bei Elastic</strong></h2><p>Bei Elastic müssen wir Hunderte oder sogar Tausende von Repositorys, sowohl privat als auch öffentlich, verwalten. Wird eine kritische CVE entdeckt, benötigen wir umgehend Antworten und Maßnahmen: Welche Repositorys sind anfällig? Wie schnell können wir sie patchen? Neben der Sicherheit stellen sich auch Produktivitätsfragen: Wie können wir die Veröffentlichung einer neuen Paketversion schnell über alle darauf angewiesenen Repositorys verbreiten, ohne zu viel Zeit mit manuellen Aufgaben zu verbringen?</p><p>Der ursprüngliche Auslöser für die Suche nach Möglichkeiten für das Abhängigkeitsmanagement war die Notwendigkeit, eine sichere Grundlage mit automatisierten Updates zur <a href="https://www.elastic.co/blog/reducing-cves-in-elastic-container-images">Reduzierung von CVEs</a> zu schaffen. Nachdem wir verschiedene Lösungen zum Abhängigkeitsmanagement sorgfältig geprüft hatten, begannen wir zunächst mit der Arbeit an einer selbstgehosteten Infrastruktur. Wir nutzten unseren eigenen Kubernetes-Cluster genutzt, um Mend Renovate Community Self-Hosted auszuführen. Die Idee war, eine Abhängigkeitsmanagement-Plattform bereitzustellen, auf die unsere Nutzer im Self-Service-Modus zugreifen könnten.</p><p>Das erste Experiment war erfolgreich, sodass immer mehr Teams begannen, unsere Plattform zu integrieren und sie im täglichen Lebenszyklus ihrer Repositorys für Updates und CVE-Patches zu nutzen. Das geschah so schnell, dass wir bald die Grenze unserer selbstgehosteten Installation erreichten.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e14d9f98d4eccaa08a335d5bd23d88e5debbb344-1600x1103.png" style="width: 100%;" /><h3><strong>Die Herausforderung: Wie können wir eine Plattform zur Verwaltung von Abhängigkeiten in einem großen Unternehmen mit einer großen Anzahl von Repositorys skalieren?</strong></h3><p>Unsere Plattform für das Abhängigkeitsmanagement verarbeitete ein Repository nach dem anderen, und das sequentielle Verarbeitungsmodell konnte aufgrund der großen Anzahl von Repositorys, die wir besitzen, nicht Schritt halten. Wir hatten bereits festgestellt, dass das Problem daran lag, dass <strong>eine einzige Instanz</strong> unseres Abhängigkeitsverwaltungstools unsere lange und ständig wachsende Liste von Repositorys verarbeiten sollte. Die Repositorys warteten in einer Warteschlange, manchmal stundenlang. Mehr als 50 % unserer Repositorys wurden noch nicht einmal täglich verarbeitet. Das bedeutet, dass bei über 50 % unserer Repositorys zwischen den Scans mehr als 24 Stunden vergingen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/45ade5bda08f82bed0b3d0d3736cbd6f056e7a4e-1312x816.jpg" style="width: 100%;" /><p>Große Repositorys erzeugten aufgrund ihrer umfangreichen Codebasen und ihrer zahlreichen offenen PRs größere Engpässe. GitHub-Webhook-Ereignisse unterbrachen den Ablauf. Die automatische Zusammenführung wurde unzuverlässig, da die Scan-Zeitpunkte unvorhersehbar waren. Wir hatten unseren Nutzern ein Versprechen für die Häufigkeit der Scans gegeben, konnten es aber nicht einhalten.</p><h3><strong>Die Entscheidung für die Eigenentwicklung: Erfüllung des individuellen Bedarfs für Skalierung und Sicherheit bei Elastic</strong></h3><p>Während wir auch kommerzielle Optionen in Betracht zogen, darunter die <strong>Renovate Self-Hosted Enterprise Edition von Mend</strong>, hatten wir intern bei Elastic einige wichtige Initiativen in der Entwicklung.</p><p>Unsere Entscheidung, eine interne Plattform zu entwickeln, beruhte auf der Erkenntnis, dass nur eine gut angepasste Lösung die spezifischen, nicht verhandelbaren Anforderungen von Elastic erfüllen kann:</p><ol><li><strong>Investitionen in unsere interne Entwicklerplattform:</strong> Zu dieser Zeit hatten wir bereits damit begonnen, stark in unsere hausinterne Entwicklerplattform zu investieren. Wir diskutierten und entwarfen Möglichkeiten dazu, wie jeder einzelne unserer Dienste darin Platz finden könnte. Wir wollten eigene Regeln und Praktiken für unsere Abhängigkeitsverwaltungsplattform testen. Außerdem waren neue Richtlinien zu erwarten, und wir wollten die Plattform im Vorfeld der Ereignisse entwickeln.</li><li><strong>Native Integration und Workflow-Anpassung:</strong> Wir benötigten eine unkomplizierte Integration mit unseren internen Tools und internen Prozessen. Zum Beispiel wollten wir die Konfiguration als Code mit unserem Servicekatalog (Backstage) zentralisieren. Wir haben spezifische Anforderungen an die Nutzung von Backstage, mit denen wir unsere Plattform kompatibel machen wollten. Obwohl es möglich wäre, die Renovate Self-Hosted-APIs zusammen mit unserer Backstage-Automatisierung zu nutzen, würde dies unsere internen Prozesse nicht vollständig abdecken.</li><li><strong>Elastic-spezifische Defense-in-Depth-Sicherheit:</strong> Unsere strengen Anforderungen an die Sicherheitskonformität erforderten besondere Sicherheitsmechanismen, die auf unser Ökosystem abgestimmt sind. Wir versuchten, <a href="https://entro.security/blog/how-elastic-scaled-secrets-nhi-security-elastics-playbook-from-visibility-to-automation/">unsere Nutzung von „nicht-menschlichen Identitäten“ besser zu sichern.</a> Die Art und Weise, wie diese Zugriffssicherung funktionierte, bedeutete, dass die nicht standardmäßigen Authentifizierungsmethoden für GitHub mit einem Standardtool, das diese interne Implementierung nicht unterstützte, nicht funktionierten. Unser Workflow umfasste die Implementierung eines geheimen Verschlüsselungsmusters für über- und untergeordnete Workflows sowie die Verwendung temporärer, einmalig verwendbarer GitHub-Token. Die Eigenentwicklung war die einzig praktikable Möglichkeit, diese individuellen Sicherheitsebenen zu integrieren und die Angriffsfläche in unserer komplexen Multi-Cloud-Umgebung zu minimieren.</li></ol><h2><strong>Die Lösung: Eine Workflow-Orchestrierung für das Abhängigkeitsmanagement</strong></h2><p>Unsere Lösung basiert auf der Tatsache, dass wir auf dem bereits von uns verwendeten Abhängigkeitsverwaltungstool aufbauen wollten, statt es zu ersetzen und nach anderen Lösungen zu suchen. Es hatte sein Potenzial bereits gezeigt, und seine Flexibilität ist für die unterschiedlichen Anforderungen innerhalb unseres Unternehmens sehr wichtig. Wir zogen verschiedene Lösungen in Betracht, und was uns bei unserer Entscheidung half, waren die großen und manchmal speziellen Bedarfe, die wir abdecken müssen. Wir entschieden uns dafür, eine zuverlässige und skalierbare Plattform für das Abhängigkeitsmanagement aufzubauen, bei der jedes Repository einzeln verarbeitet wird, um Engpässe zu beseitigen und uns für Wachstum zu rüsten.</p><p>Wir gestalteten die Plattform nach drei Kernprinzipien:</p><h3><strong>1. Parallelverarbeitung</strong></h3><p>Jedes Repository erhält seine eigene Umgebung für das Abhängigkeitsmanagement. Es gibt keine Warteschlangen mehr. Unsere Parallelität ist nur durch die Anzahl der Ressourcen begrenzt, die wir einsetzen. Wir haben außerdem eine intelligente verteilte Planung implementiert, um eine Quotenbegrenzung durch GitHub zu vermeiden.</p><h3><strong>2. Selbstbedienbarkeit</strong></h3><p>Wir nutzen unseren Servicekatalog (Backstage), um jedes neue Repository automatisch zu integrieren und zu verwalten. Wir verwenden unsere eigene Ressourcendefinition, um dem Nutzer die Möglichkeit zu geben, auszuwählen, wie oft ein Repository verarbeitet werden soll, wie viele Ressourcen er seinen Zeitplänen zuweisen möchte und ob er die Verarbeitung aus irgendeinem Grund deaktivieren oder wieder aktivieren möchte. Wir planen, auf diese Weise weitere Optionen hinzuzufügen, sobald sich die Bedürfnisse unserer Nutzer weiterentwickeln und sie mit der neuen Installation vertrauter werden.</p><h3><strong>3. Reduzierter Geheimnisbereich und Namespace-Isolation</strong></h3><p>Um die Sicherheit zu erhöhen, versorgen wir unsere Pods zur Verwaltung von Abhängigkeiten mit ephemeren GitHub-Token, die zu Beginn jedes Workflows generiert werden. Darüber hinaus isolieren wir unsere Workloads in spezifischen Namespaces, sodass ihnen nur die notwendigen Geheimnisse bereitgestellt werden. Wir steuern mithilfe von Kubernetes RBAC, auf welche Geheimnisse die einzelnen Workflows des Abhängigkeitsmanagements zugreifen dürfen. Wir verwenden auch eine Verschlüsselung, um das GitHub-Token vom übergeordneten Workflow an die untergeordneten Workflows zu übertragen.</p><p>Wir bauten unsere Plattform mit Kubernetes neu auf und nutzten dabei die Leistungsfähigkeit von Kubernetes; Argo Workflows treibt die Logik unserer Prozesse an, und Renovate CLI ist für das Scannen und Verarbeiten eines Repositorys nach dem anderen eingerichtet.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5560ed20e2bd9ecdd574a9c835126d12b24c332f-1600x1157.png" style="width: 100%;" /><p><strong>Das Schöne:</strong> Wir verwenden praxiserprobte Open-Source-Projekte auf originelle Weise, bieten neue Arbeitsbeispiele für all diese Projekte, erhöhen gleichzeitig die Entwicklungsgeschwindigkeit und konsolidieren die CVE-Reduzierung für unsere Teams.</p><h2><strong>Architektur des Abhängigkeitsmanagements: Vier Microservices</strong></h2><p>Die Plattform besteht aus vier maßgeschneiderten Komponenten:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2b3d4046c05bb261e45d40c59f864eb51fb9eaa9-1217x1600.png" style="width: 100%;" /><h3><strong>Workflows Operator (Go/Kubebuilder)</strong></h3><p>Ein Kubernetes-Operator, der den Workflow-Lebenszyklus über drei benutzerdefinierte Ressourcendefinitionen (CRDs) verwaltet:</p><ul><li><strong>RepoConfig-CRD:</strong> Eine einzige Wahrheitsquelle für die Konfiguration des Repositorys.</li></ul><p>So wird RepoConfig im Operator definiert:</p><pre><code>// RepoConfig is the Schema for the repoconfigs API
type RepoConfig struct {
	metav1.TypeMeta `json:",inline"`

	// metadata is a standard object metadata
	// +optional
	metav1.ObjectMeta `json:"metadata,omitempty,omitzero"`

	// spec defines the desired state of RepoConfig
	// +required
	Spec RepoConfigSpec `json:"spec"`

	// status defines the observed state of RepoConfig
	// +optional
	Status RepoConfigStatus `json:"status,omitempty,omitzero"`
}</code></pre><p>Und so würde eine Instanz von RepoConfig aussehen:</p><pre><code>apiVersion: workflows.elastic.co/v1
kind: RepoConfig
metadata:
  generation: 3
  name: elastic-test-repo
  namespace: dependency-management-operator
spec:
  owner: group:my-team
  renovate:
    config:
      resourceGroup: SMALL
      runFrequency: 4h
    enabled: true
  repository: elastic/test-repo</code></pre><ul><li><strong>Parent-CRD:</strong> Verwaltet CronWorkflows für geplante Scans.</li></ul><p>Innerhalb der Abstimmungsschleife des übergeordneten Controllers sorgen wir dafür, dass die Workflow-Einstellungen erstellt und auf dem neuesten Stand gehalten oder bei Bedarf sogar gelöscht werden.</p><p>Zunächst werden einige global konfigurierte Einstellungen für Workflows abgerufen:</p><pre><code>func (r *ParentReconciler) reconcileSubResources(ctx context.Context, req ctrl.Request, parent *workflowsv1.Parent) error {
	logger := logf.FromContext(ctx)
	logger.Info("Reconcile SubResources for Parent", "name", req.NamespacedName)
	wfSet := workflowsettings.WorkflowSettings{
		RunFrequency:   parent.Spec.RunFrequency,
		ResourceGroups: "parent",
	}</code></pre><p>Das stellt sicher, dass eine Mutex-Configmap auf dem neuesten Stand ist, um zu verhindern, dass ähnliche Workflows gleichzeitig ausgeführt werden:</p><pre><code>	cfMngr := resources.NewConfigMapManager(r.Client, r.Scheme, r.OperatorConfig.ParentNamespace)
	err := cfMngr.CreateOrUpdateSyncMutexConfigmap(ctx, fmt.Sprintf("%s%s", r.OperatorConfig.ResourcesPrefix, r.OperatorConfig.SyncMutexCfgMapName), strings.TrimPrefix(parent.Spec.Repository, "elastic/"), r.OperatorConfig.SemaphoreConcurrencyLimit)</code></pre><p>Anschließend wird ein Workflow-Manager erstellt, der als Struktur die CronWorkflows und die Workflow-Vorlagen erstellt oder aktualisiert:</p><pre><code>	wfMngr := resources.NewArgoWorkflowManager(r.Client,
		r.Scheme,
		curateResourceName(
			strings.ReplaceAll(parent.Spec.Repository, "/", "-"),
		),
		parent.Namespace,
		"parent-workflow",
		false).
		WithOrganization(r.OperatorConfig.GitHubOrg).
		WithRepoName(parent.Spec.Repository).
		Init(true, true).
		WithPrefix(r.OperatorConfig.ResourcesPrefix).
		WithWfTemplateName(r.OperatorConfig.ParentWorkflowTemplate).
		WithResources(wfSet.GetResourceCategory()).
		WithSchedule(wfSet.GetCronSchedule()).
		WithImagePullSecrets([]corev1.LocalObjectReference{{
			Name: r.OperatorConfig.WorkflowImagePullSecrets,
		}}).
		AddArgument(true, true, "extra_cli_args").
		SetArgument(true, false, "extra_cli_args", "none").
		AddTemplate(resources.NewParentDAGTemplateInstance()).
		AddTemplate(resources.NewWorkflowsTemplateInstance("check-child-workflows", r.OperatorConfig.WorkflowImagePullPolicy, r.OperatorConfig.WorkflowNodeSelector)).
		AddTemplate(resources.NewWorkflowsTemplateInstance("security", r.OperatorConfig.WorkflowImagePullPolicy, r.OperatorConfig.WorkflowNodeSelector)).
		AddTemplate(resources.NewWorkflowsTemplateInstance("submit-child-workflow", r.OperatorConfig.WorkflowImagePullPolicy, r.OperatorConfig.WorkflowNodeSelector))
	wfMngr.OverWriteCommand("submit-child-workflow", r.OperatorConfig.ChildNamespace)
	wfMngr.OverwriteWfTemplateName("parent-wftmpl")
	wfMngr.AddSynchronization(fmt.Sprintf("%s%s", r.OperatorConfig.ResourcesPrefix, r.OperatorConfig.SyncMutexCfgMapName), "{{workflow.parameters.repo_name}}")
	err = wfMngr.CreateOrUpdateCronWorkflow(ctx)
	if err != nil {
		return fmt.Errorf("failed to create or update cron workflow: %w", err)
	}
	err = wfMngr.CreateOrUpdateWorkflowTemplate(ctx)
	if err != nil {
		return fmt.Errorf("failed to create or update workflow template: %w", err)
	}
	return nil</code></pre><ul><li><strong>Child-CRD:</strong> Verwaltet WorkflowTemplates mit Ressourcen pro Repository.</li></ul><p>Der untergeordnete Controller hat eine ähnliche Abgleichsaufgabe wie der übergeordnete Controller, ist aber diesmal für Workflow-Vorlagen im untergeordneten Namespace verantwortlich, die von den Workflows des übergeordneten Controllers ausgelöst werden.</p><pre><code>func (r *ChildReconciler) reconcileSubResources(ctx context.Context, req ctrl.Request, child *workflowsv1.Child) error {
	logger := logf.FromContext(ctx)
	logger.Info("Reconcile SubResources for Child", "name", req.NamespacedName)
	wfSet := workflowsettings.WorkflowSettings{
		ResourceGroups: child.Spec.ResourceCategory,
	}
	wfMngr := resources.NewArgoWorkflowManager(r.Client,
		r.Scheme,
		curateResourceName(
			strings.ReplaceAll(child.Spec.Repository, "/", "-"),
		),
		child.Namespace,
		"runner",
		true).
		Init(false, true). // only manage workflow template
		WithPrefix(r.OperatorConfig.ResourcesPrefix).
		WithSuffix("-child-wftmpl").
		WithRepoName(child.Spec.Repository).
		WithOrganization(r.OperatorConfig.GitHubOrg).
		WithResources(wfSet.GetResourceCategory()). // will override resources of presets if set
		WithImagePullSecrets([]corev1.LocalObjectReference{{
			Name: r.OperatorConfig.WorkflowImagePullSecrets,
		}}).
		AddTemplate(resources.NewWorkflowsTemplateInstance("runner", r.OperatorConfig.WorkflowImagePullPolicy, r.OperatorConfig.WorkflowNodeSelector)).
		AddArgument(false, true, "repo_full_name").
		AddArgument(false, true, "repo_name").
		AddArgument(false, true, "encrypted_token").
		AddArgument(false, true, "extra_cli_args")
	wfMngr.OverWriteCommand("runner", r.OperatorConfig.ChildNamespace)
	err := wfMngr.CreateOrUpdateWorkflowTemplate(ctx)
	if err != nil {
		return fmt.Errorf("failed to create or update workflow template: %w", err)
	}
	return nil
}</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7ac70492a1266ba02cb8afbafc5a486cb38a0edc-1600x1290.png" style="width: 100%;" /><p>Das Multi-Controller-Muster bietet eine klare Trennung: Der RepoConfig-Controller übernimmt das Onboarding/Offboarding, der Parent-Controller verwaltet die Planung und der Child-Controller kümmert sich um die Ausführungsvorlagen.</p><h3><strong>GitHub Events Gateway (Go)</strong></h3><p>Ein sicherer Webhook-Proxy, der GitHub-Webhooks empfängt, Signaturen verifiziert, nach Organisation/Repository filtert und an Argo Events weiterleitet. Wir haben 10 verschiedene Sensoren entwickelt, die auf Interaktionen im Abhängigkeits-Dashboard, PR-Ereignisse und Paketaktualisierungen reagieren.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4828625456cbd6efa8020a20f10d23f294f98a02-1306x1600.png" style="width: 100%;" /><p>Dieses Gateway ermöglicht die Integration mit GitHub-Apps durch:</p><ul><li>Überprüfung eingehender GitHub-Webhook-Signaturen auf Sicherheit.</li><li>Weiterleitung gültiger Ereignisse an die Argo Events EventSource mit allen relevanten Headern und der Authentifizierung.</li><li>Wir konfigurieren außerdem ein authSecret auf der EventSource und stellen dieses als Bearer-Header in weitergeleiteten Anfragen bereit.</li><li>Bereitstellung von Protokollierung, Metriken und Wiederholungslogik.</li></ul><p>Es führt verschiedene Überprüfungen für jede GitHub-Ereignisanfrage durch.</p><p>Es stellt sicher, dass bestimmte HTTP-Attribute vorhanden sind:</p><pre><code>// ValidateRequestMethod checks if the request method is POST.
func ValidateRequestMethod(r *http.Request) error {
	if r.Method != http.MethodPost {
		return fmt.Errorf("method not allowed, only POST is accepted")
	}
	return nil
}

// ValidateRequiredHeaders checks for required GitHub headers.
func ValidateRequiredHeaders(r *http.Request) error {
	eventType := r.Header.Get("X-GitHub-Event")
	deliveryID := r.Header.Get("X-GitHub-Delivery")
	signature := r.Header.Get("X-Hub-Signature-256")
	if eventType == "" || deliveryID == "" || signature == "" {
		return fmt.Errorf("missing required GitHub headers")
	}
	return nil
}

// ValidateUserAgent checks that the User-Agent header starts with GitHub-Hookshot/
func ValidateUserAgent(r *http.Request) error {
	userAgent := r.Header.Get("User-Agent")
	if !strings.HasPrefix(userAgent, "GitHub-Hookshot/") {
		return fmt.Errorf("invalid User-Agent")
	}
	return nil
}</code></pre><p>Gleichzeitig validiert es auch die Signatur jeder Anfrage und deren Organisation:</p><pre><code>// ValidateSignature verifies the GitHub webhook signature.
func ValidateSignature(r *http.Request, secret string) ([]byte, error) {
	payload, err := GitHub.ValidatePayload(r, []byte(secret))
	if err != nil {
		return nil, fmt.Errorf("invalid GitHub signature: %w", err)
	}
	return payload, nil
}

// ValidateAllowedOwner checks if the organization login is in the allowed organizations list.
func ValidateAllowedOwner(payload []byte, allowedGitHubOrganizations []string) (string, error) {
	var orgLogin string
	var payloadMap map[string]any
	if err := json.Unmarshal(payload, &payloadMap); err == nil {
		if orgObj, ok := payloadMap["organization"].(map[string]any); ok {
			if login, ok := orgObj["login"].(string); ok {
				orgLogin = login
			} else if name, ok := orgObj["name"].(string); ok {
				orgLogin = name
			}
		}
	}
	if !slices.Contains(allowedGitHubOrganizations, orgLogin) {
		return orgLogin, fmt.Errorf("organization login not allowed")
	}
	return orgLogin, nil
}</code></pre><p>Schließlich wird je nach Ereignistyp an Argo Events weitergeleitet:</p><pre><code>	// Map eventType to Argo `EventSource` path
	var endpoint string
	switch eventType {
	case "push":
		endpoint = "/push"
	case "issues":
		endpoint = "/issues"
	case "pull_request":
		endpoint = "/pull-requests"
	default:
		slog.Info("Ignoring unhandled event type", "event_type", eventType, "delivery_id", deliveryID)
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write([]byte("ok"))
		return
	}
	forwardURL := h.config.ArgoEventSourceForwardURL + endpoint</code></pre><p>Auf der Seite von Argo Events überwachen 10 Sensoren den Argo Events EventBus auf neue Ereignisse:</p><pre><code>apiVersion: argoproj.io/v1alpha1
kind: Sensor
metadata:
  name: {{ .Values.sensors.packageUpdateOnDefaultBranch.name }}
  namespace: {{ .Release.Namespace }}
spec:
  eventBusName: {{ .Values.eventBus.name }}</code></pre><p>Anschließend wendet das Skript die Logik jedes Sensors an:</p><pre><code>script: |
          local e = event
          if not e or not e.body or not e.body.repository then
            return false
          end

          -- e.g., "refs/heads/main"
          local ref = e.body.ref
          local default_branch = e.body.repository.default_branch
          if not ref or not default_branch then
            return false
          end

          local expected = "refs/heads/" .. default_branch
          if ref ~= expected then
            return false
          end

        {{- if .Values.sensors.packageUpdateOnDefaultBranch.packageFiles }}
          patterns = { {{- range $i, $f := .Values.sensors.packageUpdateOnDefaultBranch.packageFiles }}{{ if $i }}, {{ end }}"{{ $f }}"{{- end }} }
        {{- end }}

          local function anyMatch(path)
            if type(path) ~= "string" then return false end
            for _, pat in ipairs(patterns) do
              -- match filename at repo root, or anywhere under subdirs
              if path:match(pat) or path:match(".+/" .. pat) then
                return true
              end
            end
            return false
          end

          local function filesContainPackage(paths)
            if type(paths) ~= "table" then return false end
            for _, p in ipairs(paths) do
              if anyMatch(p) then return true end
            end
            return false
          end

          -- Inspect all commits (GitHub includes added/modified/removed lists)
          local commits = e.body.commits
          if type(commits) ~= "table" then
            -- Fallback: some payloads include only head_commit
            commits = {}
            if type(e.body.head_commit) == "table" then
              table.insert(commits, e.body.head_commit)
            end
          end

          for _, c in ipairs(commits) do
            if filesContainPackage(c.added) or filesContainPackage(c.modified) or filesContainPackage(c.removed) then
              return true
            end
          end

          return false</code></pre><h3><strong>Backstage-Synchronisierer (Go)</strong></h3><p>Dies fragt unseren Service-Katalog (Backstage) nach Repository Real Resource Entities ab, wandelt sie in RepoConfig-CRDs um und hält die Plattform mit den Konfigurationsänderungen synchron. Änderungen werden innerhalb von drei Minuten wirksam.</p><pre><code>repoMap := make(map[string]map[string]interface{})
			for i := range entities {
				entity := &entities[i]
				if entity.Spec.Type != "GitHub-repository" {
					continue
				}

				implRaw, err := json.Marshal(entity.Spec.Implementation)
				if err != nil {
					logger.Error("Failed to marshal implementation", "error", err)
					continue
				}

				var implMap map[string]interface{}
				err = json.Unmarshal(implRaw, &implMap)
				if err != nil {
					logger.Error("Failed to unmarshal implementation map", "error", err)
					continue
				}
				var repoName string
				if specMap, ok := implMap["spec"].(map[string]interface{}); ok {
					if repo, ok := specMap["repository"].(string); ok {
						repoName = repo
					}
				}
				if repoName == "" {
					continue
				}

				var workflowsRaw []byte
				if v, ok := implMap["spec"].(map[string]interface{}); ok {
					if r, ok := v["renovate"]; ok {
						workflowsRaw, _ = json.Marshal(r)
					} else {
						workflowsRaw = []byte(`{}`)
					}
				} else {
					workflowsRaw = []byte(`{}`)
				}

				var workflowsWithDefaults schema.WorkflowsMetadata
				err = json.Unmarshal(workflowsRaw, &rworkflowsWithDefaults)
				if err != nil {
					logger.Error("Failed to unmarshal workflows config", "error", err)
					continue
				}

				workflowsMap := map[string]interface{}{
					"enabled":        workflowsWithDefaults.Enabled,
					"require_pr":     workflowsWithDefaults.RequirePr,
					"resource_group": string(workflowsWithDefaults.ResourceGroup),
					"run_frequency":  string(workflowsWithDefaults.RunFrequency),
				}
				repoMap[repoName] = map[string]interface{}{
					"renovate": workflowsMap,
					"owner":    entity.Spec.Owner,
				}
			}
			logger.Info("Fetched GitHub Repository data from Backstage", "repository_count", len(repoMap), "status_code", resp.StatusCode)</code></pre><p>Schließlich werden diese Daten in RepoConfig-Instanzen eingeschrieben.</p><h3><strong>Workflows-Basis (Gemischt: JavaScript, Go, Helm)</strong></h3><p>Die Basisschicht enthält Helm-Charts, JavaScript-Konfigurationen, einen Go-Wrapper für die Renovate CLI mit Verschlüsselungsunterstützung und einen benutzerdefinierten APK-Indexer für Alpine-Pakete.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/908d19278face3ce1119dbee9146c1264b6e2f30-1600x873.png" style="width: 100%;" /><h2><strong>Self-Service-Konfiguration</strong></h2><p>Teams konfigurieren ihre Repositories deklarativ über Backstage:</p><pre><code>spec:
  renovate:
    enabled: true
    config:
      resourceGroup: LARGE      # SMALL | MEDIUM | LARGE  
      runFrequency: "0 */4 * * *"  # Every 4 hours</code></pre><p>Ressourcengruppen verteilen CPU und Speicher basierend auf der Größe des Repositorys:</p><ul><li><strong>KLEIN:</strong> 500 m CPU, 1 Gi Arbeitsspeicher.</li><li><strong>MITTEL:</strong> 1000 m CPU, 2 Gi Arbeitsspeicher.</li><li><strong>GROSS:</strong> 2000 m CPU, 4 Gi Arbeitsspeicher.</li></ul><p>Die Konfiguration ist versionskontrolliert, überprüfbar und wird automatisch angewendet.</p><h2><strong>Das Parent-Child-Muster</strong></h2><p>Das Ausführungsmodell verwendet ein Parent-Child-Workflow-Muster:</p><ul><li><strong>Übergeordneter Workflow:</strong> Der Lightweight CronWorkflow läuft planmäßig. Verschlüsselt Geheimnisse, bestimmt, ob ein Scan ausgeführt werden soll, und gibt die Konfiguration an die untergeordneten Workflows weiter.</li><li><strong>Untergeordneter Workflow:</strong> Ein flüchtiger Pod, auf dem Renovate CLI läuft. Weist Ressourcen dynamisch zu, entschlüsselt Geheimnisse isoliert und beendet sich nach Abschluss.</li></ul><p>Diese Trennung bietet Sicherheit (Geheimnisse werden auf der Ebene der übergeordneten Prozesse verschlüsselt), Ressourcenoptimierung (übergeordnete Prozesse verbrauchen nur minimale Ressourcen) und Skalierbarkeit (untergeordnete Prozesse laufen parallel).</p><h2><strong>Die Ergebnisse</strong></h2><h3><strong>Leistungsveränderung</strong></h3><ul><li><strong>Vorher:</strong> Es wurde jeweils nur ein Repository bearbeitet, manche Repositorys wurden unter Umständen sogar einen Tag oder länger nicht verarbeitet, insgesamt wurden weniger als 1.000 Scans pro Tag durchgeführt.</li><li><strong>Nachher:</strong> Mehr als 100 gleichzeitige Scans, in der Regel 8.000 Scans und bis zu 10.000 aufgezeichnete Scans pro Tag, begrenzt nur durch die Menge an Ressourcen, die wir verbrauchen möchten, und unseren Umgang mit den Quotenbegrenzungen von GitHub.</li></ul><h3><strong>Kosteneffizienz</strong></h3><p>So seltsam es auch klingen mag, mit 8.000 Pods pro Tag erzielt man das gleiche Ergebnis viel günstiger als mit einem einzigen, lange laufenden Pod, der versucht, das gleiche Ergebnis zu erzielen.</p><p>In der vorherigen Konfiguration betrieben wir eine einzelne Instanz, die an einem guten Tag 500 bis 600 Scans durchführte. Da gleichzeitig verschiedene Arten von Repositorys auf demselben Pod ausgeführt werden sollten, mussten wir den Pod auf die größten Repositorys abstimmen. Diese Größe wäre mit 8 CPUs für den Pod und 16 GB Speicher deutlich größer als unser aktuelles, besonders großes Angebot.</p><p>Um die aktuelle Tagesproduktion zu erreichen, müsste der einzelne Pod 12 Tage laufen. Vergleicht man also die Kosten für einen einzelnen Pod, der 12 Tage lang läuft, mit den Kosten für 8.000 Pods unserer Größe „MEDIUM“, die täglich im Einsatz sind, so ist unser neues Design bei gleicher Scan-Ausgabe weitaus effizienter:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Metrik</td><td style="border: 1px solid #ccc; padding: 4px;">Szenario A (Workflows)</td><td style="border: 1px solid #ccc; padding: 4px;">Szenario B (Der einzelne, lange laufende Pod)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Einrichtung</td><td style="border: 1px solid #ccc; padding: 4px;">8.000 Pods (1 vCPU / 2 GB)</td><td style="border: 1px solid #ccc; padding: 4px;">1 Pod (8 vCPUs / 16 GB)*</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Dauer</td><td style="border: 1px solid #ccc; padding: 4px;">jeweils 10 Minuten</td><td style="border: 1px solid #ccc; padding: 4px;">12 Tage ununterbrochen</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Gesamtarbeitszeit</td><td style="border: 1px solid #ccc; padding: 4px;">1.333 Rechenstunden</td><td style="border: 1px solid #ccc; padding: 4px;">288 Rechenstunden</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Gesamtkosten</td><td style="border: 1px solid #ccc; padding: 4px;">65,83 $</td><td style="border: 1px solid #ccc; padding: 4px;">113,75 $</td>
                    </tr>
              </tbody>
            </table>
          <p>Allerdings sollten wir berücksichtigen, dass unsere Standardeinstellung für unsere Workloads auf „KLEIN“ gesetzt ist, wobei die große Mehrheit erfolgreich mit 0,5 CPU und 1 GB RAM läuft und nur wenige auf „MITTEL“ oder „GROSS“ umgestellt werden müssen. Mal sehen, was passiert, wenn 60 % unserer Arbeitslasten auf „KLEIN“, 30 % auf „MITTEL“ und 10 % auf „GROSS“ laufen, was den tatsächlichen Anforderungen näherkommt.</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Metrik</td><td style="border: 1px solid #ccc; padding: 4px;">Szenario A (Gemischter Schwarm)</td><td style="border: 1px solid #ccc; padding: 4px;">Szenario B (Der Langläufer)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Strategie</td><td style="border: 1px solid #ccc; padding: 4px;">8.000 Pods (unterschiedliche Größen)</td><td style="border: 1px solid #ccc; padding: 4px;">1 Pod (8 vCPUs / 16 GB)*</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Dauer</td><td style="border: 1px solid #ccc; padding: 4px;">jeweils 10 Minuten</td><td style="border: 1px solid #ccc; padding: 4px;">12 Tage ununterbrochen</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Gesamtkosten</td><td style="border: 1px solid #ccc; padding: 4px;">52,66 $</td><td style="border: 1px solid #ccc; padding: 4px;">113,75 $</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Einsparungen</td><td style="border: 1px solid #ccc; padding: 4px;">61,09 $ (54% günstiger)</td><td style="border: 1px solid #ccc; padding: 4px;">—</td>
                    </tr>
              </tbody>
            </table>
          <p>Wir können sehen, dass wir bei gleicher Ausgabe in unserem aktuellen Setup weitaus kosteneffizienter sind.</p><h3><strong>Verbesserte Sicherheit</strong></h3><ul><li>Kurzlebige GitHub-Token (Minuten der Exposition im Vergleich zu Tagen).</li><li>Namespace-Isolation mit rollenbasierten Zugriffskontrollgrenzen (RBAC).</li><li>Geheime Verschlüsselung inaktiver Daten in übergeordneten Workflows.</li><li>Kein direkter Tresorzugriff mehr.</li></ul><h3><strong>Vorhersagbare Leistung</strong></h3><p>Mit einer garantierten Scanfrequenz können wir endlich Service Level Objectives (SLOs) festlegen. Die automatische Zusammenführung funktioniert zuverlässig. Die Teams vertrauen darauf, dass die Plattform das Versprochene auch tatsächlich liefert.</p><h2><strong>Wichtige architektonische Entscheidungen</strong></h2><p>Zu den wichtigsten Designentscheidungen, die die Gestaltung der Plattform geprägt haben, gehören die Folgenden.</p><ul><li><strong>Warum über- und untergeordnete Workflows?</strong></li></ul><p>Wir haben dieses Muster übernommen, um eine <strong>tiefgreifende Verteidigungsstrategie</strong> durchzusetzen. Indem wir wertvolle Zugangsdaten (wie GitHub-App-Geheimnisse) auf einen dedizierten, gesperrten Namespace beschränken, nutzen wir <strong>RBAC</strong>, um sicherzustellen, dass flüchtige Ausführungspods keinen beliebigen Zugriff auf sensible Daten haben. Jüngste Sicherheitslücken in Lieferketten (zum Beispiel die <strong>„Shai Hulud“</strong> Continuous Integration/Continuous Delivery [CI/CD]-Angriffe) haben gezeigt, wie wichtig es ist, Laufzeitumgebungen, die dynamisches Scripting ausführen, vom Anmeldeinformationsspeicher zu isolieren.</p><p>Gleichzeitig ermöglicht diese Entkopplung eine <strong>granulare Ressourcenoptimierung</strong>. Die „übergeordneten“ Workflows fungieren als leichtgewichtige Orchestrierer mit minimalem Ressourcenbedarf, während die „untergeordneten“ Workflows die rechenintensive Abhängigkeitsanalyse übernehmen. Diese Trennung vereinfacht das <strong>Lifecycle-Management</strong>, da wir auf jede Ebene eine eigene Abstimmungslogik anwenden können, sodass die Nutzer die Kontrolle über die Ausführungsparameter haben (untergeordnet), während wir die administrative Kontrolle über die Planungs- und Sicherheitsinfrastruktur (übergeordnet) behalten.</p><ul><li><strong>Warum Selbstbedienbarkeit?</strong></li></ul><p>Die Beseitigung unseres Teams als Engpass bei der Repository-Konfiguration war eine entscheidende Voraussetzung. Unser Ziel war es, eine skalierbare <strong>Self-Service-Plattform</strong> zu entwickeln, die vielfältige Anwendungsfälle unterstützen kann. Wir haben erkannt, dass es angesichts der schieren Menge an Repositorys nicht nachhaltig ist, als <strong>Gatekeeper</strong> für jede Konfigurationsänderung zu agieren. Stattdessen verfolgten wir eine Philosophie der Befähigung: Wir stellten die „Schienen“ (Infrastruktur und <strong>Leitplanken</strong>) bereit, während wir die Nutzer in die Lage versetzten, die „Züge“ (Ausführung und Anpassung) zu fahren. Wir sind überzeugt, dass dieser Wandel hin zur <strong>Teamautonomie</strong> die Produktivität erheblich steigert, da Nutzer das System nun an ihre spezifischen operativen Bedürfnisse anpassen können.</p><ul><li><strong>Warum das Kubernetes-Operator-Muster?</strong></li></ul><p>Wie oben bereits erwähnt, wollten wir grundsätzlich sicherstellen, dass die Plattform vollkommen <strong>selbstbedienbar</strong> sein würde. Wir benötigten einen automatisierten Mechanismus, um die Absicht des Nutzers zu erfassen (z. B. das Umschalten von Scans, das Anpassen der Planungsfrequenz oder das Optimieren der Laufzeitressourcengrenzen) und diese Änderungen sofort an die zugrunde liegenden Workflows weiterzugeben. In Erwartung zukünftiger Anforderungen sollte das System zudem leicht <strong>erweiterbar</strong> sein.</p><p>Um dies zu erreichen, entwickelten wir einen individuellen <strong>Dependency Management Kubernetes Operator</strong>. Mithilfe von <strong>CRDs</strong> als Schnittstelle für die Konfiguration etablierten wir eine <strong>Kubernetes-native Abgleichschleife</strong>. Dieser Operator überwacht kontinuierlich den vom Nutzer definierten gewünschten Zustand und orchestriert automatisch die notwendigen Aktualisierungen der Workflow-Infrastruktur. Das gewährleistet einen <strong>ereignisgesteuerten</strong>, nahtlosen Betrieb, bei dem die Plattformlogik die gesamte Komplexität hinter den Kulissen bewältigt.</p><ul><li><strong>Warum ein GitHub Events Gateway entwickeln?</strong></li></ul><p>Die Einführung einer <strong>ereignisgesteuerten Architektur (EDA)</strong> war für die Reaktionsfähigkeit der Plattform unerlässlich. Zwar bot CronWorkflows einen zuverlässigen Basisplan, aber wir brauchten auch die Flexibilität zur Bewältigung von <strong>Ad-hoc-Ausführungen</strong>, wie etwa das manuelle Auslösen von Scans durch Nutzer über das Dashboard. Dafür benötigten wir ein dediziertes <strong>Ingestionsgateway</strong>, um die Integrität der Nutzlasten zu validieren und Anfragen intelligent weiterzuleiten.</p><p>Wir evaluierten bestehende Lösungen, darunter die native GitHub EventSource für Argo, aber stellten erhebliche Risiken hinsichtlich des <strong>operativen Aufwands</strong> und der strengen <strong>GitHub API-Kontingente</strong> (z. B. Webhook-Limits pro Repository) fest. Deshalb entwickelten wir ein benutzerdefiniertes Gateway, um unsere Infrastruktur von diesen Einschränkungen zu entkoppeln.</p><p>Entscheidend war, dass dieses Gateway während unserer Migration als strategischer <strong>Verkehrskontrollpunkt</strong> diente. Es fungierte als Switch und ermöglichte uns die Durchführung eines <strong>schrittweisen, granularen Rollouts</strong> (Verkehrsverlagerung) vom Altsystem zur neuen Infrastruktur. Dies stellte sicher, dass die Einbindung von Tausenden von Repositorys ein kontrollierter, risikofreier Prozess und kein abrupter „Big Bang“-Wechsel war.</p><p></p><h2><strong>Erkenntnisse</strong></h2><p>Einiges, was wir dabei gelernt haben, entspricht dem <a href="https://www.elastic.co/about/our-source-code">Elastic Source Code</a>:</p><ol><li><strong>Der Kunde steht im Mittelpunkt: </strong>Plattformen sind für Nutzer gebaut. Deshalb sind die Bedürfnisse der Nutzer an erste Stelle zu setzen. Die Plattform wird damit zu einer effizient gestalteten Infrastruktur mit Anwendungen, die Reibungsverluste für die Nutzer reduzieren, das Skalieren der Plattform vereinfachen und die Akzeptanz erhöhen.</li><li><strong>Raum und Zeit: </strong>Manchmal gerät man beim Weg des geringsten Widerstands in <strong>Treibsand</strong>. Wir haben zunächst versucht, das bestehende sequenzielle Verarbeitungsmodell zu optimieren, aber das löste unsere Probleme nicht, sondern ergab nur noch mehr Komplexität und lose Enden. Die mutige Entscheidung, die Plattform mit paralleler Verarbeitung <strong>neu zu gestalten</strong>, erforderte erhebliche Vorarbeiten. Letztendlich ebnete es jedoch den Weg für ein nachhaltiges Plattformwachstum und beseitigte praktisch die mühsame tägliche Verwaltungsarbeit.</li><li><strong>IT-Abhängigkeiten: </strong>Eine Plattform kann nicht isoliert betrieben werden; ihr Erfolg hängt davon ab, wie gut sie sich in das breitere Ökosystem integriert. In unserem Fall war die Integration mit <strong>Backstage</strong> entscheidend, da es als Wahrheitsquelle für ein nahtloses Service-Onboarding dient. In ähnlicher Weise ermöglichte uns die Verbindung zu <strong>Artifactory</strong>, private Paket-Updates effizient zu verwalten, und das sind nur einige der wichtigsten Integrationen.</li><li><strong>Fortschritt und EINFACHE Perfektion: </strong>Während der gesamten Implementierung stellten wir unsere ursprünglichen Annahmen immer wieder auf den Prüfstand und passten uns an neue Hindernisse an, sobald sie auftauchten. Anstatt uns durch Perfektionismus lähmen zu lassen, wählten wir einen <strong>iterativen Ansatz</strong>, gingen Herausforderungen nacheinander an und richteten unsere Migrationsstrategie an den realen Gegebenheiten aus.</li></ol><h2><strong>Was kommt als Nächstes?</strong></h2><p>Die Bereitstellung der Plattform ermöglicht uns sinnvollere Arbeit, die uns wiederum helfen wird, das Nutzererlebnis und die Effizienz unserer Plattform zu verbessern. Einige Beispiele sind:<br/></p><ul><li><strong>Ausweitung und Absicherung der Einführung einer automatischen Zusammenführung</strong></li></ul><p>Die Auto-Merge-Funktion beschleunigt die Teamgeschwindigkeit erheblich, da sie mühsame manuelle Aufgaben eliminiert. Allerdings müssen wir sicherstellen, dass strenge <strong>Schutzmaßnahmen</strong> vorhanden sind, um zu gewährleisten, dass diese erhöhte Geschwindigkeit nicht auf Kosten der Sicherheit geht.<br/></p><ul><li><strong>Verbesserung der Beobachtbarkeit rund um die Endnutzererfahrung</strong></li></ul><p>Ein zentrales Anliegen unserer Roadmap ist die Verbesserung der Beobachtbarkeit, nicht nur auf Plattformebene, sondern auch speziell aus der <strong>Perspektive des Endnutzers</strong>. Die Erfassung von Infrastrukturkennzahlen ist zwar einfach, aber für ein Verständnis der tatsächlichen Nutzererfahrung sind tiefere Einblicke erforderlich. Wir arbeiten daran, zentrale, benutzerzentrierte Leistungskennzahlen (KPIs) zu definieren, damit unsere Telemetrie Reibungspunkte und Leistungsprobleme erkennen kann, <strong>bevor</strong> sie zu Nutzerbeschwerden eskalieren.</p><ul><li><strong>Beseitigung von Hindernissen für eine breitere Akzeptanz</strong></li></ul><p>Mit Blick in die Zukunft liegt unser Schwerpunkt darauf, alle Barrieren zu identifizieren und zu beseitigen, die die Einführung der Plattform behindern. Ob dies die Entwicklung neuer Integrationen oder die Bereitstellung spezifischer Funktionssets erfordert – wir setzen uns für datengetriebene Planung ein. Wir haben erfolgreich eine skalierbare Plattform aufgebaut; unser Fokus verlagert sich nun darauf, <strong>ihr Potenzial zu maximieren</strong>.<br/></p><h2><strong>Im Ganzen betrachtet</strong></h2><p>Das Projekt zu den Workflows für das Abhängigkeitsmanagement demonstriert ein allgemeineres Prinzip: <strong>Wenn Sie Open-Source-Tools über deren Standard-Bereitstellungsmodell hinaus skalieren müssen, sind Kubernetes-native Muster eine Möglichkeit dafür</strong>.</p><p>Indem Sie Folgendes annehmen:</p><ul><li>CRDs für die Konfiguration.</li><li>Operatoren für das Lifecycle-Management.</li><li>Ereignisgesteuerte Architektur für Reaktionsfähigkeit</li><li>GitOps für das Deployment.</li></ul><p>Wir haben eine Orchestrierung entwickelt, die unabhängig von der Zahl der verwalteten Repositorys skaliert. Die Leistung beim Scannen eines Repositorys ist gleich, egal ob wir 100 oder 1.000 verwalten.</p><p>Wenn ein kritisches CVE angekündigt wird, haben wir jetzt Antworten innerhalb von Minuten, nicht Stunden. Das ist der Unterschied zwischen einem Engpass und einem Wettbewerbsvorteil.</p><h2><strong>Danksagungen</strong></h2><p>Diese Plattform basiert auf exzellenten Open-Source-Tools:</p><ul><li><strong>Kubebuilder:</strong> Das Open Source-Framework, das wir genutzt haben, um unsere Kubernetes-Operatoren zu starten, die unsere Arbeitsabläufe initialisieren und orchestrieren. [<a href="https://github.com/kubernetes-sigs/kubebuilder">1</a>][<a href="https://book.kubebuilder.io/">2</a>]</li><li><strong>Backstage:</strong> Das Open-Source-Framework, auf dem wir unseren Servicekatalog aufgebaut haben und das wir als unsere Informationsquelle verwenden. [<a href="https://github.com/backstage/backstage">1</a>][<a href="https://backstage.io/">2</a>]</li><li><strong>Argo Workflows und Argo Events:</strong> Die Open-Source-Suite, die wir verwendeten, um komplexe Prozesse zu orchestrieren und eine dynamische Verarbeitung basierend auf Ereignissen hinzuzufügen. [<span class="unknown__pt__mark__1e5711660421">1</span>][<a href="https://argo-workflows.readthedocs.io/en/stable/">2</a>][<a href="https://argoproj.github.io/argo-events/">3</a>][<a href="https://github.com/argoproj/argo-events">4</a>]</li><li><strong>Renovate CLI:</strong> Das Open-Source-Tool zur Verwaltung von Abhängigkeiten, das unsere Repositorys verarbeitet. [<a href="https://github.com/renovatebot/renovate">1</a>][<a href="https://docs.renovatebot.com/getting-started/running/">2</a>]</li></ul><p>* Als Referenz für die Kosten eines einzelnen Pods wurde das AWS Fargate-Preismodell verwendet, obwohl unsere Workloads nicht unbedingt auf AWS laufen, sondern auf vollwertigen Kubernetes-Clustern.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Verbessern Sie die Suchleistung mit `best_compression`]]></title>
            <description><![CDATA[Während `best_compression` typischerweise als speichersparendes Feature für Anwendungsfälle wie Elastic Observability und Elastic Security angesehen wird, zeigt dieser Blog seine Wirksamkeit als Hebel zur Leistungsoptimierung bei der Suche.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/improve-elasticsearch-performance-best-compression</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/improve-elasticsearch-performance-best-compression</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Sherry Ger, Ryan Eno]]></dc:creator>
            <pubDate>Fri, 23 Jan 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5141a8c2618337207d848ce16b258a86885955b2-1600x1034.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p></p><p>Bei der Optimierung von Elasticsearch für Workloads mit hoher Parallelität besteht der Standardansatz darin, den Arbeitsspeicher zu maximieren, um den Arbeitsdatensatz im Speicher zu halten und so eine geringe Suchlatenz zu erreichen. Daher wird <a href="https://www.elastic.co/docs/reference/elasticsearch/index-settings/index-modules"><code>best_compression</code></a> selten für Such-Workloads berücksichtigt, da es in erster Linie als Speichereinsparungsmaßnahme für Elastic Observability und Elastic Security betrachtet wird, in denen Speichereffizienz Vorrang hat.</p><p>In diesem Blog zeigen wir, dass, wenn die Datensatzgröße den OS-Seitencache deutlich übersteigt, <code>best_compression</code> die Suchleistung und Ressourceneffizienz verbessert, indem der I/O-Engpass reduziert wird.</p><h2><strong>Das Setup</strong></h2><p>Unser Anwendungsfall ist eine Suchanwendung mit hoher Parallelität, die auf <a href="https://www.elastic.co/docs/deploy-manage/deploy/elastic-cloud/ec-change-hardware-profile#ec-profiles-compute-optimized-arm">Elastic Cloud CPU-optimierten Instanzen</a> ausgeführt wird.</p><ul><li>Datenvolumen: ~500 Millionen Dokumente</li><li>Infrastruktur: 6 Elastic Cloud (Elasticsearch Service)-Instanzen (jede Instanz: 1,76 TB Speicher | 60 GB RAM | 31,9 vCPUs)</li><li>Verhältnis von Arbeitsspeicher zu Speicher: Ungefähr 5 % des gesamten Datensatzes passen in den Arbeitsspeicher</li></ul><h2><strong>Die Symptome: hohe Latenz</strong></h2><p>Wir haben beobachtet, dass sich die Suchlatenz deutlich verschlechterte, wenn die Anzahl der aktuellen Anfragen um 19:00 Uhr stark anstieg. Wie in Abbildung 1 und Abbildung 2 zu sehen ist, erreichte der Datenverkehr einen Spitzenwert von 400 Anfragen pro Minute und Elasticsearch-Instanz, während die durchschnittliche Abfragezeit auf über 60 ms sank.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f9c6cc1882e7db24336c65c54bbc1d38dcdb7fa3-697x311.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/bbb705ae2fcd14c81d335bf322346caf3bf33765-996x618.png" style="width: 100%;" /><p>Die CPU-Auslastung blieb nach der anfänglichen Verarbeitung der Verbindungen relativ niedrig, was darauf hindeutet, dass die Rechenleistung nicht der Engpass war.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/cec15a28d2d22e9adedd2951bb2334b3717890a1-1494x730.png" style="width: 100%;" /><p>Es zeigte sich eine starke Korrelation zwischen dem Abfragevolumen und den Seitenfehlern. Mit zunehmenden Anfragen beobachteten wir einen proportionalen Anstieg der Seitenfehler, mit einem Höchststand von etwa 400.000 pro Minute. Dies deutete darauf hin, dass der aktive Datensatz nicht in den Seitencache passte.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/511f2f10300a9d10ba3d7a82b9a8c8d567ac5636-1492x678.png" style="width: 100%;" /><p>Gleichzeitig schien die Heap-Nutzung der JVM normal und unauffällig zu sein. Dies schloss Probleme mit der Garbage Collection aus und bestätigte, dass der Engpass I/O war.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/336bbad638f866304358dba1d06ee987de0f23cf-1490x568.png" style="width: 100%;" /><h2><strong>Die Diagnose: I/O gebunden</strong></h2><p>Das System war I/O-gebunden. <a href="https://www.elastic.co/blog/elasticsearch-caching-deep-dive-boosting-query-speed-one-cache-at-a-time">Elasticsearch nutzt den OS-Seitencache, um Indexdaten aus dem Speicher bereitzustellen</a>. Wenn der Index zu groß für den Cache ist, lösen Abfragen kostspielige Festplatten-Lesevorgänge aus. Während die typische Lösung darin besteht, horizontal zu skalieren (Nodes/RAM hinzufügen), wollten wir zunächst alle Möglichkeiten zur Effizienzsteigerung unserer bestehenden Ressourcen ausschöpfen.</p><h2><strong>Die Lösung</strong></h2><p>Standardmäßig verwendet Elasticsearch <a href="https://en.wikipedia.org/wiki/LZ4_(compression_algorithm)">LZ4-Kompression</a> für seine Indexsegmente und findet so ein Gleichgewicht zwischen Geschwindigkeit und Größe. Wir stellten die Hypothese auf, dass ein Wechsel zu <code>best_compression</code> (das <a href="https://en.wikipedia.org/wiki/Zstd">zstd</a> verwendet) die Größe der Indizes verringern würde. Durch den geringeren Speicherbedarf kann ein größerer Prozentsatz des Index im Seitencache gespeichert werden, wodurch ein vernachlässigbarer Anstieg der CPU-Auslastung (für die Dekomprimierung) gegen eine Reduzierung der Festplatten-I/O eingetauscht wird.</p><p>Um <code>best_compression</code>zu aktivieren, haben wir die Daten mit der Indexeinstellung <code>index.codec: best_compression</code>neu indexiert. Alternativ könnte dasselbe Ergebnis erreicht werden, indem der Index geschlossen, der Indexcodec auf <code>best_compression</code>zurückgesetzt und dann eine Segmentzusammenführung durchgeführt wird.</p><pre><code>POST my-index/_close
PUT my-index/_settings
{
    "codec": "best_compression"
}
  
POST my-index/_open  
POST my-index/_forcemerge?max_num_segments=1</code></pre><h2><strong>Die Ergebnisse</strong></h2><p>Die Ergebnisse bestätigten unsere Hypothese: Die verbesserte Speichereffizienz führte direkt zu einer erheblichen Steigerung der Suchleistung, ohne dass die CPU-Auslastung anstieg.</p><p>Durch die Anwendung von <code>best_compression</code> wurde die Indexgröße um etwa 25 % reduziert. Obwohl die Reduzierung geringer ausfiel als bei sich wiederholenden Log-Daten, erhöhte diese 25%ige Reduzierung effektiv unsere Seitencache-Kapazität um denselben Faktor.</p><p>Beim nächsten Auslastungstest (ab 17:00 Uhr) war der Traffic sogar noch höher und erreichte seinen Höhepunkt bei 500 Anfragen pro Minute pro Elasticsearch-Node.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/fc1902f396cb2115c0013155ad07f6eb87389c60-660x309.png" style="width: 100%;" /><p>Trotz der höheren Last war die CPU-Auslastung geringer als in der vorherigen Ausführung. Die erhöhte Nutzung im früheren Test war wahrscheinlich auf den Overhead durch übermäßige Seitenfehlerbehandlung und Festplatten-I/O-Verwaltung zurückzuführen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/15699ef4c65b3f0a9f8a3e1bae8bb18f7b647025-819x352.png" style="width: 100%;" /><p>Entscheidend ist, dass die Seitenfehler deutlich zurückgingen. Selbst bei höherem Durchsatz lagen die Fehler bei etwa &lt;200.000 pro Minute, verglichen mit &gt;300.000 im Basistest.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f76ca967976d740af88a9359b66041701abb46fc-764x340.png" style="width: 100%;" /><p>Obwohl die Seitenfehlerergebnisse immer noch nicht optimal waren, wurde die Abfragedienstzeit um etwa 50 % reduziert und lag selbst bei höherer Last unter 30 ms.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/750ec1c59b8eb5069aed4c066d856ecea82d5bca-620x311.png" style="width: 100%;" /><p></p><h2><strong>Fazit: best_compression zum Suchen</strong></h2><p>Für Such-Anwendungsfälle, in denen das Datenvolumen den verfügbaren physischen Speicher übersteigt, ist <code>best_compression</code> ein kraftvoller Hebel zur Leistungsoptimierung.</p><p>Die herkömmliche Lösung für Cache-Fehler besteht darin, den Arbeitsspeicher (RAM) zu skalieren. Allerdings haben wir durch die Reduzierung der Indexgröße das gleiche Ziel erreicht: Maximierung der Dokumentanzahl im Seiten-Cache. Unser nächster Schritt ist es, die <a href="https://www.elastic.co/blog/space-savings-a-lesser-known-benefit-of-index-sorting-in-elasticsearch"><strong>Indexsortierung</strong></a> zu untersuchen, um den Speicher weiter zu optimieren und noch mehr Leistung aus unseren bestehenden Ressourcen herauszuholen.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Agent Builder jetzt GA: Versenden Sie kontextabhängige Agenten in wenigen Minuten]]></title>
            <description><![CDATA[Agent Builder ist jetzt allgemein verfügbar (GA). Erfahren Sie, wie Sie damit schnell kontextgesteuerte KI-Agenten entwickeln können.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/agent-builder-elastic-ga</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/agent-builder-elastic-ga</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Anish Mathur, Evan Castle]]></dc:creator>
            <pubDate>Thu, 22 Jan 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6840eb7dbb884055ab0e965dcfd614fec54936af-2210x1440.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Wir freuen uns, die allgemeine Verfügbarkeit von Agent Builder in Elastic Cloud Serverless und in der kommenden Version 9.3 bekannt zu geben. Agent Builder nutzt die Leistungsfähigkeit von Elasticsearch als Plattform für Kontextentwicklung, um schnell kontextbezogene, datenorientierte KI-Agenten zu entwickeln.</p><p>Agenten gewinnen an Bedeutung, getrieben durch ihr Potenzial, Effizienzsteigerungen und bessere Kundenerlebnisse zu liefern. Aber in der Praxis ist es schwierig, Agenten den richtigen Kontext zu bieten, insbesondere wenn sie mit unübersichtlichen, unstrukturierten Unternehmensdaten arbeiten. Entwickler müssen Tools, Prompts, Zustand, Schlussfolgerungslogik, Modelle und vor allem den relevanten Kontext aus Geschäftsquellen abrufen, um genaue Ergebnisse und Aktionen zu liefern. Elastic Agent Builder bietet diese Kernkomponenten für die Entwicklung sicherer, zuverlässiger, kontextgesteuerter Agenten.</p><h2>Kernfunktionen von Agent Builder</h2><p>Agent Builder nutzt die langfristigen Investitionen von Elastic in die Suchrelevanz und die Retrieval-Augmented Generation und arbeitet daran, Elasticsearch zur besten Vektordatenbank zu machen, um die Entwicklung kontextbezogener, datenorientierter KI-Agenten zu vereinfachen.</p><p>Mit Agent Builder können Sie:</p><ul><li>Starten Sie sofort mit einem integrierten Dialogagenten, der Fragen beantworten, Analysen durchführen und Untersuchungen über alle Daten in Elasticsearch anstoßen kann.</li><li>Wechseln Sie schnell von komplexen unstrukturierten Daten zu einem benutzerdefinierten Agenten mit konfigurationsbasierter Entwicklungserfahrung.</li><li>Nutzen Sie die erstklassige, hybride Suchrelevanz durch integriertes ES|QL oder benutzerdefinierte Tools, um die Kontextqualität und Agentenzuverlässigkeit zu verbessern.</li><li>Komplexe Workflows (Vorschau) als wiederverwendbare Werkzeuge ausführen, um Daten anzureichern, Einträge zu aktualisieren, Nachrichten zu senden und vieles mehr für eine regelbasierte Automatisierung.</li><li>Verbinden Sie sich mit Datenquellen außerhalb von Elasticsearch über Workflows und MCP, um den Kontext für Agenten zu korrelieren und zu kombinieren.</li><li>Integrieren Sie beliebige Agenten- oder Anwendungsframeworks mithilfe von integrierten und benutzerdefinierten Tools, die über MCP bereitgestellt werden, sowie mit der Möglichkeit zur Verbindung mit externen MCPs (Vorschau), Unterstützung für A2A und vollständige API-Unterstützung.</li><li>Erweitern Sie die Fähigkeiten von Agent Builder durch Integration mit Drittanbieterlösungen wie LlamaIndex für komplexe Dokumentenverarbeitung oder Arcade.dev für sicheren, strukturierten Toolzugriff.</li></ul><p>Um die Funktionen von Agent Builder weiter zu erweitern, führen wir Elastic Workflows ein, unsere neuen regelbasierten Automatisierungsfunktionen, jetzt in der technischen Vorschau. Für organisatorische Aufgaben benötigen Agenten manchmal Sicherheit und Zuverlässigkeit von regelbasierten Aktionen, die oft notwendig sind, um eine bestimmte Geschäftslogik umzusetzen. Elastic Workflows bietet Agenten eine einfache, deklarative Möglichkeit, interne und externe Systeme zu orchestrieren, um Aktionen durchzuführen, Daten und Kontext zu erfassen und zu transformieren. Workflows sind vollständig zusammensetzbar, ereignisgesteuert und flexibel und können einem Agenten über MCP als Werkzeuge zur Verfügung gestellt werden.</p><h2>In wenigen Minuten vom Daten zum Agenten</h2><p>Die Entwicklung von Agenten kann wochenlange Vorarbeit erfordern, um separate Datenspeicher zu konsolidieren, manuelle Pipelines zu erstellen, Abfragen zu optimieren und komplexe Orchestrierungen zu verwalten. Agent Builder verkürzt die Entwicklungszeit für Agenten, indem es die Notwendigkeit für separate Datenspeicher, Vektordatenbanken, RAG-Pipelines, Suchschichten, Abfrageübersetzer und Tool-Orchestratoren beseitigt, sodass Sie sich auf die Agentenlogik und die Anwendungsbereitstellung konzentrieren können.</p><p>Agent Builder integriert nativ die Primitiven der Elasticsearch-Plattform, um die Agentenentwicklung zu beschleunigen.</p><ul><li>Beginnen Sie mit einem integrierten Dialogsystem, das sofort mit Ihren indexierten Daten chatten und argumentieren kann.</li><li>Integrieren Sie Agenten in Anwendungen, Dashboards oder CI/CD-Systeme mit interaktivem Zugriff über Kibana, APIs oder MCP und A2A.</li><li>Nutzen Sie die Standardwerkzeuge, um Ihre Datenstruktur zu verstehen, den passenden Index auszuwählen, optimierte hybride, semantische und strukturierte Abfragen zu generieren und konfigurierbare Visualisierungen mit ES|QL auf Basis von natürlichsprachlichen Eingabeaufforderungen zu erstellen.</li></ul><p>Um tiefer einzutauchen, probieren Sie eine vollständige <a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch">praktische Anleitung</a>.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b55b63eae40f72952967cc8f3ea4df4cd62d7d70-1080x608.gif" style="width: 100%;" /><h2>Bauen Sie auf Elasticsearch auf, einer vollständigen Datenplattform für Kontext-Engineering</h2><p>Für KI-Agenten ist die Qualität des Kontexts entscheidend, um effektives Denken zu ermöglichen und die Gefahr von Halluzinationen zu verringern. Für viele Enterprise-KI-Agenten sind die Geschäftsdaten, die für die Ausführung einer Aufgabe erforderlich sind, der wichtigste Kontext. Als massiv skalierbarer Datenspeicher, Vektordatenbank und führender Anbieter relevanter Daten bietet Elasticsearch bereits viele leistungsstarke Kontext-Engineering-Primitive. Context Engineering geht über die einfache retrieval-augmented generation hinaus, indem es Ihnen ermöglicht, die Art und Weise, wie Daten abgerufen, sortiert, gefiltert und Agenten präsentiert werden, individuell anzupassen und zu skalieren, wodurch Rauschen und Mehrdeutigkeiten reduziert werden.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/419b9b6f13739e0a8983249d8ac31478e73dac89-1600x901.png" style="width: 100%;" /><p>Elasticsearch liefert eine Kontext-Engine, die lexikalische Suche, Vektorsuche und strukturierte Filterung für den Abruf kombiniert und <a href="https://www.elastic.co/search-labs/blog/context-engineering-relevance-ai-agents-elasticsearch">die Leistung von LLMs erheblich verbessert</a>, indem sichergestellt wird, dass das Modell auf relevantem und präzisem Kontext arbeitet. Diese Funktion wird durch agentische Abrufe unterstützt, zusammen mit integrierten Werkzeugen und einer Suchlogik, die automatisch die richtigen Indizes auswählt und natürliche Sprache in optimierte Kontextanfragen umwandelt.</p><p>Mit Agent Builder können Sie sicherstellen, dass Agenten zuerst den relevantesten Kontext erhalten, indem Sie die Relevanz und das Ranking steuern. So können Sie die Logik für Bewertung, Ranking und Filterung feinabstimmen. Mit Elasticsearch können Sie kontrollieren, was wichtig ist, warum es wichtig ist und wie es priorisiert wird, anstatt sich auf ein undurchsichtiges Abrufverhalten zu verlassen. Grundlage hierfür ist Elasticsearch als Skalierbarkeits-Datenplattform, mit der sich alle Ihre Daten – von Texten über Vektoren und Metadaten bis hin zu Logs und mehr – auf einer Plattform speichern und skalieren lassen, was die Kontextverwaltung für Agenten vereinfacht.</p><h2>Führen Sie komplexe Workflows als wiederverwendbare Tools aus</h2><p>Während KI-Agenten das Denken für komplexe Aufgaben ermöglichen, hängt ein Großteil der Automatisierung davon ab, regelbasierte Aktionen zuverlässig auszuführen, die eine spezifische Geschäftslogik durchsetzen. Elastic Workflows bietet eine einfache, deklarative Möglichkeit, interne und externe Systeme zu orchestrieren, um Aktionen auszuführen, Kontext oder Daten zu sammeln und diese als Teil der Agenten zu integrieren. Die in YAML definierten Workflows sind vollständig zusammensetzbar, so dass sie so einfach oder so komplex sein können, wie es die Aufgabe erfordert. Dies gibt Agenten eine effiziente Möglichkeit, über die Elasticsearch-Platform und solutions hinweg sowie mit Anwendungen von Drittanbietern zu agieren.</p><p>Die Integration eines Workflows mit Agent Builder kann in drei Schritten erfolgen (Voraussetzung: Workflows mit <a href="https://github.com/elastic/workflows">den hier</a> angegebenen Details aktivieren)</p><p>1. Erstellen und speichern Sie einen neuen Workflow mit dem einfachen YAML-basierten Editor mit integrierter Autovervollständigung und Testfunktion.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/308888bf3d2fa013f9391a55be6a6fbd458b6dac-1600x998.png" style="width: 100%;" /><p>2. Erstellen Sie ein neues Tool in Agent Builder mit dem Typ „Workflow“ und geben Sie eine Beschreibung an, die dem Agenten hilft zu bestimmen, wann das Workflow-Tool verwendet werden soll.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c04810d30d226112c3610bd58e208607b213fc3d-1600x945.png" style="width: 100%;" /><p>3. Fügen Sie das Workflow-Tool zu Ihrem benutzerdefinierten Agenten hinzu.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/724cd4ac93c46efb0d339fd140e5caf138f8150f-1600x948.png" style="width: 100%;" /><p>4. Das ist es! Jetzt kann der Agent den Workflow innerhalb eines Gesprächs aufrufen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8dfdd726ab89e31c48b79372650ce33946713dca-1600x929.png" style="width: 100%;" /><h2>Ihr Agent, Ihre Regeln</h2><p>Agent Builder bindet Sie nicht an ein einzelnes Entwicklungsparadigma. Stattdessen ist es darauf ausgelegt, offene, flexible Entwicklungsansätze für Agenten mit vollständiger Kontrolle über Daten, Relevanz, Modelle, Interoperabilität, Sicherheit und Agentendesign zu ermöglichen.</p><p>Mithilfe benutzerdefinierter Agentendefinitionen können Sie genau auswählen, auf welche Tools ein Agent zugreifen darf, benutzerdefinierte Systemaufforderungen einbetten, die Anweisungen des Agenten anpassen und Sicherheitsgrenzen definieren. Die Agenten bleiben modellunabhängig, sodass Sie flexibel ein bevorzugtes LLM konfigurieren können, sowohl nativ als auch im gesamten Ökosystem, ohne an einen einzigen Anbieter gebunden zu sein.</p><p>Entwickeln Sie erweiterbare Tools, die domänenspezifische Logik kapseln (z. B. spezifische Indexfilter, ES|QL-Joins, analytische Pipelines) und schränken Sie diese für einen sicheren Einsatz in der Produktion ein. Vollständige API-Unterstützung ermöglicht die Interoperabilität mit anderen agentischen Frameworks, mit nativer Unterstützung für das Model Context Protocol (MCP). Die A2A-Integration bedeutet, dass Sie Ihre Elastic-Agenten anderen Frameworks, Diensten und Client-Apps zugänglich machen können, indem Sie dieselben Daten und dieselbe Context-Engineering-Logik für alle Integrationen wiederverwenden.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5e903ba24ffb3f40231e901f63bd494c89cb7757-1600x1004.png" style="width: 100%;" /><p>Agent Builder unterstützt eine flexible, offene Entwicklung und ist so konzipiert, dass er sich problemlos in gängige Agenten-Frameworks und -Plattformen integrieren lässt. Diese Integrationen können für die Bereitstellung effektiver Agenten von entscheidender Bedeutung sein. Wie <strong>Sam Partee, Mitbegründer von Arcade.dev</strong> , beschreibt:</p><p><em>„Agentische Systeme scheitern heute, weil die Verbindung von KI mit Werkzeugen und Daten komplex ist.“ Elastic Agent Builder mit Arcade.dev bietet Entwicklern eine strukturierte und sichere Möglichkeit, die Art und Weise zu steuern, wie Agenten Kontext abrufen, Argumente liefern und handeln, und ermöglicht so die Weiterentwicklung von Demo- zu Produktionsversionen.</em></p><p>Agent Builder nutzt außerdem die Erweiterbarkeit von Elasticsearch zur Verarbeitung komplexer Daten. Wie <strong>Jerry Liu, CEO von LlamaIndex </strong>beschreibt:</p><p><em>„Das Erschließen des Unternehmenskontextes aus unstrukturierten Datenquellen ist der Schlüssel zum Aufbau effektiver Agenten. Elastic Agent Builder in Kombination mit der komplexen Dokumentenverarbeitung von LlamaIndex stärkt die kritische Kontextschicht und hilft Teams dabei, Daten abzurufen, zu verarbeiten und aufzubereiten, damit Agenten genauer argumentieren und bessere Ergebnisse erzielen können.“</em></p><h2>Was können Sie erstellen?</h2><p>Agent Builder wird bereits für eine Vielzahl von Anwendungsfällen eingesetzt. Nachfolgend finden Sie einige Beispiele und Referenzarchitekturen für den Einstieg in die Agentenarbeit:</p><ul><li><strong>Infrastruktur automatisieren: </strong>In Support-Szenarien wurden Agenten zum Lesen, Nachdenken und Chatten eingesetzt, aber bisher können sie nicht die Infrastruktur berühren, die sie möglicherweise verwalten müssen. Das Ingenieurteam von Elastic hat im Rahmen eines Hackathons einen Agenten für die <a href="https://www.elastic.co/search-labs/blog/agent-builder-augmented-infrastructure">automatische Verwaltung der Infrastruktur</a> entwickelt. Der Agent untersucht aktiv Probleme mit der Anwendungsinfrastruktur und ergreift automatisierte Maßnahmen. Es verwendet Workflows, um Konfigurationen zu optimieren, auf Probleme zu reagieren und Ressourcen zu skalieren, alles auf der Grundlage eines intelligenten Verständnisses der Infrastrukturprotokolle.</li><li><strong>Sicherheitsbedrohungsanalyse: </strong>Ein Sicherheitsschwachstellen-Agent wurde mit Elastic Agent Builder, MCP und Elasticsearch entwickelt. Es automatisiert die Bedrohungsanalyse durch die Korrelation interner Sicherheitsdaten mit externen Bedrohungsinformationen. Der Agent führt semantische Suche über historische Vorfälle und Konfigurationen durch, erweitert die Ergebnisse mit Live-Internetdaten und wendet LLM-Argumentation an, um Umweltrelevanz zu bewerten, Risiken zu priorisieren und umsetzbare Sanierungen zu erstellen. Sehen Sie sich die <a href="https://www.elastic.co/search-labs/blog/agent-builder-mcp-reference-architecture-elasticsearch">Referenzarchitektur</a>an<strong>.</strong></li><li><strong>Technischer Kundensupport: </strong>Agenten können mehrere Unterstützungsaufgaben ausführen, darunter Ticketzusammenfassungen, Issue-Deduplizierung und -Erstellung sowie tiefgehende technische Untersuchungen. Agent Builder ermöglicht dies mit mehrstufigen, hybriden Suchen, um nur die relevantesten verwandten Probleme, Lösungen und Verfahren zu finden und Hypothesen zu Grundursachen sowie Behebungspläne zu formulieren. Agent Builder kann die Architektur komplexer <a href="https://www.elastic.co/blog/generative-ai-customer-support-elastic-support-assistant">Unterstützungssysteme</a> vereinfachen und die Zeit bis zur Bereitstellung beschleunigen.</li><li><strong>Produkt- und Inhaltserkennung:</strong> Agent Builder vereinfacht den Prozess der <a href="https://www.elastic.co/search-labs/blog/build-voice-agents-elastic-agent-builder">Bereitstellung komplexer Produktkataloge für Konversationserlebnisse</a> und bietet Unternehmen gleichzeitig die Flexibilität, ihre eigene Geschäftslogik und ihre eigenen Anforderungen zu pflegen.</li><li><strong>Selbst erstellen:</strong> Nehmen Sie am <a href="https://elasticsearch.devpost.com/">Agent Builder Hackathon</a> teil, der vom 22. Januar bis 27. Februar 2026 stattfindet. Arbeiten Sie mit der Community zusammen, um kontextgesteuerte, mehrstufige KI-Agenten zu entwickeln, die Suche, Workflows, Tools und Argumentation kombinieren, um reale Aufgaben zu automatisieren*</li></ul><h2>Beginnen Sie jetzt mit der Erstellung benutzerdefinierter Agenten</h2><p>Starten Sie mit einer <a href="https://cloud.elastic.co/registration?onboarding_token=search&amp;pg=en-enterprise-search-page">Elastic Cloud-Testversion</a> und sehen Sie sich <a href="https://www.elastic.co/docs/solutions/search/elastic-agent-builder">hier</a> die Dokumentation an. Für bestehende Kunden ist Agent Builder in Cloud Serverless und auf dem Enterprise Tier in Elastic Cloud Hosted und selbstverwaltet verfügbar.</p><p>* <a href="https://elasticsearch.devpost.com/rules">Klicken Sie hier</a> für die vollständigen Bedingungen und Teilnahmevoraussetzungen für den Hackathon</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Erstellen von Sprachagenten mit Elastic Agent Builder]]></title>
            <description><![CDATA[Wir untersuchen, wie Sprachagenten funktionieren und wie man sie mit Elastic Agent Builder und LiveKit erstellt.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/build-voice-agents-elastic-agent-builder</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/build-voice-agents-elastic-agent-builder</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Kenneth Kreindler]]></dc:creator>
            <pubDate>Thu, 22 Jan 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/43ceabb9e2c0966261c188bd40e03178d5a91e5c-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>KI war bisher in einer Glasbox gefangen. Man gibt Befehle ein, sie antwortet mit Text, und das war&#x27;s. Das ist zwar nützlich, aber distanziert, als würde man jemanden hinter einem Bildschirm beobachten. Dieses Jahr, 2026, wird das Jahr sein, in dem die Wirtschaft diese Glasbox zerschlagen und KI-Agenten in Produkte integrieren wird, wo sie wirklich einen Mehrwert bieten.</p><p>Eine der Möglichkeiten, das Glas zu zerbrechen, ist die Einführung von <em>Voice Agents</em>, d. h. von KI-Agenten, die menschliche Sprache erkennen und computergenerierte Audiosignale synthetisieren. Mit dem Aufkommen von Transkriptionen mit geringer Latenz, schnellen Large Language Models (LLMs) und Text-zu-Sprach-Modellen, die menschlich klingen, ist dies möglich geworden.</p><p>Sprachagenten benötigen außerdem Zugriff auf Geschäftsdaten, um wirklich wertvoll zu werden. In diesem Blog-Artikel erfahren wir, wie Sprachagenten funktionieren, und erstellen einen für ElasticSport, ein fiktives Outdoor-Sportartikelgeschäft, mit <a href="https://livekit.io/">LiveKit</a> und <a href="https://www.elastic.co/elasticsearch/agent-builder">Elastic Agent Builder</a>. Unser Sprachagent wird kontextbewusst sein und mit unseren Daten arbeiten.</p><h2>So funktionierts</h2><p>In der Welt der Sprachagenten gibt es zwei Paradigmen: Das erste verwendet Sprache-zu-Sprache-Modelle, das zweite eine Sprachpipeline, die aus Sprache-zu-Text, LLM und Text-zu-Sprache besteht. Sprach-zu-Sprache-Modelle haben ihre eigenen Vorteile, aber Sprachpipelines bieten viel mehr Anpassungsmöglichkeiten bei den verwendeten Technologien und der Kontextverwaltung sowie Kontrolle über das Verhalten des Agenten. Wir werden uns auf das Sprachpipeline-Modell konzentrieren.</p><h3>Hauptkomponenten</h3><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b237501903f9c3a71fe1b7755c3990e40c5495c8-1600x653.png" style="width: 100%;" /><h4>Transkription (Sprache-zu-Text)</h4><p>Die Transkription ist der Einstiegspunkt für die Sprachpipeline. Die Transkriptionskomponente nimmt rohe Audio-Frames als Eingabe, transkribiert Sprache in Text und gibt diesen Text aus. Der transkribierte Text wird gepuffert, bis das System erkennt, dass das Sprechen des Nutzers beendet ist, woraufhin die LLM-Generierung gestartet wird. Verschiedene Drittanbieter bieten Transkriptionen mit geringer Latenz an. Berücksichtigen Sie bei der Auswahl eines Anbieters die Latenz und die Transkriptionsgenauigkeit und stellen Sie sicher, dass dieser Anbieter gestreamte Transkripte unterstützt.</p><p></p><p>Beispiele für Drittanbieter-APIs: <a href="https://www.assemblyai.com/">AssemblyAI</a>, <a href="https://deepgram.com/product/speech-to-text">Deepgram</a>, <a href="https://platform.openai.com/docs/guides/realtime-transcription">OpenAI</a>, <a href="https://elevenlabs.io/speech-to-text">ElevenLabs</a></p><h4>Wendeerkennung</h4><p>Die Wendeerkennung ist die Komponente der Pipeline, die erkennt, wann der Sprecher zu Ende gesprochen hat und die Generierung beginnen soll. Eine gängige Methode dazu ist ein Voice Activity Erkennung (VAD)-Modell wie <a href="https://github.com/snakers4/silero-vad">Silero VAD</a>. VAD nutzt Audio-Energiepegel, um zu erkennen, wenn ein Audiosignal Sprache enthält und wann das Sprechen beendet ist. Allerdings kann VAD allein den Unterschied zwischen einer Pause und dem Ende der Rede nicht erkennen. Aus diesem Grund wird es oft mit einem Modell für das Ende der Äußerung kombiniert, das auf der Grundlage des Zwischentranskripts oder des Rohaudiosignals vorhersagt, ob der Sprecher zu Ende gesprochen hat.</p><p>Beispiele (Hugging Face): <a href="https://huggingface.co/livekit/turn-detector">Livekit/Wendeerkennung</a>, <a href="https://huggingface.co/pipecat-ai/smart-turn-v3">pipecat-ai/smart-turn-v3</a></p><h4>Agent</h4><p>Der Agent ist der Kern einer Sprachpipeline. Er ist verantwortlich für das Verstehen der Absicht, das Erfassen des richtigen Kontexts und das Formulieren einer Antwort im Text-Format. <a href="https://www.elastic.co/elasticsearch/agent-builder">Elastic Agent Builder</a>, mit integrierten Reasoning-Funktionen, Werkzeug-Bibliothek und Workflow-Integration, ermöglicht die Erstellung eines Agenten, der Ihre Daten verarbeiten und mit externen Diensten interagieren kann.</p><h4>LLM (Text-zu-Text)</h4><p>Bei der Auswahl eines LLM für Elastic Agent Builder sind zwei Hauptmerkmale zu berücksichtigen: die LLM-Benchmarks und die Zeit bis zum ersten Token (TTFT).</p><p>Die Reasoning-Benchmarks geben Aufschluss darüber, wie gut das LLM in der Lage ist, korrekte Reaktionen zu generieren. Zu berücksichtigende Benchmarks sind solche, die die Einhaltung von Mehrrundengesprächen und Intelligenz-Benchmarks bewerten, wie z. B. MT-Bench und der Datensatz „Humanity&#x27;s Last Exam“.</p><p>TTFT-Benchmarks bewerten, wie schnell das Modell sein erstes Ausgabetoken erzeugt. Es gibt andere Arten von Latenz-Benchmarks, aber TTFT ist besonders wichtig für Sprachagenten, da die Audiosynthese sofort nach Empfang des ersten Tokens beginnen kann, was zu einer geringeren Latenz zwischen den Runden und zu einer natürlich wirkenden Konversation führt.</p><p>In der Regel muss man einen Kompromiss zwischen diesen beiden Merkmalen eingehen, da schnellere Modelle oft schlechtere Ergebnisse bei Reasoning-Benchmarks erzielen.</p><p>Beispiele (Hugging Face): <a href="https://huggingface.co/openai/gpt-oss-20b">openai/gpt-oss-20b</a>, <a href="https://huggingface.co/openai/gpt-oss-120b">openai/gpt-oss-120b</a></p><h4>Synthese (Text-zu-Sprache)</h4><p>Der letzte Teil der Pipeline ist das Text-to-Speech-Modell. Diese Komponente ist für die Umwandlung der Textausgabe des LLM in hörbare Sprache zuständig. Ähnlich wie beim LLM ist die Latenz ein Merkmal, auf das bei der Auswahl eines Text-zu-Sprache-Anbieters zu achten ist. Die Latenz bei der Umwandlung von Text in Sprache wird durch die Zeit bis zum ersten Byte (TTFB) gemessen. Das ist die Zeit, die vergeht, bis das erste Audiobyte empfangen wird. Ein niedrigeres TTFB reduziert auch die Rundenlatenz.</p><p>Beispiele: <a href="https://elevenlabs.io/text-to-speech-api">ElevenLabs</a>, <a href="https://cartesia.ai/sonic">Cartesia</a>, <a href="https://www.rime.ai/">Rime</a></p><h4>Erstellen der Sprachpipeline</h4><p>Elastic Agent Builder kann auf mehreren verschiedenen Ebenen in eine Sprachpipeline integriert werden:</p><ol><li>Nur Agent Builder-Tools: Sprache-zu-Text → LLM (mit Agent Builder-Tools) → Text-zu-Sprache</li><li>Agent Builder als MCP: Sprache-zu-Text → LLM (mit Agent Builder-Zugang über MCP) → Text-zu-Sprache</li><li>Agent Builder als Kern: Sprache-zu-Text → Agent Builder → Text-zu-Sprache</li></ol><p>Für dieses Projekt habe ich den Ansatz Agent Builder als Kern gewählt. Mit diesem Ansatz kann der volle Funktionsumfang des Agent Builder und Workflows genutzt werden. Das Projekt verwendet LiveKit, um Sprache-zu-Text, Wendeerkennung und Text-zu-Sprache zu orchestrieren, und implementiert einen benutzerdefinierten LLM-Knoten, der direkt mit Agent Builder integriert ist.</p><h2>Elastic Support-Sprachagent</h2><p>Wir werden einen benutzerdefinierten Support-Sprachagenten für ein fiktives Sportgeschäft namens ElasticSport erstellen. Kunden können die Hotline anrufen, um Produktempfehlungen bitten, Produktdetails finden, den Bestellstatus prüfen und Bestellinformationen per SMS erhalten. Um dies zu erreichen, müssen wir zunächst einen benutzerdefinierten Agenten konfigurieren und Tools für die Ausführung von Elasticsearch Query Language (ES|QL)-Abfragen und -Workflows erstellen.</p><a href="https://videos.elastic.co/watch/jpX6D21URZuh3PtXVV7Ks6">Watch video on Vidyard</a><h3>Konfigurieren des Agenten</h3><h4>Prompt</h4><p>Der Prompt teilt dem Agenten mit, welche Persönlichkeit er annehmen und wie er reagieren soll. Wichtig ist, dass es einige sprachspezifische Hinweise gibt, die sicherstellen, dass die Reaktionen korrekt in Audio umgewandelt werden und Missverständnisse elegant behoben werden.</p><pre><code>You are a Sales Assistant at ElasticSport, an outdoor sport shop specialized in hiking and winter equipment. 

[Profile]
- name: Iva
- company: ElasticSport
- role: Sales Assistant
- language: en-GB
- description: ElasticSport virtual sales assistant

[Context]
- Ask clarifying questions to understand the context.
- Use available tools to answer the user's question.
- Use the knowledge base to retrieve general information

[Style]
- Be informative and comprehensive.
- Maintain a professional, friendly and polite tone.
- Mimic human behavior and speech patterns.
- Be concise. Do not over explain initially

[Response Guideline]
- Present dates in spelled-out month date format (e.g., January fifteenth, two thousand and twenty-four).
- Avoid the use of unpronounceable punctuation such as bullet points, tables, emojis.
- Respond in plain text, avoid any formatting.
- Spell out numbers as words for more natural-sounding speech.
- Respond in short and concise sentences. Responses should be 1 or 2 sentences long.

[ERROR RECOVERY]
### Misunderstanding Protocol
1. Acknowledge potential misunderstanding
2. Request specific clarification</code></pre><h4>Workflows</h4><p>Wir fügen einen kleinen Workflow hinzu, um eine SMS über die Messaging-API von Twilio zu versenden. Der Workflow wird dem benutzerdefinierten Agenten als Tool zur Verfügung gestellt, so dass der Agent dem Anrufer während des Gesprächs eine SMS senden kann. Dadurch kann der Anrufer zum Beispiel fragen: „Können Sie mehr Informationen über <em>X</em> per SMS senden?“</p><pre><code>name: send sms
enabled: true
triggers:
  - type: manual
inputs:
  - name: message
    type: string
    description: The message to send to the phone number.

  - name: phone_number
    type: string
    description: The phone number to send the message to.

consts:
  TWILIO_ACCOUNT: "****"
  BASIC_AUTH: "****"
  FROM_PHONE_NNUMBER: "****"
steps:
  - name: http_step
    type: http
    with:
      url: https://api.twilio.com/2010-04-01/Accounts/{{consts.TWILIO_ACCOUNT}}/Messages.json
      method: POST
      headers:
        Content-Type: application/x-www-form-urlencoded
        Authorization: Basic {{consts.BASIC_AUTH | base64_encode}}
      body: From={{consts.FROM_PHONE_NNUMBER}}&To={{inputs.phone_number}}&Body={{inputs.message}}
      timeout: 30s</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b057e71b0a7c50eb3da47cd4f95e77ec7b4c6126-1600x1245.png" style="width: 100%;" /><h4>ES|QL-Tools</h4><p>Mit den folgenden Tools kann der Agent relevante Antworten geben, die auf realen Daten beruhen. Das Beispiel-Repository enthält ein Setup-Skript zur Initialisierung von Kibana mit Produkt-, Auftrags- und Wissensdatenbank-Datensätzen.</p><ul><li><strong>Product.search</strong></li></ul><p>Der Produktdatensatz enthält 65 fiktive Produkte. Dies ist ein Beispieldokument:</p><pre><code>{
      "sku": "ort3M7k",
      "name": "Ortovox Free Rider 26 Backpack",
      "price": 189,
      "currency": "USD",
      "image": "https://via.placeholder.com/150",
      "description": "The Ortovox Free Rider 26 is a technical freeride backpack with a dedicated safety compartment and diagonal ski carry system. Perfect for backcountry missions.\n\nKey Features:\n- 26L capacity\n- Diagonal ski carry system\n- Safety equipment compartment\n- Helmet holder\n- Hydration system compatible",
      "category": "Accessories",
      "subCategory": "Backpacks",
      "brand": "Ortovox",
      "sizes": ["One Size"],
      "colors": ["Black", "Blue", "Orange"],
      "materials": ["Nylon", "Polyester"]
    }</code></pre><p>Die Felder „Name“ und „Beschreibung“ sind als <code>semantic_text</code> abgebildet, wodurch das LLM die semantische Suche über ES|QL nutzen kann, um relevante Produkte abzurufen. Die hybride Suchabfrage führt einen semantischen Abgleich in beiden Feldern durch, wobei Treffer im Namensfeld mithilfe eines Boosts etwas höher gewichtet werden.</p><p>Die Abfrage ruft zunächst die 20 besten Ergebnisse ab, geordnet nach ihrer anfänglichen Relevanzbewertung. Diese Ergebnisse werden dann basierend auf ihrem Beschreibungsfeld mit dem <code>.rerank-v1-elasticsearch</code> Inferenzmodell neu bewertet und schließlich auf die fünf relevantesten Produkte reduziert.</p><pre><code>type: ES|QL
toolId: products.search
description: Use this tool to search through the product catalogue by keywords.
query: |
    FROM products
        METADATA _score
      | WHERE
          MATCH(name, ?query, {"boost": 0.6}) OR
            MATCH(description, ?query, {"boost": 0.4})
      | SORT _score DESC
      | LIMIT 20
      | RERANK ?query
            ON description
            WITH {"inference_id": ".rerank-v1-elasticsearch"}
      | LIMIT 5

parameters:
    query: space separated keywords to search for in catalogue</code></pre><ul><li><strong>Knowledgebase.search</strong></li></ul><p>Die Wissensdatenbank-Datensätze enthalten Dokumente mit folgender Struktur, wobei die Felder „Titel“ und „Inhalt“ als semantischer Text gespeichert werden:</p><pre><code>{
        id: "8273645",
        createdAt: "2025-11-14",
        title: "International Orders",
        content: `International orders are processed through our international shipping partner. Below are the countries we ship to and average delivery times.
        Germany: 3-5 working days
        France: 3-5 working days
        Italy: 3-5 working days
        Spain: 3-5 working days
        United Kingdom: 3-5 working days
        United States: 3-5 working days
        Canada: 3-5 working days
        Australia: 3-5 working days
        New Zealand: 3-5 working days
        `
}</code></pre><p>Und das Tool verwendet eine ähnliche Abfrage wie das <code>product.search</code> -Tool:</p><pre><code>type: "ES|QL"
toolId: knowledgebase.search
description: Use this tool to search the knowledgebase.
query: |
  FROM knowledge_base
    METADATA _score
  | WHERE
      MATCH(title, ?query, {"boost": 0.6}) OR
      MATCH(content, ?query, {"boost": 0.4})
  | SORT _score DESC
  | LIMIT 20
  | RERANK ?query
      ON content
      WITH {"inference_id": ".rerank-v1-elasticsearch"}
  | LIMIT 5

parameters:
  query: space separated keywords or natural language phrase to semantically search for in the knowledge base</code></pre><ul><li><strong>Orders.search</strong></li></ul><p>Das letzte Tool, das wir hinzufügen werden, ist dasjenige, das zum Abrufen von Bestellungen nach <code>order_id</code> verwendet wird:</p><pre><code>type: "ES|QL"
toolId: order.search
description: Use this tool to retrieve an order by its ID.
query: |
  FROM orders
    METADATA _score
  | WHERE order_id == ?order_id
  | SORT _score DESC
  | LIMIT 1

parameters:
  order_id: "the ID of the order"</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d22bdd540a95b5a9c2bd5f308620835e8e6f7ecb-1600x1361.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d91709a50fb5391876b714885242d998b2b21027-1600x1443.png" style="width: 100%;" /><p>Nach der Konfiguration des Agenten und dem Anhängen dieser Workflows und ES|QL-Tools an den Agenten kann der Agent in Kibana getestet werden.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8691f41624247a6b1352d158c970031e1426ce5e-1600x1056.png" style="width: 100%;" /><p>Abgesehen vom Aufbau eines ElasticSport-Supportagenten können der Agent, die Workflows und die Tools auch für andere Anwendungsfälle angepasst werden, beispielsweise für einen Vertriebsagenten, der Leads qualifiziert, einen Serviceagenten für Hausreparaturen, Reservierungen für ein Restaurant oder einen Terminplanungsagenten.</p><p></p><p>Der letzte Teil ist die Verknüpfung des Agenten, den wir gerade erstellt haben, mit LiveKit-, Text-to-Speech- und Speech-to-Text-Modellen. Das am Ende dieses Blogbeitrags verlinkte Repository enthält einen benutzerdefinierten Elastic Agent Builder LLM-Knoten, der mit LiveKit verwendet werden kann. Ersetzen Sie einfach <code>AGENT_ID</code> durch Ihren eigenen Wert und verknüpfen Sie es mit Ihrer Kibana-Instanz.</p><h2>Erste Schritte</h2><p>Sehen Sie sich den Code an und probieren Sie es hier selbst <a href="https://github.com/KDKHD/elastic_agent_builder_livekit">aus</a>. </p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Agent Builder, jenseits der Chatbox: Einführung von Augmented Infrastructure]]></title>
            <description><![CDATA[Lernen Sie Elastic Agent Builder mit Augmented Infrastructure kennen, einen KI-Agenten, der augmentierte Operationen, augmentierte Entwicklung und augmentierte Synthetics ermöglicht.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/agent-builder-augmented-infrastructure</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/agent-builder-augmented-infrastructure</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Alexander Wert, Bill Easton, Gil Raphaelli, Graham Hudgins, Norrie Taylor]]></dc:creator>
            <pubDate>Thu, 22 Jan 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e08daf78f328e826f39d06329f6a5487f75d178d-1272x700.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p><strong>Das ist kein Gerede. Wir handeln.</strong></p><p>Wir haben alle den Aufstieg von KI-Agenten gesehen. Sie sind hervorragend darin, Texte zusammenzufassen, Code-Snippets zu schreiben und Fragen anhand der Dokumentation zu beantworten. Doch für uns im Bereich DevOps und Site Reliability Engineering (SRE) gab es eine frustrierende Einschränkung. Die meisten Agenten sind im Call Center-Paradigma gefangen, d. h. sie können lesen, denken und chatten, aber sie können die Infrastruktur, die sie verwalten sollen, nicht erreichen und berühren.</p><p>Für unser neuestes Hackathon-Projekt haben wir beschlossen, diese Einschränkung aufzuheben.</p><p>Wir haben <strong>Augmented Infrastructure</strong> entwickelt: einen Infrastruktur-Copiloten, der Ihnen nicht nur Ratschläge gibt, sondern auch Ihre Live-Umgebung erstellt, bereitstellt, überwacht und repariert.</p><h2><strong>Das Problem: Kopieren, Neuformatieren, Einfügen</strong></h2><p>Standardagenten agieren isoliert. Wenn Ihre App ausfällt und das Unternehmen 5 Millionen $ kostet, kann Ihnen ein Standard-Agent die Bedienungsanleitung vorlesen, wie Sie das Problem beheben können. Aber <em>Sie</em> müssen trotzdem die Arbeit machen. Kopieren Sie den Code, formatieren Sie ihn für Ihre Umgebung um und fügen Sie ihn in Ihr Terminal ein.</p><p>Wir wollten einen Agenten, der den Unterschied zwischen dem <em>Sprechen über</em> Kubernetes und dem <em>Konfigurieren von</em> Kubernetes versteht.</p><h2><strong>Die Engine: Was ist der Elastic Agent Builder?</strong></h2><p>Um das zu realisieren, haben wir nicht bei null angefangen. Wir haben es auf <a href="https://www.elastic.co/elasticsearch/agent-builder"><strong>Elastic Agent Builder</strong></a> aufgebaut. Für diejenigen, die es noch nicht kennen: Elastic Agent Builder ist ein Framework, das für die schnelle Entwicklung von Agenten konzipiert wurde und als Brücke zwischen einem großen Sprachmodell (LLM) (in unserer Demo haben wir Google Gemini verwendet) und privaten Daten, die in Elasticsearch gespeichert sind, fungiert.</p><p>Agent Builder kann für dialogbasierte KI verwendet werden, indem er auf internen Daten wie Dokumenten oder Protokollen basiert. Aber sein leistungsstärkstes Feature ist die Möglichkeit, <strong>Tools</strong> zuzuweisen. Mithilfe dieser Tools kann das LLM die Chat-Schnittstelle verlassen, um spezifische Aufgaben auszuführen. Wir haben erkannt, dass wir, wenn wir dieses Feature bis an seine Grenzen ausreizen, Agent Builder in eine Automatisierungs-Hochburg verwandeln könnten.</p><h2><strong>So funktioniert es: Die erste Version wird erstellt</strong></h2><p>Als wir mit dem Projekt angefangen haben, wussten wir, dass wir die Agenten in die Lage versetzen wollten, die Außenwelt zu verändern. Wir hatten eine Idee: Was wäre, wenn wir eine Art „Runner“-Software entwickeln würden (die jeden Befehl, den sich der Agent ausdenken könnte, auf dem Host ausführt)? Und dann: Was wäre, wenn die Runner, Elastic Agent Builder und der Nutzer in einem Dreiergespräch wären?</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8dc8317c1301b8eb7b89438529e8d8d17411c95a-1024x559.png" style="width: 100%;" /><p>Wir begannen damit, ein Python-Projekt namens Augmented Infrastructure Runners zu entwickeln, das im Grunde eine <code>while(true)</code>-Schleife war, die jede Sekunde die Elastic Agent Builder Conversations API abfragte und nach einer speziellen Syntax prüfte, die wir erstellt hatten:</p><pre><code>{
	"tool_name": "my_tool",
       "tool_arguments": "\{stringified json arguments\}"
}</code></pre><p>Anschließend aktualisierten wir den Prompt, um ihn mit unserer neuen Tool-Aufruf-Syntax vertraut zu machen. Bill ist Maintainer von <a href="https://gofastmcp.com/getting-started/welcome">FastMCP</a>, dem beliebtesten Framework zum Aufbau von Modellkontextprotokoll-(MCP)-Servern in Python. Er machte sich daran, mit dem FastMCP-Client und dieser neuen Runner-Software MCP-Server einzubinden und deren Tools dem Runner zur Verfügung zu stellen. Als der Agent dies sah, führte er den Tool-Aufruf aus und <code>POST</code> die Ergebnisse zurück an die Konversation, als ob der Nutzer die Ergebnisse gesendet hätte. Dies veranlasste das LLM, auf das Ergebnis zu reagieren, und schon ging es los!</p><p>Das war toll, hatte aber zwei Hauptprobleme:</p><ol><li>Der Agent würde all diese JSON-Daten direkt in die Unterhaltung mit dem Nutzer einspeisen.</li><li>Der früheste Zeitpunkt, an dem Nachrichten über die Konversations-API sichtbar waren, war, als eine Konversationsrunde abgeschlossen wurde (also als das LLM antwortete).</li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7b3755aeae17722ff1bb9677712293e9195f96a0-1058x1034.png" style="width: 100%;" /><p>Also machten wir uns daran, herauszufinden, wie wir dies in den Hintergrund verschieben können.</p><p>Wir sind dann dazu übergegangen, dem Agenten ein Tool namens <code>call_external_tool</code> mit zwei Argumenten zu geben: dem <code>tool_name</code> und den stringifizierten JSON-Tool-Argumenten. Dieser externe Toolaufruf gab kein Ergebnis, war aber wichtigerweise in der <code>GET</code>-Anfrage an die Konversations-API sichtbar. Wir gaben den Runnern dann die Erlaubnis, Dokumente direkt in Elasticsearch zu schreiben, die der Elastic Agent Builder-Agent bei Bedarf abrufen konnte. Der Agent arbeitet immer als Reaktion auf eine Nutzernachricht, daher müssen wir den Agenten mit einer Nutzernachricht starten, damit er nach Ergebnissen sucht und die Verarbeitung fortsetzt. Deshalb haben wir die Agenten gebeten, eine kurze Nachricht in den Chat einzufügen, um die Konversation fortzusetzen:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/61ff59a57c68ed5fad492d19c0580644113a507d-1600x1321.png" style="width: 100%;" /><p>Nun hatten wir also externe Tool-Aufrufe. Wegen des zweiten oben genannten Problems mussten wir jedoch auf diesen letzten Anstoßmechanismus verzichten. Andernfalls erforderte jeder externe Tool-Aufruf eine komplette Gesprächsrunde, um die Ergebnisse abzurufen!</p><h2><strong>Das Beste daraus machen: Einführung von Workflows</strong></h2><p>Zusätzlich zur Elasticsearch-Abfragesprache (ES|QL) und Aufrufen von Index-Such-Tools können Agent Builder-Agenten Elastic-Workflow-basierte Tools aufrufen. Elastic-Workflows bieten eine flexible und einfach zu verwaltende Möglichkeit, eine beliebige Abfolge und Logik von Aktionen auszuführen. Für unsere Zwecke muss der Workflow lediglich eine externe Tool-Anfrage in Elasticsearch speichern und eine ID zurückgeben, anhand derer die Ergebnisse abgefragt werden können. Daraus ergibt sich die folgende einfache Workflow-Definition:</p><pre><code>name: ai-tool-call
enabled: true
triggers:
  - type: manual
inputs:
  - name: runner_id
    type: string
  - name: tool_calls
    type: string

steps:
  - name: store_request
    type: elasticsearch.create
    with:
      index: distributed-tool-requests
      id: "{{inputs.runner_id}}_{{ execution.id }}"
      document:
        request_id: "{{ execution.id }}"
        runner_id: "{{inputs.runner_id}}"
        tool_call: "{{inputs.tool_calls}}"
        status: "unhandled"

  - name: output_result
    type: console
    with:
      message: "Called tool, with execution id: {{ execution.id }}. Use this ID to poll the results."</code></pre><p>Damit können die Runner, anstatt sich darauf zu verlassen, dass die Tool-Aufrufanfrage in die Konversation geschrieben wird, einfach den Elasticsearch- <code>distributed-tool-requests</code> -Index auf neue externe Tool-Anfragen abfragen und die Ergebnisse als Bericht mit dem bereitgestellten <code>execution.id</code> in einen anderen Elasticsearch-Index zurückmelden.</p><p>Damit sind die beiden oben genannten Hauptprobleme beseitigt:</p><ol><li>Der Gesprächsverlauf ist nicht mehr mit den Nutzdaten der externen Tool-Aufrufe überladen.</li><li>Da die Runner den Elasticsearch-Index anstelle des Konversationsverlaufs abfragen, werden sie nicht dadurch blockiert, dass die Konversationsrunde abgeschlossen sein muss, damit die Anfragen an das externe Tool sichtbar werden.</li></ol><p>Der zweite Punkt hat den großen Vorteil, dass die Verarbeitung der externen Tool-Aufrufe bereits in der Denkphase des Agenten beginnt (und nicht erst nach Abschluss der Gesprächsrunde). Dadurch können wir den LLM im System-Prompt anweisen, die Ergebnisse des externen Tools abzufragen, bis diese verfügbar sind, und die Notwendigkeit einer Startnachricht entfällt. Insgesamt hat dies den angenehmen Effekt, dass sich die Konversation natürlicher anfühlt: Das LLM kann mehrere externe Tool-Anfragen innerhalb einer einzigen Konversationsrunde verarbeiten (anstatt für jede Tool-Anfrage eine Konversationsrunde zu benötigen) und kann somit komplexere Nutzeranfragen in einem Durchgang erledigen.</p><h2><strong>Aus den Einzelteilen entsteht ein Ganzes</strong></h2><p>Um die Lücke zwischen dem LLM und dem Server-Rack zu schließen, haben wir eine spezielle Architektur entwickelt, die die Funktionen des Agent Builders nutzt:</p><ol><li><strong>Runner für erweiterte Infrastruktur:</strong> Wir haben Lightweight-Runner innerhalb der Zielumgebungen (Server, Kubernetes-Cluster, Cloud-Konten) bereitgestellt. Diese Runner sind direkt mit Elastic verbunden und verwenden gesicherte Endpoints und Secrets, die nur dem jeweiligen Runner zur Verfügung stehen.</li><li><strong>ES|QL-Abfrage:</strong> Der Copilot verwendet Elastics <strong>ES|QL</strong>, um hybride Suchen durchzuführen. Er sucht nicht nur nach Wissen; er sucht nach <em>Fähigkeiten</em>. Er fragt die verbundenen Runner ab, um zu sehen, welche Werkzeuge verfügbar sind (zum Beispiel <code>list_ec2_instances</code>, <code>install_helm_chart</code>).</li><li><strong>Workflow-Ausführung:</strong> Sobald der Agent sich für eine Vorgehensweise entschieden hat, erstellt er einen strukturierten Workflow.</li><li><strong>Rückkopplungsschleife:</strong> Die Ausführenden führen den Befehl lokal aus und senden den Bericht an Elasticsearch zurück. Der Copilot liest das Ergebnis aus dem Index und entscheidet den nächsten Schritt.</li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/76be256da722c1965971fc506502768bd890f0c4-1290x1076.png" style="width: 100%;" /><h2><strong>Die Demo: Von Ausfall zu Beobachtbarkeit</strong></h2><a href="https://videos.elastic.co/watch/bRgPJfJLeABjPMMhvy1sbB">Watch video on Vidyard</a><p>Im Video haben wir zwei unterschiedliche Szenarien gezeigt, die die Kraft dieser Architektur demonstrieren.</p><h3><strong>Szenario 1: DevOps-Rettung</strong></h3><p>Wir begannen mit einem Nutzer, der wegen eines Ausfalls von 5 Millionen $ durch einen blinden Winkel in seinem Kubernetes-Cluster in Panik geriet.</p><ul><li><strong>Die Anfrage:</strong> „Wie kann ich sicherstellen, dass dies nicht wieder vorkommt?“</li><li><strong>Die Aktion:</strong> Der Agent hat nicht nur ein Tutorial bereitgestellt. Er identifizierte den Cluster, erstellte die notwendigen Namespaces, generierte Kubernetes-Secrets, installierte den OpenTelemetry Operator und stellte sofort einen Link zu einem Live-APM-Dashboard bereit.</li><li><strong>Das Ergebnis:</strong> Vollständige Kubernetes-Beobachtbarkeit und Anwendungseinblicke, ohne dass der Nutzer auch nur eine einzige YAML-Zeile schreiben muss.</li></ul><h3><strong>Szenario 2: Security-Übergabe</strong></h3><p>Eine Grundregel der Infrastruktursicherheit lautet: Was man nicht sieht, kann man nicht schützen. Während der Durchführung unserer DevOps-Rettung sieht der Agent eine Möglichkeit, die Sicherheit der Umgebung zu verbessern.</p><p>Ausgehend von einer Warnung, die im Rahmen einer früheren Untersuchung im Zusammenhang mit Elastic Observability ausgelöst wurde, zeigen wir, wie ein Sicherheitsexperte direkt mit seiner Infrastruktur kommunizieren kann: erstens, um die Assets und Ressourcen in seiner Cloud-Umgebung aufzulisten; und zweitens, um die notwendigen Tools bereitzustellen, um die Sicherheit der Umgebung zu gewährleisten.</p><ul><li><strong>Entdeckung:</strong> Der Copilot zählte die AWS-Ressourcen für den Sicherheitsexperten auf und identifizierte eine kritische Lücke: eine Amazon Elastic Compute Cloud (EC2)-Instanz und ein Amazon Elastic Kubernetes Service (EKS)-Cluster, bei denen öffentliche Endpoints keinen Endpoint-Schutz besitzen.</li><li><strong>Behebung:</strong> Mit einer einfachen Freigabe stellte der Copilot <strong>Elastic Security</strong> <strong>Erweiterte Erkennung und Reaktion (XDR) und Cloud-Erkennung und -Reaktion (CDR)</strong> für die anfälligen Assets bereit und sicherte die Umgebung in Echtzeit.</li><li><strong>Das Ergebnis:</strong> Schutz der bereitgestellten AWS-Assets und -Ressourcen mit vollständiger Laufzeitsicherheit.</li></ul><h2><strong>Die Zukunft: Alles augmentiert</strong></h2><p>Dieses Projekt beweist, dass Elastic Agent Builder die zentrale Steuerungseinheit für verteilte Operationen sein kann. Wir beschränken uns nicht nur auf die Infrastruktur. Unsere Runner-Technologie kann Folgendes mit Energie versorgen:</p><ul><li><strong>Augmented Synthetics:</strong> Diagnose von TLS-Fehlern bei globalen Runnern.</li><li><strong>Erweiterte Entwicklung:</strong> Erstellung von Pull-Anfragen und Implementierung von CAPTCHAs auf Frontend-Diensten.</li><li><strong>Erweiterter Betrieb:</strong> Automatische Neukonfiguration der DNS-Resolver bei einem Ausfall.</li></ul><h2><strong>Probieren Sie es selbst aus</strong></h2><p>Wir glauben, dass es bei der Zukunft der KI nicht nur um Chat-Support geht, sondern um <strong>erweiterte Infrastruktur</strong>. Es geht darum, einen Partner zu haben, der gemeinsam mit Ihnen bereitstellen, reparieren, beobachten und schützen kann.</p><p>Sehen Sie sich den Code an und probieren Sie ihn noch heute mit Distributed Runners (<a href="https://github.com/strawgate/augmented-infrastructure">GitHub</a>) plus Elastic Agent Builder auf <a href="https://cloud.elastic.co/">Elastic Cloud Serverless</a> aus!</p><ul><li>Erstellen Sie ein serverloses Projekt auf Elastic Cloud.</li><li>Stellen Sie den Code auf einem Runner bereit.</li><li>Richten Sie den Runner ein.</li><li>Konfigurieren Sie Ihre mcp.json.</li><li>Starten Sie den Runner, der automatisch Ihren Agenten und seine Tools erstellt.</li><li>Chatten Sie mit einem Agenten, der Aktionen für Ihre verteilten Runner planen und ausführen kann!</li></ul><p><strong>Das Team: </strong><em>Alex, Bill, Gil, Graham und Norrie</em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Schnellere ES|QL-Statistiken mit Hashtabellen im Schweizer Stil]]></title>
            <description><![CDATA[Wie von der Schweiz inspiriertes Hashing und ein SIMD-freundlicher Entwurf konsistente, messbare Geschwindigkeitssteigerungen in der Elasticsearch-Abfragesprache (ES|QL) liefern.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/esql-swiss-hash-stats</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/esql-swiss-hash-stats</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Chris Hegarty, Matthew Alp, Nik Everet]]></dc:creator>
            <pubDate>Mon, 19 Jan 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/21036e031070f14faccb2b53b22723de2750c391-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Wir haben kürzlich zentrale Teile der Hashtabellenimplementierung von Elasticsearch durch einen Entwurf im Schweizer Stil ersetzt und bis zu 2–3-mal schnellere Build- und Iterationszeiten bei einheitlichen, hochkardinalen Workloads beobachtet. Das Ergebnis ist eine geringere Latenz, ein besserer Durchsatz und eine vorhersehbarere Leistung für die Elasticsearch-Abfragesprache (ES|QL) sowie Statistik- und Analyseoperationen.</p><h2>Warum das wichtig ist</h2><p>Die meisten typischen analytischen Workflows laufen letztendlich auf die Gruppierung von Daten hinaus. Ob es nun um die Berechnung der durchschnittlichen Bytes pro Host, das Zählen von Ereignissen pro Nutzer oder das Aggregieren von Metriken über Dimensionen hinweg geht, die Kernoperation ist immer dieselbe – Schlüssel werden Gruppen zugeordnet und laufende Aggregate aktualisiert.</p><p>In kleinem Maßstab funktioniert fast jede vernünftige Hashtabelle einwandfrei. Bei großer Skalierung (Hunderte Millionen Dokumente und Millionen verschiedener Gruppen) beginnen Details eine Rolle zu spielen. Auslastungsfaktoren, Sondierungsstrategie, Speicherlayout und Cache-Verhalten können den Unterschied zwischen linearer Leistung und einer Flut von Cache-Fehlern ausmachen.</p><p>Elasticsearch unterstützt diese Workloads seit Jahren und wir suchen stets nach Möglichkeiten, die Kernalgorithmen zu modernisieren. Daher haben wir einen neueren, von Schweizer Tabellen inspirierten Ansatz evaluiert und ihn auf die Art und Weise angewendet, wie ES|QL Statistiken berechnet.</p><h2>Was genau sind Schweizer Tabellen?</h2><p>Schweizer Tabellen sind eine Familie moderner Hash-Tabellen, die von Googles SwissTable popularisiert und später in Abseil und anderen Bibliotheken übernommen wurden.</p><p>Traditionelle Hash-Tabellen verbringen viel Zeit damit, Zeiger zu verfolgen oder Schlüssel zu laden, nur um festzustellen, dass sie nicht übereinstimmen. Das definierende Feature von Schweizer Tabellen ist die Fähigkeit, die meisten Anfragen mit einer winzigen, im Cache befindlichen Array-Struktur abzulehnen, die getrennt von Schlüsseln und Werten gespeichert wird und als <em>Kontrollbytes</em> bezeichnet wird, um den Speicherverkehr drastisch zu reduzieren.</p><p>Jedes Kontrollbyte repräsentiert einen einzelnen Slot und kodiert in unserem Fall zwei Dinge: ob der Slot leer ist und einen kurzen Fingerabdruck, der aus dem Hash abgeleitet wird. Diese Steuerbytes sind zusammenhängend im Speicher angeordnet, typischerweise in Gruppen von 16, was sie ideal für die SIMD-Verarbeitung <a href="https://en.wikipedia.org/wiki/Single_instruction,_multiple_data">(Single Instruction, Multiple Data)</a>.</p><p>Anstatt einen Slot nach dem anderen zu testen, scannen Schweizer Tabellen einen gesamten Kontrollbyte-Block mithilfe von Vektorinstruktionen. In einer einzigen Operation vergleicht die CPU den Fingerabdruck des eingehenden Schlüssels mit 16 Slots und filtert leere Einträge heraus. Nur bei den wenigen Kandidaten, die diesen Schnelltest überstehen, ist das Laden und Vergleichen der tatsächlichen Schlüssel erforderlich.</p><p>Dieses Design tauscht eine kleine Menge zusätzlicher Metadaten gegen eine viel bessere Cache-Lokalität und weitaus weniger zufällige Ladevorgänge ein. Wenn die Tabelle wächst und die Sondenketten länger werden, werden diese Eigenschaften immer wertvoller.</p><h2>SIMD im Mittelpunkt</h2><p>Der eigentliche Star der Show ist SIMD.</p><p>Die Steuerbytes sind nicht nur kompakt, sondern auch explizit für die Verarbeitung mit Vektorbefehlen konzipiert. Ein einzelner SIMD-Vergleich kann 16 Fingerabdrücke gleichzeitig überprüfen und verwandelt, was normalerweise eine Schleife wäre, in eine Handvoll breiter Operationen. Zum Beispiel:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/bd418778f0c6144f8f5f18419f6220ac0c935c7a-903x407.png" style="width: 100%;" /><p>In der Praxis bedeutet das:</p><ul><li>Weniger Zweige.</li><li>Kürzere Testketten.</li><li>Weniger Ladevorgänge aus Schlüssel- und Wertspeicher.</li><li>Viel bessere Nutzung der Ausführungseinheiten der CPU.</li></ul><p>Die meisten Suchvorgänge kommen nie über den Control-Byte-Scan hinaus. Wenn sie das tun, ist die verbleibende Arbeit zielgerichtet und vorhersehbar. Das ist genau die Art von Arbeitslast, für die moderne CPUs gut geeignet sind.</p><h2>SIMD unter der Haube</h2><p>Für Leser, die gerne einen Blick hinter die Kulissen werfen, hier eine Erklärung, was beim Einfügen eines neuen Schlüssels in die Tabelle passiert. Wir verwenden die Panama Vector API mit 128-Bit-Vektoren und bearbeiten somit 16 Kontrollbytes parallel.</p><p>Der folgende Ausschnitt zeigt den Code, der auf einem Intel Rocket Lake mit AVX-512 generiert wurde. Obwohl die Anweisungen diese Umgebung widerspiegeln, hängt das Design nicht von AVX-512 ab. Die gleichen hochstufigen Vektoroperationen werden auf anderen Plattformen mit gleichwertigen Befehlen (zum Beispiel AVX2, SSE oder NEON) ausgesendet.</p><pre><code>; Load 16 control bytes from the control block
vmovdqu xmm0, XMMWORD PTR [r9+r10*1+0x10]

; Broadcast the 7-bit fingerprint of the new key across the vector
vpbroadcastb xmm1, r11d

; Compare all 16 control bytes to the new fingerprint
vpcmpeqb k7, xmm0, xmm1
kmovq rbx, k7

; Check if any matches were found
test rbx, rbx
jne <handle_match></code></pre><p>Jede Anweisung hat eine klare Rolle im Einfügeprozess:</p><ul><li><code>vmovdqu</code>: Lädt 16 aufeinanderfolgende Steuerbytes in das 128-Bit-<code>xmm0</code>-Register.</li><li><code>vpbroadcastb</code>: Repliziert den 7-Bit-Fingerabdruck des neuen Schlüssels über alle Spuren des <code>xmm1</code>-Registers.</li><li><code>vpcmpeqb</code>: Vergleicht jedes Steuerbyte mit dem gesendeten Fingerabdruck und erstellt eine Maske mit möglichen Übereinstimmungen.</li><li><code>kmovq</code> + <code>test</code>: Verschiebt die Maske in ein allgemeines Register und überprüft schnell, ob ein Match existiert.</li></ul><p>Schließlich entschieden wir uns dafür, Gruppen von jeweils 16 Kontrollbytes zu untersuchen, da Benchmarks zeigten, dass eine Erweiterung auf 32 oder 64 Bytes mit breiteren Registern keinen messbaren Leistungsvorteil brachte.</p><h2>Integration in ES|QL</h2><p>Die Einführung des Hashing im Schweizer Stil in Elasticsearch war nicht einfach ein direkter Ersatz. ES|QL stellt hohe Anforderungen an die Speicherverwaltung, die Sicherheit und die Integration mit dem Rest der Compute Engine.</p><p>Wir haben die neue Hash-Tabelle eng in die Speicherverwaltung von Elasticsearch integriert, einschließlich des Seitenrecyclers und der Abrechnung von Schutzschaltern, um sicherzustellen, dass die Zuweisungen sichtbar und begrenzt bleiben. Die Aggregationen von Elasticsearch werden dicht gespeichert und durch eine Gruppen-ID indexiert, wodurch das Speicherlayout kompakt und schnell für Iterationen bleibt und zudem bestimmte Leistungsoptimierungen durch zufälligen Zugriff ermöglicht werden.</p><p>Bei Byte-Schlüsseln mit variabler Länge wird der vollständige Hash zusammen mit der Gruppen-ID zwischengespeichert. Dadurch wird die Neuberechnung teurer Hash-Codes während der Sondierung vermieden und die Cache-Lokalität verbessert, indem zusammengehörige Metadaten nahe beieinander gehalten werden. Während des Rehashings können wir uns auf den zwischengespeicherten Hash und die Kontroll-Bytes verlassen, ohne die Werte selbst zu inspizieren, wodurch die Kosten für die Größenänderung niedrig gehalten werden.</p><p>Eine wichtige Vereinfachung in unserer Implementierung besteht darin, dass Einträge niemals gelöscht werden. Dadurch werden <em>Tombstones</em> (Markierungen zur Identifizierung zuvor belegter Steckplätze) überflüssig, und leere Steckplätze bleiben wirklich leer, was das Verhalten der Sonde weiter verbessert und Kontroll-Byte-Scans effizient macht.</p><p>Das Ergebnis ist ein Design, das sich nahtlos in das Ausführungsmodell von Elasticsearch einfügt und gleichzeitig die Leistungsmerkmale beibehält, die Schweizer Tabellen so attraktiv machen.</p><h2>Wie ist die Performance?</h2><p>Bei kleinen Kardinalitäten schneiden die Schweizer Tabellen in etwa gleich gut ab wie die bestehende Implementierung. Das ist zu erwarten: Bei kleinen Tabellen spielen Cache-Effekte eine geringere Rolle und es gibt wenig Optimierungsbedarf.</p><p>Mit zunehmender Kardinalität ändert sich das Bild schnell.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/24900afc47ab07b0e9933f6117b99d0f4613f794-962x599.png" style="width: 100%;" /><p>Die obige Heatmap zeigt die Zeitverbesserungsfaktoren für verschiedene Schlüsselgrößen (8, 32, 64 und 128 Byte) über Kardinalitäten von 1.000 bis 10.000.000 Gruppen hinweg. Mit zunehmender Kardinalität steigt der Verbesserungsfaktor stetig an und erreicht bei Gleichverteilungen Werte von bis zu 2–3x.</p><p>Dieser Trend entspricht genau dem, was die Konstruktion vorhersagt. Höhere Kardinalität führt zu längeren Prüfketten in traditionellen Hash-Tabellen, während die meisten Suchvorgänge nach wie vor in SIMD-freundlichen Kontroll-Byte-Blöcken gelöst werden.</p><h2>Das Cache-Verhalten erzählt die Geschichte</h2><p>Um die Beschleunigungen besser zu verstehen, haben wir denselben JMH-<a href="https://github.com/elastic/elasticsearch/pull/139343/files#diff-d0e0cc91a7495bf36b2d44eacce95f5185d01879e5f6c38089ac7a89aad17da7"><code>benchmarks</code></a> unter Linux <code>perf</code> ausgeführt und Cache- sowie TLB-Statistiken erfasst.</p><p>Im Vergleich zur ursprünglichen Implementierung benötigt die Schweizer Version insgesamt etwa 60 % weniger Cache-Zugriffe. Die Last-Level-Cache-Ladevorgänge sinken um mehr als das Vierfache, und LLC-Ladefehler gehen um mehr als das Sechsfache zurück. Da LLC-Fehlzugriffe oft direkt in Hauptspeicherzugriffe übersetzt werden, erklärt diese Reduzierung allein einen großen Teil der End-to-End-Verbesserung.</p><p>Näher an der CPU beobachten wir weniger L1-Datencache-Fehler und fast 6x weniger TLB-Datenfehler, was auf eine engere räumliche Lokalität und besser vorhersagbare Speicherzugriffsmuster hindeutet.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6e49b7609fba83e33692cb9834552b6ca7e42a83-998x499.png" style="width: 100%;" /><p>Dies ist der praktische Nutzen von SIMD-freundlichen Kontrollbytes. Anstatt Schlüssel und Werte immer wieder von verstreuten Speicherplätzen zu laden, werden die meisten Suchvorgänge durch das Durchsuchen einer kompakten, im Cache befindlichen Struktur gelöst. Weniger berührter Speicher bedeutet weniger Fehltritte, und weniger Fehltritte bedeuten schnellere Abfragen.</p><h2>Fazit</h2><p>Indem wir einen Hashtabellenentwurf im Schweizer Stil verwendeten und uns stark auf SIMD-freundliche Sondierungen konzentrierten, erreichten wir 2–3-fache Beschleunigungen für ES|QL-Statistik-Workloads mit hoher Kardinalität sowie eine stabilere und vorhersehbarere Leistung.</p><p>Diese Arbeit zeigt, wie moderne CPU-fähige Datenstrukturen erhebliche Verbesserungen ermöglichen können, selbst bei bekannten Problemen wie Hash-Tabellen. Hier gibt es mehr Raum für Erkundungen, wie etwa zusätzliche Spezialisierungen von primitiven Typen und die Verwendung in anderen Pfaden mit hoher Kardinalität, wie Joins, die alle Teil der umfassenderen und fortlaufenden Bemühungen sind, die internen Abläufe von Elasticsearch kontinuierlich zu modernisieren.</p><p>Wenn Sie an den Details interessiert sind oder die Arbeit verfolgen möchten, schauen Sie sich diesen <a href="https://github.com/elastic/elasticsearch/pull/139343">Pull Request</a> und den <a href="https://github.com/elastic/elasticsearch/issues/138799">Meta-Issue</a>-Fortschritt auf Github an.</p><p>Viel Spaß beim Hashing!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Verwaltung von agentischem Gedächtnis mit Elasticsearch]]></title>
            <description><![CDATA[Entwurf kontextbewussterer und effizienterer Agenten durch die Gedächtnisverwaltung mit Elasticsearch.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/agentic-memory-management-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/agentic-memory-management-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Someshwaran Mohankumar]]></dc:creator>
            <pubDate>Fri, 16 Jan 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/80907fd072e72d6ec902470b449c9f337957a0d7-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>In der aufstrebenden Disziplin des <strong>Context Engineering</strong> ist es entscheidend, KI-Agenten zur richtigen Zeit mit den richtigen Informationen zu versorgen. Einer der wichtigsten Aspekte von Context Engineering ist die Verwaltung des <strong>Gedächtnisses</strong> der KI. Ähnlich wie Menschen sind KI-Systeme sowohl auf ein Kurzzeitgedächtnis als auch auf ein Langzeitgedächtnis angewiesen, um Informationen abzurufen. Wenn wir wollen, dass große Sprachmodellagenten (LLM) logische Gespräche führen, sich an Nutzerpräferenzen erinnern oder auf früheren Ergebnissen oder Reaktionen aufbauen, müssen wir sie mit effektiven Gedächtnismechanismen ausstatten.</p><p>Schließlich beeinflusst der gesamte Kontext die Reaktionen der KI. <em>Was man hinein gibt, kommt auch wieder heraus</em> trifft in diesem Fall zu.</p><p>In diesem Artikel stellen wir vor, was Kurzzeit- und Langzeitgedächtnis für KI-Agenten bedeuten, insbesondere:</p><ul><li>Der Unterschied zwischen Kurz- und Langzeitgedächtnis.</li><li>Wie sie sich auf Retrieval-Augmented Generation (RAG) Techniken mit Vektordatenbanken wie Elasticsearch beziehen und warum ein sorgfältiges Gedächtnismanagement notwendig ist.</li><li>Die Risiken der Vernachlässigung des Gedächtnisses, einschließlich Kontextüberlauf und Kontextvergiftung.</li><li>Best Practices wie Kontextbereinigung, Zusammenfassung und Abruf nur relevanter Daten, damit das Gedächtnis eines Agenten sowohl nützlich als auch sicher bleibt.</li><li>Abschließend werden wir darauf eingehen, wie das Gedächtnis in Systemen mit mehreren Agenten gemeinsam genutzt und weitergegeben werden kann, damit Agenten mithilfe von Elasticsearch problemlos zusammenarbeiten können.</li></ul><h2>Kurzzeit- versus Langzeitgedächtnis bei KI-Agenten</h2><p>Das <strong><em>Kurzzeitgedächtnis</em></strong> eines KI-Agenten bezieht sich in der Regel auf den unmittelbaren Gesprächskontext oder -zustand – im Wesentlichen auf den aktuellen Chatverlauf oder die letzten Nachrichten in der aktiven Sitzung. Dies umfasst die letzte Anfrage des Nutzers und den jüngsten Nachrichtenaustausch. Es ähnelt den Informationen, die eine Person während eines Gesprächs im Kopf hat.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4fbcc6f68055b2bccefc4176297a4ca50056dc0d-764x498.png" style="width: 100%;" /><p>KI-Frameworks pflegen häufig dieses vorübergehende Gedächtnis als Teil des Agentenzustands (zum Beispiel durch die Verwendung eines Checkpointers, um den Konversationszustand zu speichern, wie in <a href="https://docs.langchain.com/oss/python/langgraph/persistence#checkpoints">diesem Beispiel von LangGraph</a> beschrieben). Das Kurzzeitgedächtnis ist <strong><em>sitzungsbezogen</em></strong>, das heißt, es existiert innerhalb einer einzelnen Diskussion oder Aufgabe und wird zurückgesetzt oder gelöscht, wenn diese Sitzung endet, es sei denn, sie wird explizit an anderer Stelle gespeichert. Ein Beispiel für ein sitzungsgebundenes Kurzzeitgedächtnis wäre der <strong><a href="https://help.openai.com/en/articles/8914046-temporary-chat-faq">temporäre Chat</a>, </strong>der in ChatGPT verfügbar ist.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/150bdf209cda5ed20b59cddf34e624ad1a8016aa-1100x577.png" style="width: 100%;" /><p>Das <strong><em>Langzeitgedächtnis</em></strong> hingegen bezeichnet Informationen, die <strong>über Gespräche oder Sitzungen hinweg</strong> erhalten bleiben. Dies ist das Wissen, das ein Agent im Laufe der Zeit behält: Fakten, die er zuvor gelernt hat, Nutzerpräferenzen oder alle Daten, die wir ihm zum dauerhaften Speichern gegeben haben.</p><p>Das Langzeitgedächtnis wird in der Regel durch Speichern und Abrufen aus einer externen Quelle implementiert, z.B. einer Datei oder einer Vektordatenbank, die sich außerhalb des unmittelbaren Kontextfensters befindet. Im Gegensatz zum kurzfristigen Chatverlauf wird das Langzeitgedächtnis nicht automatisch in jeden Prompt einbezogen. Stattdessen muss der Agent es auf der Grundlage eines bestimmten Szenarios <strong>zurückrufen</strong> oder abrufen, wenn die entsprechenden Tools aufgerufen werden. In der Praxis könnte das Langzeitgedächtnis beispielsweise Nutzerprofilinformationen, frühere Antworten oder Analysen des Agenten oder eine Wissensdatenbank umfassen, die der Agent abfragen kann.</p><p>Nehmen Sie als Beispiel einen Reiseplaner-Agenten, bei dem das <em>Kurzzeitgedächtnis</em> Details der aktuellen Reiseanfrage (Daten, Ziel, Budget) und alle Folgefragen in diesem Chat enthält, während das <em>Langzeitgedächtnis</em> die allgemeinen Reisepräferenzen des Nutzers, vergangene Reisepläne und andere Fakten, die in vorherigen Sitzungen geteilt wurden, speichern könnte. Wenn der Nutzer später zurückkehrt, kann der Agent auf dieses Langzeitgedächtnis zurückgreifen (zum Beispiel, dass der Nutzer Strände und Berge liebt, über ein durchschnittliches Budget von 100.000 INR verfügt, eine Liste mit Zielen hat, die er besuchen möchte, und lieber Geschichte und Kultur als kinderfreundliche Attraktionen erleben möchte), sodass der Nutzer nicht jedes Mal als unbeschriebenes Blatt behandelt wird.</p><p>Das Kurzzeitgedächtnis (Chatverlauf) bietet unmittelbaren Kontext und Kontinuität, während das Langzeitgedächtnis einen umfassenderen Kontext bereitstellt, auf den der Agent bei Bedarf zurückgreifen kann. Die fortschrittlichsten KI-Agenten-Frameworks ermöglichen beides: Sie verfolgen aktuelle Dialoge, um den Kontext zu pflegen, <em>und</em> bieten Mechanismen, um Informationen in einem längerfristigen Repository nachzuschlagen oder zu speichern. Die Verwaltung des Kurzzeitgedächtnisses stellt sicher, dass es innerhalb des Kontextfensters bleibt, während die Verwaltung des Langzeitgedächtnisses dem Agenten hilft, die Antworten auf der Grundlage früherer Interaktionen und Personas zu fundieren.</p><h2>Speicher und RAG im Kontext-Engineering</h2><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/46635aa11ceff89b8d6a26ac3e22da52407d82f3-1600x900.png" style="width: 100%;" /><p><strong><em>Wie geben wir einem KI-Agenten in der Praxis ein nützliches Langzeitgedächtnis?</em></strong></p><p>Ein bedeutender Ansatz für das Langzeitgedächtnis ist das <strong><em>semantische Gedächtnis</em></strong>, das oft über <strong>Retrieval-Augmented Generation (RAG)</strong> implementiert wird. Dabei wird der LLM an einen externen Wissensspeicher oder einem vektorfähigen Datenspeicher wie Elasticsearch gekoppelt. Wenn das LLM Informationen benötigt, die über das hinausgehen, was im Prompt oder im integrierten Training enthalten ist, führt es eine semantische Suche in Elasticsearch durch und fügt die relevantesten Ergebnisse als Kontext in den Prompt ein. Auf diese Weise umfasst der effektive Kontext des Modells nicht nur das aktuelle Gespräch (Kurzzeitgedächtnis), sondern auch relevante, kurzfristig abgerufene Langzeitfakten. Das LLM stützt seine Reaktion dann sowohl auf eigene Schlussfolgerungen als auch auf die abgerufenen Informationen und kombiniert so effektiv Kurzzeitgedächtnis und Langzeitgedächtnis, um eine genauere, kontextbezogene Reaktion zu erzeugen.</p><p><strong>Elasticsearch </strong>kann verwendet werden, um das Langzeitgedächtnis für KI-Agenten zu implementieren. Hier ist ein Beispiel dafür, wie der Kontext aus Elasticsearch für das Langzeitgedächtnis abgerufen werden kann.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/41ccbc7b5171e8170ac300139a963c0708816ba6-1600x900.png" style="width: 100%;" /><p>Auf diese Weise „erinnert“ sich der Agent, indem er nach relevanten Daten sucht, anstatt alles in seinem begrenzten Prompt zu speichern, <strong>wo es zu verschiedenen Risiken kommen kann.</strong></p><p><strong>Die Verwendung von RAG mit Elasticsearch oder einem beliebigen Vektorspeicher bietet mehrere Vorteile:</strong></p><p>Erstens <strong>erweitert</strong> es das Wissen des Modells über seinen Trainingsschnitt hinaus. Der Agent kann aktuelle Informationen oder domänenspezifische Daten abrufen, die das LLM möglicherweise nicht kennt. Dies ist besonders bei Fragen zu aktuellen Ereignissen oder speziellen Themen der Fall.</p><p>Zweitens hilft das Abrufen von Kontext nach Bedarf, Halluzinationen zu reduzieren, besonders da LLMs nicht auf proprietären oder hochspezialisierten Daten im Zusammenhang mit Nischenanwendungsfällen trainiert sind, was sehr wahrscheinlich Halluzinationen zur Folge hat. Damit das LLM nicht rät oder neue Informationen erfindet, da es durch die Bewertung einen Anreiz erhalten hat, wie in dem kürzlich erschienenen OpenAI-Paper <a href="https://arxiv.org/pdf/2509.04664">(Why Language Models Hallucinate</a>) hervorgehoben wird, kann das Modell durch faktische Referenzen aus Elasticsearch geerdet werden. Natürlich hängt das LLM von der Zuverlässigkeit der Daten im Vektorspeicher ab, damit Fehlinformationen wirklich verhindert und die relevanten Daten gemäß den Kernrelevanzmaßen abgerufen werden.</p><p>Drittens ermöglicht RAG es Agenten, mit Wissensdatenbanken zu arbeiten, die weit größer sind als alles, was man jemals in einen Prompt einfügen könnte. Anstatt ganze Dokumente, wie lange Forschungsarbeiten oder politische Dokumente, in das Kontextfenster zu schieben und das Risiko einer Überlastung oder <a href="https://www.elastic.co/search-labs/blog/agentic-memory-management-elasticsearch#context-poisoning">Kontextvergiftung</a> durch irrelevante Informationen bei Schlussfolgerungen des Modells einzugehen, verlässt sich RAG auf <a href="https://www.elastic.co/search-labs/blog/chunking-strategies-elasticsearch">Chunking</a>. Große Dokumente werden in kleinere, semantisch bedeutungsvolle Teile zerlegt, und das System ruft nur die wenigen für die Anfrage relevantesten Abschnitte ab. Auf diese Weise benötigt das Modell keinen Kontext von einer Million Token, um kompetent zu wirken, sondern lediglich Zugriff auf die richtigen Teile eines viel größeren Korpus.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e6897356c9f0940e35a63d005e9cd20bc33e5dd7-1600x931.png" style="width: 100%;" /><p>Es ist erwähnenswert, dass mit dem Wachstum der LLM-Fenster (<a href="https://www.anthropic.com/news/1m-context">einige Modelle unterstützen inzwischen Hunderttausende oder sogar Millionen von Token</a><em>)</em> eine Debatte darüber entstanden ist, ob RAG „tot“ ist. Warum nicht alle Daten in den Prompt einfügen? Wenn Sie das ähnlich sehen, lesen Sie diesen wunderbaren Artikel meiner Kollegen Jeffrey Rengifo und Eduard Martin: <a href="https://www.elastic.co/search-labs/blog/rag-vs-long-context-model-llm">Longer context ≠ better: Why RAG still matters</a>. Dies vermeidet das „Was man hinein gibt, kommt auch wieder heraus“-Problem: Das LLM bleibt auf die wenigen wichtigen Teile fokussiert, anstatt sich durch Rauschen zu arbeiten.</p><p>Nichtsdestotrotz bietet die Integration von Elasticsearch oder einem beliebigen Vektorspeicher in eine KI-Agentenarchitektur ein <strong>Langzeitgedächtnis</strong>. Der Agent speichert Wissen extern und ruft es bei Bedarf als Gedächtniskontext ab. Dies könnte als <em>Architektur</em> implementiert werden, bei der der Agent nach jeder Nutzeranfrage in Elasticsearch nach relevanten Informationen sucht und dann die Top-Ergebnisse an den Prompt anhängt, bevor er das LLM aufruft. Die Reaktion kann auch wieder in das Langzeitgedächtnis gespeichert werden, wenn sie nützliche neue Informationen enthält (wodurch ein Feedback-Loop des Learnings entsteht). Durch die Nutzung eines solchen abrufbasierten Gedächtnisses bleibt der Agent informiert und auf dem neuesten Stand, ohne alles, was er weiß, in jeden Prompt quetschen zu müssen, obwohl das Kontextfenster <em>eine Million Token</em> unterstützt. Diese Technik ist ein Eckpfeiler des Kontext-Engineerings und kombiniert die Stärken von Information Retrieval und generativer KI. </p><p>Hier ist ein Beispiel für einen verwalteten In-Memory-Gesprächszustand, der das Checkpoint-System von LangGraph für das Kurzzeitgedächtnis während der Sitzung verwendet. (Mehr dazu in unserer <a href="https://github.com/someshwaranM/elastic-context-engineering-short-term-long-term-memory">unterstützenden Context Engineering-App</a>.)</p><pre><code># Initialize chat memory (Note: This is in-memory only, not persistent)
memory = MemorySaver()

# Create a LangGraph agent
langgraph_agent = create_react_agent(model=llm, tools=tools, checkpointer=memory)

...
...
# Only process and display checkpoints if verbose mode is enabled
if args.verbose:
    # List all checkpoints that match a given configuration
    checkpoints = memory.list({"configurable": {"thread_id": "1"}})
    # Process the checkpoints
    process_checkpoints(checkpoints)</code></pre><p>So werden <strong>Checkpoints</strong> gespeichert:</p><pre><code>Checkpoint:
Timestamp: 2025-12-30T09:19:41.691087+00:00
Checkpoint ID: 1f0e560a-c2fa-69ec-8001-14ee5373f9cf
User: Hi I'm Som, how are you? (Message ID: ad0a8415-5392-4a58-85ad-84154875bbf2)
Agent: Hi Som! I'm doing well, thank you! How about you? (Message ID: 
56d31efb-14e3-4148-806e-24a839799ece)
Agent:  (Message ID: lc_run--019b6e8e-553f-7b52-8796-a8b1fbb206a4-0)

Checkpoint:
Timestamp: 2025-12-30T09:19:40.350507+00:00
Checkpoint ID: 1f0e560a-b631-6a08-8000-7796d108109a
User: Hi I'm Som, how are you? (Message ID: ad0a8415-5392-4a58-85ad-84154875bbf2)
Agent: Hi Som! I'm doing well, thank you! How about you? (Message ID: 
56d31efb-14e3-4148-806e-24a839799ece)

Checkpoint:
Timestamp: 2025-12-30T09:19:40.349027+00:00
Checkpoint ID: 1f0e560a-b62e-6010-bfff-cbebe1d865f6</code></pre><p>Für das Langzeitgedächtnis führen wir mit Elasticsearch eine semantische Suche durch, um relevante vorherige Gespräche mithilfe von Vektoreinbettungen abzurufen, nachdem wir die Checkpoints in Elasticsearch zusammengefasst und indexiert haben.</p><pre><code>Functions: 
retrieve_from_elasticsearch() 

# Enhanced Elasticsearch retrieval with rank_window and verbose display
def retrieve_from_elasticsearch(query: str, k: int = 5, rank_window: int = None) -> tuple[List[Dict[str, Any]], str]:
    """
    Retrieve context from Elasticsearch with score-based ranking
    
    Args:
        query: Search query
        k: Number of results to return
        rank_window: Number of candidates to retrieve before ranking (default: args.rank_window)
        
    Returns:
        Tuple of (retrieved_documents, formatted_context_string)
    """
    if not es_client or not es_index_name:
        return [], "Elasticsearch is not available. Cannot search long-term memory."
    
    if rank_window is None:
        rank_window = args.rank_window
    
    try:
        # Check if index exists and has documents
        if not es_client.indices.exists(index=es_index_name):
            return [], "No previous conversations stored in long-term memory yet."
        
        # Get document count
        try:
            doc_count = es_client.count(index=es_index_name)["count"]
            if doc_count == 0:
                return [], "Long-term memory is empty. No previous conversations to search."
        except Exception as e:
            return [], f"Error checking memory: {str(e)}"
        
        # Generate embedding for the query
        try:
            query_embedding = embeddings.embed_query(query)
        except Exception as e:
            return [], f"Error generating embedding: {str(e)}"
        
        # Perform semantic search using kNN with rank_window
        try:
            search_body = {
                "knn": {
                    "field": "vector",
                    "query_vector": query_embedding,
                    "k": k,
                    "num_candidates": rank_window  # Retrieve more candidates, then rank top k
                },
                "_source": ["text", "content", "message_type", "timestamp", "thread_id"],
                "size": k
            }
            
            response = es_client.search(index=es_index_name, body=search_body)
            
            if not response.get("hits") or len(response["hits"]["hits"]) == 0:
                return [], "No relevant previous conversations found in long-term memory."
            
            # Extract documents with scores
            retrieved_docs = []
            for hit in response["hits"]["hits"]:
                source = hit["_source"]
                score = hit["_score"]
                retrieved_docs.append({
                    "content": source.get("content", source.get("text", "")),
                    "message_type": source.get("message_type", "unknown"),
                    "timestamp": source.get("timestamp", "unknown"),
                    "thread_id": source.get("thread_id", "unknown"),
                    "score": score
                })
            
            # Format context string
            context_parts = []
            for i, doc in enumerate(retrieved_docs, 1):
                context_parts.append(doc["content"])
            
            context_string = "\n\n".join(context_parts)
            
            # Verbose display
            if args.verbose:
                rich.print(f"\n[bold yellow]🔍 RETRIEVAL ANALYSIS[/bold yellow]")
                rich.print("="*80)
                rich.print(f"[blue]Query:[/blue] {query}")
                rich.print(f"[blue]Retrieved:[/blue] {len(retrieved_docs)} documents (from {rank_window} candidates)")
                rich.print(f"[blue]Total context length:[/blue] {len(context_string)} characters\n")
                
                for i, doc in enumerate(retrieved_docs, 1):
                    rich.print(f"[cyan]📄 Document {i} | Score: {doc['score']:.4f} | Type: {doc['message_type']}[/cyan]")
                    rich.print(f"[cyan]   Timestamp: {doc['timestamp']} | Thread: {doc['thread_id']}[/cyan]")
                    content_preview = doc['content'][:200] + "..." if len(doc['content']) > 200 else doc['content']
                    rich.print(f"[cyan]   Content: {content_preview}[/cyan]")
                    rich.print("-" * 80)
            
            return retrieved_docs, context_string
            
        except Exception as e:
            return [], f"Error searching memory: {str(e)}"
            
    except Exception as e:
        return [], f"Error accessing long-term memory: {str(e)}"</code></pre><p>Nachdem wir untersucht haben, wie Kurzzeit- und Langzeitgedächtnis mithilfe der Checkpoints von LangGraph in Elasticsearch indexiert und abgerufen werden, wollen wir uns etwas Zeit nehmen, um zu verstehen, warum das Indexieren und Speichern der kompletten Konversationen riskant sein kann.</p><h2>Risiken der fehlenden Verwaltung des Kontextgedächtnisses</h2><p>Da wir viel über Kontextgestaltung sowie Kurzzeit- und Langzeitgedächtnis sprechen, ist es wichtig, zu verstehen, was passiert, wenn wir Gedächtnis und Kontext eines Agenten nicht effektiv verwalten.</p><p>Leider kann vieles schiefgehen, wenn der Kontext einer KI extrem lang wird oder falsche Informationen enthält. Wenn die Kontextfenster größer werden, tauchen <strong>neue Fehlerarten</strong> auf, zum Beispiel:</p><ul><li><strong>Kontextvergiftung</strong></li><li><strong>Kontextablenkung</strong></li><li><strong>Kontextverwirrung</strong></li><li><strong>Kontextkonflikt</strong></li><li><strong>Kontextlecks und Wissenskonflikte</strong></li><li><strong>Halluzinationen und Fehlinformationen</strong></li></ul><p>Werfen wir einen Blick auf diese Probleme und andere Risiken, die durch schlechtes Kontextmanagement entstehen:</p><h3>Kontextvergiftung</h3><p><em>Kontextvergiftung</em> bezieht sich darauf, wenn falsche oder schädliche Informationen in den Kontext gelangen und die nachfolgenden Ausgänge des Modells „vergiften“. Ein häufiges Beispiel ist eine Halluzination des Modells, die als Tatsache behandelt und in den Gesprächsverlauf eingefügt wird. Das Modell könnte dann in späteren Reaktionen auf diesem Fehler aufbauen und den Fehler verstärken. In iterativen Agenten-Loops kann eine falsche Information, sobald sie in den gemeinsamen Kontext gelangt (zum Beispiel in einer Zusammenfassung der Arbeitsnotizen des Agenten), immer wieder verstärkt werden. </p><p><a href="https://storage.googleapis.com/deepmind-media/gemini/gemini_v2_5_report.pdf">Forscher von DeepMind haben dies bei der Veröffentlichung des Gemini 2.5-Berichts</a> (TL;DR, mehr dazu <a href="https://www.dbreunig.com/2025/06/17/an-agentic-case-study-playing-pok%C3%A9mon-with-gemini.html">hier</a>) bei einem langjährig <em>Pokémon</em>-spielenden Agenten beobachtet: Wenn der Agent einen falschen Spielzustand halluzinierte und dieser in seinem <em>Kontext </em>(seinem Gedächtnis für Ziele) aufgezeichnet wurde, bildete der Agent <strong>unsinnige Strategien</strong> um ein unmögliches Ziel herum und blieb stecken. Mit anderen Worten: Ein vergiftetes Gedächtnis kann den Agenten auf unbestimmte Zeit auf den falschen Weg bringen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3f2facf5aad67613ad557422e09ec23a66adc0ed-1600x1388.png" style="width: 100%;" /><p>Kontextvergiftung kann aus Versehen oder auch böswillig geschehen, zum Beispiel durch Prompt-Injection-Angriffe, bei denen ein Nutzer oder Dritter eine versteckte Anweisung oder falsche Tatsache einschleust, die der Agent dann erinnert und befolgt.</p><p><strong>Empfohlene Gegenmaßnahmen:</strong></p><p>Basierend auf Einblicken von <a href="https://www.wiz.io/academy/data-poisoning">Wiz</a>, <a href="https://zerlo.net/en/blog/what-is-llm-data-poisoning">Zerlo</a> und <a href="https://www.anthropic.com/research/small-samples-poison">Anthropic</a> konzentrieren sich Gegenmaßnahmen gegen Kontextvergiftung darauf, zu verhindern, dass falsche oder irreführende Informationen in den Prompt, das Kontextfenster oder die Abrufpipeline eines LLM gelangen. Wichtige Schritte umfassen:</p><ul><li>Überprüfen Sie den Kontext regelmäßig: Überwachen Sie die Konversation oder den abgerufenen Text auf alles Verdächtige oder Schädliche, nicht nur den anfänglichen Prompt.</li><li>Verwenden Sie vertrauenswürdige Quellen: Bewerten oder kennzeichnen Sie Dokumente basierend auf ihrer Glaubwürdigkeit, damit das System zuverlässige Informationen bevorzugt und Daten mit niedriger Bewertung ignoriert.</li><li>Achten Sie auf ungewöhnliche Daten: Verwenden Sie Tools, die ungewöhnliche, unpassende oder manipulierte Inhalte erkennen, und entfernen Sie diese, bevor das Modell sie verwendet.</li><li>Filtern Sie Eingänge und Ausgänge: Integrieren Sie Schutzvorkehrungen, damit schädliche oder irreführende Texte nicht so leicht in das System gelangen oder vom Modell wiederholt werden können.</li><li>Halten Sie das Modell mit sauberen Daten auf dem neuesten Stand: Aktualisieren Sie das System regelmäßig mit verifizierten Informationen, um etwaigen fehlerhaften Daten entgegenzuwirken, die durchgerutscht sind.</li><li>Human-in-the-loop: Lassen Sie wichtige Ausgänge von Menschen überprüfen oder mit bekannten, vertrauenswürdigen Quellen vergleichen.</li></ul><p>Auch einfache Nutzergewohnheiten helfen, wie das Zurücksetzen langer Chats, das Teilen nur relevanter Informationen, das Aufteilen komplexer Aufgaben in kleinere Schritte und die Pflege sauberer Notizen außerhalb des Modells.</p><p>In Kombination bilden diese Maßnahmen einen mehrschichtigen Schutz, der LLMs vor Kontextvergiftung schützt und die Genauigkeit und Vertrauenswürdigkeit der Ausgänge gewährleistet.</p><p>Ohne Gegenmaßnahmen, wie hier erwähnt, könnte sich ein Agent an Anweisungen erinnern, z. B. vorherige Richtlinien zu ignorieren<em> </em>oder unwichtige Fakten, die ein Angreifer eingefügt hat, was zu schädlichen Ausgängen führen könnte.</p><h3>Kontextablenkung</h3><p><em>Kontextablenkung</em> liegt vor, wenn ein Kontext so lang wird, dass sich das Modell zu sehr auf den Kontext konzentriert und vernachlässigt, was es während des Trainings gelernt hat. In extremen Fällen ähnelt dies <a href="https://en.wikipedia.org/wiki/Catastrophic_interference"><em>katastrophalem Vergessen</em></a>. Das heißt, das Modell „vergisst“ effektiv sein zugrunde liegendes Wissen und konzentriert sich übermäßig auf die Informationen, die ihm präsentiert werden. Frühere Studien haben gezeigt, dass LLM oft den Fokus verlieren, wenn der Prompt extrem lang ist.</p><p>Der Gemini 2.5-Agent zum Beispiel unterstützte ein Million-Token-Fenster, aber sobald sein Kontext über einen bestimmten Punkt hinauswuchs (etwa 100.000 Token in einem Experiment), begann er, <strong>sich darauf zu fixieren, seine früheren Aktionen zu wiederholen</strong> , anstatt neue Lösungen zu finden. In gewisser Weise wurde der Agent zum Gefangenen seiner umfangreichen Geschichte. Er betrachtete kontinuierlich seinen langen Log früherer Aktionen (den Kontext) und ahmte diese nach, anstatt sein zugrunde liegendes Trainingswissen zu nutzen, um frische und neuartige Strategien zu entwickeln.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e08e5b6d2e8ec7e3511d455985eed3d7fa6241e0-1352x636.png" style="width: 100%;" /><p>Das ist kontraproduktiv. Wir wollen, dass das Modell den relevanten Kontext zur Unterstützung des Denkens nutzt, nicht aber seine Denkfähigkeit außer Kraft setzt. Bemerkenswerterweise zeigen selbst Modelle mit riesigen Fenstern diesen <a href="https://research.trychroma.com/context-rot"><em>Kontextverfall</em></a>: Ihre Leistung verschlechtert sich ungleichmäßig, wenn mehr Token hinzugefügt werden. Es scheint ein <em>Aufmerksamkeitsbudget</em> zu geben. Wie Menschen mit begrenztem Gedächtnis hat ein LLM begrenzte Kapazitäten, sich um Token zu kümmern, und wenn dieses Budget überlastet ist, sinken Präzision und Fokus.</p><p>Kontextablenkung verhindern Sie durch Chunking, die Entwicklung der richtigen Informationen, regelmäßige Kontextzusammenfassungen sowie Bewertungs- und Überwachungstechniken zur Messung der Genauigkeit der Reaktion mittels Scoring.</p><p>Diese Methoden halten das Modell sowohl im relevanten Kontext als auch in seinem zugrundeliegenden Training verankert, wodurch das Risiko von Ablenkungen reduziert und die Gesamtqualität des Schlussfolgerns verbessert wird.</p><h3>Kontextverwirrung</h3><p><em>Kontextverwirrung</em> liegt vor, wenn überflüssiger Inhalt im Kontext vom Modell verwendet wird, um eine Reaktion von geringer Qualität zu generieren. Ein gutes Beispiel ist, einem Agenten eine große Auswahl an Tools oder API-Definitionen zu geben, die er verwenden könnte. Wenn viele dieser Tools nichts mit der aktuellen Aufgabe zu tun haben, kann das Modell dennoch versuchen, sie in unangemessener Weise zu verwenden, einfach weil sie im Kontext vorhanden sind. Experimente haben gezeigt, dass die Bereitstellung weiterer Tools oder <em>Dokumente</em> die Leistung <em>beeinträchtigen</em> kann, wenn sie nicht alle benötigt werden. Der Agent beginnt Fehler zu machen, z. B. die falsche Funktion aufzurufen oder auf irrelevanten Text zu verweisen. </p><p>In einem Fall scheiterte ein kleines <strong>Llama 3.1 8B</strong> Modell an einer Aufgabe, wenn es 46 Tools zu berücksichtigen hatte, war aber erfolgreich, wenn es nur 19 Tools gab. Die zusätzlichen Tools sorgten für Verwirrung, obwohl der Kontext innerhalb der Längenbeschränkungen lag. Das zugrunde liegende Problem ist, dass alle Informationen in der <em>Aufforderung</em> vom Modell bearbeitet werden. Wenn es nicht weiß, dass es etwas ignorieren soll, könnte genau dieser Punkt seinen Ausgang auf unerwünschte Weise beeinflussen. Irrelevante Teile können „einen Teil der Aufmerksamkeit des Modells stehlen“ und es in die Irre führen (zum Beispiel könnte ein irrelevantes Dokument den Agenten dazu veranlassen, eine andere Frage zu beantworten als die gestellte). Kontextverwirrung äußert sich oft darin, dass das Modell eine minderwertige Reaktion produziert, die einen nicht zusammenhängenden Kontext integriert. Mehr dazu in der Forschungsarbeit: <a href="https://arxiv.org/pdf/2411.15399">Less is More: Optimizing Function Calling for LLM Execution on Edge Devices.</a></p><p>Es erinnert uns daran, dass mehr Kontext nicht immer besser ist, vor allem, wenn er nicht nach Relevanz <strong>kuratiert</strong> ist.</p><h3>Kontextkonflikt</h3><p><em>Kontextkonflikte</em> treten auf, wenn <strong>Teile des Kontexts sich widersprechen</strong>, was interne Abweichungen verursacht, die die Argumentation des Modells entgleisen lassen. Ein Konflikt kann auftreten, wenn der Agent mehrere Informationen ansammelt, die miteinander im Konflikt stehen. </p><p>Stellen Sie sich zum Beispiel einen Agenten vor, der Daten aus zwei Quellen abruft: Laut der einen <em>startet Flug A um 17 Uhr</em>, laut der anderen <em>startet Flug A um 18 Uhr</em>. Wenn beide Fakten im Kontext vorkommen, hat das mangelhafte Modell keine Möglichkeit zu wissen, welche richtig ist. Es kann verwirrt werden oder eine falsche oder unähnliche Antwort liefern.</p><p>Kontextkonflikte treten auch häufig in Mehrrundengesprächen auf, bei denen die <strong>früheren Antworten</strong> des Modells noch im Kontext verweilen, zusammen mit später verfeinerten Informationen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/500d7a80dc8db1923f9b5ca84728eed64fa296f7-1316x580.png" style="width: 100%;" /><p>Eine <a href="https://arxiv.org/pdf/2505.06120">Studie</a> von Microsoft und Salesforce zeigt, dass die Endgenauigkeit deutlich sinkt, wenn man eine komplexe Abfrage in mehrere Chatbot-Durchläufe aufteilt (wobei Details schrittweise hinzugefügt werden). Warum ist das so? Weil die frühen Durchläufe partielle oder inkorrekte Zwischenantworten des Modells enthalten und diese im Kontext verbleiben. Wenn das Modell später versucht, mit allen Informationen zu antworten, enthält sein <em>Gedächtnis</em> immer noch diese falschen Versuche, die mit den korrigierten Informationen kollidieren und es vom Kurs abbringen. Im Grunde genommen widerspricht sich der Kontext des Gesprächs selbst. Das Modell kann versehentlich einen veralteten Kontext (aus einem früheren Durchlauf) verwenden, der nach dem Hinzufügen neuer Informationen nicht mehr zutrifft.</p><p>In Agentensystemen sind Kontextkonflikte besonders gefährlich, weil ein Agent Ausgänge verschiedener Tools oder Unteragenten kombinieren kann. Wenn diese Ausgänge nicht übereinstimmen, ist der aggregierte Kontext unbeständig. Der Agent könnte dann stecken bleiben oder sinnlose Ergebnisse produzieren, wenn er versucht, die Widersprüche in Einklang zu bringen. Um Kontextkonflikte zu verhindern, muss sichergestellt werden, dass der Kontext aktuell <strong>und konstant</strong> ist,<strong> </strong>zum Beispiel dass alle veralteten Informationen gelöscht oder aktualisiert wurden und Quellen, die nicht auf Konstanz überprüft wurden, nicht miteinander kombiniert werden.</p><h3>Kontextlecks und Wissenskonflikte</h3><p>In Systemen, in denen mehrere Agenten oder Nutzer einen Speicher teilen, besteht das Risiko, dass Informationen auf andere Kontexte übergehen.</p><p>Wenn sich beispielsweise die Daten-Einbettungen zweier separater Nutzer in derselben Vektordatenbank ohne ordnungsgemäße Zugriffskontrolle befinden, könnte ein Agent, der die Anfrage von Nutzer A beantwortet, versehentlich einen Teil des Speichers von Nutzer B abrufen. Diese <strong><em>kontextübergreifenden Lecks</em></strong> können private Informationen offenlegen oder einfach für Verwirrung bei Reaktionen sorgen.</p><p>Laut den <a href="https://wtit.com/blog/2025/04/17/owasp-top-10-for-llm-applications-2025/">OWASP Top 10 für LLM-Anwendungen</a> müssen Multi-Tenant-Vektordatenbanken gegen solche Lecks schützen:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8f0ccf0b2f7bd6715c14aceee2deffb213d50bd9-1600x936.png" style="width: 100%;" /><p>Laut <a href="https://wtit.com/blog/2025/04/17/owasp-top-10-for-llm-applications-2025/">LLM08:2025 Vector and Embedding Weaknesses</a><em>,</em> ist eines der häufigsten Risiken die Leckage von Kontextinformationen:</p><blockquote><em>In Multi-Tenant-Umgebungen, in denen mehrere Klassen von Nutzern oder Anwendungen dieselbe Vektordatenbank nutzen, besteht die Gefahr von Kontextlecks zwischen Nutzern oder Abfragen. Fehler bei der Datenföderation können auftreten, wenn Daten aus mehreren Quellen einander widersprechen. Das kann auch passieren, wenn ein LLM altes Wissen, das es während des Trainings gelernt hat, nicht mit den neuen Daten aus Retrieval Augmentation ersetzen kann.</em></blockquote><p>Ein weiterer Aspekt ist, dass ein LLM Schwierigkeiten haben könnte, sein <strong>integriertes Wissen</strong> mit neuen Informationen aus dem Gedächtnis zu überschreiben. Wenn das Modell auf eine bestimmte Tatsache trainiert wurde und der abgerufene Kontext das Gegenteil aussagt, kann das Modell verwirrt sein, welcher Aussage es vertrauen soll. Ohne eine geeignete Konzeption könnte der Agent Kontexte verwechseln oder altes Wissen nicht mit neuen Erkenntnissen aktualisieren, was zu veralteten oder falschen Antworten führen könnte.</p><h3><strong>Halluzinationen und Fehlinformationen</strong></h3><p>Die <em>Halluzination </em>(das LLM erfindet plausibel klingende, aber falsche Informationen) ist zwar auch ohne lange Kontexte ein bekanntes Problem, aber schlechtes Gedächtnismanagement kann es verstärken. </p><p>Wenn im Gedächtnis des Agenten eine entscheidende Tatsache fehlt, kann das Modell <strong>die Lücke</strong> einfach mit einer Vermutung füllen, und wenn diese Vermutung dann in den Kontext einfließt (ihn vergiftet), bleibt der Fehler bestehen. </p><p>Der OWASP LLM-Sicherheitsbericht <a href="https://wtit.com/blog/2025/04/17/owasp-top-10-for-llm-applications-2025/"><strong>(LLM09:2025 Misinformation)</strong></a> hebt Fehlinformationen als Kernschwachstelle hervor: LLMs können zuverlässige, aber erfundene Antworten liefern, und Nutzer könnten ihnen zu sehr vertrauen. Ein Agent mit einem schlechten oder veralteten Langzeitgedächtnis könnte getrost etwas zitieren, das im letzten Jahr gestimmt hat, jetzt aber falsch ist, es sei denn, sein Gedächtnis wird auf dem neuesten Stand gehalten. </p><p>Eine übermäßige Abhängigkeit vom Ausgang der KI (entweder durch den Nutzer oder den Agenten selbst in einer Schleife) kann dies verschlimmern. Wenn niemand die Informationen im Gedächtnis überprüft, kann der Agent Unwahrheiten ansammeln. Dies ist der Grund, warum RAG oft verwendet wird, um Halluzinationen zu reduzieren: Durch den Abruf einer maßgeblichen Quelle muss das Modell keine Fakten erfinden. Wenn Sie jedoch ein falsches Dokument abrufen (z. B. eines, das Fehlinformationen enthält) oder wenn eine frühe Halluzination nicht entfernt wird, kann das System diese Fehlinformation in seinen Aktionen weitergeben. </p><p>Fazit: Wird das Gedächtnis nicht verwaltet, kann dies zu <strong>falschen und irreführenden Ausgängen</strong> führen, was womöglich Schäden zur Folge hat, besonders wenn viel auf dem Spiel steht (z. B. schlechte Ratschläge in einem Finanz- oder medizinischen Bereich). Ein Agent benötigt Mechanismen, um seinen Speicherinhalt zu überprüfen oder zu korrigieren, anstatt bedingungslos allem zu vertrauen, was im Kontext steht.</p><p>Zusammengefasst ist es <em>kein</em> Erfolgsrezept, einem KI-Agenten ein unendlich langes Gedächtnis zu geben oder alles Mögliche in seinen Kontext zu werfen.</p><h2>Best Practices für das Gedächtnismanagement in LLM-Anwendungen</h2><p>Um die oben genannten Fallstricke zu vermeiden, haben Entwickler und Forscher eine Reihe von <strong>Best Practices für die Verwaltung von Kontext und Gedächtnis</strong> in KI-Systemen entwickelt. Diese Praktiken zielen darauf ab, den Arbeitskontext der KI übersichtlich, relevant und auf dem neuesten Stand zu halten - hier sind einige der wichtigsten Strategien und Beispiele dafür, wie sie helfen können.</p><h3>RAG: Gezielten Kontext verwenden</h3><p>Ein Großteil der RAG wurde bereits im vorangegangenen Abschnitt behandelt, so dass dieser Abschnitt eine kurze Zusammenfassung praktischer Hinweise bietet:</p><ul><li>Verwenden Sie gezieltes Abrufen, nicht Bulk-Loading: Rufen Sie nur die relevantesten Teile ab, anstatt ganze Dokumente oder vollständige Konversationsverläufe in den Prompt zu laden.</li><li>Behandeln Sie RAG wie einen bedarfsgerechten Gedächtnisabruf: Rufen Sie den Kontext nur dann ab, wenn er benötigt wird, anstatt bei jedem Abruf alles zu verwenden.</li><li>Bevorzugen Sie relevanzbasierte Abrufstrategien: Ansätze wie Top-k semantische Suche, Reciprocal Rank Fusion oder Tool-Loadout-Filterung helfen, Rauschen zu reduzieren und die Verankerung zu verbessern.</li><li>Größere Kontextfenster beseitigen nicht die Notwendigkeit von RAG: Zwei hochrelevante Absätze sind fast immer effektiver als 20 lose zusammenhängende Seiten.</li></ul><p>Allerdings geht es bei RAG nicht darum, mehr Kontext, sondern den richtigen Kontext hinzuzufügen.</p><h3>Tool-Loadout</h3><p><em>Tool-Loadout</em> bedeutet, einem Modell nur die Tools zu geben, die es tatsächlich für eine Aufgabe benötigt. Der Begriff stammt aus dem Gaming-Bereich: Sie wählen ein Loadout, das zur Situation passt. Zu viele Tools verlangsamen den Fortschritt, die falschen führen zum Scheitern. LLM verhalten sich genauso, wie das Forschungspaper <a href="https://arxiv.org/abs/2411.15399">Less is more</a> besagt. Sobald Sie ~30 Tools durchgegeben haben, überschneiden sich die Beschreibungen und das Modell ist verwirrt. Nach etwa 100 Tools ist ein Ausfall nahezu garantiert. Das ist kein Problem mit dem Kontextfenster, sondern eine Kontextverwirrung.</p><p>Eine einfache und effektive Lösung ist <a href="https://arxiv.org/abs/2505.03275"><strong>RAG-MCP</strong></a>. Anstatt jedes Tool in den Prompt zu laden, werden die Tool-Beschreibungen in einer Vektordatenbank gespeichert und nur die relevantesten werden pro Anfrage abgerufen. In der Praxis hält das den Loadout klein und konzentriert, die Prompts werden drastisch verkürzt und die Genauigkeit der Tool-Auswahl kann um das bis zu Dreifache verbessert werden.</p><p>Kleinere Modelle stoßen noch früher an diese Grenze. Die Forschung zeigt, dass ein 8B-Modell mit Dutzenden von Tools versagt, jedoch Erfolg zeigt, sobald das Loadout reduziert wird. Durch die dynamische Auswahl von Tools, in Fällen zunächst mit einem LLM, und die Überlegung, was benötigt wird, kann die Leistung um 44 % gesteigert werden, während gleichzeitig Stromverbrauch und Latenz reduziert werden. Daraus ist zu erkennen, dass die meisten Agenten nur wenige Tools benötigen, mit dem Wachstum Ihres Systems jedoch Tool-Loadout und RAG-MCP zu erstrangigen Designentscheidungen werden.</p><h3>Kontextbereinigung: Begrenzung der Länge des Chatverlaufs</h3><p>Wenn sich ein Gespräch über mehrere Ausführungen erstreckt, kann der angesammelte Chatverlauf zu viel Platz einnehmen, was zu einem Kontextüberlauf führt oder das Modell zu sehr abgelenkt wird. </p><p><em>Trimming</em> bedeutet, programmatisch weniger wichtige Teile des Dialogs zu entfernen oder zu kürzen, während er wächst. Eine einfache Form besteht darin, die ältesten Runden der Konversation abzubrechen, wenn ein bestimmtes Limit erreicht ist, sodass nur die letzten <em>N</em> Nachrichten gespeichert werden. Eine ausgefeiltere Bereinigung könnte irrelevante Abschweifungen oder frühere Anweisungen entfernen, die nicht mehr benötigt werden. Das Ziel ist es, dass <strong>das Kontextfenster übersichtlich</strong> bleibt, ohne alte Nachrichten. </p><p>Wenn der Agent beispielsweise vor 10 Ausführungen ein Teilproblem gelöst hat und seitdem fortgefahren wurde, könnten wir diesen Teil des Verlaufs aus dem Kontext löschen (vorausgesetzt, er wird nicht mehr benötigt). Viele Chat-basierte Implementierungen bieten ein fortlaufendes Fenster mit den neuesten Nachrichten. </p><p>Beim Trimming handelt es sich im Grunde um das „Vergessen“ der ersten Teile eines Gesprächs, sobald diese zusammengefasst wurden oder als irrelevant erachtet werden. Dadurch verringern wir das Risiko von Kontextüberlauffehlern und reduzieren auch die <a href="https://www.elastic.co/search-labs/blog/agentic-memory-management-elasticsearch#context-distraction"><strong>Kontextablenkung</strong></a>, sodass das Modell nicht von alten oder themenfremden Inhalten gestört und abgelenkt wird. Dieser Ansatz ähnelt sehr der Art und Weise, wie Menschen sich vielleicht nicht an jedes Wort aus einem einstündigen Vortrag erinnern, aber die Höhepunkte im Gedächtnis behalten. </p><p>Falls Sie die Kontextbereinigung noch nicht ganz verstanden haben, kann, wie der Autor Drew Breunig <a href="https://www.dbreunig.com/2025/06/26/how-to-fix-your-context.html#tool-loadout:~:text=Provence%20is%20fast%2C%20accurate%2C%20simple%20to%20use%2C%20and%20relatively%20small%20%E2%80%93%20only%201.75%20GB.%20You%20can%20call%20it%20in%20a%20few%20lines%2C%20like%20so%3A">hier</a> hervorhebt, die Verwendung des Provence-Modells (<a href="https://huggingface.co/naver/provence-reranker-debertav3-v1">naver/provence-reranker-debertav3-v1</a>), ein leichter (1,75 GB), effizienter und genauer Kontext-Pruners für die Beantwortung von Fragen gut geeignet sein. Er kann große Dokumente auf nur den relevantesten Text für eine bestimmte Abfrage reduzieren. Sie können ihn in bestimmten Abständen aufrufen.</p><p>So rufen wir das Provence-Reranker-Modell in unserem Code auf, um den Kontext zu bereinigen:</p><pre><code># Context pruning with Provence
def prune_with_provence(query: str, context: str, threshold: Optional[float] = None) -> str:
    """
    Prune context using Provence reranker model
    
    Args:
        query: User's query/question
        context: Original context to prune
        threshold: Relevance threshold (0-1) for Provence reranker.
                   If None, uses args.pruning_threshold.
                   0.1 = conservative (recommended, no performance drop)
                   0.3-0.5 = moderate to aggressive pruning
    
    Returns:
        Pruned context with only relevant sentences
    """
    if provence_model is None:
        return context
    
    if threshold is None:
        threshold = args.pruning_threshold
    
    try:
        # Use Provence's process method
        provence_output = provence_model.process(
            question=query,
            context=context,
            threshold=threshold,
            always_select_title=False,
            enable_warnings=False
        )
        
        # Extract pruned context from output
        pruned_context = provence_output.get('pruned_context', context)
        reranking_score = provence_output.get('reranking_score', 0.0)
        
        # Log statistics
        original_length = len(context)
        pruned_length = len(pruned_context)
        reduction_pct = ((original_length - pruned_length) / original_length * 100) if original_length > 0 else 0
        
        if args.verbose:
            rich.print(f"[cyan]📊 Pruning stats: {pruned_length}/{original_length} chars ({reduction_pct:.1f}% reduction, threshold={threshold:.2f}, rerank_score={reranking_score:.3f})[/cyan]")
        
        return pruned_context if pruned_context else context
        
    except Exception as e:
        rich.print(f"[yellow]⚠️ Error in Provence pruning: {str(e)}[/yellow]")
        rich.print(f"[yellow]⚠️ Falling back to original context[/yellow]")
        return context</code></pre><p>Wir verwenden das Provence-Reranker-Modell (naver/provence-reranker-debertav3-v1), um die Satzrelevanz zu bewerten. Durch eine schwellenwertbasierte Filterung werden Sätze oberhalb der Relevanzschwelle beibehalten. Außerdem führen wir einen Fallback-Mechanismus ein, bei dem wir zum ursprünglichen Kontext zurückkehren, wenn die Bereinigung fehlschlägt. Zu guter Letzt verfolgt das Statistik-Logging den Reduktionsanteil im ausführlichen Modus.</p><h3>Kontextzusammenfassung: Fassen Sie ältere Informationen zusammen, anstatt sie ganz wegzulassen</h3><p><em>Die Zusammenfassung</em> ist eine Ergänzung zum Trimming. Wenn der Verlauf oder die Wissensdatenbank zu umfangreich wird, können Sie das LLM einsetzen, um eine kurze Zusammenfassung der wichtigen Punkte zu erstellen und diese anstelle des vollständigen Inhalts verwenden, so wie in unserem obigen Code geschehen.</p><p>Wenn zum Beispiel ein KI-Assistent ein Gespräch mit 50 Runden geführt hat, könnte das System, anstatt alle 50 Runden an das Modell in Runde 51 zu senden (was wahrscheinlich nicht passt), die Runden 1–40 übernehmen, das Modell diese in einem Absatz zusammenfassen lassen und dann nur diese Zusammenfassung plus die letzten 10 Runden im nächsten Prompt bereitstellen. Auf diese Weise weiß das Modell immer noch, was besprochen wurde, ohne jedes Detail zu benötigen. Frühe Chatbot-Nutzer taten dies manuell, indem sie fragten: „Können Sie zusammenfassen, worüber wir bisher gesprochen haben?“ und dann in einer neuen Sitzung mit der Zusammenfassung fortfuhren. Nun kann dies automatisiert werden. Zusammenfassungen sparen nicht nur Platz im Kontextfenster, sondern können auch <strong>Kontextverwirrung und -ablenkung</strong> verringern, indem zusätzliche Details entfernt und nur die wichtigsten Fakten erhalten bleiben.</p><p>Hier ist unsere Vorgehensweise: Wir nutzen OpenAI-Modelle (Sie können beliebige LLMs verwenden), um den Kontext zu verdichten und gleichzeitig alle relevanten Informationen zu bewahren, wodurch Redundanz und Duplikate vermieden werden.<br/></p><pre><code># Context summarization
def summarize_context(query: str, context: str) -> str:
    """
    Summarize context using LLM to reduce duplication and focus on relevant information
    
    Args:
        query: User's query/question
        context: Context to summarize
        
    Returns:
        Summarized context
    """
    try:
        summary_prompt = f"""You are an expert at summarizing conversation context.

Your task: Analyze the provided conversation context and produce a condensed summary that fully answers or supports the user's specific question.

The summary must:
1. Preserve every fact, detail, and information that directly relates to the question
2. Eliminate redundancy and duplicate information
3. Maintain chronological flow when relevant
4. Focus on information that helps answer: "{query}"

Context to summarize:
{context}

Provide a concise summary that preserves all relevant information:"""

        summary = llm.invoke(summary_prompt).content
        
        if args.verbose:
            original_length = len(context)
            summary_length = len(summary)
            reduction_pct = ((original_length - summary_length) / original_length * 100) if original_length > 0 else 0
            rich.print(f"[cyan]📝 Summarization stats: {summary_length}/{original_length} chars ({reduction_pct:.1f}% reduction)[/cyan]")
        
        return summary
        
    except Exception as e:
        rich.print(f"[yellow]⚠️ Error in context summarization: {str(e)}[/yellow]")
        rich.print(f"[yellow]⚠️ Falling back to original context[/yellow]")
        return context</code></pre><p>Wenn der Kontext zusammengefasst wird, ist es weniger wahrscheinlich, dass das Modell von unwichtigen Details oder vergangenen Fehlern überwältigt wird (vorausgesetzt, die Zusammenfassung ist korrekt). </p><p>Die Zusammenfassung muss jedoch sorgfältig erfolgen. Eine schlechte Zusammenfassung könnte ein entscheidendes Detail auslassen oder sogar zu einem Fehler führen. Es ist im Grunde eine weitere Aufforderung an das Modell („fassen Sie das zusammen“), sodass es zu Halluzinationen oder Nuancierungen kommen kann. Es empfiehlt sich, bei der Zusammenfassung schrittweise vorzugehen und möglicherweise autorisierte Fakten nicht in die Zusammenfassung einzubinden.</p><p>Dennoch hat sich dies als sehr nützlich erwiesen. <a href="https://storage.googleapis.com/deepmind-media/gemini/gemini_v2_5_report.pdf">Im Gemini-Agenten-Szenario </a>war die Zusammenfassung des Kontexts alle ~100.000 Token eine Möglichkeit, der Wiederholungstendenz des Modells entgegenzuwirken. Die Zusammenfassung wirkt wie ein komprimierter Speicher des Gesprächs oder der Daten. Als Entwickler können wir dies umsetzen, indem ein Agent periodisch eine Zusammenfassungsfunktion (vielleicht ein kleineres LLM oder eine eigene Routine) aus dem Gesprächsverlauf oder einem langen Dokument aufruft. Die resultierende Zusammenfassung ersetzt den Originalinhalt im Prompt. Diese Taktik wird häufig verwendet, um Kontexte in Grenzen zu halten und die Informationen zusammenzufassen.</p><h3>Kontextquarantäne: Kontexte nach Möglichkeit isolieren</h3><p>Dies ist insbesondere bei komplexen Agentensystemen oder mehrstufigen Workflows relevant. Die Idee der Kontextsegmentierung besteht darin, eine große Aufgabe in kleinere, isolierte Aufgaben aufzuteilen, von denen jede ihren eigenen Kontext hat, um nicht zu viel Kontext mit umfassendem Inhalt anzuhäufen. Jeder Unteragent oder jede Teilaufgabe bearbeitet einen Bereich des Problems in einem fokussierten Kontext, anschließend integriert ein übergeordneter Agent, Supervisor oder Koordinator die Ergebnisse.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f2de68c3339883d7658e633af3948f29f427e6cf-1600x900.png" style="width: 100%;" /><p>Die <a href="https://www.anthropic.com/engineering/multi-agent-research-system">Forschungsstrategie von Anthropic verwendet mehrere Unteragenten</a>, die jeweils einen anderen Aspekt einer Frage mit eigenen Kontextfenstern untersuchen, und einen Leitagenten, der die zusammengefassten Ergebnisse dieser Unteragenten ausliest. Dieser parallele, modulare Ansatz bedeutet, dass kein einzelnes Kontextfenster zu groß wird. Es reduziert auch die Wahrscheinlichkeit, dass irrelevante Informationen sich vermischen, jeder Thread bleibt beim Thema (keine Kontextverwirrung) und trägt keine unnötige Last, wenn er seine spezifische Teilfrage beantwortet. In gewisser Weise ist es, als würde man separate Gedankenstränge ausführen, die nur ihre Ergebnisse teilen, nicht ihren gesamten Denkprozess.</p><p>In Systemen mit mehreren Agenten ist dieser Ansatz unerlässlich. Wenn Agent A Aufgabe A und Agent B Aufgabe B bearbeitet, besteht für keinen Agenten der Grund, den vollständigen Kontext des anderen zu verwenden, es sei denn, dies ist unbedingt erforderlich. Stattdessen können Agenten nur die notwendigen Informationen austauschen. Zum Beispiel kann Agent A eine konsolidierte Zusammenfassung seiner Ergebnisse an Agent B über einen Supervisor-Agenten weitergeben, während jeder Unteragent seinen ganz eigenen Kontext-Thread pflegt. Bei diesem System ist kein menschliches Eingreifen erforderlich. Es basiert auf einem Überwachungsagenten mit aktivierten Tools, die einen minimalen und kontrollierten Kontextaustausch ermöglichen.</p><p>Dennoch kann die Gestaltung Ihres Systems, sodass Agenten oder Tools mit minimaler notwendiger Kontextüberschneidung arbeiten, die Klarheit und Leistung erheblich verbessern. Das kann man sich wie einen <strong>Microservices für KI</strong> vorstellen. Jede Komponente befasst sich mit ihrem Kontext, und Sie leiten Nachrichten zwischen ihnen auf kontrollierte Weise weiter, anstatt in einem monolithischen Kontext. Diese Best Practices werden oft in Kombination miteinander verwendet. Außerdem verleiht es Ihnen die Flexibilität, überflüssige Verläufe zu kürzen, wichtige ältere Nachrichten oder Unterhaltungen zusammenzufassen, detaillierte Protokolle für langfristigen Kontext an Elasticsearch zu übergeben und bei Bedarf mit Abruf alles Relevante zurückzuholen.</p><p>Wie <a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents#:~:text=While%20some%20models,to%20the%20LLM">hier</a> erwähnt, ist das Leitprinzip, dass der Kontext eine begrenzte und wertvolle Ressource ist. Jeder Token im Prompt sollte sein Geld wert sein, d.h. er sollte zur Qualität des Ausgangs beitragen. Wenn etwas im Gedächtnis seine Funktion nicht erfüllt (oder schlimmer noch, aktiv Verwirrung stiftet), dann sollte es bereinigt, zusammengefasst oder beseitigt werden.</p><p>Als Entwickler können wir nun den Kontext genauso programmieren wie den Code und entscheiden, welche Informationen einbezogen, wie sie formatiert und wann sie weggelassen oder aktualisiert werden sollen. Mithilfe dieser Methoden können wir die LLM-Agenten mit dem dringend benötigten Kontext versorgen, um Aufgaben auszuführen, ohne den zuvor beschriebenen Fehlermodi zum Opfer zu fallen. Daraus resultieren Agenten, die sich an nötige Aufgaben erinnern, unnötige Aufgaben vergessen und alles Nötige gerade rechtzeitig abrufen.</p><h2>Fazit</h2><p>Erinnerungsvermögen wird Agenten nicht einfach hinzufügt, es muss entwickelt werden. Das Kurzzeitgedächtnis ist der Notizblock für die Arbeit des Agenten, das Langzeitgedächtnis sein dauerhafter Wissensspeicher. RAG dient als Verbindung zwischen den beiden und verwandelt einen passiven Datenspeicher wie Elasticsearch in einen aktiven Abrufmechanismus, der Ausgänge erden und den Agenten auf dem neuesten Stand halten kann.</p><p>Erinnerungsvermögen ist jedoch ein zweischneidiges Schwert. Sobald man zulässt, dass der Kontext unkontrolliert wächst, ist mit Vergiftung, Ablenkung, Verwirrung und Konflikten zu rechnen, in gemeinsam genutzten Systemen sogar mit Datenlecks. Deshalb ist die wichtigste Aufgabe hierbei nicht „mehr speichern“, sondern „besser kuratieren“: Selektiv abrufen, aktiv bereinigen, sorgfältig zusammenfassen und das Kombinieren unzusammenhängender Kontexte vermeiden, es sei denn, die Aufgabe erfordert dies wirklich.</p><p>In der Praxis ähnelt gutes Kontext-Engineering einem guten Systemdesign: kleinere, ausreichende Kontexte, kontrollierte Schnittstellen zwischen Komponenten und eine klare Trennung zwischen dem Rohzustand und dem destillierten Zustand, den das Modell tatsächlich sehen soll. Wenn man es richtig macht, führt dies nicht zu einem Agenten, der sich an alles erinnert, sondern zu einem Agenten, der sich zum richtigen Zeitpunkt und aus dem richtigen Grund an die richtigen Dinge erinnert.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Höherer Durchsatz und geringere Latenz: Elastic Cloud Serverless auf AWS erhält einen deutlichen Leistungsschub.]]></title>
            <description><![CDATA[Wir haben die AWS-Infrastruktur für Elasticsearch Serverless auf neuere, schnellere Hardware upgegradet. Erfahren Sie, wie dieser enorme Leistungsschub schnellere Abfragen, besseres Skalieren und niedrigere Kosten liefert.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/elasticsearch-serverless-aws-performance-boost</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/elasticsearch-serverless-aws-performance-boost</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Pete Galeotti, Yuvraj Gupta, Rachel Forshee]]></dc:creator>
            <pubDate>Wed, 14 Jan 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3aa0b481994d2445ba979d3c79fff64c5ee6676a-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Elastic Cloud Serverless ist bereits die endgültige Lösung für Entwickler:innen, die effiziente Such- und KI-Anwendungen ohne die operative Belastung der Infrastruktur entwickeln möchten. Jetzt heben wir die Performance Ihrer serverlosen Projekte auf ein ganz neues Niveau.</p><p>Wir haben ein umfassendes Infrastruktur-Upgrade für alle <a href="https://www.elastic.co/cloud/serverless">Elastic Cloud Serverless-Projekte</a> abgeschlossen, die auf AWS laufen und auf neuere, schnellere Hardware migriert wurden. Diese Änderung wurde automatisch auf alle Serverless-Projekte ausgerollt. Es bietet <strong>einen höheren Durchsatz und geringere Latenz</strong> für Serverless-Projekte mit Elasticsearch, Elastic Observability und Elastic Security auf AWS.</p><h2><strong>Wichtigste Leistungsvorteile für Entwickler:innen</strong></h2><p>Die neue AWS-Hardwareinfrastruktur bildet die Grundlage für alles, was Sie mit Elastic Cloud Serverless tun, und führt zu spürbaren Vorteilen hinsichtlich der Geschwindigkeit und Reaktionsfähigkeit Ihrer Anwendungen.</p><h3><strong>Reduzierte Abfragelatenz … erhöhter Durchsatz</strong></h3><p>Die verbesserte Hardware steigert die Geschwindigkeit der Rechenressourcen erheblich, sodass Ihre Suchanfragen schneller als je zuvor verarbeitet werden.</p><ul><li><strong>Suchen und Vektorsuche:</strong> Egal, ob Sie traditionelle Volltextabfragen durchführen oder modernste Vektorsuche für Ihre <a href="https://www.elastic.co/generative-ai">generative KI- und Retrieval-Augmented-Generation (RAG)-Anwendungen</a> verwenden – Sie werden eine deutliche Verringerung der Latenzzeit feststellen. Interne Benchmarkings zeigten einen durchschnittlichen Rückgang der Suchlatenz um 35 %.</li><li><strong>Schnellere Indexierung:</strong> Die Ingestion-Raten sind optimiert, sodass Sie riesige Datenmengen und komplexe Dokumente mit erhöhtem Durchsatz indexieren können. Das ist besonders wichtig für Anwendungen, die Daten nahezu in Echtzeit anzeigen müssen. Interne Benchmarkings zeigten einen durchschnittlichen Anstieg des Indexierungsdurchsatzes um 26 %.</li></ul><h3><strong>Konstante Leistung unter Last</strong></h3><p>Elastic Cloud Serverless ist so konzipiert, dass es sich dynamisch in Echtzeit an die Nachfrage anpasst und die Latenz minimiert, unabhängig von Ihrer Arbeitslast. Mit diesem Hardware-Upgrade ist das Skalieren nun leistungsfähiger und reaktionsschneller.</p><ul><li><strong>Problemloser Umgang mit Spitzen:</strong> Egal, ob Sie mit einem plötzlichen Anstieg des Nutzerverkehrs oder einem massiven Batch-Ingest konfrontiert sind – die neue Infrastruktur stellt sicher, dass Ihre Such- und Indexierungsressourcen effizienter skaliert werden, um eine gleichbleibend niedrige Latenz zu gewährleisten.</li><li><strong>Optimierte Entkopplung von Rechenleistung und Speicher:</strong> Die Serverless-Architektur trennt Rechenleistung und Speicher, wodurch Workloads unabhängig voneinander skaliert werden können, um optimale Leistung und Kosteneffizienz zu gewährleisten. Die schnellere Hardware verbessert die Computerschicht und maximiert die Effizienz dieses entkoppelten Designs.</li></ul><h2><strong>Hinter den Kulissen: Ergebnisse interner Benchmarks</strong></h2><p>Um die Auswirkungen unseres AWS-Infrastruktur-Upgrades zu quantifizieren, führte das Elastic-Engineering-Team ein umfassendes internes Benchmarking mit einer Reihe von Serverless-Workloads durch. Diese Workloads lieferten empirische Beweise für Leistungsverbesserungen, die Sie in Ihren Anwendungen erwarten können, unabhängig von Ihrem Anwendungsfall.</p><h3><strong>Der Benchmarking-Ansatz</strong></h3><p>Wir konzentrierten unsere Tests auf die wichtigsten Kennzahlen, die das Entwicklererlebnis und die Anwendungsreaktionsfähigkeit direkt beeinflussen: Reaktionszeit (also Latenz) und Durchsatz bei Such- und Indexierungsoperationen.</p><ul><li><strong>Getestete Arbeitslasten:</strong> Die Tests umfassten Suchvorgänge mit hoher Parallelität, wie sie typisch für benutzerorientierte Anwendungen sind, komplexe Vektorsuchanfragen sowie die Erfassung/Indexierung großer Datenmengen für Anwendungsfälle im Bereich Beobachtbarkeit und Sicherheit. Insbesondere nutzte unsere Testmethodik <a href="https://github.com/elastic/rally-tracks/tree/master">öffentlich</a> <a href="https://github.com/elastic/rally-tracks/tree/master">verfügbare Datensätze für Rally</a>, das Benchmarking-Tool von Elastic.<ul><li><a href="https://github.com/elastic/rally-tracks/tree/3bedd51/wikipedia"><code>wikipedia</code></a>Ein Datensatz, der aus einem Snapshot des Wikipedia-Textinhalts abgeleitet wurde, um die allgemeine Textsuchleistung zu messen.</li><li><a href="https://github.com/elastic/rally-tracks/tree/3bedd51/msmarco-passage-ranking"><code>MSMARCO-Passage-Ranking</code></a>Ein Datensatz, abgeleitet von Microsofts Machine Reading Comprehension (MS MARCO), um die Suchleistung auf spärlichen Vektorfeldern zu messen.</li><li><a href="https://github.com/elastic/rally-tracks/tree/3bedd51/openai_vector"><code>OpenAI_Vector</code></a>: Ein Datensatz, der aus BEIRs NQ abgeleitet und mit Einbettungen angereichert wurde, die vom <code>text-embedding-ada-002</code>-Modell von OpenAI generiert wurden, um die Suchleistung auf dichten Vektorfeldern zu messen.</li></ul></li><li><strong>Messung:</strong> Wir verglichen die Leistung der alten und neuen Infrastruktur und maßen die Latenz im 99. Perzentil (P99), um den Worst-Case, die Tail-Latenz-Performance und die Operationen pro Sekunde zu erfassen. Jeder Track wurde für jedes Hardware-Profil fünfmal ausgeführt, um die Konsistenz der Ergebnisse zu gewährleisten.</li><li><strong>Das Ziel:</strong> Unser Ziel war es, die Fähigkeit der Infrastruktur zu validieren, um eine konstant <strong>schnellere und vorhersehbarere Leistung</strong> zu liefern, selbst in Phasen schneller automatischer Skalierung.</li></ul><h3><strong>Zusammenfassung der Leistungsdaten</strong></h3><p>Die Ergebnisse bestätigen deutliche Verbesserungen in Effizienz und Geschwindigkeit. Diese Vorteile schlagen sich direkt in kürzeren Reaktionszeiten für Ihre Benutzer:innen und niedrigeren Betriebskosten nieder, da Sie die gleiche Menge an Arbeit mit weniger Rechenressourcen erledigen können.</p><p>Die folgenden Tabellen zeigen die quantitativen Verbesserungen. Höhere Werte sind besser für den Durchsatz; niedrigere Werte sind besser für die Latenz.</p><p><strong>Suche nach Benchmark-Ergebnissen:</strong></p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Benchmark</td><td style="border: 1px solid #ccc; padding: 4px;">Vergleich</td><td style="border: 1px solid #ccc; padding: 4px;">Alte Infrastruktur</td><td style="border: 1px solid #ccc; padding: 4px;">Neue Infrastruktur</td><td style="border: 1px solid #ccc; padding: 4px;">Differential</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">„Wikipedia“ (Klartext)</td><td style="border: 1px solid #ccc; padding: 4px;">Durchsatz der Suchvorgänge (Operationen/s)</td><td style="border: 1px solid #ccc; padding: 4px;">729</td><td style="border: 1px solid #ccc; padding: 4px;">1.107</td><td style="border: 1px solid #ccc; padding: 4px;">+52 %</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">„Wikipedia“ (Klartext)</td><td style="border: 1px solid #ccc; padding: 4px;">Latenz der Suchoperation (p99, ms)</td><td style="border: 1px solid #ccc; padding: 4px;">56</td><td style="border: 1px solid #ccc; padding: 4px;">35</td><td style="border: 1px solid #ccc; padding: 4px;">-37 %</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">„MSMARCO-Passage-Ranking“ (dünnbesetzte Vektoren)</td><td style="border: 1px solid #ccc; padding: 4px;">Durchsatz der Suchvorgänge (Operationen/s)</td><td style="border: 1px solid #ccc; padding: 4px;">22</td><td style="border: 1px solid #ccc; padding: 4px;">31</td><td style="border: 1px solid #ccc; padding: 4px;">+40 %</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">„MSMARCO-Passage-Ranking“ (dünnbesetzte Vektoren)</td><td style="border: 1px solid #ccc; padding: 4px;">Latenz der Suchoperation (p99, ms)</td><td style="border: 1px solid #ccc; padding: 4px;">108</td><td style="border: 1px solid #ccc; padding: 4px;">67</td><td style="border: 1px solid #ccc; padding: 4px;">-38 %</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">`OpenAI_Vector` (dichte Vektoren)</td><td style="border: 1px solid #ccc; padding: 4px;">Durchsatz der Suchvorgänge (Operationen/s)</td><td style="border: 1px solid #ccc; padding: 4px;">475</td><td style="border: 1px solid #ccc; padding: 4px;">624</td><td style="border: 1px solid #ccc; padding: 4px;">+31 %</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">`OpenAI_Vector` (dichte Vektoren)</td><td style="border: 1px solid #ccc; padding: 4px;">Latenz der Suchoperation (p99, ms)</td><td style="border: 1px solid #ccc; padding: 4px;">35</td><td style="border: 1px solid #ccc; padding: 4px;">22</td><td style="border: 1px solid #ccc; padding: 4px;">-37 %</td>
                    </tr>
              </tbody>
            </table>
          <p><strong>Indexieren der Benchmark-Ergebnisse:</strong></p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Benchmark</td><td style="border: 1px solid #ccc; padding: 4px;">Vergleich</td><td style="border: 1px solid #ccc; padding: 4px;">Alte Infrastruktur</td><td style="border: 1px solid #ccc; padding: 4px;">Neue Infrastruktur</td><td style="border: 1px solid #ccc; padding: 4px;">Differential</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">„Wikipedia“ (Klartext)</td><td style="border: 1px solid #ccc; padding: 4px;">Durchsatz der Suchvorgänge (Operationen/s)</td><td style="border: 1px solid #ccc; padding: 4px;">2.845</td><td style="border: 1px solid #ccc; padding: 4px;">3220</td><td style="border: 1px solid #ccc; padding: 4px;">+13 %</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">„Wikipedia“ (Klartext)</td><td style="border: 1px solid #ccc; padding: 4px;">Latenz der Suchoperation (p99, ms)</td><td style="border: 1px solid #ccc; padding: 4px;">1769</td><td style="border: 1px solid #ccc; padding: 4px;">1120</td><td style="border: 1px solid #ccc; padding: 4px;">-37 %</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">„MSMARCO-Passage-Ranking“ (dünnbesetzte Vektoren)</td><td style="border: 1px solid #ccc; padding: 4px;">Durchsatz der Suchvorgänge (Operationen/s)</td><td style="border: 1px solid #ccc; padding: 4px;">7.087</td><td style="border: 1px solid #ccc; padding: 4px;">8900</td><td style="border: 1px solid #ccc; padding: 4px;">+26 %</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">„MSMARCO-Passage-Ranking“ (dünnbesetzte Vektoren)</td><td style="border: 1px solid #ccc; padding: 4px;">Latenz der Suchoperation (p99, ms)</td><td style="border: 1px solid #ccc; padding: 4px;">824</td><td style="border: 1px solid #ccc; padding: 4px;">677</td><td style="border: 1px solid #ccc; padding: 4px;">-18 %</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">`OpenAI_Vector` (dichte Vektoren)</td><td style="border: 1px solid #ccc; padding: 4px;">Durchsatz der Suchvorgänge (Operationen/s)</td><td style="border: 1px solid #ccc; padding: 4px;">2.972</td><td style="border: 1px solid #ccc; padding: 4px;">3187</td><td style="border: 1px solid #ccc; padding: 4px;">+7 %</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">`OpenAI_Vector` (dichte Vektoren)</td><td style="border: 1px solid #ccc; padding: 4px;">Latenz der Suchoperation (p99, ms)</td><td style="border: 1px solid #ccc; padding: 4px;">2946</td><td style="border: 1px solid #ccc; padding: 4px;">2944</td><td style="border: 1px solid #ccc; padding: 4px;">0 %</td>
                    </tr>
              </tbody>
            </table>
          <h2><strong>Der zusätzliche Bonus: Kostenreduzierung</strong></h2><p>Unser Fokus liegt zwar auf der Bereitstellung einer Performance mit geringer Latenz, aber die Effizienz der neuen Hardware hat auch einen direkten, positiven Einfluss auf die Kosten von Elasticsearch-Projekten.</p><p><a href="https://www.elastic.co/pricing/serverless-search">Die Preisgestaltung von Elasticsearch Serverless</a> basiert auf der Nutzung, das heißt, Sie zahlen nur für die Ingest- und Suchressourcen, die Sie verbrauchen. Da die neuere, schnellere Hardware effizienter ist, werden Ihre Arbeitslasten oft mit weniger Ressourcen erledigt, was bei den meisten Projekten zu einer erheblichen Kostenreduzierung führt. Sie erhalten eine Premium-Leistungssteigerung ohne den Premium-Preis – die Definition von optimierter Effizienz.</p><h2><strong>Was bedeutet das für Sie als Entwickler:in?</strong></h2><p>Dieses Infrastruktur-Upgrade wird vollständig von Elastic verwaltet, sodass Sie keinen Finger rühren müssen – keine Migrationen und keine Konfigurationsänderungen. Die Verbesserung erfolgt sofort und automatisch bei all Ihren AWS-basierten Serverless-Projekten.</p><p>Dieses Upgrade ermöglicht Ihnen Folgendes:</p><ul><li><strong>Erstellen Sie schnellere Anwendungen:</strong> Konzentrieren Sie sich auf die Geschwindigkeit der Features, da Sie wissen, dass Ihre zugrundeliegende Such-Platform die Geschwindigkeit bietet, die Ihre Benutzer:innen erwarten.</li><li><strong>Innovation mit Zuversicht:</strong> Stellen Sie neue Such-, Beobachtbarkeits- und Sicherheitsfeatures – einschließlich komplexer KI-Features wie Vektorsuche und Relevanzranking – mit der Gewissheit bereit, dass die Platform die Last mit maximaler Leistung bewältigen kann.</li><li><strong>Vereinfachen Sie Ihren Stack:</strong> Nutzen Sie einen vollständig verwalteten Service, der Infrastrukturmanagement, Kapazitätsplanung und Skalierung übernimmt, damit Sie sich auf Ihren Code und Ihre Daten konzentrieren können.<br/></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Erstellung von Human-in-the-Loop-Agenten mit LangGraph und Elasticsearch]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie mit LangGraph und Elasticsearch Human-in-the-Loop-Agenten erstellen, die Menschen in den Entscheidungsprozess einbeziehen, um kontextuelle Lücken zu schließen und Tool-Aufrufe vor ihrer Ausführung zu überprüfen.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/human-in-the-loop-agents-langgraph-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/human-in-the-loop-agents-langgraph-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
            <pubDate>Mon, 05 Jan 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/69634710b14adff494b51b2d57d119196ea6d644-838x532.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>In diesem Artikel werden wir untersuchen, wie <a href="https://langchain-ai.github.io/langgraph/">LangGraph</a> und Elasticsearch kombiniert werden können, um eine Human-in-the-Loop (HITL)-Anwendung zu erstellen. Dieser Ansatz ermöglicht es KI-Systemen, Nutzer direkt in den Entscheidungsprozess einzubeziehen, wodurch Interaktionen zuverlässiger und kontextsensitiver werden. Wir werden ein praktisches Beispiel mit einem kontextgesteuerten Szenario implementieren, um zu demonstrieren, wie LangGraph-Workflows mit Elasticsearch integriert werden können, um Daten abzurufen, Benutzereingaben zu verarbeiten und verfeinerte Ergebnisse zu liefern.</p><h2>Voraussetzungen</h2><ul><li>NodeJS Version 18 oder neuer</li><li>OpenAI-API-Schlüssel</li><li>Elasticsearch 8.x+ Deployment</li></ul><h2>Warum LangGraph für Produktions-HITL-Systeme verwenden</h2><p>In einem <a href="https://www.elastic.co/search-labs/blog/ai-agent-workflow-finance-langgraph-elasticsearch">früheren Artikel</a> haben wir LangGraph und seine Vorteile für den Aufbau eines RAG-Systems mit LLMs sowie <a href="https://docs.langchain.com/oss/javascript/langgraph/graph-api#conditional-edges">bedingten Kanten</a> vorgestellt, um automatisch Entscheidungen zu treffen und Ergebnisse anzuzeigen. Manchmal möchten wir nicht, dass das System komplett autonom agiert, sondern dass die Nutzer innerhalb der Ausführungsschleife Optionen auswählen und Entscheidungen treffen. Dieses Konzept heißt <strong><em>Human in the Loop</em></strong>.</p><h2>Human-in-the-loop oder in der Schleife</h2><p>Dabei handelt es sich um ein KI-Konzept, das es einer realen Person ermöglicht, mit KI-Systemen zu interagieren, um mehr Kontext zu liefern, Reaktionen zu bewerten, Reaktionen zu bearbeiten, nach weiteren Informationen zu fragen usw. Dies ist in Szenarien mit niedriger Fehlertoleranz wie Compliance, Entscheidungsfindung oder Inhaltsgenerierung sehr nützlich und trägt zur Verbesserung der Zuverlässigkeit der LLM-Outputs bei.</p><p>Ein häufiges Beispiel ist, wenn Ihr Programmierassistent Sie um die Erlaubnis bittet, einen bestimmten Befehl am Terminal auszuführen, oder Ihnen den Schritt-für-Schritt-Denkprozess zeigt, den Sie genehmigen müssen, bevor Sie mit dem Programmieren beginnen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4df9fe7383f0dc665a1b0866651f4f6a40a14aa8-499x215.png" style="width: 100%;" /><h2>Elasticsearch + LangGraph: Wie sie interagieren</h2><p><a href="https://www.langchain.com/">LangChain</a> ermöglicht uns die Verwendung von Elasticsearch als <a href="https://js.langchain.com/docs/integrations/vectorstores/elasticsearch/">Vektorspeicher</a> und die Durchführung von Abfragen innerhalb von LangGraph-Anwendungen, was nützlich ist, um Volltext- oder semantische Suchen auszuführen, während LangGraph verwendet wird, um den spezifischen Workflow, die Tools und die Interaktionen zu definieren. HITL wird außerdem als zusätzliche Interaktionsebene mit dem Nutzer hinzugefügt.</p><h2>Praktische Umsetzung: Human-in-the-loop</h2><p>Stellen wir uns einen Fall vor, in dem ein Anwalt eine Frage zu einem Fall hat, den er kürzlich übernommen hat. Ohne die richtigen Hilfsmittel müsste er juristische Artikel und Präzedenzfälle manuell suchen, sie vollständig lesen und dann interpretieren, wie sie auf seine Situation anwendbar sind. Mit LangGraph und Elasticsearch können wir jedoch ein System aufbauen, das eine Datenbank von Rechtspräzedenzfällen durchsucht und eine Fallanalyse erstellt, die die spezifischen Details und den Kontext des Anwalts einbezieht.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e95bf4ce115cbe9954ee5033776408bed485b8d2-1600x906.png" style="width: 100%;" /><p>Der Workflow beginnt, wenn der Anwalt eine Rechtsfrage einreicht. Das System führt eine Vektorsuche in Elasticsearch durch, ruft die relevantesten Präzedenzfälle ab und präsentiert sie dem Anwalt zur Auswahl in natürlicher Sprache. Nach der Auswahl erstellt der LLM einen Analyseentwurf und prüft, ob die Informationen vollständig sind. An dieser Stelle kann der Workflow zwei Pfaden folgen: Wenn alles klar ist, wird direkt eine endgültige Analyse generiert; wenn nicht, pausiert er, um eine Klärung vom Anwalt anzufordern. Sobald der fehlende Kontext bereitgestellt wird, schließt das System die Analyse ab und gibt sie unter Berücksichtigung der Klärungen zurück.</p><p>Nachfolgend ein von LangGraph erstellter Graph, der zeigt, wie die App am Ende der Entwicklung aussehen wird. Jeder Node repräsentiert ein Tool oder eine Funktionalität:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/fcdc0dd53c436afc8e575d1f8fd3705b4dfe9a32-262x753.png" style="width: 100%;" /><h3>Datensatz</h3><p>Hier ist der <a href="https://github.com/Delacrobix/LangGraph-JS-Elasticsearch-human-in-the-loop/blob/main/dataset.json">Datensatz</a>, der für dieses Beispiel verwendet wird. Dieser Datensatz enthält eine Sammlung von Präzedenzfällen, die jeweils einen Fall mit Verzögerungen bei der Leistungserbringung, die Begründung des Gerichts und das Endergebnis beschreiben.</p><pre><code>[
  {
    "pageContent": "Legal precedent: Case B - Service delay not considered breach. A consulting contract used term 'timely delivery' without specific dates. A three-week delay occurred but contract lacked explicit schedule. Court ruled no breach as parties had not defined concrete timeline and delay did not cause demonstrable harm.",
    "metadata": {
      "caseId": "CASE-B-2022",
      "contractType": "consulting agreement",
      "delayPeriod": "three weeks",
      "outcome": "no breach found",
      "reasoning": "no explicit deadline defined, no demonstrable harm",
      "keyTerms": "timely delivery, open terms, schedule definition",
      "title": "Case B: Delay Without Explicit Schedule"
    }
  },
  ...
]</code></pre><h3>Einrichtung der Ingestion und des Indexes</h3><p>Die Indexeinrichtung und die Logik zur Daten-Ingestion sind in der Datei <a href="https://github.com/Delacrobix/LangGraph-JS-Elasticsearch-human-in-the-loop/blob/main/dataIngestion.ts">dataIngestion.ts</a> definiert, in der wir Funktionen deklarieren, die die Indexerstellung übernehmen. Dieses Setup ist kompatibel mit der LangChain <a href="https://python.langchain.com/docs/integrations/vectorstores/elasticsearch/">Vektorspeicher-Schnittstelle für Elasticsearch</a>.</p><p><em>Hinweis: Die Mapping-Konfiguration ist ebenfalls in der <a href="https://github.com/Delacrobix/LangGraph-JS-Elasticsearch-human-in-the-loop/blob/main/dataIngestion.ts">dataIngestion.ts</a> Datei enthalten.</em></p><h3>Pakete installieren und Umgebungsvariablen einrichten</h3><p>Lassen Sie uns ein <a href="https://nodejs.org/en">Node.js-Projekt</a> mit den Standardeinstellungen initialisieren:</p><ul><li><strong>@elastic/Elasticsearch: </strong>Elasticsearch-Client für Node.js. Wird verwendet, um Verbindungen herzustellen, Indizes zu erstellen und Abfragen auszuführen.</li><li><strong>@langchain/community:</strong> Bietet Integrationen für von der Community unterstützte Tools, einschließlich des ElasticVectorSearch-Stores.</li><li><strong>@langchain/core:</strong> Kernbausteine von LangChain, wie Ketten, Prompts und Hilfsmittel.</li><li><strong>@langchain/langgraph:</strong> Fügt graphbasierte Orchestrierung hinzu, die Workflows mit Knoten, Kanten und Zustandsverwaltung ermöglicht.</li><li><strong>@langchain/openai: </strong>Bietet Zugriff auf OpenAI-Modelle (LLMs und Einbettungen) über LangChain.</li><li><strong>dotenv:</strong> Lädt Umgebungsvariablen aus einer.env Datei in process.env.</li><li><strong>tsx: </strong>Ist ein nützliches Tool zum Ausführen von TypeScript-Code.</li></ul><p>Führen Sie folgenden Befehl in der Konsole aus, um alle zu installieren:</p><pre><code>npm install @elastic/elasticsearch @langchain/community @langchain/core @langchain/langgraph @langchain/openai dotenv --legacy-peer-deps && npm install --save-dev tsx</code></pre><p>Erstellen Sie eine <code>.env</code> Datei, um die Umgebungsvariablen einzurichten:</p><pre><code>ELASTICSEARCH_ENDPOINT=
ELASTICSEARCH_API_KEY=
OPENAI_API_KEY=</code></pre><p>Wir werden <a href="https://www.typescriptlang.org/">TypeScript</a> zum Schreiben des Codes verwenden, da es eine Ebene der Typsicherheit und eine bessere Entwicklererfahrung bietet. Erstellen Sie eine TypeScript-Datei mit dem Namen <a href="https://github.com/Delacrobix/LangGraph-JS-Elasticsearch-human-in-the-loop/blob/main/main.ts">main.ts</a> und fügen Sie den Code des nächsten Abschnitts ein.</p><h3>Pakete importieren</h3><p>In der Datei <a href="https://github.com/Delacrobix/LangGraph-JS-Elasticsearch-human-in-the-loop/blob/main/main.ts">main.ts</a> importieren wir zunächst die benötigten Module und initialisieren die Umgebungsvariablenkonfiguration. Dazu gehören die Kernkomponenten von LangGraph, die OpenAI-Modellintegrationen und der Elasticsearch-Client.</p><p>Wir importieren außerdem Folgendes aus der <a href="https://github.com/Delacrobix/LangGraph-JS-Elasticsearch-human-in-the-loop/blob/main/dataIngestion.ts">dataIngestion.ts-Datei</a> :</p><ul><li><strong>ingestData: </strong>eine Funktion, die den Index erstellt und die Daten aufnimmt.</li><li><strong>Dokument und Dokumentmetadaten:</strong> Schnittstellen, die die Dokumentstruktur des Datensatzes definieren.</li></ul><h3>Elasticsearch Vector Store Client, Embeddings Client und OpenAI-Client</h3><p>Dieser Code initialisiert den Vektorspeicher, den Embeddings-Client und einen OpenAI-Client.</p><pre><code>const VECTOR_INDEX = "legal-precedents";

const llm = new ChatOpenAI({ model: "gpt-4o-mini" });
const embeddings = new OpenAIEmbeddings({
  model: "text-embedding-3-small",
});

const esClient = new Client({
  node: process.env.ELASTICSEARCH_ENDPOINT,
  auth: {
    apiKey: process.env.ELASTICSEARCH_API_KEY ?? "",
  },
});

const vectorStore = new ElasticVectorSearch(embeddings, {
  client: esClient,
  indexName: VECTOR_INDEX,
});</code></pre><p>Das Workflow-Statusschema der Anwendung hilft bei der Kommunikation zwischen den Nodes:</p><pre><code>const LegalResearchState = Annotation.Root({
  query: Annotation<string>(),
  analyzedConcepts: Annotation<string[]>(),
  precedents: Annotation<Document[]>(),
  selectedPrecedent: Annotation<Document | null>(),
  draftAnalysis: Annotation<string>(),
  ambiguityDetected: Annotation<boolean>(),
  userClarification: Annotation<string>(),
  finalAnalysis: Annotation<string>(),
});</code></pre><p>Im Zustandsobjekt geben wir durch die Nodes die Nutzeranfrage, die daraus extrahierten Konzepte, die abgerufenen Rechtspräzedenzfälle und etwaige Mehrdeutigkeiten durch. Der Zustand verfolgt auch den vom Nutzer ausgewählten Präzedenzfall, die während des Prozesses erstellte Entwurfsanalyse und die endgültige Analyse, sobald alle Klärungen abgeschlossen sind.</p><h3>Knoten</h3><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9bda47908dc0a73fdda222c8ba1382a1ef9dc77a-304x300.png" style="width: 100%;" /><p><strong>searchPrecedents: </strong>Dieser Node führt eine Ähnlichkeitssuche im Elasticsearch-Vektorspeicher basierend auf dem Eingang des Nutzers durch. Er ruft bis zu 5 übereinstimmende Dokumente ab und druckt sie aus, damit der Nutzer sie einsehen kann.</p><pre><code>async function searchPrecedents(state: typeof LegalResearchState.State) {
  console.log(
    "📚 Searching for relevant legal precedents with query:\n",
    state.query
  );

  const results = await vectorStore.similaritySearch(state.query, 5);
  const precedents = results.map((d) => d as Document);

  console.log(`Found ${precedents.length} relevant precedents:\n`);

  for (let i = 0; i < precedents.length; i++) {
    const p = precedents[i];
    const m = p.metadata;
    console.log(
      `${i + 1}. ${m.title} (${m.caseId})\n` +
        `   Type: ${m.contractType}\n` +
        `   Outcome: ${m.outcome}\n` +
        `   Key reasoning: ${m.reasoning}\n` +
        `   Delay period: ${m.delayPeriod}\n`
    );
  }

  return { precedents };
}</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/82afb46c4373b01daf7640a528c2ea54db0837b5-304x300.png" style="width: 100%;" /><p><strong>Präzedenzfallauswahl:</strong> Dieser Node ermöglicht es dem Nutzer, mithilfe natürlicher Sprache denjenigen Anwendungsfall auszuwählen, der durch die Proximity-Suche ermittelt wurde und am besten zur Frage passt. An diesem Punkt unterbricht die Anwendung<strong> den Workflow</strong> und wartet auf die Nutzereingabe.</p><pre><code>function precedentSelection(state: typeof LegalResearchState.State) {
  console.log("\n⚖️  HITL #1: Human input needed\n");
  const question = "👨‍⚖️  Which precedent is most similar to your case? ";
  const userChoice = interrupt({ question });

  return { userChoice };
}</code></pre><p><strong>selectPrecedent:</strong> Dieser Node sendet den Nutzerinput zusammen mit den abgerufenen Dokumenten zur Interpretation, sodass eines von ihnen ausgewählt werden kann. Das LLM erfüllt diese Aufgabe, indem es eine Zahl zurückgibt, die das Dokument repräsentiert, das es aus der natürlichen Spracheingabe des Nutzers ableitet.</p><pre><code>async function selectPrecedent(state: typeof LegalResearchState.State) {
  const precedents = state.precedents || [];
  const userInput = (state as any).userChoice || "";

  const precedentsList = precedents
    .map((p, i) => {
      const m = p.metadata;
      return `${i + 1}. ${m.caseId}: ${m.title} - ${m.outcome}`;
    })
    .join("\n");

  const structuredLlm = llm.withStructuredOutput({
    name: "precedent_selection",
    schema: {
      type: "object",
      properties: {
        selected_number: {
          type: "number",
          description:
            "The precedent number selected by the lawyer (1-based index)",
          minimum: 1,
          maximum: precedents.length,
        },
      },
      required: ["selected_number"],
    },
  });

  const prompt = `
    The lawyer said: "${userInput}"

    Available precedents:
    ${precedentsList}

    Which precedent number (1-${precedents.length}) matches their selection?
  `;

  const response = await structuredLlm.invoke([
    {
      role: "system",
      content:
        "You are an assistant that interprets lawyer's selection and returns the corresponding precedent number.",
    },
    { role: "user", content: prompt },
  ]);

  const selectedIndex = response.selected_number - 1;
  const selectedPrecedent = precedents[selectedIndex] || precedents[0];

  console.log(`✅ Selected: ${selectedPrecedent.metadata.title}\n`);
  return { selectedPrecedent };
}</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/714e840cc8dab48c436cd066a6fdb4b7dceccf13-428x260.png" style="width: 100%;" /><p><strong>createDraft: </strong>Dieser Node generiert die erste rechtliche Analyse basierend auf dem vom Nutzer gewählten Präzedenzfall. Er verwendet einen LLM, um zu beurteilen, inwieweit der gewählte Präzedenzfall auf die Frage des Anwalts anwendbar ist, und um festzustellen, ob dem System genügend Informationen vorliegen, um fortzufahren.</p><p>Wenn der Präzedenzfall direkt angewendet werden kann, erstellt der Node einen Analyseentwurf und nimmt den richtigen Pfad zum End-Node. Wenn das LLM Unklarheiten wie undefinierte Vertragsbedingungen, fehlende Zeitrahmendetails oder unklare Bedingungen erkennt, gibt es eine Markierung zurück, die darauf hinweist, dass eine Klärung erforderlich ist, zusammen mit einer Liste der spezifischen Informationen, die bereitgestellt werden müssen. In diesem Fall löst die Mehrdeutigkeit den linken Pfad des Graphen aus.</p><pre><code>async function createDraft(state: typeof LegalResearchState.State) {
  console.log("📝 Drafting initial legal analysis...\n");

  const precedent = state.selectedPrecedent;
  if (!precedent) return { draftAnalysis: "" };

  const m = precedent.metadata;

  const structuredLlm = llm.withStructuredOutput({
    name: "draft_analysis",
    schema: {
      type: "object",
      properties: {
        needs_clarification: {
          type: "boolean",
          description:
            "Whether the analysis requires clarification about contract terms or context",
        },
        analysis_text: {
          type: "string",
          description: "The draft legal analysis or the ambiguity explanation",
        },
        missing_information: {
          type: "array",
          items: { type: "string" },
          description:
            "List of specific information needed if clarification is required (empty if no clarification needed)",
        },
      },
      required: ["needs_clarification", "analysis_text", "missing_information"],
    },
  });

  const prompt = `
    Based on this precedent:
    Case: ${m.title}
    Outcome: ${m.outcome}
    Reasoning: ${m.reasoning}
    Key terms: ${m.keyTerms}

    And the lawyer's question: "${state.query}"

    Draft a legal analysis applying this precedent to the question.
    
    If you need more context about the specific contract terms, timeline details, 
    or other critical information to provide accurate analysis, set needs_clarification 
    to true and list what information is missing.
    
    Otherwise, provide the legal analysis directly.
  `;

  const response = await structuredLlm.invoke([
    {
      role: "system",
      content:
        "You are a legal research assistant that analyzes cases and identifies when additional context is needed.",
    },
    { role: "user", content: prompt },
  ]);

  let displayText: string;
  if (response.needs_clarification) {
    const missingInfoList = response.missing_information
      .map((info: string, i: number) => `${i + 1}. ${info}`)
      .join("\n");
    displayText = `AMBIGUITY DETECTED:\n${response.analysis_text}\n\nMissing information:\n${missingInfoList}`;
  } else {
    displayText = `ANALYSIS:\n${response.analysis_text}`;
  }

  console.log(displayText + "\n");

  return {
    draftAnalysis: displayText,
    ambiguityDetected: response.needs_clarification,
  };
}</code></pre><p>Die beiden möglichen Pfade des Graphen sehen folgendermaßen aus:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/72dda52e1d84818c9f98437f2fa6ecb8e5b2f4ff-438x638.png" style="width: 100%;" /><p>Der linke Pfad enthält einen zusätzlichen Node, der die Klarstellung übernimmt.</p><p><strong>RequestClarification:</strong> Dieser Node löst den zweiten Human-in-the-Loop-Schritt aus, wenn das System feststellt, dass dem Analyseentwurf grundlegender Kontext fehlt. Der Workflow wird unterbrochen und der Nutzer wird aufgefordert, die fehlenden Vertragsdetails zu klären, die der vorherige Node entdeckt hat.</p><pre><code>function requestClarification(state: typeof LegalResearchState.State) {
  console.log("\n⚖️  HITL #2: Additional context needed\n");
  const userClarification = interrupt({
    question: "👨‍⚖️  Please provide clarification about your contract terms:",
  });
  return { userClarification };
}</code></pre><p><strong>generateFinalAnalysis: </strong>Dieser Node erstellt die endgültige rechtliche Analyse, indem er den ausgewählten Präzedenzfall mit dem vom Nutzer bereitgestellten zusätzlichen Kontext kombiniert, falls nötig. Anhand der im vorherigen HITL-Schritt gesammelten Klarstellung fasst das LLM die Begründung des Präzedenzfalls, die vom Nutzer bereitgestellten Vertragsdetails und die Bedingungen zusammen, die bestimmen, ob ein Verstoß stattgefunden haben könnte.</p><p>Der Node liefert eine vollständige Analyse, die rechtliche Auslegung und praktische Empfehlungen integriert.</p><pre><code>async function generateFinalAnalysis(state: typeof LegalResearchState.State) {
  console.log("📋 Generating final legal analysis...\n");

  const precedent = state.selectedPrecedent;
  if (!precedent) return { finalAnalysis: "" };

  const m = precedent.metadata;

  const prompt = `
    Original question: "${state.query}"
    
    Selected precedent: ${m.title}
    Outcome: ${m.outcome}
    Reasoning: ${m.reasoning}
    
    Lawyer's clarification: "${state.userClarification}"
    
    Provide a comprehensive legal analysis integrating:
    1. The selected precedent's reasoning
    2. The lawyer's specific contract context
    3. Conditions for breach vs. no breach
    4. Practical recommendations
  `;

  const response = await llm.invoke([
    {
      role: "system",
      content:
        "You are a legal research assistant providing comprehensive analysis.",
    },
    { role: "user", content: prompt },
  ]);

  const finalAnalysis = response.content as string;

  console.log(
    "\n" +
      "=".repeat(80) +
      "\n" +
      "⚖️  FINAL LEGAL ANALYSIS\n" +
      "=".repeat(80) +
      "\n\n" +
      finalAnalysis +
      "\n\n" +
      "=".repeat(80) +
      "\n"
  );

  return { finalAnalysis };
}</code></pre><p>Graph erstellen:</p><pre><code>const workflow = new StateGraph(LegalResearchState)
  .addNode("analyzeQuery", analyzeQuery)
  .addNode("searchPrecedents", searchPrecedents)
  .addNode("precedentSelection", precedentSelection)
  .addNode("selectPrecedent", selectPrecedent)
  .addNode("createDraft", createDraft)
  .addNode("requestClarification", requestClarification)
  .addNode("generateFinalAnalysis", generateFinalAnalysis)
  .addEdge("__start__", "analyzeQuery")
  .addEdge("analyzeQuery", "searchPrecedents")
  .addEdge("searchPrecedents", "precedentSelection") // HITL #1
  .addEdge("precedentSelection", "selectPrecedent")
  .addEdge("selectPrecedent", "createDraft")
  .addConditionalEdges(
    "createDraft",
    (state: typeof LegalResearchState.State) => {
      // If ambiguity detected, request clarification (HITL #2)
      if (state.ambiguityDetected) return "needsClarification";
      // Otherwise, generate final analysis
      return "final";
    },
    {
      needsClarification: "requestClarification",
      final: "generateFinalAnalysis",
    }
  )
  .addEdge("requestClarification", "generateFinalAnalysis") // HITL #2
  .addEdge("generateFinalAnalysis", "__end__");</code></pre><p>Im Graph können wir sehen, dass die bedingte Kante die Bedingung für die Wahl des „finalen“ Pfades definiert. Wie gezeigt, hängt die Entscheidung nun davon ab, ob der Analyseentwurf Unklarheiten aufgedeckt hat, die einer weiteren Klärung bedürfen.</p><p>Zusammengefasst zur Ausführung:</p><pre><code>await ingestData();

// Compile workflow
const app = workflow.compile({ checkpointer: new MemorySaver() });
const config = { configurable: { thread_id: "hitl-circular-thread" } };

await saveGraphImage(app);

// Execute workflow
const legalQuestion =
    "Does a pattern of repeated delays constitute breach even if each individual delay is minor?"; 

console.log(`⚖️  LEGAL QUESTION: "${legalQuestion}"\n`);

let currentState = await app.invoke({ query: legalQuestion }, config);

// Handle all interruptions in a loop
while ((currentState as any).__interrupt__?.length > 0) {
  console.log("\n💭 APPLICATION PAUSED WAITING FOR USER INPUT...");

  const interruptQuestion = (currentState as any).__interrupt__[0]?.value
    ?.question;
  const userChoice = await getUserInput(
    interruptQuestion || "👤 YOUR CHOICE: "
  );

  currentState = await app.invoke(
    new Command({ resume: userChoice }),
    config
  );
}</code></pre><h3>Führen Sie das Skript aus:</h3><p>Nachdem wir den gesamten Code zugewiesen haben, führen wir die Datei <code>main.ts</code> aus, indem wir den folgenden Befehl im Terminal eingeben:</p><pre><code>tsx main.ts</code></pre><p>Sobald das Skript ausgeführt wird, wird die Frage <strong>„Stellt ein Muster wiederholter Verzögerungen einen Verstoß dar, selbst wenn jede einzelne Verzögerung geringfügig ist?“</strong> an Elasticsearch gesendet, um eine Proximity-Suche durchzuführen, und die aus dem Index abgerufenen Ergebnisse werden angezeigt. Die App erkennt, dass mehrere relevante Präzedenzfälle mit der Abfrage übereinstimmen, pausiert die Ausführung und bittet den Nutzer, bei der Klarstellung zu helfen, welcher rechtliche Präzedenzfall am relevantesten ist:</p><pre><code>📚 Searching for relevant legal precedents with query:
 Does a pattern of repeated delays constitute breach even if each individual delay is minor?
Found 5 relevant precedents:

1. Case H: Pattern of Repeated Delays (CASE-H-2021)
   Type: ongoing service agreement
   Outcome: breach found
   Key reasoning: pattern demonstrated failure to perform, cumulative effect
   Delay period: multiple instances

2. Case E: Minor Delay Quality Maintained (CASE-E-2022)
   Type: service agreement
   Outcome: minor breach only
   Key reasoning: delay minimal, quality maintained, termination unjustified
   Delay period: five days

3. Case A: Delay Breach with Operational Impact (CASE-A-2023)
   Type: service agreement
   Outcome: breach found
   Key reasoning: delay affected operations and caused financial harm
   Delay period: two weeks

4. Case B: Delay Without Explicit Schedule (CASE-B-2022)
   Type: consulting agreement
   Outcome: no breach found
   Key reasoning: no explicit deadline defined, no demonstrable harm
   Delay period: three weeks

5. Case C: Justified Delay External Factors (CASE-C-2023)
   Type: construction service
   Outcome: no breach found
   Key reasoning: external factors beyond control, force majeure applied
   Delay period: one month

⚖️  HITL #1: Human input needed

💭 APPLICATION PAUSED WAITING FOR USER INPUT...
👨‍⚖️  Which precedent is most similar to your case? </code></pre><p>Das Interessante an dieser Anwendung ist, dass wir in natürlicher Sprache eine Option auswählen können, wobei das LLM den Input des Nutzers interpretiert, um die richtige Wahl zu ermitteln. Lassen Sie uns sehen, was passiert, wenn wir den Text eingeben: <strong>„Fall H“</strong></p><pre><code>💭 APPLICATION PAUSED WAITING FOR USER INPUT...
👨‍⚖️  Which precedent is most similar to your case? Case H

✅ Selected: Case H: Pattern of Repeated Delays

📝 Drafting initial legal analysis...

AMBIGUITY DETECTED:
Based on Case H, a pattern of repeated delays can indeed constitute a breach of contract, even if each individual delay is minor. The outcome in Case H indicates that the cumulative effect of these minor delays led to a significant failure to perform the contractual obligations adequately. The reasoning emphasizes that consistent performance is critical in fulfilling the terms of a contract. Therefore, if the repeated delays create a situation where the overall performance is hindered, this pattern could be interpreted as a breach. However, the interpretation may depend on the specific terms of the contract at issue, as well as the expectations of performance set forth in that contract.

Missing information:
1. Specific contract terms regarding performance timelines
2. Details on the individual delays (duration, frequency)
3. Context on consequences of delays stated in the contract
4. Other parties' expectations or agreements related to performance


⚖️  HITL #2: Additional context needed


💭 APPLICATION PAUSED WAITING FOR USER INPUT...
👨‍⚖️  Please provide clarification about your contract terms:</code></pre><p>Das Modell nimmt die Erläuterungen des Nutzers auf und integriert sie in den Workflow, um mit der endgültigen Analyse fortzufahren, sobald genügend Kontext vorhanden ist. In diesem Schritt nutzt das System auch die zuvor festgestellte Unklarheit: Die Entwurfsanalyse hat fehlende Vertragsdetails hervorgehoben, die die rechtliche Auslegung maßgeblich beeinflussen könnten. Diese „fehlenden Informationen“ dienen dem Modell als Leitfaden, um festzustellen, welche Klarstellungen unerlässlich sind, um Unsicherheiten zu beseitigen, bevor eine verlässliche endgültige Meinung abgegeben werden kann.</p><p>Der Nutzer muss bei dem nächsten Input die gewünschten Erläuterungen angeben. Versuchen wir es mit <strong>„Vertrag erfordert &#x27;pünktliche Lieferung&#x27; ohne Zeitpläne. 8 Verzögerungen von 2-4 Tagen über 6 Monate. 50.000 $ Verluste durch 3 verpasste Kundenfristen. Verkäufer benachrichtigt, aber das Muster hält an.“</strong></p><pre><code>💭 APPLICATION PAUSED WAITING FOR USER INPUT...
👨‍⚖️  Please provide clarification about your contract terms: Contract requires "prompt delivery" without timelines. 8 delays of 2-4 days over 6 months. $50K in losses from 3 missed client deadlines. Vendor notified but pattern continued.

📋 Generating final legal analysis...

================================================================================
⚖️  FINAL LEGAL ANALYSIS
================================================================================

To analyze the question of whether a pattern of repeated minor delays constitutes a breach of contract, we need to combine insights from the selected precedent, the specifics of the lawyer's contract situation, conditions that typically govern breach versus non-breach, and practical recommendations for the lawyer moving forward.

### 1. Selected Precedent's Reasoning

The precedent case, referred to as Case H, found that a pattern of repeated delays amounted to a breach of contract. The court reasoned that even minor individual delays, when considered cumulatively, demonstrated a failure to perform as stipulated in the contract. The underlying rationale was that the cumulative effect of these minor delays could significantly undermine the purpose of the contract, which typically aims for timely performance and reliable delivery.

### 2. Lawyer's Specific Contract Context

In the lawyer's situation, the contract specified "prompt delivery" but did not provide a strict timeline. The vendor experienced 8 delays ranging from 2 to 4 days over a period of 6 months. These delays culminated in $50,000 in losses due to three missed client deadlines. The vendor was notified regarding these delays; however, the pattern of delays persisted.

Key considerations include:
- **Nature of the Obligations**: While “prompt delivery” does not define a strict timeline, it does imply an expectation for timely performance.
- **Material Impact**: The missed client deadlines indicate that these delays had a material adverse effect on the lawyer's ability to fulfill contractual obligations to third parties, likely triggering damages.

### 3. Conditions for Breach vs. No Breach

**Conditions for Breach**:
- **Pattern and Cumulative Effect**: Similar to the reasoning in Case H, evidence of a habitual pattern of delays can amount to a breach. Even if individual delays are minor, when combined, they may show a lack of diligence or reliability by the vendor.
- **Materiality**: The impact of these delays is crucial. If the cumulative delays adversely affect the contract's purpose or cause significant losses, this reinforces the case for a breach.
- **Notification and Opportunity to Cure**: The fact that the vendor was notified of the delays and failed to rectify the behavior can often be interpreted as a further indication of breach.

**Conditions for No Breach**:
- **Non-Material Delays**: If the delays did not affect the overall contractual performance or client obligations, this may lessen the likelihood of establishing a breach. However, given the risks and losses involved, this seems less relevant in this scenario.
- **Force Majeure or Justifiable Delays**: If the vendor could show that these delays were due to justify circumstances not within their control, it may potentially provide a defense against breach claims.

### 4. Practical Recommendations

1. **Assess Damages**: Document the exact nature of the financial losses incurred due to the missed deadlines to substantiate claims of damages.
  
2. **Gather Evidence**: Collect all communication regarding the delays, including any notifications sent to the vendor about the issues.

3. **Consider Breach of Contract Action**: Based on the precedent and accumulated delays, consider formalized communication to the vendor regarding a breach of contract claim, highlighting both the pattern and the impact of these repeated delays.

4. **Evaluate Remedies**: Depending upon the contract specifics, the lawyer may wish to pursue several remedies, including:
   - **Compensatory Damages**: For the financial losses due to missed deadlines.
   - **Specific Performance**: If timely delivery is critical and can still be enforced.
   - **Contract Termination**: Depending on the severity, terminating the contract and seeking replacements may be warranted.

5. **Negotiate Terms**: If continuing to work with the current vendor is strategic, the lawyer should consider renegotiating terms for performance guarantees or penalties for further delays.

6. **Future Contracts**: In future contracts, consider including explicit timelines and conditions for prompt delivery, as well as specified damages for delays to better safeguard against this issue.

By integrating the legal principles from the precedent with the specific context and conditions outlined, the lawyer can formulate a solid plan to address the repeated delays by the vendor effectively.</code></pre><p>Dieser Ausgang zeigt die letzte Phase des Workflows, in der das Modell den ausgewählte Präzedenzfall (Fall H) und die Klärungen des Anwalts integriert, um eine vollständige rechtliche Analyse zu generieren. Das System erklärt, warum das Muster der Verzögerungen wahrscheinlich einen Verstoß darstellt, skizziert die Faktoren, die diese Interpretation stützen, und gibt praktische Empfehlungen. Insgesamt zeigt der Output, wie die HITL-Klärungen Mehrdeutigkeiten auflösen und es dem Modell ermöglichen, eine fundierte, kontextspezifische rechtliche Stellungnahme zu erstellen.</p><h3>Andere reale Szenarien</h3><p>Diese Art von Anwendung, die Elasticsearch, LangGraph und Human-in-the-Loop verwendet, kann in anderen Apps nützlich sein wie:</p><ul><li>Bei der <strong>Überprüfung von Tool-Aufrufen vor ihrer Ausführung</strong>, zum Beispiel im Finanzhandel, genehmigt ein Mensch Kauf-/Verkaufsaufträge, bevor sie erteilt werden.</li><li><strong>Fügen Sie bei Bedarf zusätzliche Parameter an</strong>, zum Beispiel bei der Triage des Kundensupports, bei der ein menschlicher Mitarbeiter die richtige Problemkategorie auswählt, wenn die KI mehrere mögliche Interpretationen des Problems des Kunden findet.</li></ul><p>Und es gibt viele Anwendungsfälle, die es noch zu entdecken gilt, in denen Human-in-the-Loop ein entscheidender Faktor sein wird.</p><h2>Fazit</h2><p>Mit LangGraph und Elasticsearch können wir Agenten erstellen, die eigene Entscheidungen treffen und als lineare Workflows agieren oder Bedingungen erfüllen, die sie dazu veranlassen, den einen oder anderen Pfad zu wählen. Mit Human-in-the-Loop können die Agenten den tatsächlichen Nutzer in den Entscheidungsprozess einbeziehen, um kontextuelle Lücken zu füllen und Bestätigungen für Systeme anzufordern, bei denen Fehlertoleranz entscheidend ist.</p><p>Einer der Vorteile dieses Ansatzes ist, dass man einen großen Datensatz mithilfe der Elasticsearch-Funktionen filtern und dann mit einem LLM ein einzelnes Dokument als Nutzerauswahl erhalten kann. Dieser letzte Schritt wäre mit Elasticsearch allein viel komplizierter, da es viele Möglichkeiten gibt, wie ein Mensch ein Ergebnis in natürlicher Sprache interpretieren kann.</p><p>Mit diesem Ansatz bleibt das System schnell und Token-effizient, da wir dem LLM nur das senden, was für die endgültige Entscheidung benötigt wird, und nicht die gesamten Datensätze. Gleichzeitig wird die Absicht des Nutzers sehr genau erkannt und so lange iteriert, bis die gewünschte Option ausgewählt ist.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Automatisierung des Log-Parsing in Streams mit ML]]></title>
            <description><![CDATA[Erfahren Sie, wie ein hybrider ML-Ansatz durch Automatisierungsexperimente mit Log-Format-Fingerprinting in Streams eine Genauigkeit von 94 % beim Log-Parsing und 91 % bei der Log-Partitionierung erreicht hat.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/log-parsing-partitioning-automation-experiments-streams</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/log-parsing-partitioning-automation-experiments-streams</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Nastia Havriushenko]]></dc:creator>
            <pubDate>Fri, 02 Jan 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/965c58f19742361160593c38fcaa8b2f4b0d6cc5-3838x2159.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>In modernen Beobachtbarkeits-Stacks bleibt es eine Herausforderung, unstrukturierte Logs von verschiedenen Datenanbietern in Plattformen wie Elasticsearch zu Ingestieren. Die Abhängigkeit von manuell erstellten Parsing-Regeln führt zu fehleranfälligen Daten-Pipelines, bei denen selbst geringfügige Aktualisierungen des vorgelagerten Codes zu Parsing-Fehlern und nicht indizierten Daten führen. Diese Fragilität wird durch die Herausforderung der Skalierbarkeit noch verstärkt: In dynamischen Microservices-Umgebungen macht die kontinuierliche Hinzufügung neuer Services die manuelle Regelwartung zu einem operativen Albtraum.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9108ec303339dd091faa3c363c7cf5c228155f49-3840x2160.png" style="width: 100%;" /><p>Unser Ziel war es, zu einem automatisierten, adaptiven Ansatz überzugehen, der sowohl Log-Parsing (Feldextraktion) als auch Log-Partitionierung (Quellenidentifikation) bewältigen kann. Wir vermuteten, dass Large Language Models (LLMs) mit ihrem inhärenten Verständnis von Codesyntax und semantischen Mustern diese Aufgaben mit minimalem menschlichem Eingreifen automatisieren könnten.</p><p>Wir freuen uns, Ihnen mitteilen zu können, dass dieses Feature bereits in <a href="http://elastic.co/elasticsearch/streams"><span style="text-decoration:underline">Streams</span></a> verfügbar ist!</p><h2>Beschreibung des Datensatzes</h2><p>Wir haben für PoC-Zwecke eine <strong><a href="https://github.com/logpai/loghub">Loghub-Sammlung</a> </strong>von Logs gewählt. Für unsere Untersuchung wählten wir repräsentative Stichproben aus den folgenden Schlüsselbereichen aus:</p><ul><li>Verteilte Systeme: Wir verwendeten die HDFS- (Hadoop Distributed File System) und Spark-Datensätze. Diese enthalten eine Mischung aus Info-, Fehlerbehebungs- und Fehlermeldungen, die für Big Data-Plattformen typisch sind.</li><li>Server- und Webanwendungen: Logs von Apache-Webservern und OpenSSH boten eine wertvolle Quelle für Zugriffs-, Fehler- und sicherheitsrelevante Ereignisse. Diese sind entscheidend für die Überwachung des Webverkehrs und die Erkennung potenzieller Bedrohungen.</li><li>Betriebssysteme: Wir haben Protokolle von Linux und Windows aufgenommen. Diese Datensätze repräsentieren die üblichen, semistrukturierten Ereignisse auf Systemebene, denen Betriebsteams täglich begegnen.</li><li>Mobile Systeme: Um sicherzustellen, dass unser Modell auch Logs aus mobilen Umgebungen verarbeiten kann, haben wir den Android-Datensatz mit einbezogen. Diese Logs sind oft ausführlich und erfassen eine Vielzahl von Aktivitäten auf Anwendungs- und Systemebene auf Mobilgeräten.</li><li>Supercomputer: Um die Leistung in Hochleistungs-Computing-Umgebungen (HPC) zu testen, haben wir den BGL-Datensatz (Blue Gene/L) integriert, der hochstrukturierte Logs mit spezifischer Domänenterminologie enthält.</li></ul><p>Ein entscheidender Vorteil der Loghub-Sammlung ist, dass die Logs größtenteils unsaniert und unbeschriftet sind, was eine geräuschvolle Live-Produktionsumgebung mit Microservice-Architektur widerspiegelt.</p><p>Log-Beispiele:</p><pre><code>[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</code></pre><p>Zusätzlich haben wir einen Kubernetes-Cluster mit einer typischen Webanwendung und Datenbank erstellt, die zusätzliche Logs in der gängigsten Domäne sammelt.</p><p>Beispiel für gängige Logfelder: Zeitstempel, Log-Ebene (INFO, WARN, FEHLER), Quelle, Nachricht.</p><h2>Few-Shot-Log-Parsing mit einem LLM</h2><p>Unsere erste Reihe von Experimenten konzentrierte sich auf eine grundlegende Frage: <strong>Kann ein LLM zuverlässig Schlüsselfelder identifizieren und konsistente Parsing-Regeln erzeugen, um sie zu extrahieren?</strong></p><p>Wir haben ein Modell gebeten, rohe Log-Stichproben zu analysieren und Log-Parsing-Regeln im regulären Ausdruck (Regex) und im <a href="https://www.elastic.co/docs/explore-analyze/scripting/grok">Grok-Format</a> zu generieren. Unsere Ergebnisse zeigten, dass dieser Ansatz großes Potenzial hat, aber auch erhebliche Herausforderungen bei der Implementierung.</p><h3>Hohe Zuverlässigkeit und Kontextbewusstsein</h3><p>Die ersten Ergebnisse waren vielversprechend. Das LLM zeigte eine starke Fähigkeit, Parsing-Regeln zu generieren, die mit hoher Wahrscheinlichkeit zu den bereitgestellten Beispielen passten. Neben der einfachen Mustererkennung zeigte das Modell die Fähigkeit zum Log-Verständnis – es konnte die Log-Quelle (z. B. Gesundheits-Tracking-App, Nginx-Web-App, Mongo-Datenbank) korrekt identifizieren und benennen.</p><h3>Das „Goldlöckchen“-Dilemma der Eingabestichproben</h3><p>Unsere Experimente zeigten schnell einen erheblichen Mangel an Robustheit aufgrund der extremen<strong> Empfindlichkeit gegenüber der Eingabestichprobe.</strong> Die Leistung des Modells schwankt stark in Abhängigkeit von den spezifischen Log-Beispielen, die im Prompt enthalten sind. Wir haben ein Log-Ähnlichkeitsproblem beobachtet, bei dem die Log-Stichprobe <em>gerade ausreichend unterschiedliche </em>Logs enthalten muss:</p><ul><li>Zu homogen (Overfitting)<strong>:</strong> Wenn die Eingabe-Logs zu ähnlich sind, neigt das LLM dazu, zu <strong>überspezifizieren</strong>. Es behandelt variable Daten – wie spezifische Java-Klassennamen in einem Stack-Trace – als statische Teile der Vorlage. Das Ergebnis sind spröde Regeln, die nur einen winzigen Teil der Logs abdecken und unbrauchbare Felder extrahieren.</li><li>Zu heterogen (Verwirrung): Umgekehrt, wenn die Stichprobe erhebliche Formatierungsunterschiede enthält – oder schlimmer noch, „Müll-Logs“ wie Fortschrittsbalken, Speichertabellen oder ASCII-Art – kämpft das Modell damit, einen gemeinsamen Nenner zu finden. Oftmals greift man dabei auf die Generierung komplexer, fehlerhafter regulärer Ausdrücke zurück oder verallgemeinert die gesamte Zeile vorschnell zu einem einzigen Nachrichten-Feld.</li></ul><h3>Die Einschränkung des Kontextfensters</h3><p>Wir sind außerdem auf einen Engpass im Kontextfenster gestoßen. Wenn die Eingabe-Logs lang, heterogen oder reich an extrahierbaren Feldern waren, verschlechterte sich oft die Ausgabe des Modells und wurde „unübersichtlich“ oder zu lang, um in das Ausgabekontextfenster zu passen. Natürlich hilft Chunking in diesem Fall. Durch das Aufteilen von Protokollen mithilfe zeichenbasierter und entitätsbasierter Trennzeichen könnten wir dem Modell helfen, sich auf das Extrahieren der Hauptfelder zu konzentrieren, ohne von Rauschen überwältigt zu werden.</p><h3>Die Konsistenz- und Standardisierungslücke</h3><p>Selbst wenn das Modell erfolgreich Regeln generierte, stellten wir leichte Inkonsistenzen fest:</p><ul><li>Namensvariationen für Dienste: Das Modell schlägt unterschiedliche Namen für dieselbe Entität vor (z. B. wird die Quelle in verschiedenen Ausführungen als „Spark“, „Apache Spark“ und „Spark log Analytics“ bezeichnet).</li><li>Variationen bei der Feldbenennung: Es fehlte an Standardisierung bei den Feldnamen (z. B. <code>id</code> vs. <code>service.id</code> vs. <code>device.id</code>). Wir haben Namen mithilfe einer standardisierten <a href="https://www.elastic.co/docs/reference/ecs/ecs-field-reference">Elastic-Feldbenennung</a> normalisiert.</li><li>Auflösungsvarianz: Die Auflösung der Feldextraktion variierte je nachdem, wie ähnlich die Eingabe-Logs einander waren.</li></ul><h2>Log-Format-Fingerprint</h2><p>Um die Herausforderung der Log-Ähnlichkeit anzugehen, führen wir eine leistungsstarke Heuristik ein: <strong>Log-Format-Fingerprint (LFF)</strong>.</p><p>Anstatt rohe, verrauschte Logs direkt in ein LLM einzuspeisen, wenden wir zunächst eine deterministische Transformation an, um die zugrundeliegende Struktur jeder Nachricht zu enthüllen. Dieser Vorverarbeitungsschritt abstrahiert variable Daten und generiert einen vereinfachten „Fingerabdruck“, der es uns ermöglicht, verwandte Logs zu gruppieren.</p><p>Die Mapping-Logik ist einfach, um Geschwindigkeit und Konsistenz zu gewährleisten:</p><ol><li>Ziffernabstraktion: Jede Ziffernfolge (0–9) wird durch eine einzelne „0“ ersetzt.</li><li>Textabstraktion: Jede Folge von alphabetischen Zeichen mit Leerzeichen wird durch ein einzelnes „a“ ersetzt.</li><li>Normalisierung von Leerzeichen: Alle Sequenzen von Leerzeichen (Leerzeichen, Tabulatoren, Zeilenumbrüche) werden zu einem einzigen Leerzeichen zusammengefasst.</li><li>Symbolerhaltung: Zeichensetzung und Sonderzeichen (z. B. :, [, ], /) werden beibehalten, da sie oft die stärksten Indikatoren für die Log-Struktur sind.</li></ol><p>Wir stellen den Log-Mapping-Ansatz vor. Die grundlegenden Mapping-Muster umfassen Folgendes:</p><ul><li>Ziffern 0–9 von beliebiger Länge -&gt; auf „0“.</li><li>Text (alphabetische Zeichen mit Leerzeichen) von beliebiger Länge -&gt; auf „a“.</li><li>Leerzeichen, Tabulatoren und neue Zeilen -&gt; auf ein einzelnes Leerzeichen.</li></ul><p>Schauen wir uns ein Beispiel an, wie uns dieses Mapping die Transformation der Logs ermöglicht.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/78fa2887486eb9417804354ee3bf2a4fdb0f6383-846x252.png" style="width: 100%;" /><p>Dadurch erhalten wir folgende Log-Masken:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ec439a3d3a25002498b97defcff733ea5ebc6b55-826x94.png" style="width: 100%;" /><p>Beachten Sie die Fingerabdrücke der ersten beiden Logs. Trotz unterschiedlicher Zeitstempel, Quellklassen und Nachrichteninhalte sind ihre Präfixe (<code>0/0/0 0:0:0 a a.a:</code>) identisch. Durch diese strukturelle Ausrichtung können wir diese Logs automatisch in denselben Cluster einordnen.</p><p>Das dritte Log erzeugt jedoch einen völlig abweichenden Fingerabdruck (<code>0-0-0...</code>). Dies ermöglicht es uns, es algorithmisch von der ersten Gruppe zu trennen, <em>bevor</em> wir überhaupt ein LLM aufrufen.</p><h2>Bonus: Sofortige Implementierung mit ES|QL</h2><p>Es ist so einfach wie das Übergeben dieser Abfrage in Discover.</p><pre><code>FROM loghub |
EVAL pattern = REPLACE(REPLACE(REPLACE(REPLACE(raw_message, "[ \t\n]+", " "), "[A-Za-z]+", "a"), "[0-9]+", "0"), "a( a)+", "a") |
STATS total_count = COUNT(), ratio = COUNT() / 2000.0, datasources=VALUES(filename), example=TOP(raw_message, 3, "desc") BY SUBSTRING(pattern, 0, 15) |
SORT total_count DESC |
LIMIT 100</code></pre><p><strong>Abfrage-Aufschlüsselung:</strong></p><p><strong>FROM</strong> LogHub: Zielt auf unseren Index mit den Rohprotokolldaten ab.</p><p><strong>EVAL</strong> Muster = …: Die Kern-Mapping-Logik. Wir verketten REPLACE-Funktionen, um die Abstraktion durchzuführen (z. B. Ziffern zu &#x27;0&#x27;, Text zu &#x27;a&#x27; usw.) und speichern das Ergebnis in einem „Muster“-Feld.</p><p><strong>STATS </strong>[column1 =] expression1, …<strong> BY </strong>SUBSTRING(pattern, 0, 15):</p><p>Dies ist ein Clustering-Schritt. Wir gruppieren Protokolle, die die ersten 15 Zeichen ihres Musters gemeinsam haben, und erstellen aggregierte Felder wie die Gesamtzahl der Protokolle pro Gruppe, eine Liste der Protokoll-Datenquellen, das Musterpräfix und 3 Protokollbeispiele.</p><p><strong>SORT</strong> total_count DESC | <strong>LIMIT</strong> 100 : Zeigt die 100 häufigsten Log-Muster an</p><p>Die Abfrageergebnisse auf LogHub werden unten angezeigt:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b119498f124376c41d242a099bf9081fd6536be8-1600x394.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4dcfc0a5b7fa753497cc5def5ea3cd54449c0481-1600x719.png" style="width: 100%;" /><p>Wie in der Visualisierung gezeigt, partitioniert dieser „LLM-freie“ Ansatz Protokolle mit hoher Genauigkeit. Es gelang ihm, 10 von 16 Datenquellen (basierend auf LogHub-Labels) vollständig zu clustern (&gt;90 %), und er erreichte ein Mehrheits-Clustering in 13 von 16 Quellen (&gt;60 %) – alles ohne zusätzliche Reinigung, Vorverarbeitung oder Feinabstimmung.</p><p>Log-Format-Fingerprinting bietet eine pragmatische, wirkungsvolle Alternative und Ergänzung zu ausgefeilten ML-Lösungen wie der <a href="https://www.elastic.co/docs/reference/aggregations/search-aggregations-bucket-categorize-text-aggregation">Log-Pattern-Analyse.</a> Es bietet sofortige Einblicke in die Zusammenhänge der Logs und verwaltet große Log-Cluster effektiv.</p><ul><li>Vielseitigkeit als Grundform </li></ul><p>Dank <a href="https://www.elastic.co/blog/getting-started-elasticsearch-query-language">ES|QL-Implementierung</a> dient LFF sowohl als eigenständiges Werkzeug für schnelle Datendiagnostik/-visualisierungen als auch als Baustein in Loganalyse-Pipelines für Anwendungsfälle mit hohem Volumen. </p><ul><li>Flexibilität</li></ul><p>LFF lässt sich leicht anpassen und erweitern, um spezifische Muster zu erfassen, z. B. hexadezimale Zahlen und IP-Adressen.</p><ul><li>Deterministische Stabilität</li></ul><p>Im Gegensatz zu ML-basierten Clustering-Algorithmen ist die LFF-Logik geradlinig und deterministisch. Neue eingehende Logs wirken sich nicht rückwirkend auf bestehende Log-Cluster aus.</p><ul><li>Leistung und mMemory</li></ul><p>Es benötigt nur minimalen Speicher, kein Training und keine GPU und ist daher ideal für Echtzeit-Umgebungen mit hohem Durchsatz geeignet.</p><h2>Kombination des Log-Format-Fingerprints mit einem LLM</h2><p>Zur Validierung der vorgeschlagenen hybriden Architektur enthielt jedes Experiment eine zufällige 20%ige Teilmenge der Logs aus jeder Datenquelle. Diese Einschränkung simuliert eine reale Produktionsumgebung, in der Logs in Batches und nicht als monolithischer historischer Dump verarbeitet werden.</p><p>Das Ziel war zu demonstrieren, dass LFF als effektive Kompressionsschicht fungiert. Wir wollten beweisen, dass Parsing-Regeln mit hoher Abdeckung aus kleinen, kuratierten Stichproben generiert und erfolgreich auf den gesamten Datensatz verallgemeinert werden können.</p><h2>Ausführungspipeline</h2><p>Wir haben eine mehrstufige Pipeline implementiert, die die Daten filtert, gruppiert und stratifizierte Stichproben auf sie anwendet, bevor sie das LLM erreichen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b3f46ea471760b406a32fc7d4bc74cc03faaced2-3840x1660.png" style="width: 100%;" /><p>1. Zweistufiges hierarchisches Clustering</p><ul><li>Unterklassen (exakte Übereinstimmung): Logs werden anhand identischer Fingerabdrücke aggregiert. Alle Logs einer Unterklasse haben exakt die gleiche Formatstruktur.</li><li>Ausreißerbereinigung. Wir verwerfen alle Unterklassen, die weniger als 5 % des gesamten Logvolumens ausmachen. Dadurch wird sichergestellt, dass sich das LLM auf das dominante Signal konzentriert und nicht durch Rauschen oder fehlerhafte Logs abgelenkt wird.</li><li>Metaklassen (Präfixübereinstimmung): Verbleibende Unterklassen werden in Metaklassen nach den ersten N Zeichen der Format-Fingerabdruckübereinstimmung gruppiert. Wir haben N=5 für das Log-Parsing und N=15 für die Log-Partitionierung gewählt, wenn die Datenquellen unbekannt sind.</li></ul><p>2. Stratifiziertes Sampling. Sobald der hierarchische Baum erstellt ist, erstellen wir die Log-Stichprobe für das LLM. Das strategische Ziel ist es, die Varianzabdeckung zu maximieren und gleichzeitig die Verwendung von Token zu minimieren.</p><ul><li>Wir wählen repräsentative Logs aus <em>jeder</em> gültigen Unterklasse innerhalb der breiteren Metaklasse aus.</li><li>Um einen Randfall mit zu vielen Unterklassen zu managen, wenden wir zufälliges Downsampling an, um die Zielfenstergröße anzupassen.</li></ul><p>3. Regelgenerierung. Abschließend fordern wir das LLM auf, eine Regex-Parsing-Regel zu generieren, die auf alle Logs in der bereitgestellten Stichprobe für jede Metaklasse zutrifft. Für unseren PoC haben wir das Modell GPT-4o Mini verwendet.</p><h2>Experimentelle Ergebnisse und Beobachtungen</h2><p>Wir haben auf dem Loghub-Datensätze eine Parsing-Genauigkeit von 94 % und eine Partitionierungs-Genauigkeit von 91 % erreicht.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/49b2b6a1401dd1f33951da68e5a3fac37d0b5aaa-1600x1506.png" style="width: 100%;" /><p>Die obige Konfusionsmatrix veranschaulicht die Ergebnisse der Log-Partitionierung. Die vertikale Achse stellt die tatsächlichen Datenquellen dar, die horizontale Achse die vorhergesagten Datenquellen. Die Intensität der Heatmap entspricht dem Log-Volumen, wobei leichtere Kacheln auf eine höhere Anzahl hinweisen. Die diagonale Ausrichtung zeigt die hohe Genauigkeit des Modells bei der Quellenzuweisung mit minimaler Streuung.</p><h2>Einblicke aus unseren Leistungsvergleichsanalysen:</h2><ul><li><strong>Optimale Ausgangsbasis:</strong> Ein Kontextfenster von <strong>30 bis 40 Log-Stichproben</strong> pro Kategorie erwies sich als der „Sweet Spot“, der sowohl mit Regex- als auch mit Grok-Mustern durchweg ein robustes Parsing ermöglichte.</li><li><strong>Eingabeminimierung:</strong> Wir haben die Eingabegröße für Regex-Muster auf 10 Logs pro Kategorie erhöht und nur einen 2%igen Rückgang der Parsing-Leistung festgestellt, was bestätigt, dass diversitätsbasierte Stichproben kritischer sind als das rohe Volumen.</li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Eine Einführung in Jina-Modelle, ihre Funktionalität und ihre Einsatzmöglichkeiten in Elasticsearch]]></title>
            <description><![CDATA[Entdecken Sie multimodale Einbettungen von Jina, Reranker v3 und semantische Einbettungsmodelle und erfahren Sie, wie Sie diese nativ in Elasticsearch verwenden können.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/jina-models-elasticsearch-guide</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/jina-models-elasticsearch-guide</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Scott Martens]]></dc:creator>
            <pubDate>Thu, 01 Jan 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/407b4c862b51ebdfc7f26db4e25950a65caf1673-656x442.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Jina by Elastic bietet Suchgrundlagenmodelle für Anwendungen und die Automatisierung von Geschäftsprozessen. Diese Modelle bieten Kernfunktionen für den Einsatz von KI in Elasticsearch-Anwendungen und innovativen KI-Projekten.</p><p>Jina-Modelle lassen sich in drei Hauptkategorien einteilen, die zur Unterstützung der Informationsverarbeitung, Organisation und den Informationsabruf entwickelt wurden:</p><ul><li>Semantische Einbettungsmodelle</li><li>Reranking-Modelle</li><li>Kleine generative Sprachmodelle</li></ul><h2>Semantische Einbettungsmodelle</h2><p>Die Idee hinter semantischen Einbettungen ist, dass ein KI-Modell lernen kann, Aspekte der Bedeutung seiner Eingaben in Bezug auf die Geometrie hochdimensionaler Räume darzustellen.</p><p>Sie können sich eine semantische Einbettung als einen Punkt (technisch gesehen einen <em>Vektor</em>) in einem hochdimensionalen Raum vorstellen. Ein Einbettungsmodell ist ein neuronales Netz, das digitale Daten als Eingabe aufnimmt (potenziell alles, aber meist Text oder Bild) und die Position eines entsprechenden hochdimensionalen Punktes als numerische Koordinaten ausgibt. Wenn das Modell seine Aufgabe gut erfüllt, ist der Abstand zwischen zwei semantischen Einbettungen proportional dazu, wie sehr ihre entsprechenden digitalen Objekte dieselbe Bedeutung haben.</p><p>Um zu verstehen, wie wichtig das für Suchanwendungen ist, stellen Sie sich eine Einbettung für das Wort „dog“ und eine für das Wort „cat“ als Punkte im Raum vor:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/802cf9bbcb82180d3fc91009f9f62027eee8f031-615x615.png" style="width: 100%;" /><p>Ein gutes Einbettungsmodell sollte eine Einbettung für das Wort „feline“ generieren, die viel näher an „cat“ als an „dog“ liegt, und „canine“ sollte eine Einbettung haben, die viel näher an „dog“ als an „cat“ liegt, weil diese Wörter fast dasselbe bedeuten:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/bce49daf9a31b8fb7ce1c6ef7ae4e8117a4e8b33-615x615.png" style="width: 100%;" /><p>Wenn ein Modell mehrsprachig ist, würden wir dasselbe für Übersetzungen von „cat“ und „dog“ erwarten:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ce4d030385324526cbd7539140e0e634d939371c-615x615.png" style="width: 100%;" /><p>Einbettungsmodelle übersetzen Ähnlichkeiten oder Unterschiede in der Bedeutung zwischen Dingen in räumliche Beziehungen zwischen Einbettungen. Die Bilder oben haben nur zwei Dimensionen, sodass Sie sie auf einem Bildschirm sehen können, aber das Einbetten von Modellen erzeugt Vektoren mit Dutzenden bis Tausenden von Dimensionen. Dies ermöglicht es ihnen, die Feinheiten der Bedeutung ganzer Texte zu kodieren, indem sie einen Punkt in einem Raum mit Hunderten oder Tausenden von Dimensionen für Dokumente mit Tausenden von Wörtern oder mehr zuweisen.</p><h2>Multimodale Einbettungen</h2><p>Multimodale Modelle erweitern das Konzept der semantischen Einbettung auf andere Dinge als Texte, insbesondere auf Bilder. Wir würden erwarten, dass eine Einbettung für ein Bild nahe an einer Einbettung einer getreuen Beschreibung des Bildes liegt:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1ac6aef5b1423e5fe4853e8a547a74e66b0885c2-615x615.png" style="width: 100%;" /><p>Semantische Einbettungen haben viele Einsatzmöglichkeiten. Unter anderem können Sie sie verwenden, um effiziente Klassifikatoren zu erstellen, Clustering durchzuführen und eine Vielzahl von Aufgaben zu erledigen, wie z. B. die Deduplizierung von Daten und die Untersuchung der Datendiversität. Beides ist wichtig für Big-Data-Anwendungen, bei denen mit zu vielen Daten gearbeitet wird, um sie manuell zu verwalten.</p><p>Die größte direkte Anwendung von Einbettungen ist der Informationsabruf. Elasticsearch kann Abrufobjekte mit Einbettungen als Schlüssel speichern. Abfragen werden in Einbettungsvektoren umgewandelt, und eine Suche gibt die gespeicherten Objekte zurück, deren Schlüssel den Abfrage-Einbettungen am nächsten sind.</p><p>Bei der traditionellen <em>vektorbasierten Suche</em> (manchmal auch als <em>Sparse Vector Retrieval</em> bezeichnet) werden Vektoren verwendet, die auf Wörtern oder Metadaten in Dokumenten und Anfragen basieren. <em>Die einbettungsbasierte Suche</em> (auch bekannt als <em>Dense Vector Retrieval</em>) verwendet hingegen KI-bewertete Bedeutungen anstelle von Wörtern. Dadurch sind sie im Allgemeinen wesentlich flexibler und genauer als herkömmliche Suchmethoden.</p><h2>Matryoshka-Darstellungslernen</h2><p>Die Anzahl der Dimensionen, die eine Einbettung hat, und die Präzision der darin enthaltenen Zahlen haben erhebliche Auswirkungen auf die Leistung. Äußerst hochdimensionale Räume und extrem hochpräzise Zahlen können sehr detaillierte und komplexe Informationen darstellen, erfordern aber größere KI-Modelle, deren Training und Nutzung teurer sind. Die erzeugten Vektoren benötigen mehr Speicherplatz, und es braucht mehr Rechenzyklen, um die Abstände zwischen ihnen zu berechnen. Bei der Verwendung semantischer Einbettungsmodelle müssen wichtige Kompromisse zwischen Präzision und Ressourcenverbrauch eingegangen werden.</p><p>Um die Flexibilität für die Nutzer zu maximieren, werden Jina-Modelle mit einer Technik namens <a href="https://arxiv.org/abs/2205.13147">Matryoshka Representation Learning</a> trainiert. Dies führt dazu, dass die Modelle die wichtigsten semantischen Unterscheidungen in die ersten Dimensionen des Einbettungsvektors vorladen, sodass man die höheren Dimensionen einfach abschneiden und trotzdem eine gute Leistung erzielen kann.</p><p>In der Praxis bedeutet das, dass Nutzer von Jina-Modellen wählen können, wie viele Dimensionen ihre Einbettungen haben sollen. Die Wahl von weniger Dimensionen verringert die Präzision, der Leistungsverlust ist jedoch gering. Bei den meisten Aufgaben sinken die Leistungskennzahlen für Jina-Modelle um 1 bis 2 %, wenn man die Einbettungsgröße um 50 % reduziert, bis hin zu einer Reduzierung um etwa 95 %.</p><h2>Asymmetrischer Informationsabruf</h2><p>Semantische Ähnlichkeit wird normalerweise symmetrisch gemessen. Der Wert, den man beim Vergleich von „cat“ mit „dog“ erhält, ist derselbe wie der Wert, den man beim Vergleich von „dog“ mit „cat“ erhält. Bei der Verwendung von Einbettungen für den Informationsabruf funktionieren diese jedoch besser, wenn man die Symmetrie aufhebt und Anfragen anders kodiert als die zu kodierenden Objekte.</p><p>Das liegt an der Art, wie wir Einbettungsmodelle trainieren. Die Trainingsdaten enthalten Instanzen der gleichen Elemente, wie z. B. Wörter, in vielen verschiedenen Kontexten. Die Modelle lernen die Semantik, indem sie die kontextuellen Ähnlichkeiten und Unterschiede zwischen den Elementen vergleichen.</p><p>So könnten wir beispielsweise feststellen, dass das Wort „animal“ nicht in sehr vielen der gleichen Kontexte wie „cat“ oder „dog“ vorkommt, und daher ist die Einbettung für „animal“ möglicherweise nicht besonders nahe an der von „cat“ oder „dog“:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9a33163405af6c71ee7f4ba8ebc86af39e295a69-615x615.png" style="width: 100%;" /><p>Dies macht es unwahrscheinlicher, dass eine Abfrage nach „animal“ Dokumente über Katzen und Hunde zurückgibt – das Gegenteil unseres Ziels. Deshalb kodieren wir „Tier“ anders, wenn es sich um eine Suchanfrage handelt, als wenn es ein Ziel für den Informationsabruf ist:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/363992d4f0affba7937c0c8a9f82c9a531fcd3ba-615x615.png" style="width: 100%;" /><p><em>Asymmetrischer Informationsabruf</em> bedeutet, ein anderes Modell für Abfragen zu verwenden oder ein Einbettungsmodell speziell zu trainieren, um Dinge auf eine bestimmte Weise zu kodieren, wenn sie für den Abruf gespeichert sind, und Abfragen auf eine andere Weise zu kodieren.</p><h2>Multivektor-Einbettungen</h2><p>Einzelne Einbettungen sind gut für die Informationsabfrage, da sie in das grundlegende Framework einer indizierten Datenbank passen: Wir speichern Objekte für die Abfrage mit einem einzelnen Einbettungsvektor als deren Abfrageschlüssel. Wenn Nutzer den Dokumentenspeicher abfragen, werden ihre Abfragen in Einbettungsvektoren übersetzt und die Dokumente, deren Schlüssel der Abfrage-Einbettung am nächsten sind (im hochdimensionalen Einbettungsraum), werden als Kandidatenmatches abgerufen.</p><p>Multivektor-Einbettungen funktionieren etwas anders. Anstatt einen Vektor mit fester Länge zu erzeugen, um eine Abfrage und ein ganzes gespeichertes Objekt darzustellen, erzeugen sie eine Folge von Einbettungen, die kleinere Teile davon repräsentieren. Die einzelnen Teile sind typischerweise Tokens oder Wörter für Texte und Bildkacheln für visuelle Daten. Diese Einbettungen spiegeln die Bedeutung des Teils in seinem Kontext wider.</p><p>Betrachten wir zum Beispiel die folgenden Sätze:</p><ul><li>Sie hatte ein Herz aus Gold.</li><li>Sie hatte einen Herzenswandel.</li><li>Sie hatte einen Herzinfarkt.</li></ul><p>Oberflächlich betrachtet sehen sie sehr ähnlich aus, aber ein Multivektor-Modell würde wahrscheinlich sehr unterschiedliche Einbettungen für jede Instanz von „Herz“ generieren, was darstellt, wie jede im Kontext des gesamten Satzes etwas anderes bedeutet:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a33e60c8d8ee3d312bca8375ca2a8b0a0cd40ba9-615x615.png" style="width: 100%;" /><p>Der Vergleich zweier Objekte anhand ihrer Multivektor-Einbettungen beinhaltet oft die Messung ihres Chamfer-Abstands: der Vergleich jedes Teils einer Multivektor-Einbettung mit jedem Teil einer anderen und die Summierung der minimalen Abstände zwischen ihnen. Andere Systeme, einschließlich des unten beschriebenen Jina Rerankers, geben sie in ein KI-Modell ein, das speziell für die Bewertung ihrer Ähnlichkeit trainiert wurde. Beide Ansätze haben in der Regel eine höhere Präzision als der Vergleich von Einzeleinbettungen, da Einbettungen mit mehreren Vektoren viel detailliertere Informationen enthalten als solche mit einem Vektor.</p><p>Allerdings eignen sich Multivektor-Einbettungen nicht gut zum Indexieren. Sie werden häufig bei Neubewertungsaufgaben verwendet, wie im nächsten Abschnitt für das Modell <code>jina-colbert-v2</code> beschrieben.</p><h2>Jina-Einbettungsmodelle</h2><h3>Jina-Einbettungen – v4</h3><p><a href="https://jina.ai/news/jina-embeddings-v4-universal-embeddings-for-multimodal-multilingual-retrieval/"><strong>jina-embeddings-v4</strong></a> ist ein 3,8 Milliarden (3,8x10⁹) Parameter umfassendes mehrsprachiges und multimodales Einbettungsmodell, das Bilder und Texte in einer Vielzahl weit verbreiteter Sprachen unterstützt. Es verwendet eine neuartige Architektur, um visuelle Kenntnisse und Sprachkenntnisse zu nutzen, um die Leistung bei beiden Aufgaben zu verbessern, sodass es beim Abrufen von Bildern und insbesondere beim <a href="https://huggingface.co/tasks/visual-document-retrieval">visuellen Abrufen von Dokumenten</a> hervorragende Leistungen erbringt. Das bedeutet, dass es Bilder wie Diagramme, Dias, Karten, Screenshots, Seitenscans und Diagramme verarbeitet – gängige Bildtypen, oft mit wichtigem eingebettetem Text, die außerhalb des Bereichs von Computer-Vision-Modellen fallen, die auf Bildern realer Szenen trainiert sind.</p><p>Wir haben dieses Modell mithilfe kompakter <a href="https://huggingface.co/docs/peft/en/package_reference/lora">Low-Rank Adaptation (LoRA)-Adapter</a> für verschiedene Aufgaben optimiert. Dadurch können wir ein einzelnes Modell trainieren, um sich auf mehrere Aufgaben zu spezialisieren, ohne bei einer davon die Leistung zu beeinträchtigen – mit minimalen zusätzlichen Kosten für Speicher oder Verarbeitung.</p><p>Zu den wichtigsten Funktionen gehören:</p><ul><li>Spitzenleistung beim visuellen Dokumentenabruf sowie eine mehrsprachige Text- und Bildverarbeitungsleistung, die deutlich größere Modelle übertrifft.</li><li>Unterstützung für große Eingabekontextgrößen: 32.768 Tokens entsprechen ungefähr 80 Seiten doppeltzeiligem englischen Text, und 20 Megapixel entsprechen einem Bild von 4.500 x 4.500 Pixeln.</li><li>Vom Nutzer ausgewählte Einbettungsgrößen, von maximal 2048 Dimensionen bis zu 128 Dimensionen. Wir haben empirisch festgestellt, dass die Leistung unterhalb dieser Schwelle dramatisch abnimmt.</li><li>Unterstützung für sowohl einzelne Einbettungen als auch Multivektor-Einbettungen. Für Texte besteht die Multivektor-Ausgabe aus einer 128-dimensionalen Einbettung für jedes Eingabetoken. Für Bilder erzeugt es eine 128-dimensionale Einbettung für jede 28x28 Pixel große Kachel, die zur Abdeckung des Bildes benötigt wird.</li><li>Optimierung für asymmetrischen Datenabruf mittels eines Paares von LoRA-Adaptern, die speziell für diesen Zweck trainiert wurden.</li><li>Ein LoRA-Adapter, optimiert für die Berechnung semantischer Ähnlichkeit.</li><li>Spezielle Unterstützung für Programmiersprachen und IT-Frameworks, ebenfalls über einen LoRA-Adapter.</li></ul><p>Wir haben <code>jina-embeddings-v4</code> als allgemeines Mehrzweckwerkzeug für eine breite Palette gängiger Such-, Sprachverarbeitungs- und KI-Analyseaufgaben entwickelt. Es handelt sich im Verhältnis zu seinen Fähigkeiten um ein relativ kleines Modell, dessen Bereitstellung jedoch erhebliche Ressourcen erfordert und das sich am besten für die Nutzung über eine Cloud-API oder in einer Umgebung mit hohem Datenaufkommen eignet.</p><h3>Jina-Einbettungen – v3</h3><p><a href="https://jina.ai/news/jina-embeddings-v3-a-frontier-multilingual-embedding-model/"><strong>jina-embeddings-v3</strong></a> ist ein kompaktes, leistungsstarkes, mehrsprachiges, ausschließlich textbasiertes Einbettungsmodell mit weniger als 600 Millionen Parametern. Es unterstützt bis zu 8192 Texteingabetoken und liefert Einzelvektor-Einbettungen mit vom Nutzer gewählten Größen von einem Standard von 1024 Dimensionen bis zu 64 als Ausgabe.</p><p>Wir haben <code>jina-embeddings-v3</code> für eine Vielzahl von Textaufgaben trainiert – nicht nur für den Informationsabruf und die semantische Ähnlichkeit, sondern auch für Klassifikationsaufgaben wie die Sentiment-Analyse und Inhaltsmoderation sowie für Clustering-Aufgaben wie die Nachrichtenaggregation und -empfehlung. Wie <code>jina-embeddings-v4</code> bietet auch dieses Modell LoRA-Adapter, die auf die folgenden Nutzungskategorien spezialisiert sind:</p><ul><li>Asymmetrischer Informationsabruf</li><li>Semantische Ähnlichkeit</li><li>Klassifizierung</li><li>Clustering</li></ul><p><code>jina-embeddings-v3</code> ist ein deutlich kleineres Modell als <code>jina-embeddings-v4</code> mit einer deutlich reduzierten Eingabekontextgröße, aber es ist günstiger in der Nutzung. Dennoch bietet es eine sehr wettbewerbsfähige Leistung, wenn auch nur für Texte, und ist für viele Anwendungsfälle eine bessere Wahl.</p><h3>Jina Code-Einbettungen</h3><p>Die spezialisierten Code-Einbettungsmodelle von Jina – <a href="https://jina.ai/models/jina-code-embeddings-1.5b"><strong>jina-code-embeddings (0.5b und 1.5b)</strong></a> – unterstützen 15 Programmierschemata und Frameworks sowie englischsprachige Texte aus dem Bereich Informatik und Informationstechnologie. Es handelt sich um kompakte Modelle mit einer halben Milliarde (0,5x10⁹) bzw. eineinhalb Milliarden (1,5x10⁹) Parametern. Beide Modelle unterstützen Eingabekontextgrößen von bis zu 32.768 Token und ermöglichen es den Nutzern, ihre Ausgabe-Einbettungsgrößen auszuwählen, von 896 bis 64 Dimensionen für das kleinere Modell und 1536 bis 128 für das größere.</p><p>Diese Modelle unterstützen asymmetrische Abrufe für fünf aufgabenspezifische Spezialisierungen, wobei <a href="https://arxiv.org/abs/2101.00190">Präfixabstimmung</a> statt LoRA-Adapter verwendet wird:</p><ul><li><strong>Code zu Code.</strong> Ähnlichen Code in verschiedenen Programmiersprachen abrufen. Dies wird für die Code-Ausrichtung, Code-Deduplizierung sowie die Unterstützung von Portierung und Refactoring verwendet.</li><li><strong>Natürliche Sprache zu Code.</strong> Abrufen von Code, um Abfragen, Kommentare, Beschreibungen und Dokumentation in natürlicher Sprache abzugleichen.</li><li><strong>Code zu natürlicher Sprache. </strong>Vergleichen des Codes mit der Dokumentation oder anderen Texten in natürlicher Sprache.</li><li><strong>Code-zu-Code-Vervollständigung.</strong> Vorschlag von relevantem Code, um bestehenden Code zu ergänzen oder zu verbessern.</li><li><strong>Technische Fragen und Antworten.</strong> Identifizierung von Antworten in natürlicher Sprache auf Fragen zu Informationstechnologien, ideal geeignet für Anwendungsfälle im technischen Support.</li></ul><p>Diese Modelle bieten eine überlegene Leistung für Aufgaben, die Computerdokumentation und Programmiermaterialien erfordern, bei relativ geringen Rechenkosten. Sie eignen sich hervorragend für die Integration in Entwicklungsumgebungen und Code-Assistenten.</p><h3>Jina ColBERT v2</h3><p><a href="https://jina.ai/models/jina-colbert-v2"><strong>jina-colbert-v2</strong></a> ist ein Multivektor-Texteinbettungsmodell mit 560 Millionen Parametern. Es ist mehrsprachig, mit Materialien in 89 Sprachen trainiert und unterstützt variable Einbettungsgrößen sowie asymmetrischen Informationsabruf.</p><p>Wie bereits erwähnt, sind Multivektor-Einbettungen schlecht für das Indexieren geeignet, aber sehr nützlich, um die Genauigkeit von Ergebnissen anderer Suchstrategien zu erhöhen. <strong>Mit jina-colbert-v2können Sie Multivektor-Einbettungen im Voraus berechnen und sie dann verwenden, um Abrufkandidaten zur Abfragezeit neu zu ordnen.</strong> Dieser Ansatz ist weniger präzise als die Verwendung eines der Reranking-Modelle im nächsten Abschnitt, aber viel effizienter, da er nur den Vergleich gespeicherter Multivektor-Einbettungen beinhaltet, anstatt das gesamte KI-Modell für jede Abfrage und jedes Kandidatenmatch aufzurufen. Es eignet sich ideal für Anwendungsfälle, in denen die Latenz und der Rechenaufwand durch die Nutzung von Reranking-Modellen zu groß sind oder die Anzahl der Kandidaten zum Vergleich zu groß für das Reranking von Modellen ist.</p><p>Dieses Modell gibt eine Folge von Einbettungen aus – eine pro Eingabetoken – und Nutzer können Token-Einbettungen aus 128-, 96- oder 64-dimensionalen Einbettungen auswählen. Kandidaten-Textmatches sind auf 8.192 Token begrenzt. Abfragen werden asymmetrisch kodiert, daher müssen Nutzer angeben, ob ein Text eine Abfrage oder ein Kandidatenmatch ist, und die Abfragen auf 32 Token begrenzen.</p><h3>Jina CLIP v2</h3><p><a href="https://jina.ai/news/jina-clip-v2-multilingual-multimodal-embeddings-for-text-and-images/"><strong>jina-clip-v2</strong></a> ist ein multimodales Einbettungsmodell mit 900 Millionen Parametern, das so trainiert wurde, dass Texte und Bilder Einbettungen erzeugen, die nahe beieinander liegen, wenn der Text den Inhalt des Bildes beschreibt. Es dient in erster Linie zum Abrufen von Bildern auf der Grundlage von Textabfragen, ist aber auch ein leistungsstarkes reines Textmodell, das die Nutzerkosten senkt, da Sie keine separaten Modelle für Text-zu-Text- und Text-zu-Bild-Abfragen benötigen.</p><p>Dieses Modell unterstützt einen Texteingabekontext von 8.192 Tokens, und Bilder werden vor der Einbettung auf 512x512 Pixel skaliert.</p><p>CLIP-Architekturen („Contrastive Language-Image Pretraining“) sind einfach zu trainieren und zu bedienen und können sehr kompakte Modelle erzeugen, aber sie haben einige grundlegende Einschränkungen. Sie können ihr Wissen aus einem Medium nicht nutzen, um ihre Leistung in einem anderen zu verbessern. Sie können nicht ein Medium nutzen, um ihre Leistung in einem anderen zu verbessern. Obwohl das System also wissen mag, dass die Wörter „dog“ und „cat“ in ihrer Bedeutung näher beieinander liegen als jedes von ihnen bei „car“, weiß es nicht unbedingt, dass ein Bild von einem Hund und ein Bild von einer Katze enger miteinander verwandt sind als jedes von ihnen bei einem Bild von einem Auto.</p><p>Sie leiden auch unter der sogenannten <em>Modalitätslücke</em>: Die Einbettung eines Textes über Hunde ist wahrscheinlich näher an der Einbettung eines Textes über Katzen als an der Einbettung eines Bildes von Hunden. Aufgrund dieser Einschränkung empfehlen wir, CLIP entweder als Text-zu-Bild-Abfragemodell oder als reines Textmodell zu verwenden, jedoch nicht beide in einer einzigen Abfrage zu vermischen.</p><h2>Reranking-Modelle</h2><p>Reranking-Modelle nehmen ein oder mehrere Kandidatenmatches zusammen mit einer Abfrage als Eingabe für das Modell und vergleichen sie direkt, wodurch deutlich präzisere Übereinstimmungen entstehen.</p><p>Grundsätzlich könnte man einen Reranker direkt für die Informationsabfrage verwenden, indem man jede Abfrage mit jedem gespeicherten Dokument vergleicht, dies wäre jedoch sehr rechenintensiv und für alle außer den kleinsten Sammlungen unpraktisch. Daher werden Reranker tendenziell zur Bewertung relativ kurzer Listen von Kandidatenmatches verwendet, die mit anderen Mitteln gefunden wurden, wie z. B. durch einbettungsbasierte Suche oder andere Algorithmen für den Informationsabruf. Reranking-Modelle eignen sich ideal für hybride und föderierte Suchverfahren, bei denen eine Suche bedeuten kann, dass Anfragen an separate Suchsysteme mit unterschiedlichen Datensätzen gesendet werden, die jeweils unterschiedliche Ergebnisse liefern. Sie sind sehr gut darin, vielfältige Ergebnisse zu einem einzigen, hochwertigen Ergebnis zu vereinen.</p><p>Die auf Einbettungen basierende Suche kann eine große Herausforderung darstellen, da sie eine Neuindizierung aller gespeicherten Daten erfordert und die Erwartungen der Nutzer an die Ergebnisse verändert. Wenn Sie einen Reranker zu einem bestehenden Suchschema hinzufügen, können Sie viele der Vorteile von KI nutzen, ohne Ihre gesamte Suchlösung neu zu entwickeln.</p><h2>Jina-Reranker-Modelle</h2><h3>Jina Reranker m0</h3><p><a href="https://jina.ai/models/jina-reranker-m0/"><strong>jina-reranker-m0</strong></a> ist ein multimodaler Reranker mit 2,4 Milliarden (2,4x10⁹) Parametern, der Textanfragen und Kandidatenmatches aus Texten und/oder Bildern unterstützt. Es ist das führende Modell für die visuelle Dokumentensuche und somit eine ideale Lösung für Sammlungen von PDFs, Scans von Texten, Screenshots und anderen computergenerierten oder modifizierten Bildern, die Text oder andere semistrukturierte Informationen enthalten, sowie für gemischte Daten, die aus Textdokumenten und Bildern bestehen.</p><p>Dieses Modell nimmt eine einzelne Abfrage und einen Kandidatenmatch entgegen und gibt einen Score zurück. Wenn dieselbe Abfrage mit verschiedenen Kandidaten verwendet wird, sind die Scores vergleichbar und können zur Rangfolge herangezogen werden. Es unterstützt eine gesamte Eingabegröße von bis zu 10.240 Tokens, einschließlich des Anfragetextes und des Kandidatentextes oder -bildes. Jede 28x28 Pixel große Kachel, die zum Abdecken eines Bildes benötigt wird, zählt als Token zur Berechnung der Eingabegröße.</p><h3>Jina Reranker v3</h3><p><a href="https://jina.ai/models/jina-reranker-v3/"><strong>jina-reranker-v3</strong></a> ist ein Text-Reranker mit 600 Millionen Parametern und modernster Leistung für Modelle vergleichbarer Größe. Im Gegensatz zu <code>jina-reranker-m0</code> nimmt er eine einzelne Abfrage und eine Liste von bis zu 64 Kandidatenübereinstimmungen und gibt die Rangfolge zurück. Es hat einen Eingabekontext von 131.000 Token, einschließlich der Abfrage und aller Textkandidaten.</p><h3>Jina Reranker v2</h3><p><a href="https://jina.ai/models/jina-reranker-v2"><strong>jina-reranker-v2-base-multilingual</strong></a> ist ein äußerst kompakter, universeller Reranker mit zusätzlichen Features, die Funktionsaufrufe und SQL-Abfragen unterstützen. Mit weniger als 300 Millionen Parametern bietet er ein schnelles, effizientes und genaues mehrsprachiges Text-Reranking mit zusätzlicher Unterstützung für die Auswahl von SQL-Tabellen und externen Funktionen, die Textabfragen entsprechen, wodurch es sich für agentenbasierte Anwendungsfälle eignet.</p><h2>Kleine generative Sprachmodelle</h2><p>Generative Sprachmodelle sind Modelle wie ChatGPT von OpenAI, Google Gemini und Claude von Anthropic, die Text- oder Multimedia-Eingaben aufnehmen und mit Textausgaben antworten. Es gibt keine klar definierte Grenze, die <em>große</em> Sprachmodelle (LLMs) von <em>kleinen</em> Sprachmodellen (SLMs) unterscheidet, aber die praktischen Probleme bei der Entwicklung, dem Betrieb und der Nutzung von Top-LLMs sind wohlbekannt. Die bekanntesten werden nicht öffentlich verbreitet, daher können wir ihre Größe nur schätzen, aber es wird erwartet, dass ChatGPT, Gemini und Claude im Bereich von 1 bis 3 Billionen (1–3x10¹²) Parametern liegen.</p><p>Das Ausführen dieser Modelle – selbst wenn sie öffentlich verfügbar sind – geht weit über den Umfang herkömmlicher Hardware hinaus und erfordert die fortschrittlichsten Chips, die in riesigen parallelen Arrays angeordnet sind. Der Zugriff auf LLMs ist über kostenpflichtige APIs möglich, dies verursacht jedoch erhebliche Kosten, führt zu einer hohen Latenz und lässt sich nur schwer mit den Anforderungen an Datenschutz, digitale Souveränität und Cloud-Repatriierung vereinbaren. Außerdem können die Kosten für das Training und die Anpassung von Modellen dieser Größe beträchtlich sein.</p><p>Aus diesem Grund wurde viel Forschung in die Entwicklung kleinerer Modelle gesteckt, die vielleicht nicht alle Fähigkeiten der größten LLMs haben, aber bestimmte Aufgaben genauso gut erledigen können, und das zu geringeren Kosten. Unternehmen setzen Software in der Regel ein, um spezifische Probleme zu lösen, und KI-Software bildet da keine Ausnahme. Daher sind SLM-basierte Lösungen oft LLM-basierten Lösungen vorzuziehen. Sie können üblicherweise auf Standardhardware laufen, sind schneller, verbrauchen weniger Energie und lassen sich viel leichter anpassen.</p><p>Das SLM-Angebot von Jina wächst, da wir uns darauf konzentrieren, wie wir KI am besten in praktische Suchlösungen integrieren können.</p><h2>Jina SLMs</h2><h3>ReaderLM v2</h3><p><a href="https://jina.ai/models/ReaderLM-v2"><strong>ReaderLM-v2</strong></a> ist ein generatives Sprachmodell, das HTML gemäß benutzerdefinierten JSON-Schemata und natürlichen Sprachanweisungen in Markdown oder JSON umwandelt.</p><p>Datenvorverarbeitung und -normalisierung sind ein wesentlicher Bestandteil der Entwicklung guter Suchlösungen für digitale Daten, aber reale Daten, insbesondere webbasierte Informationen, sind oft chaotisch, und einfache Umwandlungsstrategien erweisen sich häufig als sehr zerbrechlich. Stattdessen bietet <code>ReaderLM-v2</code> eine intelligente KI-Modelllösung, die das Chaos eines DOM-Tree-Dumps einer Webseite verstehen und nützliche Elemente robust identifizieren kann.</p><p>Mit 1,5 Milliarden (1,5x10⁹) Parametern ist es drei Größenordnungen kompakter als modernste LLMs, leistet aber bei dieser einen engen Aufgabe auf Augenhöhe.</p><h3>Jina VLM</h3><p><a href="https://jina.ai/models/jina-vlm"><strong>jina-vlm</strong></a> ist ein generatives Sprachmodell mit 2,4 Milliarden (2,4x10⁹) Parametern, das darauf trainiert wurde, natürlichsprachliche Fragen zu Bildern zu beantworten. Es bietet eine sehr starke Unterstützung für die visuelle Dokumentenanalyse, d. h. für die Beantwortung von Fragen zu Scans, Screenshots, Folien, Diagrammen und ähnlichen nicht natürlichen Bilddaten.</p><p>Zum Beispiel:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/adfa1420d079ca4fd5582eef4349b1265b378e76-950x500.png" style="width: 100%;" /><p>Es ist auch sehr gut darin, Text in Bildern zu lesen:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ea3956e7ad86f8e171841cab2c28c8b3498da1d4-1002x500.png" style="width: 100%;" /><p>Die wahre Stärke von <code>jina-vlm</code> liegt jedoch im Verständnis des Inhalts von informativen und von Menschen erstellten Bildern:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f68606f9d2d99e2cd616d4ff81db3574dc4e26a5-1020x700.png" style="width: 100%;" /><p>Oder:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/297e85e7e78f296388a02301e1e08fed70827423-1000x500.png" style="width: 100%;" /><p><code>jina-vlm</code> ist gut geeignet für die automatische Generierung von Bildunterschriften, Produktbeschreibungen, Alternativtexten für Bilder und Barrierefreiheitsanwendungen für Sehbehinderte. Es schafft zudem Möglichkeiten für RAG-Systeme („Retrieval-Augmented-Generation“), visuelle Informationen zu verwenden, und für KI-Agenten, Bilder ohne menschliche Unterstützung zu verarbeiten.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Erste Schritte mit Elastic Agent Builder und Strands Agents SDK]]></title>
            <description><![CDATA[Lernen Sie, wie Sie mit Elastic Agent Builder einen Agenten erstellen, und erkunden Sie anschließend, wie Sie den Agenten über das A2A-Protokoll nutzen, das mit dem Strands Agents SDK orchestriert wird.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/agent-builder-a2a-strands-agents-guide</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/agent-builder-a2a-strands-agents-guide</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jonathan Simon]]></dc:creator>
            <pubDate>Mon, 15 Dec 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/31b6a5c1c30dacbb4d5e58d1c566071e7143a0c8-1600x879.gif?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Haben Sie eine Idee für einen KI-Agenten? Wahrscheinlich geht es darum, etwas mit Daten anzufangen, denn wenn ein Agent eine nützliche Aktion auslösen will, muss er eine Entscheidung treffen, und für diese Entscheidung braucht er die richtigen Daten.</p><p>Mit dem Elastic Agent Builder können Sie ganz einfach datenverbundene KI-Agenten erstellen. Wie das geht, zeigen wir Ihnen in diesem Blogbeitrag. Lassen Sie uns alle erforderlichen Schritte durchgehen, um einen Agenten mit einem MCP-Tool zu erstellen, der auf in Elastic gespeicherte Daten zugreift. Dann verwenden wir das Strands Agents SDK und seine A2A-Funktionen (Agent2Agent), um den Agenten zu betreiben. Das <a href="https://strandsagents.com/">Strands Agents SDK</a> ist eine Multiagenten-KI-Entwicklungsplattform, mit der Sie agentenbasierte Anwendungen mit genau dem Code erstellen können, der erforderlich ist, um das gewünschte Ergebnis zu erzielen.</p><p>Lassen Sie uns einen KI-Agenten entwickeln, der das Spiel RPS+ spielt, eine Variante des klassischen Spiels „Schere, Stein, Papier“ mit einer zusätzlichen Wendung: Es bietet den Spieler:innen einige zusätzliche Auswahlmöglichkeiten.</p><h2>Voraussetzungen</h2><p>Folgendes ist erforderlich, um die Schritte in diesem Blogbeitrag zu befolgen:</p><ul><li>Ein Texteditor, der auf Ihrem lokalen Computer ausgeführt wird<ul><li>Für die Beispielanleitungen in diesem Blogbeitrag verwenden wir <a href="https://code.visualstudio.com/download">Visual Studio Code</a>.</li></ul></li><li><a href="https://www.python.org/downloads/">Python 3.10 oder höher</a>, das auf Ihrem lokalen Computer ausgeführt wird</li></ul><h2>Ein Serverless-Projekt erstellen</h2><p>Als Erstes benötigen wir ein Elasticsearch Serverless-Projekt, das den Elastic Agent Builder beinhaltet.</p><p>Gehen Sie zu <a href="http://cloud.elastic.co/">Cloud.elastic.co/de/</a> und erstellen Sie ein neues Elasticsearch Serverless-Projekt.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/31b6a5c1c30dacbb4d5e58d1c566071e7143a0c8-1600x879.gif" style="width: 100%;" /><h2>Einen Index erstellen und Daten hinzufügen</h2><p>Als Nächstes fügen wir einige Daten zu unserem Elasticsearch-Projekt hinzu. Öffnen Sie die Entwicklertools, wo wir Befehle ausführen können, um einen neuen Index zu erstellen und einige Daten darin einzufügen. Wählen Sie im Hauptmenü „Entwicklungstools“ aus.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f97d5af077504463155655a9e27c171a7f974f71-1600x879.jpg" style="width: 100%;" /><p>Kopieren Sie den folgenden PUT-Befehl und fügen Sie ihn in den Anfrage-Eingangsbereich der Konsole des Entwicklungstools ein. Diese Anweisung erstellt einen Elasticsearch-Index mit dem Namen „game-docs“.</p><pre><code>PUT /game-docs
{
  "mappings": {
    "properties": {
      "title": { "type": "text" },
      "content": { 
        "type": "text"
      },
      "filename": { "type": "keyword" },
      "last_modified": { "type": "date" }
    }
  }
}</code></pre><p>Klicken Sie auf die Schaltfläche <strong>„Anfrage senden“</strong>, die in den Entwicklungstools rechts neben der Anweisung angezeigt wird. Sie sollten eine Benachrichtigung sehen, die bestätigt, dass der <em>Game-docs-Index</em> im Reaktion Flächendiagramm der Entwicklungstools erstellt wurde.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/be0555a1930e4d4f58b7ed8b669c9b702532ed17-1600x880.jpg" style="width: 100%;" /><p>Ein Index namens <em>game-docs</em> ist ein großartiger Ort, um die Daten für das Spiel zu speichern, das wir gerade entwickeln. Fügen wir ein Dokument namens <em>rps+-md</em> in diesen Index ein, das alle Daten enthält, die unser Spiel benötigt. Kopieren Sie den folgenden PUT-Befehl und fügen Sie ihn in das Entwicklungstool ein.</p><pre><code>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"
}</code></pre><p>Klicken Sie auf die Schaltfläche <strong>„Anfrage senden“</strong> neben der Anweisung, um sie auszuführen und das <em>rps+-md-Dokument</em> zum game-docs-Index hinzuzufügen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3c01d8a4602de5c33337457591a388a4a4e3fad3-1600x879.jpg" style="width: 100%;" /><p>Wir sollten jetzt einige Daten abfragen können, und mit Agent Builder ist das einfacher als je zuvor.</p><p>Wählen Sie <strong>Agenten</strong> aus dem Hauptnavigationsmenü aus.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/82dbd2e9a439cabd5a5eea3d0ce005b87df0c3ea-1600x879.jpg" style="width: 100%;" /><p>Dann müssen Sie nur noch den standardmäßigen Elastic AI Agent fragen: „Welche Daten habe ich?“</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f7a2f39c9d1486bdf02d9e88a732b540ac2e2cd1-1600x872.gif" style="width: 100%;" /><p>Der Elastic AI Agent bewertet die Daten und liefert eine prägnante Erklärung der vorhandenen Daten.</p><h2>Ein Tool erstellen</h2><p>Okay, wir haben jetzt einige Daten in Elastic. Lassen Sie uns diese nutzen. Der Agent Builder bietet integrierte Unterstützung zur Erstellung von <a href="https://modelcontextprotocol.io/">MCP-Tools</a>, die Agenten helfen, auf die Daten zuzugreifen, die sie benötigen, um den richtigen Kontext für ihre Aufgabe zu haben. Lassen Sie uns ein einfaches Tool erstellen, das unsere Spieldaten abruft.</p><p>Klicken Sie auf das Aktionsmenü des Agent Builders.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0e327c202674dda33bcc0e494d2b588fa8b32e4f-1600x879.png" style="width: 100%;" /><p>Wählen Sie in den Menüoptionen <strong>Alle Werkzeuge anzeigen</strong>aus.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1ebf58650e9fb56750d3f0b1700fab50b44f9bdf-1600x879.png" style="width: 100%;" /><p>Klicken Sie auf <strong>+ Neues Tool.</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6c03a7f28b99ac2d805f34f39948979893316a00-1600x879.png" style="width: 100%;" /><p>Wählen Sie im Formular <strong>Tool erstellen</strong> die <strong><a href="https://www.elastic.co/docs/explore-analyze/query-filter/languages/esql">Option ES|QL</a> aus. Wählen Sie als Werkzeugtyp</strong> die gewünschte Option aus und geben Sie die folgenden Werte ein.</p><p>Für die <strong>Tool-ID</strong>:</p><pre><code>example.get_game_docs</code></pre><p>Für die <strong>Beschreibung</strong>:</p><pre><code>Get RPS+ doc from Elasticsearch game-docs index.</code></pre><p>Für <strong>Konfiguration </strong>geben Sie die folgende Abfrage in das <strong>ES|QL-Abfrage</strong>-Textfeld ein:</p><pre><code>FROM game-docs | WHERE filename == "RPS+.md"</code></pre><p>Ihr ausgefülltes Formular <strong>Tool erstellen</strong> sollte wie folgt aussehen. Klicken Sie auf <strong>Speichern</strong>, um das Tool zu erstellen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b6c93e344600f319b9d2c3030020cf2d171ac1c4-1600x1312.png" style="width: 100%;" /><p>Wir haben ein neues Werkzeug am Werkzeugständer hängen. Werkzeuge sollten nicht einfach nur an einem Regal hängen; sie sollten sinnvoll eingesetzt werden. Lassen Sie uns einen Agenten erstellen, der unser neues benutzerdefiniertes Tool verwenden kann.</p><h2>Einen Agenten erstellen und ihm ein Tool zuweisen</h2><p>Mit dem Agent Builder ist das Erstellen eines Agenten erfreulich einfach. Sie müssen einfach nur die Agentenanweisungen mit ein paar Details eingeben. Lassen Sie uns jetzt einen Agenten erstellen.</p><p>Klicken Sie auf <strong>Agenten verwalten.</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/53934b93db07187e251d4b321cb9ca647e2fd51b-1600x858.png" style="width: 100%;" /><p>Klicken Sie<strong> + Neuer Agent.</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/fae3ad8f31e71a6dfd044e1daa025a4e280b4e68-1600x490.png" style="width: 100%;" /><p>Geben Sie die folgenden Informationen in das Formular <strong>Neuer Agent</strong> ein.</p><p>Geben Sie für die <strong>Agenten-ID</strong> den folgenden Text ein:</p><pre><code>rps_plus_agent</code></pre><p>Geben Sie im Textfeld <strong>Benutzerdefinierte Anweisungen </strong>die folgenden Anweisungen ein:</p><pre><code>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.</code></pre><p>Geben Sie für den <strong>Anzeigenamen </strong>den folgenden Text ein:</p><pre><code>RPS+ Agent</code></pre><p>Für die <strong>Anzeigebeschreibung </strong>geben Sie den untenstehenden Text ein:</p><pre><code>An agent that plays the game RPS+</code></pre><p>Geben Sie dem Agenten das benutzerdefinierte Tool, das wir zuvor erstellt haben, indem Sie auf den Tab <strong>Tools</strong> klicken.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1778f64bc3a1b4004998dc3668ef7f666788e193-1600x1390.png" style="width: 100%;" /><p>Wählen Sie nur das Tool <em>example.get_game_docs</em> aus, das wir zuvor erstellt haben.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7d734cd80161bcc058817482eb330ffcf1cb567b-1600x1363.png" style="width: 100%;" /><p>Klicken Sie auf <strong>Speichern</strong>, um den neuen Agenten zu erstellen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c0014faf605ce50c03679ed0d073bd9f3ae7234d-1600x468.png" style="width: 100%;" /><p>Lassen Sie uns unseren neuen Agenten ausprobieren. Es gibt einen praktischen Link, um einen Chat mit einem beliebigen Agenten aus der Liste der Agenten zu starten.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b7d6943ad90a4f68691207caf66b81742e712145-1600x560.png" style="width: 100%;" /><p>Geben Sie einfach „start game“ ein und das Spiel beginnt. Es funktioniert!</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/984d008e4cc3f08cc1f101720673b0f7347c066c-1600x874.gif" style="width: 100%;" /><p>Sie können sehen, dass der Agent seine Spielobjektauswahl oben in seiner Reaktion anzeigt. Dies ist nützlich, da wir die Wahl des Agenten sehen und bestätigen können, dass das Spiel wie erwartet funktioniert. Wenn man jedoch die Wahl des Gegners kennt, bevor man selbst wählt, wird das Spiel „Schere, Stein, Papier“ nicht besonders unterhaltsam. Um das Spiel zu verfeinern und seine endgültige Form zu erreichen, können wir eine Agenten-Orchestrierungsplattform verwenden, die Agenten mit Code steuern kann.</p><p>Hier kommt das Strands Agents SDK ins Spiel.</p><h2>Strands Agents SDK</h2><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c195bba6ff2754f5d8fda174a0c1d247bc283710-456x156.png" style="width: 100%;" /><p>Wenn Sie neugierig darauf sind, neue Frameworks für die Agentenentwicklung auszuprobieren, dann ist das <a href="https://strandsagents.com/latest/">Strands Agents SDK</a> einen Versuch wert. Das <a href="https://aws.amazon.com/blogs/opensource/introducing-strands-agents-an-open-source-ai-agents-sdk/">Strands Agents SDK wurde von AWS (Mai 2025)</a> als <a href="https://github.com/strands-agents/sdk-python">Open-Source-Python-Implementierung</a> veröffentlicht, und es gibt jetzt auch eine <a href="https://dev.to/aws/strands-agents-now-speaks-typescript-a-side-by-side-guide-12b3">Typescript-Version</a>.</p><h2>Erste Schritte mit dem Strands Agents SDK in Python</h2><p>Starten Sie Ihre Programmiermaschinen. Wir werden jetzt im Eiltempo den Prozess des Klonens und Ausführens einer Beispiel-App durchgehen, die Strands Agents verwendet, um den <em>RPS+ Agent</em> über das A2A-Protokoll zu steuern. Lassen Sie uns eine optimierte Version des RPS+ Spiels entwickeln, bei der die Wahl des Agenten erst nach Ihrer Wahl bekannt gegeben wird, denn schließlich ist es das Rätselraten und der überraschende Ausgang, der Spiele wie „Schere, Stein, Papier“ so unterhaltsam macht.</p><p>Öffnen Sie auf Ihrem lokalen Computer <a href="https://code.visualstudio.com/download">Visual Studio Code</a> und öffnen Sie ein neues Terminal.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2339cc37c89a3524f2b2a21684bc61dae958e1cf-915x460.jpg" style="width: 100%;" /><p>Im neu geöffneten Terminal führen Sie folgenden Befehl aus, um das Elasticsearch Labs-Repository zu klonen:</p><pre><code>git clone https://github.com/elastic/elasticsearch-labs</code></pre><p>Führen Sie den folgenden <em>cd</em>-Befehl aus, um das Verzeichnis in das Verzeichnis „elasticsearch-labs“ zu ändern:</p><pre><code>cd elasticsearch-labs</code></pre><p>Führen Sie anschließend folgenden Befehl aus, um das Repository in Visual Studio Code zu öffnen:</p><pre><code>code .</code></pre><p>Erweitern Sie im Visual Studio File Explorer die Ordner <em>supporting-blog-content</em> und <em>agent-builder-a2a-strands-agents</em> und öffnen Sie anschließend die Datei <em>elastic_agent_builder_a2a_rps+.py.</em> So sieht die Datei in Visual Studio Code aus:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d153b19e0e016c701576edb99ccab5af7c554f34-1484x1530.jpg" style="width: 100%;" /><p>Hier ist der Inhalt der Datei <em>elastic_agent_builder_a2a_rps+.py</em>, den Sie in Ihrem Texteditor sehen sollten:</p><pre><code>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) -> 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())</code></pre><p>Lassen Sie uns einen Blick darauf werfen, was in diesem Code passiert. Ausgehend von der Methode <em><code>main()</code></em> greift der Code zunächst auf die Umgebungsvariablen für die Agenten-URL und den API-Schlüssel zu. Dann verwenden wir diese Werte, um ein <code><em>httpx</em> client</code> zu erstellen, mit dem wir die Agentenkarte für den Agenten abrufen können. Der Client verwendet dann die Agentenkartendaten, um eine „Spiel starten“-Anfrage an den Agenten zu senden. Interessant ist hierbei, dass wir einen <code>random_game_object</code> -Wert als Teil der <code>&quot;start game&quot;</code> -Anfrage einfügen. Dieser Wert ist eine Zufallszahl, die mit dem <em>Zufallsmodul</em> der Standardbibliothek von Python generiert wird. Der Grund dafür ist, dass sich herausgestellt hat, dass die leistungsstarken LLMs (die KI-Agenten ermöglichen) nicht besonders gut in Bezug auf Zufälligkeit sind. Kein Problem, hier kann Python helfen.</p><p>Im weiteren Verlauf des Codes wird, sobald der Agent auf die Anfrage „Spiel starten“ antwortet, die Spielobjektauswahl des Agenten extrahiert und in der Variable <em>agent_choice</em> gespeichert. Der Rest der Reaktion wird dem:der Nutzer:in als Text angezeigt. Anschließend wird der:die Nutzer:in zur Eingabe der eigenen Spielobjektauswahl aufgefordert. Diese Auswahl wird an den Agenten gesendet. Der Code zeigt dann die Auswahl des Spielobjekts durch den Agenten sowie dessen endgültige Entscheidung über den Spielausgang an.</p><h2>Einrichten Ihrer Agenten-URL und Ihres API-Schlüssels als Umgebungsvariablen</h2><p>Da die Beispiel-App auf Ihrem lokalen Computer ausgeführt wird, müssen wir dem Strands Agents SDK eine A2A-URL und einen API-Schlüssel für den Agenten bereitstellen, damit dieser mit unserem Agent Builder-Agenten kommunizieren kann. Die Beispiel-App verwendet eine Datei namens <em>.env</em>, um diese Werte zu speichern.</p><p>Erstellen Sie eine Kopie der Datei <em>env.example</em> und benennen Sie die neue Datei <em>.env.</em></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/25ead5f15a17dedb777132a082097cffb06cae4d-1600x843.jpg" style="width: 100%;" /><p>Wechseln Sie zurück zum Elastic Agent Builder, wo wir die beiden benötigten Werte abrufen können.</p><p>Wählen Sie im Aktionsmenü des Agent Builders oben rechts auf der Seite die Option <strong>Alle Tools anzeigen</strong> aus.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9c4f4e4a3bd76e11e0a182fa007a2f6aec7777b4-1600x880.jpg" style="width: 100%;" /><p>Klicken Sie oben auf der Seite „Tools“ auf das Dropdown-Menü <strong>MCP-Server</strong> und wählen Sie <strong>MCP-Server-URL kopieren.</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6cde0de678bb6f81bef8a59deffb110ad6c6ce26-1600x882.jpg" style="width: 100%;" /><p>Fügen Sie die <strong>MCP-Server-URL</strong> als Ersatz für den <strong>&lt;YOUR-ELASTIC-AGENT-BUILDER-URL&gt;</strong>-Platzhalterwert in die <em>.env</em>-Datei ein. Nun müssen wir eine Aktualisierung an der URL vornehmen, das heißt, den Endtext „mcp“ durch „a2a“ ersetzen, da das <a href="https://a2a-protocol.org/">A2A-Protokoll</a> dasjenige ist, das das Agent Strands SDK zur Kommunikation mit dem in Elastic Agent Builder ausgeführten Agenten verwenden wird.</p><p>Die bearbeitete URL sollte in etwa so aussehen:</p><pre><code>https://rps-game-project-12345a.kb.us-east-1.aws.elastic.cloud/api/agent_builder/a2a</code></pre><p>Der andere Wert, den wir hier in Elastic Cloud brauchen, ist ein API-Schlüssel. Klicken Sie in der oberen Navigationsebene auf <strong>Elasticsearch</strong>.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/651676b9be65178cdad50b5d24f26441c0bf3f97-1600x549.jpg" style="width: 100%;" /><p>Klicken Sie auf die Schaltfläche <strong>API-Schlüssel kopieren</strong>, um den API-Schlüssel zu kopieren.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/17f1e2ed5c7682630c71e75b0b09ffb1d9036210-1600x879.jpg" style="width: 100%;" /><p>Zurück in Visual Studio Code fügen Sie nun den API-Schlüssel in die <em>.env</em>-Datei ein, um den Platzhaltertext <strong>&lt;YOUR-ELASTIC-API-KEY&gt; </strong>zu ersetzen. Ihre <em>.env</em>-Datei sollte etwa so aussehen:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a357947e07f29c8c03382e00c7baedf04a399297-1600x286.jpg" style="width: 100%;" /><h2>Die Beispiel-App ausführen</h2><p>Öffnen Sie ein neues Terminal in Visual Studio Code.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/33e1174c68ea1ed47c7fe62ab6a6da657c606f56-1413x711.jpg" style="width: 100%;" /><p>Führen Sie zunächst den folgenden <em>cd</em>-Befehl im Terminal aus:</p><pre><code>cd elasticsearch-labs/supporting-blog-content/agent-builder-a2a-strands-agents</code></pre><p>Führen Sie den folgenden Befehl aus, um eine virtuelle Python-Umgebung zu erstellen.</p><pre><code>python -m venv .venv</code></pre><p>Führen Sie je nach Betriebssystem Ihres lokalen Computers den folgenden Befehl aus, um die virtuelle Umgebung zu aktivieren.</p><ul><li>macOS/Linux</li></ul><pre><code>source .venv/bin/activate</code></pre><ul><li>Windows</li></ul><pre><code>.venv\Scripts\activate</code></pre><p>Die Beispiel-App verwendet das Strands Agents SDK, und wir sind nun an dem Punkt in dieser Anleitung angelangt, an dem wir es installieren müssen. Führen Sie folgenden Befehl aus, um das Strands Agents SDK zusammen mit allen erforderlichen Python-Bibliotheksabhängigkeiten zu installieren.</p><pre><code>pip install -r requirements.txt</code></pre><p>Es ist Zeit, die Startrampe freizumachen und den Countdown zu starten. Wir sind bereit, diese App zu starten. Zurücktreten. Führen wir sie mit folgendem Befehl aus:</p><pre><code>python elastic_agent_builder_a2a_rps+.py</code></pre><p>Sie sollten mit einer Partie RPS+ herausgefordert werden. Gut gemacht und viel Erfolg!</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/041df81fbf1776f09e1243af0a435c4c0af6aca1-1600x948.gif" style="width: 100%;" /><h2>Erstellen Sie Ihre KI-Apps mit relevantem Kontext</h2><p>Die Entwicklung eines KI-Agenten gehört nun zu Ihren Kernkompetenzen. Und Sie haben gesehen, wie einfach die Verwendung von Elastic Agent Builder-Agenten über A2A in Agenten-Entwicklungs-Frameworks wie dem Strands Agents SDK ist. <a href="https://cloud.elastic.co/registration?utm_source=agentic-ai-category&amp;utm_medium=search-labs&amp;utm_campaign=agent-builder">Testen Sie Elastic,</a> um KI-Agenten zu erstellen, die mit dem relevanten Kontext Ihrer benutzerdefinierten Daten verknüpft sind.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Einführung der Elasticsearch-Unterstützung in der Google MCP Toolbox for Databases]]></title>
            <description><![CDATA[Erfahren Sie, wie die Unterstützung für Elasticsearch jetzt in der Google MCP Toolbox for Databases verfügbar ist, und nutzen Sie ES|QL-Tools, um Ihren Index sicher mit jedem MCP-Client zu integrieren.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/google-mcp-toolbox-elasticsearch-support</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/google-mcp-toolbox-elasticsearch-support</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Enrico Zimuel, Laurent Saint-Félix]]></dc:creator>
            <pubDate>Fri, 12 Dec 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/425a48691f436ed47c9bdfaf5d561ac122b2c472-1062x668.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>In diesem Artikel zeigen wir Ihnen, wie Sie die Google MCP Toolbox mit <a href="https://github.com/elastic/elasticsearch">Elasticsearch</a> nutzen, um ein einfaches Tool zur Extraktion von Informationen aus einem Elasticsearch-Index zu erstellen.</p><p>Wir haben vor Kurzem zum Open-Source-Projekt <a href="https://github.com/googleapis/genai-toolbox">Google MCP Toolbox for Databases</a> beigetragen, indem wir Unterstützung für Elasticsearch als Datenbank hinzugefügt haben.</p><p>Mit diesem neuen Feature können Sie jetzt die Google MCP Toolbox verwenden, um eine Verbindung zu Elasticsearch herzustellen und direkt mit Ihren Daten zu „kommunizieren“.</p><h2>Elasticsearch</h2><p>Wir benötigen eine laufende Elasticsearch-Instanz. Sie können eine kostenlose Testversion auf <a href="https://www.elastic.co/cloud">Elastic Cloud</a> aktivieren oder es lokal mit dem <a href="https://github.com/elastic/start-local">start-local-Skript</a> installieren:</p><pre><code>curl -fsSL https://elastic.co/start-local | sh</code></pre><p>Dadurch werden Elasticsearch und Kibana auf Ihrem Computer installiert und ein API-Schlüssel generiert, der zur Konfiguration der Google MCP Toolbox verwendet wird.</p><p>Der API-Schlüssel wird als Ausgabe des vorherigen Befehls angezeigt und in einer .env-Datei im Ordner elastic-start-local gespeichert.</p><h2>Installieren Sie den Beispieldatensatz</h2><p>Nach der Installation können Sie sich mit dem Benutzernamen <em>elastic</em> und dem vom start-local-Skript generierten Passwort (gespeichert in einer .env-Datei) bei Kibana anmelden.</p><p>Sie können den Datensatz für <strong>E-Commerce-Bestellungen </strong>, der von Kibana verfügbar ist, installieren. Sie enthält einen einzigen Index namens <strong>kibana_sample_data_ecommerce</strong>, der Informationen über 4.675 Bestellungen von einer E-Commerce-Website enthält. Für jede Bestellung haben wir folgende Informationen:</p><ul><li>Kundeninformationen (Name, Ausweis, Geburtsdatum, E-Mail-Adresse usw.)</li><li>Bestelldatum</li><li>Bestell-ID</li><li>Produkte (Liste aller Produkte mit Preis, Menge, ID, Kategorie, Rabatt usw.)</li><li>SKU</li><li>Gesamtpreis (ohne Steuern, mit Steuern)</li><li>Gesamtmenge</li><li>Geoinformationen (Stadt, Land, Kontinent, Ort, Region)</li></ul><p>Um die Beispieldaten zu installieren, öffnen Sie die Seite <strong>Integrationen</strong> in Kibana (suchen Sie in der Suchleiste oben nach „Integration“) und installieren Sie die „Beispieldaten“. Weitere Details finden Sie in der Dokumentation hier: <a href="https://www.elastic.co/docs/explore-analyze/#gs-get-data-into-kibana">https://www.elastic.co/docs/explore-analyze/#gs-get-data-into-kibana</a>.</p><p>Ziel dieses Artikels ist es, zu zeigen, wie einfach es ist, die Google MCP Toolbox so zu konfigurieren, dass sie sich mit Elasticsearch verbindet und mit dem <strong>kibana_sample_data_ecommerce-Index</strong> in natürlicher Sprache interagiert.</p><h2>Google MCP Toolbox</h2><p>Die Google MCP Toolbox ist ein Open-Source-MCP-Server, der entwickelt wurde, um Anwendungen und KI-Agenten die sichere und effiziente Interaktion mit Datenbanken zu erleichtern. Das Projekt, das zuvor unter dem Namen „GenAI Toolbox for Databases“ bekannt war, wurde nach der vollständigen Kompatibilität mit dem <a href="https://www.anthropic.com/news/model-context-protocol">Model Context Protocol</a> (MCP) umbenannt. Ziel ist es, den üblicherweise beim Verbinden von Agenten mit Datenbanken erforderlichen hohen Aufwand zu reduzieren, indem Verbindungspooling, Authentifizierung, Beobachtbarkeit und andere betriebliche Belange im Hintergrund übernommen werden.</p><p>Im Kern ermöglicht es die Toolbox Entwicklern, wiederverwendbare, hochwertige Werkzeuge zu definieren, die Datenbankinteraktionen kapseln. Diese Tools können dann von jedem MCP-kompatiblen Client – wie beispielsweise einem KI-Agenten – aufgerufen werden, ohne dass der Client Low-Level-SQL-Abfragen implementieren oder Datenbankverbindungen verwalten muss. Dieser Ansatz reduziert die Menge an Boilerplate-Code, die für den Aufbau datenbankbewusster Agenten benötigt wird, drastisch und ermöglicht die Integration fortgeschrittener Datenoperationen in nur wenigen Anwendungszeilen. Sobald ein Werkzeug definiert ist, kann es von mehreren Agenten, Frameworks oder Sprachen gemeinsam genutzt werden (Abbildung 1).</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/137cea290bb70ad5da21853f9a6358cef4cf7451-1248x1056.png" style="width: 100%;" /><p>Ein großer Vorteil der Nutzung der Toolbox ist das integrierte Sicherheitsmodell. Authentifizierungsabläufe wie OAuth2 und OIDC werden nativ unterstützt, sodass Entwickler:innen die Verarbeitung oder Speicherung sensibler Datenbankzugangsdaten in Agenten vermeiden können. Die Plattform bietet außerdem Beobachtbarkeit-Features – einschließlich Metriken und Tracing – über OpenTelemetry, was für Debugging, Monitoring und Deployments in der Produktionsumgebung unerlässlich ist. Insgesamt dient die MCP Toolbox als einheitliche, sichere und erweiterbare Schnittstelle zur Interaktion mit Ihren Daten von jedem MCP-fähigen System.</p><h2>So installieren Sie die MCP Toolbox</h2><p>Sie können den MCP-Toolbox-Server unter Linux mit folgendem Befehl installieren:</p><pre><code>export VERSION=0.21.0
curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/linux/amd64/toolbox
chmod +x toolbox</code></pre><p>Für eine Installation unter macOS oder Windows können Sie den <a href="https://googleapis.github.io/genai-toolbox/getting-started/introduction/#installing-the-server">hier</a> beschriebenen Anweisungen folgen.</p><h2>Toolbox für Elasticsearch konfigurieren</h2><p>Um die MCP Toolbox für Elasticsearch zu konfigurieren, müssen wir eine <strong>tools.yaml</strong>-Datei erstellen, und zwar wie folgt:</p><pre><code>sources:
  my-cluster:
    kind: elasticsearch
    addresses:
      - http://localhost:9200
    apikey: <insert-here-api-key>

tools:
  customer-orders:
    kind: elasticsearch-esql
    source: my-cluster
    description: Get the orders made by a customer identified by name.
    query: |
    	FROM kibana_sample_data_ecommerce | WHERE MATCH(customer_full_name, ?name, {"operator": "AND"})
    parameters:
      - name: name
        type: string
        description: The customer name.

toolsets:
  elasticsearch-tools:
    - customer-orders</code></pre><p>Sie müssen den Wert <strong>&lt;insert-here-api-key&gt;</strong> durch einen gültigen Elasticsearch-API-Schlüssel ersetzen. Wenn Sie Elasticsearch lokal mit start-local ausführen, finden Sie den API-Schlüssel in der von start-local generierten .env-Datei unter der Variable <strong>ES_LOCAL_API_KEY</strong>. Wenn Sie Elastic Cloud verwenden, können Sie einen API-Schlüssel generieren, indem Sie das <a href="https://www.elastic.co/docs/deploy-manage/api-keys/elastic-cloud-api-keys">hier</a> beschriebene Verfahren befolgen.</p><p>Die vorherigen Tools enthalten die folgende ES|QL-Abfrage für Elasticsearch:</p><pre><code>FROM kibana_sample_data_ecommerce | WHERE MATCH(customer_full_name, ?name)</code></pre><p>Falls Ihnen ES|QL nicht geläufig ist: Es handelt sich um eine von Elastic entwickelte Abfragesprache, die ähnlich wie SQL funktioniert und mit der man einen oder mehrere Indizes durchsuchen kann. Mehr über ES|QL finden Sie in der offiziellen Dokumentation <a href="https://www.elastic.co/docs/reference/query-languages/esql">hier</a>.</p><p>Die obige Abfrage sucht nach allen im <strong>kibana_sample_data_ecommerce-Index</strong> gespeicherten Bestellungen, die den Namen des:der angegebenen Kund:in enthalten, und verwendet den <strong>?name-Parameter</strong> (das Fragezeichen bezeichnet einen Parameter).</p><p>Der Name des Kunden wird in der vorherigen YAML-Konfiguration mit dem Typ „Zeichenfolge“ und der Beschreibung „Der Name des Kunden“ definiert.</p><p>Mit diesem Tool können Fragen zu den Bestellungen eines:einer Kund:in beantwortet werden – zum Beispiel: <em>Wie viele Bestellungen hat Kund:in Foo im Oktober 2025 aufgegeben?</em></p><p>Die Beschreibungen der Werkzeuge und ihrer Parameter sind unerlässlich, um die relevanten Informationen aus der natürlichsprachlichen Anfrage des:der Nutzer:in zu extrahieren. Diese Extraktion erfolgt mithilfe der <strong>Funktionsaufruf-Funktion</strong> eines Large Language Models (LLM). In der Praxis kann ein LLM bestimmen, welche Funktion (welches Werkzeug) ausgeführt werden muss, um die notwendigen Informationen zu erhalten, sowie die entsprechenden Parameter für diese Funktion.</p><p>Für weitere Informationen zu Funktionsaufrufen empfehlen wir, den Artikel <a href="https://www.elastic.co/search-labs/blog/function-calling-with-elastic">OpenAI Function Calling with Elasticsearch</a> von Ashish Tiwari zu lesen.</p><h2>Führen Sie den Toolbox-Server aus</h2><p>Sie können die MCP Toolbox unter Verwendung der vorherigen tools.yaml-Datei mit folgendem Befehl ausführen:</p><pre><code>./toolbox --tools-file tools.yaml --ui</code></pre><p>Der Parameter<strong> –ui</strong> führt eine Webanwendung unter <a href="http://127.0.0.1:5000/ui">http://127.0.0.1:5000/ui</a> aus (Abbildung 2).</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3caf2339b632bafd5847af1ed8b33b518a25b8a2-1600x314.png" style="width: 100%;" /><p>Sie können die Option <strong>Tools</strong> &gt; <strong>customer-orders</strong> auswählen und im Feld <strong>Parametername</strong> einen Kundennamen eingeben (z. B. Gwen Sanders) und anschließend auf die Schaltfläche <strong>Tool ausführen</strong> klicken. Sie sollten eine JSON-Reaktion sehen, wie in Abbildung 3 dargestellt.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b167e0142afb8919d9cedf6d0fa431d33d0e55f8-1600x933.png" style="width: 100%;" /><p>Die Einrichtung ist abgeschlossen, und die MCP Toolbox kann das Tool <strong>customer-orders</strong> ausführen, um mit Elasticsearch zu kommunizieren und dabei die ES|QL-Anfrage auszuführen.</p><h2>Verwendung der MCP Toolbox mit Gemini CLI</h2><p>Wir können jeden beliebigen MCP-Client verwenden, um mit der MCP Toolbox für Datenbanken zu kommunizieren. Zum Beispiel können wir <a href="https://github.com/google-gemini/gemini-cli">Gemini CLI</a> verwenden, ein Befehlszeilen-Tool zur Nutzung von Gemini. Sie können die Gemini CLI gemäß den <a href="https://geminicli.com/docs/get-started/installation/">hier</a> beschriebenen Anweisungen installieren.</p><p>Gemini CLI bietet eine vorkonfigurierte Erweiterung für die MCP Toolbox an, verfügbar unter <a href="https://github.com/gemini-cli-extensions/mcp-toolbox">gemini-cli-extensions/mcp-toolbox</a>. Sie können diese Erweiterung installieren, indem Sie folgenden Befehl ausführen:</p><pre><code>gemini extensions install https://github.com/gemini-cli-extensions/mcp-toolbox</code></pre><p>Nach der Installation müssen Sie in das Verzeichnis gehen, in dem Sie die Konfigurationsdatei tools.yaml für die MCP Toolbox gespeichert haben, und Gemini CLI wie folgt ausführen (dieser Schritt ist erforderlich, damit die Gemini-CLI automatisch mit der MCP-Toolbox konfiguriert wird):</p><pre><code>gemini</code></pre><p>In Abbildung 4 sollten Sie eine Ausgabeanzeige sehen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0f22df6d3da13c1dc50dcb560414fa7c630eb9a7-1434x341.png" style="width: 100%;" /><p>Mit dem folgenden Befehl können Sie überprüfen, ob die MCP Toolbox verbunden ist:</p><pre><code>/mcp list</code></pre><p>Sie sollten die <strong>mcp_toolbox</strong> mit den aufgelisteten Tools<strong> customer-orders</strong> sehen (Abbildung 5).</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/97edbc40de9e44f469f6f3a09427532be167de0e-493x155.png" style="width: 100%;" /><p>Wenn die MCP Toolbox mit der Gemini CLI verbunden ist, können wir nun versuchen, einige Fragen zu stellen, wie zum Beispiel: „ <em>Gib mir die Bestellungen für die Kundin Gwen Sanders</em>.“ Anschließend fordert die Gemini CLI die Berechtigung an, das Tool customer-orders vom Server mcp_toolbox auszuführen (siehe Abbildung 6).</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c052f3b5e49436903b804280c0065f67ee02444b-1432x284.png" style="width: 100%;" /><p>Nach der Bestätigung führt Gemini CLI die Anfrage an die MCP Toolbox aus und erhält als Ergebnis eine JSON-Reaktion, die zur Formatierung der Reaktion verwendet wird (Abbildung 7).</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7ea5128f1705951c2757af6da4b456d394d4a080-1432x734.png" style="width: 100%;" /><p>Die Reaktion von Gemini CLI wird berichten, dass Gwen Sanders lediglich eine Bestellung über zwei Produkte zum Gesamtpreis von 132 Euro aufgegeben hat.</p><h2>MCP Toolbox SDKs</h2><p>Die Google MCP Toolbox bietet außerdem ein SDK an, um alle Funktionen eines in Go, Python und Javascript geschriebenen Programms abzurufen.</p><p>Das Python SDK ist beispielsweise auf GitHub unter folgender Seite verfügbar: <a href="https://github.com/googleapis/mcp-toolbox-sdk-python">https://github.com/googleapis/mcp-toolbox-sdk-python</a>.</p><p>Wir müssen einen einfachen Agenten erstellen, der mit der MCP Toolbox verbunden werden kann. Wir müssen die folgenden Pakete installieren:</p><pre><code>pip install toolbox-core
pip install google-adk</code></pre><p>Und erstellen Sie ein neues Agentenprojekt mit folgendem Befehl:</p><pre><code>adk create my_agent</code></pre><p>Dadurch wird ein neues Verzeichnis namens <strong>my_agent</strong> mit einer Datei <strong>agent.py</strong> erstellt.</p><p>Aktualisieren Sie <strong>my_agent/agent.py</strong> mit dem folgenden Inhalt, um eine Verbindung zu Toolbox herzustellen:</p><pre><code>from google.adk import Agent
from google.adk.apps import App
from toolbox_core import ToolboxSyncClient

client = ToolboxSyncClient("http://127.0.0.1:5000")

root_agent = Agent(
    name='root_agent',
    model='gemini-2.5-flash',
    instruction="You are a helpful AI assistant designed to search information about a dataset of ecommerce orders.",
    tools=client.load_toolset(),
)

app = App(root_agent=root_agent, name="my_agent")</code></pre><p>Erstellen Sie eine <strong>.env</strong>-Datei mit Ihrem Google API-Schlüssel: </p><pre><code>echo 'GOOGLE_API_KEY="YOUR_API_KEY"' > my_agent/.env</code></pre><p>Schließlich können wir den Agenten ausführen und die Ergebnisse beobachten. Um den Agenten auszuführen, können Sie folgenden Befehl ausführen:</p><pre><code>adk run my_agent</code></pre><p>Alternativ können Sie ihn über eine Webschnittstelle bereitstellen:</p><pre><code>adk web --port 8000</code></pre><p>In beiden Tickets können Sie mit der MCP Toolbox über eine Q&amp;A-Schnittstelle interagieren. Sie können zum Beispiel die vorherige Frage stellen: <em>Geben Sie mir die Bestellungen der Kundin Gwen Sanders</em>.</p><p>Weitere Informationen zu den verschiedenen SDKs finden Sie auf <a href="https://googleapis.github.io/genai-toolbox/sdks/">dieser Dokumentationsseite</a>.</p><h2>Fazit</h2><p>In diesem Artikel haben wir die Elasticsearch-Integration für die Google MCP Toolbox for Databases demonstriert. Mithilfe einer einfachen YAML-Konfigurationsdatei können wir eine Reihe von Tools definieren, die Fragen in natürlicher Sprache mithilfe der ES|QL-Sprache in Elasticsearch-Abfragen übersetzen.</p><p>Wir haben gezeigt, wie man mit dem Datensatz kibana_sample_data_ecommerce interagiert, der Bestellungen von einer Website enthält. Mit dieser Konfigurationsdatei können wir den MCP Toolbox-Server einfach ausführen und uns von jedem MCP-Client aus mit ihm verbinden.</p><p>Abschließend haben wir gezeigt, wie man die Gemini-CLI als Client nutzt, um sich mit der MCP Toolbox for Databases zu verbinden und die in Elasticsearch gespeicherten E-Commerce-Daten abzufragen. Wir haben eine Abfrage in natürlicher Sprache ausgeführt, um Informationen über Bestellungen für eine:n bestimmte:n, namentlich identifizierte:n Kund:in abzurufen.</p><p>Während das MCP-Ökosystem weiter wächst, schafft dieses Muster – leichte Werkzeugdefinitionen mit sicherer, produktionsreifer Infrastruktur – neue Möglichkeiten, immer leistungsfähigere, datenbewusste Agenten mit minimalem Aufwand zu entwickeln. Ob Sie nun lokal mit den Elastic-Beispieldatensätzen experimentieren oder Suchfunktionen in eine größere Anwendung integrieren, die MCP Toolbox bietet eine zuverlässige, erweiterbare Grundlage für die Interaktion mit Ihren Elasticsearch-Daten unter Verwendung natürlicher Sprache.</p><p>Weitere Informationen zur Entwicklung agentischer KI-Anwendungen finden Sie im Artikel <a href="https://search-labs-redesign.vercel.app/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder">Building AI Agentic Workflows with Elasticsearch</a> von Anish Mathur und Dana Juratoni.</p><p>Weitere Informationen über die Google MCP Toolbox finden Sie unter <a href="https://googleapis.github.io/genai-toolbox/getting-started/introduction/">https://googleapis.github.io/genai-toolbox/getting-started/introduction/.</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Bewertung der Relevanz von Suchanfragen mit Bewertungslisten]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie Bewertungslisten erstellen, um die Relevanz von Suchanfragen objektiv zu bewerten und Leistungsmetriken wie den Recall zu verbessern – für skalierbare Suchtests in Elasticsearch.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/judgment-lists-search-query-relevance-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/judgment-lists-search-query-relevance-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jhon Guzmán]]></dc:creator>
            <pubDate>Thu, 11 Dec 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/25478d0ffb228afd5d65d82312998ec1c299c565-700x490.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Entwickler:innen, die an Suchmaschinen arbeiten, stoßen oft auf dasselbe Problem: Das Business-Team ist mit einer bestimmten Suche nicht zufrieden, weil die Dokumente, die es an erster Stelle der Suchergebnisse erwartet, an dritter oder vierter Stelle in der Ergebnisliste erscheinen.</p><p>Bei der Behebung dieses einen Problems werden jedoch versehentlich andere Abfragen beeinträchtigt, da nicht alle Fälle manuell getestet werden konnten. Aber wie können Sie oder Ihr QA-Team testen, ob eine Änderung in einer Abfrage Auswirkungen auf andere Abfragen hat? Noch wichtiger: Wie können Sie sicher sein, dass Ihre Änderungen eine Abfrage tatsächlich verbessert haben?</p><h2>In Richtung einer systematischen Bewertung</h2><p>Hier kommen Bewertungslisten ins Spiel. Anstatt bei jeder Änderung auf manuelle und subjektive Tests angewiesen zu sein, können Sie einen festen Satz von Abfragen festlegen, die für Ihren Anwendungsfall relevant sind, zusammen mit den entsprechenden Ergebnissen.</p><p>Dieser Satz wird zu Ihrer Referenzgrundlage. Bei jeder Änderung, die Sie vornehmen, nutzen Sie diese, um zu bewerten, ob sich Ihre Suche tatsächlich verbessert hat oder nicht.</p><p>Der Wert dieses Ansatzes liegt in Folgendem:</p><ul><li><strong>Beseitigt Unsicherheit</strong>: Sie müssen sich nicht mehr fragen, ob Ihre Änderungen andere Anfragen beeinflussen; die Daten werden es Ihnen mitteilen.</li><li><strong>Stoppt das manuelle Testen</strong>: Sobald die Bewertungssätze aufgezeichnet sind, erfolgt der Test automatisch.</li><li><strong>Unterstützt Veränderungen</strong>: Sie können klare Metriken zeigen, die die Vorteile einer Veränderung untermauern.</li></ul><h2>So beginnen Sie mit dem Erstellen Ihrer Bewertungsliste</h2><p>Eine der einfachsten Möglichkeiten ist die Auswahl einer repräsentativen Abfrage und die manuelle Auswahl der relevanten Dokumente. Zur Erstellung dieser Liste haben Sie zwei Möglichkeiten:</p><ul><li><strong>Binäre Bewertungen:</strong> Jedes Dokument, das mit einer Suchanfrage verknüpft ist, erhält ein <strong>einfaches Tag</strong>: <em>relevant</em> (in der Regel mit einer Punktzahl von „1“) und nicht relevant („0“).</li><li><strong>Abgestufte Bewertungen:</strong> Hier erhält jedes Dokument eine Punktzahl mit unterschiedlichen Stufen. Beispiel: Festlegung einer Skala von 0 bis 4, ähnlich einer <a href="https://en.wikipedia.org/wiki/Likert_scale">Likert-Skala</a>, wobei 0 = „überhaupt nicht relevant” und 4 = „vollkommen relevant” bedeutet, mit Varianten wie „relevant”, „eher relevant” usw.</li></ul><p>Binäre Urteile funktionieren gut, wenn die Suchintention klare Grenzen hat: Sollte dieses Dokument in den Ergebnissen enthalten sein oder nicht?</p><p>Abgestufte Bewertungen sind vor allem bei Grauzonen sinnvoll: Einige Ergebnisse sind besser als andere, sodass Sie „sehr gute“, „gute“ und „nutzlose“ Ergebnisse erhalten und Metriken verwenden können, die die Reihenfolge der Ergebnisse und das Feedback der Benutzer bewerten. Allerdings haben abgestufte Skalen auch Nachteile: Verschiedene Prüfer können die Bewertungsstufen unterschiedlich anwenden, was die Ergebnisse weniger konsistent macht. Und da bei abgestuften Metriken höhere Werte stärker gewichtet werden, kann selbst eine kleine Änderung (z. B. eine Bewertung von 3 statt 4) zu einer viel größeren Verschiebung der Metrik führen, als vom Prüfer beabsichtigt. Diese zusätzliche Subjektivität macht abgestufte Bewertungen im Laufe der Zeit unübersichtlicher und schwieriger zu verwalten.</p><h2>Muss ich die Dokumente selbst klassifizieren?</h2><p>Nicht unbedingt, da es verschiedene Möglichkeiten gibt, Ihre Bewertungsliste zu erstellen, jede mit ihren eigenen Vor- und Nachteilen:</p><ul><li><strong>Explizite Bewertungen:</strong> Hier gehen SMEs jede Anfrage/jedes Dokument durch und entscheiden manuell, ob (oder inwieweit) sie relevant ist. Obwohl dies Qualität und Kontrolle bietet, ist die Skalierbarkeit geringer.</li><li><strong>Implizite Bewertungen:</strong> Mit dieser Methode leiten Sie die relevanten Dokumente auf der Grundlage des tatsächlichen Nutzerverhaltens wie Klicks, Abwanderungsraten und Käufe ab. Mit diesem Ansatz können Sie Daten automatisch erfassen, allerdings könnten die Ergebnisse verzerrt sein. Zum Beispiel klicken Nutzer häufiger auf Top-Ergebnisse, auch wenn sie nicht relevant sind.</li><li><strong>KI-generierte Bewertungen:</strong> Diese letzte Option verwendet Modelle (wie LLMs), um Anfragen und Dokumente automatisch zu bewerten, oft als <a href="https://en.wikipedia.org/wiki/LLM-as-a-Judge">LLM-Jurys</a> bezeichnet. Sie lässt sich schnell und einfach skalieren, doch die Qualität der Daten hängt von der Qualität des verwendeten Modells und davon ab, wie gut die LLM-Trainingsdaten mit Ihren <a href="http://interests.as/">Geschäftsinteressen</a> übereinstimmen. Wie bei menschlichen Bewertungen können auch LLM-Jurys ihre eigenen Vorurteile oder Inkonsistenzen einbringen. Daher ist es wichtig, ihre Ergebnisse anhand einer kleineren Gruppe vertrauenswürdiger Bewertungen zu validieren. LLM-Modelle sind von Natur aus probabilistisch, daher ist es nicht ungewöhnlich, dass ein LLM-Modell dem gleichen Ergebnis unterschiedliche Bewertungen zuweist, unabhängig davon, ob <a href="https://www.ibm.com/think/topics/llm-temperature">der Temperaturparameter</a> auf 0 gesetzt wird.</li></ul><p>Unten finden Sie einige Empfehlungen zur Auswahl der besten Methode für die Erstellung Ihres Bewertungssatzes:</p><ul><li>Entscheiden Sie, wie kritisch einige Features für Sie sind, die nur Nutzer richtig bewerten können (wie Preis, Marke, Sprache, Stil und Produktdetails). Wenn diese kritisch sind, benötigen Sie <strong>explizite Bewertungen</strong> für mindestens einen Teil Ihrer <em>Bewertungsliste</em>.</li><li>Verwenden Sie <strong>implizite Bewertungen</strong>, wenn Ihre Suchmaschine bereits genügend Traffic hat, sodass Sie Metriken zu Klicks, Conversions und Verweildauer nutzen können, um Nutzungstrends zu erkennen. Sie sollten diese dennoch sorgfältig interpretieren und sie mit Ihren expliziten Bewertungssätzen vergleichen, um Verzerrungen zu vermeiden (z. B.: Nutzer neigen dazu, häufiger auf die Ergebnisse mit den höchsten Platzierungen zu klicken, auch wenn Ergebnisse mit niedrigeren Platzierungen relevanter sind)</li></ul><p>Um dieses Problem zu beheben, werden mithilfe von Techniken zur Positionsentzerrung Klickdaten angepasst oder neu gewichtet, um das tatsächliche Interesse der Nutzer besser widerzuspiegeln. Mögliche Ansätze hierfür sind:</p><ul><li><strong>Ergebnisse neu mischen</strong>: Ändern Sie die Reihenfolge der Suchergebnisse für eine Untergruppe von Nutzern, um zu schätzen, wie sich die Position auf die Klicks auswirkt.</li><li>Zu den <strong>Click-Modellen </strong>gehören<strong> </strong><a href="https://wiki.math.uwaterloo.ca/statwiki/index.php?title=a_Dynamic_Bayesian_Network_Click_Model_for_web_search_ranking">Dynamic Bayesian Network <strong>DBN</strong></a>, <a href="https://rsrikant.com/papers/kdd10.pdf">Nutzer Browsing Model <strong>UBM</strong></a>. Diese statistischen Modelle schätzen die Wahrscheinlichkeit, dass ein Klick echtes Interesse widerspiegelt und nicht nur die Position, indem sie Muster wie Scrollverhalten, Verweildauer, Klicksequenz und Rückkehr zur Ergebnisseite verwenden.</li></ul><h2>Beispiel: App zur Filmbewertung</h2><h3>Voraussetzungen</h3><p>Um dieses Beispiel auszuführen, benötigen Sie einen laufenden Elasticsearch 8.x-Cluster, <a href="https://www.elastic.co/downloads/elasticsearch">lokal</a> oder <a href="https://www.elastic.co/cloud/cloud-trial-overview">Elastic Cloud Hosted</a> (gehostet oder serverlos), sowie Zugriff auf die <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis">REST API</a> oder Kibana.</p><p>Stellen Sie sich eine App vor, in der Nutzer ihre Meinungen zu Filmen hochladen und auch nach Filmen suchen können, die sie sich ansehen möchten. Da die Texte von den Nutzern selbst geschrieben werden, können sie Tippfehler und viele Ausdrucksvariationen aufweisen. Daher ist es unerlässlich, dass die Suchmaschine diese Vielfalt interpretieren und den Nutzern hilfreiche Ergebnisse liefern kann.</p><p>Um Abfragen wiederholen zu können, ohne das gesamte Suchverhalten zu beeinträchtigen, hat das Business-Team in Ihrem Unternehmen anhand der häufigsten Suchanfragen die folgenden binären Bewertungssätze erstellt:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Abfrage</td><td style="border: 1px solid #ccc; padding: 4px;">DocID</td><td style="border: 1px solid #ccc; padding: 4px;">Text</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Leistung von DiCaprio</td><td style="border: 1px solid #ccc; padding: 4px;">doc1</td><td style="border: 1px solid #ccc; padding: 4px;">Die Leistung von DiCaprio in The Revenant war atemberaubend.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Leistung von DiCaprio</td><td style="border: 1px solid #ccc; padding: 4px;">doc2</td><td style="border: 1px solid #ccc; padding: 4px;">Inception zeigt Leonardo DiCaprio in einer seiner ikonischsten Rollen.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Leistung von DiCaprio</td><td style="border: 1px solid #ccc; padding: 4px;">doc3</td><td style="border: 1px solid #ccc; padding: 4px;">Brad Pitt liefert in diesem Krimi-Thriller eine solide Leistung ab.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Leistung von DiCaprio</td><td style="border: 1px solid #ccc; padding: 4px;">doc4</td><td style="border: 1px solid #ccc; padding: 4px;">Ein actiongeladenes Abenteuer mit atemberaubenden visuellen Effekten.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Traurige Filme, die einen zum Weinen bringen</td><td style="border: 1px solid #ccc; padding: 4px;">doc5</td><td style="border: 1px solid #ccc; padding: 4px;">Eine herzzerreißende Geschichte über Liebe und Verlust, bei der ich stundenlang geweint habe.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Traurige Filme, die einen zum Weinen bringen</td><td style="border: 1px solid #ccc; padding: 4px;">doc6</td><td style="border: 1px solid #ccc; padding: 4px;">Einer der traurigsten Filme, die je gedreht wurden – Taschentücher bereithalten!</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Traurige Filme, die einen zum Weinen bringen</td><td style="border: 1px solid #ccc; padding: 4px;">doc7</td><td style="border: 1px solid #ccc; padding: 4px;">Eine unbeschwerte Komödie, die Sie zum Lachen bringen wird</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Traurige Filme, die einen zum Weinen bringen</td><td style="border: 1px solid #ccc; padding: 4px;">doc8</td><td style="border: 1px solid #ccc; padding: 4px;">Ein Science-Fiction-Epos voller Action und Spannung.</td>
                    </tr>
              </tbody>
            </table>
          <p>Erstellung des Indexes:</p><pre><code>PUT movies
{
  "mappings": {
    "properties": {
      "text": {
        "type": "text"
      }
    }
  }
}</code></pre><p>BULK-Anfrage:</p><pre><code>POST /movies/_bulk
{ "index": { "_id": "doc1" } }
{ "text": "DiCaprio performance in The Revenant was breathtaking." }
{ "index": { "_id": "doc2" } }
{ "text": "Inception shows Leonardo DiCaprio in one of his most iconic roles." }
{ "index": { "_id": "doc3" } }
{ "text": "Brad Pitt delivers a solid performance in this crime thriller." }
{ "index": { "_id": "doc4" } }
{ "text": "An action-packed adventure with stunning visual effects." }
{ "index": { "_id": "doc5" } }
{ "text": "A heartbreaking story of love and loss that made me cry for hours." }
{ "index": { "_id": "doc6" } }
{ "text": "One of the saddest movies ever made -- bring tissues!" }
{ "index": { "_id": "doc7" } }
{ "text": "A lighthearted comedy that will make you laugh." }
{ "index": { "_id": "doc8" } }
{ "text": "A science-fiction epic full of action and excitement." }</code></pre><p>Nachfolgend finden Sie die Elasticsearch-Abfrage, die die App verwendet:</p><pre><code>GET movies/_search
{
 "query": {
   "match": {
     "text": {
       "query": "DiCaprio performance",
       "minimum_should_match": "100%"
     }
   }
 }
}</code></pre><h3>Von der Bewertung zu den Metriken</h3><p>Für sich genommen liefern Bewertungslisten nicht viele Informationen; sie stellen lediglich eine Erwartung der Ergebnisse unserer Abfragen dar. Ihre wahre Stärke zeigen sie, wenn wir sie zur Berechnung objektiver Metriken zur Messung unserer Suchleistung verwenden.</p><p>Heutzutage umfassen die meisten gängigen Metriken Folgendes:</p><ul><li><strong><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#k-precision">Genauigkeit</a>: </strong>Misst den Anteil der Ergebnisse, die innerhalb aller Suchergebnisse tatsächlich relevant sind.</li><li><strong><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#k-recall">Recall</a>: </strong>Misst den Anteil relevanter Ergebnisse, die die Suchmaschine unter den x Ergebnissen gefunden hat.</li><li><strong><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#_discounted_cumulative_gain_dcg">Discounted Cumulative Gain (DCG):</a> </strong>Misst die Qualität der Rangfolge der Ergebnisse, wobei die relevantesten Ergebnisse ganz oben stehen sollten.</li><li><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#_mean_reciprocal_rank"><strong>Mean Reziprocal Rank (MRR):</strong></a> Misst die Position des ersten relevanten Ergebnisses. Je höher es in der Liste steht, desto höher ist der Score.</li></ul><p>Anhand derselben App zur Bewertung von Filmen berechnen wir die Recall-Metrik, um festzustellen, ob Informationen in unseren Abfragen ausgelassen werden.</p><p>In Elasticsearch können wir die <em>Bewertungslisten</em> nutzen, um Metriken über die <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval">Ranking Evaluation API</a> zu berechnen. Diese API erhält als Eingabe die Bewertungsliste, die Abfrage und die zu bewertende Metrik und gibt einen Wert zurück, der einen Vergleich des Abfrageergebnisses mit der Bewertungsliste darstellt.</p><p>Lassen Sie uns die Ergebnisliste für die beiden vorliegenden Anfragen ausführen:</p><pre><code>POST /movies/_rank_eval
{
 "requests": [
   {
     "id": "dicaprio-performance",
     "request": {
       "query": {
         "match": {
           "text": {
             "query": "DiCaprio performance",
             "minimum_should_match": "100%"
           }
         }
       }
     },
     "ratings": [
       {
         "_index": "movies",
         "_id": "doc1",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc2",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc3",
         "rating": 0
       },
       {
         "_index": "movies",
         "_id": "doc4",
         "rating": 0
       }
     ]
   },
   {
     "id": "sad-movies",
     "request": {
       "query": {
         "match": {
           "text": {
             "query": "sad movies that make you cry",
             "minimum_should_match": "100%"
           }
         }
       }
     },
     "ratings": [
       {
         "_index": "movies",
         "_id": "doc5",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc6",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc7",
         "rating": 0
       },
       {
         "_index": "movies",
         "_id": "doc8",
         "rating": 0
       }
     ]
   }
 ],
 "metric": {
   "recall": {
     "k": 10,
     "relevant_rating_threshold": 1
     }
 }
}</code></pre><p>Wir verwenden zwei Anfragen an _rank_eval: eine für die DiCaprio-Abfrage und eine für traurige Filme. Jede Anfrage enthält eine Fragestellung und die dazugehörige Bewertungsliste (Bewertungen). Wir müssen nicht alle Dokumente bewerten, da diejenigen, die nicht in die Bewertung einbezogen werden, als unbewertet gelten. Für die Berechnungen berücksichtigt Recall nur den „relevanten Satz“, also die Dokumente, die für die Bewertung als relevant gelten.</p><p>In diesem Fall hat die DiCaprio-Abfrage einen Recall von 1, während die traurigen Filme einen Recall von 0 haben. Das bedeutet, dass wir bei der ersten Abfrage alle relevanten Ergebnisse erhalten haben, während wir bei der zweiten Abfrage keine Ergebnisse erhalten haben. Der durchschnittliche Recall beträgt daher 0,5.</p><pre><code>{
 "metric_score": 0.5,
 "details": {
   "dicaprio-performance": {
     "metric_score": 1,
     "unrated_docs": [],
     "hits": [
       {
         "hit": {
           "_index": "movies",
           "_id": "doc1",
           "_score": 2.4826927
         },
         "rating": 1
       },
       {
         "hit": {
           "_index": "movies",
           "_id": "doc2",
           "_score": 2.0780432
         },
         "rating": 1
       }
     ],
     "metric_details": {
       "recall": {
         "relevant_docs_retrieved": 2,
         "relevant_docs": 2
       }
     }
   },
   "sad-movies": {
     "metric_score": 0,
     "unrated_docs": [],
     "hits": [],
     "metric_details": {
       "recall": {
         "relevant_docs_retrieved": 0,
         "relevant_docs": 2
       }
     }
   }
 },
 "failures": {}
}</code></pre><p>Vielleicht sind wir mit dem Parameter <strong>minimum_should_match </strong>zu streng, da wir durch die Forderung, dass 100 % der Wörter in der Suchanfrage in den Dokumenten vorkommen müssen, wahrscheinlich relevante Ergebnisse auslassen. Entfernen wir den Parameter <strong>minimum_should_match</strong>, damit ein Dokument als relevant angesehen wird, wenn nur ein Wort aus der Suchanfrage darin vorkommt.</p><pre><code>POST /movies/_rank_eval
{
 "requests": [
   {
     "id": "dicaprio-performance",
     "request": {
       "query": {
         "match": {
           "text": {
             "query": "DiCaprio performance"
           }
         }
       }
     },
     "ratings": [
       {
         "_index": "movies",
         "_id": "doc1",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc2",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc3",
         "rating": 0
       },
       {
         "_index": "movies",
         "_id": "doc4",
         "rating": 0
       }
     ]
   },
   {
     "id": "sad-movies",
     "request": {
       "query": {
         "match": {
           "text": {
             "query": "sad movies that make you cry"
           }
         }
       }
     },
     "ratings": [
       {
         "_index": "movies",
         "_id": "doc5",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc6",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc7",
         "rating": 0
       },
       {
         "_index": "movies",
         "_id": "doc8",
         "rating": 0
       }
     ]
   }
 ],
 "metric": {
   "recall": {
     "k": 10,
     "relevant_rating_threshold": 1
     }
 }
}</code></pre><p>Wie Sie sehen können, erhalten wir durch Entfernen des Parameters <strong>minimum_should_match</strong> in einer der beiden Abfragen nun in beiden Fällen einen durchschnittlichen Recall von 1.</p><pre><code>{
  "metric_score": 1,
  "details": {
    "dicaprio-performance": {
      "metric_score": 1,
      "unrated_docs": [],
      "hits": [
        {
          "hit": {
            "_index": "movies",
            "_id": "doc1",
            "_score": 2.0661702
          },
          "rating": 1
        },
        {
          "hit": {
            "_index": "movies",
            "_id": "doc3",
            "_score": 0.732218
          },
          "rating": 0
        },
        {
          "hit": {
            "_index": "movies",
            "_id": "doc2",
            "_score": 0.6271719
          },
          "rating": 1
        }
      ],
      "metric_details": {
        "recall": {
          "relevant_docs_retrieved": 2,
          "relevant_docs": 2
        }
      }
    },
    "sad-movies": {
      "metric_score": 1,
      "unrated_docs": [],
      "hits": [
        {
          "hit": {
            "_index": "movies",
            "_id": "doc7",
            "_score": 2.1307156
          },
          "rating": 0
        },
        {
          "hit": {
            "_index": "movies",
            "_id": "doc5",
            "_score": 1.3160692
          },
          "rating": 1
        },
        {
          "hit": {
            "_index": "movies",
            "_id": "doc6",
            "_score": 1.190063
          },
          "rating": 1
        }
      ],
      "metric_details": {
        "recall": {
          "relevant_docs_retrieved": 2,
          "relevant_docs": 2
        }
      }
    }
  },
  "failures": {}
}</code></pre><p>Zusammenfassend lässt sich sagen, dass durch das Entfernen der Klausel „minimum_should_match: 100%“ eine perfekte Trefferquote für beide Abfragen erzielt werden kann.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/24d055da4348c63827ba7046fe8cafb6f47cadd8-546x628.png" style="width: 100%;" /><p>Wir haben es geschafft! Richtig?</p><p>Nicht so schnell!</p><p>Durch die Verbesserung des Recalls öffnen wir die Tür zu einer größeren Bandbreite an Ergebnissen. Jede Anpassung impliziert jedoch einen Kompromiss. Deshalb ist es wichtig, vollständige Testfälle festzulegen und verschiedene Metriken zur Bewertung von Änderungen zu verwenden.</p><p>Die Verwendung von Bewertungslisten und Metriken verhindert, dass Sie Änderungen blind vornehmen, da Sie nun über Daten verfügen, die diese stützen. Die Validierung erfolgt nicht mehr manuell und wiederholt, und Sie können Ihre Änderungen in mehr als nur einem Anwendungsfall testen. Darüber hinaus können Sie mit A/B-Tests live testen, welche Konfiguration für Ihre Nutzer und Ihren Anwendungsfall am besten geeignet ist, wodurch sich der Kreis von technischen Metriken und realen Metriken schließt.</p><h2>Abschließende Empfehlungen zur Verwendung von Bewertungslisten</h2><p>Bei der Arbeit mit Bewertungslisten geht es nicht nur um das Messen, sondern auch darum, einen Rahmen zu schaffen, der es Ihnen ermöglicht, mit Zuversicht zu iterieren. Zu diesem Zweck beachten Sie folgende Empfehlungen:</p><ol><li><strong>Fangen Sie klein an, aber fangen Sie an</strong>. Sie benötigen keine 10.000 Anfragen mit jeweils 50 Bewertungslisten. Sie müssen lediglich die 5 bis 10 wichtigsten Suchanfragen für Ihren Anwendungsfall identifizieren und festlegen, welche Dokumente Ihrer Meinung nach ganz oben in den Ergebnissen erscheinen sollten. Damit haben Sie bereits eine Grundlage. Sie möchten in der Regel mit den Top-Suchanfragen sowie den Suchanfragen ohne Ergebnisse beginnen. Sie können auch mit einer einfach zu konfigurierenden Metrik wie Genauigkeit beginnen und sich dann in der Komplexität steigern.</li><li><strong>Validieren Sie mit Nutzern.</strong> Ergänzen Sie die Zahlen durch A/B-Tests in der Produktion. Auf diese Weise können Sie feststellen, ob Änderungen, die in den Metriken gut aussehen, auch tatsächlich Auswirkungen haben.</li><li><strong>Führen Sie die Liste weiter.</strong> Ihr Anwendungsfall wird sich weiterentwickeln. Und damit auch Ihre kritischen Fragen. Aktualisieren Sie Ihre Bewertung regelmäßig, um neue Anforderungen zu berücksichtigen.</li><li><strong>Integrieren Sie sie in Ihren Workflow.</strong> Integrieren Sie Bewertungslisten in Ihre Entwicklungs-Pipelines. Stellen Sie sicher, dass jede Konfigurationsänderung, jedes Synonym und jede Textanalyse automatisch mit Ihrer Basisliste abgeglichen wird.</li><li><strong>Verbinden Sie technisches Wissen mit Strategie.</strong> Beschränken Sie sich nicht auf die Messung technischer Metriken wie Genauigkeit oder Recall. Nutzen Sie Ihre Bewertungsergebnisse, um die Geschäftsergebnisse zu verbessern.</li></ol>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[So erstellen Sie mit LangGraph.js und Elasticsearch einen KI-Suchworkflow für das Finanzwesen]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie LangGraph.js mit Elasticsearch verwenden können, um einen KI-gestützten Finanz-Suchworkflow zu erstellen, der Abfragen in natürlicher Sprache in dynamische, bedingte Filter für Investitions- und Marktanalysen umwandelt.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/ai-agent-workflow-finance-langgraph-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/ai-agent-workflow-finance-langgraph-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
            <pubDate>Fri, 05 Dec 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/12b6057d84c6356267cd178a3c6c1a5c61123ece-2000x1256.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Beim Erstellen von KI-Suchanwendungen müssen häufig mehrere Aufgaben, der Datenabruf und die Datenextraktion zu einem nahtlosen Workflow koordiniert werden. LangGraph vereinfacht diesen Prozess, indem es Entwicklern ermöglicht, KI-Agenten mithilfe einer node-basierten Struktur zu orchestrieren. In diesem Artikel werden wir eine Finanzlösung mit <a href="https://langchain-ai.github.io/langgraphjs/">LangGraph.js</a> erstellen.</p><h2>Was ist LangGraph?</h2><p><a href="https://langchain-ai.github.io/langgraphjs/">LangGraph</a> ist ein Framework zum Erstellen von KI-Agenten und deren Orchestrierung in einem Workflow, um KI-unterstützte Anwendungen zu entwickeln. LangGraph verfügt über eine Knotenarchitektur, in der wir Funktionen deklarieren können, die Aufgaben darstellen, und diese als Knoten des Workflows zuweisen können. Das Ergebnis der Interaktion mehrerer Knoten ist ein Graph. LangGraph ist Teil des umfassenderen <a href="https://js.langchain.com/docs/introduction/">LangChain-Ökosystems</a>, das Tools für die Erstellung modularer und zusammensetzbarer KI-Systeme bereitstellt.</p><p>Zur Veranschaulichung dessen, warum LangGraph nützlich ist, werden wir eine problematische Situation damit lösen.</p><h2>Überblick über die Lösung</h2><p>In einem Risikokapitalunternehmen haben Investoren Zugriff auf eine umfangreiche Datenbank mit zahlreichen Filteroptionen, aber wenn man Kriterien kombinieren möchte, wird es schwierig und langsam. Dies kann dazu führen, dass einige relevante Start-ups für Investitionen nicht entdeckt werden. Das Ergebnis: Man verbringt viele Stunden damit, die besten Kandidaten zu identifizieren, oder verpasst sogar Chancen.</p><p>Mit LangGraph und Elasticsearch können wir gefilterte Suchen in natürlicher Sprache durchführen, sodass Nutzer komplexe Anfragen mit Dutzenden von Filtern nicht manuell erstellen müssen. Um die Flexibilität zu erhöhen, entscheidet der Workflow anhand der Nutzereingaben automatisch zwischen zwei Abfragetypen.</p><ul><li><strong>Investitionsorientierte Anfragen</strong>: Diese zielen auf finanzielle und finanzierungsbezogene Aspekte von Start-ups ab, wie <a href="https://www.investopedia.com/articles/personal-finance/102015/series-b-c-funding-what-it-all-means-and-how-it-works.asp">Finanzierungsrunden</a>, Bewertung oder <a href="https://www.investopedia.com/terms/r/revenue.asp">Umsatz</a>. <em>Beispiel:</em> „Suche Start-ups mit einer Series-A- oder Series-B-Finanzierung zwischen 8 und 25 Mio. Dollar und einem monatlichen Umsatz von über 500.000 Dollar.“</li><li><strong>Marktorientierte Anfragen</strong>: Diese konzentrieren sich auf <a href="https://en.wikipedia.org/wiki/Vertical_market">Branchensegmente</a>, <a href="https://en.wikipedia.org/wiki/Target_market">geografische Märkte</a> oder <a href="https://www.investopedia.com/terms/b/businessmodel.asp">Geschäftsmodelle</a> und helfen dabei, Chancen in bestimmten Sektoren oder Regionen zu identifizieren. <em>Beispiel:</em> „Suche Fintech- und Healthcare-Start-ups in San Francisco, New York oder Boston.“</li></ul><p>Um die Abfragen robust zu halten, werden wir das LLM dazu bringen, <a href="https://www.elastic.co/docs/solutions/search/search-templates">Suchvorlagen</a> anstelle vollständiger <a href="https://www.elastic.co/docs/explore-analyze/query-filter/languages/querydsl">DSL-Abfragen</a> zu erstellen. Auf diese Weise erhalten Sie immer die gewünschte Abfrage, und der LLM muss lediglich die Lücken ausfüllen und trägt nicht die Verantwortung, die benötigte Abfrage jedes Mal neu zu erstellen.</p><h2>Was Sie brauchen, um loszulegen</h2><ul><li>Elasticsearch API-Schlüssel</li><li>OpenAPI-API-Schlüssel</li><li>Node 18 oder neuer</li></ul><h2>Schritt-für-Schritt-Anweisungen</h2><p>In diesem Abschnitt schauen wir uns an, wie die App aussehen wird. Dafür verwenden wir <a href="https://www.typescriptlang.org/">TypeScript</a>, ein Superset von JavaScript, das statische Typen hinzufügt, um den Code zuverlässiger, leichter zu pflegen und sicherer zu machen, indem Fehler frühzeitig erkannt werden, während er vollständig kompatibel mit bestehendem JavaScript bleibt.</p><p>Der Knotenfluss sieht wie folgt aus:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b47d7f238c4964a6febc0de7fe5e68b186f539c3-363x555.png" style="width: 100%;" /><p>Das obige Bild wird von LangGraph generiert und stellt den Workflow dar, der die Ausführungsreihenfolge und die bedingte Logik zwischen den Knoten definiert:</p><ul><li><strong>decideStrategy: </strong>Verwendet ein LLM, um die Anfrage des Nutzers zu analysieren und zwischen zwei spezialisierten Suchstrategien zu entscheiden – investitionsorientiert oder marktorientiert.</li><li><strong>prepareInvestmentSearch: </strong>Extrahiert Filterwerte aus der Abfrage und erstellt eine vordefinierte Vorlage, die finanzielle und finanzierungsbezogene Parameter hervorhebt.</li><li><strong>prepareMarketSearch</strong>: Extrahiert ebenfalls Filterwerte, baut jedoch dynamisch Parameter auf, die den Markt-, Branchen- und geografischen Kontext betonen.</li><li><strong>executeSearch: </strong>Sendet die konstruierte Abfrage mit einer Suchvorlage an Elasticsearch und ruft die entsprechenden Start-up-Dokumente ab.</li><li><strong>VisualizeResults: </strong>Formatiert die Endergebnisse in einer klaren, lesbaren Zusammenfassung, die die wichtigsten Startup-Attribute wie Finanzierung, Branche und Umsatz aufzeigt.</li></ul><p>Dieser Fluss umfasst eine <a href="https://langchain-ai.github.io/langgraphjs/how-tos/branching/?h=conditional#how-to-create-branches-for-parallel-node-execution">bedingte Verzweigung</a>, die als „if“-Anweisung fungiert und basierend auf der Eingabe des Nutzers bestimmt, ob der Investitions- oder Marktsuchpfad verwendet wird. Diese vom LLM gesteuerte Entscheidungslogik macht den Workflow adaptiv und kontextsensitiv – ein Mechanismus, den wir in den nächsten Abschnitten genauer untersuchen werden.</p><h3>LangGraph-Status</h3><p>Bevor wir jeden Knoten einzeln betrachten, müssen wir verstehen, wie die Knoten kommunizieren und Daten austauschen. Dafür ermöglicht uns LangGraph, den Workflow-Status zu definieren. Dies definiert den gemeinsamen Status, der zwischen Knoten weitergegeben wird.</p><p>Der Zustand fungiert als gemeinsamer Container, der während des gesamten Workflows Zwischendaten speichert: Er beginnt mit der natürlichsprachlichen Anfrage des Nutzers, speichert dann die ausgewählte Suchstrategie, die vorbereiteten Parameter für Elasticsearch, die abgerufenen Suchergebnisse und schließlich den formatierten Ausgang.</p><p>Diese Struktur ermöglicht es jedem Knoten, den Status zu lesen und zu aktualisieren, wodurch ein konsistenter Informationsfluss vom Nutzer-Eingang bis zur endgültigen Visualisierung gewährleistet wird.</p><pre><code>const VCState = Annotation.Root({
  input: Annotation<string>(), // User's natural language query
  searchStrategy: Annotation<string>(), // Search strategy chosen by LLM
  searchParams: Annotation<any>(), // Prepared search parameters
  results: Annotation<any[]>(), // Search results
  final: Annotation<string>(), // Final formatted response
});</code></pre><h3>Einrichten der Anwendung</h3><p>Der gesamte Code in diesem Abschnitt ist im <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch">elasticsearch-labs-Repository</a> zu finden.</p><p>Öffnen Sie ein Terminal in dem Ordner, in dem sich die App befindet, und initialisieren Sie eine Node.js-Anwendung mit folgendem Befehl:</p><pre><code>npm init -y</code></pre><p>Nun können wir die notwendigen Abhängigkeiten für dieses Projekt installieren:</p><pre><code>npm install @elastic/elasticsearch @langchain/langgraph @langchain/openai @langchain/core dotenv zod && npm install --save-dev @types/node tsx typescript</code></pre><ul><li><strong><code>@elastic/elasticsearch</code></strong>: Hilft uns bei der Bearbeitung von Elasticsearch-Anfragen, z. B. bei der Daten-Ingestion und dem Abrufen von Daten.</li><li><strong><code>@langchain/langgraph</code></strong>: JS-Abhängigkeit zur Bereitstellung aller LangGraph-Tools.</li><li><strong><code>@langchain/openai</code></strong>: LLM-Client von OpenAI für LangChain.</li><li>@langchain/core: Bietet die grundlegenden Bausteine für LangChain-Apps, einschließlich Prompt-Vorlagen.</li><li><strong><code>dotenv</code></strong>: Notwendige Abhängigkeit zur Verwendung von Umgebungsvariablen in JavaScript.</li><li><strong><code>zod</code></strong>: Abhängigkeit von Typdaten.</li></ul><p><code>@types/node</code> <code>tsx</code> <code>typescript</code> ermöglicht es uns, TypeScript-Code zu schreiben und auszuführen.</p><p>Erstellen Sie nun die folgenden Dateien:</p><ul><li><code>elasticsearchSetup<a href="http://ingest.ts/">.ts</a></code>: Erstellt die Index-Mappings, lädt die Daten aus einer JSON-Datei und führt den Ingest der Daten in Elasticsearch durch.</li><li><a href="http://main.ts/"><code>main.ts</code></a>: Wird die LangGraph-Anwendung enthalten.</li><li><code>.env</code>Datei zum Speichern der Umgebungsvariablen</li></ul><p>In der <code>.env</code>-Datei fügen wir die folgenden Umgebungsvariablen hinzu:</p><pre><code>ELASTICSEARCH_ENDPOINT="your-endpoint-here"
ELASTICSEARCH_API_KEY="your-key-here"
OPENAI_API_KEY="your-key-here"</code></pre><p>Der OpenAPI-APIKey wird nicht direkt im Code verwendet; stattdessen wird er intern von der Bibliothek <code>@langchain/openai</code> verwendet.</p><p>Die gesamte Logik bezüglich der Erstellung von Mappings, der Erstellung von Suchvorlagen und der Datensatz-Ingestion kann in der Datei <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/elasticsearchSetup.ts"><code>elasticsearchSetup.ts</code></a> gefunden werden. In den nächsten Schritten konzentrieren wir uns auf die <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/main.ts"><code>main.ts</code></a>-Datei. Sie können den Datensatz auch überprüfen, um besser zu verstehen, wie die Daten in <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/dataset.json"><code>dataset.json</code></a> aussehen.</p><h3>LangGraph-App</h3><p>In der <code>main.ts</code>-Datei importieren wir einige notwendige Abhängigkeiten, um die LangGraph-Anwendung zu konsolidieren. In dieser Datei müssen Sie auch die Knotenfunktionen und die Zustandsdeklaration angeben. Die Graphdeklaration erfolgt in den nächsten Schritten anhand einer<code>main</code>-Methode. Die <code>elasticsearchSetup.ts</code>-Datei wird Elasticsearch-Helfer enthalten, die wir in den nächsten Schritten in den Knoten verwenden werden.</p><pre><code>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" });</code></pre><p>Wie bereits erwähnt, wird der LLM-Client verwendet, um die Elasticsearch-Suchvorlagenparameter basierend auf der Frage des Nutzers zu generieren.</p><pre><code>async function saveGraphImage(app: any): Promise<void> {
  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);
  }
}</code></pre><p>Die oben dargelegte Methode erzeugt das Graphbild im PNG-Format und verwendet hinter den Kulissen die <a href="https://mermaid.ink/">Mermaid.INK-API</a>. Dies ist nützlich, wenn Sie sehen möchten, wie die App-Knoten mit einer gestylten Visualisierung zusammenwirken.</p><h3>LangGraph-Knoten</h3><p>Sehen wir uns nun die einzelnen Knoten im Detail an:</p><h3>decideSearchStrategy-Knoten</h3><p>Der <code>decideSearchStrategy</code>-Knoten analysiert die Eingabe und entscheidet, ob eine investitions- oder marktorientierte Suche durchgeführt werden soll. Er verwendet ein LLM mit einem strukturierten Ausgang (definiert mit Zod), um den Abfragetyp zu klassifizieren. Bevor die Entscheidung getroffen wird, werden mithilfe einer Aggregation die verfügbaren Filter aus dem Index abgerufen, um sicherzustellen, dass das Modell über einen aktuellen Kontext zu Branchen, Standorten und Finanzierungsdaten verfügt.</p><p>Um die möglichen Filterwerte zu extrahieren und an das LLM zu senden, verwenden wir eine <a href="https://www.elastic.co/docs/explore-analyze/query-filter/aggregations">Aggregation</a>, um sie direkt aus dem Elasticsearch-Index abzurufen. Diese Logik wird in einer Methode namens <code>getAvailableFilters</code>zugeordnet:</p><pre><code>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 {};
  }
}</code></pre><p>Mit der obigen Aggregationsanfrage erhalten wir die folgenden Ergebnisse:</p><pre><code>{
  "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
  }
}</code></pre><p>Alle Ergebnisse finden Sie <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/responses/aggregationsResponse.json">hier</a>.</p><p>Für beide Strategien verwenden wir eine hybride Suche, um sowohl den strukturierten Teil der Frage (Filter) als auch die subjektiveren Teile (Semantik) zu erkennen. Hier ist ein Beispiel für beide Abfragen unter Verwendung von <a href="https://www.elastic.co/docs/solutions/search/search-templates">Suchvorlagen</a>:</p><pre><code>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
            }
          }
        }`,
      },
    });</code></pre><p>Sehen Sie sich die Abfragen an, die in der<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/elasticsearchSetup.ts#L119"><code>elasticsearchSetup.ts</code></a>-Datei detailliert beschrieben sind. Im folgenden Knoten wird entschieden, welche der beiden Abfragen verwendet wird:</p><pre><code>// 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",
    };
  }
}</code></pre><h3>prepareInvestmentSearch- und prepareMarketSearch-Knoten</h3><p>Beide Knoten verwenden eine gemeinsame Hilfsfunktion, <code>extractFilterValues</code>, die das LLM nutzt, um relevante Filter zu identifizieren, die in den Nutzereingaben erwähnt werden, wie z. B. Branche, Standort, Finanzierungsphase, Geschäftsmodell usw. Wir verwenden dieses Schema, um unsere <a href="https://www.elastic.co/docs/solutions/search/search-templates">Suchvorlage</a> zu erstellen.</p><pre><code>// 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);
}</code></pre><p>Je nach erkannter Absicht wählt der Workflow einen von zwei Pfaden:</p><p><strong>prepareInvestmentSearch:</strong> Erstellt finanzorientierte Suchparameter, einschließlich Finanzierungsphase, Finanzierungsbetrag, Investor und Erneuerungsinformationen. Die gesamte Abfragevorlage finden Sie in der <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/elasticsearchSetup.ts"><code>elasticsearchSetup.ts</code></a>-Datei:</p><pre><code>// 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: {},
    };
  }
}</code></pre><p><strong>prepareMarketSearch:</strong> erstellt marktorientierte Parameter, die sich auf Branchen, Geografien und Geschäftsmodelle konzentrieren. Die vollständige Abfrage finden Sie in der Datei <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/elasticsearchSetup.ts"><code>elasticsearchSetup.ts</code></a>:</p><pre><code>// 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 {};
  }
}</code></pre><h3>executeSearch-Knoten</h3><p>Dieser Knoten nimmt die gesuchten Parameter aus dem Zustand und sendet sie zuerst an Elasticsearch, wobei er die <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-render-search-template">_render-API</a> verwendet, um die Abfrage für Debugging-Zwecke zu visualisieren, und sendet dann eine Anfrage zur Abrufung der Ergebnisse.</p><pre><code>// 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) => hit._source),
    };
  } catch (error: any) {
    console.error(`❌ ${state.searchParams.search_type} search error:`, error);
    return { results: [] };
  }
}</code></pre><h3>visualizeResults-Knoten</h3><p>Dieser Knoten zeigt schließlich die Elasticsearch-Ergebnisse an.</p><pre><code>// 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) => {
    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,
  };
}</code></pre><p>Programmatisch sieht der gesamte Graph so aus:</p><pre><code>  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) => state.searchStrategy, // Conditional function
      {
        investment_focused: "prepareInvestment", // If investment focused -> RRF template preparation
        market_focused: "prepareMarket", // If market focused -> dynamic query preparation
      }
    )
    .addEdge("prepareInvestment", "executeSearch") // Investment prep -> execute
    .addEdge("prepareMarket", "executeSearch") // Market prep -> execute
    .addEdge("executeSearch", "visualizeResults") // Execute -> visualize
    .addEdge("visualizeResults", END); // End workflow</code></pre><p>Wie Sie sehen können, haben wir eine bedingte Kante, bei der die App entscheidet, welcher „Pfad“ oder Knoten als Nächstes ausführen wird. Dieses Feature ist nützlich, wenn Workflows Verzweigungslogik benötigen, etwa die Wahl zwischen mehreren Tools oder das Einfügen eines Human-in-the-Loop-Schrittes.</p><p>Nachdem wir die Kern-Features von LangGraph verstanden haben, können wir die Anwendung einrichten, in der der Code ausgeführt werden soll:</p><p>Alles wird in einer <code>main</code>-Methode zusammengefasst. Hier deklarieren wir den Graphen mit allen Elementen unter der Variablen „Workflow“:</p><pre><code>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) => state.searchStrategy, // Conditional function
      {
        investment_focused: "prepareInvestment", // If investment focused -> RRF template preparation
        market_focused: "prepareMarket", // If market focused -> dynamic query preparation
      }
    )
    .addEdge("prepareInvestment", "executeSearch") // Investment prep -> execute
    .addEdge("prepareMarket", "executeSearch") // Market prep -> execute
    .addEdge("executeSearch", "visualizeResults") // Execute -> 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);
}</code></pre><p>Die Abfragevariable simuliert die in einer hypothetischen Suchleiste eingegebenen Nutzereingaben:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e8d76909eb2bc1bb62f3ca9a8b3e4b85fcec2893-1600x164.png" style="width: 100%;" /><p>Aus der natürlichsprachlichen Formulierung „Suche Start-ups mit Series-A- oder Series-B-Finanzierung zwischen 8 und 25 Millionen US-Dollar und einem monatlichen Umsatz von über 500.000 US-Dollar“ werden alle Filter extrahiert.</p><p>Rufen Sie abschließend die Hauptmethode auf:</p><pre><code>main().catch(console.error);</code></pre><h3>Ergebnisse</h3><pre><code>🔍 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.</code></pre><p>Für die gesendete Eingabe wählt die Anwendung den <strong>investitionsorientierten</strong> Pfad, wodurch wir die Elasticsearch-Abfrage sehen, die vom Workflow generiert wird und die Werte und Bereiche aus dem Eingang des Nutzers extrahiert. Wir können auch die an Elasticsearch gesendete Anfrage mit den extrahierten Werten sehen, und schließlich die vom <code>visualizeResults</code>-Knoten formatierten Ergebnisse.</p><p>Testen wir nun den <strong>marktorientierten</strong> Knoten mit der Abfrage „Suche Fintech- und Healthcare-Start-ups in San Francisco, New York oder Boston.“:</p><pre><code>...

🔍 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.</code></pre><h2>Erkenntnisse</h2><p>Während des Schreibprozesses habe ich Folgendes gelernt:</p><ul><li>Wir müssen dem LLM die exakten Werte der Filter zeigen, sonst sind wir darauf angewiesen, dass der Nutzer die präzisen Werte eingibt. Bei niedriger Kardinalität ist dieser Ansatz akzeptabel, aber wenn die Kardinalität hoch ist, benötigen wir einen Mechanismus zum Herausfiltern der Ergebnisse.</li><li>Die Verwendung von Suchvorlagen führt zu deutlich konsistenteren Ergebnissen als die automatische Generierung der Elasticsearch-Abfrage durch das LLM und ist zudem schneller.</li><li>Bedingte Kanten sind ein leistungsstarker Mechanismus, um Anwendungen mit mehreren Varianten und verzweigten Pfaden zu erstellen.</li><li>Strukturierte Ausgaben sind bei der Informationsgenerierung mit LLMs äußerst nützlich, da sie vorhersehbare, typsichere Antworten erzwingen. Dies verbessert die Zuverlässigkeit und reduziert Fehlinterpretationen von Prompts.</li></ul><p>Die Kombination von semantischem und strukturiertem Suchen durch hybriden Abruf führt zu besseren und relevanteren Ergebnissen, wobei Präzision und Kontextverständnis in Einklang gebracht werden.</p><h2>Fazit</h2><p>In diesem Beispiel kombinieren wir LangGraph.js mit Elasticsearch, um einen dynamischen Workflow zu schaffen, der natürliche Sprachanfragen interpretieren und zwischen finanz- oder marktorientierten Suchstrategien entscheiden kann. Dieser Ansatz reduziert die Komplexität manueller Abfragen und verbessert gleichzeitig die Flexibilität und Genauigkeit für Risikokapitalanalysten.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Verbesserung der Interaktivität des Kibana-Dashboards mit Steuerelementen für Variablen]]></title>
            <description><![CDATA[Entdecken Sie, wie Sie mit Steuerelementen für Variablen in Kibana 8.18+ einzelne Visualisierungen filtern, Zeitintervalle anpassen und nach verschiedenen Feldern in Kibana-Dashboards gruppieren können.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/kibana-dashboard-interactivity-variable-controls-overview</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/kibana-dashboard-interactivity-variable-controls-overview</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Teresa Alvarez Soler]]></dc:creator>
            <pubDate>Thu, 04 Dec 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/59aa3adffc8c759e42b961ef7d63719ce232893a-1348x830.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Wir freuen uns, Ihnen mitteilen zu können, dass <strong>Steuerelemente für Variablen ab Version 8.18 und in allen Versionen der Serie 9.x in Kibana-Dashboards verfügbar sind</strong>! Diese Funktion war eine der am häufigsten nachgefragten Ergänzungen von Dashboard-Benutzern – und nun ist sie endlich da 🎉 In den letzten Monaten haben wir die <a href="https://www.elastic.co/docs/explore-analyze/dashboards/add-controls#add-variable-control">Steuerelemente für Variablen</a> weiter ausgebaut und optimiert, sodass es nun an der Zeit ist, ihnen einen eigenen Blogbeitrag zu widmen.</p><h2>Was sind Steuerelemente für Variablen?</h2><p>Wenn Sie bereits mit Kibana-Dashboards gearbeitet haben, kennen Sie wahrscheinlich unsere klassischen Dashboard-Steuerelemente – diese praktischen Dropdown-Menüs, die Werte aus Ihren Daten anzeigen, sodass Sie mit wenigen Klicks Filter setzen können.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b82c1b25a0b38661e5ce4552f763be487d5074aa-1600x701.png" style="width: 100%;" /><p>Variable Bedienelemente sehen auf der Oberfläche ähnlich aus, haben aber eine clevere Wendung: Anstatt automatisch jedes Panel auf deinem Dashboard zu filtern, können sie direkt an <a href="https://www.elastic.co/docs/explore-analyze/visualize/esorql">ES|QL-Abfragen innerhalb einzelner Visualisierungen</a> angeschlossen werden.</p><p>Das bedeutet, <em>Sie</em> können entscheiden, wo jedes Steuerelement angewendet wird. Noch besser: Sie können sie für alle möglichen kreativen Tricks verwenden – beispielsweise zum Anpassen von Zeitintervallen, zum Wechseln von Aufschlüsselungsfeldern oder zum spontanen Ändern von Visualisierungsparametern. Im Grunde genommen sorgen sie für ein wahrhaft interaktives Erlebnis auf Ihren Dashboards, sodass Sie schneller und einfacher zu Ihren Erkenntnissen gelangen.</p><h2>Anwendungsfälle für Steuerelemente für Variablen</h2><p>Steuerelemente für Variablen sind zwar nützlich, aber was kann man damit eigentlich machen? Hier sind einige Beispiele dafür, wie sie Ihre Dashboards verbessern:</p><h3>Ausgewählte Visualisierungen filtern</h3><p>Möchten Sie <em>einige</em> Visualisierungen filtern, aber andere unberührt lassen? Steuerelemente für Variablen ermöglichen Ihnen genau das. Wählen Sie die Panels aus, auf die Sie reagieren möchten, und verknüpfen Sie sie in den ES|QL-Abfragen hinter Ihren Visualisierungen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/efa367363830b03bc67028aceafe78c4b44e578f-1440x562.gif" style="width: 100%;" /><h3>Wählen Sie unterschiedliche Zeitintervalle aus</h3><p>Geben Sie Ihren Nutzern die Möglichkeit, zwischen „5 Minuten“, „1 Stunde“, „1 Tag“ oder anderen sinnvollen Buckets zu wechseln. Erstellen Sie ein Steuerelement für Variablen mit vordefinierten Intervallen und verbinden Sie diese mit Ihrer Zeitreihenabfrage.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/62aa9f4e728036f8c70213b76b1cf131f36f5b4d-1440x606.gif" style="width: 100%;" /><h3>Funktionen ändern</h3><p>Anstatt mehrere Diagramme für jeden Vorgang zu erstellen, können die Nutzer des Dashboards wählen, ob sie den Maximalwert, den Durchschnittswert, verschiedene Perzentile oder einen anderen Aggregator sehen möchten.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f6a2b4c73dc35efe462c2924a153d7b3fa3a7922-1436x606.gif" style="width: 100%;" /><h3>Nach verschiedenen Feldern gruppieren</h3><p>Mitunter müssen Sie die Daten während einer Untersuchung nach verschiedenen Dimensionen aufschlüsseln. Mit Steuerelementen für Variablen können Sie mehrere „Gruppieren nach“-Felder festlegen und Dashboard-Nutzer so das Feld auswählen lassen, das sie bei der Gewinnung ihrer Erkenntnisse unterstützt.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/fe8745a6fddccadba0666686b8ebc67fdaf64158-1438x606.gif" style="width: 100%;" /><h2>Wie kann man sie erstellen?</h2><p>Die einfachste (und wahrscheinlich angenehmste) Methode zur Erstellung eines Steuerelements für Variablen ist direkt über den <strong>ES|QL-Abfrage-Editor</strong> in Ihrer Visualisierung. Beginnen Sie einfach mit der Eingabe Ihrer Suchanfrage, nutzen Sie das Autovervollständigungsmenü und Kibana unterstützt Sie bei der Erstellung Ihres Steuerelements.</p><p>Wenn Sie jedoch lieber mit der Variablen selbst beginnen möchten, können Sie auch zu <strong>Panel hinzufügen → Steuerelemente → Steuerelement für Variablen</strong> gehen und die Variable nach dem Erstellen des Steuerelements zu Ihren Visualisierungen hinzufügen.</p><h3>Beispiel 1: Filter-Steuerelement mit Auswahl mehrerer Werte</h3><p>1. Wählen Sie eine Visualisierung aus, die auf einer ES|QL-Abfrage basiert, und klicken Sie innerhalb der WHERE-Klausel auf „Steuerelement erstellen“</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/46cb6f2a6775aee152d42eb5ee85170f1bdf26cb-1600x668.png" style="width: 100%;" /><p>2. Sie werden automatisch zum Flyout für die Erstellung von Variablen weitergeleitet, wo der Typ „Werte aus einer Abfrage“ für Sie ausgewählt ist und der Name der Variablen bereits vorausgefüllt ist. Beachten Sie, dass der Name eines Steuerelements immer mit „?...” beginnen muss, damit er in der Visualisierungsabfrage funktioniert.</p><p>In der Regel benötigen Sie eine Abfrage wie diese, um die Werte eines Feldes abzurufen und sie entsprechend dem im Dashboard ausgewählten Zeitraum zu aktualisieren:</p><pre><code>FROM <datasource_name>
| WHERE @timestamp <=?_tend and @timestamp >?_tstart
| STATS BY <field_name></code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a2a72d4e3159923c6207908da9b4172e27cd5f81-1600x716.png" style="width: 100%;" /><p>3. Beim Speichern des Steuerelements erscheint sie oben im Dashboard, und Ihre Visualisierungsabfrage wird mit dem Namen des Steuerelements für Variablen aktualisiert.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5fc434c8951889e9769652b675191711d126a685-1600x653.png" style="width: 100%;" /><p>4. Wenn Sie <a href="https://www.elastic.co/docs/explore-analyze/dashboards/add-controls#esql-multi-values-controls">Mehrfachauswahl</a> zum Steuerelement hinzufügen möchten, verwenden Sie die Funktion <code>MV_CONTAINS</code> in der Abfrage und wählen Sie bei der Erstellung des Steuerelements in Schritt 2 (verfügbar ab 9.3) die Option „Mehrfachauswahl zulassen“ aus.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1f237cea0a37cb25a7917a2a683707a269adae8e-1600x670.png" style="width: 100%;" /><h3>Beispiel 2: Zeitintervallsteuerung</h3><p>Bei der Erstellung einer Zeitreihe können Sie ganz einfach ein Steuerelement für Variablen für Ihr Datums-Histogramm-Intervall hinzufügen:</p><p>1. Klicken Sie beim Schreiben einer ES|QL-Abfrage für Ihre Zeitreihe auf „Steuerelement erstellen“. Bei der Erstellung einer Variablen für Intervalle ist es besser, <code>TBUCKET</code> anstelle von <code>BUCKET</code> zu verwenden, damit besser lesbare Intervalle wie „1 Stunde“, „1 Tag“ usw. akzeptiert werden. In Kürze steht auch eine automatische Option für <code>TBUCKET</code> zur Verfügung, sodass sich das System automatisch an Zeitbereiche anpassen kann.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b0ad53d790ff9bdd42db5e77477318319f423534-1600x664.png" style="width: 100%;" /><p>2. Legen Sie die Intervalle zum Ausfüllen der Optionen im Dropdown-Menü fest.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f3bd83f530cfa4698c1a3b1ae60d08d0414043b5-1600x757.png" style="width: 100%;" /><p>3. Wählen Sie im Dropdown-Menü verschiedene Intervalle aus und sehen Sie, wie sich Ihre Visualisierung verändert.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0f928d9c70929f64926e065059188d140cd48943-1600x671.png" style="width: 100%;" /><h3>Beispiel 3: Variablen für Funktionen</h3><ol><li>Erstellen Sie eine Variable mithilfe des Kontrolltyps „Statische Werte“ und fügen Sie Funktionsnamen zu Ihren Dropdown-Werten hinzu. Es ist wichtig, für Ihre Variable einen Namen zu verwenden, der mit „??…“ beginnt, um Funktionen zu ersetzen.</li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/531444237b7e152d3c8a6f3ca7e464f954f9e856-1600x663.png" style="width: 100%;" /><p>2. Fügen Sie den Variablennamen in Ihre ES|QL-Abfrage ein.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9858442abb26d8d266d464852871b139fde63b89-1600x665.png" style="width: 100%;" /><h3>Beispiel 4: Variablen für Felder</h3><ol><li>Sie können den Kontrolltyp „Statische Werte“ verwenden und die Namen der gewünschten Felder eingeben. Es ist wichtig, einen Variablennamen zu verwenden, der mit „??...“ beginnt, damit er für Felder funktioniert.</li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/33534c3df2fae024b25c28b4aed5d742e54202a2-1600x710.png" style="width: 100%;" /><p>2. Verweisen Sie in der Visualisierungsabfrage auf die gewünschte Variable.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/71cdf3e9df72c59d957628a3aa6e4aa9bd60d6d5-1600x676.png" style="width: 100%;" /><h2>Steuerelemente für Variablen in Discover</h2><p>Steuerelemente für Variablen sind nicht nur eine Dashboard-Funktion, sondern auch direkt im ES|QL-Editor in Discover verfügbar. Sie können Steuerelemente für eine schnellere Datenexploration in Discover erstellen, diese in das Dashboard übertragen und umgekehrt.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/fdddeec902d0bc746caed9276d01d7d48793dd85-1600x709.png" style="width: 100%;" /><h2>Technische Details</h2><p>Mittlerweile haben Sie wahrscheinlich bemerkt, dass Steuerelemente für Variablen einigen Regeln unterliegen – beispielsweise, auf welche Teile einer Abfrage sie verweisen können und welche Namenspräfixe Sie verwenden müssen („?...” für Werte und „??...” für Felder oder Funktionen). Das liegt daran, dass Variablen nicht nur einfache Zeichenfolgenersetzungen sind, die auf dem Client stattfinden. Es handelt sich dabei um erstklassige Elemente der Abfragesprache selbst (bekannt als <a href="https://www.elastic.co/docs/solutions/search/agent-builder/tools/esql-tools#parameter-types">Parameter in ES|QL</a>).</p><p></p><p>Dieses Design bietet einige große Vorteile. Zum einen kann Kibana den Kontext jeder Variablen verstehen, wodurch wir die Konfiguration automatisch für Sie generieren und vorausfüllen können. Außerdem ist es viel sicherer: Da die Sprache Variableneingaben streng validiert, verhindert sie böswillige Injektionen und gibt bei Unstimmigkeiten eine Fehlermeldung aus. Darüber hinaus werden Leistung und Stabilität verbessert, indem komplexe Validierungen und Fehlerbehandlungen vom Client auf den Server verlagert werden. Ein Hinweis zur Leistung: Eine bewährte Vorgehensweise besteht in der Erstellung von Variablen, die schnelle Abfragen enthalten, da diese vor dem Dashboard geladen werden. Langsame Abfragen können sich daher auf die gesamte Dashboard-Leistung auswirken.</p><p>Natürlich bringt diese Architektur vorerst auch einige <a href="https://www.elastic.co/docs/solutions/search/agent-builder/limitations-known-issues#esql-limitations">Einschränkungen</a> mit sich. Variablen unterstützen noch keine „Alle“-Option für das Filtern und können derzeit nicht mit bestimmten Operatoren wie <code>LIKE</code><strong> </strong>oder <code>FROM</code> (zum Wechseln der Datenquellen) verwendet werden. Die gute Nachricht? Wir arbeiten aktiv an der Erweiterung um diese Funktionen.</p><h2>Was die Zukunft für Bedienelemente bereithält</h2><p>Wir machen hier noch lange nicht Schluss! Zu den geplanten Verbesserungen gehören unter anderem:</p><p>✨ Die Möglichkeit zur beliebigen Platzierung von Steuerelementen auf dem Dashboard</p><p>✨ Verkettung Ihrer Steuerelemente – das bedeutet, der Ausgang eines Steuerelements wird zum Eingang für das nächste Steuerelement</p><p>✨ Verbesserte Auswahlmöglichkeiten wie die Option „Alle“ für Variablen</p><p>✨ Neue Kontrolltypen (Suchtyp-Steuerelement und Variablen für Ihre Datenquellen)</p><p>✨ Und weitere Verbesserungen der von Ihnen Benutzerfreundlichkeit, wie beispielsweise die Vorfilterung normaler Steuerelemente</p><p>Wir freuen uns über Ihre Ideen und Ihr Feedback.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Bis zu 12 Mal schnellere Vektorindizierung in Elasticsearch mit NVIDIA cuVS: GPU-Beschleunigung Kapitel 2]]></title>
            <description><![CDATA[Entdecken Sie, wie Elasticsearch mit GPU-beschleunigter Vektorindizierung und NVIDIA cuVS einen fast 12-mal höheren Indizierungsdurchsatz erzielt.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/elasticsearch-gpu-accelerated-vector-indexing-nvidia</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/elasticsearch-gpu-accelerated-vector-indexing-nvidia</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Chris Hegarty, Hemant Malik, Corey Nolet, Manas Singh, Mithun Radhakrishnan, Mayya Sharipova, Lorenzo Dematte, Ben Frederickson]]></dc:creator>
            <pubDate>Wed, 03 Dec 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/08f7469a4daaf67b7c5999585aae179b6680c78d-896x746.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Anfang dieses Jahres kündigte Elastic die <a href="https://ir.elastic.co/news/news-details/2025/Elastic-Brings-Enterprise-Data-to-NVIDIA-AI-Factories/default.aspx">Zusammenarbeit</a> mit NVIDIA an, um die GPU-Beschleunigung für Elasticsearch zu realisieren und sie in <a href="https://developer.nvidia.com/cuvs">NVIDIA cuVS</a> zu integrieren – wie in einer <a href="https://www.nvidia.com/en-us/on-demand/session/gtc25-S71286/">Session auf der NVIDIA GTC</a> und in verschiedenen <a href="https://www.elastic.co/search-labs/blog/gpu-accelerated-vector-search-elasticsearch-nvidia">Blogs</a> detailliert beschrieben. Dieser Beitrag ist ein Update zum gemeinsamen Entwicklungsprojekt mit dem NVIDIA-Vektorsuchteam.</p><h2>Zusammenfassung</h2><p>Zunächst einmal ein kurzer Überblick. Elasticsearch hat sich als leistungsstarke Vektordatenbank etabliert und bietet eine Vielzahl von Funktionen sowie eine hohe Leistungsfähigkeit für die Ähnlichkeitssuche im großen Maßstab. Mit Funktionen wie Skalarquantisierung, Better Binary Quantization (<a href="https://www.elastic.co/search-labs/blog/better-binary-quantization-lucene-elasticsearch">BBQ</a>), <a href="https://www.elastic.co/blog/accelerating-vector-search-simd-instructions">SIMD-</a> Vektoroperationen und speichereffizienteren Algorithmen wie <a href="https://www.elastic.co/search-labs/blog/diskbbq-elasticsearch-introduction">DiskBBQ</a> bietet es bereits effiziente und flexible Optionen für die Verwaltung von Vektor-Workloads.</p><p>Durch die Integration von NVIDIA cuVS als aufrufbares Modul für Vektorsuchaufgaben wollen wir signifikante Verbesserungen bei der Vektorindizierungsleistung und -effizienz erzielen, um große Vektor-Workloads besser zu unterstützen.</p><h2>Die Herausforderung</h2><p>Eine der größten Herausforderungen beim Aufbau einer leistungsstarken Vektordatenbank ist die Konstruktion des Vektorindexes – des <a href="https://arxiv.org/abs/1603.09320">HNSW-Graphen</a>. Die Indexbildung wird schnell von Millionen oder sogar Milliarden arithmetischer Operationen dominiert, da jeder Vektor mit vielen anderen verglichen wird. Darüber hinaus können Index-Lebenszyklusoperationen wie Komprimierung und Zusammenführungen den gesamten Rechenaufwand für die Indizierung weiter erhöhen. Da Datenmengen und die damit verbundenen Vektoreinbettungen exponentiell wachsen, sind beschleunigte Rechen-GPUs, die für massive Parallelität und mathematische Berechnungen mit hohem Durchsatz ausgelegt sind, ideal für die Bewältigung dieser Arbeitslasten geeignet.</p><h2>Öffnen Sie das Elasticsearch-GPU-Plugin.</h2><p><a href="https://developer.nvidia.com/cuvs">NVIDIA cuVS</a> ist eine Open-Source-CUDA-X-Bibliothek für GPU-beschleunigte Vektorsuche und Datenclustering, die schnelles Indexieren und Einbetten für KI- und Empfehlungs-Workloads ermöglicht.</p><p>Elasticsearch nutzt cuVS über <a href="https://mvnrepository.com/artifact/com.nvidia.cuvs/cuvs-java">cuvs-java</a>, eine Open-Source-Bibliothek, die von der Community entwickelt und von NVIDIA gepflegt wird. Die cuvs-java Bibliothek ist leichtgewichtig und baut auf der <a href="https://docs.rapids.ai/api/cuvs/nightly/c_api/">cuVS C API</a> auf, indem sie <a href="https://openjdk.org/projects/panama/">Panama</a> Foreign Function verwendet, um cuVS-Funktionen auf idiomatische Java-Art bereitzustellen und dabei modern und leistungsstark zu bleiben.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5f6daa1eb07f704a6707d9e6b7ccb81d0abaa8c9-566x419.png" style="width: 100%;" /><p>Die cuvs-java-Bibliothek ist in ein <a href="https://github.com/elastic/elasticsearch/pull/135545">neues Elasticsearch-Plugin</a> integriert; daher kann die Vektorindizierung auf der GPU auf demselben Elasticsearch-Knoten und -Prozess erfolgen, ohne dass externer Code oder Hardware bereitgestellt werden muss. Während des Indexaufbaus nutzt Elasticsearch die GPU, um den Vektorindexierungsprozess zu beschleunigen, sofern die cuVS-Bibliothek installiert und eine GPU vorhanden und konfiguriert ist. Die Vektoren werden der GPU übergeben, die daraus einen <a href="https://arxiv.org/abs/2308.15136">CAGRA-Graphen</a> erstellt. Dieser Graph wird dann in das HNSW-Format umgewandelt, wodurch er sofort für die Vektorsuche auf der CPU verfügbar ist. Das endgültige Format des erstellten Graphen ist identisch mit dem, das auf der CPU erstellt würde; dadurch kann Elasticsearch GPUs für die Vektorindizierung mit hohem Durchsatz nutzen, wenn die zugrunde liegende Hardware dies unterstützt, während gleichzeitig CPU-Leistung für andere Aufgaben (gleichzeitige Suche, Datenverarbeitung usw.) frei bleibt.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3ea255bd9bfd7983f78143c5eba999d2149d72be-671x356.png" style="width: 100%;" /><h2>Beschleunigung des Indexaufbaus</h2><p>Im Rahmen der Integration der GPU-Beschleunigung in Elasticsearch wurden mehrere Verbesserungen an cuvs-java vorgenommen, die sich auf einen effizienten Dateneingang/-ausgang und Funktionsaufruf konzentrieren. Eine wichtige Verbesserung ist die Verwendung von <a href="https://github.com/rapidsai/cuvs/blob/2cf5fa7666d703dccbe655f8214656b0952bb69b/java/cuvs-java/src/main/java/com/nvidia/cuvs/CuVSMatrix.java">cuVSMatrix</a> zur transparenten Modellierung von Vektoren, unabhängig davon, ob sie sich auf dem Java-Heap, außerhalb des Heaps oder im GPU-Speicher befinden. Dadurch können Daten effizient zwischen Arbeitsspeicher und GPU übertragen werden, wodurch unnötige Kopien von potenziell Milliarden von Vektoren vermieden werden.</p><p>Dank dieser zugrundeliegenden Zero-Copy-Abstraktion können sowohl die Übertragung in den GPU-Speicher als auch der Abruf des Graphen direkt erfolgen. Beim Indizieren werden die Vektoren zunächst im Speicher auf dem Java-Heap zwischengespeichert und dann an die GPU gesendet, um den CAGRA-Graphen zu erstellen. Der Graph wird anschließend von der GPU abgerufen, in das HNSW-Format konvertiert und auf der Festplatte gespeichert.</p><p>Beim Zusammenführen sind die Vektoren bereits auf der Festplatte gespeichert, wodurch der Java-Heap vollständig umgangen wird. Indexdateien sind speicherabgebildet und die Daten werden direkt in den GPU-Speicher übertragen. Das Design unterstützt zudem problemlos unterschiedliche Bitbreiten, z. B. float32 oder int8, und lässt sich natürlich auf andere Quantisierungsschemata erweitern.</p><h2>Trommelwirbel ... wie gut funktioniert es?</h2><p>Bevor wir zu den Zahlen kommen, ist ein bisschen Kontext hilfreich. Die Segmentzusammenführung in Elasticsearch läuft in der Regel automatisch im Hintergrund während der Indizierung, was ein isoliertes Benchmarking schwierig macht. Um reproduzierbare Ergebnisse zu erzielen, haben wir in einem kontrollierten Experiment die Segmentzusammenführung explizit mit der Funktion Force-Merge ausgelöst. Da Force-Merge die gleichen zugrunde liegenden Zusammenführungsoperationen wie das Zusammenführen im Hintergrund durchführt, dient seine Leistung als nützlicher Indikator für erwartete Verbesserungen, auch wenn die genauen Gewinne in realen Indexierungs-Workloads abweichen können.</p><p>Schauen wir uns nun die Zahlen an.</p><p>Unsere ersten Benchmark-Ergebnisse sind sehr vielversprechend. Wir haben den Benchmark auf einer AWS <code>g6.4xlarge</code> Instanz mit lokal angeschlossenem NVMe-Speicher ausgeführt. Ein einzelner Knoten von Elasticsearch wurde so konfiguriert, dass er die standardmäßige, optimale Anzahl von Indizierungs-Threads (8 – einer für jeden physischen Kern) verwendet und <a href="https://www.elastic.co/docs/reference/elasticsearch/index-settings/merge">Merge Throttling</a> deaktiviert (was bei schnellen NVMe-Festplatten weniger relevant ist).</p><p>Für den Datensatz verwendeten wir 2,6 Millionen Vektoren mit 1.536 Dimensionen aus der <a href="https://github.com/elastic/rally-tracks/blob/master/openai_vector/README.md">OpenAI Rally-Vektorstrecke</a>, kodiert als <a href="https://github.com/elastic/elasticsearch/pull/137072">Base64-Zeichenfolgen</a> und indexiert als float32 <em>hnsw</em>. In allen Szenarien erreichen die konstruierten Graphen Recall-Werte von bis zu 95 %. Hier sind unsere Ergebnisse:</p><ul><li><strong>Indizierungsdurchsatz:</strong> Durch die Verlagerung der Graphenkonstruktion auf die GPU während des Speicherpuffer-Flushs steigern wir den Durchsatz um das 12-fache.</li><li><strong>Force-Merge:</strong> Nach Abschluss der Indizierung beschleunigt die GPU weiterhin die Segmentzusammenführung und beschleunigt die Force-Merge-Phase um das ~7-fache.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f60ea9ee5996e456f393ffd195ee7eada6e5a7c2-948x387.png" style="width: 100%;" /><ul><li><strong>CPU-Auslastung:</strong> Durch die Auslagerung der Graph-Konstruktion auf die GPU werden sowohl die durchschnittliche als auch die maximale CPU-Auslastung deutlich reduziert. Die folgenden Graphen veranschaulichen die CPU-Auslastung während der Indizierung und Zusammenführung und zeigen, wie viel geringer sie ist, wenn diese Vorgänge auf der GPU ausgeführt werden. Eine geringere CPU-Auslastung während der GPU-Indexierung setzt CPU-Zyklen frei, die zur Verbesserung der Suchleistung umgeleitet werden können.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5e680a5fc41700a877f3d8b2e5ce18ebd3f37a0b-1600x562.png" style="width: 100%;" /><ul><li><strong>Recall:</strong> Die Genauigkeit bleibt zwischen CPU- und GPU-Läufen praktisch gleich, wobei der mit der GPU erstellte Graph einen geringfügig höheren Recall erreicht.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/48a2b7758606bd321712b7d8378cd2640e652a4e-1384x544.png" style="width: 100%;" /><h2>Vergleich anhand einer weiteren Dimension: Preis</h2><p>Beim vorherigen Vergleich wurde absichtlich identische Hardware verwendet; der einzige Unterschied bestand darin, ob die GPU während der Indizierung verwendet wurde. Diese Konfiguration ist nützlich, um die Auswirkungen der reinen Rechenleistung zu isolieren, aber wir können den Vergleich auch aus einer Kostenperspektive betrachten.</p><p>Bei etwa demselben Stundenpreis wie die GPU-beschleunigte Konfiguration kann man ein CPU-reines Setup mit etwa doppelt so vielen vergleichbaren CPU- und Speicherressourcen bereitstellen: 32 vCPUs (AMD EPYC) und 64 GB RAM, wodurch die Anzahl der Indexierungs-Threads auf 16 verdoppelt werden kann.</p><p>Um den Vergleich fair und konsistent zu halten, haben wir dieses reine CPU Experiment auf einer AWS g6.8xlarge Instanz ausgeführt, wobei die GPU explizit deaktiviert war. Dadurch konnten wir alle anderen Hardwareeigenschaften konstant halten und gleichzeitig den Kosten-Leistungs-Kompromiss zwischen GPU-Beschleunigung und reiner CPU-Indexierung evaluieren.</p><p>Die leistungsstärkere CPU-Instanz zeigt erwartungsgemäß eine verbesserte Performance im Vergleich zu den Benchmarks im obigen Abschnitt. Wenn wir jedoch diese leistungsstärkere CPU-Instanz mit den ursprünglichen GPU-beschleunigten Ergebnissen vergleichen, liefert die GPU immer noch erhebliche Leistungssteigerungen: <strong>~5-fache</strong> Verbesserung des Indizierungsdurchsatzes und <strong>~6-fache</strong> Verbesserung beim Force Merge, während gleichzeitig Graphen erstellt werden, die Recall-Werte von bis zu <strong>95 %</strong> erreichen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8ffa58cae3ad175ef2932a351aeef4c34a1407b9-948x394.png" style="width: 100%;" /><h2>Fazit</h2><p>In End-to-End-Szenarien bietet die GPU-Beschleunigung mit NVIDIA cuVS eine nahezu 12-fache Verbesserung des Indexierungsdurchsatzes und eine 7-fache Verringerung der Force-Merge-Latenz bei deutlich geringerer CPU-Auslastung. Dies zeigt, dass Vektorindizierungs- und Merge-Workloads erheblich von der GPU-Beschleunigung profitieren. Im Kostenvergleich bietet die GPU-Beschleunigung weiterhin deutliche Leistungssteigerungen, mit einem etwa 5-fach höheren Indexierungsdurchsatz und 6-fach schnelleren Force-Merge-Operationen.</p><p>Die GPU-beschleunigte Vektorindizierung ist derzeit für die Tech Preview in Elasticsearch 9.3 geplant, deren Veröffentlichung für Anfang 2026 vorgesehen ist.</p><p>Mehr dazu in Kürze.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ES|QL in 9.2: Unterstützung für intelligente Lookup Joins und Zeitreihen]]></title>
            <description><![CDATA[Entdecken Sie drei separate Updates für ES|QL in Elasticsearch 9.2: ein verbessertes LOOKUP JOIN für ausdrucksstärkere Datenkorrelation, den neuen TS-Befehl für die Zeitreihenanalyse und den flexiblen INLINE STATS-Befehl für die Aggregation.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/esql-elasticsearch-9-2-multi-field-joins-ts-command</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/esql-elasticsearch-9-2-multi-field-joins-ts-command</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Tyler Perkins, Kostas Krikellas, Julian Kiryakov]]></dc:creator>
            <pubDate>Tue, 02 Dec 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/415894e21e7758c907d6e60d4efc94230349beef-2012x1164.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Elasticsearch 9.2, das im Oktober veröffentlicht wurde, bietet zahlreiche bedeutende Verbesserungen, die die Analyse Ihrer Daten schneller, flexibler und zugänglicher als je zuvor machen. Im Mittelpunkt dieser Version stehen wichtige Verbesserungen an ES|QL, unserer Pipe-Abfragesprache, die entwickelt wurden, um Endnutzer:innen direkt noch mehr Nutzen zu bieten.</p><p>Hier ist ein Überblick über die Features in Elasticsearch 9.2, die Ihre Datenanalyse-Workflows mit ES|QL verändern werden.</p><h2>Revolutionierung der Datenkorrelation: Eine intelligentere, schnellere und flexiblere Lookup-Verknüpfung</h2><p>Der Befehl <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/lookup-join">LOOKUP JOIN</a> in ES|QL hat in Elasticsearch 9.2 eine bedeutende Transformation durchlaufen und ist deutlich effizienter und vielseitiger geworden. LOOKUP JOIN kombiniert Daten aus Ihrer ES|QL-Abfrageergebnistabelle mit übereinstimmenden Einträgen aus einem angegebenen Lookup-Modus-Index. Es fügt Felder aus dem Lookup-Index als neue Spalten zu Ihrer Ergebnistabelle hinzu, basierend auf übereinstimmenden Werten im Join-Feld. Zuvor war die Verknüpfung von Daten auf ein einziges Feld und einfache Gleichheit beschränkt. Das ist Geschichte! Dank dieser Erweiterungen können Sie komplexe Datenkorrelationsszenarien mühelos bewältigen.</p><p><strong>Zu den wichtigsten Verbesserungen von Lookup Join gehören:</strong></p><ul><li><strong>Verknüpfungen mit mehreren Feldern:</strong> Einfaches Verknüpfen mehrerer Felder. So verbinden Sie beispielsweise <code>application_logs</code> mit <code>service_registry</code> anhand von <code>service_name</code>, <code>environment</code> und <code>version:</code></li></ul><pre><code>FROM application_logs
| LOOKUP JOIN service_registry ON service_name, environment, version</code></pre><ul><li><strong>Freischalten komplexer Join-Prädikate mit Ausdrücken (technische Vorschau):</strong></li></ul><p>Sie sind nicht mehr auf einfache Gleichheit beschränkt. LOOKUP JOIN ermöglicht es Ihnen nun, <strong>mehrere Kriterien</strong> für die Korrelation anzugeben und eine Reihe von <strong>binären Operatoren</strong> einzubeziehen, darunter ==, !=, &lt;, &gt;, &lt;= und &gt;=. Dies bedeutet, dass Sie hochgradig nuancierte Join-Bedingungen erstellen können, die es Ihnen ermöglichen, viel anspruchsvollere Fragen an Ihre Daten zu stellen.</p><p>Beispiel 1: Ermittlung von Anwendungsmetriken mit SLA-Schwellenwert pro Dienst</p><pre><code>FROM application_metrics
| LOOKUP JOIN sla_thresholds
      ON service_name == sla_service AND response_time > sla_response_time</code></pre><p>Beispiel 2: Diese Abfrage berechnet den fälligen Betrag auf Grundlage regionaler Preisrichtlinien, die sich im Laufe der Zeit ändern. Es verknüpft drei Datensätze basierend auf komplexen Datumsbereichs- und Gleichheitsbedingungen, um eine endgültige <code>due_amount</code> zu berechnen. Der zweite Lookup-Join verwendet das Feld <code>measurement_date</code> aus dem Index <code>meter_readings</code> und das Feld <code>region_id</code> aus dem Index <code>customers</code>, um mit dem Index <code>pricing_policies</code> verknüpft zu werden und die richtige Preispolitik für die jeweiligen <code>region</code> und <code>measurement_date</code> zu finden.</p><pre><code>FROM meter_readings
| LOOKUP JOIN customers
      ON meter_id
| LOOKUP JOIN pricing_policies
      ON
        region_id == region AND
          measurement_date >= policy_begin_date AND
          measurement_date < policy_end_date
| EVAL due_amount = (kwh_consumed * rate_per_kwh + base_charge) * (1 + tax_rate)
| EVAL period = policy_name
| KEEP customer_name, period, due_amount, measurement_date, kwh_consumed,
    rate_per_kwh, base_charge, tax_rate
| SORT measurement_date</code></pre><ul><li><strong>Enorme Leistungsgewinne bei gefilterten Joins: </strong></li></ul><p>Wir haben die Leistung für „erweiterte Verknüpfungen” verbessert, die anhand von Lookup-Tabellenbedingungen gefiltert werden. Erweiterte Verknüpfungen führen zu mehreren Übereinstimmungen pro Eingabezeile, wodurch große Zwischenergebnismengen entstehen können. Dies wird noch schlimmer, wenn viele dieser Zeilen durch einen nachfolgenden Filter verworfen werden. In 9.2 optimieren wir diese Verknüpfungen, indem wir unnötige Zeilen herausfiltern, wenn ein Filter auf Suchdaten angewendet wird. Dadurch wird die Verarbeitung von Zeilen vermieden, die verworfen würden. In einigen Szenarien können diese Joins bis zu <strong>1000-mal schneller</strong> sein!</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/002c014ee29b1aaf9ddeb8c554bb76efe3ed180c-1572x954.png" style="width: 100%;" /><p>Diese Optimierung ist entscheidend bei der Verarbeitung von „expandierenden Joins“, bei denen eine Suche anfänglich viele potenzielle Übereinstimmungen erzeugen kann. Durch intelligentes Herunterdrücken von Filtern werden nur die relevanten Daten verarbeitet, was die Ausführungszeit von Abfragen drastisch verkürzt und die Echtzeitanalyse großer Datensätze ermöglicht. Das bedeutet, dass Sie Ihre Einblicke viel schneller erhalten, selbst bei sehr großen oder komplexen Join-Operationen.</p><p><strong>Kompatibilität mit der clusterübergreifenden Suche (CCS) Lookup Join:</strong></p><p>Als Lookup Join in den Versionen 8.19 und 9.1 allgemein verfügbar wurde, fehlte die Unterstützung für die clusterübergreifende Suche (CCS). Für Organisationen, die in mehreren Clustern arbeiten, lässt sich LOOKUP JOIN in 9.2 jetzt nahtlos in CCS integrieren. Platzieren Sie einfach Ihren Lookup-Index auf allen Remote-Clustern, mit denen Sie einen Join durchführen möchten, und ES|QL nutzt diese Remote-Lookup-Indizes automatisch, um den Join mit Ihren Remote-Daten durchzuführen. Dies vereinfacht die verteilte Datenanalyse und gewährleistet eine konsistente Anreicherung Ihres gesamten Elasticsearch-Deployments.</p><p>Diese Verbesserungen ermöglichen es Ihnen, vielfältige Datensätze mit beispielloser Präzision, Geschwindigkeit und Leichtigkeit zu korrelieren und so tiefere, umsetzbare Einblicke ohne komplexe Workarounds oder Vorverarbeitungsschritte zu gewinnen.</p><h2>Reichern Sie Ihre Daten mühelos an: Kibana Discover UX für Lookup-Indizes</h2><p>Die Datenanreicherung sollte unkompliziert sein und keine Hürde darstellen. Wir haben in Kibana Discover eine fantastische neue Nutzererfahrung für die Erstellung und Verwaltung von Lookup-Indizes eingeführt.</p><p><strong>Intuitiver Workflow:</strong> Die umfassende Autovervollständigung von Discover führt Sie durch den Prozess, schlägt Suchindizes und Join-Felder im ES|QL-Editor vor und macht es unglaublich einfach, Ihre hochgeladenen Daten mit vorhandenen Indizes zu verbinden. Geben Sie den Namen eines Lookup-Index ein, der nicht existiert, und erhalten Sie mit einem Klick direkten Zugriff auf den Lookup-Editor, um den Index zu erstellen. Geben Sie den Namen eines bestehenden Nachschlageindex ein, und wir schlagen eine Option zur Bearbeitung vor:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d36fd6ffd6bc0bf8d31067f6266445c68d15c71c-1400x184.png" style="width: 100%;" /><p><strong>Inline-Management (CRUD):</strong> Halten Sie Ihre Datensätze mit Inline-Bearbeitungsfunktionen (Erstellen, Lesen, Aktualisieren, Löschen) und Liniendiagramm direkt in Discover auf dem neuesten Stand.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a0e4aa66540b1f725c24ccb0519d978415073bb6-1453x842.png" style="width: 100%;" /><p><strong>Müheloses Hochladen von Dateien: </strong>Sie können jetzt Dateien wie CSVs direkt in Discover hochladen und sofort in Ihren <code>LOOKUP JOIN</code> verwenden. Keine Kontextwechsel mehr durch das Springen zwischen verschiedenen Bereichen von Kibana!</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0f5dc2c712af4c4cada50292a7c8b836eb02aa67-1600x748.png" style="width: 100%;" /><p>Egal, ob Sie Nutzer-IDs mit Namen mappen, Geschäfts-Metadaten hinzufügen oder statische Referenzdateien verknüpfen – dieses Feature demokratisiert die Datenanreicherung und versorgt die Joins direkt in den Händen aller Nutzer:innen mit Energie – schnell, einfach und alles an einem Ort.</p><h2>Bewahren Sie Ihren Kontext: Einführung von INLINE STATS (technische Vorschau)</h2><p>Die Aggregation von Daten ist entscheidend, aber manchmal müssen Sie die Aggregate <em>neben</em> Ihren ursprünglichen Daten sehen. Wir freuen uns, <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/inlinestats-by">INLINE STATS</a> als <strong>Tech Preview</strong>-Feature vorzustellen.</p><p>Im Gegensatz zum Befehl <code>STATS</code>, der Ihre Eingabefelder durch aggregierte Ausgaben ersetzt, behält <code>INLINE STATS</code> alle Ihre ursprünglichen Eingabefelder bei und fügt lediglich die neuen aggregierten Felder hinzu. Dies ermöglicht es Ihnen, <em>nach</em> der Aggregation weitere Operationen auf Ihren ursprünglichen Eingabefeldern durchzuführen und bietet so einen kontinuierlicheren und flexibleren Analyse-Workflow.</p><p>Um beispielsweise die durchschnittliche Flugdistanz unter Beibehaltung der einzelnen Flugreihen zu berechnen:</p><pre><code>FROM kibana_sample_data_flights
 | KEEP Carrier, Dest, DistanceMiles
 | INLINE STATS avgDist = ROUND(AVG(DistanceMiles))
       BY Dest
 | WHERE DistanceMiles > avgDist</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6eea943035e0ab371270084c504a06bb89f8b82b-1496x290.png" style="width: 100%;" /><p>Bei dieser Abfrage wird jeder Zeile <code>avgDist</code> mit der entsprechenden <code>Dest</code>(ination) hinzugefügt, nach der wir gruppiert haben. Da wir dann immer noch die Spalten mit den Fluginformationen haben, können wir die Ergebnisse auf die Flüge mit einer Entfernung, die größer als der Durchschnitt ist, filtern.</p><h2>Zeitreihenunterstützung in ES|QL (technische Vorschau)</h2><p>Elasticsearch verwendet <a href="https://www.elastic.co/docs/manage-data/data-store/data-streams/time-series-data-stream-tsds">Zeitreihen-Datenströme</a> zum Speichern von Metriken. Wir fügen Unterstützung für Zeitreihenaggregationen in ES|QL über den <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/ts"><code>TS</code></a> Source-Befehl hinzu. Dies ist in Elastic Cloud Serverless und 9.2 Basic als Tech-Vorschau verfügbar.</p><p>Die Zeitreihenanalyse basiert größtenteils auf Aggregationsabfragen, die Metrikwerte in Zeit-Buckets zusammenfassen, unterteilt durch eine oder mehrere Filterdimensionen. Die meisten Aggregationsabfragen basieren auf einer zweistufigen Verarbeitung: (a) eine innere Aggregationsfunktion, die Werte pro Zeitreihe zusammenfasst, und (b) eine äußere Aggregationsfunktion, die die Ergebnisse von (a) über Zeitreihen hinweg kombiniert.</p><p>Der Quellbefehl <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/ts"><code>TS</code></a> bietet in Kombination mit <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/stats-by"><code>STATS</code></a> eine prägnante und dennoch effektive Möglichkeit, solche Abfragen über Zeitreihen auszudrücken. Betrachten wir konkreter das folgende Beispiel zur Berechnung der Gesamtanforderungsrate pro Host und Stunde:</p><pre><code>TS my_metrics
| WHERE @timestamp > NOW() - 1 day
| STATS SUM(RATE(requests))
      BY host, TBUCKET(1h)</code></pre><p>In diesem Fall wird die Aggregationsfunktion <code>RATE</code> zuerst pro Zeitreihe und Stunde ausgewertet. Die erzeugten Teilaggregate werden dann mit <code>SUM</code> kombiniert, um die endgültigen Aggregatwerte pro Host und Stunde zu berechnen.</p><p>Eine Liste der verfügbaren Funktionen zur Aggregation von Zeitreihen finden Sie <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/time-series-aggregation-functions">hier</a>. <a href="https://www.elastic.co/docs/manage-data/data-store/data-streams/time-series-data-stream-tsds#time-series-metric">Zählerrate</a> wird jetzt unterstützt, die wohl wichtigste Aggregationsfunktion für die Verarbeitung von Zählern.</p><p>Der Quellbefehl <code>TS</code> ist so konzipiert, dass er mit <code>STATS</code> kombiniert werden kann, wobei die Ausführung so abgestimmt ist, dass sie Zeitreihenaggregationen effizient unterstützt. Zum Beispiel werden die Daten sortiert, bevor sie in die <code>STATS</code> gelangen. Verarbeitungsbefehle, die die Zeitreihendaten oder ihre Reihenfolge anreichern oder verändern können, wie <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/fork"><code>FORK</code></a> oder <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/inlinestats-by"><code>INLINE STATS</code></a>, sind derzeit zwischen <code>TS</code> und <code>STATS</code> nicht zulässig. Diese Beschränkung könnte in Zukunft aufgehoben werden.</p><p>Die tabellarische Ausgabe von <code>STATS</code> kann mit einem beliebigen Befehl weiterverarbeitet werden. Zum Beispiel berechnet die folgende Abfrage das Verhältnis des durchschnittlichen <code>cpu_usage</code> pro Host und Stunde bis zum maximalen Wert pro Host:</p><pre><code>TS my_metrics
| STATS avg_usage = AVG(AVG_OVER_TIME(cpu_usage))
      BY host, time_bucket = TBUCKET(1h)
| INLINE STATS max_avg_usage = MAX(avg_usage)
      BY host
| EVAL ratio = avg_usage / max_avg_usage
| KEEP host, time_bucket, ratio
| SORT host, time_bucket DESC</code></pre><p>Zeitreihendaten werden auf unserer zugrunde liegenden spaltenförmigen Speicher-Engine gespeichert, die von Lucene-Doc-Werten betrieben wird. Der TS-Befehl fügt die vektorisierte Abfrageausführung über die ES|QL-Compute-Engine hinzu. Die Abfrageleistung wird im Vergleich zu äquivalenten <a href="https://www.elastic.co/docs/reference/query-languages/querydsl">DSL</a>-Abfragen oft um mehr als eine Größenordnung verbessert und ist mit etablierten, metrikspezifischen Systemen vergleichbar. Wir werden in Zukunft eine detaillierte Architektur- und Leistungsanalyse bereitstellen, also bleiben Sie gespannt.</p><h2>Erweiterung Ihres Toolkits: Neue ES|QL-Funktionen</h2><div style="display:none">Unknown block type "block", specify a component for it in the `components.types` option</div><p><strong>Zeichenfolgenmanipulation: </strong><a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/string-functions#esql-contains">CONTAINS</a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/mv-functions#esql-mv_contains">MV_CONTAINS</a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/string-functions#esql-url_encode">URL_ENCODE</a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/string-functions#esql-url_encode_component">URL_ENCODE_COMPONENT</a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/string-functions#esql-url_decode">URL_DECODE</a> für eine robustere Text- und URL-Verarbeitung.</p><p><strong>Zeitreihen und Geodaten:</strong> <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/grouping-functions#esql-tbucket">TBUCKET</a> für flexible Buckets, TO_DENSE_VECTOR für Vektoroperationen und ein umfassender Satz von <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/spatial-functions">Geodatenfunktionen</a> wie <code>ST_GEOHASH</code>, <code>ST_GEOTILE</code>, <code>ST_GEOHEX</code>, <code>TO_GEOHASH</code>, <code>TO_GEOTILE</code>, <code>TO_GEOHEX</code> für fortgeschrittene ortsbezogene Analysen.</p><p><strong>Datumsformatierung:</strong> <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/date-time-functions#esql-day_name">DAY_NAME</a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/date-time-functions#esql-month_name">MONTH_NAME</a> für besser lesbare Datumsdarstellungen.</p><p>Diese Funktionen bieten Ihnen eine umfangreichere Auswahl an Werkzeugen, um Ihre Daten direkt innerhalb von ES|QL zu bearbeiten und zu analysieren.</p><h2>Unter der Haube: Mehr Leistung und Effizienz</h2><p>Neben den hervorgehobenen Features bietet Elasticsearch 9.2 zahlreiche Leistungsoptimierungen für ES|QL. Wir haben <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/where#like-and-rlike">RLIKE (LIST)</a> mit Pushdown beschleunigt, wenn die Funktion mehrere ähnliche RLIKE-Abfragen in einem Ticket ersetzt. Mit <code>RLIKE</code> (LIST) können wir diese Abfragen zu einem einzigen Automaten zusammenführen und einen Automaten anstelle mehrerer anwenden. Wir bieten außerdem ein schnelleres Laden von Schlüsselwortfeldern durch Indexsortierungen und allgemeine Abfrageoptimierungen – diese Verbesserungen gewährleisten, dass Ihre ES|QL-Abfragen effizienter als je zuvor ausgeführt werden.</p><h2>Legen Sie noch heute los!</h2><p>Elasticsearch 9.2 ist ein bedeutender Fortschritt für ES|QL und bringt beispiellose Leistung und Flexibilität in Ihre Datenanalyse-Workflows. Wir ermutigen Sie, diese neuen Features zu erkunden und den Unterschied selbst zu erleben.</p><p>Eine vollständige Liste aller Änderungen und Verbesserungen in Elasticsearch 9.2 finden Sie in den <a href="https://www.elastic.co/guide/en/elasticsearch/reference/9.2/release-notes-9.2.0.html">offiziellen Versionshinweisen</a>. Viel Spaß beim Abfragen!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Erstellung eines ChatGPT-Konnektors mit Elasticsearch zur Abfrage von GitHub-Issues]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie einen benutzerdefinierten ChatGPT-Konnektor erstellen und einen Elasticsearch MCP-Server bereitstellen, der die hybride Suche verwendet, um interne GitHub Issues abzufragen.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/chatgpt-connector-mcp-server-github-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/chatgpt-connector-mcp-server-github-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Tomás Murúa]]></dc:creator>
            <pubDate>Mon, 01 Dec 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/77a6fc1eb263a0eb16aac64f2ecaca5f4ac12ec2-966x568.gif?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Kürzlich hat OpenAI <a href="https://help.openai.com/en/articles/11487775-connectors-in-chatgpt">benutzerdefinierte Konnektoren</a> für ChatGPT für Pro-, Business-, Enterprise- und Edu-Abos angekündigt. Sie ergänzen die vorkonfigurierten Konnektoren zum Abrufen von Daten auf Gmail, GitHub, Dropbox usw. Es ist möglich, mithilfe von MCP-Servern benutzerdefinierte Konnektoren zu erstellen.</p><p>Benutzerdefinierte Konnektoren ermöglichen es Ihnen, Ihre vorhandenen ChatGPT-Konnektoren mit zusätzlichen Datenquellen wie Elasticsearch zu kombinieren, um umfassende Antworten zu erhalten.</p><p>In diesem Artikel werden wir einen <a href="https://modelcontextprotocol.io/docs/getting-started/intro">MCP</a>-Server erstellen, der ChatGPT mit einem Elasticsearch-Index verbindet, der Informationen zu internen GitHub Issues und Pull Requests enthält. So können Abfragen in natürlicher Sprache mit Ihren Elasticsearch-Daten beantwortet werden.</p><p>Wir werden den MCP-Server mithilfe von <a href="https://gofastmcp.com/getting-started/welcome">FastMCP</a> auf Google Colab mit ngrok bereitstellen, um eine öffentliche URL zu erhalten, mit der ChatGPT eine Verbindung herstellen kann. Dadurch entfällt die Notwendigkeit einer komplexen Infrastruktureinrichtung.</p><p>Einen umfassenden Überblick über MCP und sein Ökosystem finden Sie unter <a href="https://www.elastic.co/search-labs/blog/mcp-current-state">„Der aktuelle Stand von MCP“</a>.</p><h2>Voraussetzungen</h2><p>Vor Beginn benötigen Sie Folgendes:</p><ul><li>Elasticsearch-Cluster (8.X oder höher)</li><li>Elasticsearch-API-Schlüssel mit Lesezugriff auf Ihren Index</li><li>Google-Konto (für Google Colab)</li><li>Ngrok-Konto (das kostenlose Abo genügt)</li><li>ChatGPT-Konto mit Pro-, Enterprise-, Business- oder Edu-Plan</li></ul><h2>Die Anforderungen für ChatGPT MCP-Konnektoren verstehen</h2><p>Für die ChatGPT MCP-Konnektoren müssen zwei Tools implementiert werden: <code>search</code> und <code>fetch</code>. Weitere Einzelheiten finden Sie in den <a href="https://platform.openai.com/docs/mcp#create-an-mcp-server">OpenAI Dokumenten</a>.</p><h3><a href="https://platform.openai.com/docs/mcp#search-tool">Suchtool</a></h3><p>Gibt eine Liste relevanter Ergebnisse aus Ihrem Elasticsearch-Index auf der Grundlage einer Benutzerabfrage zurück.</p><h4>Das erhält es:</h4><ul><li>Eine einzelne Zeichenfolge mit der Anfrage des Benutzers in natürlicher Sprache.</li><li>Beispiel: „Finde Probleme im Zusammenhang mit der Elasticsearch-Migration.“</li></ul><h4>Was es zurückgibt: </h4><ul><li>Ein Objekt mit einem <code>result</code>-Schlüssel, der ein Array von result-Objekten enthält. Jedes Ergebnis umfasst:<ul><li><code>id</code> - Eindeutige Dokumentkennung</li><li><code>title</code> - Titel eines Issues oder Pull Requests (PR)</li><li><code>url</code> - Link zum Issue/PR</li></ul></li></ul><h4>In unserer Implementierung:</h4><pre><code>return {
    "results": [
        {
            "id": "PR-612",
            "title": "Fix memory leak in WebSocket notification service",
            "url": "https://internal-git.techcorp.com/pulls/612"
        },
        # ... more results
    ]
}</code></pre><h3><a href="https://platform.openai.com/docs/mcp#fetch-tool">Abrufwerkzeug</a></h3><p>Ruft den vollständigen Inhalt eines bestimmten Dokuments ab.</p><h4>Das erhält es:</h4><ul><li>Eine einzelne Zeichenfolge mit der Elasticsearch-Dokument-ID aus dem Suchergebnis</li><li>Beispiel: „Nenne mir die Details zum PR-578.“</li></ul><h4>Was es zurückgibt:</h4><ul><li>Ein vollständiges Dokumentobjekt mit:<ul><li><code>id</code> - Eindeutige Dokumentkennung</li><li><code>title</code> - Titel eines Issues oder Pull Requests (PR)</li><li><code>text</code> - Vollständige Beschreibung und Details des Problems/PR</li><li><code>url</code> - Link zum Issue/PR</li><li><code>type</code> - Dokumenttyp (Issue, Pull-Request)</li><li><code>status</code> - Aktueller Status (offen, in Bearbeitung, abgeschlossen)</li><li><code>priority</code> - Prioritätsstufe (niedrig, mittel, hoch, kritisch)</li><li><code>assignee</code> - Person, der das Issue/der PR zugewiesen wurde</li><li><code>created_date</code> - Erstellungsdatum</li><li><code>resolved_date</code> - Wann das Issue gelöst wurde (falls zutreffend)</li><li><code>labels</code> - Mit dem Dokument verbundene Tags</li><li><code>related_pr</code> - Verwandte Pull-Request-ID</li></ul></li></ul><pre><code>return {
    "id": "PR-578",
    "title": "Security hotfix: Patch SQL injection vulnerabilities",
    "text": "Description: CRITICAL SECURITY FIX for ISSUE-1889. Patches SQL...",
    "url": "https://internal-git.techcorp.com/pulls/578",
    "type": "pull_request",
    "status": "closed",
    "priority": "critical",
    "assignee": "sarah_dev",
    "created_date": "2025-09-19",
    "resolved_date": "2025-09-19",
    "labels": "security, hotfix, sql",
    "related_pr": null
}</code></pre><p><strong>Hinweis</strong>: In diesem Beispiel wird eine flache Struktur verwendet, bei der sich alle Felder auf der Stammebene befinden. Die Anforderungen von OpenAI sind flexibel und unterstützen auch verschachtelte Metadatenobjekte.</p><h2>Datensatz zu GitHub Issues und Pull Requests</h2><p>Für dieses Tutorial verwenden wir einen internen GitHub Datensatz mit Issues und Pull Requests. Dies stellt ein Szenario dar, in dem Sie private, interne Daten über ChatGPT abfragen möchten.</p><p>Den Datensätze finden Sie <a href="https://gist.github.com/TomasMurua/4e7bbdf7a7ebbdffaa663c43578d934a">hier</a>. Wir werden außerdem den Index der Daten mit der <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-bulk">Bulk-API</a> aktualisieren.</p><p>Dieser Datensatz umfasst:</p><ul><li>Issues mit Beschreibungen, Status, Priorität und Zuweisungen</li><li>Pull-Requests mit Codeänderungen, Bewertungen und Deployment-Informationen</li><li>Beziehungen zwischen Issues und PRs (z. B. PR-578 behebt ISSUE-1889)</li><li>Labels, Daten und andere Metadaten</li></ul><h3>Index-Mappings</h3><p>Der Index verwendet die folgenden <a href="https://www.elastic.co/docs/manage-data/data-store/mapping">Mappings</a>, um die hybride Suche mit <a href="https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-elser">ELSER</a> zu unterstützen. Das <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text">text_semantic</a> wird für die semantische Suche verwendet, während andere Felder die Schlüsselwortsuchen ermöglichen.</p><pre><code>{
  "mappings": {
    "properties": {
      "id": {
        "type": "keyword"
      },
      "title": {
        "type": "text"
      },
      "text": {
        "type": "text"
      },
      "text_semantic": {
        "type": "semantic_text",
        "inference_id": ".elser-2-elasticsearch"
      },
      "url": {
        "type": "keyword"
      },
      "type": {
        "type": "keyword"
      },
      "status": {
        "type": "keyword"
      },
      "priority": {
        "type": "keyword"
      },
      "assignee": {
        "type": "keyword"
      },
      "created_date": {
        "type": "date",
        "format": "iso8601"
      },
      "resolved_date": {
        "type": "date",
        "format": "iso8601"
      },
      "labels": {
        "type": "keyword"
      },
      "related_pr": {
        "type": "keyword"
      }
    }
  }
}</code></pre><h2>Den MCP-Server erstellen</h2><p>Unser MCP-Server implementiert zwei Tools gemäß den OpenAI Spezifikationen und verwendet die hybride Suche, um den semantischen und den textbasierten Abgleich für bessere Ergebnisse zu kombinieren.</p><h3>Suchtool</h3><p>Nutzt hybride Suche mit <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion">RRF</a> (Reciprocal Rank Fusion), die semantische Suche mit Textabgleich kombiniert:</p><pre><code>@mcp.tool()
    async def search(query: str) -> Dict[str, List[Dict[str, Any]]]:
        """
        Search for internal issues and PRs using hybrid search (semantic + text with RRF).
        Returns list with id, title, and url per OpenAI spec.
        """
        if not query or not query.strip():
            return {"results": []}

        logger.info(f"Searching for: '{query}'")

        try:
            # Hybrid search with RRF (Reciprocal Rank Fusion)
            response = es_client.search(
                index=ELASTICSEARCH_INDEX,
                size=10,
                source=["id", "title", "url", "type", "priority"],
                retriever={
                    "rrf": {
                        "retrievers": [
                            {
                                # Semantic search with ELSER
                                "standard": {
                                    "query": {
                                        "semantic": {
                                            "field": "text_semantic",
                                            "query": query
                                        }
                                    }
                                }
                            },
                            {
                                # Text search (BM25) for keyword matching
                                "standard": {
                                    "query": {
                                        "multi_match": {
                                            "query": query,
                                            "fields": [
                                                "title^3",
                                                "text^2",
                                                "assignee^2",
                                                "type",
                                                "labels",
                                                "priority"
                                            ],
                                            "type": "best_fields",
                                            "fuzziness": "AUTO"
                                        }
                                    }
                                }
                            }
                        ],
                        "rank_window_size": 50,
                        "rank_constant": 60
                    }
                }
            )

            results = []
            if response and 'hits' in response:
                for hit in response['hits']['hits']:
                    source = hit['_source']
                    results.append({
                        "id": source.get('id', hit['_id']),
                        "title": source.get('title', 'Unknown'),
                        "url": source.get('url', '')
                    })

            logger.info(f"Found {len(results)} results")
            return {"results": results}

        except Exception as e:
            logger.error(f"Search error: {e}")
            raise ValueError(f"Search failed: {str(e)}")</code></pre><h3>Wichtige Punkte:</h3><ul><li><strong>Hybride Suche mit RRF:</strong> Kombiniert semantische Suche (ELSER) und Text-Suche (BM25) für bessere Ergebnisse.</li><li><strong>Multi-Match-Abfrage:</strong> <a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-multi-match-query">Sucht über mehrere Felder hinweg</a> mit Boosting (title^3, text^2, assignee^2). Das Caret-Symbol (^) multipliziert die Relevanzwerte und priorisiert dabei Treffer in Titeln gegenüber solchen im Inhalt.</li><li><strong>Fuzzy-Matching:</strong> <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/common-options#fuzziness"><code>fuzziness: AUTO</code></a> behebt Tippfehler und Rechtschreibfehler, indem es ungefähre Übereinstimmungen erlaubt.</li><li><strong>RRF-Parameterabstimmung:</strong><ul><li><code>rank_window_size: 50</code> - Legt fest, wie viele Top-Ergebnisse von jedem Retriever (semantische und textbasierte) vor dem Zusammenführen berücksichtigt werden.</li><li><code>rank_constant: 60</code> - Dieser Wert bestimmt, wie viel Einfluss Dokumente in einzelnen Ergebnismengen auf das endgültige Ranking haben.</li></ul></li><li><strong>Gibt nur die erforderlichen Felder zurück:</strong> <code>id</code>, <code>title</code>, <code>url</code> gemäß OpenAI-Spezifikation, und vermeidet das unnötige Freilegen zusätzlicher Felder.</li></ul><h3>Abrufwerkzeug</h3><p>Ruft Dokumentdetails anhand der Dokumenten-ID ab, sofern vorhanden:</p><pre><code>@mcp.tool()
    async def fetch(id: str) -> Dict[str, Any]:
        """
        Retrieve complete issue/PR details by ID.
        Returns id, title, text, url.
        """
        if not id:
            raise ValueError("ID is required")

        logger.info(f"Fetching: {id}")

        try:
            # Search by the 'id' field (not _id) since IDs are stored as a field
            response = es_client.search(
                index=ELASTICSEARCH_INDEX,
                body={
                    "query": {
                        "term": {
                            "id": id  # Search by your custom 'id' field
                        }
                    },
                    "size": 1
                }
            )

            if not response or not response['hits']['hits']:
                raise ValueError(f"Document with id '{id}' not found")

            hit = response['hits']['hits'][0]
            source = hit['_source']

            result = {
                "id": source.get('id', id),
                "title": source.get('title', 'Unknown'),
                "text": source.get('text', ''),
                "url": source.get('url', ''),
                "type": source.get('type', ''),
                "status": source.get('status', ''),
                "priority": source.get('priority', ''),
                "assignee": source.get('assignee', ''),
                "created_date": source.get('created_date', ''),
                "resolved_date": source.get('resolved_date', ''),
                "labels": source.get('labels', ''),
                "related_pr": source.get('related_pr', '')
            }

            logger.info(f"Fetched: {result['title']}")
            return result

        except Exception as e:
            logger.error(f"Fetch error: {e}")
            raise ValueError(f"Failed to fetch '{id}': {str(e)}")</code></pre><h3>Wichtige Punkte:</h3><ul><li><strong>Suchen nach Dokumenten-ID-Feld:</strong> Verwendet eine Begriffsabfrage im benutzerdefinierten <code>id</code>-Feld.</li><li><strong>Vollständige Rückgabe des Dokuments:</strong> Mit vollständigem <code>text</code>-Feld mit allen Inhalten</li><li><strong>Flache Struktur:</strong> Alle Felder auf der Wurzelebene, entsprechend der Dokumentstruktur von Elasticsearch.</li></ul><h2>Bereitstellung auf Google Colab</h2><p>Wir nutzen Google Colab, um unseren MCP-Server zu betreiben, und ngrok, um ihn öffentlich zugänglich zu machen, damit ChatGPT eine Verbindung herstellen kann.</p><h3>Schritt 1: Öffnen Sie das Google Colab Notebook</h3><p>Greifen Sie auf unser vorkonfiguriertes Notebook <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/elasticsearch-chatgpt-connector">Elasticsearch MCP für ChatGPT</a> zu.</p><h3>Schritt 2: Konfigurieren Sie Ihre Anmeldeinformationen</h3><p>Sie benötigen drei Informationen:</p><ul><li><strong>Elasticsearch URL:</strong> Ihre <a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-enterprise/connect-elasticsearch">Elasticsearch-Cluster-URL</a>.</li><li><strong>Elasticsearch API-Schlüssel:</strong> <a href="https://www.elastic.co/docs/deploy-manage/api-keys/elasticsearch-api-keys">API-Schlüssel</a> mit Lesezugriff auf Ihren Index.</li><li><strong>Ngrok-Auth-Token:</strong> Kostenloses Token von <a href="https://ngrok.com/">ngrok</a>. Wir nutzen ngrok, um die MCP-URL im Internet sichtbar zu machen, damit ChatGPT sich damit verbinden kann.</li></ul><h4>So erhalten Sie Ihr ngrok-Token</h4><ol><li>Registrieren Sie sich für ein kostenloses Konto bei <a href="https://ngrok.com/">ngrok</a></li><li>Gehe zu Ihrem <a href="https://dashboard.ngrok.com/">Dashboard</a></li><li>Kopieren Sie Ihr Authentifizierungs-Token</li></ol><h4>Secrets zu Google Colab hinzufügen</h4><p>Im Google Colab-Notizbuch:</p><ol><li>Klicken Sie auf das <strong>Schlüsselsymbol </strong>in der linken Seitenleiste, um <strong>Secrets</strong> zu öffnen.</li><li>Fügen Sie diese drei Secrets hinzu:</li></ol><pre><code>ELASTICSEARCH_URL=https://your-cluster.elastic.com:443
ELASTICSEARCH_API_KEY=your-api-key
NGROK_TOKEN=your-ngrok-token</code></pre><p>3. Aktivieren Sie den Notebook-Zugriff für jedes Secret.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d5dd6ac19fe816a562c6351fdb0f11369da0e877-609x321.jpg" style="width: 100%;" /><h3>Schritt 3: Führen Sie das Notebook aus</h3><ol><li>Klicken Sie auf <strong>Runtime</strong> und dann auf <strong>alle ausführen</strong>, um alle Zellen auszuführen.</li><li>Warten Sie, bis der Server startet (etwa 30 Sekunden)</li><li>Suchen Sie nach der Ausgabe, die Ihre öffentliche ngrok-URL anzeigt.</li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f185100e8869624bc9e1c7b2b4eb32785e2d89e7-1189x283.png" style="width: 100%;" /><p>4. Die Ausgabe sieht in etwa so aus:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e02e625e91ed9136454e4401b184575fb03a336e-1052x465.jpg" style="width: 100%;" /><h2>Mit ChatGPT verbinden</h2><p>Nun verbinden wir den MCP-Server mit Ihrem ChatGPT-Konto.</p><ol><li>Öffnen Sie ChatGPT und gehen Sie zu <strong>Einstellungen</strong>.</li><li>Navigieren Sie zu <strong>Konnektoren. </strong>Wenn Sie ein Pro-Konto nutzen, müssen Sie den <a href="https://platform.openai.com/docs/guides/developer-mode">Entwicklermodus</a> in den Konnektoren aktivieren.</li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/32c02192912fc0e7e5a52e9399077ba7ae3b4901-739x715.png" style="width: 100%;" /><p><em>Wenn Sie ChatGPT Enterprise oder Business verwenden, müssen Sie den Konnektor an Ihrem Workspace veröffentlichen.</em></p><p>3. Klicken Sie auf <strong>Erstellen.</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/15c53e5ccc381108a9dc0052cca05bf0fc97679a-755x683.png" style="width: 100%;" /><p><em><strong>Hinweis</strong>: In Business-, Enterprise- und Edu-Workspaces können nur Workspace-Inhaber, Administratoren sowie Benutzer, bei denen die entsprechende Einstellung aktiviert ist (für Enterprise/Edu), benutzerdefinierte Konnektoren hinzufügen. Benutzer mit der regulären Mitgliederrolle sind nicht berechtigt, selbst benutzerdefinierte Konnektoren hinzuzufügen.</em></p><p><em>Sobald ein Konnektor von einem Benutzer mit der Inhaber- oder Admin-Rolle hinzugefügt und aktiviert wurde, kann er von allen Mitgliedern des Workspaces verwendet werden.</em></p><p>4. Geben Sie die erforderlichen Informationen ein sowie Ihre ngrok-URL, die mit <code>/sse/</code> endet. Beachten Sie den „/“ nach „sse“. Ohne diesen funktioniert es nicht:</p><ul><li><strong>Name:</strong> Elasticsearch MCP</li><li><strong>Beschreibung: </strong>Benutzerdefiniertes MCP zum Suchen und Abrufen von internen GitHub-Informationen.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/162a85705cc8ac48a3f2f665551d513e0719f93d-479x684.png" style="width: 100%;" /><p>5. Klicken Sie auf <strong>Erstellen</strong>, um das benutzerdefinierte MCP zu speichern.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/97eb5fb0a32b86bfadfb35561f698616f217c049-913x629.png" style="width: 100%;" /><p>Die Verbindung ist sofort hergestellt, wenn Ihr Server läuft. Keine zusätzliche Authentifizierung ist erforderlich, da der Elasticsearch-API-Schlüssel auf Ihrem Server konfiguriert ist.</p><h2>Teste den MCP-Server</h2><p>Bevor Sie Fragen stellen, müssen Sie auswählen, welchen Konnektor ChatGPT verwenden soll.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/77a6fc1eb263a0eb16aac64f2ecaca5f4ac12ec2-966x568.gif" style="width: 100%;" /><h3>Aufforderung 1: Nach Problemen suchen</h3><p>Anfrage: „<strong>Finde Probleme im Zusammenhang mit der Elasticsearch-Migration“ </strong>und bestätigen Sie die Aktionen des aufgerufenen Tools.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/cfd781acbff8cd7c8095bbe29224f8b26d581f77-650x375.png" style="width: 100%;" /><p>ChatGPT wird das Tool <code>search</code> mit Ihrer Abfrage aufrufen. Sie können sehen, dass es nach verfügbaren Tools sucht, sich darauf vorbereitet, das Elasticsearch-Tool aufzurufen. Vor jeglichen Aktionen gegenüber dem Tool wird die Bestätigung des Benutzers eingeholt.</p><h4>Anfrage zum Aufruf des Tools:</h4><pre><code>{
  "query": "Elasticsearch migration issues"
}</code></pre><h4>Reaktion des Tools:</h4><pre><code>{
  "results": [
    {
      "id": "PR-598",
      "title": "Elasticsearch 8.x migration - Application code changes",
      "url": "https://internal-git.techcorp.com/pulls/598"
    },
    {
      "id": "ISSUE-1712",
      "title": "Migrate from Elasticsearch 7.x to 8.x",
      "url": "https://internal-git.techcorp.com/issues/1712"
    },
    {
      "id": "RFC-045",
      "title": "Design Proposal: Microservices Migration Architecture",
      "url": "https://internal-git.techcorp.com/rfcs/045"
    }
    // ... 7 more results
  ]
}</code></pre><p>ChatGPT verarbeitet die Ergebnisse und präsentiert sie in einem natürlichen, gesprächsorientierten Format.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9d5b6cff85c7e54ccc2584b8ae96d45495fae8c1-923x1352.png" style="width: 100%;" /><h3>Hinter den Kulissen</h3><h4>Prompt: „Finde Probleme im Zusammenhang mit der Elasticsearch-Migration.“</h4><p>1. ChatGPT-Anrufe <code>search(“Elasticsearch migration”)</code></p><p>2. Elasticsearch führt eine hybride Suche durch.</p><ul><li>Die <strong>semantische Suche</strong> versteht Konzepte wie „Upgrade“ und „<em>Versionskompatibilität“.</em></li><li><strong>Die Textsuche</strong> findet exakte Treffer für „<em>Elasticsearch</em>“ und „Migration“.</li><li><strong>RRF</strong> kombiniert und bewertet Ergebnisse beider Ansätze</li></ul><p>3. Sie gibt die 10 am besten mit <code>id</code>, <code>title</code>, übereinstimmenden Ergebnisse zurück <code>url</code></p><p>4. ChatGPT identifiziert „<em>ISSUE-1712: Migration von Elasticsearch 7.x auf 8.x</em>“ als relevantestes Ergebnis.</p><h3>Prompt 2: Nenne mir die vollständigen Details</h3><p>Anfrage: <strong><em>„Nenne mir die Details zu ISSUE-1889“</em></strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5c0db5245535ce67a36056e61e135bddc97ce496-934x629.png" style="width: 100%;" /><p>ChatGPT erkennt, dass Sie detaillierte Informationen zu einem bestimmten Problem wünschen, ruft das Tool <code>fetch</code> auf holt beim Benutzer die Bestätigung ein, bevor Maßnahmen gegen das Tool ergriffen werden.</p><h4>Anfrage zum Aufruf des Tools:</h4><pre><code>{
  "id": "ISSUE-1889"
}</code></pre><h4>Reaktion des Tools:</h4><pre><code>{
  "id": "ISSUE-1889",
  "title": "SQL injection vulnerability in search endpoint",
  "text": "Description: Security audit identified SQL injection vulnerability in /api/v1/search endpoint. User input from query parameter is not properly sanitized before being used in raw SQL query. Severity: HIGH - Immediate action required Affected Code: - File: services/search/query_builder.py - Line: 145-152 - Issue: String concatenation used instead of parameterized queries Investigation: - @security_team_alice: Confirmed exploitable with UNION-based injection - @sarah_dev: Checking all other endpoints for similar patterns - @john_backend: Found 3 more instances in legacy codebase Remediation: - Rewrite using SQLAlchemy ORM or parameterized queries - Add input validation and sanitization - Implement WAF rules as additional layer - Security regression tests Comments: - @tech_lead_mike: Stop all other work, this is P0 - @sarah_dev: PR-578 ready with fixes for all 4 vulnerable endpoints - @alex_devops: Deployed hotfix to production 2025-09-19 at 14:30 UTC - @security_team_alice: Verified fix, conducting full pentest next week Resolution: All vulnerable endpoints patched. Added pre-commit hooks to catch raw SQL queries. Security training scheduled for team.",
  "url": "https://internal-git.techcorp.com/issues/1889",
  "type": "issue",
  "status": "closed",
  "priority": "critical",
  "assignee": "sarah_dev",
  "created_date": "2025-09-18",
  "resolved_date": "2025-09-19",
  "labels": "security, vulnerability, bug, sql",
  "related_pr": "PR-578"
}</code></pre><p>ChatGPT fasst die Informationen zusammen und präsentiert sie übersichtlich.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/410f19f213e94fc4e3c47eeef6e04b69e0c86159-602x462.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/54d8ffa117628a1e3afc317c3ab75d4f7731d7ab-767x1600.png" style="width: 100%;" /><h3>Hinter den Kulissen</h3><h4>Prompt: „Nenne mir die Details von ISSUE-1889“</h4><ol><li>ChatGPT-Anrufe <code>fetch(“ISSUE-1889”)</code></li><li>Elasticsearch ruft das vollständige Dokument ab</li><li>Gibt ein vollständiges Dokument mit allen Feldern auf der Stammebene zurück</li><li>ChatGPT synthetisiert die Informationen und antwortet mit korrekten Zitaten.</li></ol><h2>Fazit</h2><p>In diesem Artikel haben wir einen maßgeschneiderten MCP-Server erstellt, der ChatGPT mit Elasticsearch über spezielle <strong>Such-</strong> und <strong>Abruf-</strong>-MCP-Tools verbindet und so natürliche Sprachanfragen zu privaten Daten ermöglicht.</p><p>Dieses MCP-Muster funktioniert für jeden Elasticsearch-Index, jede Dokumentation, jedes Produkt, jedes Log oder andere Daten, die Sie über natürliche Sprache abfragen möchten.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Entwicklung eines agentenbasierten RAG-Assistenten mit LangChain und Elasticsearch]]></title>
            <description><![CDATA[Lerne, wie du mit LangChain und Elasticsearch einen agentenbasierten Nachrichtenassistenten entwickelst, der Anfragen zu Artikeln mit adaptivem Routing beantwortet.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/agentic-rag-news-assistant-langchain-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/agentic-rag-news-assistant-langchain-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Kirti Sodhi]]></dc:creator>
            <pubDate>Fri, 28 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d7213956309c6b5863627aca2ba95569b02fc4c7-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Dieser Blogbeitrag befasst sich eingehend mit agentenbasierten RAG-Workflows und erläutert deren Hauptmerkmale und gängige Designmuster. Des Weiteren wird anhand eines praktischen Beispiels, das Elasticsearch als Vektorspeicher und LangChain zum Aufbau des agentenbasierten RAG-Frameworks verwendet, demonstriert, wie diese Arbeitsabläufe implementiert werden können. Abschließend werden in dem Artikel kurz bewährte Verfahren und Herausforderungen im Zusammenhang mit der Entwicklung und Implementierung solcher Architekturen erörtert. Sie können anhand dieses <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/agentic-rag/agent_rag_news_assistant.ipynb">Jupyter-Notebooks</a> eine einfache agentenbasierte RAG-Pipeline erstellen.</p><h2>Einführung in agentische RAG</h2><p>Retrieval Augmented Generation (<a href="https://www.elastic.co/docs/solutions/search/rag">RAG</a>) hat sich zu einem Eckpfeiler in LLM-basierten Anwendungen entwickelt und ermöglicht es Modellen, optimale Antworten zu liefern, indem sie relevanten Kontext auf der Grundlage von Benutzeranfragen abrufen. RAG-Systeme verbessern die Genauigkeit und den Kontext von LLM-Antworten, indem sie auf externe Informationen aus APIs oder Datenspeichern zurückgreifen, anstatt sich auf vorab trainiertes LLM-Wissen zu beschränken. Die KI-Agenten hingegen agieren autonom, treffen Entscheidungen und ergreifen Maßnahmen, um ihre vorgegebenen Ziele zu erreichen.</p><p>Agentic RAG ist ein Framework, das die Stärken sowohl der abrufgestützten Generierung als auch des agentenbasierten Schließens vereint. Es integriert RAG in den Entscheidungsprozess des Agenten und ermöglicht dem System so, Datenquellen dynamisch auszuwählen, Abfragen für einen besseren Kontextabruf zu verfeinern, genauere Antworten zu generieren und eine Rückkopplungsschleife anzuwenden, um die Ausgabequalität kontinuierlich zu verbessern.</p><h2>Hauptmerkmale von agentic RAG</h2><p>Das agentenbasierte RAG-Framework stellt einen bedeutenden Fortschritt gegenüber traditionellen RAG-Systemen dar. Anstatt einem festgelegten Abrufprozess zu folgen, nutzt es dynamische Agenten, die in der Lage sind, Ergebnisse in Echtzeit zu planen, auszuführen und zu optimieren.</p><p>Betrachten wir einige der wichtigsten Merkmale, die agentenbasierte RAG-Pipelines auszeichnen:</p><ul><li><strong>Dynamische Entscheidungsfindung</strong>: Agentic RAG verwendet einen Schlussfolgerungsmechanismus, um die Absicht des Benutzers zu verstehen und jede Anfrage an die relevanteste Datenquelle weiterzuleiten, wodurch genaue und kontextbezogene Antworten erzeugt werden.</li><li><strong>Umfassende Abfrageanalyse:</strong> Agentic RAG analysiert Benutzerabfragen eingehend, einschließlich Unterfragen und deren Gesamtabsicht. Es bewertet die Komplexität der Anfrage und wählt dynamisch die relevantesten Datenquellen aus, um Informationen abzurufen und so genaue und vollständige Antworten zu gewährleisten.</li><li><strong>Mehrstufige Zusammenarbeit</strong>: Dieses Framework ermöglicht eine mehrstufige Zusammenarbeit durch ein Netzwerk spezialisierter Agenten. Jeder Agent bearbeitet einen bestimmten Teil eines größeren Ziels und arbeitet dabei sequenziell oder gleichzeitig, um ein zusammenhängendes Ergebnis zu erzielen.</li><li><strong>Selbstbewertungsmechanismen</strong>: Die agentenbasierte RAG-Pipeline nutzt Selbstreflexion, um abgerufene Dokumente und generierte Antworten zu bewerten. Es kann prüfen, ob die abgerufenen Informationen die Anfrage vollständig beantworten, und anschließend die Ausgabe auf Richtigkeit, Vollständigkeit und sachliche Konsistenz überprüfen.</li><li><strong>Integration mit externen Tools</strong>: Dieser Workflow kann mit externen APIs, Datenbanken und Echtzeit-Informationsquellen interagieren, aktuelle Informationen einbeziehen und sich dynamisch an sich verändernde Daten anpassen.</li></ul><h2>Workflow-Muster von agentischen RAG</h2><p>Die Workflow-Muster definieren, wie agentenbasierte KI LLM-basierte Anwendungen zuverlässig und effizient strukturiert, verwaltet und orchestriert. Zur Implementierung dieser agentenbasierten Arbeitsabläufe können verschiedene Frameworks und Plattformen wie <a href="https://www.langchain.com/">LangChain</a>, <a href="https://www.langchain.com/langgraph">LangGraph</a>, <a href="https://www.crewai.com/">CrewAI</a> und <a href="https://www.llamaindex.ai/">LlamaIndex</a> verwendet werden.</p><ol><li><strong>Sequenzielle Abrufkette</strong>: Sequenzielle Arbeitsabläufe unterteilen komplexe Aufgaben in einfache, geordnete Schritte. Jeder Schritt verbessert die Ausgangslage für den nächsten, was zu besseren Ergebnissen führt. Wenn beispielsweise ein Kundenprofil erstellt wird, ruft ein Mitarbeiter grundlegende Daten aus einem CRM-System ab, ein anderer die Kaufhistorie aus einer Transaktionsdatenbank, und ein letzter Mitarbeiter kombiniert diese Informationen, um ein vollständiges Profil für Empfehlungen oder Berichte zu erstellen.</li><li><strong>Routing-Abrufkette</strong>: In diesem Workflow-Muster analysiert ein Router-Agent die Eingabe und leitet sie an den am besten geeigneten Prozess oder die am besten geeignete Datenquelle weiter. Dieser Ansatz ist besonders effektiv, wenn mehrere unterschiedliche Datenquellen mit minimaler Überschneidung vorliegen. In einem Kundenservicesystem kategorisiert beispielsweise der Router-Agent eingehende Anfragen wie technische Probleme, Rückerstattungen oder Beschwerden und leitet sie zur effizienten Bearbeitung an die zuständige Abteilung weiter.</li><li><strong>Parallele Abrufkette</strong>: Bei diesem Workflow-Muster werden mehrere unabhängige Teilaufgaben gleichzeitig ausgeführt, und ihre Ergebnisse werden später zusammengeführt, um eine endgültige Antwort zu generieren. Dieser Ansatz reduziert die Bearbeitungszeit erheblich und erhöht die Effizienz des Arbeitsablaufs. In einem parallelen Arbeitsablauf im Kundenservice ruft beispielsweise ein Mitarbeiter ähnliche, frühere Anfragen ab, während ein anderer Mitarbeiter relevante Artikel in der Wissensdatenbank konsultiert. Ein Aggregator kombiniert diese Ausgaben dann zu einer umfassenden Auflösung.</li><li><strong>Orchestrator-Worker-Kette</strong>: Dieser Workflow weist aufgrund der Verwendung unabhängiger Teilaufgaben Ähnlichkeiten mit der Parallelisierung auf. Ein wesentlicher Unterschied besteht jedoch in der Integration eines Orchestrator-Agenten. Dieser Agent ist dafür zuständig, Benutzeranfragen zu analysieren, sie während der Laufzeit dynamisch in Teilaufgaben zu unterteilen und die geeigneten Prozesse oder Werkzeuge zu identifizieren, die zur Formulierung einer genauen Antwort erforderlich sind.</li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ece6fc2403f211556c93e99d5227bfb7053b0c31-1600x1047.png" style="width: 100%;" /><h2>Aufbau einer agentenbasierten RAG-Pipeline von Grund auf</h2><p>Um die Prinzipien von agentic RAG zu veranschaulichen, entwerfen wir einen Workflow mit LangChain und Elasticsearch. Dieser Workflow verwendet eine routingbasierte Architektur, bei der mehrere Agenten zusammenarbeiten, um Anfragen zu analysieren, relevante Informationen abzurufen, Ergebnisse auszuwerten und kohärente Antworten zu generieren. Sie können dieses <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/agentic-rag/agent_rag_news_assistant.ipynb">Jupyter-Notebook</a> als Referenz verwenden, um diesem Beispiel zu folgen.</p><p>Der Workflow beginnt mit dem Router-Agenten, der die Anfrage des Benutzers analysiert, um die optimale Abrufmethode auszuwählen, d. h. entweder einen <code>vectorstore</code>, <code>websearch</code>, oder einen <code>composite</code> Ansatz. Der Vektorspeicher übernimmt die traditionelle RAG-basierte Dokumentenabfrage, die Websuche ruft die aktuellsten Informationen ab, die nicht im Vektorspeicher gespeichert sind, und der kombinierte Ansatz vereint beide, wenn Informationen aus mehreren Quellen benötigt werden.</p><p>Wenn die Dokumente als geeignet erachtet werden, generiert der Zusammenfassungsagent eine klare und kontextbezogene Antwort. Sind die Dokumente jedoch unzureichend oder irrelevant, formuliert der Abfrageumschreibungsagent die Abfrage neu, um die Suche zu verbessern. Diese überarbeitete Abfrage initiiert dann den Routing-Prozess erneut, wodurch das System seine Suche verfeinern und das Endergebnis verbessern kann.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ed8701a7f15558fbf2e967a884b3e770eccb826b-1256x1092.png" style="width: 100%;" /><h3>Voraussetzungen</h3><p>Dieser Workflow benötigt zur effektiven Ausführung des Beispiels die folgenden Kernkomponenten:</p><ul><li>Python 3.10</li><li><a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/agentic-rag/agent_rag_news_assistant.ipynb">Jupyter Notebook</a></li><li>Azure OpenAI</li><li>Elasticsearch</li><li>LangChain</li></ul><p>Bevor Sie fortfahren, werden Sie aufgefordert, die folgenden erforderlichen Umgebungsvariablen für dieses Beispiel zu konfigurieren.</p><pre><code>AZURE_OPENAI_ENDPOINT="Add your azure openai endpoint"
AZURE_OPENAI_KEY="Add your azure openai key"
AZURE_OPENAI_DEPLOYMENT="gpt-4.1"
AZURE_OPENAI_API_VERSION="Add your azure openai api version"

ES_ENDPOINT = "Add your Elasticsearch ENDPOINT"
ES_API_KEY = "Add your Elasticsearch API KEY"</code></pre><h3>Datenquellen</h3><p>Dieser Arbeitsablauf wird anhand eines Teildatensatzes von AG News veranschaulicht. Der Datensatz umfasst Nachrichtenartikel aus verschiedenen Kategorien, wie etwa Internationales, Sport, Wirtschaft und Wissenschaft/Technologie.</p><pre><code>dataset = load_dataset("ag_news", split="train[:1000]")
docs = [
    Document(
        page_content=sample["text"],
        metadata={"category": sample["label"]}
    )
    for sample in dataset
]</code></pre><p>Das <a href="https://python.langchain.com/docs/integrations/vectorstores/elasticsearch/">ElasticsearchStore-Modul</a> wird ab <code>langchain_elasticsearch</code> als unser Vektorspeicher verwendet. Für den Datenabruf implementieren wir die SparseVectorStrategy unter Verwendung <a href="https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-elser">von ELSER</a>, dem proprietären Einbettungsmodell von Elastic. Es ist unerlässlich, sicherzustellen, dass das ELSER-Modell in Ihrer Elasticsearch-Umgebung korrekt installiert und bereitgestellt ist, bevor Sie den Vektorspeicher initialisieren.</p><pre><code>elastic_vectorstore = ElasticsearchStore.from_documents(
    docs,
    es_url=ES_ENDPOINT,
    es_api_key=ES_API_KEY,
    index_name=index_name,
    strategy=SparseVectorStrategy(model_id=".elser_model_2"),
)

elastic_vectorstore.client.indices.refresh(index=index_name)</code></pre><p>Die Web-Suchfunktion wird mithilfe von <a href="https://python.langchain.com/api_reference/community/tools/langchain_community.tools.ddg_search.tool.DuckDuckGoSearchRun.html">DuckDuckGoSearchRun</a> aus den LangChain-Community-Tools implementiert, wodurch das System effizient Live-Informationen aus dem Web abrufen kann. Sie können auch die Verwendung anderer Such-APIs in Betracht ziehen, die möglicherweise relevantere Ergebnisse liefern. Dieses Tool wurde gewählt, da es Suchvorgänge ohne API-Schlüssel ermöglicht.</p><pre><code>duckduckgo = DuckDuckGoSearchRun(description= "A custom DuckDuckGo search tool for finding latest news stories.", verbose=True)
def websearch_retriever(query):
    results = duckduckgo.run(f"{query}")
    return results</code></pre><p>Der Composite Retriever ist für Abfragen konzipiert, die eine Kombination von Datenquellen erfordern. Es dient dazu, eine umfassende und kontextbezogene Antwort zu liefern, indem gleichzeitig Echtzeitdaten aus dem Web abgerufen und historische Nachrichten aus dem Vektorspeicher konsultiert werden.</p><pre><code>def composite_retriever(query):
    related_docs = vectorstore_retriever(query)
    related_docs += websearch_retriever(query)
    return related_docs</code></pre><h3>Einrichtung der Agenten</h3><p>Im nächsten Schritt werden die LLM-Agenten so definiert, dass sie innerhalb dieses Arbeitsablaufs Denk- und Entscheidungsfähigkeiten bereitstellen. Die von uns erstellten LLM-Ketten umfassen: <code>router_chain</code>, <code>grade_docs_chain</code>, <code>rewrite_query_chain</code>, und <code>summary_chain</code>.</p><p>Der Router-Agent verwendet einen LLM-Assistenten, um zur Laufzeit die am besten geeignete Datenquelle für eine gegebene Abfrage zu ermitteln. Der Bewertungsagent prüft die abgerufenen Dokumente auf Relevanz. Werden die Dokumente als relevant erachtet, werden sie an den Zusammenfassungsagenten weitergeleitet, um eine Zusammenfassung zu erstellen. Andernfalls formuliert der Rewrite-Abfrageagent die Abfrage neu und sendet sie zur erneuten Abfrage an den Routing-Prozess zurück. Die Anweisungen für alle Agenten finden Sie im Abschnitt „LLM-Ketten“ des <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/agentic-rag/agent_rag_news_assistant.ipynb">Notizbuchs</a>.</p><pre><code>class RouteQuery(BaseModel):
    datasource: Literal["vectorstore", "websearch", "composite"] = Field(
        ...,
        description="Choose to route the query to web search, vectorstore or composite."
    )

router_prompt = ChatPromptTemplate.from_template("""You are an assistant that decides the best data source for questions based on news articles.
Choose one of the following options:
- 'vectorstore': for general, background, or historical news articles.
- 'websearch': for recent discoveries, 'latest', 'current', or '2025' type queries.
- 'composite': when the question needs both historical and current knowledge on news articles.

Question: {query}

Return one word: 'vectorstore', 'websearch', or 'composite'.
""")
router_structured = llm.with_structured_output(RouteQuery)
router_chain: RunnableSequence = router_prompt | router_structured</code></pre><p>Die <code>llm.with_structured_output</code> -Klausel sorgt dafür, dass die Ausgabe des Modells einem vordefinierten Schema folgt, das vom BaseModel unter der Klasse <code>RouteQuery</code> definiert wird, und gewährleistet so die Konsistenz der Ergebnisse. Die zweite Zeile bildet ein <code>RunnableSequence</code> , indem sie <code>router_prompt</code> mit <code>router_structured</code> verbindet und so eine Pipeline erzeugt, in der die Eingabeaufforderung vom Sprachmodell verarbeitet wird, um strukturierte, schemakonforme Ergebnisse zu erzeugen.</p><h3>Graphknoten definieren</h3><p>Dieser Teil beinhaltet die Definition der Zustände des Graphen, welche die Daten darstellen, die zwischen verschiedenen Komponenten des Systems fließen. Eine klare Spezifikation dieser Zustände stellt sicher, dass jeder Knoten im Workflow weiß, auf welche Informationen er zugreifen und welche er aktualisieren kann.</p><pre><code>class RAGState(TypedDict):
    query: str
    docs: List[Document]
    router: str
    summary: str
    self_reflection: bool
    retry_count: int = 0</code></pre><p>Sobald die Zustände definiert sind, besteht der nächste Schritt darin, die Knoten des Graphen zu definieren. Knoten sind wie die funktionalen Einheiten des Graphen, die spezifische Operationen an den Daten durchführen. Unsere Pipeline besteht aus 7 verschiedenen Knoten.</p><pre><code>def router(state: RAGState):
   router = router_chain.invoke({'query': state["query"]})
   logger.info(f"Router selected the datasource: {router.datasource}")
   logger.info(f"User query: {state['query']}")
   return {"router": router.datasource}

def vectorstore(state: RAGState):
   return {"docs": vectorstore_retriever(state["query"])}

def websearch(state: RAGState):
   return {"docs": websearch_retriever(state["query"])}

def composite(state: RAGState):
   return {"docs": composite_retriever(state["query"])}

def self_reflection(state: RAGState):
   evaluation = grade_docs_chain.invoke(
       {"query": state["query"], "docs": state["docs"]}
   )
   if evaluation.binary_score:
       logger.info(f"Self-reflection passed -- binary_score={evaluation.binary_score}")
   else:
       logger.info(f"Self-reflection failed -- binary_score={evaluation.binary_score}")

   return {
       "self_reflection": evaluation.binary_score,
   }

def query_rewriter(state: RAGState):
   retry_count = state.get("retry_count", 0) + 1
   new_query = rewrite_query_chain.invoke({"query": state["query"]})
   logger.info(f"Query rewritten: {new_query}, retry_count: {retry_count}")
   return {
       "query": new_query,
       "retry_count": retry_count,
   }

def summarize(state: RAGState):
   summary = summarize_chain.run(
       query=state["query"],
       docs=state["docs"],
   )
   return {"summary": summary}</code></pre><p>Der <code>query_rewriter</code> -Knoten erfüllt im Arbeitsablauf zwei Zwecke. Zunächst wird die Benutzeranfrage mithilfe von <code>rewrite_query_chain</code> umgeschrieben, um die Trefferquote zu verbessern, wenn die vom Selbstreflexionsagenten ausgewerteten Dokumente als unzureichend oder irrelevant erachtet werden. Zweitens fungiert es als Zähler, der verfolgt, wie oft die Abfrage neu geschrieben wurde.</p><p>Bei jedem Aufruf des Knotens wird der im Workflow-Status gespeicherte Wert <code>retry_count</code> erhöht. Dieser Mechanismus verhindert, dass der Arbeitsablauf in eine Endlosschleife gerät. Wenn der Wert <code>retry_count</code> einen vordefinierten Schwellenwert überschreitet, kann das System auf einen Fehlerzustand, eine Standardantwort oder eine andere von Ihnen gewählte vordefinierte Bedingung zurückgreifen.</p><h3>Zusammenstellen des Graphen</h3><p>Im letzten Schritt werden die Kanten des Graphen definiert und gegebenenfalls notwendige Bedingungen hinzugefügt, bevor er kompiliert wird. Jeder Graph muss von einem festgelegten Startknoten ausgehen, der als Einstiegspunkt für den Workflow dient. Die Kanten im Graphen stellen den Datenfluss zwischen den Knoten dar und können zweierlei Art sein:</p><ul><li>Gerade Kanten: Diese definieren einen direkten, bedingungslosen Fluss von einem Knoten zum anderen. Sobald der erste Knoten seine Aufgabe abgeschlossen hat, fährt der Workflow automatisch mit dem nächsten Knoten entlang der geraden Kante fort.</li><li>Bedingte Kanten: Diese ermöglichen es dem Workflow, sich basierend auf dem aktuellen Zustand oder den Ergebnissen der Berechnung eines Knotens zu verzweigen. Der nächste Knoten wird dynamisch anhand von Bedingungen wie Auswertungsergebnissen, Routing-Entscheidungen oder Wiederholungsanzahl ausgewählt.</li></ul><pre><code>graph.add_edge(START, "router")

def after_router(state: RAGState):
   route = state.get("router", None)
   if route == "vectorstore":
       return "vectorstore"
   elif route == "websearch":
       return "websearch"
   else:
       return "composite"

def after_self_reflection(state: RAGState):
   if state["self_reflection"]:
           return "summarize"
   return "query_rewriter"

def after_query_rewriter(state: RAGState):
   while state['retry_count'] <= 3:
           return "router"
   raise RuntimeError("Maximum retries (3) reached -- evaluation failed.")

graph.add_conditional_edges(
   "router",
   after_router,
   {
       "vectorstore": "vectorstore",
       "websearch": "websearch",
       "composite": "composite"
   }
)

graph.add_edge("vectorstore", "self_reflection")
graph.add_edge("websearch", "self_reflection")
graph.add_edge("composite", "self_reflection")
graph.add_conditional_edges(
   "self_reflection",
   after_self_reflection,
   {
       "summarize": "summarize",
       "query_rewriter": "query_rewriter"
   }
)
graph.add_conditional_edges("query_rewriter", after_query_rewriter, {"router": "router"})
graph.add_edge("summarize", END)
agent=graph.compile()</code></pre><p>Damit ist Ihre erste agentenbasierte RAG-Pipeline einsatzbereit und kann mithilfe des kompilierten Agenten getestet werden.</p><pre><code>result = agent.invoke({"query": query1})
logger.info(f"\nFinal Summary:\n: {result['summary']}")</code></pre><h3>Testen der agentischen RAG-Pipeline</h3><p>Wir werden diese Pipeline nun anhand von drei verschiedenen Abfragetypen testen, wie unten dargestellt. Beachten Sie, dass die Ergebnisse unterschiedlich ausfallen können und die unten aufgeführten Beispiele nur ein mögliches Ergebnis veranschaulichen.</p><pre><code>query1="What are the latest AI models released this month?"
query2="What technological innovations are discussed in Sci/Tech news?"
query3="Compare a Sci/Tech article from the dataset with a current web article about AI trends."</code></pre><p>Bei der ersten Abfrage wählt der Router <code>websearch</code> als Datenquelle aus. Die Anfrage besteht die Selbstreflexionsprüfung nicht und wird anschließend zur Anfrageumschreibungsphase weitergeleitet, wie in der Ausgabe gezeigt.</p><pre><code>INFO     | __main__:router:11 - Router selected the datasource: websearch
INFO     | __main__:router:12 - User query: What are the latest AI models released this month?
Latest Singapore news, including the city state's relationships with Malaysia and Mahathir, China and Xi Jinping, and the rest of Southeast Asia. 3 days ago · The latest military news, insights and analysis from China. All the latest news, opinions and analysis on Hong Kong, China, Asia and around the world Latest news, in-depth features and opinion on Malaysia, covering politics, economy, society and the Asean member-nation's relationships with China, Singapore, and other Southeast Asian ... Oct 12, 2025 · Brics (an acronym for Brazil, Russia, India, China and South Africa) refers to an association of 10 leading emerging markets. The other member states are Egypt, Ethiopia, ...
INFO     | __main__:self_reflection:31 - Self-reflection failed -- binary_score=False
INFO     | __main__:query_rewriter:40 - Query rewritten: query='Which AI models have been officially released in June 2024?', retry_count: 1
INFO     | __main__:router:11 - Router selected the datasource: websearch
INFO     | __main__:router:12 - User query: query='Which AI models have been officially released in June 2024?'
Dream Machine is a text-to-video model created by Luma Labs and launched in June 2024 . It generates video output based on user prompts or still images. Dream Machine has been noted for its ability to realistically capture motion... Released in June 2023. In June 2024 , Baidu announced Ernie 4.0 Turbo. In April 2025, Ernie 4.5 Turbo and X1 Turbo were released . These models are optimized for faster response times and lower operational costs.[28][29]. The meaning of QUERY is question, inquiry. How to use query in a sentence. Synonym Discussion of Query. QUERY definition: 1. a question, often expressing doubt about something or looking for an answer from an authority.... Learn more. Query definition: a question; an inquiry.. See examples of QUERY used in a sentence.
INFO     | __main__:self_reflection:29 - Self-reflection passed -- binary_score=True
INFO     | __main__:<module>:2 - 
Final Summary:
: In June 2024, two AI models were officially released: Dream Machine, a text-to-video model launched by Luma Labs, and Ernie 4.0 Turbo, announced by Baidu, which is optimized for faster response times and lower operational costs.</code></pre><p>Als nächstes betrachten wir ein Beispiel, bei dem die <code>vectorstore</code> -Suche verwendet wird, was anhand der zweiten Abfrage demonstriert wird.</p><pre><code>INFO     | __main__:router:11 - Router selected the datasource: vectorstore
INFO     | __main__:router:12 - User query: What technological innovations are discussed in Sci/Tech news?
INFO     | __main__:self_reflection:29 - Self-reflection passed -- binary_score=True
INFO     | __main__:<module>:2 - 
Final Summary:
: Recent Sci/Tech news highlights several technological innovations: NASA is collaborating with Silicon Valley firms to build a powerful Linux-based supercomputer to support theoretical research and shuttle engineering; new chromatin transfer techniques have enabled the cloning of cats; cybersecurity advancements are being discussed in relation to protecting personal technology; Princeton University scientists assert that existing technologies can be used immediately to stabilize global warming; and a set of GameBoy micro-games has been recognized for innovation in game design.</code></pre><p>Die letzte Anfrage wird an die zusammengesetzte Abfrage gerichtet, die sowohl den Vektorspeicher als auch die Websuche nutzt.</p><pre><code>INFO     | __main__:router:11 - Router selected the datasource: composite
INFO     | __main__:router:12 - User query: Compare a Sci/Tech article from the dataset with a current web article about AI trends.
Atlas currently only available on macOS, built on Chromium with planned features like ad-blocking still in development. OpenAI's Atlas browser launched with bold promises of AI -powered web browsing, but early real-world testing reveals a different story. Career-long data are updated to end-of-2024 and single recent year data pertain to citations received during calendar year 2024. The selection is based on the top 100,000 scientists by c-score (with and without self-citations) or a percentile rank of 2% or above in the sub-field. In this article I list 45 AI tools across 21 different categories. After exploring all the available options in each category, I've carefully selected the best tools based on my personal experience. Reading a complex technical article ? Simply highlight confusing terminology and ask "what's this?" to receive instant explanations. compare browsers. Comparison showing traditional browser navigation versus OpenAI Atlas AI -powered workflows. After putting Gemini, ChatGPT, Grok, and DeepSeek through rigorous testing in October 2025, it's clear that there isn't one AI that reigns supreme across all categories.
INFO     | __main__:self_reflection:29 - Self-reflection passed -- binary_score=True
INFO     | __main__:<module>:2 - 
Final Summary:
: A Sci/Tech article from the dataset highlights NASA's development of robust artificial intelligence software for planetary rovers, aiming to make them more self-reliant and capable of decision-making during missions. In contrast, a current web article about AI trends focuses on the proliferation of AI-powered tools across various categories, including browsers like OpenAI Atlas, and compares leading models such as Gemini, ChatGPT, Grok, and DeepSeek, noting that no single AI currently excels in all areas. While the NASA article emphasizes specialized AI applications for autonomous robotics in space exploration, the current trends article showcases the broadening impact of AI across consumer and professional technologies, with ongoing competition and rapid innovation among major AI platforms.</code></pre><p>Im oben beschriebenen Workflow ermittelt agentic RAG intelligent, welche Datenquelle beim Abruf von Informationen für eine Benutzeranfrage verwendet werden soll, wodurch die Genauigkeit und Relevanz der Antwort verbessert wird. Sie können zusätzliche Beispiele erstellen, um den Agenten zu testen und die Ausgaben zu überprüfen, um zu sehen, ob sie interessante Ergebnisse liefern.</p><h2>Bewährte Verfahren zum Erstellen agentenbasierter RAG-Workflows</h2><p>Nachdem wir nun verstanden haben, wie agentic RAG funktioniert, schauen wir uns einige Best Practices für die Erstellung dieser Workflows an. Die Einhaltung dieser Richtlinien trägt dazu bei, dass das System effizient und wartungsfreundlich bleibt.</p><ul><li><strong>Bereiten Sie sich auf Ausweichlösungen vor</strong>: Planen Sie im Voraus Ausweichstrategien für Szenarien, in denen ein Schritt des Arbeitsablaufs fehlschlägt. Dies kann die Rückgabe von Standardantworten, das Auslösen von Fehlerzuständen oder die Verwendung alternativer Tools umfassen. Dadurch wird sichergestellt, dass das System Fehler reibungslos behebt, ohne den Gesamt-Workflow zu unterbrechen.</li><li><strong>Implementieren Sie eine umfassende Protokollierung</strong>: Versuchen Sie, in jeder Phase des Workflows eine Protokollierung zu implementieren, z. B. bei Wiederholungsversuchen, generierten Ausgaben, Routing-Entscheidungen und Abfrageumschreibungen. Diese Protokolle tragen dazu bei, die Transparenz zu verbessern, das Debuggen zu vereinfachen und die Eingabeaufforderungen, das Verhalten der Agenten und die Abrufstrategien im Laufe der Zeit zu verfeinern.</li><li><strong>Wählen Sie das passende Workflow-Muster</strong>: Analysieren Sie Ihren Anwendungsfall und wählen Sie das Workflow-Muster, das Ihren Bedürfnissen am besten entspricht. Nutzen Sie sequentielle Arbeitsabläufe für schrittweise Schlussfolgerungen, parallele Arbeitsabläufe für unabhängige Datenquellen und Orchestrator-Worker-Muster für Abfragen mit mehreren Tools oder komplexen Abfragen.</li><li><strong>Evaluierungsstrategien einbeziehen</strong>: Evaluierungsmechanismen in verschiedenen Phasen des Arbeitsablaufs integrieren. Dies können Selbstreflexionsagenten, die Bewertung abgerufener Dokumente oder automatisierte Qualitätskontrollen sein. Die Auswertung hilft dabei zu überprüfen, ob die abgerufenen Dokumente relevant sind, die Antworten korrekt sind und alle Teile einer komplexen Anfrage berücksichtigt werden.</li></ul><h2>Herausforderungen</h2><p>Während agentenbasierte RAG-Systeme hinsichtlich Anpassungsfähigkeit, Präzision und dynamischem Denken erhebliche Vorteile bieten, bringen sie auch gewisse Herausforderungen mit sich, die während ihrer Entwurfs- und Implementierungsphase bewältigt werden müssen. Zu den wichtigsten Herausforderungen gehören:</p><ul><li><strong>Komplexe Arbeitsabläufe</strong>: Mit zunehmender Anzahl von Agenten und Entscheidungspunkten wird der gesamte Arbeitsablauf immer komplexer. Dies kann zu einer höheren Wahrscheinlichkeit von Fehlern oder Ausfällen zur Laufzeit führen. Priorisieren Sie nach Möglichkeit optimierte Arbeitsabläufe, indem Sie redundante Agenten und unnötige Entscheidungspunkte eliminieren.</li><li><strong>Skalierbarkeit</strong>: Die Skalierung agentenbasierter RAG-Systeme zur Bewältigung großer Datensätze und hoher Abfragevolumina kann eine Herausforderung darstellen. Um die Leistungsfähigkeit auch bei großem Umfang aufrechtzuerhalten, sollten effiziente Indexierungs-, Caching- und verteilte Verarbeitungsstrategien integriert werden.</li><li><strong>Orchestrierung und Rechenaufwand</strong>: Die Ausführung von Arbeitsabläufen mit mehreren Agenten erfordert eine fortgeschrittene Orchestrierung. Dies umfasst eine sorgfältige Terminplanung, das Management von Abhängigkeiten und die Koordination der Agenten, um Engpässe und Konflikte zu vermeiden, die alle zur Gesamtkomplexität des Systems beitragen.</li><li><strong>Evaluierungskomplexität</strong>: Die Evaluierung dieser Arbeitsabläufe birgt inhärente Herausforderungen, da jede Phase eine eigene Bewertungsstrategie erfordert. Beispielsweise muss die RAG-Phase hinsichtlich der Relevanz und Vollständigkeit der abgerufenen Dokumente bewertet werden, während die generierten Zusammenfassungen auf Qualität und Genauigkeit geprüft werden müssen. Ebenso erfordert die Effektivität der Abfrageumformulierung eine separate Auswertungslogik, um festzustellen, ob die umgeschriebene Abfrage die Suchergebnisse verbessert.</li></ul><h2>Fazit</h2><p>In diesem Blogbeitrag haben wir das Konzept des agentischen RAG vorgestellt und hervorgehoben, wie es das traditionelle RAG-Framework durch die Einbeziehung autonomer Fähigkeiten aus der agentischen KI erweitert. Wir untersuchten die Kernfunktionen von agentic RAG und demonstrierten diese Funktionen anhand eines praktischen Beispiels, indem wir einen Nachrichtenassistenten mit Elasticsearch als Vektorspeicher und LangChain zur Erstellung des agentic Frameworks entwickelten.</p><p>Darüber hinaus erörterten wir bewährte Vorgehensweisen und wichtige Herausforderungen, die bei der Konzeption und Implementierung einer agentenbasierten RAG-Pipeline zu berücksichtigen sind. Diese Erkenntnisse sollen Entwicklern als Leitfaden dienen, um robuste, skalierbare und effiziente agentenbasierte Systeme zu erstellen, die Abruf, Schlussfolgerung und Entscheidungsfindung effektiv kombinieren.</p><h2>Was kommt als Nächstes?</h2><p>Der von uns entwickelte Workflow ist einfach gehalten und bietet viel Raum für Verbesserungen und Experimente. Dies lässt sich verbessern, indem wir mit verschiedenen Einbettungsmodellen experimentieren und die Abrufstrategien verfeinern. Darüber hinaus könnte die Integration eines Re-Ranking-Agenten zur Priorisierung der abgerufenen Dokumente von Vorteil sein. Ein weiteres Forschungsfeld umfasst die Entwicklung von Evaluierungsstrategien für agentenbasierte Frameworks, insbesondere die Identifizierung gemeinsamer und wiederverwendbarer Ansätze, die für verschiedene Framework-Typen anwendbar sind. Abschließend werden diese Frameworks an großen und komplexeren Datensätzen erprobt.</p><p>Sollten Sie in der Zwischenzeit ähnliche Experimente durchgeführt haben, würden wir uns freuen, davon zu hören! Geben Sie uns gerne Feedback oder treten Sie über unseren <a href="https://ela.st/slack">Community-Slack-Kanal</a> oder <a href="https://discuss.elastic.co/c/security">unsere Diskussionsforen</a> mit uns in Kontakt.</p><h2>Ressourcen</h2><ul><li><a href="https://arxiv.org/abs/2310.11511">Selbst-RAG: Lernen, durch Selbstreflexion abzurufen, zu generieren und zu kritisieren</a></li><li><a href="https://arxiv.org/abs/2501.09136">Agentic Retrieval-Augmented Generation: Eine Übersicht über Agentic RAG</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Hybride Suche ohne Probleme: Vereinfachte hybride Suche mit Retrievern]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie die hybride Suche in Elasticsearch mit einem mehrfeldrigen Abfrageformat für lineare und RRF-Retriever vereinfachen und Abfragen erstellen können, ohne vorher Kenntnisse über Ihren Elasticsearch-Index haben zu müssen.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/hybrid-search-multi-field-query-retrievers-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/hybrid-search-multi-field-query-retrievers-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Mike Pellegrini]]></dc:creator>
            <pubDate>Thu, 27 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4494f98ae3958bbdbc6171df9677fc4d65ec5640-1536x1024.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p><a href="https://www.elastic.co/what-is/hybrid-search">Die hybride Suche</a> gilt weithin als leistungsstarker Suchansatz, der die Präzision und Geschwindigkeit der <a href="https://www.elastic.co/search-labs/blog/lexical-and-semantic-search-with-elasticsearch#lexical-search---sparse-retrieval">lexikalischen Suche</a> mit den Möglichkeiten der <a href="https://www.elastic.co/what-is/semantic-search">semantischen Suche</a> im Bereich der natürlichen Sprache kombiniert. Die praktische Anwendung gestaltet sich jedoch oft schwierig und erfordert häufig fundierte Kenntnisse über den Index sowie die Erstellung ausführlicher Abfragen mit komplexen Konfigurationen. In diesem Blogbeitrag werden wir untersuchen, wie das <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers#multi-field-query-format">Mehrfeld-Abfrageformat für lineare und RRF-Retriever die</a> hybride Suche vereinfacht und zugänglicher macht, häufige Probleme beseitigt und es Ihnen ermöglicht, ihre volle Leistungsfähigkeit leichter auszuschöpfen. Wir werden auch untersuchen, wie das Abfrageformat mit mehreren Feldern es Ihnen ermöglicht, hybride Suchanfragen durchzuführen, ohne vorher Kenntnisse über Ihren Index zu haben.</p><h2>Das Problem der Punktespanne</h2><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c8b49153c47d2cdc233c0d2e440db04711d48ca5-1600x1600.jpg" style="width: 100%;" /><p>Um die Ausgangslage zu verdeutlichen, betrachten wir zunächst einen der Hauptgründe, warum die hybride Suche schwierig sein kann: die variierenden Bewertungsbereiche. Unser alter Bekannter <a href="https://www.elastic.co/elasticon/conf/2016/sf/improved-text-scoring-with-bm25">BM25</a> liefert unbegrenzte Ergebnisse. Mit anderen Worten: BM25 kann Werte generieren, die von nahe 0 bis (theoretisch) unendlich reichen. Im Gegensatz dazu liefern Abfragen gegen <code>dense_vector</code> -Felder Ergebnisse im Bereich zwischen 0 und 1. Erschwerend kommt hinzu, dass <code>semantic_text</code> den Feldtyp verschleiert, der zur Indizierung von Einbettungen verwendet wird. Daher ist es ohne detaillierte Kenntnisse über die Konfiguration Ihres Index und Inferenzendpunkts schwierig abzuschätzen, in welchem Bereich die Ergebnisse Ihrer Abfrage liegen werden. Dies stellt ein Problem dar, wenn versucht wird, lexikalische und semantische Suchergebnisse zu verschachteln, da die lexikalischen Ergebnisse Vorrang vor den semantischen haben können, selbst wenn die semantischen Ergebnisse relevanter sind. Die allgemein anerkannte Lösung für dieses Problem besteht darin, die Werte vor der Verschachtelung der Ergebnisse zu normalisieren. Elasticsearch bietet hierfür zwei Tools an: den <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers/linear-retriever">linearen</a> und <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers/rrf-retriever">den RRF-</a> Retriever.<br/></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/264481268c8b6ac259e3c257b85431b513f16672-1077x586.png" style="width: 100%;" /><p>Der <strong>RRF-</strong> Retriever wendet den <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion">RRF-Algorithmus</a> an, wobei der Dokumentenrang als Relevanzmaß verwendet und der Score verworfen wird. Da die Punktzahl nicht berücksichtigt wird, stellen Abweichungen im Punktzahlbereich kein Problem dar.</p><p>Der <strong>lineare</strong> Retriever verwendet eine lineare Kombination, um die endgültige Punktzahl eines Dokuments zu bestimmen. Dabei wird für jede einzelne Abfrage die Punktzahl der Komponenten des Dokuments ermittelt, normalisiert und anschließend summiert, um die Gesamtpunktzahl zu erhalten. Mathematisch lässt sich die Operation wie folgt ausdrücken:</p><pre><code>Total Score = 𝚺(N(Sx))</code></pre><p>Dabei ist <code>N</code> die Normalisierungsfunktion und SX die Punktzahl für die Anfrage X. Die Normalisierungsfunktion ist hierbei von zentraler Bedeutung, da sie die Punktzahl jeder Abfrage so transformiert, dass sie denselben Wertebereich verwendet. <a href="https://www.elastic.co/search-labs/blog/linear-retriever-hybrid-search">Hier</a> erfahren Sie mehr über den linearen Retriever.</p><h2>Aufgeschlüsselt</h2><p>Mit diesen Tools können Benutzer eine effektive Hybridsuche implementieren, dies erfordert jedoch gewisse Kenntnisse über ihren Index. Betrachten wir ein Beispiel mit dem linearen Retriever, bei dem wir einen Index mit zwei Feldern abfragen:</p><pre><code>PUT linear_retriever_example
{
  "mappings": {
    "properties": {
      "semantic_text_field": { <1>
        "type": "semantic_text",
        "inference_id": ".multilingual-e5-small-elasticsearch"
      },
      "text_field": { <2>
        "type": "text"
      }
    }
  }
}</code></pre><p>1. <code>semantic_text_field</code> ist ein <code>semantic_text</code> -Feld, das <a href="https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-e5">E5</a>, ein Text-Embedding-Modell, verwendet.</p><p>2. <code>text_field</code> ist ein Standard- <code>text</code> -Feld</p><pre><code>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "retrievers": [
        {
          "retriever": {
            "standard": {
              "query": {
                "match": { <1>
                  "semantic_text_field": "foo"
                }
              }
            }
          },
          "normalizer": "minmax"
        },
        {
          "retriever": {
            "standard": {
              "query": {
                "match": {
                  "text_field": "foo"
                }
              }
            }
          },
          "normalizer": "minmax"
        }
      ]
    }
  }
}</code></pre><p>1. Wir verwenden eine <code>match</code> -Abfrage für unser <code>semantic_text</code> -Feld, dessen <a href="https://www.elastic.co/search-labs/blog/semantic-search-match-knn-sparse-vector#we-made-match-happen-in-semantic-search!">Unterstützung wir in Elasticsearch 8.18/9.0 hinzugefügt haben.</a></p><p><br/>Bei der Erstellung der Abfrage müssen wir berücksichtigen, dass <code>semantic_text_field</code> ein Text-Embedding-Modell verwendet, sodass alle Abfragen darauf eine Punktzahl zwischen 0 und 1 generieren. Wir müssen außerdem wissen, dass <code>text_field</code> ein Standardfeld <code>text</code> ist und dass Abfragen darauf eine unbegrenzte Punktzahl erzeugen. Um ein Ergebnis-Set mit der richtigen Relevanz zu erstellen, müssen wir einen Retriever verwenden, der die Abfrage-Scores normalisiert, bevor er sie kombiniert. In diesem Beispiel verwenden wir den linearen Retriever mit <code>minmax</code> -Normalisierung, der den Score jeder Abfrage auf einen Wert zwischen 0 und 1 normalisiert.</p><p>Die Abfragekonstruktion in diesem Beispiel ist recht einfach, da nur zwei Felder beteiligt sind. Allerdings kann es sehr schnell kompliziert werden, wenn weitere Felder unterschiedlicher Art hinzugefügt werden. Dies zeigt, dass das Schreiben einer effektiven hybriden Suchanfrage oft ein tieferes Verständnis des abgefragten Index erfordert, damit die Punktzahlen der einzelnen Suchanfragen vor der Kombination richtig normalisiert werden. Dies stellt ein Hindernis für die breitere Akzeptanz der hybriden Suche dar.</p><h3>Abfragegruppierung</h3><p>Erweitern wir das Beispiel: Was wäre, wenn wir ein <code>text</code> -Feld und zwei <code>semantic_text</code> -Felder abfragen wollten? Wir könnten eine Abfrage wie diese erstellen:</p><pre><code>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "retrievers": [
        {
          "retriever": {
            "standard": {
              "query": {
                "semantic": {
                  "field": "semantic_text_field_1",
                  "query": "foo"
                }
              }
            }
          },
          "normalizer": "minmax"
        },
        {
          "retriever": {
            "standard": {
              "query": {
                "semantic": {
                  "field": "semantic_text_field_2",
                  "query": "foo"
                }
              }
            }
          },
          "normalizer": "minmax"
        },
        {
          "retriever": {
            "standard": {
              "query": {
                "match": {
                  "text_field": "foo"
                }
              }
            }
          },
          "normalizer": "minmax"
        }
      ]
    }
  }
}</code></pre><p>Das klingt auf den ersten Blick gut, aber es gibt ein potenzielles Problem. Die Treffer im Feld <code>semantic_text</code> machen nun ⅔ der Gesamtpunktzahl aus:</p><pre><code>Total Score = N(semantic_text_field_1 score) + N(semantic_text_field_2 score) + N(text_field score)</code></pre><p>Das ist wahrscheinlich nicht das, was Sie wollen, denn dadurch entsteht ein unausgewogenes Ergebnis. Die Auswirkungen sind in einem Beispiel wie diesem mit nur 3 Feldern möglicherweise nicht so deutlich erkennbar, aber es wird problematisch, wenn mehr Felder abgefragt werden. Beispielsweise enthalten die meisten Indizes weitaus mehr lexikalische als semantische Felder (d. h. <code>dense_vector</code>, <code>sparse_vector</code>, oder <code>semantic_text</code>). Was wäre, wenn wir einen Index mit 9 lexikalischen Feldern und 1 semantischen Feld nach dem oben genannten Muster abfragen würden? Die lexikalischen Übereinstimmungen würden 90 % der Punktzahl ausmachen und somit die Effektivität der semantischen Suche beeinträchtigen.</p><p>Eine gängige Methode, um diesem Problem zu begegnen, besteht darin, Anfragen in lexikalische und semantische Kategorien zu gruppieren und beide gleich zu gewichten. Dadurch wird verhindert, dass eine der beiden Kategorien die Gesamtpunktzahl dominiert.</p><p>Lasst uns das in die Praxis umsetzen. Wie sähe dieser Ansatz mit gruppierten Abfragen in diesem Beispiel bei Verwendung des linearen Retrievers aus?</p><pre><code>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "retrievers": [
        {
          "retriever": {
            "linear": {
              "retrievers": [
                {
                  "retriever": {
                    "standard": {
                      "query": {
                        "semantic": {
                          "field": "semantic_text_field_1",
                          "query": "foo"
                        }
                      }
                    }
                  },
                  "normalizer": "minmax"
                },
                {
                  "retriever": {
                    "standard": {
                      "query": {
                        "semantic": {
                          "field": "semantic_text_field_2",
                          "query": "foo"
                        }
                      }
                    }
                  },
                  "normalizer": "minmax"
                }
              ]
            }
          },
          "normalizer": "minmax"
        },
        {
          "retriever": {
            "standard": {
              "query": {
                "match": {
                  "text_field": "foo"
                }
              }
            }
          },
          "normalizer": "minmax"
        }
      ]
    }
  }
}</code></pre><p>Wow, das wird aber ausführlich! Möglicherweise mussten Sie sogar mehrmals auf- und abscrollen, um die gesamte Abfrage zu prüfen! Hier verwenden wir zwei Normalisierungsebenen, um die Abfragegruppen zu erstellen. Mathematisch lässt sich dies wie folgt ausdrücken:</p><pre><code>Total Score = N(N(semantic_text_field_1 score) + N(semantic_text_field_2 score)) + N(text_field score)</code></pre><p>Diese zweite Normalisierungsebene stellt sicher, dass die Anfragen an die Felder <code>semantic_text</code> und <code>text</code> gleich gewichtet werden. Beachten Sie, dass wir in diesem Beispiel die Normalisierung zweiter Ebene für <code>text_field</code> weglassen, da es nur ein lexikalisches Feld gibt, wodurch Sie sich <em>noch mehr</em> Ausführlichkeit ersparen.</p><p>Diese Abfragestruktur ist schon jetzt unhandlich, und wir fragen nur drei Felder ab. Je mehr Felder man abfragt, desto unübersichtlicher wird es, selbst für erfahrene Suchmaschinenexperten.</p><h2>Das Abfrageformat mit mehreren Feldern</h2><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/dd04e1686076a574b717c1460acfe4eb79299208-1600x1600.jpg" style="width: 100%;" /><p>Um das Ganze zu vereinfachen, haben wir das <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers#multi-field-query-format">Multi-Field-Abfrageformat</a> für die linearen und RRF-Retriever in Elasticsearch 8.19, 9.1 und <a href="https://www.elastic.co/cloud/serverless">Serverless</a> hinzugefügt. Sie können die gleiche Abfrage wie oben nun mit folgendem Befehl durchführen:</p><pre><code>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "fields": [ "semantic_text_field_1", "semantic_text_field_2", "text_field" ],
      "query": "foo",
      "normalizer": "minmax"
    }
  }
}</code></pre><p>Dadurch verkürzt sich die Abfrage von 55 Zeilen auf nur noch 9! Elasticsearch verwendet automatisch die Indexzuordnungen für:</p><ul><li>Ermitteln Sie den Typ jedes abgefragten Feldes.</li><li>Ordnen Sie jedes Feld einer lexikalischen oder semantischen Kategorie zu.</li><li>Jede Kategorie sollte im Endergebnis gleich gewichtet werden.</li></ul><p>Dies ermöglicht es jedem, eine effektive hybride Suchanfrage auszuführen, ohne Details über den Index oder die verwendeten Inferenzendpunkte kennen zu müssen.</p><p>Bei Verwendung von RRF kann das <code>normalizer</code> weggelassen werden, da der Rang als Indikator für die Relevanz dient:</p><pre><code>GET rrf_retriever_example/_search
{
  "retriever": {
    "rrf": {
      "fields": [ "semantic_text_field_1", "semantic_text_field_2", "text_field" ],
      "query": "foo"
    }
  }
}</code></pre><h2>Steigerung pro Spielfeld</h2><p>Bei Verwendung des linearen Retrievers können Sie eine Gewichtung pro Feld anwenden, um die Wichtigkeit von Übereinstimmungen in bestimmten Feldern anzupassen. Nehmen wir beispielsweise an, Sie fragen vier Felder ab: zwei <code>semantic_text</code> -Felder und zwei <code>text</code> -Felder:</p><pre><code>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "fields": [ "semantic_text_field_1", "semantic_text_field_2", "text_field_1", "text_field_2" ],
      "query": "foo",
      "normalizer": "minmax"
    }
  }
}</code></pre><p>Standardmäßig wird jedes Feld innerhalb seiner Gruppe (lexikalisch oder semantisch) gleich gewichtet. Die Punkteverteilung sieht wie folgt aus:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ba31c76189e3a1e5b1638437ccf0528aafec2598-1600x549.png" style="width: 100%;" /><p>Mit anderen Worten: Jedes Feld macht 25 % der Gesamtpunktzahl aus.</p><p>Mit der Syntax <code>field^boost</code> können wir jedem Feld einen feldbezogenen Boost hinzufügen. Wenden wir einen Boost von 2 auf <code>semantic_text_field_1</code> und <code>text_field_1</code> an:</p><pre><code>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "fields": [ "semantic_text_field_1^2", "semantic_text_field_2", "text_field_1^2", "text_field_2" ]
      "query": "foo",
      "normalizer": "minmax"
    }
  }
}</code></pre><p>Die Aufschlüsselung der Punkte sieht nun wie folgt aus:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/de20e555d52f914bf483a048d056f54f4fece757-1600x549.png" style="width: 100%;" /><p>Jede Abfragegruppe ist weiterhin gleich gewichtet, aber die Feldgewichtung innerhalb der Gruppen hat sich geändert:</p><ul><li><code>semantic_text_field_1</code> entspricht 66 % der Punktzahl der semantischen Abfragegruppe und 33 % der Gesamtpunktzahl.</li><li><code>text_field_1</code> macht 66 % der Punktzahl der lexikalischen Abfragegruppe und 33 % der Gesamtpunktzahl aus.</li></ul>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">ℹ️ Beachten Sie, dass sich die Gesamtpunktzahl nicht ändert, wenn ein Bonus pro Feld angewendet wird. Dies ist ein beabsichtigter Nebeneffekt der Score-Normalisierung, der sicherstellt, dass lexikalische und semantische Anfrage-Scores direkt miteinander vergleichbar bleiben.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">ℹ️ Die feldbezogene Gewichtung kann auch mit dem RRF-Retriever in Elasticsearch 9.2+ verwendet werden.</td>
                    </tr>
              </tbody>
            </table>
          <h3>Wildcard-Auflösung</h3><p>Sie können das Platzhalterzeichen <code>*</code> im Parameter <code>fields</code> verwenden, um mehrere Felder abzugleichen. Um das obige Beispiel fortzuführen: Diese Abfrage ist funktional äquivalent zur expliziten Abfrage von s<code>emantic_text_field_1</code>, <code>semantic_text_field_2</code>, und <code>text_field_1</code> :</p><pre><code>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "fields": [ "semantic_text_field_*", "*_field_1" ],
      "query": "foo",
      "normalizer": "minmax"
    }
  }
}</code></pre><p>Interessanterweise passt das Muster <code>*_field_1</code> sowohl zu <code>text_field_1</code> als auch <code>semantic_text_field_1</code>. Dies wird automatisch gehandhabt; die Abfrage wird so ausgeführt, als ob jedes der Felder explizit abgefragt würde. Es ist auch in Ordnung, dass <code>semantic_text_field_1</code> beiden Mustern entspricht; alle Feldnamenübereinstimmungen werden vor der Abfrageausführung dedupliziert.</p><p>Sie können das Wildcard-Zeichen auf verschiedene Arten verwenden:</p><ul><li>Präfixübereinstimmung (z. B. <code>*_text_field</code>)</li><li>Inline-Matching (z. B. <code>semantic_*_field</code>)</li><li>Suffix-Matching (z. B. <code>semantic_text_field_*</code>)</li></ul><p>Sie können auch mehrere Platzhalter verwenden, um eine Kombination der oben genannten anzuwenden, z. B. <code>*_text_field_*</code>.</p><h3>Standardabfragefelder</h3><p>Das Abfrageformat mit mehreren Feldern ermöglicht es Ihnen auch, einen Index abzufragen, über den Sie nichts wissen. Wenn Sie den Parameter <code>fields</code> weglassen, werden alle Felder abgefragt, die durch die <a href="https://www.elastic.co/docs/reference/elasticsearch/index-settings/index-modules">Indexeinstellung index.query.default_field</a> angegeben sind:</p><pre><code>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "query": "foo",
      "normalizer": "minmax"
    }
  }
}</code></pre><p>Standardmäßig ist <code>index.query.default_field</code> auf <code>*</code> gesetzt. Dieser Platzhalter wird auf jeden Feldtyp im Index aufgelöst, der Termabfragen unterstützt, was auf die meisten zutrifft. Die Ausnahmen sind:</p><ul><li><code>dense_vector</code> Felder</li><li><code>rank_vector</code> Felder</li><li>Geometrische Felder: <code>geo_point</code>, <code>shape</code></li></ul><p>Diese Funktionalität ist besonders nützlich, wenn Sie eine hybride Suchanfrage auf einem von einem Drittanbieter bereitgestellten Index durchführen möchten. Das Abfrageformat mit mehreren Feldern ermöglicht es Ihnen, auf einfache Weise eine passende Abfrage auszuführen. Lassen Sie einfach den Parameter <code>fields</code> weg, und alle relevanten Felder werden abgefragt.</p><h2>Fazit</h2><p>Das Problem der Bewertungsbereiche kann die Implementierung einer effektiven hybriden Suche zu einer echten Herausforderung machen, insbesondere wenn nur begrenzter Einblick in den abgefragten Index oder die verwendeten Inferenzendpunkte besteht. Das Mehrfeld-Abfrageformat für die linearen und RRF-Retriever mindert dieses Problem, indem es einen automatisierten, auf Abfragegruppierung basierenden hybriden Suchansatz in einer einfachen und zugänglichen API bündelt. Zusätzliche Funktionen wie die Gewichtung einzelner Felder, die Auflösung von Platzhaltern und die Verwendung von Standardabfragefeldern erweitern den Funktionsumfang und decken viele Anwendungsfälle ab.</p><h2>Probieren Sie heute noch das Abfrageformat mit mehreren Feldern aus.</h2><p>Sie können die linearen und RRF-Retriever mit dem Multi-Field-Query-Format in vollständig verwalteten Elasticsearch <a href="https://www.elastic.co/cloud/serverless">Serverless-</a> Projekten mit einer <a href="https://www.elastic.co/docs/deploy-manage/deploy/elastic-cloud/create-serverless-project">kostenlosen Testversion</a> ausprobieren. Es ist auch in Stack-Versionen ab 8.19 und 9.1 verfügbar.</p><p>Legen Sie in wenigen Minuten in Ihrer lokalen Umgebung mit einem einzigen Befehl los:</p><pre><code>curl -fsSL https://elastic.co/start-local | sh</code></pre><p></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Entwicklung eines KI-Agenten für die Personalabteilung mit Elastic Agent Builder und GPT-OSS]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie mit Elastic Agent Builder und GPT-OSS einen KI-Agenten erstellen, der in natürlicher Sprache Anfragen zu Ihren Mitarbeiter-HR-Daten beantworten kann.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/build-an-ai-agent-hr-elastic-agent-builder-gpt-oss</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/build-an-ai-agent-hr-elastic-agent-builder-gpt-oss</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Tomás Murúa]]></dc:creator>
            <pubDate>Wed, 26 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/05d2d0513fff67d975f9223d75108aa9f50646bc-1600x914.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<h2>Einleitung</h2><p>Dieser Artikel zeigt Ihnen, wie Sie mit <a href="https://openai.com/index/introducing-gpt-oss/">GPT-OSS</a> und Elastic Agent Builder einen KI-Agenten für HR erstellen. Der Agent kann Ihre Fragen beantworten, ohne Daten an OpenAI, Anthropic oder einen externen Dienst zu senden.</p><p>Wir werden LM Studio verwenden, um GPT-OSS lokal bereitzustellen und es mit dem Elastic Agent Builder zu verbinden.</p><p>Am Ende dieses Artikels verfügen Sie über einen individuell angepassten KI-Agenten, der Fragen in natürlicher Sprache zu Ihren Mitarbeiterdaten beantworten kann und gleichzeitig die volle Kontrolle über Ihre Informationen und Ihr Modell behält.</p><h2>Voraussetzungen</h2><p>Für diesen Artikel benötigen Sie:</p><ul><li><a href="https://www.elastic.co/cloud">Elastic Cloud-</a> gehostete Version 9.2, serverlose oder <a href="https://www.elastic.co/docs/deploy-manage/deploy/self-managed/local-development-installation-quickstart">lokale</a> Bereitstellung</li><li>Empfohlen wird ein Rechner mit 32 GB RAM (mindestens 16 GB für GPT-OSS 20B).</li><li><a href="https://lmstudio.ai/">LM Studio</a> installiert</li><li><a href="https://www.docker.com/products/docker-desktop/">Docker Desktop</a> installiert</li></ul><h2>Warum GPT-OSS verwenden?</h2><p>Mit einem lokalen LLM haben Sie die Kontrolle, es in Ihrer eigenen Infrastruktur einzusetzen und es genau an Ihre Bedürfnisse anzupassen. All dies, während Sie die Kontrolle über die Daten behalten, die Sie mit dem Modell teilen, und natürlich müssen Sie keine Lizenzgebühr an einen externen Anbieter zahlen.</p><p>OpenAI <a href="https://openai.com/index/introducing-gpt-oss/">veröffentlichte GPT-OSS</a> am 5. August 2025 als Teil seines Engagements für das offene Ökosystem von Computermodellen.</p><p>Das Parametermodell 20B bietet:</p><ul><li><strong>Werkzeugnutzungsfähigkeiten</strong></li><li><strong>Effiziente Schlussfolgerung</strong></li><li><strong>OpenAI SDK-kompatibel</strong></li><li><strong>Kompatibel mit agentenbasierten Workflows</strong></li></ul><p>Vergleich der Benchmarks:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/29160e3345352088e8213297630882f252b00c47-1600x680.png" style="width: 100%;" /><h2>Lösungsarchitektur</h2><p>Die Architektur läuft vollständig auf Ihrem lokalen Rechner. Elastic (läuft in Docker) kommuniziert direkt mit Ihrem lokalen LLM über LM Studio, und der Elastic Agent Builder nutzt diese Verbindung, um benutzerdefinierte KI-Agenten zu erstellen, die Ihre Mitarbeiterdaten abfragen können.</p><p>Weitere Einzelheiten entnehmen Sie bitte dieser <a href="https://www.elastic.co/docs/solutions/observability/connect-to-own-local-llm">Dokumentation</a>.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a4a886750ff25fa8bb7aefc7448161e52cf73ed3-1600x896.png" style="width: 100%;" /><h2>Entwicklung eines KI-Agenten für die Personalabteilung: Schritte</h2><p>Wir werden die Implementierung in 5 Schritte unterteilen:</p><ol><li>Konfigurieren Sie LM Studio mit einem lokalen Modell</li><li>Lokalen Elastic-Speicher mit Docker bereitstellen</li><li>Erstellen Sie den OpenAI-Konnektor in Elastic</li><li>Mitarbeiterdaten in Elasticsearch hochladen</li><li>Erstellen und testen Sie Ihren KI-Agenten</li></ol><h2>Schritt 1: LM Studio mit GPT-OSS 20B konfigurieren</h2><p>LM Studio ist eine benutzerfreundliche Anwendung, mit der Sie große Sprachmodelle lokal auf Ihrem Computer ausführen können. Es bietet einen OpenAI-kompatiblen API-Server, wodurch die Integration mit Tools wie Elastic ohne komplexen Einrichtungsprozess vereinfacht wird. Weitere Details finden Sie in der <a href="https://lmstudio.ai/docs/app">LM Studio-Dokumentation</a>.</p><p>Laden Sie zunächst <span class="unknown__pt__mark__51566e628851">LM Studio</span> von der offiziellen Website herunter und installieren Sie es. Nach der Installation öffnen Sie die Anwendung.</p><h3>In der LM Studio-Oberfläche:</h3><ol><li>Gehen Sie zum Suchfeld und suchen Sie nach „GPT-OSS“.</li><li>Wählen Sie <code>openai/gpt-oss-20b</code> aus OpenAI aus.</li><li>Klicken Sie auf „Herunterladen“.</li></ol><p>Die Größe dieses Modells sollte ungefähr <strong>12,10 GB</strong> betragen. Der Download kann je nach Ihrer Internetverbindung einige Minuten dauern.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5d01bc4dcb377b5259fc6b521fe2425a31b90ca4-1312x872.png" style="width: 100%;" /><h4>Sobald das Modell heruntergeladen ist:</h4><ol><li>Wechseln Sie zur Registerkarte „Lokaler Server“.</li><li>Wählen Sie openai/gpt-oss-20b aus.</li><li>Verwenden Sie den Standardport 1234.</li><li>Gehen Sie im rechten Bereich auf <strong>„Laden“</strong>und stellen Sie die Kontextlänge auf <strong>40 KB</strong> oder höher ein.</li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e546033f916381647b876815b2c1f1ae2a08365f-326x337.png" style="width: 100%;" /><p>5. Klicken Sie auf „Server starten“.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/28ee78a3caa84d14e04db3d42f30acbe4d4d005a-1312x872.png" style="width: 100%;" /><p>Dies sollte Ihnen angezeigt werden, wenn der Server läuft.</p><pre><code>[LM STUDIO SERVER] Success! HTTP server listening on port 1234
[LM STUDIO SERVER] Supported endpoints:
[LM STUDIO SERVER] ->	GET  http://localhost:1234/v1/models
[LM STUDIO SERVER] ->	POST http://localhost:1234/v1/responses
[LM STUDIO SERVER] ->	POST http://localhost:1234/v1/chat/completions
[LM STUDIO SERVER] ->	POST http://localhost:1234/v1/completions
[LM STUDIO SERVER] ->	POST http://localhost:1234/v1/embeddings
Server started.</code></pre><h2>Schritt 2: Lokalen Elastic-Speicher mit Docker bereitstellen</h2><p>Nun richten wir Elasticsearch und Kibana lokal mit Docker ein. Elastic stellt ein praktisches Skript zur Verfügung, das den gesamten Einrichtungsprozess übernimmt. Für weitere Einzelheiten verweisen wir auf die <a href="https://www.elastic.co/docs/deploy-manage/deploy/self-managed/local-development-installation-quickstart">offizielle Dokumentation</a>.</p><h3>Führen Sie das Start-Lokal-Skript aus</h3><p>Führen Sie folgenden Befehl in Ihrem Terminal aus:</p><pre><code>curl -fsSL https://elastic.co/start-local | sh</code></pre><p>Dieses Skript wird:</p><ul><li>Elasticsearch und Kibana herunterladen und konfigurieren</li><li>Starten Sie beide Dienste mit Docker Compose.</li><li>Automatische Aktivierung einer 30-tägigen Platinum-Testlizenz</li></ul><h3>Erwartete Ausgabe</h3><p>Warten Sie einfach auf die folgende Meldung und speichern Sie das angezeigte Passwort und den API-Schlüssel; Sie benötigen diese für den Zugriff auf Kibana:</p><pre><code>🎉 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</code></pre><h3>Zugriff auf Kibana</h3><p>Öffnen Sie Ihren Browser und navigieren Sie zu:</p><pre><code>http://localhost:5601</code></pre><p>Melden Sie sich mit den Anmeldeinformationen an, die Sie in der Terminalausgabe erhalten haben.</p><h3>Agent Builder aktivieren</h3><p>Nach dem Einloggen in Kibana navigieren Sie zu <strong>Management </strong>&gt;<strong> AI </strong>&gt;<strong> Agent Builder </strong>und aktivieren den Agent Builder.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/92e104cb846c20d875865ded8a3d37f5c7daae9b-1491x1528.png" style="width: 100%;" /><h2>Schritt 3: Erstellen Sie den OpenAI-Konnektor in Elastic</h2><p>Nun konfigurieren wir Elastic so, dass es Ihr lokales LLM verwendet.</p><h3>Zugangsanschlüsse</h3><ol><li>In Kibana</li><li>Gehen Sie zu <strong>Projekteinstellungen</strong> &gt; <strong>Verwaltung</strong></li><li>Unter <strong>„Warnungen und Einblicke“</strong> wählen Sie <strong>„Konnektoren“</strong>aus.</li><li>Klicken Sie auf „Connector erstellen“.</li></ol><h3>Konfigurieren Sie den Anschluss</h3><p>Wählen Sie <strong>OpenAI</strong> aus der Liste der Konnektoren aus. LM Studio nutzt das OpenAI SDK und ist daher kompatibel.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5ac87042e086c7a2bd47a8039e646ec831f0dcc6-923x974.png" style="width: 100%;" /><p>Füllen Sie die Felder mit diesen Werten aus:</p><ul><li><strong>Anschlussname: </strong>LM Studio - GPT-OSS 20B</li><li><strong>Wählen Sie einen OpenAI-Anbieter: </strong>Andere (OpenAI-kompatibler Dienst)</li><li><strong>URL: </strong><code>http://host.docker.internal:1234/v1/chat/completions</code></li><li><strong>Standardmodell: </strong>openai/gpt-oss-20b</li><li><strong>API-Schlüssel:</strong> testkey-123 (Jeder beliebige Text funktioniert, da LM Studio Server keine Authentifizierung erfordert.)</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2084ac32fcf1fb810c8b54ecab1c85a1e3e8905b-672x1302.png" style="width: 100%;" /><p>Klicken Sie zum Abschluss der Konfiguration auf <strong>Speichern &amp; Testen</strong>.</p><p><strong>Wichtig:</strong> Aktivieren Sie die Option „ <strong>Native Funktionsaufrufe aktivieren</strong>“; dies ist erforderlich, damit der Agent Builder ordnungsgemäß funktioniert. Wenn Sie dies nicht aktivieren, erhalten Sie einen <strong><code>No tool calls found in the response</code></strong> -Fehler.</p><h3>Testen Sie die Verbindung</h3><p>Elastic sollte die Verbindung automatisch testen. Wenn alles korrekt konfiguriert ist, wird eine Erfolgsmeldung wie diese angezeigt:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f567d767f1969c4730c1daa92f651789dc3742ac-1042x812.png" style="width: 100%;" /><p>Abwehr:</p><pre><code>{
  "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"
}</code></pre><h2>Schritt 4: Mitarbeiterdaten in Elasticsearch hochladen</h2><p>Nun laden wir den <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/gpt-oss-with-elasticsearch/hr-employees-bulk.json">HR-Mitarbeiterdatensatz</a> hoch, um zu demonstrieren, wie der Agent mit sensiblen Daten arbeitet. Ich habe einen fiktiven Datensatz mit dieser Struktur generiert.</p><h3>Struktur des Datensatzes</h3><pre><code>{
  "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"
}</code></pre><h3>Erstellen Sie den Index mit Zuordnungen</h3><p>Zuerst muss der Index mit den entsprechenden Zuordnungen erstellt werden. Beachten Sie, dass wir für einige Schlüsselfelder <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text">semantische</a> Textfelder verwenden; dies ermöglicht semantische Suchfunktionen für unseren Index.</p><pre><code>​​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"
      }
    }
  }
}</code></pre><h3>Indexierung mit Bulk-API</h3><p>Kopieren Sie den <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/gpt-oss-with-elasticsearch/hr-employees-bulk.json">Datensatz</a> in Ihre Entwicklertools in Kibana und führen Sie ihn aus:</p><pre><code>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": "...", ...}</code></pre><h3>Überprüfen Sie die Daten.</h3><p>Führen Sie eine Abfrage zur Überprüfung durch:</p><pre><code>GET hr-employees/_search</code></pre><h2>Schritt 5: KI-Agent erstellen und testen</h2><p>Nachdem alles konfiguriert ist, ist es nun an der Zeit, mit dem Elastic Agent Builder einen benutzerdefinierten KI-Agenten zu erstellen. Weitere Details finden Sie in der <a href="https://www.elastic.co/docs/solutions/search/agent-builder/get-started">Elastic-Dokumentation</a>.</p><h3>Fügen Sie den Verbinder hinzu.</h3><p>Bevor wir unseren neuen Agenten erstellen können, müssen wir unseren Agent Builder so einstellen, dass er unseren benutzerdefinierten Konnektor mit der Bezeichnung <code>LM Studio - GPT-OSS 20B</code> verwendet, da der Standardkonnektor <a href="https://www.elastic.co/docs/reference/kibana/connectors-kibana/elastic-managed-llm">Elastic Managed LLM</a> ist. Dazu müssen wir zu <strong>Projekteinstellungen</strong> &gt; <strong>Verwaltung</strong> &gt; <strong>GenAI-Einstellungen</strong> gehen; jetzt wählen wir die von uns erstellte Einstellung aus und klicken auf <strong>Speichern</strong>.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/11e830c3e2fb4c298b020c928fa5422f3397ba08-1600x1152.png" style="width: 100%;" /><h3>Zugriffsagenten-Generator</h3><ol><li>Gehen Sie zu <strong>den Agenten.</strong></li><li>Klicken Sie auf <strong>„Neuen Agenten erstellen“.</strong></li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c1e60541563650163f972ac9088dc1ed1de759a7-1600x1054.png" style="width: 100%;" /><h3>Konfigurieren Sie den Agenten</h3><p>Zum Anlegen eines neuen Agenten sind die Felder <strong>Agenten-ID</strong>, <strong>Anzeigename</strong> und <strong>Anzeigeanweisungen</strong> erforderlich.</p><p>Es gibt aber noch weitere Anpassungsmöglichkeiten, wie zum Beispiel die benutzerdefinierten Anweisungen, die vorgeben, wie sich Ihr Agent verhalten und mit Ihren Tools interagieren soll, ähnlich einer Systemaufforderung, aber für unseren benutzerdefinierten Agenten. Mithilfe von Labels lassen sich Agenten, Avatarfarben und Avatarsymbole organisieren.</p><p>Diejenigen, die ich anhand des Datensatzes für unseren Agenten ausgewählt habe, sind:<br/><br/><strong>Agenten-ID:</strong> <code>hr_assistant</code></p><p><strong>Benutzerdefinierte Anweisungen:</strong></p><pre><code>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</code></pre><p><br/>Labels: <code>Human Resources</code> und <code>GPT-OSS</code></p><p>Anzeigename: <code>HR Analytics Assistant</code></p><p>Anzeigebeschreibung:</p><pre><code>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.</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f94bb2bf08497e5e756ca76b30a3a51f42927756-1424x1217.png" style="width: 100%;" /><p>Nachdem alle Daten eingegeben wurden, können wir auf „Unseren neuen Agenten <strong>speichern</strong> “ klicken.</p><h3>Testen Sie den Agenten</h3><p>Sie können nun Fragen in natürlicher Sprache zu Ihren Mitarbeiterdaten stellen, und GPT-OSS 20B versteht die Absicht und generiert eine angemessene Antwort.</p><h4>Prompt:</h4><pre><code>Which employee is the one with the highest salary in the hr-employees index?</code></pre><h4>Antwort:</h4><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/94ad19f80b96304028a59f60beca51dfc9aecc8a-899x631.png" style="width: 100%;" /><p>Der Agentenprozess war wie folgt:</p><p>1. Verstehen Sie Ihre Frage mithilfe des GPT-OSS-Connectors.</p><p>2. Generieren Sie die entsprechende Elasticsearch-Abfrage (mithilfe der integrierten Tools oder einer benutzerdefinierten <a href="https://www.elastic.co/docs/reference/query-languages/esql">ES|QL-</a> Abfrage).</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6f2961d0d1b97475f6dda300acee84da540938e6-844x466.png" style="width: 100%;" /><p>3. Abrufen passender Mitarbeiterdatensätze</p><p>4. Die Ergebnisse in natürlicher Sprache und mit geeigneter Formatierung präsentieren</p><p>Im Gegensatz zur herkömmlichen lexikalischen Suche versteht der von GPT-OSS unterstützte Agent Absicht und Kontext, wodurch es einfacher wird, Informationen zu finden, ohne die genauen Feldnamen oder die Abfragesyntax kennen zu müssen. Weitere Einzelheiten zum Denkprozess des Agenten finden Sie in diesem <a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-experiments-performance">Artikel</a>.</p><h2>Fazit</h2><p>In diesem Artikel haben wir mithilfe des Agent Builders von Elastic einen benutzerdefinierten KI-Agenten erstellt, um eine Verbindung zum lokal laufenden OpenAI GPT-OSS-Modell herzustellen. Durch die Bereitstellung von Elastic und LLM auf Ihrem lokalen Rechner ermöglicht Ihnen diese Architektur die Nutzung generativer KI-Funktionen bei gleichzeitiger vollständiger Kontrolle über Ihre Daten, ohne dass Informationen an externe Dienste gesendet werden müssen.</p><p>Wir haben GPT-OSS 20B als Experiment verwendet, aber die offiziell empfohlenen Modelle für Elastic Agent Builder sind <a href="https://www.elastic.co/docs/solutions/search/agent-builder/models#recommended-models">hier</a> aufgeführt. Falls Sie fortgeschrittenere Schlussfolgerungsfähigkeiten benötigen, gibt es auch die <a href="https://huggingface.co/openai/gpt-oss-120b">120B-Parametervariante</a> , die bei komplexen Szenarien besser abschneidet, allerdings ist für die lokale Ausführung ein leistungsstärkerer Rechner erforderlich. Weitere Einzelheiten finden Sie in der <a href="https://openai.com/open-models/">offiziellen OpenAI-Dokumentation</a>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Die besten Elastic Agent Builder-Projekte und Erkenntnisse aus Cal Hacks 12.0]]></title>
            <description><![CDATA[Entdecken Sie die besten Elastic Agent Builder-Projekte von Cal Hacks 12.0 und tauchen Sie ein in unsere technischen Erkenntnisse zu Serverless, ES|QL und Agentenarchitekturen.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/agent-builder-projects-learnings-cal-hacks-12-0</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/agent-builder-projects-learnings-cal-hacks-12-0</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[JD Armada]]></dc:creator>
            <pubDate>Tue, 25 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8ba034a6f19b50521f541b8131756a8acdb52975-1280x960.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<a href="https://www.youtube.com/watch/GUI0WXNZduo">Watch video on YouTube</a><p>Vor einigen Wochen hatten wir die unglaubliche Gelegenheit, <a href="https://cal-hacks-12-0.devpost.com/">Cal Hacks 12.0</a> zu sponsern, einen der größten Präsenz-Hackathons mit über 2000 Teilnehmern aus aller Welt. Wir haben einen eigenen Preiswettbewerb für die beste Nutzung von Elastic Agent Builder auf Serverless-Plattformen angeboten, und die Resonanz war phänomenal. Innerhalb von nur 36 Stunden erhielten wir 29 Einsendungen, die Agent Builder auf kreative Weise nutzten, von der Entwicklung von Tools zur Waldbrandanalyse bis hin zu StackOverflow-Validatoren.</p><p>Neben den beeindruckenden Projekten hat uns die Erfahrung bei Cal Hacks 12.0 auch etwas ebenso Wertvolles gebracht: schnelles, unverfälschtes Feedback von Entwicklern, die zum ersten Mal mit unserem Stack in Berührung kamen. Hackathons sind einzigartige Drucktests mit engen Zeitvorgaben, keinerlei Vorkenntnissen und unvorhersehbaren Hindernissen (wie den berüchtigten WLAN-Ausfällen). Sie zeigen genau, wo die Entwicklererfahrung glänzt und wo noch Verbesserungsbedarf besteht. Dies ist heute umso wichtiger, da Entwickler auf neue Weise mit dem Elastic Stack interagieren, zunehmend über LLM-gesteuerte Workflows. In diesem Blogbeitrag werden wir genauer darauf eingehen, was die Teilnehmer mit Agent Builder erstellt haben und was wir dabei gelernt haben.</p><h2>Die Gewinnerprojekte</h2><h3>Erster Platz: AgentOverflow</h3><p>Stack Overflow neu entwickelt für die LLM- und Agentenära.</p><p>Lesen Sie <a href="https://devpost.com/software/agentoverflow">hier</a> mehr über AgentOverflow.</p><p>AgentOverflow löst ein Problem, mit dem die meisten KI-Entwickler konfrontiert sind: LLMs halluzinieren, Chatverläufe verschwinden, und Entwickler verschwenden Zeit damit, dieselben Probleme immer wieder zu lösen.</p><p>AgentOverflow erfasst, validiert und präsentiert reale Problem-Lösungs-Paare, damit Entwickler die Illusionsspirale durchbrechen und schneller Ergebnisse liefern können.</p><h4>So funktioniert es:</h4><p><strong>1. JSON teilen – das „Lösungsschema“.</strong></p><p>Ein Klick auf eine Claude-Freigabe extrahiert und erstellt eine Share Solution JSON-Datei in einem strukturierten Format, das Folgendes enthält:</p><ul><li>Problem</li><li>Kontext</li><li>Code</li><li>Tags</li><li>Die Lösungsschritte wurden verifiziert.</li></ul><p>Ein Validator (LAVA) prüft und erzwingt die Struktur, der Benutzer fügt eine Zeile zusätzlichen Kontexts hinzu, dann wird das Ganze in Elasticsearch gespeichert und indiziert.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/45a3e96f4474050a855419628c2a7338bb12c706-1600x877.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2d65cb154d8ee32fc96ff17dfa5b0bf2636e3777-1600x1002.png" style="width: 100%;" /><p><strong>2. Lösung finden</strong></p><p>Wenn Sie nicht weiterkommen, klicken Sie auf <code>Find Solution</code> AgentOverflow extrahiert dann Ihre aktuelle Konversation, erstellt daraus eine Abfrage und führt eine hybride Elasticsearch-Suche durch, um Folgendes anzuzeigen:</p><ul><li>Rangliste, von der Community validierte Korrekturen</li><li>Die genauen Eingabeaufforderungen, die das Problem ursprünglich gelöst haben</li></ul><p>Dies ermöglicht es Entwicklern, ihre aktuelle Sitzung schnell zu kopieren, einzufügen und zu entsperren.</p><p><strong>3. MCP – Kontextinjektion für LLMs</strong></p><p>Durch die Anbindung an die in Elasticsearch gespeicherten strukturierten Lösungen über MCP (Model Context Protocol) erhalten LLMs zur Laufzeit einen hochsignifikanten Kontext (Code, Protokolle, Konfigurationen, vorherige Korrekturen) ohne zusätzliche Störungen.</p><p>AgentOverflow verwendet Agent Builder mit Elasticsearch als strukturierte Speicherschicht, die relevanten Kontext in LLMs einfügt. Dadurch werden sie von passiven Chatbots zu kontextsensitiven Problemlösern.</p><h3>Zweiter Platz: MarketMind</h3><p>Eine in Echtzeit interpretierbare Darstellung der Marktenergie, ermöglicht durch sechs elastische Agenten.</p><p>Lesen Sie <a href="https://devpost.com/software/marketmind-b6cy2q">hier</a> mehr über MarketMind.</p><p>MarketMind hat sich seinen Platz verdient, indem es unerfahrenen Händlern eine Plattform bietet, die fragmentierte Marktdaten in klare Echtzeitsignale umwandelt. Anstatt Kursentwicklung, Fundamentaldaten, Stimmung und Volatilität über verschiedene Tools hinweg zu jonglieren, konsolidiert MarketMind all diese Informationen auf einer einzigen Plattform und hilft Händlern so, umsetzbare Erkenntnisse zu gewinnen. Dieses Projekt verwendete beim Erstellen seiner Agenten auch einige komplexe ES|QL-Abfragen.</p><h4>So funktioniert es:</h4><p><strong>1. Marktdaten in Echtzeit erfassen</strong></p><p>MarketMind bezieht Kursdaten, Fundamentaldaten, Stimmungsanalysen, Volatilitäts- und Risikokennzahlen von Yahoo Finance. Diese Daten werden erfasst und in mehreren Elasticsearch-Indizes organisiert.</p><p><strong>2. Sechs spezialisierte Agenten analysieren den Markt.</strong></p><p>Jeder mit Agent Builder erstellte Agent konzentriert sich auf eine andere Marktebene. Sie lesen Daten aus einem Elasticsearch-Index, berechnen ihre eigenen domänenspezifischen Metriken und generieren eine standardisierte JSON-Ausgabe mit Bewertungen und Begründungen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7d9716cca06a047a2b3584378b5c7e592a785ba1-1284x878.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5aac6a833347c0d2e596c02049ec4b4d3aae5cd7-794x764.png" style="width: 100%;" /><p><strong>3. Signale in einem einheitlichen „Marktenergie“-Modell aggregieren</strong></p><p>Die kombinierten Ergebnisse erscheinen als leuchtende Impulse um jede Aktie herum und veranschaulichen, ob sich die Dynamik verstärkt, das Risiko steigt oder sich die Stimmung ändert.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/46b3da8e3d528c5dd4e2829416c5446098acb3aa-744x718.png" style="width: 100%;" /><p><strong>4. Erkenntnisse visualisieren</strong></p><p>Das Frontend wurde mit React und <a href="https://github.com/vercel/next.js">Next.js</a> unter Verwendung von TypeScript, SVG-basierten physikbasierten Visualisierungen und <a href="https://github.com/chartjs">Chart.js</a> für Live-Candlestick-Charts erstellt. Dadurch wird die Rohanalyse in direkt umsetzbares Feedback in Echtzeit umgewandelt.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3f000c043117b77ed4127202be5a49c12e3682ba-1600x930.png" style="width: 100%;" /><h2>Weitere interessante Projekte:</h2><p>Hier sind einige weitere starke Konkurrenten, die Elastic in verschiedenen Teilen ihres Technologie-Stacks eingesetzt haben:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/76c49a853426844f475cd6b2a74999e60af20e8c-926x1080.png" style="width: 100%;" /><p>Die vollständige Liste der Projekte, die in unserem Wettbewerb eingereicht wurden, finden Sie <a href="https://cal-hacks-12-0.devpost.com/submissions/search?utf8=%E2%9C%93&amp;prize_filter%5Bprizes%5D%5B%5D=91882">hier</a>.</p><h2>Was wir von Entwicklern gelernt haben</h2><ul><li><strong>Agent Builder ist benutzerfreundlich:</strong></li></ul><p>Die meisten Teams hatten Elastic noch nie zuvor benutzt und waren dennoch in der Lage, mit wenig Unterstützung schnell Agenten zu erstellen. Wir haben einen Workshop für diejenigen veranstaltet, die mehr Unterstützung benötigten, aber die meisten waren in der Lage, ihre Daten zu erfassen und einen Agenten zu erstellen, der Aktionen auf diesen Daten durchführt.</p><ul><li><strong>LLMs sind hervorragend in <code>kNN</code> -Abfragen, benötigen aber dennoch Unterstützung bei der Generierung von ES|QL:</strong></li></ul><p>Die Aufforderung an ChatGPT-5, ES|QL-Abfragen zu generieren, lieferte falsche Informationen, wobei häufig ES|QL und SQL vermischt wurden. Die Bereitstellung der Dokumente in einer Markdown-Datei für das LLM schien eine praktikable Lösung zu sein.</p><ul><li><strong>Nur für Snapshots verfügbare ES|QL-Funktionen in die Dokumentation gelangten:</strong></li></ul><p>Die kommenden Aggregationsfunktionen <code>FIRST</code> und <code>LAST</code> sind versehentlich in unsere ES|QL-Dokumentation eingeschlichen. Da wir diese Dokumente an ChatGPT übermittelt haben, nutzte das Modell diese Funktionen pflichtgemäß, obwohl sie in Serverless noch nicht verfügbar sind. Dank des Feedbacks der Gruppe hat das Entwicklungsteam schnell einen Fix erstellt und zusammengeführt, um die Funktionen aus der veröffentlichten Dokumentation zu entfernen (<a href="https://github.com/elastic/elasticsearch/pull/137341">PR #137341</a>).</p><ul><li><strong>Fehlende Serverless-spezifische Anleitung:</strong></li></ul><p>Ein Team versuchte, <code>LOOKUP JOIN</code> für einen Index zu aktivieren, der nicht im Lookup-Modus erstellt wurde. Die Fehlermeldung veranlasste sie, Befehle zu verfolgen, die auf Serverless nicht existieren. Wir haben dies dem Produktteam mitgeteilt, das umgehend einen Fix für eine Serverless-spezifische, umsetzbare Fehlermeldung erstellt hat. Längerfristig besteht die Vision darin, die Komplexität der Neuindizierung vollständig zu verbergen (<a href="https://github.com/elastic/elasticsearch-serverless/issues/4838">Problem #4838</a>).</p><ul><li><strong>Wert von Präsenzveranstaltungen:</strong></li></ul><p>Online-Hackathons sind toll, aber nichts kommt an den schnellen Feedback-Loop heran, den man erhält, wenn man Seite an Seite mit Entwicklern Fehler behebt. Wir haben beobachtet, wie Teams Agent Builder in verschiedenen Anwendungsfällen integriert haben, festgestellt, wo die Entwicklererfahrung mit ES|QL verbessert werden konnte, und Probleme viel schneller behoben, als dies über asynchrone Kanäle zu versuchen.</p><h2>Fazit</h2><p>Cal Hacks 12.0 bot uns mehr als ein Wochenende voller cooler Demos; es gab uns auch Einblick in die Art und Weise, wie neue Entwickler mit dem Elastic Stack interagieren. Innerhalb von nur 36 Stunden sahen wir, wie Teams Agent Builder einsetzten, Daten in Elasticsearch einspielten, Multiagentensysteme entwarfen und unsere Funktionen auf vielfältige Weise testeten. Die Veranstaltung erinnerte uns auch daran, warum Präsenzveranstaltungen so wichtig sind. Die schnellen Feedbackschleifen, die echten Gespräche und das praktische Debugging halfen uns, die aktuellen Bedürfnisse der Entwickler zu verstehen. Wir freuen uns darauf, das Gelernte an das Ingenieurteam weiterzugeben. Wir sehen uns beim nächsten Hackathon.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Erstellung eines LLM-Agenten-Newsrooms mit A2A-Protokoll und MCP in Elasticsearch: Teil II]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie einen spezialisierten hybriden LLM-Agenten-Newsroom aufbauen, indem Sie das A2A-Protokoll für die Agentenzusammenarbeit und MCP für den Werkzeugzugriff in Elasticsearch verwenden.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/a2a-protocol-mcp-llm-agent-workflow-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/a2a-protocol-mcp-llm-agent-workflow-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Justin Castilla]]></dc:creator>
            <pubDate>Mon, 24 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f87330e5d4ca961593b3cfb861ca850a4cc34186-1519x1173.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<h2>A2A und MCP: Der Code in Aktion</h2><p>Dies ist der Begleitartikel zum Artikel „Erstellung eines LLM-Agent-Newsrooms mit A2A-Protokoll und MCP in Elasticsearch!“, in dem die Vorteile der Implementierung von A2A- und MCP-Architekturen innerhalb desselben Agenten erläutert wurden, um die einzigartigen Vorteile beider Frameworks voll auszuschöpfen. Ein <a href="https://github.com/justincastilla/elastic-newsroom">Repository</a> steht zur Verfügung, falls Sie die Demo selbst ausführen möchten.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7196f004089127f83547b2e5dc3f663205cfcdce-1162x1600.png" style="width: 100%;" /><p>Lassen Sie uns gemeinsam durchgehen, wie unsere Redakteure mithilfe von A2A und MCP zusammenarbeiten, um einen Nachrichtenartikel zu erstellen. Das zugehörige Repository, um die Agenten in Aktion zu sehen, finden Sie <a href="https://github.com/justincastilla/elastic-newsroom">hier</a>.</p><h3>Schritt 1: Aufgabenstellung für die Geschichte</h3><p>Der <strong>Nachrichtenchef</strong> (in der Rolle des Auftraggebers) vergibt eine Story:</p><pre><code>{
  "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"
    }
  }
}</code></pre><h3>Schritt 2: Der Reporter bittet um Recherche</h3><p>Der <strong>Reporteragent</strong> erkennt, dass er Hintergrundinformationen benötigt und delegiert diese über A2A an den <strong>Rechercheagenten</strong> :</p><pre><code>{
  "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"
    }
  }
}</code></pre><h3>Schritt 3: Der Reporter bittet den Archivar um historischen Kontext.</h3><p>Der <strong>Reporteragent</strong> erkennt, dass ein historischer Kontext die Geschichte stärken würde. Es delegiert über A2A an den <strong>Archivagenten</strong> (basierend auf <a href="https://www.elastic.co/docs/solutions/search/elastic-agent-builder">dem A2A-Agenten von Elastic</a>), um das Elasticsearch-basierte Artikelarchiv des Newsrooms zu durchsuchen:</p><pre><code>{
  "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
    }
  }
}</code></pre><h3>Schritt 4: Der Archivierungsagent verwendet den Elastic A2A-Agenten mit MCP.</h3><p>Der <strong>Archive Agent</strong> verwendet den A2A Agent von Elastic, der wiederum MCP nutzt, um auf die Elasticsearch-Tools zuzugreifen. Dies veranschaulicht die Hybridarchitektur, bei der A2A die Zusammenarbeit von Agenten ermöglicht, während MCP den Zugriff auf Werkzeuge bereitstellt:</p><pre><code># 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</code></pre><p>Der <strong>Archivagent</strong> empfängt umfassende historische Daten vom A2A-Agenten von Elastic und sendet sie an den Reporter zurück:</p><pre><code>{
  "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
    }
  }
}</code></pre><p>Dieser Schritt demonstriert, wie sich der A2A-Agent von Elastic in den Workflow der Nachrichtenredaktion integriert. Der Archive Agent (ein speziell für Redaktionen entwickelter Agent) koordiniert sich mit dem A2A Agent von Elastic (einem Drittanbieter-Spezialisten), um die leistungsstarken Such- und Analysefunktionen von Elasticsearch optimal zu nutzen. Der Elastic-Agent nutzt intern MCP für den Zugriff auf Elasticsearch-Tools, was die klare Trennung zwischen Agentenkoordination (A2A) und Toolzugriff (MCP) verdeutlicht.</p><h3>Schritt 5: Der Forscher nutzt MCP-Server</h3><p>Der <strong>Forschungsagent</strong> greift auf mehrere MCP-Server zu, um Informationen zu sammeln:</p><pre><code># 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)</code></pre><h3>Schritt 6: Der Forscher übermittelt die Daten an den Reporter.</h3><p>Der <strong>Rechercheagent</strong> sendet die umfassenden Rechercheergebnisse über A2A zurück:</p><pre><code>{
  "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
    }
  }
}</code></pre><h3>Schritt 7: Der Reporter schreibt einen Artikel</h3><p>Der <strong>Reporteragent</strong> nutzt die Forschungsdaten und seine eigenen LLM-Fähigkeiten, um den Artikel zu schreiben. Beim Schreiben nutzt der Reporter die MCP-Server für Stil und Vorlagen:</p><pre><code># 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</code></pre><h3>Schritt 8: Geringes Vertrauen löst erneute Recherche aus</h3><p>Der <strong>Reporteragent</strong> prüft den Entwurf und stellt fest, dass eine Behauptung nur geringes Vertrauen genießt. Es sendet eine weitere Anfrage an den <strong>Forschungsagenten</strong>:</p><pre><code>{
  "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"
  }
}</code></pre><p>Der <strong>Forscher</strong> überprüft die Behauptung mithilfe von Faktencheck-Servern von MCP und liefert aktualisierte Informationen zurück:</p><pre><code>{
  "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"]
      }
    ]
  }
}</code></pre><h3>Schritt 9: Der Reporter überarbeitet den Text und reicht ihn beim Redakteur ein.</h3><p>Der <strong>Reporter</strong> integriert die verifizierten Fakten und sendet den fertigen Entwurf per A2A an den <strong>Redaktionsagenten</strong> :</p><pre><code>{
  "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
    }
  }
}</code></pre><h3>Schritt 10: Redaktionelle Überprüfungen mithilfe von MCP-Tools</h3><p>Der <strong>Redaktionsagent</strong> nutzt mehrere MCP-Server zur Überprüfung des Artikels:</p><pre><code># 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</code></pre><p>Der <strong>Redakteur</strong> genehmigt den Artikel und leitet ihn weiter:</p><pre><code>{
  "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]"
  }
}</code></pre><h3>Schritt 11: Der Herausgeber veröffentlicht über CI/CD</h3><p>Schließlich veröffentlicht der <strong>Druckeragent</strong> den genehmigten Artikel mithilfe der MCP-Server für die CMS- und CI/CD-Pipeline:</p><pre><code># 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"]
    }</code></pre><p>Der <strong>Verlag</strong> bestätigt die Veröffentlichung via A2A:</p><pre><code>{
  "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
    }
  }
}</code></pre><p>Hier ist die vollständige Abfolge des A2A-Workflows im zugehörigen Repository unter Verwendung der oben beschriebenen Agenten.</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">#</td><td style="border: 1px solid #ccc; padding: 4px;">Aus</td><td style="border: 1px solid #ccc; padding: 4px;">Zu</td><td style="border: 1px solid #ccc; padding: 4px;">Aktion</td><td style="border: 1px solid #ccc; padding: 4px;">Protokoll</td><td style="border: 1px solid #ccc; padding: 4px;">Beschreibung</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">1</td><td style="border: 1px solid #ccc; padding: 4px;">Benutzer</td><td style="border: 1px solid #ccc; padding: 4px;">Nachrichtenchef</td><td style="border: 1px solid #ccc; padding: 4px;">Geschichte zuweisen</td><td style="border: 1px solid #ccc; padding: 4px;">HTTP POST</td><td style="border: 1px solid #ccc; padding: 4px;">Der Nutzer gibt Thema und Blickwinkel der Geschichte vor.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">2</td><td style="border: 1px solid #ccc; padding: 4px;">Nachrichtenchef</td><td style="border: 1px solid #ccc; padding: 4px;">Intern</td><td style="border: 1px solid #ccc; padding: 4px;">Geschichte erstellen</td><td style="border: 1px solid #ccc; padding: 4px;">-</td><td style="border: 1px solid #ccc; padding: 4px;">Erstellt einen Story-Datensatz mit eindeutiger ID</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">3</td><td style="border: 1px solid #ccc; padding: 4px;">Nachrichtenchef</td><td style="border: 1px solid #ccc; padding: 4px;">Reporter</td><td style="border: 1px solid #ccc; padding: 4px;">Delegiertenzuweisung</td><td style="border: 1px solid #ccc; padding: 4px;">A2A</td><td style="border: 1px solid #ccc; padding: 4px;">Sendet die Story-Aufgabe über das A2A-Protokoll.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">4</td><td style="border: 1px solid #ccc; padding: 4px;">Reporter</td><td style="border: 1px solid #ccc; padding: 4px;">Intern</td><td style="border: 1px solid #ccc; padding: 4px;">Auftrag annehmen</td><td style="border: 1px solid #ccc; padding: 4px;">-</td><td style="border: 1px solid #ccc; padding: 4px;">Interne Zuordnung der Lagerbestände</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">5</td><td style="border: 1px solid #ccc; padding: 4px;">Reporter</td><td style="border: 1px solid #ccc; padding: 4px;">MCP-Server</td><td style="border: 1px solid #ccc; padding: 4px;">Gliederung erstellen</td><td style="border: 1px solid #ccc; padding: 4px;">MCP/HTTP</td><td style="border: 1px solid #ccc; padding: 4px;">Erstellt Gliederungen für Artikel und Forschungsfragen</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">6a</td><td style="border: 1px solid #ccc; padding: 4px;">Reporter</td><td style="border: 1px solid #ccc; padding: 4px;">Forscher</td><td style="border: 1px solid #ccc; padding: 4px;">Forschungsanfrage</td><td style="border: 1px solid #ccc; padding: 4px;">A2A</td><td style="border: 1px solid #ccc; padding: 4px;">Sendet Fragen (parallel zu 6b)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">6b</td><td style="border: 1px solid #ccc; padding: 4px;">Reporter</td><td style="border: 1px solid #ccc; padding: 4px;">Archivar</td><td style="border: 1px solid #ccc; padding: 4px;">Archiv durchsuchen</td><td style="border: 1px solid #ccc; padding: 4px;">A2A JSONRPC</td><td style="border: 1px solid #ccc; padding: 4px;">Durchsucht historische Artikel (parallel zu 6a)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">7</td><td style="border: 1px solid #ccc; padding: 4px;">Forscher</td><td style="border: 1px solid #ccc; padding: 4px;">MCP-Server</td><td style="border: 1px solid #ccc; padding: 4px;">Forschungsfragen</td><td style="border: 1px solid #ccc; padding: 4px;">MCP/HTTP</td><td style="border: 1px solid #ccc; padding: 4px;">Nutzt Anthropic über MCP, um Fragen zu beantworten</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">8</td><td style="border: 1px solid #ccc; padding: 4px;">Forscher</td><td style="border: 1px solid #ccc; padding: 4px;">Reporter</td><td style="border: 1px solid #ccc; padding: 4px;">Forschung zurückgeben</td><td style="border: 1px solid #ccc; padding: 4px;">A2A</td><td style="border: 1px solid #ccc; padding: 4px;">Antworten zur Renditeforschung</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">9</td><td style="border: 1px solid #ccc; padding: 4px;">Archivar</td><td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">Suchindex</td><td style="border: 1px solid #ccc; padding: 4px;">ES REST-API</td><td style="border: 1px solid #ccc; padding: 4px;">Abfragen zum Nachrichtenarchivindex</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">10</td><td style="border: 1px solid #ccc; padding: 4px;">Archivar</td><td style="border: 1px solid #ccc; padding: 4px;">Reporter</td><td style="border: 1px solid #ccc; padding: 4px;">Zurück zum Archiv</td><td style="border: 1px solid #ccc; padding: 4px;">A2A JSONRPC</td><td style="border: 1px solid #ccc; padding: 4px;">Gibt historische Suchergebnisse zurück</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">11</td><td style="border: 1px solid #ccc; padding: 4px;">Reporter</td><td style="border: 1px solid #ccc; padding: 4px;">MCP-Server</td><td style="border: 1px solid #ccc; padding: 4px;">Artikel generieren</td><td style="border: 1px solid #ccc; padding: 4px;">MCP/HTTP</td><td style="border: 1px solid #ccc; padding: 4px;">Erstellt einen Artikel mit Recherche-/Archivkontext</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">12</td><td style="border: 1px solid #ccc; padding: 4px;">Reporter</td><td style="border: 1px solid #ccc; padding: 4px;">Intern</td><td style="border: 1px solid #ccc; padding: 4px;">Entwurf speichern</td><td style="border: 1px solid #ccc; padding: 4px;">-</td><td style="border: 1px solid #ccc; padding: 4px;">Speichert den Entwurf intern.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">13</td><td style="border: 1px solid #ccc; padding: 4px;">Reporter</td><td style="border: 1px solid #ccc; padding: 4px;">Nachrichtenchef</td><td style="border: 1px solid #ccc; padding: 4px;">Entwurf einreichen</td><td style="border: 1px solid #ccc; padding: 4px;">A2A</td><td style="border: 1px solid #ccc; padding: 4px;">Reicht den fertigen Entwurf ein</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">14</td><td style="border: 1px solid #ccc; padding: 4px;">Nachrichtenchef</td><td style="border: 1px solid #ccc; padding: 4px;">Intern</td><td style="border: 1px solid #ccc; padding: 4px;">Update zur Geschichte</td><td style="border: 1px solid #ccc; padding: 4px;">-</td><td style="border: 1px solid #ccc; padding: 4px;">Speichert den Entwurf, aktualisiert den Status auf "draft_submitted"</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">15</td><td style="border: 1px solid #ccc; padding: 4px;">Nachrichtenchef</td><td style="border: 1px solid #ccc; padding: 4px;">Editor</td><td style="border: 1px solid #ccc; padding: 4px;">Entwurf prüfen</td><td style="border: 1px solid #ccc; padding: 4px;">A2A</td><td style="border: 1px solid #ccc; padding: 4px;">Automatische Weiterleitung an den Redakteur zur Überprüfung</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">16</td><td style="border: 1px solid #ccc; padding: 4px;">Editor</td><td style="border: 1px solid #ccc; padding: 4px;">MCP-Server</td><td style="border: 1px solid #ccc; padding: 4px;">Rezensionsartikel</td><td style="border: 1px solid #ccc; padding: 4px;">MCP/HTTP</td><td style="border: 1px solid #ccc; padding: 4px;">Analysiert Inhalte mithilfe von Anthropic über MCP</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">17</td><td style="border: 1px solid #ccc; padding: 4px;">Editor</td><td style="border: 1px solid #ccc; padding: 4px;">Nachrichtenchef</td><td style="border: 1px solid #ccc; padding: 4px;">Rückgabeprüfung</td><td style="border: 1px solid #ccc; padding: 4px;">A2A</td><td style="border: 1px solid #ccc; padding: 4px;">Sendet redaktionelles Feedback und Vorschläge</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">18</td><td style="border: 1px solid #ccc; padding: 4px;">Nachrichtenchef</td><td style="border: 1px solid #ccc; padding: 4px;">Intern</td><td style="border: 1px solid #ccc; padding: 4px;">Ladenbewertung</td><td style="border: 1px solid #ccc; padding: 4px;">-</td><td style="border: 1px solid #ccc; padding: 4px;">Feedback der Ladenredaktion</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">19</td><td style="border: 1px solid #ccc; padding: 4px;">Nachrichtenchef</td><td style="border: 1px solid #ccc; padding: 4px;">Reporter</td><td style="border: 1px solid #ccc; padding: 4px;">Änderungen anwenden</td><td style="border: 1px solid #ccc; padding: 4px;">A2A</td><td style="border: 1px solid #ccc; padding: 4px;">Feedback zur Routenbewertung an den Reporter</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">20</td><td style="border: 1px solid #ccc; padding: 4px;">Reporter</td><td style="border: 1px solid #ccc; padding: 4px;">MCP-Server</td><td style="border: 1px solid #ccc; padding: 4px;">Änderungen anwenden</td><td style="border: 1px solid #ccc; padding: 4px;">MCP/HTTP</td><td style="border: 1px solid #ccc; padding: 4px;">Überarbeitet den Artikel basierend auf dem Feedback</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">21</td><td style="border: 1px solid #ccc; padding: 4px;">Reporter</td><td style="border: 1px solid #ccc; padding: 4px;">Intern</td><td style="border: 1px solid #ccc; padding: 4px;">Entwurf aktualisieren</td><td style="border: 1px solid #ccc; padding: 4px;">-</td><td style="border: 1px solid #ccc; padding: 4px;">Aktualisiert den Entwurf mit den Überarbeitungen</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">22</td><td style="border: 1px solid #ccc; padding: 4px;">Reporter</td><td style="border: 1px solid #ccc; padding: 4px;">Nachrichtenchef</td><td style="border: 1px solid #ccc; padding: 4px;">Rückgabe überarbeitet</td><td style="border: 1px solid #ccc; padding: 4px;">A2A</td><td style="border: 1px solid #ccc; padding: 4px;">Artikel über die Rückgabe überarbeitet</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">23</td><td style="border: 1px solid #ccc; padding: 4px;">Nachrichtenchef</td><td style="border: 1px solid #ccc; padding: 4px;">Intern</td><td style="border: 1px solid #ccc; padding: 4px;">Update zur Geschichte</td><td style="border: 1px solid #ccc; padding: 4px;">-</td><td style="border: 1px solid #ccc; padding: 4px;">Die Geschäfte haben den Entwurf überarbeitet, der Status lautet nun „überarbeitet“.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">24</td><td style="border: 1px solid #ccc; padding: 4px;">Nachrichtenchef</td><td style="border: 1px solid #ccc; padding: 4px;">Herausgeber</td><td style="border: 1px solid #ccc; padding: 4px;">Artikel veröffentlichen</td><td style="border: 1px solid #ccc; padding: 4px;">A2A</td><td style="border: 1px solid #ccc; padding: 4px;">Automatische Weiterleitung zum Herausgeber</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">25</td><td style="border: 1px solid #ccc; padding: 4px;">Herausgeber</td><td style="border: 1px solid #ccc; padding: 4px;">MCP-Server</td><td style="border: 1px solid #ccc; padding: 4px;">Tags generieren</td><td style="border: 1px solid #ccc; padding: 4px;">MCP/HTTP</td><td style="border: 1px solid #ccc; padding: 4px;">Erstellt Tags und Kategorien</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">26</td><td style="border: 1px solid #ccc; padding: 4px;">Herausgeber</td><td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">Indexartikel</td><td style="border: 1px solid #ccc; padding: 4px;">ES REST-API</td><td style="border: 1px solid #ccc; padding: 4px;">Indexiert Artikel im Nachrichtenarchivindex</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">27</td><td style="border: 1px solid #ccc; padding: 4px;">Herausgeber</td><td style="border: 1px solid #ccc; padding: 4px;">Dateisystem</td><td style="border: 1px solid #ccc; padding: 4px;">Markdown speichern</td><td style="border: 1px solid #ccc; padding: 4px;">Datei-E/A</td><td style="border: 1px solid #ccc; padding: 4px;">Speichert den Artikel als .md-Datei Datei in /articles</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">28</td><td style="border: 1px solid #ccc; padding: 4px;">Herausgeber</td><td style="border: 1px solid #ccc; padding: 4px;">Nachrichtenchef</td><td style="border: 1px solid #ccc; padding: 4px;">Veröffentlichung bestätigen</td><td style="border: 1px solid #ccc; padding: 4px;">A2A</td><td style="border: 1px solid #ccc; padding: 4px;">Rückgabestatus: Erfolgreich</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">29</td><td style="border: 1px solid #ccc; padding: 4px;">Nachrichtenchef</td><td style="border: 1px solid #ccc; padding: 4px;">Intern</td><td style="border: 1px solid #ccc; padding: 4px;">Update zur Geschichte</td><td style="border: 1px solid #ccc; padding: 4px;">-</td><td style="border: 1px solid #ccc; padding: 4px;">Aktualisiert den Status der Story auf „veröffentlicht“.</td>
                    </tr>
              </tbody>
            </table>
          <h2>Fazit</h2><p>Sowohl A2A als auch MCP spielen eine wichtige Rolle im modernen erweiterten LLM-Infrastrukturparadigma. A2A bietet Flexibilität für komplexe Multiagentensysteme, jedoch potenziell geringere Portabilität und höhere operative Komplexität. MCP bietet einen standardisierten Ansatz für die Tool-Integration, der einfacher zu implementieren und zu warten ist, ist jedoch nicht für die Orchestrierung mehrerer Agenten ausgelegt.</p><p>Die Wahl ist nicht binär. Wie unser Beispiel aus der Redaktion zeigt, kombinieren die anspruchsvollsten und effektivsten LLM-gestützten Systeme oft beide Ansätze: Agenten koordinieren und spezialisieren sich über A2A-Protokolle, während sie über MCP-Server auf ihre Tools und Ressourcen zugreifen. Diese Hybridarchitektur bietet die organisatorischen Vorteile von Multiagentensystemen sowie die Standardisierungs- und Ökosystemvorteile von MCP. Dies deutet darauf hin, dass möglicherweise gar keine Wahlmöglichkeit besteht: Man könnte einfach beide als Standardansatz verwenden.</p><p>Es liegt an Ihnen als Entwickler oder Architekt, die beste Mischung beider Lösungen zu testen und zu ermitteln, um das richtige Ergebnis für Ihren spezifischen Anwendungsfall zu erzielen. Das Verständnis der Stärken, Grenzen und geeigneten Anwendungsbereiche der einzelnen Ansätze ermöglicht es Ihnen, effektivere, wartungsfreundlichere und skalierbare KI-Systeme zu entwickeln.</p><p>Egal, ob Sie eine digitale Nachrichtenredaktion, eine Kundenserviceplattform, einen Forschungsassistenten oder eine andere LLM-gestützte Anwendung entwickeln – die sorgfältige Berücksichtigung Ihrer Koordinierungsbedürfnisse (A2A) und Ihrer Anforderungen an den Werkzeugzugriff (MCP) wird Sie auf den Weg zum Erfolg führen.</p><h2>Weitere Ressourcen</h2><ul><li><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></li><li><strong>A2A-Spezifikation</strong>: <a href="https://a2a-protocol.org/latest/specification/">https://a2a-protocol.org/latest/specification/</a></li><li><strong>A2A- und MCP-Integration</strong>: <a href="https://a2a-protocol.org/latest/topics/a2a-and-mcp/">https://a2a-protocol.org/latest/topics/a2a-and-mcp/</a></li><li><strong>Model Context Protocol</strong>: <a href="https://modelcontextprotocol.io/">https://modelcontextprotocol.io</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Sie wissen schon, Kontext – Teil III: Die Leistungsfähigkeit der hybriden Suche im Kontext-Engineering]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie Kontext-Engineering und hybride Suche nutzen können, um die Genauigkeit der KI-Ausgabe mithilfe von Aggregationen, RBAC und Nicht-Inhaltssignalen zu verbessern.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/context-engineering-hybrid-search-agentic-ai-accuracy</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/context-engineering-hybrid-search-agentic-ai-accuracy</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Woody Walton]]></dc:creator>
            <pubDate>Thu, 20 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b82ff25242e4229cc20b218d9cc91c60cfd680bc-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Wir haben sowohl die hybride Suche (<a href="https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-evolution-agentic-ai">Teil I</a>) als auch das Kontext-Engineering (<a href="https://www.elastic.co/search-labs/blog/context-engineering-llm-evolution-agentic-ai">Teil II</a>) besprochen; nun wollen wir uns damit befassen, wie sie zusammenwirken, um den größtmöglichen Effekt bei der Bereitstellung zielgerichteten Kontexts für RAG- und agentenbasierte KI-Operationen zu erzielen.</p><h2>Die Suche ist nicht tot, sie hat sich nur verlagert.</h2><p>Wir haben also einen Wandel erlebt: von der primären Suche nach Kontext über ein Textfeld und der Verwendung der zurückgegebenen Informationen (des Kontexts), um die Antworten selbst zu konstruieren, hin zur Verwendung natürlicher Sprache, um einem Agenten mitzuteilen, was wir wollen, und lassen ihn die Antwort automatisch für uns recherchieren und zusammenstellen. Viele in der Tech-Welt verweisen auf diesen Wandel und verkünden, dass „Suche tot ist“ (nun ja, die SEO- und AdWords-Welt <a href="https://www.pewresearch.org/short-reads/2025/07/22/google-users-are-less-likely-to-click-on-links-when-an-ai-summary-appears-in-the-results/">verändert sich definitiv</a>: <a href="https://www.wired.com/story/goodbye-seo-hello-geo-brandlight-openai/">GEO,</a> irgendjemand?), aber die Suche ist für operative Abläufe immer noch absolut entscheidend – sie wird nur heute größtenteils im Verborgenen mithilfe von Tools durchgeführt.</p><p>Bisher waren Menschen die Hauptverantwortlichen für die Beurteilung der subjektiven Relevanz: Jeder Nutzer hatte seine eigenen Gründe für die Durchführung der Suche, und seine persönlichen Erfahrungen prägten die relative Genauigkeit der Ergebnisse. Wenn wir darauf vertrauen sollen, dass Agenten zu demselben (oder einem besseren) Schluss kommen können, zu dem wir gekommen wären, müssen wir sicherstellen, dass die Kontextinformationen, auf die sie Zugriff haben, so nah wie möglich an unserer subjektiven Absicht liegen. Wir müssen den Kontext, den wir LLMs bieten, auf dieses Ziel ausrichten!</p><h2>Kontextgenerierung mit hybrider Suchabfrage</h2><p>Zur Erinnerung an Teil I: Die Hybridsuche von Elastic kombiniert die Stärken der traditionellen schlüsselwortbasierten Suche (Syntaxflexibilität, Schlüsselwortgenauigkeit und Relevanzbewertung) mit dem semantischen Verständnis der Vektorähnlichkeitssuche und bietet mehrere Reranking-Techniken. Diese Synergie (eine treffendere Verwendung dieses Wortes wurde noch nie gefunden!) ermöglicht hochrelevante Ergebnisse mit Suchanfragen, die wesentlich differenzierter auf die Inhalte abzielen. Es geht nicht nur darum, dass man die subjektive Relevanz als <em>eine</em> der Abrufphasen anwenden kann; es geht vielmehr darum, dass der Abruf in der ersten Phase die Relevanzbewertung zusammen mit all diesen anderen Modi gleichzeitig beinhalten kann.</p><h3>Überragende Genauigkeit und Effizienz</h3><p>Die Verwendung einer Datenplattform, die verteilte Suche, Abfrage und Neubewertung als primäre Kontextabfrage-Engine ermöglicht, ist sehr sinnvoll. Sie können eine erweiterte Abfragesyntax verwenden, um die fehlende Komponente der subjektiven Absicht hinzuzufügen und Inhalte herauszufiltern, die vom Wert der zurückgegebenen Kontextinformationen ablenken oder diesen verfälschen könnten. Sie können aus den verfügbaren Syntaxoptionen auswählen oder Modalitäten zu einer einzigen Suche kombinieren, die auf jeden Datentyp auf die für ihn beste Weise abzielt, und diese dann mit Reranking kombinieren/neu anordnen. Sie können die Antwort so filtern, dass sie nur die gewünschten Felder/Werte enthält und überflüssige Daten fernhält. Im Dienste der Agenten ermöglicht diese Targeting-Flexibilität die Entwicklung von Tools, die äußerst präzise beim Abrufen von Kontextinformationen sind.</p><h3>Kontextverfeinerung (Aggregationen und nicht-inhaltliche Signale)</h3><p>Aggregationen können besonders nützlich sein, um den Inhalt zu gestalten, den ein Tool im Kontextfenster anzeigt. Aggregationen liefern naturgemäß numerisch basierte Fakten über die Struktur der zurückgegebenen Kontextdaten, was es LLMs erleichtert und genauer macht, darüber zu argumentieren. Da Aggregationen hierarchisch verschachtelt werden können, ist dies eine einfache Möglichkeit, dem LLM mehrstufige Details hinzuzufügen, um ein differenzierteres Verständnis zu erzeugen. Aggregationen können auch bei der Verwaltung der Kontextfenstergröße helfen – Sie können ein Abfrageergebnis von 100.000 Dokumenten leicht auf einige hundert Tokens aggregierter Erkenntnisse reduzieren.</p><p>Nicht-inhaltliche Signale sind die in Ihren Daten enthaltenen Indikatoren, die Ihnen ein umfassenderes Bild dessen vermitteln, was Sie betrachten; es handelt sich um zusätzliche Merkmale der Ergebnisse, wie beispielsweise Popularität, Aktualität, geografische Lage, Kategorien, Vielfalt der Anbieter oder Preisklassen. Diese Informationsschnipsel können für den Agenten hilfreich sein, um die Bedeutung des empfangenen Kontextes einzuschätzen. Einige einfache Beispiele veranschaulichen dies am besten:</p><ul><li><strong>Hervorhebung kürzlich veröffentlichter und beliebter Inhalte</strong> – Stellen Sie sich vor, Sie verfügen über eine Wissensdatenbank mit Artikeln. Sie möchten Artikel finden, die für die Suchanfrage eines Nutzers relevant sind, aber Sie möchten auch Artikel hervorheben, die sowohl aktuell sind als auch von anderen Nutzern als hilfreich empfunden wurden (z. B. eine hohe Anzahl von „Gefällt mir“-Angaben haben). In diesem Szenario können wir eine Hybridsuche verwenden, um relevante Artikel zu finden und sie dann anhand einer Kombination aus Veröffentlichungsdatum und Popularität neu zu ordnen.</li><li><strong>E-Commerce-Suche mit Umsatz- und Lagerbestandsanpassung</strong> – Im E-Commerce-Umfeld möchten Sie Ihren Kunden Produkte anzeigen, die zu ihrem Suchbegriff passen, aber Sie möchten auch Produkte bewerben, die sich gut verkaufen und auf Lager sind. Um Frustration bei den Kunden zu vermeiden, sollten Sie Produkte mit geringem Lagerbestand möglicherweise in der Rangfolge herabstufen.</li><li><strong>Priorisierung von schwerwiegenden Problemen in einem Bugtracker</strong> - Für ein Softwareentwicklungsteam ist es bei der Suche nach Problemen entscheidend, dass schwerwiegende, prioritäre und kürzlich aktualisierte Probleme zuerst angezeigt werden. Sie können nicht-signalgebende Kriterien wie „Kritikalität“ und „meistdiskutiert“ verwenden, um verschiedene Faktoren unabhängig voneinander zu gewichten und so sicherzustellen, dass die wichtigsten und am aktivsten diskutierten Themen ganz oben stehen.</li></ul><p>Diese und weitere Beispielabfragen finden Sie auf der zugehörigen <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/you-know-for-context/">Inhaltsseite</a> von Elasticsearch Labs.</p><h3>Sicherheitsdurchsetzung</h3><p>Ein entscheidender Vorteil der Nutzung einer suchbasierten Geschwindigkeitsschicht wie Elastic für Context Engineering ist ihr integriertes Sicherheitsframework. Die Plattform von Elastic stellt sicher, dass der Kontext, der an agentenbasierte und generative KI-Operationen geliefert wird, sensible, privat gehaltene Informationen durch granulare rollenbasierte Zugriffskontrolle (RBAC) und attributbasierte Zugriffskontrolle (ABAC) respektiert und schützt. Dies bedeutet, dass Anfragen nicht nur effizient bearbeitet werden, sondern dass die Ergebnisse auch nach den spezifischen Berechtigungen des Agenten oder des Benutzers, der die Anfrage initiiert, gefiltert werden.</p><p>Die Agenten werden als authentifizierter Benutzer ausgeführt, sodass die Sicherheit implizit durch die in die Plattform integrierten Sicherheitsfunktionen gewährleistet ist:</p><ul><li><strong>Feingranulare Berechtigungen:</strong> Definieren Sie den Zugriff auf Dokument-, Feld- oder sogar Begriffsebene und stellen Sie so sicher, dass KI-Agenten nur Daten erhalten, zu deren Einsicht sie berechtigt sind.</li><li><strong>Rollenbasierte Zugriffskontrolle (RBAC):</strong> Agenten oder Benutzern werden Rollen zugewiesen, wodurch ihnen basierend auf ihren definierten Verantwortlichkeiten Zugriff auf bestimmte Datensätze oder Funktionalitäten gewährt wird.</li><li><strong>Attributbasierte Zugriffskontrolle (ABAC):</strong> Implementieren Sie dynamische Zugriffsrichtlinien basierend auf Attributen der Daten, des Benutzers oder der Umgebung, wodurch eine hochgradig anpassungsfähige und kontextsensitive Sicherheit ermöglicht wird.</li><li><strong>Dokumentenebene-Sicherheit (DLS) und Feldebene-Sicherheit (FLS):</strong> Diese Funktionen gewährleisten, dass auch innerhalb eines abgerufenen Dokuments nur autorisierte Abschnitte sichtbar sind, wodurch die Offenlegung sensibler Informationen verhindert wird.</li><li><strong>Integration mit der Unternehmenssicherheit:</strong> Nahtlose Integration mit bestehenden Identitätsmanagementsystemen (wie LDAP, SAML, OIDC), um einheitliche Sicherheitsrichtlinien im gesamten Unternehmen durchzusetzen.</li></ul><p>Durch die direkte Integration dieser Sicherheitsmaßnahmen in den Kontextabrufmechanismus fungiert Elastic als sicherer Wächter, der sicherstellt, dass KI-Agenten innerhalb definierter Datengrenzen arbeiten, eine unbefugte Datenoffenlegung verhindert und die Einhaltung der Datenschutzbestimmungen gewährleistet. Dies ist von entscheidender Bedeutung für den Aufbau von Vertrauen in agentenbasierte KI-Systeme, die vertrauliche oder geschützte Informationen verarbeiten.</p><p>Als zusätzlichen Vorteil verringern Sie durch die Verwendung einer einheitlichen Datengeschwindigkeitsschicht über Ihren Unternehmensdatenquellen die unerwarteten Ad-hoc-Abfragelasten auf diese Repositories, die agentenbasierte Tools erzeugen würden. Sie erhalten einen zentralen Ort, um alles nahezu in Echtzeit zu durchsuchen, und einen Ort, um Sicherheits- und Governance-Kontrollen anzuwenden.</p><h2>Hybride suchbasierte Tools</h2><p>Es gibt einige Kernfunktionen (und <a href="https://www.elastic.co/blog/whats-new-elastic-9-2-0">ständig kommen neue hinzu</a>) der Elastic-Plattform, die das Streben nach Kontextentwicklung enorm beschleunigen. Das Wichtigste hierbei ist, dass die Plattform eine Vielzahl von Möglichkeiten bietet, um Ziele zu erreichen, und die Flexibilität besitzt, Methoden anzupassen, zu verändern und zu erweitern, wenn sich das KI-Ökosystem weiterentwickelt.</p><h3>Wir stellen Ihnen Agent Builder vor</h3><p>Elastic <a href="https://www.elastic.co/elasticsearch/agent-builder">Agent Builder</a> ist unser erster Ausflug in die Welt der agentenbasierten KI-Tools, die entwickelt wurden, um mit den Daten zu kommunizieren, die Sie bereits in Elastic speichern. Agent Builder bietet eine Chat-Oberfläche, mit der Benutzer ihre eigenen Agenten und Tools innerhalb von Kibana erstellen und verwalten können. Es verfügt über integrierte MCP- und A2A-Server, programmatische APIs und eine Reihe vorkonfigurierter Systemtools zum Abfragen und Erkunden von Elasticsearch-Indizes sowie zum Generieren von ES|QL-Abfragen aus natürlicher Sprache. Mit Agent Builder können Sie benutzerdefinierte Tools erstellen, die die an den Agenten zurückgegebenen Kontextdaten mithilfe einer ausdrucksstarken <a href="https://www.elastic.co/docs/reference/query-languages/esql">ES|QL-</a> Abfragesyntax gezielt ansprechen und formen.</p><p>Wie funktioniert die hybride Suche in ES|QL, fragen Sie? Die Kernfunktionalität wird durch die Kombination des Feldtyps <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text">semantic_text</a> und der Befehle <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/fork">FORK</a>/<a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/fuse">FUSE</a> erreicht (FUSE verwendet standardmäßig <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion">RRF</a> , um die Ergebnisse der einzelnen Forks zusammenzuführen). Hier ein einfaches Beispiel für eine fiktive Produktsuche:</p><pre><code>FROM products
| FORK
  (MATCH description "high performance gaming laptop" | EVAL search_type = "bm25"),
  (MATCH description_semantic "high performance gaming laptop" | EVAL search_type = "semantic")
| FUSE 
| LIMIT 20
| KEEP product_name, description, _score, search_type</code></pre><p>Die <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/eval">EVAL-</a> Klausel, die in jedem der FORK-Zweige im obigen Beispiel enthalten ist, ist nicht unbedingt notwendig; sie dient lediglich dazu, zu veranschaulichen, wie Sie nachverfolgen können, aus welcher Suchmodalität ein bestimmtes Ergebnis zurückgegeben wurde.</p><h3>Suchvorlagen</h3><p>Angenommen, Sie möchten Ihre eigenen externen Agenten-Tools auf Ihre Elastic-Bereitstellung verweisen. Anstelle von ES|QL möchten Sie mehrstufige Retriever verwenden oder eine bereits entwickelte DSL-Syntax wiederverwenden und außerdem die Eingaben, die die Abfrage akzeptiert, die Syntax, die zur Ausführung der Suche verwendet wird, und die in der Ausgabe zurückgegebenen Felder steuern können. <a href="https://www.elastic.co/docs/solutions/search/search-templates">Suchvorlagen</a> ermöglichen es Benutzern, vordefinierte Strukturen für häufige Suchmuster festzulegen, wodurch die Effizienz und Konsistenz beim Abrufen von Daten verbessert wird. Dies ist besonders vorteilhaft für agentenbasierte Tools, die mit Such-APIs interagieren, da sie dazu beitragen, Boilerplate-Code zu standardisieren und eine schnellere Iteration der Suchlogik zu ermöglichen. Und falls Sie jemals einen dieser Faktoren anpassen müssen, aktualisieren Sie einfach die Suchvorlage und voilà, die Änderungen werden übernommen. Wenn Sie ein Beispiel für die Verwendung von Suchvorlagen mit agentenbasierten Tools suchen, schauen Sie sich den Blog von Elasticsearch Labs an: „<a href="https://www.elastic.co/search-labs/blog/mcp-intelligent-search">MCP für intelligente Suche</a> “. Dort wird eine Suchvorlage hinter einem Tool-Aufruf von einem externen MCP-Server verwendet.</p><h3>Integrierte Arbeitsabläufe (FTW!)</h3><p>Eine der größten Herausforderungen in unserer neuen Welt der agentenbasierten KI ist die nicht-deterministische Natur von semi-autonomen, selbstgesteuerten „denkenden“ Agenten. Kontextgestaltung ist eine entscheidende Disziplin für agentenbasierte KI: Es handelt sich um Techniken, die dazu beitragen, die möglichen Schlussfolgerungen, die unser Agent generieren kann, auf das einzugrenzen, was wir als Wahrheit kennen. Selbst bei einem hochpräzisen und relevanten Kontextfenster (wenn wir den Bereich numerischer Fakten verlassen) fehlt uns immer noch die Gewissheit, dass die Reaktion des Agenten vollständig wiederholbar und verlässlich ist.</p><p>Wenn Sie dieselbe Anfrage mehrmals an einen Agenten senden, können die Antworten <em>im Wesentlichen</em> gleich sein, wobei <em>sich lediglich die Antwort geringfügig</em> unterscheidet. Das ist in der Regel für einfache Abfragen ausreichend, vielleicht kaum wahrnehmbar, und wir können versuchen, die Ausgabe mithilfe von Kontextmanipulationstechniken zu gestalten. Doch je komplexer die Aufgaben werden, die wir unseren Agenten stellen, desto größer ist die Wahrscheinlichkeit, dass eine oder mehrere Teilaufgaben eine Abweichung hervorrufen, die das Endergebnis geringfügig verändert. Es wird wahrscheinlich noch schlimmer werden, wenn wir uns stärker auf die Kommunikation zwischen den Agenten verlassen, und diese Abweichungen werden sich summieren. Dies unterstreicht erneut die Notwendigkeit, dass die Werkzeuge, mit denen unsere Agenten interagieren, sehr flexibel und genau auf die jeweiligen Kontextdaten abstimmbar sein müssen und dass sie in einem erwarteten Ausgabeformat reagieren sollten. Dies deutet auch darauf hin, dass wir für viele Anwendungsfälle die Interaktion zwischen Agenten und Werkzeugen steuern müssen – hier kommen Workflows ins Spiel!</p><p>Elastic wird schon bald vollständig anpassbare Workflows in den Kern der Plattform integrieren. Diese Workflows werden in der Lage sein, bidirektional mit Agenten und Tools zu interagieren, sodass Workflows Agenten und Tools aufrufen können und Agenten und Tools Workflows aufrufen können. Die vollständige Integration dieser Funktionen in dieselbe Such-KI-Plattform, auf der sich all Ihre Daten befinden, wird einen grundlegenden Wandel bewirken; das Potenzial der Arbeitsabläufe ist äußerst spannend! Bald, schon sehr bald!</p><h3>Elastisch wie die einheitliche Speicherbank</h3><p>Da Elastic eine verteilte Datenplattform ist, die für die Suche in nahezu Echtzeit konzipiert wurde, übernimmt sie auf natürliche Weise die Langzeitgedächtnisfunktionen für agentenbasierte KI-Systeme. Mit der integrierten Chat-Funktion des Agent Builders bieten wir auch die Möglichkeit, das Kurzzeitgedächtnis und den Chatverlauf zu verfolgen und zu verwalten. Und weil die gesamte Plattform API-first ist, ist es extrem einfach, Elastic als Plattform zu nutzen, um die Kontextausgabe eines Tools zu speichern (und später darauf zurückgreifen zu können), die das Kontextfenster des Agenten überfordern könnte; diese Technik wird in Kontext-Engineering-Kreisen manchmal als „ <a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents#:~:text=Agents%20can%20assemble%20understanding%20layer%20by%20layer%2C%20maintaining%20only%20what%27s%20necessary%20in%20working%20memory%20and%20leveraging%20note%2Dtaking%20strategies%20for%20additional%20persistence">Notizen-Making</a>“ bezeichnet.</p><p>Die Kombination von Kurzzeit- und Langzeitgedächtnis auf derselben Suchplattform bietet viele Vorteile: Stellen Sie sich vor, Sie könnten Chatverläufe und gespeicherte Kontextantworten als semantische Einflussfaktoren für zukünftige Chatinteraktionen nutzen, Bedrohungsanalysen durchführen oder persistente Datenprodukte erstellen, die automatisch aus häufig wiederholten Toolaufrufen generiert werden… Die Möglichkeiten sind endlos!</p><h2>Fazit</h2><p>Das Aufkommen großer Sprachmodelle hat die Art und Weise verändert, wie wir Inhalte abgleichen und wie wir unsere Daten analysieren. Wir bewegen uns rasant weg von unserer gegenwärtigen Welt, in der Menschen Recherche, Kontextbetrachtung und logisches Denken betreiben, um ihre eigenen Fragen zu beantworten, hin zu einer Welt, in der diese Schritte weitgehend durch agentenbasierte KI automatisiert werden. Damit wir den generierten Antworten vertrauen können, benötigen wir die Gewissheit, dass der Agent bei der Generierung seiner Antwort <em>alle</em> <em>relevanten</em> Informationen (einschließlich des Faktors der subjektiven Relevanz) berücksichtigt hat. Unsere primäre Methode, um agentenbasierte KI vertrauenswürdig zu machen, besteht darin, die Werkzeuge, die zusätzlichen Kontext abrufen, durch RAG- und Kontext-Engineering-Techniken zu verankern. Die Art und Weise, wie diese Werkzeuge den <em>anfänglichen Abruf</em> durchführen, kann jedoch entscheidend für die Genauigkeit der Antwort sein.</p><p>Die Elastic Search AI-Plattform bietet die Flexibilität und Vorteile der hybriden Suche sowie zahlreiche integrierte Funktionen, die agentenbasierte KI in Bezug auf Genauigkeit, Leistung und Skalierbarkeit unterstützen; mit anderen Worten: Elastic ist eine fantastische Plattform für verschiedene Aspekte des Kontext-Engineerings! Durch die Standardisierung des Kontextabrufs über eine Suchplattform vereinfachen wir die Funktionsweise von agentenbasierten Werkzeugen in vielerlei Hinsicht – und ähnlich dem Widerspruch „langsamer werden, um schneller zu werden“ bedeutet Einfachheit auf der Ebene der Kontextgenerierung eine schnellere und vertrauenswürdigere agentenbasierte KI.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Sie wissen schon, Kontext – Teil II: Agentische KI und die Notwendigkeit von Kontextgestaltung]]></title>
            <description><![CDATA[Erfahren Sie, wie die Weiterentwicklung von LLMs hin zu agentenbasierter KI den Bedarf an Kontextentwicklung erhöht, um die Grenzen des RAG-Kontexts und das Speichermanagement zu überwinden.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/context-engineering-llm-evolution-agentic-ai</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/context-engineering-llm-evolution-agentic-ai</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Woody Walton]]></dc:creator>
            <pubDate>Tue, 18 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6153ce399e5498841ef2ffa5363a3e2d2dea726d-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Vor diesem (ziemlich ausführlichen) <a href="https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-evolution-agentic-ai">Hintergrund</a> zu den Veränderungen, die LLMs den zugrundeliegenden Prozessen der Informationswiedergewinnung zugeschrieben haben, wollen wir uns nun ansehen, wie sie auch die Art und Weise verändert haben, wie wir nach Daten suchen.</p><h2>Eine neue Art der Interaktion mit Daten</h2><p>Generative KI (genAI) und agentenbasierte KI gehen anders vor als die traditionelle Suche. Während wir früher mit der Informationssuche durch eine Suche begannen („Lass mich das mal googeln…“), erfolgt die erste Aktion sowohl bei generischer KI als auch bei Agenten in der Regel durch die Eingabe von natürlicher Sprache in eine Chat-Oberfläche. Die Chat-Oberfläche ist eine Diskussion mit einem LLM, der sein semantisches Verständnis nutzt, um unsere Frage in eine destillierte Antwort umzuwandeln, eine zusammenfassende Antwort, die scheinbar von einem Orakel stammt, das über ein breites Wissen über alle Arten von Informationen verfügt. Was den LLM wirklich auszeichnet, ist seine Fähigkeit, kohärente, durchdachte Sätze zu formulieren, die die einzelnen Wissensfragmente miteinander verknüpfen – selbst wenn diese ungenau oder völlig realitätsfern sind, steckt doch ein <a href="https://en.wikipedia.org/wiki/Truthiness">Körnchen Wahrheit</a> darin.</p><p>Die alte Suchleiste, mit der wir so vertraut geworden sind, kann man sich als die Ampelmaschine vorstellen, die wir benutzt haben, als <strong><em>wir selbst</em></strong> der denkende Agent waren. Inzwischen wandeln sogar Internet-Suchmaschinen unsere altbekannte, mühsame Suche nach Wörtern in KI-gesteuerte Übersichten um, die die Anfrage mit einer Zusammenfassung der Ergebnisse beantworten und den Nutzern so die Notwendigkeit ersparen, sich durch die einzelnen Ergebnisse zu klicken und diese selbst zu bewerten.</p><h2>Generative KI &amp; RAG</h2><p>Generative KI versucht, mithilfe ihres semantischen Verständnisses der Welt die in einer Chatanfrage geäußerte subjektive Absicht zu analysieren und anschließend mithilfe ihrer Schlussfolgerungsfähigkeiten spontan eine Expertenantwort zu erstellen. Eine generative KI-Interaktion besteht aus mehreren Teilen: Sie beginnt mit der Eingabe/Anfrage des Benutzers, frühere Konversationen in der Chat-Sitzung können als zusätzlicher Kontext verwendet werden, und die Anweisung gibt dem LLM vor, wie er argumentieren und welche Verfahren er bei der Erstellung der Antwort befolgen soll. Die Hilfestellungen haben sich von einfachen Erklärungen im Stil von „Erkläre es mir so, als wäre ich fünf Jahre alt“ zu vollständigen Anleitungen für die Bearbeitung von Anfragen weiterentwickelt. Diese Aufschlüsselungen enthalten oft separate Abschnitte, die Details zur Persona/Rolle der KI, zum vor der Generierung stattfindenden Denkprozess/internen Denkprozess, zu objektiven Kriterien, Einschränkungen, zum Ausgabeformat, zur Zielgruppe sowie Beispiele zur Veranschaulichung der zu erwartenden Ergebnisse beschreiben.</p><p>Zusätzlich zur Benutzeranfrage und der Systemaufforderung liefert Retrieval Augmented Generation (RAG) weitere Kontextinformationen in einem sogenannten „Kontextfenster“. RAG war eine entscheidende Ergänzung der Architektur; wir nutzen es, um das LLM über die fehlenden Puzzleteile in seinem semantischen Verständnis der Welt zu informieren.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5b9671d5d07d4caefde372bb3188000754a91eed-1470x746.png" style="width: 100%;" /><p>Kontextfenster können ziemlich <a href="https://www.dbreunig.com/2025/06/22/how-contexts-fail-and-how-to-fix-them.html">pingelig</a> sein, wenn es darum geht, was, wo und wie viel man ihnen gibt. Welcher Kontext ausgewählt wird, ist natürlich sehr wichtig, aber auch das Signal-Rausch-Verhältnis des bereitgestellten Kontexts sowie die Länge des Fensters spielen eine Rolle.</p><h3>Zu wenige Informationen</h3><p>Werden in einer Abfrage, einer Eingabeaufforderung oder einem Kontextfenster zu wenige Informationen angegeben, kann dies zu Halluzinationen führen, da das LLM den korrekten semantischen Kontext für die Generierung einer Antwort nicht genau bestimmen kann. Es gibt auch Probleme mit der Vektorähnlichkeit der Dokumentabschnittsgrößen – eine kurze, einfache Frage passt möglicherweise nicht semantisch zu den umfangreichen, detaillierten Dokumenten in unseren vektorisierten Wissensdatenbanken. Es wurden Techniken zur Erweiterung von Anfragen entwickelt, wie zum Beispiel <a href="https://medium.com/data-science/how-to-use-hyde-for-better-llm-rag-retrieval-a0aa5d0e23e8">Hypothetical Document Embeddings (HyDE)</a> , die LLMs verwenden, um eine hypothetische Antwort zu generieren, die reichhaltiger und ausdrucksstärker ist als die kurze Anfrage. Die Gefahr hierbei ist natürlich, dass das hypothetische Dokument selbst eine Halluzination ist, die den LLM noch weiter vom richtigen Kontext entfernt.</p><h3>Zu viele Informationen</h3><p>Genau wie bei uns Menschen kann eine zu große Informationsmenge in einem Kontextfenster auch einen LLM überfordern und verwirren, sodass er nicht mehr weiß, was die wichtigen Teile sein sollen. Kontextüberlauf (oder „ <a href="https://research.trychroma.com/context-rot">Kontextverfall</a>“) beeinträchtigt die Qualität und Leistung generativer KI-Operationen; er wirkt sich stark auf das „Aufmerksamkeitsbudget“ (das Arbeitsgedächtnis) des LLM aus und verwässert die Relevanz über viele konkurrierende Token hinweg. Zum Konzept der „Kontextverrottung“ gehört auch die Beobachtung, dass LLMs tendenziell eine <a href="https://alexandrabarr.beehiiv.com/p/context-windows">Positionsverzerrung</a> aufweisen – sie bevorzugen den Inhalt am Anfang oder Ende eines Kontextfensters gegenüber dem Inhalt im mittleren Abschnitt.</p><h3>Ablenkende oder widersprüchliche Informationen</h3><p>Je größer das Kontextfenster wird, desto größer ist die Wahrscheinlichkeit, dass es überflüssige oder widersprüchliche Informationen enthält, die das LLM davon ablenken können, den richtigen Kontext auszuwählen und zu verarbeiten. In gewisser Hinsicht wird es zu einem Problem von Müll rein/Müll raus: Wenn man einfach eine Reihe von Dokumentenergebnissen in ein Kontextfenster einfügt, erhält das LLM eine Menge Informationen zum Verarbeiten (möglicherweise zu viele), aber je nachdem, wie der Kontext ausgewählt wurde, besteht eine größere Wahrscheinlichkeit, dass widersprüchliche oder irrelevante Informationen einfließen.</p><h2>Agentische KI</h2><p>Ich hatte es Ihnen ja gesagt, es gäbe noch viel zu besprechen, aber wir haben es geschafft – wir sprechen jetzt endlich über agentenbasierte KI-Themen! Agentic AI ist eine sehr spannende neue Anwendung von LLM-Chat-Schnittstellen, die die Fähigkeit generativer KI (können wir sie schon als „Legacy“ bezeichnen?) erweitert, Antworten auf der Grundlage ihres eigenen Wissens und der von Ihnen bereitgestellten Kontextinformationen zu synthetisieren. Mit zunehmender Reife der generativen KI wurde uns bewusst, dass es ein gewisses Maß an Aufgaben und Automatisierung gibt, die LLMs übernehmen können, zunächst beschränkt auf mühsame, risikoarme Tätigkeiten, die leicht von einem Menschen überprüft/validiert werden können. Innerhalb kurzer Zeit erweiterte sich dieser anfängliche Umfang: Ein LLM-Chatfenster kann nun der Funke sein, der einen KI-Agenten dazu veranlasst, autonom zu planen, auszuführen und iterativ zu evaluieren und anzupassen, um sein festgelegtes Ziel zu erreichen. Die Agenten haben Zugriff auf die Schlussfolgerungen ihrer LLMs, den Chatverlauf und das Denkvermögen (sofern vorhanden) und verfügen außerdem über spezielle Werkzeuge, die sie zu diesem Zweck einsetzen können. Wir sehen jetzt auch Architekturen, die es einem übergeordneten Agenten ermöglichen, als Orchestrator mehrerer <a href="https://www.philschmid.de/the-rise-of-subagents">Unteragenten</a> zu fungieren, von denen jeder über eigene Logikketten, Befehlssätze, Kontext und Werkzeuge verfügt.</p><p>Die Agenten sind der Einstiegspunkt in einen weitgehend automatisierten Arbeitsablauf: Sie arbeiten selbstständig, indem sie mit einem Benutzer chatten und dann mithilfe von „Logik“ ermitteln, welche Tools zur Beantwortung der Frage des Benutzers zur Verfügung stehen. Werkzeuge gelten im Vergleich zu Agenten üblicherweise als passiv und sind darauf ausgelegt, nur eine bestimmte Art von Aufgabe zu erfüllen. Die <em>Aufgaben</em> , die ein Tool ausführen kann, sind quasi unbegrenzt (was wirklich spannend ist!), aber eine Hauptaufgabe von Tools besteht darin, Kontextinformationen zu sammeln, die ein Agent bei der Ausführung seines Arbeitsablaufs berücksichtigen kann.</p><p>Als Technologie steckt agentenbasierte KI noch in den Kinderschuhen und ist anfällig für das LLM-Äquivalent einer Aufmerksamkeitsdefizitstörung – sie vergisst leicht, was sie tun sollte, und macht oft andere Dinge, die überhaupt nicht Teil der Aufgabenstellung waren. Hinter der scheinbaren Magie verbergen sich die „logischen“ Fähigkeiten von LLMs, die darauf beruhen, das nächste wahrscheinlichste Token in einer Sequenz vorherzusagen. Damit logisches Denken (oder eines Tages künstliche allgemeine Intelligenz (AGI)) zuverlässig und vertrauenswürdig wird, müssen wir überprüfen können, ob es bei der Bereitstellung korrekter und aktueller Informationen so argumentiert, wie wir es erwarten (und uns vielleicht noch das kleine Quäntchen mehr liefert, an das wir selbst nicht gedacht hätten). Damit dies gelingt, benötigen agentenbasierte Architekturen die Fähigkeit, klar zu kommunizieren (Protokolle), sich an die von uns vorgegebenen Arbeitsabläufe und Einschränkungen zu halten (Leitplanken), sich zu merken, wo sie sich in einer Aufgabe befinden (Zustand), ihren verfügbaren Speicherplatz zu verwalten und zu überprüfen, ob ihre Antworten korrekt sind und die Aufgabenkriterien erfüllen.</p><h2>Sprich mit mir in einer Sprache, die ich verstehen kann.</h2><p>Wie es in neuen Entwicklungsbereichen üblich ist (insbesondere in der Welt der LLMs), gab es anfänglich eine ganze Reihe von Ansätzen für die Kommunikation zwischen Agent und Werkzeug, aber sie einigten sich schnell auf das <a href="https://modelcontextprotocol.io/docs/getting-started/intro">Model Context Protocol (MCP)</a> als De-facto-Standard. Die Definition des Model Context Protocol steckt bereits im Namen – es ist das <strong>Protokoll,</strong> das ein <strong>Modell</strong> verwendet, um <strong>Kontextinformationen</strong> anzufordern und zu empfangen. MCP fungiert als universeller Adapter für LLM-Agenten, um Verbindungen zu externen Tools und Datenquellen herzustellen; es vereinfacht und standardisiert die APIs, sodass verschiedene LLM-Frameworks und -Tools problemlos interoperabel sind. Das macht MCP zu einer Art Dreh- und Angelpunkt zwischen der Orchestrierungslogik und den Systemaufforderungen, die einem Agenten zur autonomen Ausführung im Dienste seiner Ziele gegeben werden, und den Operationen, die an Werkzeuge gesendet werden, um sie isolierter auszuführen (zumindest isoliert in Bezug auf den initiierenden Agenten).</p><p>Dieses Ökosystem ist so neu, dass sich jede Expansionsrichtung wie ein neues Terrain anfühlt. Wir verfügen über ähnliche Protokolle für Agent-zu-Agent-Interaktionen (<a href="https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/">Agent2Agent (A2A)</a> natürlich!) sowie über andere Projekte zur Verbesserung des Agenten-Schlussfolgerungsgedächtnisses (<a href="https://venturebeat.com/ai/new-memory-framework-builds-ai-agents-that-can-handle-the-real-worlds">ReasoningBank</a>), zur Auswahl des besten MCP-Servers für die jeweilige Aufgabe (<a href="https://arxiv.org/abs/2505.03275">RAG-MCP</a>) und zur Verwendung semantischer Analysen wie Zero-Shot-Klassifizierung und Mustererkennung bei Ein- und Ausgaben als <a href="https://openai.github.io/openai-guardrails-python/">Leitplanken</a> , um zu steuern, worauf ein Agent zugreifen darf.</p><p>Möglicherweise ist Ihnen aufgefallen, dass das zugrundeliegende Ziel jedes dieser Projekte darin besteht, die Qualität und Kontrolle der Informationen zu verbessern, die an ein Agenten-/genAI-Kontextfenster zurückgegeben werden? Während das agentenbasierte KI-Ökosystem seine Fähigkeit zur besseren Verarbeitung dieser Kontextinformationen stetig weiterentwickelt (um sie zu kontrollieren, zu verwalten und darauf zu reagieren), wird es immer notwendig sein, die <em>relevantesten</em> Kontextinformationen als Grundlage für die Aktionen des Agenten abzurufen.</p><h2>Willkommen im Kontext-Engineering!</h2><p>Wer mit Begriffen aus dem Bereich der generativen KI vertraut ist, hat wahrscheinlich schon von „Prompt Engineering“ gehört – mittlerweile ist es fast schon eine eigene Pseudowissenschaft. Prompt Engineering wird eingesetzt, um die besten und effizientesten Wege zu finden, die Verhaltensweisen, die das LLM bei der Generierung seiner Antwort verwenden soll, proaktiv zu beschreiben. „<a href="https://www.elastic.co/search-labs/blog/context-engineering-overview">Context Engineering</a> “ erweitert die Techniken des „Prompt Engineering“ über die Agentenseite hinaus und umfasst auch verfügbare Kontextquellen und -systeme auf der Werkzeugseite des MCP-Protokolls sowie die umfassenden Themen Kontextmanagement, -verarbeitung und -generierung:</p><ul><li><strong>Kontextmanagement </strong>– Bezieht sich auf die Aufrechterhaltung der Zustands- und Kontexteffizienz in langlaufenden und/oder komplexeren agentenbasierten Arbeitsabläufen. Iterative Planung, Nachverfolgung und Orchestrierung von Aufgaben und Werkzeugaufrufen zur Erreichung der Ziele des Agenten. Da Agenten nur über ein begrenztes „Aufmerksamkeitsbudget“ verfügen, befasst sich das Kontextmanagement hauptsächlich mit Techniken, die dazu beitragen, das Kontextfenster so zu verfeinern, dass sowohl der größtmögliche Umfang als auch die wichtigsten Kontextinformationen erfasst werden (Präzision versus Trefferquote!). Zu den Techniken gehören Komprimierung, Zusammenfassung und Beibehaltung des Kontextes aus vorherigen Schritten oder Werkzeugaufrufen, um im Arbeitsspeicher Platz für zusätzlichen Kontext in nachfolgenden Schritten zu schaffen.</li><li><strong>Kontextverarbeitung </strong>– Die logischen und hoffentlich größtenteils programmatischen Schritte zur Integration, Normalisierung oder Verfeinerung des aus unterschiedlichen Quellen gewonnenen Kontextes, damit der Agent den gesamten Kontext auf eine einigermaßen einheitliche Weise verarbeiten kann. Die grundlegende Aufgabe besteht darin, Kontext aus allen Quellen (Eingabeaufforderungen, RAG-System, Speicher usw.) so aufzubereiten, dass er vom Agenten möglichst effizient genutzt werden kann. </li><li><strong>Kontextgenerierung </strong>– Wenn es bei der Kontextverarbeitung darum geht, den abgerufenen Kontext für den Agenten nutzbar zu machen, dann gibt die Kontextgenerierung dem Agenten die Möglichkeit, diese zusätzlichen Kontextinformationen nach Belieben, aber auch unter bestimmten Einschränkungen, anzufordern und zu empfangen.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4a8240e1eb078b2294b8d981b9caa8593589cac4-1600x900.png" style="width: 100%;" /><p>Die verschiedenen Elemente von LLM-Chatanwendungen lassen sich direkt (und manchmal auch überlappend) auf jene übergeordneten Funktionen des Kontextmanagements abbilden:</p><ul><li><strong>Anweisungen / Systemaufforderung</strong> - Die Aufforderungen dienen als Gerüst dafür, wie die generative (oder agentenbasierte) KI-Aktivität ihr Denken auf die Erreichung des Ziels des Benutzers ausrichtet. Eingabeaufforderungen stellen einen eigenen Kontext dar; sie sind nicht nur tonale Anweisungen – sie beinhalten häufig auch Logik zur Aufgabenausführung und Regeln für Dinge wie „Schritt für Schritt nachdenken“ oder „tief durchatmen“, bevor man antwortet, um sicherzustellen, dass die Antwort die Anfrage des Benutzers vollständig erfüllt. Jüngste Tests haben gezeigt, dass Auszeichnungssprachen sehr effektiv sind, um die verschiedenen Teile einer Aufgabenstellung zu strukturieren. Es sollte jedoch darauf geachtet werden, die Anweisungen so zu formulieren, dass sie weder zu vage noch zu spezifisch sind; wir wollen dem LLM genügend Anweisungen geben, um den richtigen Kontext zu finden, aber nicht so präskriptiv sein, dass es unerwartete Erkenntnisse verpasst.</li><li><strong>Kurzzeitgedächtnis</strong> (Zustand/Verlauf) - Das Kurzzeitgedächtnis umfasst im Wesentlichen die Interaktionen der Chat-Sitzung zwischen dem Benutzer und dem LLM. Diese sind nützlich, um den Kontext in Live-Sitzungen zu verfeinern, und können zur späteren Verwendung und Fortsetzung gespeichert werden. </li><li><strong>Langzeitgedächtnis</strong> – Das Langzeitgedächtnis sollte Informationen enthalten, die über mehrere Sitzungen hinweg nützlich sind. Und es geht nicht nur um domänenspezifische Wissensdatenbanken, auf die über RAG zugegriffen wird; neuere Forschungen nutzen die Ergebnisse vorheriger agentischer/generativer KI-Anfragen, um innerhalb aktueller agentischer Interaktionen zu lernen und darauf zu verweisen. Zu den interessantesten Innovationen im Bereich des Langzeitgedächtnisses gehören solche, die die Art und Weise der <a href="https://steve-yegge.medium.com/introducing-beads-a-coding-agent-memory-system-637d7d92514a">Speicherung und Verknüpfung von</a> Zuständen so anpassen, dass Agenten dort weitermachen können, wo sie aufgehört haben. </li><li><strong>Strukturierte Ausgabe</strong> - Kognition erfordert Anstrengung, daher ist es wohl keine Überraschung, dass LLMs (genau wie Menschen) selbst mit Denkfähigkeiten beim Denken weniger Anstrengung aufwenden wollen, und in Ermangelung einer definierten API oder eines Protokolls ist eine Karte (ein Schema) für das Lesen der von einem Toolaufruf zurückgegebenen Daten äußerst hilfreich. Die Einbeziehung <a href="https://platform.openai.com/docs/guides/structured-outputs?lang=javascript">strukturierter Ausgaben</a> in das agentenbasierte Framework trägt dazu bei, diese Interaktionen zwischen Maschinen schneller und zuverlässiger zu gestalten, wodurch weniger gedankengesteuertes Parsen erforderlich wird.</li><li><strong>Verfügbare Tools</strong> – Tools können die unterschiedlichsten Aufgaben übernehmen, von der Erfassung zusätzlicher Informationen (z. B. durch Abfragen von Unternehmensdatenbanken mittels RAG-Code oder über Online-APIs) bis hin zur Durchführung automatisierter Aktionen im Auftrag des Agenten (z. B. durch Buchung eines Hotelzimmers auf Basis der Kriterien der Anfrage des Agenten). Werkzeuge könnten auch Unteragenten mit eigenen agentenbasierten Verarbeitungsketten sein. </li><li><strong>Retrieval Augmented Generation (RAG)</strong> – Mir gefällt die Beschreibung von RAG als „dynamische Wissensintegration“ sehr gut. Wie bereits erwähnt, ist RAG die Technik, um die zusätzlichen Informationen bereitzustellen, auf die das LLM beim Training keinen Zugriff hatte, oder es ist eine Wiederholung der Ideen, die wir für am wichtigsten halten, um die richtige Antwort zu erhalten – diejenige, die am relevantesten für unsere subjektive Anfrage ist.</li></ul><h2>Phänomenale kosmische Energie, winziger Wohnraum!</h2><p>Agentic AI bietet so viele faszinierende und aufregende neue Welten zum Erkunden! Es gibt zwar noch viele der alten, traditionellen Probleme der Datenbeschaffung und -verarbeitung zu lösen, aber auch ganz neue Herausforderungen, die erst jetzt im neuen Zeitalter der LLMs ans Licht der Öffentlichkeit treten. Viele der aktuellen Probleme, mit denen wir uns heute auseinandersetzen, hängen mit Kontextgestaltung zusammen, also damit, wie man Lernlern die zusätzlichen Kontextinformationen bereitstellt, die sie benötigen, ohne ihren begrenzten Arbeitsgedächtnisraum zu überlasten.</p><p>Die Flexibilität von halbautonomen Agenten, die Zugriff auf eine Reihe von Werkzeugen (und andere Agenten) haben, führt zu so vielen neuen Ideen für die Implementierung von KI, dass es schwer ist, sich die verschiedenen Möglichkeiten vorzustellen, wie wir die einzelnen Teile zusammensetzen könnten. Der Großteil der aktuellen Forschung fällt in den Bereich des Kontext-Engineerings und konzentriert sich auf den Aufbau von Speichermanagementstrukturen, die größere Mengen an Kontext verarbeiten und verfolgen können – denn die tiefgründigen Denkprobleme, die wir mit LLMs lösen wollen, weisen eine erhöhte Komplexität und länger andauernde, mehrphasige Denkprozesse auf, bei denen das Erinnern von extrem großer Bedeutung ist.</p><p>Viele der laufenden Experimente auf diesem Gebiet versuchen, die optimale Aufgabenverwaltung und Werkzeugkonfigurationen zu finden, um den agentenbasierten Schlund zu füttern. Jeder Werkzeugaufruf in der Argumentationskette eines Agenten verursacht kumulative Kosten, sowohl hinsichtlich des Rechenaufwands zur Ausführung der Funktion dieses Werkzeugs als auch der Auswirkungen auf das begrenzte Kontextfenster. Einige der neuesten Techniken zur Kontextverwaltung für LLM-Agenten haben unbeabsichtigte Ketteneffekte wie den „ <a href="https://venturebeat.com/ai/ace-prevents-context-collapse-with-evolving-playbooks-for-self-improving-ai">Kontextkollaps</a>“ verursacht, bei dem die Komprimierung/Zusammenfassung des akkumulierten Kontexts für langlaufende Aufgaben zu <em>verlustbehaftet</em> wird. Das angestrebte Ergebnis sind Werkzeuge, die einen prägnanten und genauen Kontext liefern, ohne dass überflüssige Informationen in den wertvollen Speicherplatz des Kontextfensters eindringen.</p><h3>So viele/zu viele Möglichkeiten</h3><p>Wir wollen eine klare Aufgabentrennung mit der Flexibilität, Werkzeuge/Komponenten wiederzuverwenden. Daher ist es absolut sinnvoll, dedizierte Agentenwerkzeuge für die Verbindung mit bestimmten Datenquellen zu entwickeln – jedes Werkzeug kann sich auf die Abfrage eines bestimmten Repository-Typs, eines bestimmten Datenstroms oder sogar eines bestimmten Anwendungsfalls spezialisieren. Aber Vorsicht: Im Bestreben, Zeit/Geld zu sparen/zu beweisen, dass etwas möglich ist, wird die Versuchung groß sein, LLMs als Instrument der Föderation zu nutzen… Versuchen Sie, das zu vermeiden, wir haben <a href="https://www.elastic.co/pdf/elastic-distributed-not-federated-search.pdf">das schon einmal erlebt</a> ! Die föderierte Abfrage fungiert als „universeller Übersetzer“, der eine eingehende Abfrage in die Syntax umwandelt, die das entfernte Repository versteht, und anschließend die Ergebnisse aus mehreren Quellen zu einer kohärenten Antwort zusammenführen muss. Die Federation als Technik <em>funktioniert</em> im kleinen Maßstab <em>ganz gut</em> , aber im großen Maßstab und insbesondere bei multimodalen Daten versucht die Federation, Lücken zu schließen, die einfach zu groß sind.</p><p>In der agentenbasierten Welt wäre der Agent der Föderator und die Werkzeuge (über MCP) wären die manuell definierten Verbindungen zu unterschiedlichen Ressourcen. Der Einsatz spezieller Tools zur Erfassung unverbundener Datenquellen mag zwar ein vielversprechender neuer Ansatz sein, um verschiedene Datenströme dynamisch pro Abfrage zu vereinen, doch die Verwendung von Tools, um dieselbe Frage an mehrere Quellen zu stellen, wird wahrscheinlich mehr Probleme verursachen als lösen. Bei jeder dieser Datenquellen handelt es sich wahrscheinlich um einen anderen Typ von Datenspeicher, der jeweils über eigene Funktionen zum Abrufen, Sortieren und Sichern der darin enthaltenen Daten verfügt. Diese Abweichungen oder „Impedanzfehlanpassungen“ zwischen den Repositories erhöhen natürlich die Verarbeitungslast. Außerdem können sie widersprüchliche Informationen oder Signale einbringen, wobei etwas so scheinbar Harmloses wie eine Fehlausrichtung der Bewertung die Bedeutung, die einem Teil des zurückgegebenen Kontextes beigemessen wird, stark beeinträchtigen und letztendlich die Relevanz der generierten Antwort beeinflussen kann.</p><h3>Auch für Computer ist der Kontextwechsel schwierig.</h3><p>Wenn man einen Agenten auf eine Mission schickt, besteht seine erste Aufgabe oft darin, alle relevanten Daten zu finden, auf die er Zugriff hat. Genau wie bei Menschen entsteht auch bei uns eine kognitive Belastung, wenn jede Datenquelle, mit der der Agent eine Verbindung herstellt, unterschiedliche und disaggregierte Antworten liefert. Diese Belastung entsteht durch das Herausfiltern der relevanten Kontextinformationen aus den abgerufenen Inhalten. Das braucht Zeit/Rechenleistung, und jedes kleine bisschen summiert sich in der agentenbasierten Logikkette. Daraus lässt sich schließen, dass, ähnlich wie bei <a href="https://blog.cloudflare.com/code-mode/">MCP</a>, die meisten agentenbasierten Werkzeuge sich eher wie APIs verhalten sollten – isolierte Funktionen mit bekannten Ein- und Ausgaben, die auf die Bedürfnisse verschiedener Agententypen abgestimmt sind. Wir stellen sogar fest, dass <a href="https://arxiv.org/html/2501.12372v5">LLMs Kontext für Kontext benötigen</a> – sie sind viel besser darin, die semantischen Punkte zu verbinden, insbesondere wenn es um eine Aufgabe wie die Übersetzung von natürlicher Sprache in strukturierte Syntax geht, wenn sie ein Schema haben, auf das sie sich beziehen können (RTFM in der Tat!).</p><h2>Pause im 7. Inning!</h2><p>Wir haben nun die <a href="https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-evolution-agentic-ai">Auswirkungen von LLMs auf das Abrufen und Abfragen von Daten</a> sowie die Entwicklung des Chatfensters hin zu einer agentenbasierten KI-Erfahrung behandelt. Lasst uns die beiden Themen miteinander verknüpfen und sehen, wie wir unsere neuartigen Such- und Abruffunktionen nutzen können, um unsere Ergebnisse im Bereich Context Engineering zu verbessern. Weiter zu <a href="https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-agentic-ai-accuracy">Teil III: Die Leistungsfähigkeit der hybriden Suche im Kontext-Engineering</a>!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[So stellen Sie Elasticsearch auf Azure AKS Automated bereit]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie Elasticsearch mit Kibana auf Azure mithilfe von AKS Automatic und ECK für eine teilweise verwaltete Elasticsearch-Setup-Konfiguration bereitstellen.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/elasticsearch-azure-aks-automatic-deployment</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/elasticsearch-azure-aks-automatic-deployment</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Eduard Martin]]></dc:creator>
            <pubDate>Fri, 14 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4d821659a606e04390b09215e9a0d32eb01f0d1b-854x489.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Dieser Artikel ist Teil einer Reihe, in der wir lernen werden, wie man Elasticsearch mithilfe verschiedener Infrastrukturen installiert.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0a65ca8b62fd8a42d7751b8f4bf057e33d877304-940x458.png" style="width: 100%;" /><p>ECK erfordert deutlich mehr Aufwand als die Marketplace-basierten Elastic Cloud-Lösungen, ist aber stärker automatisiert als die manuelle Bereitstellung von VMs, da der Kubernetes-Operator die Systemorchestrierung und die Skalierung der Knoten übernimmt.</p><p>Dieses Mal werden wir mit dem Azure Kubernetes Service (AKS) arbeiten und dabei die automatische Methode verwenden. In den anderen Artikeln erfahren Sie, wie Sie <a href="https://www.elastic.co/search-labs/blog/azure-elasticsearch-vm-deployment">Azure VM</a> und <a href="https://www.elastic.co/search-labs/blog/deploy-elasticsearch-azure-marketplace">Azure Marketplace</a> nutzen.</p><h2>Was ist AKS Automatic?</h2><p><a href="https://learn.microsoft.com/en-us/azure/aks/intro-aks-automatic">Der Azure Kubernetes Service (AKS) verwaltet automatisch </a>die Clusterkonfiguration, weist Ressourcen dynamisch zu und integriert bewährte Sicherheitspraktiken bei gleichzeitiger Beibehaltung der Flexibilität von Kubernetes. Dadurch können Entwickler innerhalb von Minuten vom Container-Image zur bereitgestellten Anwendung gelangen.</p><p>AKS Automatic beseitigt den Großteil des Aufwands für die Clusterverwaltung und schafft ein gutes Gleichgewicht zwischen Einfachheit und Flexibilität. Die richtige Wahl hängt von Ihrem Anwendungsfall ab, aber die Entscheidung wird einfacher, wenn Sie Folgendes planen:</p><ul><li><strong>Bereitstellung einer Testumgebung: </strong>Die Bereitstellung ist schnell und unkompliziert und eignet sich daher ideal für schnelle Experimente oder kurzlebige Cluster.</li><li><strong>Arbeiten Sie ohne strenge VM-, Speicher- oder Netzwerkanforderungen: </strong>AKS Automatic bietet vordefinierte Standardeinstellungen. Wenn diese Ihren Bedürfnissen entsprechen, erspart Ihnen das zusätzliche Konfigurationsaufwand.</li><li><strong>So starten Sie zum ersten Mal mit Kubernetes: </strong>AKS Automatic übernimmt einen Großteil der Cluster-Einrichtung, senkt die Lernkurve und ermöglicht es Teams, sich auf ihre Anwendungen zu konzentrieren.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2e4c09b8c5e0ce5e8ea9c369626a373b7030a5ba-854x489.png" style="width: 100%;" /><p>Für Elasticsearch werden wir <a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-on-k8s">Elastic Cloud on Kubernetes </a>(ECK) verwenden, den offiziellen Elastic Kubernetes Operator, der die Orchestrierung von Kubernetes-Bereitstellungen des Elastic Stack vereinfacht.</p><h2>So richten Sie AKS Automatic ein</h2><p>1. Melden Sie sich beim <a href="https://azure.microsoft.com/">Microsoft Azure Portal</a> an.</p><p>2. Klicken Sie <strong>oben rechts.  </strong>Klicken Sie auf die Schaltfläche<strong> „Cloud Shell“</strong> , um auf die Konsole zuzugreifen und den AKS-Cluster von dort aus bereitzustellen. Alternativ können Sie die <a href="https://learn.microsoft.com/en-us/azure/cloud-shell/overview">Azure Cloud Shell</a> verwenden.</p><p><strong><em>Denken Sie daran, die Projekt-ID während des Tutorials durch Ihre eigene zu ersetzen.</em></strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0aa60605777c0a6e3aef8faa4e54388c2cb582c8-624x495.png" style="width: 100%;" /><p><em>Das Öffnen des AKS sollte wie im obigen Screenshot aussehen.</em></p><p>3. Installieren Sie die Azure CLI-Erweiterung aks-preview. Diese Vorabversion ermöglicht es uns, bei der Clustererstellung <code>--sku automatic</code> auszuwählen, wodurch die AKS-Automatikfunktion aktiviert wird.</p><pre><code>az extension add --name aks-preview</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2e05ab67fc554c5fb5208683c179fdeaeadd95db-624x56.png" style="width: 100%;" /><p><em>Wenn Sie diese Meldung sehen, bedeutet dies, dass die AKS-Erweiterung ordnungsgemäß installiert wurde.</em></p><p>4. Registrieren Sie<a href="https://learn.microsoft.com/en-us/azure/azure-app-configuration/concept-feature-management"> Feature-Flags</a> mit dem Befehl <code>az feature register</code></p><pre><code>az feature register --namespace Microsoft.ContainerService --name AutomaticSKUPreview</code></pre><p><em>Sie sehen nun die Details zu dem soeben erstellten Funktionsabonnement:</em></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3aca710c1f312ba91de461638e518386919ec722-801x138.png" style="width: 100%;" /><p>Überprüfen Sie den Registrierungsstatus, bis er sich von „ <strong><em>Registrierung läuft</em></strong>“ in „ <strong><em>Registriert</em></strong>“ ändert. Die Registrierung kann einige Minuten dauern.</p><pre><code>az feature show --namespace Microsoft.ContainerService --name AutomaticSKUPreview</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/186486b08ab8e1c372efaff50f10cbddeaf4e0cd-844x177.png" style="width: 100%;" /><p>Führen Sie <code>az provider register</code> aus, um die Änderungen zu verbreiten.</p><pre><code>az provider register --namespace Microsoft.ContainerService</code></pre><p>5. Erstellen Sie eine Ressourcengruppe</p><p>Eine Ressourcengruppe ist eine logische Gruppe von Azure-Ressourcen, die verwaltet und bereitgestellt werden sollen.</p><pre><code>az group create --name elastic-resource --location eastus</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d1952269e97d94f914020754bd02702f9eafd037-770x212.png" style="width: 100%;" /><p>6. Erstellen Sie einen Autopilot-Cluster. Wir werden es <strong><em>myAKSAutomaticCluster </em></strong>nennen und die soeben erstellte Ressourcengruppe verwenden. Stellen Sie sicher, dass auf einer der folgenden VM-Größen <strong><em>16 vCPUs</em></strong> verfügbar sind: <a href="https://learn.microsoft.com/en-us/azure/virtual-machines/sizes/general-purpose/dpsv5-series">Standard_D4pds_v5</a>, <a href="https://learn.microsoft.com/en-us/azure/virtual-machines/sizes/general-purpose/dldsv5-series">Standard_D4lds_v5</a>, <a href="https://learn.microsoft.com/en-us/azure/virtual-machines/sizes/general-purpose/dadsv5-series">Standard_D4ads_v5</a>, <a href="https://learn.microsoft.com/en-us/azure/virtual-machines/sizes/general-purpose/ddsv5-series">Standard_D4ds_v5</a>, <a href="https://learn.microsoft.com/en-us/azure/virtual-machines/sizes/general-purpose/ddv5-series">Standard_D4d_v5</a>, <a href="https://learn.microsoft.com/en-us/azure/virtual-machines/sizes/general-purpose/ddv4-series">Standard_D4d_v4</a>, <a href="https://learn.microsoft.com/en-us/azure/virtual-machines/sizes/general-purpose/dsv3-series">Standard_DS3_v2</a>, <a href="https://learn.microsoft.com/en-us/azure/virtual-machines/sizes/memory-optimized/dv2-dsv2-series-memory">Standard_DS12_v2,</a> damit AKS Ressourcen zuweisen kann.</p><pre><code>az aks create \
    --resource-group elastic-resource \
    --name myAKSAutomaticCluster \
    --sku automatic \
    --generate-ssh-keys</code></pre><p>* Falls<em><code>MissingSubscriptionRegistration</code></em>-Fehler <em>auftreten , gehen Sie mit den fehlenden Abonnements zurück zu Schritt 4. Beispielsweise erfordert <code>The subscription is not registered to use namespace &#x27;<strong>microsoft.insights</strong>&#x27;</code></em>die Ausführung von<em><code>az provider register --namespace Microsoft.Insights.</code></em></p><p>Folgen Sie dem interaktiven Login:</p><p><em>Es erscheint eine Meldung mit der Aufforderung, „az login“ auszuführen. Sie müssen diesen Befehl ausführen und dann warten.</em></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/14c0238f755fe6347519e69d3cb28c0fa52ec044-775x203.png" style="width: 100%;" /><p>7. Warten Sie, bis es fertig ist. Die Erstellung dauert etwa 10 Minuten.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/eb00c3ad18f884f47db6645b196808ebec07c1fc-797x177.png" style="width: 100%;" /><p>8. Konfigurieren Sie den Zugriff auf die kubectl-Befehlszeile.</p><pre><code>az aks get-credentials --resource-group elastic-resource --name myAKSAutomaticCluster</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5136d72a5d455345b0b6205bb232c4bdf7762998-793x52.png" style="width: 100%;" /><p><em>Beachten Sie, dass die von uns installierte Erweiterung AKS Automatic aktiviert.</em></p><p>9. Bestätigen Sie, dass die Knoten bereitgestellt wurden.</p><pre><code>kubectl get nodes</code></pre><p>Es wird eine Fehlermeldung angezeigt, die den Zugriff verweigert. Kopieren Sie die Benutzer-ID aus der Fehlermeldung.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d6c021fa54f4db00d2d795f5ba9b5a93376d03cd-818x47.png" style="width: 100%;" /><p>10. Fügen Sie Ihren Benutzer zur AKS-Zugriffskontrolle hinzu.</p><p>AKS-ID abrufen. Kopiere die Ausgabe des Befehls.</p><pre><code>az aks show --resource-group elastic-resource  --name myAKSAutomaticCluster --query id --output tsv</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/22a1cdc538bd379812a752c6a368a0651000abb8-810x36.png" style="width: 100%;" /><p>Erstellen Sie eine Rollenzuweisung mithilfe der AKS-ID und der Principal-ID Ihres Benutzers.</p><pre><code>az role assignment create --role "Azure Kubernetes Service RBAC Cluster Admin" --assignee <PRINCIPAL_ID> --scope <AKS_ID></code></pre><p>11. Versuchen Sie erneut zu überprüfen, ob die Knoten bereitgestellt wurden.</p><pre><code>kubectl get nodes</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c9b3a5c1cc540ef732c3e7f60b0a973bdbd0b6fd-617x99.png" style="width: 100%;" /><p>12. Installieren Sie den Elastic Cloud on the Kubernetes (ECK)-Operator.</p><pre><code># Install ECK Custom Resource Definitions
kubectl create -f https://download.elastic.co/downloads/eck/2.16.1/crds.yaml

# Install the ECK operator
kubectl apply -f https://download.elastic.co/downloads/eck/2.16.1/operator.yaml</code></pre><p>13. Wir erstellen eine Elasticsearch-Instanz mit einem einzelnen Knoten und den Standardwerten.</p><pre><code>cat <<EOF | kubectl apply -f -
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
spec:
  version: 9.0.0
  nodeSets:
  - name: default
    count: 1
    config:
      node.store.allow_mmap: false
EOF</code></pre><p>Wir haben <code>nmap</code> deaktiviert, da die Standard-AKS-Maschine einen zu niedrigen Wert für <code>vm.max_map_count</code> hat. Für den Produktivbetrieb wird von einer Deaktivierung abgeraten, stattdessen sollte der Wert von <code>vm.max_map_count</code> erhöht werden. Mehr dazu können Sie <a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-on-k8s/virtual-memory">hier</a> nachlesen.</p><p>14. Wir werden auch einen Kibana-Einzelknotencluster bereitstellen. Für Kibana werden wir einen Load Balancer hinzufügen, der uns eine externe IP-Adresse bereitstellt, über die wir Kibana von unserem Gerät aus erreichen können.</p><pre><code>cat <<EOF | kubectl apply -f -
apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
  name: quickstart
spec:
  version: 9.0.0
  http:
    service:
      spec:
        type: LoadBalancer
  count: 1
  elasticsearchRef:
    name: quickstart
EOF</code></pre><p>Standardmäßig konfiguriert AKS Automatic den Load Balancer als öffentlich; Sie können dieses Verhalten ändern, indem Sie die Metadaten-Annotation festlegen:</p><p><code>service.beta.kubernetes.io/azure-load-balancer-internal: &quot;true&quot;</code></p><p>15. Überprüfen Sie, ob Ihre Pods ausgeführt werden.</p><pre><code>kubectl get pods</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/213a897176c0af6cea19c7c777cfaf8734e3ee6e-616x84.png" style="width: 100%;" /><p>16. Sie können auch <code>kubectl get elasticsearch</code> und <code>kubectl get kibana</code> ausführen, um spezifischere Statistiken wie Elasticsearch-Version, Knoten und Zustand zu erhalten.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c84988e725ef892eddd8fb7e5a03d58c35a8f9d6-470x62.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/452dd03d314cd00c8a3c19e19862b968592a0435-415x62.png" style="width: 100%;" /><p>17. Greifen Sie auf Ihre Dienste zu.</p><pre><code>kubectl get svc</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b2d3e8f368be22b89aa2ed4d4d514f97dd6cbabd-624x115.png" style="width: 100%;" /><p>Hier wird Ihnen die <a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-on-k8s/accessing-services">externe URL</a> für Kibana unter EXTERNAL-IP angezeigt. Es kann einige Minuten dauern, bis der Load Balancer bereitgestellt ist. <strong><em>Kopieren Sie den Wert von EXTERNAL-IP.</em></strong></p><p>18. Ermitteln Sie das Elasticsearch-Passwort für den Benutzer „elastic“:</p><pre><code>kubectl get secret quickstart-es-elastic-user -o=jsonpath='{.data.elastic}' | base64 --decode</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/bd74537f8c35c4e027c518913fdb0a0524621d56-624x31.png" style="width: 100%;" /><p>19. <strong>Greifen Sie über Ihren Browser auf Kibana zu</strong> :</p><p>a. URL: https://:5601&lt;EXTERNAL_IP&gt;</p><p>b. Benutzername:elastic</p><p>c. Passwort:c44A295CaEt44D6xIzN6Zs5m (aus dem vorherigen Schritt)</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a601112527d80721b292328ed8da58386d2837eb-463x503.png" style="width: 100%;" /><p>20. Wenn Sie Elastic Cloud über Ihren Browser aufrufen, wird Ihnen der Begrüßungsbildschirm angezeigt.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f91e71fa961d215a8d886601d1a9fc5c452ce329-1999x1256.png" style="width: 100%;" /><p>Wenn Sie die Spezifikationen des Elasticsearch-Clusters ändern möchten, z. B. die Anzahl oder Größe der Knoten anpassen, können Sie das YML-Manifest mit den neuen Einstellungen erneut anwenden:</p><pre><code>cat <<EOF | kubectl apply -f -
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
spec:
  version: 9.0.0
  nodeSets:
    - name: default
      count: 2
      config:
        node.store.allow_mmap: false
      podTemplate:
        spec:
          containers:
            - name: elasticsearch
              resources:
                requests:
                  memory: 1.5Gi
                  cpu: 2
                limits:
                  memory: 1.5Gi
                  cpu: 2
EOF</code></pre><p>In diesem Beispiel fügen wir einen weiteren Knoten hinzu und modifizieren RAM und CPU. Wie Sie sehen können, zeigt <code>kubectl get elasticsearch</code> jetzt 2 Knoten an:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4b139a476b50933d45d99e09479112817964f76a-624x60.png" style="width: 100%;" /><p>Das Gleiche gilt für Kibana:</p><pre><code>cat <<EOF | kubectl apply -f -
apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
  name: quickstart
spec:
  version: 9.0.0
  http:
    service:
      spec:
        type: LoadBalancer
  count: 1
  elasticsearchRef:
    name: quickstart
  podTemplate:
    spec:
      containers:
        - name: kibana
          env:
            - name: NODE_OPTIONS
              value: "--max-old-space-size=1024"
          resources:
            requests:
              memory: 0.5Gi
              cpu: 0.5
            limits:
              memory: 1Gi
              cpu: 1
EOF</code></pre><p>Wir können die CPU/RAM-Auslastung des Containers sowie die Speichernutzung von <a href="https://nodejs.org/">Node.js </a>(<a href="https://nodejs.org/api/cli.html#--max-old-space-sizesize-in-mib">max-old-space-size</a>) anpassen.</p><p>Beachten Sie, dass <a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-on-k8s/volume-claim-templates">bestehende Volumenansprüche nicht reduziert werden können</a>. Nach der Installation des Updates kann der Betreiber die Änderungen mit minimalen Unterbrechungszeiten vornehmen.</p><p>Denken Sie daran, den Cluster nach Abschluss der Tests zu löschen, um unnötige Kosten zu vermeiden.</p><pre><code>az aks delete --name myAKSAutomaticCluster --resource-group elastic-resource</code></pre><h2>Fazit</h2><p>Die Verwendung von Azure AKS Automatic mit ECK bietet eine ausgewogene Lösung für die Bereitstellung von Elasticsearch und Kibana: Sie reduziert die operative Komplexität, gewährleistet automatisierte Skalierung und Aktualisierungen und nutzt die Flexibilität von Kubernetes. Dieser Ansatz ist ideal für Teams, die einen zuverlässigen, wiederholbaren und wartungsfreundlichen Bereitstellungsprozess wünschen, ohne jedes Infrastrukturdetail manuell verwalten zu müssen. Daher ist er eine praktische Wahl sowohl für Test- als auch für Produktionsumgebungen.</p><h2>Wie geht es weiter?</h2><p>Wenn Sie mehr über Kubernetes erfahren möchten, können Sie die offizielle Dokumentation hier einsehen:</p><ul><li><a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-on-k8s">Elastic Cloud auf Kubernetes | Elastic Docs</a></li><li><a href="https://learn.microsoft.com/en-us/azure/aks/intro-aks-automatic">Einführung in Azure Kubernetes Service (AKS) Automatisch (Vorschau)</a></li><li><a href="https://azure.github.io/AKS/2024/05/22/aks-automatic">AKS Automatic – AKS Engineering Blog</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Erstellung eines LLM-Agent-Newsrooms mit A2A-Protokoll und MCP in Elasticsearch: Teil 1]]></title>
            <description><![CDATA[Erkunden Sie die Konzepte des A2A-Protokolls und des MCP anhand eines praktischen Beispiels aus einer Nachrichtenredaktion, in der spezialisierte LLM-Agenten zusammenarbeiten, um Nachrichtenartikel zu recherchieren, zu schreiben, zu bearbeiten und zu veröffentlichen.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/a2a-protocol-mcp-llm-agent-newsroom-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/a2a-protocol-mcp-llm-agent-newsroom-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Justin Castilla]]></dc:creator>
            <pubDate>Thu, 13 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9f938d8e2f0fdf7509edf028816c48bdbc8b3fc7-1600x900.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<h2>Einleitung</h2><p>Die derzeitigen LLM-gestützten Systeme entwickeln sich rasant über Einzelmodellanwendungen hinaus zu komplexen Netzwerken, in denen spezialisierte Agenten zusammenarbeiten, um Aufgaben zu bewältigen, die mit modernen Computern bisher für unmöglich gehalten wurden. Mit zunehmender Komplexität dieser Systeme rückt die Infrastruktur, die die Kommunikation der Agenten und den Zugriff auf Werkzeuge ermöglicht, in den Mittelpunkt der Entwicklung. Zur Erfüllung dieser Anforderungen haben sich zwei komplementäre Ansätze herausgebildet: <strong>Agent2Agent (A2A)</strong> -Protokolle für die Koordination mehrerer Agenten und das <strong>Model Context Protocol (MCP)</strong> für den standardisierten Zugriff auf Werkzeuge und Ressourcen.</p><p>Das Verständnis dafür, wann man die einzelnen Elemente harmonisch miteinander und wann man sie ohne die anderen einsetzt, kann die Skalierbarkeit, Wartbarkeit und Effektivität Ihrer Anwendungen erheblich beeinflussen. Dieser Artikel untersucht die Konzepte und Implementierungen von <strong>A2A</strong> am praktischen Beispiel einer digitalen Nachrichtenredaktion, in der spezialisierte LLM-Agenten zusammenarbeiten, um Nachrichtenartikel zu recherchieren, zu schreiben, zu bearbeiten und zu veröffentlichen.</p><p>Ein zugehöriges Repository finden Sie <a href="https://github.com/justincastilla/elastic-newsroom/tree/main">hier</a>, und konkrete Beispiele für A2A in der Praxis werden wir gegen Ende des Artikels in Abschnitt 5 untersuchen.</p><h3>Voraussetzungen</h3><p>Das <a href="https://github.com/justincastilla/elastic-newsroom/tree/main">Repository</a> besteht aus Python-basierten Implementierungen der A2A-Agenten. Flask stellt einen API-Server sowie einen benutzerdefinierten Python-Messaging-Dienst namens Event Hub bereit, der Nachrichten für die Protokollierung und UI-Aktualisierungen weiterleitet. Schließlich wird eine React-Benutzeroberfläche für die eigenständige Nutzung der Newsroom-Funktionen bereitgestellt. Alles ist zur einfacheren Implementierung in einem Docker-Image enthalten. Wenn Sie die Dienste direkt auf Ihrem Rechner ausführen möchten, sollten Sie sicherstellen, dass die folgenden Technologien installiert sind:</p><p>Sprachen und Laufzeiten</p><ul><li>Python 13.12 – Kern-Backend-Sprache</li><li>Node.js 18+ - Optionale React-Benutzeroberfläche</li></ul><p>Kern-Frameworks und SDKs:</p><ul><li>A2A SDK 0.3.8 – Agentenkoordination und -kommunikation</li><li>Anthropic SDK - Claude-Integration für die KI-Generierung</li><li>Uvicorn – ASGI-Server zum Ausführen von Agenten</li><li>FastMCP 2.12.5+ - MCP-Server-Implementierung</li><li>React 18.2 – Frontend-UI-Framework</li></ul><p>Daten &amp; Suche</p><ul><li>Elasticsearch 9.1.1+ - Artikelindexierung und -suche</li></ul><p>Docker-Bereitstellung (optional, aber empfohlen)</p><ul><li>Docker 28.5.1+</li></ul><h2>Abschnitt 1: Was ist Agent2Agent (A2A)?</h2><h3>Definition und Kernkonzepte</h3><div style="display:none">Unknown block type "block", specify a component for it in the `components.types` option</div><p><strong>Offizielle Spezifikation</strong>: <a href="https://a2a-protocol.org/latest/specification/">https://a2a-protocol.org/latest/specification/</a></p><h3>Ursprung und Evolution</h3><p>Das Konzept der Agent2Agent-Kommunikation bzw. von Multiagentensystemen hat seine Wurzeln in der Forschung zu verteilten Systemen, Microservices und Multiagentensystemen, die <a href="https://en.wikipedia.org/wiki/Multi-agent_system">Jahrzehnte</a> zurückreicht. Frühe Arbeiten im Bereich der verteilten künstlichen Intelligenz legten den Grundstein für Agenten, die verhandeln, koordinieren und zusammenarbeiten können. Diese frühen Systeme dienten der Durchführung groß angelegter <a href="https://www.jasss.org/5/1/7.html">sozialer Simulationen</a>, <a href="https://arxiv.org/html/2410.09403v1">der akademischen Forschung</a> und <a href="https://www.researchgate.net/publication/334765661_Generation_Expansion_Planning_Considering_Investment_Dynamic_of_Market_Participants_Using_Multi-agent_System">dem Management von Stromnetzen</a>.</p><p>Mit der Verfügbarkeit von LLM und den gesunkenen Betriebskosten wurden Multiagentensysteme auch für den „Prosumer“-Markt zugänglich, unterstützt von Google und der breiteren KI-Forschungsgemeinschaft. Die Einführung des A2A-Protokolls, die heute als Agent2Agent-Systeme bekannt ist, hat sich zu einem modernen Standard entwickelt, der speziell für das Zeitalter mehrerer großer Sprachmodelle konzipiert wurde, die ihre Bemühungen und Aufgaben koordinieren.</p><p>Das A2A-Protokoll gewährleistet eine reibungslose Kommunikation und Koordination zwischen den Agenten, indem es einheitliche Standards und Prinzipien an den Interaktionspunkten anwendet, an denen LLMs sich verbinden und kommunizieren. Diese Standardisierung ermöglicht es Agenten verschiedener Entwickler – die unterschiedliche zugrunde liegende Modelle verwenden – effektiv zusammenzuarbeiten.</p><p>Kommunikationsprotokolle sind nicht neu und haben sich in nahezu jeder digitalen Transaktion im Internet fest etabliert. Wenn Sie <a href="https://www.elastic.co/search-labs">https://www.elastic.co/search-labs</a> eingegeben haben Wenn Sie einen Browser verwenden, um diesen Artikel zu erreichen, ist die Wahrscheinlichkeit hoch, dass alle TCP/IP-, HTTP-Transport- und DNS-Lookup-Protokolle ausgeführt wurden, um ein konsistentes Browsererlebnis für uns zu gewährleisten.</p><h3>Hauptmerkmale</h3><p>A2A-Systeme basieren auf mehreren grundlegenden Prinzipien, um eine reibungslose Kommunikation zu gewährleisten. Aufbauend auf diesen Prinzipien wird sichergestellt, dass verschiedene Agenten, die auf unterschiedlichen LLMs, Frameworks und Programmiersprachen basieren, nahtlos miteinander interagieren.</p><p>Hier sind die vier Hauptprinzipien:</p><ul><li><strong>Nachrichtenübermittlung</strong>: Agenten kommunizieren über strukturierte Nachrichten mit klar definierten Eigenschaften und Formaten.</li><li><strong>Koordination</strong>: Agenten orchestrieren komplexe Arbeitsabläufe, indem sie Aufgaben untereinander delegieren und Abhängigkeiten verwalten, ohne andere Agenten zu blockieren.</li><li><strong>Spezialisierung</strong>: Jeder Agent konzentriert sich auf einen bestimmten Bereich oder eine bestimmte Fähigkeit, wird Experte auf seinem Gebiet und bietet Aufgabenerledigung basierend auf diesen Fähigkeiten an.</li><li><strong>Verteilter Zustand</strong>: Zustand und Wissen sind auf mehrere Agenten verteilt und nicht zentralisiert. Die Agenten können sich gegenseitig über den Fortschritt des Aufgabenstatus und Teilergebnisse (Artefakte) informieren.</li></ul><h3>Die Redaktion: Ein laufendes Beispiel</h3><p>Stellen Sie sich eine digitale Nachrichtenredaktion vor, die von KI-Agenten gesteuert wird, von denen jeder auf einen anderen Aspekt des Journalismus spezialisiert ist:</p><ul><li><strong>Nachrichtenchef</strong> (Koordinator/Kunde): Vergibt Artikel und überwacht den Arbeitsablauf.</li><li><strong>Reporteragent</strong>: Verfasst Artikel auf Grundlage von Recherchen und Interviews.</li><li><strong>Rechercheagent</strong>: Sammelt Fakten, Statistiken und Hintergrundinformationen.</li><li><strong>Archivagent</strong>: Durchsucht historische Artikel und identifiziert Trends mithilfe von Elasticsearch.</li><li><strong>Redaktionsassistent</strong>: Überprüft Artikel auf Qualität, Stil und Suchmaschinenoptimierung.</li><li><strong>Verlagsagent</strong>: Veröffentlicht freigegebene Artikel über CI/CD auf der Blog-Plattform.</li></ul><p>Diese Akteure arbeiten nicht isoliert; wenn der Nachrichtenchef einen Artikel über <em>die Einführung erneuerbarer Energien</em> in Auftrag gibt, benötigt der Reporter den Rechercheur, um Statistiken zu sammeln, den Redakteur, um den Entwurf zu überprüfen, und den Verleger, um den endgültigen Artikel zu veröffentlichen. Diese Koordination erfolgt über A2A-Protokolle.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/cc0760dbd74c49b92fa00dafbb8c2e8740eb70b6-963x693.png" style="width: 100%;" /><h2>Abschnitt 2: A2A-Architektur verstehen</h2><h3>Kundenbetreuer- und Fernbetreuerrollen</h3><p>In der A2A-Architektur übernehmen Agenten zwei Hauptrollen. Der <strong>Kundenbetreuer</strong> ist dafür verantwortlich, Aufgaben zu formulieren und an andere Betreuer im System zu kommunizieren. Es identifiziert Remote-Agenten und deren Fähigkeiten und nutzt diese Informationen, um fundierte Entscheidungen über die Aufgabenverteilung zu treffen. Der Kundenbetreuer koordiniert den gesamten Arbeitsablauf und sorgt dafür, dass die Aufgaben ordnungsgemäß verteilt werden und das System seine Ziele erreicht.</p><p>Der <strong>Remote Agent</strong> hingegen erledigt Aufgaben, die ihm von Clients übertragen werden. Es stellt Informationen bereit oder ergreift konkrete Maßnahmen als Reaktion auf Anfragen, initiiert aber keine Aktionen selbstständig. Remote-Agenten können bei Bedarf auch mit anderen Remote-Agenten kommunizieren, um ihre zugewiesenen Aufgaben zu erfüllen, wodurch ein kollaboratives Netzwerk spezialisierter Kompetenzen entsteht.</p><p>In unserer Redaktion fungiert der Nachrichtenchef als Ansprechpartner für den Kunden, während Reporter, Rechercheur, Redakteur und Verleger als externe Mitarbeiter Anfragen bearbeiten und sich untereinander abstimmen.</p><h3>Kernfunktionen für A2A</h3><p>A2A-Protokolle definieren verschiedene Funktionen, die die Zusammenarbeit mehrerer Agenten ermöglichen:</p><h4>1. Entdeckung</h4><p>A2A-Server müssen ihre Fähigkeiten bekanntgeben, damit Clients wissen, wann und wie sie diese für bestimmte Aufgaben nutzen können. Dies geschieht mithilfe von Agentenkarten – JSON-Dokumenten, die die Fähigkeiten, Eingaben und Ausgaben eines Agenten beschreiben. Agentenkarten werden über einheitliche, bekannte Endpunkte (wie den empfohlenen <code>/.well-known/agent-card.json</code> -Endpunkt) bereitgestellt, sodass Clients die Fähigkeiten eines Agenten ermitteln und abfragen können, bevor sie die Zusammenarbeit initiieren.</p><p>Nachfolgend sehen Sie eine Beispiel-Agentenkarte für den benutzerdefinierten Archivagenten von Elastic, „Archie Archivist“. Beachten Sie, dass Softwareanbieter wie Elastic ihre A2A-Agenten hosten und eine URL für den Zugriff bereitstellen:</p><pre><code>{
  "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"]
}</code></pre><p>Diese Agentenkarte offenbart mehrere wichtige Aspekte des Archivagenten von Elastic. Der Agent stellt sich als „Archie Archivist“ vor und gibt seinen Zweck klar an: die Suche nach historischen Nachrichtendokumenten in einem Elasticsearch-Index. Die Karte gibt den Anbieter (Elastic) und die Protokollversion (0.3.0) an und gewährleistet so die Kompatibilität mit anderen A2A-kompatiblen Agenten. Am wichtigsten ist jedoch, dass das <code>skills</code> -Array die spezifischen Fähigkeiten auflistet, die dieser Agent bietet, darunter leistungsstarke Suchfunktionen und intelligente Indexerkundung. Jede Fähigkeit definiert, welche Eingabe- und Ausgabemodi sie unterstützt, sodass Clients genau verstehen, wie sie mit diesem Agenten kommunizieren können. Dieser Agent basiert auf dem Agent Builder-Service von Elastic, der eine Reihe nativer, LLM-gestützter Tools und API-Endpunkte bereitstellt, um mit Ihrem Datenspeicher zu kommunizieren und nicht nur Daten daraus abzurufen. Zugriff auf A2A-Agenten in Elasticsearch finden Sie <a href="https://www.elastic.co/docs/solutions/search/agent-builder/a2a-server">hier</a>.</p><h4>2. Verhandlung</h4><p>Kunden und Agenten müssen sich auf Kommunikationsmethoden einigen – egal ob die Interaktion per Text, Formular, iFrames oder sogar Audio/Video erfolgt –, um eine ordnungsgemäße Benutzerinteraktion und einen reibungslosen Datenaustausch zu gewährleisten. Diese Aushandlung findet zu Beginn der Zusammenarbeit der Agenten statt und legt die Protokolle fest, die ihre Interaktion während des gesamten Arbeitsablaufs regeln. Ein Mitarbeiter im telefonischen Kundenservice könnte beispielsweise die Kommunikation über Audiostreams aushandeln, während ein Mitarbeiter im Datenanalyse-Bereich strukturiertes JSON bevorzugen könnte. Der Verhandlungsprozess stellt sicher, dass beide Parteien Informationen effektiv in einem Format austauschen können, das ihren Fähigkeiten und den Anforderungen der jeweiligen Aufgabe entspricht.</p><p>Die im obigen JSON-Ausschnitt aufgeführten Funktionen verfügen alle über Eingabe- und Ausgabeschemata; diese legen fest, wie andere Agenten mit diesem Agenten interagieren sollen.</p><h4>3. Aufgaben- und Zustandsmanagement</h4><p>Kunden und Agenten benötigen Mechanismen, um während der gesamten Aufgabenausführung über Aufgabenstatus, Änderungen und Abhängigkeiten zu kommunizieren. Dies umfasst die Verwaltung des gesamten Lebenszyklus einer Aufgabe von der Erstellung und Zuweisung bis hin zu Fortschrittsaktualisierungen und Statusänderungen. Typische Status sind: ausstehend, in Bearbeitung, abgeschlossen oder fehlgeschlagen. Das System muss außerdem Abhängigkeiten zwischen Aufgaben verfolgen, um sicherzustellen, dass die erforderlichen Vorarbeiten abgeschlossen sind, bevor die abhängigen Aufgaben beginnen. Fehlerbehandlung und Wiederholungslogik sind ebenfalls wesentliche Bestandteile, die es dem System ermöglichen, sich nach Fehlern reibungslos zu erholen und weiterhin Fortschritte in Richtung des Hauptziels zu erzielen.</p><p>Beispiel einer Aufgabenmeldung:</p><pre><code>{
  "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"
    }
  }
}</code></pre><p>Diese Beispiel-Aufgabennachricht veranschaulicht mehrere wichtige Aspekte der A2A-Kommunikation.</p><ul><li>Die <strong>Nachrichtenstruktur</strong> umfasst Metadaten wie eine eindeutige Nachrichtenkennung, die Art der gesendeten Nachricht, die Kennung von Absender und Empfänger sowie einen Zeitstempel zur Nachverfolgung und Fehlerbehebung.</li><li>Die <strong>Nutzlast</strong> enthält die eigentlichen Aufgabeninformationen, in denen angegeben wird, welche Funktion auf dem Remote-Agenten aufgerufen wird, und die notwendigen Parameter zur Ausführung dieser Funktion bereitgestellt werden.</li><li>Der Abschnitt <strong>„Kontext“</strong> liefert zusätzliche Informationen, die dem empfangenden Agenten helfen, den übergeordneten Arbeitsablauf zu verstehen, einschließlich Fristen und Prioritätsstufen, die Aufschluss darüber geben, wie der Agent seine Ressourcen einsetzen und seine Arbeit planen sollte.</li></ul><h4>4. Zusammenarbeit</h4><p>Kunden und Agenten <strong>müssen</strong> eine dynamische, aber dennoch strukturierte Interaktion unterstützen, die es den Agenten ermöglicht, Klärungen, Informationen oder Teilaktionen vom Kunden, anderen Agenten oder Benutzern anzufordern. Dadurch entsteht eine kollaborative Umgebung, in der Agenten bei unklaren Anfangsanweisungen Nachfragen stellen, zusätzlichen Kontext anfordern können, um bessere Entscheidungen zu treffen, Teilaufgaben an andere Agenten mit besser geeigneter Expertise delegieren und Zwischenergebnisse zur Rückmeldung liefern können, bevor sie mit der Gesamtaufgabe fortfahren. Diese multidirektionale Kommunikation stellt sicher, dass die Agenten nicht isoliert arbeiten, sondern in einen fortlaufenden Dialog eingebunden sind, der zu besseren Ergebnissen führt.</p><h3>Verteilte Peer-to-Peer-Kommunikation</h3><p>A2A ermöglicht verteilte Kommunikation, bei der Agenten von verschiedenen Organisationen gehostet werden können, wobei einige Agenten intern verwaltet werden, während andere von Drittanbietern bereitgestellt werden. Diese Agenten können auf verschiedenen Infrastrukturen ausgeführt werden – potenziell über mehrere Cloud-Anbieter oder lokale Rechenzentren hinweg. Sie verwenden möglicherweise unterschiedliche zugrunde liegende LLMs, wobei einige Agenten auf GPT-Modellen, andere auf Claude und wieder andere auf Open-Source-Alternativen basieren. Agenten könnten sogar über verschiedene geografische Regionen hinweg operieren, um den Anforderungen der Datensouveränität nachzukommen oder die Latenz zu reduzieren. Trotz dieser Vielfalt stimmen alle Agenten einem gemeinsamen Kommunikationsprotokoll für den Informationsaustausch zu, wodurch die Interoperabilität unabhängig von Implementierungsdetails gewährleistet wird. Diese verteilte Architektur bietet Flexibilität beim Aufbau und der Bereitstellung von Systemen und ermöglicht es Organisationen, die besten Agenten und Infrastrukturen für ihre spezifischen Bedürfnisse zu kombinieren.</p><p>Dies ist die endgültige Architektur der Newsroom-Anwendung:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/82e01a0d9746038eafd69d11177042b5390507ae-1600x838.png" style="width: 100%;" /><h2>Abschnitt 3: Modellkontextprotokoll (MCP)</h2><h3>Definition und Zweck</h3><p>Das Model Context Protocol (MCP) ist ein von Anthropic entwickeltes standardisiertes Protokoll, das dazu dient, ein einzelnes LLM mit benutzerdefinierten Werkzeugen, Ressourcen und Eingabeaufforderungen sowie anderen ergänzenden Codebasiserweiterungen zu erweitern und zu stärken. MCP bietet eine universelle Schnittstelle zwischen Sprachmodellen und den externen Ressourcen, die sie benötigen, um Aufgaben effektiv zu erledigen. Dieser <a href="https://www.elastic.co/search-labs/blog/mcp-current-state">Artikel</a> beschreibt den aktuellen Stand von MCP anhand von Anwendungsbeispielen, aufkommenden Trends und der Implementierung von Elastic.</p><h3>Kernkonzepte des MCP</h3><p>MCP arbeitet mit einer Client-Server-Architektur und besteht aus drei Hauptkomponenten:</p><ul><li><strong>Clients:</strong> Anwendungen (wie Claude Desktop oder kundenspezifische KI-Anwendungen), die sich mit MCP-Servern verbinden, um auf deren Funktionen zuzugreifen.</li><li><strong>Server</strong>: Anwendungen, die Ressourcen, Werkzeuge und Eingabeaufforderungen für Sprachmodelle bereitstellen. Jeder Server ist darauf spezialisiert, Zugriff auf bestimmte Funktionen oder Datenquellen zu bieten.<ul><li><strong>Tools</strong>: Benutzerdefinierte Funktionen, die Modelle aufrufen können, um Aktionen auszuführen, wie z. B. Datenbanken zu durchsuchen, externe APIs aufzurufen oder Datentransformationen durchzuführen.</li><li><strong>Ressourcen:</strong> Datenquellen, aus denen Modelle lesen können, die dynamische oder statische Daten liefern und über URI-Muster aufgerufen werden (ähnlich wie REST-Routen).</li><li><strong>Eingabeaufforderungen: </strong>Wiederverwendbare Eingabeaufforderungsvorlagen mit Variablen, die das Modell bei der Erfüllung spezifischer Aufgaben unterstützen.</li></ul></li></ul><h3>Anfrage-Antwort-Muster</h3><p>MCP folgt einem bekannten Anfrage-Antwort-Interaktionsmuster, ähnlich wie REST-APIs. Der Client (LLM) fordert eine Ressource an oder ruft ein Tool auf. Anschließend verarbeitet der MCP-Server die Anfrage und gibt das Ergebnis zurück, das der LLM zur Fortsetzung seiner Aufgabe verwendet. Dieses zentralisierte Modell mit peripheren Servern bietet im Vergleich zur Peer-to-Peer-Agentenkommunikation ein einfacheres Integrationsmuster.</p><h3>MCP im Newsroom</h3><p>In unserem Beispiel aus der Nachrichtenredaktion nutzen einzelne Mitarbeiter MCP-Server, um auf die benötigten Tools und Daten zuzugreifen:</p><ul><li><strong>Der Forscheragent</strong> verwendet:<ul><li>News API MCP Server (Zugriff auf Nachrichtendatenbanken)</li><li>Faktencheck-Server von MCP (Überprüfung von Behauptungen anhand vertrauenswürdiger Quellen)</li><li>Akademische Datenbank MCP-Server (wissenschaftliche Artikel und Forschungsergebnisse)</li></ul></li><li><strong>Reporteragent</strong> verwendet:<ul><li>Styleguide MCP Server (Schreibstandards für Redaktionen)</li><li>Template MCP Server (Artikelvorlagen und -formate)</li><li>Bildbibliothek MCP-Server (Stockfotos und Grafiken)</li></ul></li><li><strong>Redaktionsagenten</strong> verwenden:<ul><li>Grammatikprüfung MCP-Server (Tools zur Sprachqualitätsprüfung)</li><li>Plagiatserkennungsserver MCP (Originalitätsprüfung)</li><li>SEO-Analyse MCP-Server (Überschriften- und Keyword-Optimierung)</li></ul></li><li><strong>Verlagsagenten</strong> verwenden:<ul><li>CMS MCP Server (API für Content-Management-Systeme)</li><li>CI/CD MCP-Server (Bereitstellungspipeline)</li><li>Analytics MCP Server (Tracking und Monitoring)</li></ul></li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b67311e3b58b27f9eb1b42a7b1dbad47ef3be4ad-808x535.png" style="width: 100%;" /><h2><br/>Abschnitt 4: Architekturvergleich</h2><h3>Wann sollte man A2A verwenden?</h3><p>Die A2A-Architektur ist besonders geeignet für <strong>Szenarien, die eine echte Zusammenarbeit mehrerer Agenten erfordern</strong>. Mehrstufige Arbeitsabläufe, die eine Koordination erfordern, profitieren stark von A2A, insbesondere wenn Aufgaben mehrere sequentielle oder parallele Schritte umfassen, Arbeitsabläufe Iteration und Verfeinerung erfordern und Prozesse Kontrollpunkte und Validierungsanforderungen haben. In unserem Beispiel aus der Nachrichtenredaktion sieht der Arbeitsablauf vor, dass der Reporter den Artikel schreibt, ihn aber gegebenenfalls an den Rechercheur zurückgeben muss, wenn das Vertrauen in bestimmte Fakten gering ist, bevor er ihn an den Redakteur und schließlich an den Verleger weiterleitet.</p><p><strong>Domänenspezifische Spezialisierung über mehrere Bereiche hinweg</strong> ist ein weiterer wichtiger Anwendungsfall für A2A. Wenn mehrere Experten aus verschiedenen Bereichen benötigt werden, um eine größere Aufgabe zu bewältigen, wobei jeder Experte über fundiertes Fachwissen und spezialisierte Denkfähigkeiten für unterschiedliche Aspekte verfügt, bietet A2A den Koordinierungsrahmen, der für die Herstellung dieser Verbindungen erforderlich ist. Die Redaktion ist dafür ein perfektes Beispiel: Der Rechercheur ist auf die Informationsbeschaffung spezialisiert, der Reporter auf das Schreiben und der Redakteur auf die Qualitätskontrolle – jeder mit einem besonderen Fachgebiet.</p><p>Die Notwendigkeit eines autonomen Agentenverhaltens macht A2A besonders wertvoll. Agenten, die<strong> selbstständig Entscheidungen treffen können, proaktives Verhalten auf der Grundlage sich ändernder Bedingungen zeigen und sich dynamisch an die Workflow-Anforderungen anpassen können,</strong> sind in einer A2A-Architektur besonders erfolgreich. Die horizontale Skalierung spezialisierter Funktionen ist ein weiterer entscheidender Vorteil – anstatt eines einzigen Alleskönners arbeiten mehrere spezialisierte Agenten koordiniert zusammen, und mehrere Instanzen desselben Agenten können Teilaufgaben asynchron bearbeiten. Bei Eilmeldungen in unserer Redaktion arbeiten beispielsweise mehrere Reporter gleichzeitig an verschiedenen Aspekten derselben Geschichte.</p><p>Schließlich eignen sich Aufgaben, die eine echte Zusammenarbeit mehrerer Agenten erfordern, ideal für A2A. Dies umfasst <a href="https://arxiv.org/abs/2404.18796">LLM-als-Jury-Bewertungsmechanismen</a> , Konsensfindungs- und Abstimmungssysteme sowie <strong>kollaborative Problemlösungsansätze, bei denen mehrere Perspektiven erforderlich sind,</strong> um das beste Ergebnis zu erzielen.</p><h3>Wann sollte MCP verwendet werden?</h3><p>Das Model Context Protocol eignet sich ideal zur Erweiterung der Fähigkeiten eines einzelnen KI-Modells. Wenn ein einzelnes KI-Modell Zugriff auf mehrere Tools und Datenquellen benötigt, bietet MCP die perfekte Lösung mit zentralisierter Datenverarbeitung in Verbindung mit verteilten Tools und unkomplizierter Tool-Integration. In unserem Beispiel aus der Nachrichtenredaktion benötigt der Rechercheagent (ein Modell) Zugriff auf mehrere Datenquellen, darunter die News-API, Faktencheck-Dienste und akademische Datenbanken – der Zugriff erfolgt über standardisierte MCP-Server.</p><p>Die Integration standardisierter Werkzeuge gewinnt dann an Bedeutung, wenn die breite Weitergabe und Wiederverwendbarkeit von Werkzeugintegrationen wichtig ist. MCP glänzt hier mit seinem Ökosystem aus vorkonfigurierten MCP-Servern, die die Entwicklungszeit für gängige Integrationen erheblich reduzieren. Wenn Einfachheit und Wartbarkeit gefordert sind, sind die Anfrage-Antwort-Muster von MCP den Entwicklern vertraut, leichter zu verstehen und zu debuggen als verteilte Systeme und weisen eine geringere betriebliche Komplexität auf.</p><p>Schließlich wird MCP häufig von Softwareanbietern angeboten, um die Fernkommunikation mit ihren Systemen zu vereinfachen. Diese vom Anbieter bereitgestellten MCP-Server reduzieren die Einarbeitungs- und Entwicklungszeit erheblich und bieten gleichzeitig eine standardisierte Schnittstelle zu proprietären Systemen, wodurch die Integration wesentlich einfacher wird als die Entwicklung kundenspezifischer APIs.</p><h3>Wann man beides verwendet (A2A ❤️&#x27;s MCP)</h3><p>Viele hochentwickelte Systeme profitieren von der Kombination von A2A und MCP, wie in der <a href="https://a2a-protocol.org/latest/topics/a2a-and-mcp/">A2A-Dokumentation zur MCP-Integration</a> beschrieben. Systeme, die sowohl Koordination als auch Standardisierung erfordern, sind ideale Kandidaten für einen hybriden Ansatz. A2A übernimmt die Agentenkoordination und Workflow-Orchestrierung, während MCP den einzelnen Agenten Zugriff auf die Tools ermöglicht. In unserem Beispiel aus der Redaktion koordinieren sich die Mitarbeiter über A2A; der Arbeitsablauf verläuft dabei vom Reporter über den Rechercheur zum Redakteur und schließlich zum Verleger. Allerdings nutzt jeder Agent MCP-Server für seine spezialisierten Tools, wodurch eine klare architektonische Trennung entsteht.</p><p>Mehrere spezialisierte Agenten, die jeweils MCP für den Werkzeugzugriff nutzen, stellen ein gängiges Muster dar, bei dem es eine Agentenkoordinierungsschicht gibt, die von A2A verwaltet wird, und eine Werkzeugzugriffsschicht, die von MCP verwaltet wird. Durch diese klare Trennung der Zuständigkeiten werden die Systeme leichter verständlich und wartungsfreundlicher.</p><p>Die Vorteile der Kombination beider Ansätze sind beträchtlich. Sie profitieren von den organisatorischen Vorteilen von Multiagentensystemen, einschließlich Spezialisierung, Autonomie und Parallelverarbeitung, und genießen gleichzeitig die Vorteile von MCP in Bezug auf Standardisierung und Ökosystem, wie z. B. Werkzeugintegration und Ressourcenzugriff. Zwischen Agentenkoordination (A2A) und Ressourcenzugriff (MCP) besteht eine klare Trennung, und vor allem ist A2A für kleinere Aufgaben wie den API-Zugriff allein nicht erforderlich – MCP erledigt diese effizient ohne den Aufwand einer Multi-Agenten-Orchestrierung.</p><p><strong>Häufig gestellte Fragen: A2A vs. MCP – Anwendungsfälle</strong></p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Feature</td><td style="border: 1px solid #ccc; padding: 4px;">Agent2Agent (A2A)</td><td style="border: 1px solid #ccc; padding: 4px;">Model Context Protocol (MCP)</td><td style="border: 1px solid #ccc; padding: 4px;">Hybrid (A2A + MCP)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Hauptziel</td><td style="border: 1px solid #ccc; padding: 4px;">Multi-Agent-Koordination: Ermöglicht es einem Team spezialisierter Agenten, gemeinsam an komplexen, mehrstufigen Arbeitsabläufen zu arbeiten.</td><td style="border: 1px solid #ccc; padding: 4px;">Erweiterung für einzelne Agenten: Erweitert die Funktionalität eines einzelnen LLM/Agenten um externe Tools, Ressourcen und Daten.</td><td style="border: 1px solid #ccc; padding: 4px;">Gemeinsame Stärke: A2A kümmert sich um den Workflow des Teams, während MCP jedem Teammitglied die nötigen Werkzeuge zur Verfügung stellt.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Beispiel eines Redaktionsteams</td><td style="border: 1px solid #ccc; padding: 4px;">Der Arbeitsablauf: Nachrichtenchef → Reporter → Rechercheur → Redakteur → Verleger. Dies ist die Koordinationsebene.</td><td style="border: 1px solid #ccc; padding: 4px;">Tools für den einzelnen Agenten: Der Reporter-Agent greift über MCP auf den Styleguide-Server und den Vorlagenserver zu. Dies ist die Werkzeugzugriffsschicht.</td><td style="border: 1px solid #ccc; padding: 4px;">Das vollständige System: Der Reporter stimmt sich mit dem Redakteur ab (A2A) und nutzt den Image Library MCP Server, um ein Bild für den Artikel zu finden.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Wann welches Werkzeug verwenden?</td><td style="border: 1px solid #ccc; padding: 4px;">Wenn Sie echte Zusammenarbeit, Iteration und Verfeinerung benötigen oder spezialisiertes Fachwissen auf mehrere Mitarbeiter verteilt ist.</td><td style="border: 1px solid #ccc; padding: 4px;">Wenn ein einzelner Agent Zugriff auf mehrere Tools und Datenquellen benötigt oder eine standardisierte Integration mit proprietären Systemen erfordert.</td><td style="border: 1px solid #ccc; padding: 4px;">Wenn Sie die organisatorischen Vorteile von Multiagentensystemen und die Standardisierungs- und Ökosystemvorteile von MCP benötigen.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Kernnutzen</td><td style="border: 1px solid #ccc; padding: 4px;">Autonomie und Skalierbarkeit: Agenten können unabhängige Entscheidungen treffen, und das System ermöglicht die horizontale Skalierung spezialisierter Funktionen.</td><td style="border: 1px solid #ccc; padding: 4px;">Einfachheit und Standardisierung: Durch die zentrale Logik ist die Fehlersuche und Wartung einfacher, und es wird eine universelle Schnittstelle für Ressourcen bereitgestellt.</td><td style="border: 1px solid #ccc; padding: 4px;">Klare Trennung der Zuständigkeiten: Macht das System verständlicher: A2A = Teamarbeit, MCP = Werkzeugzugriff.</td>
                    </tr>
              </tbody>
            </table>
          <img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ddf6a29b1107ac6a63e94ecef703abc561a29e1e-986x656.png" style="width: 100%;" /><h2>Fazit</h2><p>Dies ist der erste von zwei Teilen, die die Implementierung von A2A-basierten Agenten behandeln, die durch MCP-Server unterstützt werden, um Support und externen Zugriff auf Daten und Tools zu ermöglichen. Im nächsten Abschnitt wird der konkrete Code untersucht, um zu demonstrieren, wie die einzelnen Elemente zusammenarbeiten, um die Abläufe in einer Online-Nachrichtenredaktion nachzubilden. Obwohl beide Frameworks für sich genommen äußerst leistungsfähig und flexibel sind, werden Sie erst im Zusammenspiel sehen, wie sehr sie sich ergänzen.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Sie wissen schon, Kontext – Teil I: Die Entwicklung von hybrider Suche und Kontextgestaltung]]></title>
            <description><![CDATA[Erfahren Sie, wie sich hybride Suche und Kontextgestaltung von lexikalischen Grundlagen weiterentwickelt haben, um die nächste Generation agentenbasierter KI-Workflows zu ermöglichen.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/context-engineering-hybrid-search-evolution-agentic-ai</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/context-engineering-hybrid-search-evolution-agentic-ai</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Woody Walton]]></dc:creator>
            <pubDate>Wed, 12 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7e59a0671aa9ea32d68188a693936a66ebf48625-1000x628.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<h2>Unsere brandneue agentische KI-Welt</h2><p>Wie viele von uns bin auch ich gleichermaßen begeistert und erstaunt über das Tempo, mit dem sich die Fähigkeiten der KI weiterentwickeln. Wir erlebten zum ersten Mal, wie große Sprachmodelle (LLMs) und die Vektorsuche uns in die semantische Revolution katapultierten, bei der wir nicht mehr mühsam mit Schlüsselwörtern herumsuchen mussten, um Dinge zu finden. Dann zeigten uns die LLMs neue Wege der Interaktion mit unseren Daten auf, indem sie Chat-Schnittstellen nutzten, um Anfragen in natürlicher Sprache in Antworten umzuwandeln, die riesige Wissensdatenbanken in leicht verständliche Zusammenfassungen destillierten. Wir jetzt (schon!) haben die Anfänge einer automatisierten LLM-gesteuerten Logik in Form von „agentischen KI“-Workflows, die eine eingehende Anfrage semantisch verstehen, über die zu unternehmenden Schritte nachdenken und dann aus den verfügbaren Werkzeugen auswählen können, um iterativ Aktionen auszuführen, um diese Ziele zu erreichen.</p><p>Das Versprechen agentenbasierter KI zwingt uns, uns von der primären Verwendung von „Prompt Engineering“ zur Gestaltung unserer generativen KI-Interaktionen hin zu einem Fokus darauf zu entwickeln, wie wir agentenbasierte Werkzeuge dabei unterstützen können, die relevantesten und effizientesten Zusatzinformationen zu erhalten, die das LLM bei der Generierung seiner Antworten berücksichtigen muss – „Context Engineering“ ist die nächste Herausforderung. Die hybride Suche ist mit Abstand das leistungsstärkste und flexibelste Mittel, um relevante Kontextinformationen zu finden, und die Search AI-Plattform von Elastic eröffnet völlig neue Möglichkeiten, Daten im Dienste des Context Engineering zu nutzen. In diesem Artikel werden wir aus zwei Blickwinkeln erörtern, wie LLMs die Welt der Informationswiedergewinnung verändert haben, und anschließend darauf eingehen, wie sie für bessere Ergebnisse zusammenarbeiten können. Es gibt noch viel zu besprechen…</p><h2>Teil I: Wie LLMs die Suche verändert haben</h2><p>Beginnen wir mit der Frage, wie LLMs die Art und Weise verändert haben, wie wir auf Informationen zugreifen und sie abrufen.</p><h3>Unser lexikalisches Erbe</h3><p>Wir alle leben schon seit langer Zeit in der etwas eingeschränkten Welt der lexikalischen Suche (ziemlich gut, so gut es eben geht). Die Suche ist das erste Werkzeug, zu dem wir greifen, wenn wir recherchieren oder ein neues Projekt beginnen, und bis vor kurzem lag es an uns, unsere Suchanfragen so zu formulieren, dass eine lexikalische Suchmaschine sie versteht. Die lexikalische Suche basiert auf dem Abgleich von Suchbegriffen mit Schlüsselwörtern in einem Dokumentenkorpus – unabhängig davon, ob der Inhalt unstrukturiert oder strukturiert ist. Damit eine lexikalische Suche ein Dokument als Treffer zurückgibt, muss dieses mit dem entsprechenden Schlüsselwort übereinstimmen (oder über ein kontrolliertes Vokabular wie eine Synonymliste oder ein Wörterbuch verfügen, um die konzeptionelle Verbindung für uns herzustellen).</p><pre><code>POST my-index/_search
{
  "size": 10,
  "query": {
    "semantic": {
      "query": "machine learning applications",
      "field": "semantic-content-field"
    }
  }
}</code></pre><p><em>Beispiel einer lexikalischen <a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-multi-match-query"></a> Mehrfachabfrage</em></p><p>Suchmaschinen haben zumindest die Möglichkeit, Treffer mit einer Relevanzbewertung zurückzugeben. Suchmaschinen bieten eine Fülle von Abfragesyntaxoptionen, um indizierte Daten effektiv anzusprechen, sowie integrierte Relevanzalgorithmen, die die Ergebnisse im Verhältnis zur Absicht der Abfragesyntax des Benutzers bewerten. Suchmaschinen profitieren von jahrzehntelangen Fortschritten bei Relevanz-Ranking-Algorithmen und sind dadurch eine effiziente Datenabrufplattform, die Ergebnisse liefern kann, die nach ihrer Relevanz für die Suchanfrage bewertet und sortiert sind. Datenbanken und andere Systeme, die SQL als ihre Hauptmethode zum Abrufen von Daten verwenden, sind hier im Nachteil: Es gibt kein Relevanzkonzept in einer Datenbankabfrage; sie können bestenfalls Ergebnisse alphabetisch oder numerisch sortieren. Die gute Nachricht ist, dass Sie mit diesen Schlüsselwörtern alle Treffer (Recall) erhalten, diese aber nicht unbedingt in einer hilfreichen Reihenfolge im Hinblick darauf, <em>warum</em> Sie danach gesucht haben (Präzision). Das ist ein wichtiger Punkt, wie wir gleich sehen werden…</p><h3>Betreten Sie den (semantischen) Drachen</h3><p>Das Potenzial von Vektordarstellungen von Informationen als Alternative zur Stichwortsuche wird schon seit <a href="https://www.elastic.co/search-labs/blog/introduction-to-vector-search">geraumer Zeit</a> erforscht. Vektoren bergen großes Potenzial, da sie uns aus dem rein schlüsselwortbasierten Modus des Inhaltsabgleichs herausführen – da Vektoren numerische Darstellungen von Begriffen und Gewichtungen sind, ermöglichen sie es, Konzepte mathematisch nahe beieinander zu bringen, basierend auf dem Verständnis eines Sprachmodells darüber, wie Begriffe im Trainingsbereich miteinander in Beziehung stehen. Die lange Verzögerung bei der allgemeinen Vektorsuche war darauf zurückzuführen, dass die Modelle größtenteils auf spezifische Domänen beschränkt waren; sie waren einfach nicht groß genug, um die vielen verschiedenen Konzepte, die ein Begriff in unterschiedlichen Kontexten repräsentieren könnte, ausreichend zu verstehen.</p><p>Erst mit dem Aufkommen der Large Language Models (LLMs) vor einigen Jahren, die in der Lage sind, mit viel größeren Datenmengen zu trainieren (unter Verwendung <a href="https://en.wikipedia.org/wiki/Transformer_(deep_learning_architecture)">von Transformatoren</a> und <a href="https://en.wikipedia.org/wiki/Attention_(machine_learning)">Aufmerksamkeit</a>), wurde die Vektorsuche praktikabel – die Größe und Tiefe der LLMs ermöglichten es Vektoren schließlich, genügend Nuancen zu speichern, um tatsächlich semantische Bedeutung zu erfassen. Dieser plötzliche Anstieg des Verständnisses ermöglichte es LLMs, nun eine große Anzahl von Funktionen der natürlichen Sprachverarbeitung (NLP) zu erfüllen, die zuvor gesperrt waren. Die vielleicht wirkungsvollste Funktion ist die Fähigkeit, aus dem Kontext dessen, was sich bisher in der Sequenz befindet, auf das wahrscheinlichste nächste Glied in einer Sequenz zu schließen. Inferenz ist der Prozess, der generativer KI ihre nahezu menschenähnliche Fähigkeit verleiht, Texte zu erzeugen. Der KI-generierte Text basiert auf dem Verständnis des LLM darüber, wie Begriffe in seinen Trainingsdaten miteinander in Beziehung stehen, und verwendet außerdem die Formulierung der Anfrage, um zwischen verschiedenen Kontexten, in denen die Begriffe vorkommen könnten, zu unterscheiden.</p><p>So magisch generative KI auch sein mag, es <em>gibt</em> Einschränkungen bei LLMs, die zu Fehlern in Qualität und Genauigkeit führen, die gemeinhin als Halluzinationen bezeichnet werden. Halluzinationen treten auf, wenn das LLM keinen Zugang zu den Informationen hat (oder nicht in den richtigen Kontext geführt wird), um seine Antwort auf die Wahrheit zu gründen. Stattdessen generiert es, um hilfreich zu sein, eine selbstsicher und plausibel klingende, aber erfundene Antwort. Ein Teil der Ursache liegt darin, dass LLMs zwar den Sprachgebrauch in großen Bereichen mit vielfältigen Informationen erlernen, das Training aber irgendwann beendet werden muss. Daher gibt es einen Zeitfaktor für ihr Verständnis – das heißt, das Modell kann nur das wissen, was bis zum Zeitpunkt des Trainingsstopps korrekt war. Ein weiterer Faktor für Halluzinationen ist, dass das Modell normalerweise keine Kenntnis von privat gespeicherten Daten hat (Daten, die nicht im öffentlichen Internet verfügbar sind), und das ist besonders bedeutsam, wenn diese Daten spezifische Begriffe und Nomenklatur enthalten.</p><h3>Vektordatenbanken</h3><p>LLMs vektorisieren Inhalte in ihren Modellraum mithilfe einer Technik namens Text Embedding. Dabei wird die semantische Bedeutung des Inhalts auf der Grundlage des erhaltenen Trainings in die Weltanschauung des Modells <a href="https://www.elastic.co/search-labs/blog/hybrid-search-multiple-embeddings">eingebettet</a> oder abgebildet. Zur Vorbereitung und Verarbeitung von Inhalten für die Einbettung sind einige Schritte erforderlich, darunter <a href="https://www.elastic.co/search-labs/blog/chunking-strategies-elasticsearch">Chunking</a> und Tokenisierung (sowie <a href="https://www.kaggle.com/code/danishmahdi/subword-tokenization-bpe-wordpiece-and-unigram">Subwort-Tokenisierung</a>). Das Ergebnis ist typischerweise eine Menge dichter Vektoren, die das Verständnis des Modells für die Bedeutung dieses Inhaltsabschnitts innerhalb seines Vektorraums darstellen. Chunking ist ein ungenaues Verfahren, das darauf abzielt, Inhalte an die Verarbeitungsbeschränkungen eines Modells zur Generierung von Einbettungen anzupassen und gleichzeitig verwandten Text mithilfe semantischer Konstrukte wie Satz- und Absatzindikatoren zu einem Chunk zusammenzufassen.</p><p>Die Notwendigkeit der Segmentierung kann zu einem gewissen semantischen Verlust in einem eingebetteten Dokument führen, da einzelne Segmente nicht vollständig mit anderen Segmenten aus demselben Dokument verknüpft sind. Die inhärente Undurchsichtigkeit neuronaler Netze kann diesen Verlust noch verschlimmern – ein LLM ist in Wahrheit eine „Black Box“, bei der die während des Trainings hergestellten Verbindungen zwischen Begriffen und Konzepten nicht deterministisch und für Menschen nicht interpretierbar sind. Dies führt zu Problemen mit der Erklärbarkeit, der Wiederholbarkeit, unbewussten Voreingenommenheit und möglicherweise zu einem Verlust an Vertrauen und Genauigkeit. Dennoch ist die Möglichkeit, Ideen semantisch zu verknüpfen und bei Suchanfragen nicht an bestimmte Schlüsselwörter gebunden zu sein, extrem wirkungsvoll:</p><pre><code>POST my-index/_search 
{
  "size": 10, 
  "query": {
    "semantic": {
      "query": "machine learning applications",
      "field": "semantic-content-field"
    }
  }
} </code></pre><p><em>Ein Beispiel für <a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-semantic-query">eine semantische</a> Anfrage</em></p><p>Bei Vektordatenbanken gibt es noch einen weiteren Punkt zu beachten: Sie sind keine Suchmaschinen, sondern Datenbanken! Bei einer <a href="https://www.elastic.co/search-labs/blog/introduction-to-vector-search">Vektorähnlichkeitssuche</a> werden die Suchbegriffe kodiert, um einen Satz von (Einbettungs-)Koordinaten innerhalb des Vektorraums des Modells zu finden. Diese Koordinaten dienen dann als Zielscheibe, um die Dokumente zu finden, die die „nächsten Nachbarn“ der Zielscheibe sind – das heißt, der Rang eines Dokuments (oder seine Platzierung in den Ergebnissen) wird durch die berechnete <em>Ähnlichkeitsdistanz</em> der Koordinaten dieses Dokuments zu den Koordinaten der Anfrage bestimmt. In welche Richtung sollte die Rangfolge Vorrang haben, welcher der möglichen Kontexte entspricht am ehesten der Absicht des Nutzers? Das Bild, mit dem ich es vergleiche, ist eine Szene aus dem Film <a href="https://www.youtube.com/watch?v=x3h7xz558EY&amp;start=3&amp;end=86">Stargate</a>, in der wir die sechs Koordinatenpunkte haben, die sich schneiden, um uns das Ziel (die Zielscheibe) zu nennen, aber wir können es nicht erreichen, ohne das „7. Symbol“ zu kennen – die Koordinaten des Startpunkts, die die subjektive Absicht des Benutzers repräsentieren. Anstatt also die relative Rangfolge der Vektoren auf einer sich ständig erweiternden und undifferenzierten Sphäre der Ähnlichkeit zu basieren, können wir durch die Berücksichtigung der subjektiven Absicht der Anfrage mittels ausdrucksstarker Syntax und Relevanzbewertung so etwas wie einen <em>Zylinder</em> abgestufter subjektiver Relevanz erhalten.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1ddfaae0c1496d08d7d30419e6d2aeaeacfc0ea2-1600x544.png" style="width: 100%;" /><p>Die Inferenzfähigkeiten eines LLM können zwar helfen, den wahrscheinlichsten Kontext <em>für</em> die Anfrage zu identifizieren, das Problem besteht jedoch darin, dass <em>ohne diese Unterstützung</em> die Koordinaten der eingehenden Anfrage <em>nur</em> anhand der Art und Weise bestimmt werden können, wie das Modell ursprünglich trainiert wurde.</p><p>In gewisser Hinsicht könnte man sagen, dass Vektorähnlichkeit das entgegengesetzte Extrem darstellt als eine strikte Stichwortübereinstimmung – ihre Stärke liegt in ihrer Fähigkeit, die Probleme der Begriffsabweichung zu überwinden, aber <a href="https://medium.com/data-science/vector-embeddings-are-lossy-heres-what-to-do-about-it-4f9a8ee58bb7">fast bis zum Exzess</a>: LLMs neigen dazu, verwandte Konzepte zu vereinheitlichen, anstatt zwischen ihnen zu unterscheiden. Die Vektorähnlichkeit verbessert unsere Fähigkeit, Inhalte semantisch abzugleichen, garantiert aber keine Präzision, da sie exakte Schlüsselwörter und spezifische Details übersehen kann, die vom Modell nicht ausreichend unterschieden werden. Die Vektorähnlichkeitssuche ist an sich schon leistungsstark, aber wir brauchen Möglichkeiten, die Ergebnisse, die wir aus einer Vektordatenbank abrufen, mit Ergebnissen anderer Abrufmethoden zu korrelieren.</p><h3>Neubewertungstechniken</h3><p>An dieser Stelle sei eine allgemeine Technik namens Reranking erwähnt, bei der die Ergebnismengen neu bewertet oder normalisiert werden, um eine einheitliche Rangfolge zu erhalten. Die Notwendigkeit einer Neubewertung könnte darauf zurückzuführen sein, dass Ergebnisse aus mehreren Quellen oder Abrufmethoden unterschiedliche Bewertungsmechanismen (oder gar keine, SQL!) haben, oder die Neubewertung könnte dazu dienen, die Ergebnisse aus nicht-semantischen Quellen semantisch an die Anfrage des Benutzers anzupassen. Das Reranking ist ein zweiter Schritt, bei dem es sich um eine Reihe von Ergebnissen handelt, die durch eine <em>erste Abrufmethode</em> (z. B. Anschließend werden SQL-, lexikalische und Vektorsuchen mit einer anderen Bewertungsmethode neu geordnet.</p><p>Es stehen verschiedene Ansätze zur Verfügung, darunter <a href="https://www.elastic.co/docs/solutions/search/ranking/learning-to-rank-ltr">Learning-To-Rank (LTR)</a> und <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion">Reciprocal Rank Fusion (RRF)</a> – LTR eignet sich, um Suchergebnisse zu erfassen (Likes, Bewertungen, Klicks usw.) und diese zu nutzen, um Ergebnisse zu bewerten und zu verstärken oder zu verzerren. RRF eignet sich perfekt zum Zusammenführen von Ergebnissen, die von verschiedenen Abfragemodalitäten zurückgegeben werden (z. B. lexikalische und Vektordatenbankrecherchen) werden zu einer einzigen Ergebnisliste zusammengeführt. Elastic bietet außerdem die Flexibilität, die Ergebnisse mithilfe <a href="https://www.elastic.co/search-labs/blog/linear-retriever-hybrid-search">linearer Neubewertungsmethoden</a> anzupassen.</p><p>Eine der effektivsten Reranking-Techniken ist jedoch <a href="https://www.elastic.co/docs/solutions/search/ranking/semantic-reranking">das semantische Reranking</a>, bei dem das semantische Verständnis eines LLM genutzt wird, um die Vektoreinbettungen sowohl der Anfrage als auch der Ergebnisse gemeinsam zu analysieren und anschließend eine Relevanzbewertung/Rescoring anzuwenden, um die endgültige Reihenfolge zu bestimmen. Für das semantische Reranking ist natürlich eine Verbindung zu einem Reranking-Modell erforderlich. Elasticsearch bietet eine <a href="https://www.elastic.co/docs/api/doc/elasticsearch/group/endpoint-inference">Inference API</a> , mit der Sie <strong>Rerank-</strong> Endpunkte erstellen können, die integrierte Modelle (<a href="https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-rerank">Elastic Rerank</a>), <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/eland/machine-learning">importierte</a> Modelle von Drittanbietern oder extern gehostete Dienste wie <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put-cohere">Cohere</a> oder <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put-googlevertexai">Google Vertex AI nutzen</a>. Anschließend können Sie mithilfe der Abstraktionssyntax <a href="https://www.elastic.co/docs/solutions/search/retrievers-overview">der Retriever</a> -Abfrage ein Reranking durchführen:</p><pre><code>POST my-index/_search 
{
  "size": 10,
  "retriever": {
    "text_similarity_reranker": {
      "retriever": {
        "rrf": {
          "retrievers": [
            {
              "standard": {
                "query": {
                  "multi_match": {
                    "query": "machine learning applications",
                    "fields": ["title", "content"]
                  }
                }
              }
            },
            {
              "knn": {
                "field": "semantic-content-field",
                "k": 10,
                "num_candidates": 100,
                "query_vector_builder": {
                  "text_embedding": {
                    "model_id": "my-text-embedding-model",
                    "model_text": "machine learning applications"
                  }
                }
              }
            }
          ],
          "rank_window_size": 50,
          "rank_constant": 20
        }
      }
    },
    "field": "content",
    "inference_id": "my-reranker",
    "inference_text": "machine learning applications",
    "rank_window_size": 20
  }
}</code></pre><p><em>Ein Beispiel für eine mehrstufige Retriever-Neubewertungsoperation</em></p><p>Klingt super, oder? Wir können eine Neubewertung der Ergebnisse aus unterschiedlichen Quellen durchführen und so ein nahezu vollständiges semantisches Verständnis aller Inhaltsarten erreichen… Die semantische Neubewertung kann sowohl rechenintensiv als auch zeitaufwendig sein, weshalb sie nur bei einer begrenzten Anzahl von Ergebnissen praktikabel ist. Daher ist es wichtig, <em>wie</em> die ursprünglichen Ergebnisse abgerufen werden.</p><h3>Die Methode zur Kontextabfrage ist wichtig.</h3><p>Die subjektive Intention ist ein wichtiger Faktor bei der Bestimmung der Genauigkeit eines Ergebnisses und bei der Bewertung seiner Relevanz. Ohne die Möglichkeit, die Absicht des Benutzers bei der Durchführung der Abfrage zu berücksichtigen (ausgedrückt durch eine flexible Syntax oder durch eine Neubewertung in einer zweiten Stufe), können wir nur aus den bereits im Modellraum kodierten Kontexten auswählen. Um diesem Mangel an Kontext zu begegnen, setzen wir üblicherweise Techniken wie <a href="https://en.wikipedia.org/wiki/Retrieval-augmented_generation">Retrieval Augment Generation (RAG)</a> ein. Die Funktionsweise von RAG besteht darin, dass die Koordinaten der Abfrage effektiv verschoben werden, indem zusätzliche verwandte Begriffe aus einer Vorabfrage für kontextrelevante Daten einbezogen werden. Dadurch wird die Art und Weise, wie die Engine diesen zusätzlichen Kontext bereitstellt, und <em>ihre</em> anfängliche Methode zur Datenabfrage umso wichtiger für die Genauigkeit des Kontextes!</p><p>Lassen Sie uns die verschiedenen Methoden zur Kontextabfrage und deren Einfluss auf eine RAG-Operation betrachten:</p><ul><li><strong>Hybride Suchabrufe ohne Suchmaschine weisen immer noch einen Mangel an subjektiver Relevanz auf.</strong> Wenn die Plattform, die RAG bereitstellt, im Wesentlichen auf SQL basiert (was auf die meisten „Data Lake“-Plattformen zutrifft), fehlt ihr die Relevanzbewertung in der ersten Abrufphase. Viele Data-Lake-Plattformen bieten ihre eigene Version des hybriden Retrieval (nicht der Suche) an, wobei in der Regel Reranking-Techniken wie semantisches Reranking und RRF auf ihren SQL-basierten Retrieval- und Vektordatenbankergebnissen kombiniert werden. Eine einfache Sortierung reicht offensichtlich nicht für eine subjektive Rangfolge aus, aber selbst wenn sie als Grundlage für eine semantische Neubewertung in einem zweiten Schritt verwendet wird, wird SQL als erste Stufe der Abfrage problematisch, wenn die semantische Neubewertung nur auf den „Top k“ Treffern durchgeführt wird – ohne eine Möglichkeit, die Ergebnisse bei der Abfrage zu bewerten, welche Garantie haben wir, dass die <em>besten</em> Ergebnisse tatsächlich unter den Top-Ergebnissen enthalten sind?</li><li><strong>Vektorähnlichkeit allein reicht für RAG nicht aus</strong>. Das liegt eigentlich an einer Reihe von sich gegenseitig verstärkenden Problemen – es ist der Verlust beim Einbetten, zusammen mit naiven Chunking-Methoden, der Art und Weise, wie Ähnlichkeit berechnet wird, und der entscheidenden fehlenden Komponente der subjektiven Absicht. Eines der Hauptziele von RAG ist es, generative KI-Interaktionen auf objektiver Wahrheit zu gründen, um sowohl Halluzinationen zu verhindern als auch das LLM über private Informationen zu informieren, von denen es während des Trainings keine Kenntnis hatte. Wir können den durch RAG bereitgestellten zusätzlichen Kontext nutzen, um LLMs einzuschränken und anzuleiten, die Verbindungen und Details zu berücksichtigen, von denen wir wissen, dass sie für die Beantwortung der jeweiligen Frage am wichtigsten sind. Dazu müssen wir <em>sowohl</em> semantische als auch lexikalische Ansätze verwenden.</li><li><strong>Dateibasierte grep/regex RAG.</strong> Einige <a href="https://www.nicolasbustamante.com/p/the-rag-obituary-killed-by-agents">Kreise</a> im Universum der agentenbasierten KI plädieren für die Verwendung stark vergrößerter Kontextfenster, die über grep und reguläre Ausdrücke für RAG auf lokale Dateien zugreifen, anstatt externe Abrufplattformen zu nutzen. Die Idee dahinter ist, dass LLMs mit einem wesentlich größeren Kontextfenster in der Lage sein werden, konzeptionelle Verbindungen innerhalb ihres eigenen Denkraums herzustellen, anstatt sich auf fragmentierte Informationen und verschiedene Abrufmethoden/Plattformen zu verlassen, um relevante Informationen zu sammeln. Theoretisch ist es zwar richtig, dass ein ganzes Dokument ein umfassenderes Bild liefert als Dokumentsegmente, dies funktioniert jedoch nur in kleinen Datenbereichen (oder beispielsweise bei der Bereitstellung von Dateien für <a href="https://en.wikipedia.org/wiki/Vibe_coding">Vibecoding</a>), und selbst dann besteht die erste Abrufmethode in einem Scan aller Dokumente mit einer reinen Stichwortübereinstimmung.</li></ul><p><strong>Suche ist mehr als nur Abruf.</strong></p><p>Suchmaschinen sind speziell dafür entwickelt, Suchanfragen so schnell und flexibel wie möglich zu gestalten. Intern nutzen sie spezialisierte Datenstrukturen zum Speichern und Abrufen verschiedener Datentypen, die auf diese Datentypen zugeschnitten sind. Elasticsearch bietet optimiertes Speichern und Abfragen für praktisch alle Datentypen, einschließlich unstrukturierter/Volltext-Lexikalsuche (Match, Phrase, Proximity, Multi-Match), schneller Keyword-Suche (exakte Übereinstimmung) und Filterung, numerischer Bereiche, Datumsangaben, IP-Adressen und ist sehr flexibel in der Speicherung von Dokumentstrukturen (z. B. …). verschachtelte oder flache Dokumente). Elasticsearch ist außerdem eine native Vektordatenbank, die sowohl dünnbesetzte als auch dichte Vektortypen speichern und abfragen kann, und wir erforschen weiterhin innovative Wege (zum Beispiel <a href="https://www.elastic.co/search-labs/blog/better-binary-quantization-lucene-elasticsearch">Better Binary Quantization (BBQ)</a> &amp; <a href="https://www.elastic.co/search-labs/blog/diskbbq-elasticsearch-introduction">DiskBBQ</a>), um die Suchgenauigkeit zu erhalten und gleichzeitig die Geschwindigkeit, Skalierbarkeit und Kosten im Zusammenhang mit vektorisierten Inhalten zu verbessern. Die Elasticsearch-Plattform bietet zudem integrierte Datenstabilität und Hochverfügbarkeit und beinhaltet Funktionen für das Datenlebenszyklusmanagement wie <a href="https://www.elastic.co/docs/deploy-manage/tools/snapshot-and-restore/searchable-snapshots">Searchable Snapshots</a> , mit denen Sie selten genutzte oder langfristig aufzubewahrende Daten auf kostengünstigem Objektspeicher speichern können – und diese dennoch vollständig durchsuchbar sind.</p><h3>Die Hybridsuche vereint das Beste aus allen Welten.</h3><p><a href="https://www.elastic.co/what-is/hybrid-search">Hybride Suche</a> (nicht nur hybride Abfrage!) kombiniert die Stärken der traditionellen lexikalischen Suche mit dem semantischen Verständnis von LLMs und der Vektorähnlichkeitssuche. Diese Synergie ermöglicht es, bereits in der <em>Abrufphase</em> hochrelevante Ergebnisse durch die flexiblen Abfragesyntaxoptionen einer Suchmaschine zu erzielen: absichtsgesteuerte Syntaxoptionen und Relevanzbewertung, multimodaler Datenabruf, Filterung, Aggregation und Biasing. Mit Suchsyntax wie <a href="https://www.elastic.co/docs/reference/query-languages/esql">ES|QL</a> und mehrstufigen <a href="https://www.elastic.co/docs/solutions/search/retrievers-overview">Abrufern</a> können wir die traditionelle Suche flexibel mit semantischer Suche, Filtern und mehreren Reranking-Techniken in einer einzigen Anfrage kombinieren.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f25180cb430414b99ae553d3b8eb161dbccea4d4-1920x1080.png" style="width: 100%;" /><p>Einer der größten Vorteile der hybriden Suche ist, dass Ihre Abfragen eine spezialisierte Syntax für mehrere verschiedene Datentypen gleichzeitig verwenden können. Diese unterschiedlichen Abfragesyntaxen können nicht nur zum <em>Auffinden</em> von Ergebnissen verwendet werden, sondern auch als Filter oder Aggregationen <em>der</em> Ergebnisse. Ein Beispiel hierfür ist <a href="https://www.elastic.co/docs/explore-analyze/geospatial-analysis">die Geodatenanalyse</a>, eine der häufigsten Abfragearten, die oft mit anderen Syntaxelementen kombiniert wird. Sie können beispielsweise Abfragen durchführen, um Ergebnisse zu erhalten, deren Geokoordinaten sich innerhalb einer bestimmten Entfernung von einem Punkt befinden, oder um Aggregationen Ihrer Ergebnisse nach Region anzufordern, oder um Aggregationen anzufordern, um Bewegungen in/aus einer Zone zu verfolgen und Warnungen auszugeben. Mit der Hybridsuche haben Sie die Flexibilität, Syntaxen zu kombinieren, um Ergebnisse so präzise wie möglich zu liefern und die Inhalte abzurufen, die Ihrem Kontext am nächsten kommen.</p><h2>Pause</h2><p>Dieser erste Teil erzählt die Geschichte, wie die Vektorsuche die Art und Weise verändert hat, wie wir Daten abrufen können, und bereitet den Boden für die Veränderungen, die LLMs an den Abfragemechanismen mit sich gebracht haben, mit denen wir mit Daten interagieren. Wir werden so tun, als hätten wir das in mehrere Teile aufteilen müssen, damit LLMs es verstehen können, ohne den Kontext zu verlieren… ;-) Erfahren wir mehr darüber, <em>warum das wichtig ist,</em> in <a href="https://www.elastic.co/search-labs/blog/context-engineering-llm-evolution-agentic-ai">Teil II: Agentische KI und die Notwendigkeit des Kontext-Engineerings</a>, und in Teil III kehren wir zu unserer Diskussion über die hybride Suche zurück.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Konfiguration der rekursiven Segmentierung für strukturierte Dokumente in Elasticsearch]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie rekursives Chunking in Elasticsearch mit Chunk-Größe, Trenngruppen und benutzerdefinierten Trennlisten für eine optimale strukturierte Dokumentenindizierung konfigurieren.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/recursive-chunking-structured-documents-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/recursive-chunking-structured-documents-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Daniel Rubinstein]]></dc:creator>
            <pubDate>Tue, 11 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/591872e31880768ca927507654a621addc0d124d-1600x960.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Seit Version 8.16 können Benutzer die Chunking-Strategie konfigurieren, die beim Importieren langer Dokumente in semantische Textfelder verwendet wird. Ab Version 9.1 / 8.19 haben wir eine neue konfigurierbare rekursive Chunking-Strategie eingeführt, die eine Liste regulärer Ausdrücke verwendet, um das Dokument in Abschnitte zu unterteilen. Das Ziel des Chunking ist es, ein langes Dokument in Abschnitte zu unterteilen, die zusammengehörige Inhalte enthalten. Unsere bisherigen Strategien zerlegen Texte auf der Ebene einzelner Wörter/Sätze, aber Dokumente, die in strukturierten Formaten geschrieben sind (z. B. Markdown-Dateien enthalten oft zusammengehörige Inhalte innerhalb von Abschnitten, die durch Trennzeichen definiert sind (z. B. Überschriften). Für diese Art von Dokumenten führen wir die rekursive Chunking-Strategie ein, um das Format strukturierter Dokumente zu nutzen und bessere Chunks zu erstellen!</p><h2>Was ist rekursives Chunking?</h2><p>Bei der rekursiven Segmentierung wird eine Liste von vorgegebenen Abschnittstrennungsmustern durchlaufen, um ein Dokument schrittweise in kleinere Segmente zu unterteilen, bis eine gewünschte maximale Segmentgröße erreicht ist.</p><h3>Wie konfiguriere ich rekursives Chunking?</h3><p>Folgende Werte können vom Benutzer für die rekursive Segmentierung konfiguriert werden:</p><ul><li>(erforderlich) <code>max_chunk_size</code>: Die maximale Anzahl von Wörtern in einem Chunk.</li><li>Entweder eines von beiden:<ul><li><code>separators</code>Eine Liste von regulären Ausdrücken, die verwendet werden, um das Dokument in Abschnitte zu unterteilen.</li><li><code>separator_group</code>Eine Zeichenkette, die einer von Elastic definierten Standardliste von Trennzeichen zugeordnet wird, die für bestimmte Dokumenttypen verwendet werden. Aktuell sind <code>markdown</code> und <code>plaintext</code> verfügbar.</li></ul></li></ul><h3>Wie funktioniert rekursives Chunking?</h3><p>Der Prozess des rekursiven Chunkings bei gegebenem Eingabedokument, einem <code>max_chunk_size</code> (gemessen in Wörtern) und einer Liste von Trennzeichenketten verläuft wie folgt:</p><ol><li>Wenn das Eingabedokument bereits innerhalb der maximalen Chunk-Größe liegt, wird ein einzelner Chunk zurückgegeben, der die gesamte Eingabe umfasst.</li><li>Teile den Text anhand des Vorkommens des Trennzeichens in mögliche Abschnitte auf. Für jeden potenziellen Teil:<ol><li>Wenn der potenzielle Datenblock innerhalb der maximalen Datenblockgröße liegt, fügen Sie ihn der Liste der an den Benutzer zurückzugebenden Datenblöcke hinzu.</li><li>Andernfalls wiederholen Sie ab Schritt 2, wobei Sie nur den Text aus dem potenziellen Chunk verwenden und diesen anhand des nächsten Trennzeichens in der Liste aufteilen. Wenn keine weiteren Trennzeichen mehr übrig sind, sollte man auf satzbasierte Segmentierung zurückgreifen.</li></ol></li></ol><h2>Beispiele für die Konfiguration von rekursivem Chunking</h2><p>Abgesehen von der Chunk-Größe besteht die wichtigste Konfiguration für rekursives Chunking in der Auswahl der Trennzeichen, die zum Aufteilen der Dokumente verwendet werden sollen. Wenn Sie nicht sicher sind, wo Sie anfangen sollen, bietet Elasticsearch einige Standardtrennzeichengruppen an, die für gängige Anwendungsfälle verwendet werden können.</p><h3>Verwendung von Trenngruppen</h3><p>Um eine Trenngruppe zu verwenden, geben Sie einfach den Namen der Gruppe an, die Sie bei der Konfiguration der Chunking-Einstellungen verwenden möchten. Zum Beispiel:</p><pre><code>"chunking_settings": {
    "strategy": "recursive",
    "max_chunk_size": 25,
    "separator_group": "plaintext"
}</code></pre><p>Dies führt zu einer rekursiven Chunking-Strategie, die die Trennzeichenliste <code>[&quot;(?&lt;!\\n)\\n\\n(?!\\n)&quot;, &quot;(?&lt;!\\n)\\n(?!\\n)&quot;)]</code> verwendet. Dies funktioniert gut für allgemeine Klartextanwendungen, wobei der Text an zwei Zeilenumbruchzeichen, gefolgt von einem weiteren Zeilenumbruchzeichen, geteilt wird.</p><p>Wir bieten außerdem eine Trennzeichengruppe <code>markdown</code> an, die die Trennzeichenliste verwendet:</p><pre><code>[
"\n# ",
       "\n## ",
       "\n### ",
       "\n#### ",
       "\n##### ",
       "\n###### ",
       "\n^(?!\\s*$).*\\n-{1,}\\n",
       "\n^(?!\\s*$).*\\n={1,}\\n"
]</code></pre><p>Diese Trennzeichenliste eignet sich gut für allgemeine Markdown-Anwendungsfälle, da sie an jeder der 6 Überschriftenebenen und den Abschnittsumbruchzeichen aufteilt.</p><p>Beim Erstellen einer Ressource (Inferenzendpunkt/semantisches Textfeld) wird die Liste der Trennzeichen, die der Trennzeichengruppe zu diesem Zeitpunkt entsprechen, in Ihren Konfigurationen gespeichert. Wenn die Trenngruppe zu einem späteren Zeitpunkt aktualisiert wird, ändert sich dadurch das Verhalten Ihrer bereits erstellten Ressourcen nicht.</p><h3>Verwendung einer benutzerdefinierten Trennliste</h3><p>Falls eine der vordefinierten Trennzeichengruppen für Ihren Anwendungsfall nicht geeignet ist, können Sie eine benutzerdefinierte Liste von Trennzeichen definieren, die Ihren Anforderungen entspricht. Beachten Sie, dass reguläre Ausdrücke innerhalb der Trennzeichenliste angegeben werden können. Nachfolgend ein Beispiel für Chunking-Einstellungen mit benutzerdefinierten Trennzeichen:</p><pre><code>"chunking_settings": {
    "strategy": "recursive",
    "max_chunk_size": 25,
    "separators": ["\n\n", "\n", "<my-custom-separator>"]
}</code></pre><p>Die oben beschriebene Chunking-Strategie teilt an zwei Zeilenumbruchzeichen, gefolgt von einem Zeilenumbruchzeichen und schließlich an der Zeichenkette <code>“&lt;my-custom-separator&gt;”</code> auf.</p><h2>Ein Beispiel für rekursives Chunking in der Praxis</h2><p>Schauen wir uns ein Beispiel für rekursives Chunking in der Praxis an. In diesem Beispiel verwenden wir die folgenden Chunking-Einstellungen mit einer benutzerdefinierten Liste von Trennzeichen, die ein Markdown-Dokument anhand der beiden obersten Header-Ebenen aufteilen:</p><pre><code>"chunking_settings": {
    "strategy": "recursive",
    "max_chunk_size": 25,
    "separators": ["\n# ", "\n## "]
}</code></pre><p>Werfen wir einen Blick auf ein einfaches, unstrukturiertes Markdown-Dokument:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3a5507f4a1288065097231548e5b18e240508785-1302x1446.png" style="width: 100%;" /><p>Nun verwenden wir die oben definierten Chunking-Einstellungen, um das Dokument in Chunking-Elemente zu unterteilen:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a3313c4c40ff39b8dbcdd7c4878c723f088e6c1a-1600x1187.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/79a2921943191ea631df94c9d465818ec8d3e738-1600x1206.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/459e695cce7540267422396b9a62ff4ad35f61db-1600x1260.png" style="width: 100%;" /><p>Hinweis: Der Zeilenumbruch am Ende jedes Abschnitts (außer Abschnitt 3) ist nicht hervorgehoben, befindet sich aber innerhalb der eigentlichen Abschnittsgrenzen.</p><h3>Legen Sie noch heute mit rekursivem Chunking los!</h3><p>Weitere Informationen zur Nutzung dieser Funktion finden Sie in der Dokumentation zur <span class="unknown__pt__mark__88e241676926">Konfiguration der Chunking-Einstellungen</span>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Einführung der Elasticsearch-Abfrageregeln-Benutzeroberfläche in Kibana]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie mit der Elasticsearch Query Rules UI Dokumente mithilfe anpassbarer Regelsätze in Kibana zu Suchanfragen hinzufügen oder ausschließen können, ohne das organische Ranking zu beeinträchtigen.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/elasticsearch-query-rules-ui-introduction</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/elasticsearch-query-rules-ui-introduction</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jhon Guzmán]]></dc:creator>
            <pubDate>Fri, 07 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1fb10bd31c509cc9b9bb4f71f49970f140e6c36f-1600x945.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Die Aufgabe einer Suchmaschine besteht darin, relevante Ergebnisse zu liefern. Es gibt jedoch geschäftliche Anforderungen, die darüber hinausgehen – wie die Hervorhebung von Sonderangeboten, die Priorisierung saisonaler Produkte oder die Präsentation gesponserter Artikel – und Entwickler können dies nicht immer in der Suchanfrage umsetzen.</p><p>Darüber hinaus sind diese Anwendungsfälle in der Regel zeitkritisch, und das Durchlaufen der typischen Entwicklungsphasen (Erstellen eines Codezweigs und anschließendes Warten auf eine neue Version) ist ein zeitaufwändiger Prozess.</p><p>Was wäre, wenn wir diesen gesamten Prozess mit einem einzigen API-Aufruf oder, noch besser, mit nur wenigen Klicks in Kibana erledigen könnten?</p><h2>Benutzeroberfläche für Abfrageregeln</h2><p>Elasticsearch 8.10 führte <a href="https://www.elastic.co/blog/introducing-query-rules-elasticsearch-8-10"><strong>Abfrageregeln</strong></a> und <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers/rule-retriever"><strong>Regelabrufer</strong></a> ein. Hierbei handelt es sich um Tools, die entwickelt wurden, um <a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-pinned-query"><em>festgelegte Ergebnisse</em></a> in die Suchanfragen einzufügen, ohne die Rangfolge der organischen Ergebnisse gemäß den Regeln zu beeinträchtigen. Sie fügen den Ergebnissen lediglich auf deklarative und einfache Weise Geschäftslogik hinzu.</p><p>Einige gängige Anwendungsfälle für Abfrageregeln sind:</p><ul><li><strong>Hervorhebung von beworbenen Angeboten oder Verkäufen</strong>: Anzeige von reduzierten oder gesponserten Artikeln ganz oben.</li><li><strong>Ausschluss nach Kontext oder Standort</strong>: Ausblenden bestimmter Elemente, wenn deren Anzeige aufgrund lokaler Vorschriften nicht zulässig ist.</li><li><strong>Priorisierung wichtiger Ergebnisse</strong>: Sicherstellen, dass beliebte oder feste Suchanfragen unabhängig vom organischen Ranking immer ganz oben stehen.</li></ul><p>Um auf die Benutzeroberfläche zuzugreifen und mit diesen Tools zu interagieren, müssen Sie im Kibana-Seitenmenü auf <strong>„Abfrageregeln“</strong> unter <strong>„Relevanz“ klicken:</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/242e33e89d1a07ffa0e76009c46b3a9236722741-458x1010.png" style="width: 100%;" /><p>Sobald das Menü mit den Abfrageregeln erscheint, klicken Sie auf <strong>„Erstes Regelset erstellen“:</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/30b3a91bbbf243d314cf38298e01ca5cff784430-1600x945.png" style="width: 100%;" /><p>Als Nächstes müssen Sie Ihrem Regelsatz einen Namen geben.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/26c5462f88678867776f933b5655ca0df0d72a16-708x446.png" style="width: 100%;" /><p>Das Formular zur Definition jeder Regel besteht aus drei Hauptkomponenten:</p><ul><li><strong>Kriterien</strong>: Die Bedingungen, die erfüllt sein müssen, damit die Regel Anwendung findet. Zum Beispiel: „wenn das Feld query_string den Wert <em>Christmas</em> enthält“ oder „wenn das Feld country <em>CO ist“.</em></li><li><strong>Aktion</strong>: Das soll passieren, wenn die Bedingungen erfüllt sind. Es kann angeheftet (ein Dokument wird an der Spitze der Suchergebnisse fixiert) oder ausgeschlossen (ein Dokument wird ausgeblendet) werden.</li><li><strong>Metadaten</strong>: Dies sind die Felder, die bei der Ausführung der Abfrage mitgeführt werden. Sie können sowohl Benutzerinformationen (wie Standort oder Sprache) als auch Suchdaten (Query-String) enthalten. Dies sind die Werte, anhand derer die Kriterien entscheiden, ob eine Regel angewendet werden soll oder nicht.</li></ul><h2>Beispiel: beliebte Artikel</h2><p>Stellen wir uns vor, wir hätten eine E-Commerce-Website mit verschiedenen Artikeln. Bei der Überprüfung der Kennzahlen stellen wir fest, dass einer der meistverkauften Artikel in der Kategorie Konsolen der „DualShock 4 Wireless Controller“ ist, insbesondere wenn Nutzer nach den Schlüsselwörtern „PS4“ oder „PlayStation 4“ suchen. Deshalb haben wir beschlossen, dieses Produkt ganz oben in den Suchergebnissen anzuzeigen, wenn ein Nutzer nach diesen Schlüsselwörtern sucht.</p><p>Als Erstes indizieren wir die Dokumente für jeden Artikel mithilfe einer Bulk-API-Anfrage:</p><pre><code>POST _bulk
{ "index": { "_index": "products", "_id": "1" } }
{ "id": "1", "name": "PlayStation 4 Slim 1TB", "category": "console", "brand": "Sony", "price": 1200 }
{ "index": { "_index": "products", "_id": "2" } }
{ "id": "2", "name": "DualShock 4 Wireless Controller", "category": "accessory", "brand": "Sony", "price": 250 }
{ "index": { "_index": "products", "_id": "3" } }
{ "id": "3", "name": "PlayStation 4 Camera", "category": "accessory", "brand": "Sony", "price": 200 }
{ "index": { "_index": "products", "_id": "4" } }
{ "id": "4", "name": "PlayStation 4 VR Headset", "category": "accessory", "brand": "Sony", "price": 900 }
{ "index": { "_index": "products", "_id": "5" } }
{ "id": "5", "name": "Charging Station for DualShock 4", "category": "accessory", "brand": "Sony", "price": 80 }</code></pre><p>Wenn wir nicht in die Abfrage eingreifen, erscheint der Eintrag normalerweise an vierter Stelle. Hier die Anfrage:</p><pre><code>GET products/_search
{
 "query": {
   "match": {
     "name": "PlayStation 4"
   }
 }
}</code></pre><p>Und hier sind die Ergebnisse.</p><pre><code>{
 "took": 1,
 "timed_out": false,
 "_shards": {
   "total": 1,
   "successful": 1,
   "skipped": 0,
   "failed": 0
 },
 "hits": {
   "total": {
     "value": 5,
     "relation": "eq"
   },
   "max_score": 0.6973252,
   "hits": [
     {
       "_index": "products",
       "_id": "3",
       "_score": 0.6973252,
       "_source": {
         "id": "3",
         "name": "PlayStation 4 Camera",
         "category": "accessory",
         "brand": "Sony",
         "price": 200
       }
     },
     {
       "_index": "products",
       "_id": "1",
       "_score": 0.6260078,
       "_source": {
         "id": "1",
         "name": "PlayStation 4 Slim 1TB",
         "category": "console",
         "brand": "Sony",
         "price": 1200
       }
     },
     {
       "_index": "products",
       "_id": "4",
       "_score": 0.6260078,
       "_source": {
         "id": "4",
         "name": "PlayStation 4 VR Headset",
         "category": "accessory",
         "brand": "Sony",
         "price": 900
       }
     },
     {
       "_index": "products",
       "_id": "2",
       "_score": 0.08701137,
       "_source": {
         "id": "2",
         "name": "DualShock 4 Wireless Controller",
         "category": "accessory",
         "brand": "Sony",
         "price": 250
       }
     },
     {
       "_index": "products",
       "_id": "5",
       "_score": 0.07893815,
       "_source": {
         "id": "5",
         "name": "Charging Station for DualShock 4",
         "category": "accessory",
         "brand": "Sony",
         "price": 80
       }
     }
   ]
 }
}</code></pre><p>Lasst uns eine Abfrageregel erstellen, um dies zu ändern. Als Erstes fügen wir es dem Regelwerk folgendermaßen hinzu:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/fdc42646fb3e76a09bca7d19047a76efe343f7a2-1600x650.png" style="width: 100%;" /><p>Oder eine entsprechende <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-query-rules-put-ruleset">API-Anfrage</a>:</p><pre><code>PUT _query_rules/my-rules
{
  "rules": [
    {
      "rule_id": "rule-1232",
      "type": "pinned",
      "criteria": [
        {
          "type": "exact",
          "metadata": "query_string",
          "values": [
            "PS4",
            "PlayStation 4"
          ]
        }
      ],
      "actions": {
        "docs": [
          {
            "_index": "products",
            "_id": "2"
          }
        ]
      }
    }
  ]
}</code></pre><p>Um das <strong>Regelset </strong>in unserer Abfrage verwenden zu können, müssen wir einen Abfrageregeltyp verwenden. Diese Art von Anfrage besteht aus zwei Hauptteilen:</p><pre><code>GET /products/_search
{
 "retriever": {
   "rule": {
     "retriever": {
       "standard": {
         "query": {
           "match": { "name": "PlayStation 4" }
         }
       }
     },
     "match_criteria": {
       "query_string": "PlayStation 4"
     },
     "ruleset_ids": ["my-rules"]
   }
 }
}</code></pre><ul><li><strong>match_criteria</strong>: Dies sind die Metadaten, die zum Vergleich mit der Benutzeranfrage verwendet werden. In diesem Beispiel wird das Regelset aktiviert, wenn das Feld query_string den Wert „PlayStation 4“ hat.</li><li><strong>Suchanfrage</strong>: Die eigentliche Suchanfrage, die verwendet wird, um die organischen Suchergebnisse zu finden.</li></ul><p>Auf diese Weise führen Sie zuerst die organische Abfrage aus, und anschließend wendet Elasticsearch die Regeln aus Ihrem Regelsatz an:</p><pre><code>{
 "took": 17,
 "timed_out": false,
 "_shards": {
   "total": 1,
   "successful": 1,
   "skipped": 0,
   "failed": 0
 },
 "hits": {
   "total": {
     "value": 5,
     "relation": "eq"
   },
   "max_score": 1.7014122e+38,
   "hits": [
     {
       "_index": "products",
       "_id": "2",
       "_score": 1.7014122e+38,
       "_source": {
         "id": "2",
         "name": "DualShock 4 Wireless Controller",
         "category": "accessory",
         "brand": "Sony",
         "price": 250
       }
     },
     {
       "_index": "products",
       "_id": "3",
       "_score": 0.6973252,
       "_source": {
         "id": "3",
         "name": "PlayStation 4 Camera",
         "category": "accessory",
         "brand": "Sony",
         "price": 200
       }
     },
     {
       "_index": "products",
       "_id": "1",
       "_score": 0.6260078,
       "_source": {
         "id": "1",
         "name": "PlayStation 4 Slim 1TB",
         "category": "console",
         "brand": "Sony",
         "price": 1200
       }
     },
     {
       "_index": "products",
       "_id": "4",
       "_score": 0.6260078,
       "_source": {
         "id": "4",
         "name": "PlayStation 4 VR Headset",
         "category": "accessory",
         "brand": "Sony",
         "price": 900
       }
     },
     {
       "_index": "products",
       "_id": "5",
       "_score": 0.07893815,
       "_source": {
         "id": "5",
         "name": "Charging Station for DualShock 4",
         "category": "accessory",
         "brand": "Sony",
         "price": 80
       }
     }
   ]
 }
}</code></pre><h2>Beispiel: benutzerbasierte Metadaten</h2><p>Eine weitere interessante Anwendung von Abfrageregeln besteht darin, Metadaten zu verwenden, um bestimmte Dokumente basierend auf Kontextinformationen des Benutzers oder der Webseite anzuzeigen.</p><p>Nehmen wir beispielsweise an, wir möchten Artikel oder personalisierte Angebote basierend auf dem Loyalitätsgrad eines Nutzers hervorheben, der als numerischer Wert dargestellt wird.</p><p>Dies erreichen wir, indem wir diese Metadaten direkt in die Abfrage einbinden, sodass die Regeln aktiviert werden, wenn der genannte Wert bestimmte Kriterien erfüllt.</p><p>Zunächst indexieren wir ein Dokument, das nur Nutzer mit einer hohen Loyalitätsstufe sehen können:</p><pre><code>POST _bulk
{ "index": { "_index": "products", "_id": "6" } }
{ "id": "6", "name": "PlayStation Plus Deluxe Card - 12 months", "category": "membership", "brand": "Sony", "price": 300 }</code></pre><p>Jetzt erstellen wir eine neue Regel innerhalb desselben Regelsatzes, sodass der Artikel ganz oben in den Ergebnissen erscheint, wenn der Loyalitätswert (loyality_level) gleich oder höher als 80 ist.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/58de12dff93305440608f51465462fcc68653a08-1421x496.png" style="width: 100%;" /><p>Speichern Sie die Regel und das Regelwerk.</p><p>Hier ist die entsprechende REST-Anfrage:</p><pre><code>PUT _query_rules/my-rules
{
  "rules": [
    {
      "rule_id": "pin-premiun-user",
      "type": "pinned",
      "criteria": [
        {
          "type": "gte",
          "metadata": "loyalty_level",
          "values": [
            80
          ]
        }
      ],
      "actions": {
        "docs": [
          {
            "_index": "products",
            "_id": "6"
          }
        ]
      }
    }
  ]
}</code></pre><p>Beim Ausführen einer Abfrage muss nun der neue Parameter <strong>loyalty_level </strong>in die Metadaten aufgenommen werden. Wenn die Bedingung in der Regel erfüllt ist, erscheint das neue Dokument ganz oben in den Suchergebnissen.</p><p>Beispiel: Senden einer Anfrage, bei der der Loyalitätsgrad 80 beträgt:</p><pre><code>POST /products/_search
{
  "retriever": {
    "rule": {
      "retriever": {
        "standard": {
          "query": {
            "match": {
              "name": "PlayStation"
            }
          }
        }
      },
      "match_criteria": {
        "query_string": "PlayStation",
        "loyalty_level": 80
      },
      "ruleset_ids": ["my-rules"]
    }
  }
}</code></pre><p>Das Treuezertifikat wird oben auf den Ergebnissen angezeigt:</p><pre><code>{
  "took": 31,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 4,
      "relation": "eq"
    },
    "max_score": 1.7014122e+38,
    "hits": [
      {
        "_index": "products",
        "_id": "6",
        "_score": 1.7014122e+38,
        "_source": {
          "id": "6",
          "name": "PlayStation Plus Deluxe Card - 12 months",
          "category": "membership",
          "brand": "Sony",
          "price": 300
        }
      },
      {
        "_index": "products",
        "_id": "3",
        "_score": 0.5054567,
        "_source": {
          "id": "3",
          "name": "PlayStation 4 Camera",
          "category": "accessory",
          "brand": "Sony",
          "price": 200
        }
      },
      {
        "_index": "products",
        "_id": "1",
        "_score": 0.45618832,
        "_source": {
          "id": "1",
          "name": "PlayStation 4 Slim 1TB",
          "category": "console",
          "brand": "Sony",
          "price": 1200
        }
      },
      {
        "_index": "products",
        "_id": "4",
        "_score": 0.45618832,
        "_source": {
          "id": "4",
          "name": "PlayStation 4 VR Headset",
          "category": "accessory",
          "brand": "Sony",
          "price": 900
        }
      }
    ]
  }
}</code></pre><p>Im folgenden Fall ist die Regel aufgrund des Loyalitätslevels von 70 nicht erfüllt, und der Artikel sollte nicht oben erscheinen:</p><pre><code>POST /products/_search
{
  "retriever": {
    "rule": {
      "retriever": {
        "standard": {
          "query": {
            "match": {
              "name": "PlayStation"
            }
          }
        }
      },
      "match_criteria": {
        "query_string": "PlayStation",
        "loyalty_level": 70
      },
      "ruleset_ids": ["my-rules"]
    }
  }
}</code></pre><p>Hier die Ergebnisse:</p><pre><code>{
  "took": 7,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 4,
      "relation": "eq"
    },
    "max_score": 0.5054567,
    "hits": [
      {
        "_index": "products",
        "_id": "3",
        "_score": 0.5054567,
        "_source": {
          "id": "3",
          "name": "PlayStation 4 Camera",
          "category": "accessory",
          "brand": "Sony",
          "price": 200
        }
      },
      {
        "_index": "products",
        "_id": "1",
        "_score": 0.45618832,
        "_source": {
          "id": "1",
          "name": "PlayStation 4 Slim 1TB",
          "category": "console",
          "brand": "Sony",
          "price": 1200
        }
      },
      {
        "_index": "products",
        "_id": "4",
        "_score": 0.45618832,
        "_source": {
          "id": "4",
          "name": "PlayStation 4 VR Headset",
          "category": "accessory",
          "brand": "Sony",
          "price": 900
        }
      },
      {
        "_index": "products",
        "_id": "6",
        "_score": 0.3817649,
        "_source": {
          "id": "6",
          "name": "PlayStation Plus Deluxe Card - 12 months",
          "category": "membership",
          "brand": "Sony",
          "price": 300
        }
      }
    ]
  }
}</code></pre><h2>Beispiel: sofortiger Ausschluss</h2><p>Nehmen wir an, unser <strong>DualShock 4 Wireless-Controller (ID 2)</strong> ist vorübergehend nicht verfügbar und kann nicht verkauft werden. Anstatt das Dokument manuell zu löschen oder auf die Verarbeitung der Daten zu warten, beschließt das Business-Team, es in der Zwischenzeit aus den Suchergebnissen zu entfernen.</p><p>Wir werden ein ähnliches Verfahren anwenden wie das, das wir gerade bei den beliebten Artikeln angewendet haben, aber diesmal wählen wir anstelle von <em>&quot;Angeheftet&quot;</em> <em>die Option &quot;Ausschließen&quot;</em>. Diese Regel funktioniert wie eine Art Blacklist. Ändern Sie das Kriterium auf <strong>„Immer“</strong> , damit der Ausschluss bei jeder Ausführung der Abfrage funktioniert.</p><p>Die Regel sollte folgendermaßen aussehen:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f10971e4f1bc9520105111adfa3a476581a27130-1600x623.png" style="width: 100%;" /><p>Speichern Sie die Regel und das Regelset, um die Änderungen anzuwenden. Hier ist die entsprechende REST-Anfrage:</p><pre><code>PUT _query_rules/my-rules
{
  "rules": [
    {
      "rule_id": "rule-6358",
      "type": "pinned",
      "criteria": [
        {
          "type": "always"
        }
      ],
      "actions": {
        "docs": [
          {
            "_index": "products",
            "_id": "2"
          }
        ]
      }
    }
  ]
}</code></pre><p>Wenn wir die Abfrage nun erneut ausführen, werden Sie feststellen, dass das Element nicht mehr in den Ergebnissen enthalten ist, obwohl die vorherige Regel vorsah, es zu fixieren. Dies liegt daran, dass <strong>Ausschlüsse Vorrang vor dem Anheften von Ergebnissen haben</strong>.</p><pre><code>{
 "took": 6,
 "timed_out": false,
 "_shards": {
   "total": 1,
   "successful": 1,
   "skipped": 0,
   "failed": 0
 },
 "hits": {
   "total": {
     "value": 4,
     "relation": "eq"
   },
   "max_score": 2.205655,
   "hits": [
     {
       "_index": "products",
       "_id": "3",
       "_score": 2.205655,
       "_source": {
         "id": "3",
         "name": "PlayStation 4 Camera",
         "category": "accessory",
         "brand": "Sony",
         "price": 200
       }
     },
     {
       "_index": "products",
       "_id": "1",
       "_score": 1.9738505,
       "_source": {
         "id": "1",
         "name": "PlayStation 4 Slim 1TB",
         "category": "console",
         "brand": "Sony",
         "price": 1200
       }
     },
     {
       "_index": "products",
       "_id": "4",
       "_score": 1.9738505,
       "_source": {
         "id": "4",
         "name": "PlayStation 4 VR Headset",
         "category": "accessory",
         "brand": "Sony",
         "price": 900
       }
     },
     {
       "_index": "products",
       "_id": "5",
       "_score": 0.69247496,
       "_source": {
         "id": "5",
         "name": "Charging Station for DualShock 4",
         "category": "accessory",
         "brand": "Sony",
         "price": 80
       }
     }
   ]
 }
}</code></pre><h2>Fazit</h2><p><strong>Mit Abfrageregeln</strong> lässt sich die Relevanz ganz einfach und ohne Codeänderungen anpassen. Die neue <strong>Kibana</strong> <strong>-Benutzeroberfläche </strong>ermöglicht Ihnen<strong> </strong>Diese Änderungen lassen sich in Sekundenschnelle vornehmen, wodurch Sie und Ihr Team mehr Kontrolle über Ihre Suchergebnisse erhalten.</p><p>Über den E-Commerce hinaus können Abfrageregeln in vielen anderen Szenarien eingesetzt werden: Hervorhebung von Anleitungen zur Fehlerbehebung in Supportportalen, Auffinden wichtiger interner Dokumente in Wissensdatenbanken, Förderung aktueller Meldungen auf Nachrichtenseiten oder Herausfiltern abgelaufener Stellen- oder Inhaltsangebote. Sie können sogar Compliance-Regeln durchsetzen, wie beispielsweise das Ausblenden von eingeschränktem Material nach Benutzerrolle oder Region.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Erstellung eines Wissensagenten mit semantischem Recall unter Verwendung von Mastra und Elasticsearch]]></title>
            <description><![CDATA[Lernen Sie, wie Sie einen Wissensagenten mit semantischer Erinnerung unter Verwendung von Mastra und Elasticsearch als Vektorspeicher für Gedächtnis und Informationsabruf erstellen.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/knowledge-agent-semantic-recall-mastra-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/knowledge-agent-semantic-recall-mastra-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[JD Armada]]></dc:creator>
            <pubDate>Thu, 06 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b8d51c2726d5573385c9246a7821d12ade4f1b0e-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p><a href="https://www.elastic.co/search-labs/blog/context-engineering-overview">Kontextentwicklung</a> gewinnt zunehmend an Bedeutung beim Aufbau zuverlässiger KI-Agenten und -Architekturen. Je besser die Modelle werden, desto weniger hängen ihre Effektivität und Zuverlässigkeit von den Trainingsdaten ab, sondern vielmehr davon, wie gut sie im richtigen Kontext verankert sind. Agenten, die die relevantesten Informationen zum richtigen Zeitpunkt abrufen und anwenden können, liefern mit viel höherer Wahrscheinlichkeit genaue und verlässliche Ergebnisse.</p><p>In diesem Blogbeitrag verwenden wir <a href="https://mastra.ai/">Mastra</a> , um einen Wissensagenten zu entwickeln, der sich merkt, was Benutzer sagen, und relevante Informationen später abrufen kann. Als Speicher- und Abruf-Backend nutzen wir Elasticsearch. Dieses Konzept lässt sich problemlos auf reale Anwendungsfälle übertragen. Man denke beispielsweise an Supportmitarbeiter, die sich an frühere Gespräche und Lösungen erinnern können, sodass sie ihre Antworten auf bestimmte Benutzer zuschneiden oder Lösungen schneller auf Basis des vorherigen Kontextes präsentieren können.</p><p>Folgen Sie dieser Anleitung, um zu sehen, wie Sie es Schritt für Schritt bauen können. Falls Sie nicht weiterkommen oder einfach nur ein fertiges Beispiel ausführen möchten, schauen Sie sich das Repository <a href="https://github.com/jdarmada/getting-started-mastra-elastic/tree/main">hier</a> an.</p><h2>Was ist Mastra?</h2><p>Mastra ist ein Open-Source-TypeScript-Framework zum Erstellen von KI-Agenten mit austauschbaren Teilen für Schlussfolgerungen, Speicher und Werkzeuge. Die <a href="https://mastra.ai/docs/memory/semantic-recall">semantische Abruffunktion</a> ermöglicht es Agenten, vergangene Interaktionen zu erinnern und abzurufen, indem Nachrichten als Einbettungen in einer Vektordatenbank gespeichert werden. Dies ermöglicht es den Agenten, den Gesprächskontext und die Kontinuität langfristig aufrechtzuerhalten. Elasticsearch ist ein hervorragender Vektorspeicher, um diese Funktion zu ermöglichen, da er eine effiziente dichte Vektorsuche unterstützt. Wenn der semantische Abruf ausgelöst wird, ruft der Agent relevante vergangene Nachrichten in das Kontextfenster des Modells ab, sodass das Modell diesen abgerufenen Kontext als Grundlage für seine Schlussfolgerungen und Antworten nutzen kann.</p><h2>Was Sie für den Einstieg benötigen</h2><ul><li>Node v18+</li><li>Elasticsearch (Version 8.15 oder neuer)</li><li>Elasticsearch API-Schlüssel</li><li><a href="https://help.openai.com/en/articles/4936850-where-do-i-find-my-openai-api-key">OpenAI API-Schlüssel</a></li></ul><p>Hinweis: Sie benötigen dies, da die Demo den OpenAI-Provider verwendet. Mastra unterstützt jedoch auch andere KI-SDKs und Community-Modell-Provider, sodass Sie ihn je nach Ihrer Konfiguration problemlos austauschen können.</p><h2>Aufbau eines Mastra-Projekts</h2><p>Wir werden die integrierte CLI von Mastra verwenden, um das Grundgerüst für unser Projekt bereitzustellen. Führen Sie folgenden Befehl aus:</p><pre><code>npm create mastra@latest</code></pre><p>Sie erhalten eine Reihe von Eingabeaufforderungen, beginnend mit:</p><p>1. Gib deinem Projekt einen Namen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2b9fe559e0276140dd539e24f916a73c60870405-620x84.png" style="width: 100%;" /><p>2. Wir können diese Standardeinstellung beibehalten; Sie können dieses Feld gerne leer lassen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e04729eb03bce8499e973e18c28642402340d0e5-852x68.png" style="width: 100%;" /><p>3. Für dieses Projekt verwenden wir ein von OpenAI bereitgestelltes Modell.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a86596a469a71bdf8bd99cbaf528d0f0cf7272c0-436x222.png" style="width: 100%;" /><p>4. Wählen Sie die Option „Jetzt überspringen“, da wir alle unsere Umgebungsvariablen in einer `.env`-Datei speichern, die wir in einem späteren Schritt konfigurieren werden.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/02b19ccc34af0bdacf52fd94b519d036540ca2e6-426x114.png" style="width: 100%;" /><p>5. Diese Option können wir auch überspringen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b3fe63d19d270bc2e0de1dd92033bf8b26750819-990x208.png" style="width: 100%;" /><p>Sobald dieser Initialisierungsprozess abgeschlossen ist, können wir zum nächsten Schritt übergehen.</p><h3>Abhängigkeiten installieren</h3><p>Als Nächstes müssen wir einige Abhängigkeiten installieren:</p><pre><code>npm install ai @ai-sdk/openai @elastic/elasticsearch dotenv</code></pre><ul><li><code>ai</code> - Core AI SDK-Paket, das Werkzeuge zur Verwaltung von KI-Modellen, Eingabeaufforderungen und Arbeitsabläufen in JavaScript/TypeScript bereitstellt. Mastra basiert auf dem <a href="https://ai-sdk.dev/">AI SDK</a> von Vercel, daher benötigen wir diese Abhängigkeit, um Modellinteraktionen mit Ihrem Agenten zu ermöglichen.</li><li><code>@ai-sdk/openai</code> - Plugin, das das AI SDK mit OpenAI-Modellen (wie GPT-4, GPT-4o usw.) verbindet und API-Aufrufe mit Ihrem OpenAI-API-Schlüssel ermöglicht.</li><li><code>@elastic/elasticsearch</code> - <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/javascript">Offizieller Elasticsearch-Client für Node.js</a>, Wird verwendet, um eine Verbindung zu Ihrer Elastic Cloud oder Ihrem lokalen Cluster für Indizierungs-, Such- und Vektoroperationen herzustellen.</li><li><code>dotenv</code> Lädt Umgebungsvariablen aus einer .env-Datei Datei in process.env, ermöglicht das sichere Einfügen von Anmeldeinformationen wie API-Schlüsseln und Elasticsearch-Endpunkten.</li></ul><h3>Konfiguration von Umgebungsvariablen</h3><p>Erstellen Sie eine <code>.env</code> -Datei in Ihrem Projektstammverzeichnis, falls dort noch keine vorhanden ist. Alternativ können Sie das von mir im <a href="https://github.com/jdarmada/getting-started-mastra-elastic/blob/main/.env.example">Repository</a> bereitgestellte Beispiel <code>.env</code> kopieren und umbenennen. In dieser Datei können wir die folgenden Variablen hinzufügen:</p><pre><code>ELASTICSEARCH_ENDPOINT="your-endpoint-here"
ELASTICSEARCH_API_KEY="your-key-here"
OPENAI_API_KEY="your-key-here"</code></pre><p>Damit ist die grundlegende Einrichtung abgeschlossen. Von hier aus können Sie bereits mit dem Erstellen und Orchestrieren von Agenten beginnen. Wir gehen noch einen Schritt weiter und fügen Elasticsearch als Speicher- und Vektorsuchschicht hinzu.</p><h2>Elasticsearch als Vektorspeicher hinzufügen</h2><p>Erstellen Sie einen neuen Ordner namens <code>stores</code> und fügen Sie darin diese <a href="https://github.com/jdarmada/getting-started-mastra-elastic/blob/main/src/mastra/stores/elastic-store.ts">Datei</a> ein. Bevor Mastra und Elastic eine offizielle Elasticsearch-Vektorspeicherintegration veröffentlichten, teilte <a href="https://github.com/abhiaiyer91">Abhi Aiyer</a>(CTO von Mastra) diese frühe Prototypklasse mit dem Namen <code>ElasticVector</code>. Vereinfacht gesagt verbindet es die Speicherabstraktion von Mastra mit den dichten Vektorfunktionen von Elasticsearch, sodass Entwickler Elasticsearch als Vektordatenbank für ihre Agenten verwenden können.</p><p>Werfen wir einen genaueren Blick auf die wichtigen Aspekte der Integration:</p><h3>Aufnahme des Elasticsearch-Clients</h3><p>Dieser Abschnitt definiert die Klasse <code>ElasticVector</code> und richtet die Elasticsearch-Clientverbindung mit Unterstützung für Standard- und serverlose Bereitstellungen ein.</p><pre><code>export interface ElasticVectorConfig extends ClientOptions {
    /**
     * Explicitly specify if connecting to Elasticsearch Serverless.
     * If not provided, will be auto-detected on first use.
     */
    isServerless?: boolean;
    
    /**
     * Maximum documents to count accurately when describing indices.
     * Higher values provide accurate counts but may impact performance on large indices.
     * 
     * @default 10000
     */
    maxCountAccuracy?: number;
}

export class ElasticVector extends MastraVector {
    private client: Client;
    private isServerless: boolean | undefined;
    private deploymentChecked: boolean = false;
    private readonly maxCountAccuracy: number;

    constructor(config: ElasticVectorConfig) {
        super();
        this.client = new Client(config);
        this.isServerless = config.isServerless;
        this.maxCountAccuracy = config.maxCountAccuracy ?? 10000;
    }
}</code></pre><ul><li><code>ElasticVectorConfig extends ClientOptions</code>Dadurch wird eine neue Konfigurationsschnittstelle erstellt, die alle Elasticsearch-Clientoptionen (wie <code>node</code>, <code>auth</code>, <code>requestTimeout</code>) erbt und unsere benutzerdefinierten Eigenschaften hinzufügt. Das bedeutet, dass Benutzer jede gültige Elasticsearch-Konfiguration zusammen mit unseren serverlosen Optionen übergeben können.</li><li><code>extends MastraVector</code>Dies ermöglicht es <code>ElasticVector</code> von Mastras Basisklasse <code>MastraVector</code> zu erben, die eine gemeinsame Schnittstelle darstellt, der alle Vektorspeicherintegrationen entsprechen. Dadurch wird sichergestellt, dass sich Elasticsearch aus Sicht des Agenten wie jedes andere Mastra-Vektor-Backend verhält.</li><li><code>private client: Client</code>Dies ist eine private Eigenschaft, die eine Instanz des Elasticsearch JavaScript-Clients enthält. Dadurch kann die Klasse direkt mit Ihrem Cluster kommunizieren.</li><li><code>isServerless</code> und <code>deploymentChecked</code>: Diese Eigenschaften arbeiten zusammen, um zu erkennen und zwischenzuspeichern, ob wir mit einer serverlosen oder einer Standard-Elasticsearch-Bereitstellung verbunden sind. Diese Erkennung erfolgt automatisch bei der ersten Nutzung oder kann explizit konfiguriert werden.</li><li><code>constructor(config: ClientOptions)</code>Dieser Konstruktor nimmt ein Konfigurationsobjekt entgegen (das Ihre Elasticsearch-Zugangsdaten und optionale Serverless-Einstellungen enthält) und verwendet es, um den Client in der Zeile <code>this.client = new Client(config)</code> zu initialisieren.</li><li><code>super()</code>: Dadurch wird der Basiskonstruktor von Mastra aufgerufen, sodass Logging, Validierungshilfsmechanismen und andere interne Hooks geerbt werden.</li></ul><p>Zu diesem Zeitpunkt weiß Mastra, dass es einen neuen Vektor-Shop namens gibt. <code>ElasticVector</code></p><h3>Erkennung des Bereitstellungstyps</h3><p>Vor dem Erstellen von Indizes erkennt der Adapter automatisch, ob Sie Elasticsearch Standard oder Elasticsearch Serverless verwenden. Dies ist wichtig, da serverlose Bereitstellungen keine manuelle Shard-Konfiguration zulassen.</p><pre><code>private async detectServerless(): Promise<boolean> {
    // Return cached result if already detected
    if (this.deploymentChecked) {
        return this.isServerless ?? false;
    }

    // Use explicit configuration if provided
    if (this.isServerless !== undefined) {
        this.deploymentChecked = true;
        this.logger?.info(
            `Using explicit deployment type: ${this.isServerless ? 'Serverless' : 'Standard'}`
        );
        return this.isServerless;
    }

    try {
        const info = await this.client.info();
        
        // Primary detection: build flavor (most reliable)
        const isBuildFlavorServerless = info.version?.build_flavor === 'serverless';
        
        // Secondary detection: tagline (fallback)
        const isTaglineServerless = info.tagline?.toLowerCase().includes('serverless') ?? false;
        
        this.isServerless = isBuildFlavorServerless || isTaglineServerless;
        this.deploymentChecked = true;
        
        this.logger?.info(
            `Auto-detected ${this.isServerless ? 'Serverless' : 'Standard'} Elasticsearch deployment`,
            { 
                buildFlavor: info.version?.build_flavor, 
                version: info.version?.number,
                detectionMethod: isBuildFlavorServerless ? 'build_flavor' : 'tagline'
            }
        );
        
        return this.isServerless;
    } catch (error) {
        this.logger?.warn(
            'Could not auto-detect deployment type, assuming Standard Elasticsearch. ' +
            'Set isServerless: true explicitly in config if using Serverless.',
            { error: error instanceof Error ? error.message : String(error) }
        );
        this.isServerless = false;
        this.deploymentChecked = true;
        return false;
    }
}</code></pre><p>Was passiert:</p><ul><li>Zuerst wird geprüft, ob Sie <code>isServerless</code> explizit in der Konfiguration festgelegt haben (überspringt die automatische Erkennung).</li><li>Ruft die <code>info()</code> -API von Elasticsearch auf, um Clusterinformationen zu erhalten.</li><li>Prüft den Wert <code>build_flavor field</code> (serverlose Bereitstellungen geben <code>serverless</code> zurück)</li><li>Falls die Build-Variante nicht verfügbar ist, wird auf die Überprüfung des Slogans zurückgegriffen.</li><li>Speichert das Ergebnis im Cache, um wiederholte API-Aufrufe zu vermeiden.</li><li>Wird standardmäßig die Bereitstellung durchgeführt, wenn die Erkennung fehlschlägt.</li></ul><p> Anwendungsbeispiel:</p><pre><code>// Option 1: Auto-detect (recommended)
const vector = new ElasticVector({
    node: 'https://your-cluster.es.cloud',
    auth: { apiKey: 'your-api-key' }
});
// Detection happens automatically on first index operation

// Option 2: Explicit configuration (faster startup)
const vector = new ElasticVector({
    node: 'https://your-serverless.es.cloud',
    auth: { apiKey: 'your-api-key' },
    isServerless: true  // Skips auto-detection
});</code></pre><h3>Erstellen des „Speichers“ in Elasticsearch</h3><p>Die folgende Funktion richtet einen Elasticsearch-Index zum Speichern von Einbettungen ein. Es wird geprüft, ob der Index bereits existiert. Andernfalls wird eine solche mit der unten stehenden Zuordnung erstellt, die ein <code>dense_vector</code> -Feld zum Speichern von Einbettungen und benutzerdefinierten Ähnlichkeitsmetriken enthält.</p><p>Einige Dinge sind zu beachten:</p><ul><li>Der Parameter <code>dimension</code> gibt die Länge des jeweiligen Einbettungsvektors an und hängt davon ab, welches Einbettungsmodell Sie verwenden. In unserem Fall generieren wir Einbettungen mithilfe des <code>text-embedding-3-small</code> -Modells von OpenAI, das Vektoren der Größe <code>1536</code> ausgibt. Dies werden wir als Standardwert verwenden.</li><li>Die in der folgenden Zuordnung verwendete Variable <code>similarity</code> wird durch die Hilfsfunktion c<code>onst similarity = this.mapMetricToSimilarity(metric)</code> definiert, welche den Wert für den Parameter <code>metric</code> entgegennimmt und ihn in ein Elasticsearch-kompatibles Schlüsselwort für die gewählte Distanzmetrik umwandelt.<ul><li>Zum Beispiel: Mastra verwendet allgemeine Begriffe für Vektorähnlichkeit wie <code>cosine</code>, <code>euclidean</code>, und <code>dotproduct</code>. Würden wir die Metrik <code>euclidean</code> direkt in das Elasticsearch-Mapping einfügen, würde dies einen Fehler auslösen, da Elasticsearch erwartet, dass das Schlüsselwort <code>l2_norm</code> die euklidische Distanz repräsentiert.</li></ul></li><li>Serverless-Kompatibilität: Der Code lässt Shard- und Replikateinstellungen für serverlose Bereitstellungen automatisch aus, da diese von Elasticsearch Serverless automatisch verwaltet werden.</li></ul><pre><code>async createIndex(params: CreateIndexParams): Promise<void> {
    const { indexName, dimension = 1536, metric = 'cosine' } = params;

    try {
        const exists = await this.client.indices.exists({ index: indexName });

        if (exists) {
            try {
                await this.validateExistingIndex(indexName, dimension, metric);
                this.logger?.info(`Index "${indexName}" already exists and is valid`);
                return;
            } catch (validationError) {
                throw new Error(
                    `Index "${indexName}" exists but does not match the required configuration: ${
                        validationError instanceof Error ? validationError.message : String(validationError)
                    }`
                );
            }
        }

        const isServerless = await this.detectServerless();
        const similarity = this.mapMetricToSimilarity(metric);

        const indexConfig: any = {
            index: indexName,
            mappings: {
                properties: {
                    vector: {
                        type: 'dense_vector',
                        dims: dimension,
                        index: true,
                        similarity: similarity,
                    },
                    metadata: {
                        type: 'object',
                        enabled: true,
                        dynamic: true, // Allows flexible metadata structures
                    },
                },
            },
        };

        // Only configure shards/replicas for non-serverless deployments
        // Serverless manages infrastructure automatically
        if (!isServerless) {
            indexConfig.settings = {
                number_of_shards: 1,
                number_of_replicas: 0, // Increase for production HA deployments
            };
        }

        await this.client.indices.create(indexConfig);

        this.logger?.info(
            `Created ${isServerless ? 'Serverless' : 'Standard'} Elasticsearch index "${indexName}"`,
            { dimension, metric, similarity }
        );
    } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        this.logger?.error(`Failed to create index "${indexName}": ${errorMessage}`);
        throw new Error(`Failed to create index "${indexName}": ${errorMessage}`);
    }
}</code></pre><h3>Speichern einer neuen Erinnerung oder Notiz nach einer Interaktion</h3><p>Diese Funktion nimmt die nach jeder Interaktion neu generierten Einbettungen zusammen mit den Metadaten entgegen und fügt sie anschließend mithilfe der <code>bulk</code> -API von Elastic in den Index ein oder aktualisiert sie. Die <code>bulk</code> API bündelt mehrere Schreibvorgänge in einer einzigen Anfrage; diese Verbesserung unserer Indexierungsleistung stellt sicher, dass Aktualisierungen effizient bleiben, während der Speicher unseres Agenten immer größer wird.</p><pre><code>async upsert(params: UpsertVectorParams): Promise<string[]> {
    const { indexName, vectors, metadata = [], ids } = params;

    try {
        // Generate unique IDs if not provided
        const vectorIds = ids || vectors.map((_, i) => 
            `vec_${Date.now()}_${i}_${Math.random().toString(36).substr(2, 9)}`
        );

        const operations = vectors.flatMap((vec, index) => [
            { index: { _index: indexName, _id: vectorIds[index] } },
            {
                vector: vec,
                metadata: metadata[index] || {},
            },
        ]);

        const response = await this.client.bulk({
            refresh: true,
            operations,
        });

        if (response.errors) {
            const erroredItems = response.items.filter((item: any) => item.index?.error);
            const erroredIds = erroredItems.map((item: any) => item.index?._id);
            const errorDetails = erroredItems.slice(0, 3).map((item: any) => ({
                id: item.index?._id,
                error: item.index?.error?.reason || item.index?.error,
                type: item.index?.error?.type
            }));
            
            const errorMessage = `Failed to upsert ${erroredIds.length}/${vectors.length} vectors`;
            console.error(`${errorMessage}. Sample errors:`, JSON.stringify(errorDetails, null, 2));
            this.logger?.error(errorMessage, { 
                failedCount: erroredIds.length, 
                totalCount: vectors.length,
                sampleErrors: errorDetails 
            });
            
            // Still return successfully inserted IDs
            const successfulIds = vectorIds.filter((id, idx) => 
                !erroredIds.includes(id)
            );
            
            if (successfulIds.length === 0) {
                throw new Error(`${errorMessage}. All operations failed. See logs for details.`);
            }
            
            return successfulIds;
        }

        this.logger?.info(`Successfully upserted ${vectors.length} vectors to "${indexName}"`);
        return vectorIds;
    } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        this.logger?.error(`Failed to upsert vectors to "${indexName}": ${errorMessage}`);
        throw new Error(`Failed to upsert vectors to "${indexName}": ${errorMessage}`);
    }
}</code></pre><h3>Abfrage ähnlicher Vektoren für semantische Wiedererkennung</h3><p>Diese Funktion ist der Kern des semantischen Recall-Features. Der Agent verwendet eine Vektorsuche, um ähnliche gespeicherte Einbettungen in unserem Index zu finden.</p><pre><code>async query(params: QueryVectorParams<any>): Promise<QueryResult[]> {
    const { indexName, queryVector, topK = 10, filter, includeVector = false } = params;

    try {
        const knnQuery: any = {
            field: 'vector',
            query_vector: queryVector,
            k: topK,
            num_candidates: Math.max(topK * 10, 100), // Search more candidates for better recall
        };

        // Apply metadata filters if provided
        if (filter) {
            knnQuery.filter = this.buildElasticFilter(filter);
        }

        const sourceFields = ['metadata'];
        if (includeVector) {
            sourceFields.push('vector');
        }

        const response = await this.client.search({
            index: indexName,
            knn: knnQuery,
            size: topK,
            _source: sourceFields,
        });

        const results = response.hits.hits.map((hit: any) => ({
            id: hit._id,
            score: hit._score || 0,
            metadata: hit._source?.metadata || {},
            vector: includeVector ? hit._source?.vector : undefined,
        }));

        this.logger?.debug(`Query returned ${results.length} results from "${indexName}"`);
        return results;
    } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        this.logger?.error(`Failed to query vectors from "${indexName}": ${errorMessage}`);
        throw new Error(`Failed to query vectors from "${indexName}": ${errorMessage}`);
    }
}</code></pre><p>Unter der Haube:</p><ul><li>Führt eine <a href="https://www.elastic.co/docs/solutions/search/vector/knn">kNN-</a> Abfrage (k-nächste Nachbarn) mit Hilfe der <code>knn</code> -API in Elasticsearch aus.</li><li>Gibt die K ähnlichsten Vektoren zum Eingabeabfragevektor zurück.</li><li>Optional können Metadatenfilter angewendet werden, um die Ergebnisse einzugrenzen (z. B. nur innerhalb einer bestimmten Kategorie oder eines bestimmten Zeitraums zu suchen).</li><li>Gibt strukturierte Ergebnisse zurück, einschließlich der Dokument-ID, des Ähnlichkeitswerts und der gespeicherten Metadaten.</li></ul><h2>Erstellung des Wissensagenten</h2><p>Nachdem wir nun die Verbindung zwischen Mastra und Elasticsearch durch die <code>ElasticVector</code> -Integration kennengelernt haben, erstellen wir den Knowledge Agent selbst.</p><p>Erstellen Sie im Ordner <code>agents</code> eine Datei namens <code>knowledge-agent.ts</code>. Wir können damit beginnen, unsere Umgebungsvariablen zu verbinden und den Elasticsearch-Client zu initialisieren.</p><pre><code>import { Agent } from '@mastra/core/agent';
import { Memory } from '@mastra/memory';
import { openai } from '@ai-sdk/openai';
import { Client } from '@elastic/elasticsearch';
import { ElasticVector } from '../stores/elastic-store';
import dotenv from "dotenv";

dotenv.config();

const ELASTICSEARCH_ENDPOINT = process.env.ELASTICSEARCH_ENDPOINT;
const ELASTICSEARCH_API_KEY = process.env.ELASTICSEARCH_API_KEY;

//Error check for undefined credentials
if (!ELASTICSEARCH_ENDPOINT || !ELASTICSEARCH_API_KEY) {
  throw new Error('Missing Elasticsearch credentials');
}

//Check to see if a connection can be established
const testClient = new Client({
  node: ELASTICSEARCH_ENDPOINT,
  auth: { 
    apiKey: ELASTICSEARCH_API_KEY 
  },
});

try {
  await testClient.ping();
  console.log('Connected to Elasticsearch successfully');
} catch (error: unknown) {
  if (error instanceof Error) {
    console.error('Failed to connect to Elasticsearch:', error.message);
  } else {
    console.error('Failed to connect to Elasticsearch:', error);
  }
  process.exit(1);
}
//Initialize the Elasticsearch vector store
const vectorStore = new ElasticVector({
  node: ELASTICSEARCH_ENDPOINT,
  auth: {
    apiKey: ELASTICSEARCH_API_KEY,
  },
//Optional: Explicitly set to true if using Elasticsearch Serverless to skip auto-detection and improve startup time
//isServerless: true,
});</code></pre><p>Hier, wir:</p><ul><li>Verwenden Sie <code>dotenv</code> um unsere Variablen aus unserer <code>.env</code> -Datei zu laden.</li><li>Prüfen Sie, ob die Elasticsearch-Zugangsdaten korrekt eingefügt werden, dann können wir eine erfolgreiche Verbindung zum Client herstellen.</li><li>Übergeben Sie den Elasticsearch-Endpunkt und den API-Schlüssel an den <code>ElasticVector</code> -Konstruktor, um eine Instanz unseres zuvor definierten Vektorspeichers zu erstellen.</li><li>Optional können Sie <code>isServerless: true</code> angeben, wenn Sie Elasticsearch Serverless verwenden. Dadurch wird der automatische Erkennungsschritt übersprungen und die Startzeit verkürzt. Wird dieser Parameter weggelassen, erkennt der Adapter Ihren Bereitstellungstyp bei der ersten Verwendung automatisch.</li></ul><p>Als nächstes können wir den Agenten mithilfe der Klasse <code>Agent</code> von Mastra definieren.</p><pre><code>export const knowledgeAgent = new Agent({
    name: 'KnowledgeAgent',
    instructions: 'You are a helpful knowledge assistant.',
    model: openai('gpt-4o'),
    memory: new Memory({

        vector: vectorStore,

        //embedder used to create embeddings for each message
        embedder: 'openai/text-embedding-3-small',

        //set semantic recall options
        options: {
            semanticRecall: {
                topK: 3, // retrieve 3 similar messages
                messageRange: 2, // include 2 messages before/after each match
                scope: 'resource',
            },
        },
    }),
});</code></pre><p>Folgende Felder können wir definieren:</p><ul><li><code>name</code> und <code>instructions</code>: Gib ihr eine Identität und eine primäre Funktion.</li><li><code>model</code>Wir verwenden OpenAIs <code>gpt-4o</code> über das <code>@ai-sdk/openai</code> -Paket.</li><li><code>memory</code>:<ul><li><code>vector</code>: Verweist auf unseren Elasticsearch-Speicher, sodass Einbettungen dort gespeichert und abgerufen werden.</li><li><code>embedder</code>Welches Modell soll zur Generierung von Einbettungen verwendet werden?</li><li><code>semanticRecall</code> Die Optionen bestimmen, wie der Rückruf funktioniert:<ul><li><code>topK</code>: Wie viele semantisch ähnliche Nachrichten sollen abgerufen werden?</li><li><code>messageRange</code>: Wie viel vom Gespräch soll bei jedem Spielzug einbezogen werden?</li><li><code>scope</code>: Definiert die Speichergrenze.</li></ul></li></ul></li></ul><p>Fast fertig. Wir müssen diesen neu erstellten Agenten lediglich zu unserer Mastra-Konfiguration hinzufügen. Importieren Sie in der Datei mit dem Namen <a href="http://index.ts/"><code>index.ts</code></a> den Wissensagenten und fügen Sie ihn in das Feld <code>agents</code> ein.</p><pre><code>export const mastra = new Mastra({
  agents: { knowledgeAgent },
  storage: new LibSQLStore({
    // stores observability, scores, ... into memory storage, if it needs to persist, change to file:../mastra.db
    url: ":memory:",
  }),
  logger: new PinoLogger({
    name: 'Mastra',
    level: 'info',
  }),
  telemetry: {
    // Telemetry is deprecated and will be removed in the Nov 4th release
    enabled: false, 
  },
  observability: {
    // Enables DefaultExporter and CloudExporter for AI tracing
    default: { enabled: true }, 
  },
});</code></pre><p>Zu den weiteren Bereichen gehören:</p><ul><li><code>storage</code>Dies ist Mastras interner Datenspeicher für Laufzeitverlauf, Observability-Metriken, Scores und Caches. Weitere Informationen zu Mastra-Speicherlösungen finden Sie <a href="https://mastra.ai/docs/server-db/storage">hier</a>.</li><li><code>logger</code>Mastra verwendet <a href="https://github.com/pinojs/pino">Pino</a>, einen leichtgewichtigen, strukturierten JSON-Logger. Es erfasst Ereignisse wie Agentenstart und -stopp, Toolaufrufe und -ergebnisse, Fehler und LLM-Reaktionszeiten.</li><li><code>observability</code>: Steuert die KI-Verfolgung und die Sichtbarkeit der Ausführung von Agenten. Es verfolgt:<ul><li>Beginn/Ende jedes Denkschritts.</li><li>Welches Modell oder Werkzeug wurde verwendet?</li><li>Ein- und Ausgänge.</li><li>Bewertungen und Beurteilungen</li></ul></li></ul><h3>Testen des Agenten mit Mastra Studio</h3><p>Glückwunsch! Wenn Sie es bis hierher geschafft haben, sind Sie bereit, diesen Agenten auszuführen und seine semantischen Erinnerungsfähigkeiten zu testen. Zum Glück bietet Mastra eine integrierte Chat-Benutzeroberfläche, sodass wir keine eigene entwickeln müssen.</p><p>Um den Mastra-Entwicklungsserver zu starten, öffnen Sie ein Terminal und führen Sie folgenden Befehl aus:</p><pre><code>npm run dev</code></pre><p>Nach der ersten Bündelung und dem Start des Servers sollte Ihnen eine Adresse für den Playground bereitgestellt werden.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8b045f70008d26aec4d2e6b59d61085555b9c5b2-686x116.png" style="width: 100%;" /><p>Fügen Sie diese Adresse in Ihren Browser ein, und Sie gelangen zum Mastra Studio.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/69bc80fe8486edd9e0cf91d87b39f465aeb23111-1600x438.png" style="width: 100%;" /><p>Wählen Sie die Option für <code>knowledgeAgent</code> und legen Sie los.</p><p>Um schnell zu prüfen, ob alles richtig verkabelt ist, geben Sie ihm beispielsweise folgende Information: „Das Team gab bekannt, dass die Umsatzentwicklung im Oktober um 12 % gestiegen ist, hauptsächlich aufgrund von Vertragsverlängerungen im Unternehmensbereich.“ Der nächste Schritt besteht darin, die Kundenansprache auf mittelständische Unternehmen auszuweiten.“ Starten Sie anschließend einen neuen Chat und stellen Sie eine Frage wie: „Auf welches Kundensegment sollten wir uns als Nächstes konzentrieren?“ Der Wissensagent sollte in der Lage sein, die Informationen aus dem ersten Chat abzurufen. Sie sollten eine Antwort wie diese sehen:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/da8ebddad89874023ed440a8f1ad2cb04ed043f4-1070x288.png" style="width: 100%;" /><p>Eine solche Antwort bedeutet, dass der Agent unsere vorherige Nachricht erfolgreich als Einbettungen in Elasticsearch gespeichert und später mithilfe der Vektorsuche abgerufen hat.</p><h3>Überprüfung des Langzeitspeichers des Agenten</h3><p>Wechseln Sie im Mastra Studio zur Registerkarte <code>memory</code> in der Konfiguration Ihres Agenten. So können Sie sehen, was Ihr Agent im Laufe der Zeit gelernt hat. Jede Nachricht, Antwort und Interaktion, die in Elasticsearch eingebettet und gespeichert wird, wird Teil dieses Langzeitgedächtnisses. Sie können vergangene Interaktionen semantisch durchsuchen, um schnell erinnerte Informationen oder Kontexte wiederzufinden, die der Agent zuvor gelernt hat. Dies ist im Wesentlichen derselbe Mechanismus, den der Agent beim semantischen Abruf verwendet, aber hier können Sie ihn direkt untersuchen. In unserem unten stehenden Beispiel suchen wir nach dem Begriff „Vertrieb“ und erhalten jede Interaktion zurück, die etwas mit Vertrieb zu tun hat.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3decaa0c332d288c5ae0b11c25f592c7d50c2f0f-1104x1320.png" style="width: 100%;" /><h2>Fazit</h2><p>Durch die Verbindung von Mastra und Elasticsearch können wir unseren Agenten Speicher zur Verfügung stellen, was eine wichtige Ebene im Kontext-Engineering darstellt. Mithilfe des semantischen Abrufs können Agenten im Laufe der Zeit Kontext aufbauen und ihre Antworten auf dem basieren, was sie gelernt haben. Das bedeutet genauere, zuverlässigere und natürlichere Interaktionen.</p><p>Diese frühe Integration ist nur der Ausgangspunkt. Das gleiche Prinzip kann hier Support-Mitarbeitern ermöglichen, die sich an frühere Tickets erinnern, internen Bots, die relevante Dokumente abrufen, oder KI-Assistenten, die sich mitten im Gespräch an Kundendetails erinnern können. Wir arbeiten außerdem an einer offiziellen Mastra-Integration, wodurch diese Verbindung in naher Zukunft noch nahtloser wird.</p><p>Wir sind gespannt, was Sie als Nächstes entwickeln werden. Probieren Sie es aus, erkunden Sie <a href="https://mastra.ai/">Mastra</a> und seine Speicherfunktionen und teilen Sie Ihre Entdeckungen gerne mit der Community.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Multimodale Suche nach Berggipfeln mit Elasticsearch und SigLIP-2 ]]></title>
            <description><![CDATA[Lernen Sie, wie Sie die multimodale Suche von Text zu Bild und von Bild zu Bild mithilfe von SigLIP-2-Einbettungen und der Elasticsearch kNN-Vektorsuche implementieren. Projektschwerpunkt: Auffinden von Fotos des Gipfels des Mount Ama Dablam während einer Everest-Trekkingtour.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/multimodal-search-siglip-2-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/multimodal-search-siglip-2-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Navneet Kumar]]></dc:creator>
            <pubDate>Tue, 04 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ffcf93358a7c5dadcea82faf3de460bf060d003c-1600x1200.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Wollten Sie schon immer einmal Ihr Fotoalbum nach Bedeutung durchsuchen? Versuchen Sie Suchanfragen wie „Zeig mir meine Bilder, auf denen ich eine blaue Jacke trage und auf einer Bank sitze“, „Zeig mir Bilder vom Mount Everest“ oder „Sake und Sushi“. Schnapp dir eine Tasse Kaffee (oder dein Lieblingsgetränk) und lies weiter. In diesem Blog zeigen wir Ihnen, wie Sie eine multimodale hybride Suchanwendung erstellen. Multimodal bedeutet, dass die App verschiedene Arten von Eingaben verstehen und durchsuchen kann – Text, Bilder und Audio – und nicht nur Wörter. Hybrid bedeutet, dass Techniken wie Keyword-Matching, kNN-Vektorsuche und Geofencing kombiniert werden, um präzisere Ergebnisse zu liefern.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0ec6bbb45013846b59ee00d2bf73ee2182ee7392-1920x1080.gif" style="width: 100%;" /><p>Um dies zu erreichen, verwenden wir Googles SigLIP-2, um Vektoreinbettungen sowohl für Bilder als auch für Texte zu generieren und diese in der Elasticsearch-Vektordatenbank zu speichern. Zum Zeitpunkt der Abfrage wandeln wir die Sucheingabe, Text oder Bild, in Einbettungen um und führen schnelle kNN-Vektorsuchen durch, um Ergebnisse abzurufen. Diese Konfiguration ermöglicht eine effiziente Text-zu-Bild- und Bild-zu-Bild-Suche. Eine Streamlit-Benutzeroberfläche erweckt dieses Projekt zum Leben, indem sie uns ein Frontend zur Verfügung stellt, mit dem wir nicht nur textbasiert nach passenden Fotos aus dem Album suchen und diese anzeigen können, sondern auch den Berggipfel auf dem hochgeladenen Bild identifizieren und weitere Fotos dieses Berges im Fotoalbum anzeigen können.<br/>Wir beschreiben außerdem die Schritte, die wir zur Verbesserung der Suchgenauigkeit unternommen haben, und geben praktische Tipps und Tricks. Zur weiteren Erkundung stellen wir ein <a href="https://github.com/navneet83/multimodal-mountain-peak-search">GitHub-Repository</a> und ein <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/notebooks/multimodal_mountain_peak_search.ipynb">Colab-Notebook</a> zur Verfügung.</p><h2>Wie alles begann</h2><p>Dieser Blogbeitrag entstand auf Anregung eines 10-Jährigen, der mich bat, ihm alle Bilder des Mount Ama Dablam von meiner Everest-Basislager-Trekkingtour zu zeigen. Während wir das Fotoalbum durchsahen, wurde ich auch gebeten, mehrere andere Berggipfel zu identifizieren, von denen ich einige nicht benennen konnte.</p><p>Das brachte mich auf die Idee, dass dies ein unterhaltsames Computer-Vision-Projekt werden könnte. Was wir erreichen wollten:</p><ul><li>Finde Bilder eines Berggipfels anhand seines Namens</li><li>Errate den Namen des Berggipfels anhand eines Bildes und finde ähnliche Gipfel im Fotoalbum.</li><li>Konzeptabfragen zum Laufen bringen (<em>Person</em>, <em>Fluss</em>, <em>Gebetsfahnen</em> <em>usw.)</em></li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e9d0d720a9b565d5b749bdc915068852d4f157ad-1200x1600.png" style="width: 100%;" /><h2>Zusammenstellung des Dreamteams: SigLIP-2, Elasticsearch &amp; Streamlit</h2><p>Es wurde schnell klar, dass wir, um dies zu ermöglichen, sowohl den Text („Ama Dablam“) als auch die Bilder (Fotos aus meinem Album) in Vektoren umwandeln müssten, die sinnvoll verglichen werden können, d. h. im selben Vektorraum. Sobald wir das getan haben, besteht die Suche nur noch darin, „die nächstgelegenen Nachbarn zu finden“.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/20e6f8b7d4fa48414f407ec200adbe00ee28d517-1536x1024.png" style="width: 100%;" /><div style="display:none">Unknown block type "block", specify a component for it in the `components.types` option</div><p><a href="https://huggingface.co/blog/siglip2"><strong>SigLIP-2</strong></a>, das kürzlich von Google veröffentlicht wurde, passt hier gut hinein. Es kann Einbettungen ohne aufgabenspezifisches Training generieren (eine <strong>Zero-Shot</strong> -Einstellung) und eignet sich hervorragend für unseren Anwendungsfall: unbeschriftete Fotos und Gipfel mit unterschiedlichen Namen und Sprachen. Da es für die Zuordnung von Text zu Bild trainiert wurde, werden ein Bergbild von der Wanderung und eine kurze Texteingabeaufforderung als Einbettungen sehr ähnlich dargestellt, selbst wenn die Abfragesprache oder die Rechtschreibung variiert.</p><p>SigLIP-2 bietet ein gutes Verhältnis von Qualität zu Geschwindigkeit, unterstützt mehrere Eingangsauflösungen und läuft sowohl auf der CPU als auch auf der GPU. Der SigLIP-2 ist im Vergleich zu Vorgängermodellen wie dem ursprünglichen CLIP robuster für Außenaufnahmen. Während unserer Tests lieferte SigLIP-2 durchweg zuverlässige Ergebnisse. Es wird zudem sehr gut unterstützt, was es zur naheliegenden Wahl für dieses Projekt macht.</p><p>Als nächstes benötigen wir eine Vektordatenbank zum Speichern der Einbettungen und zur Durchführung der Power-Suche. Es sollte nicht nur die Cosinus-kNN-Suche über Bildeinbettungen unterstützen, sondern auch Geofencing und Textfilter in einer einzigen Abfrage anwenden. Elasticsearch passt hier gut: Es verarbeitet Vektoren (HNSW kNN auf dense_vector-Feldern) sehr gut, unterstützt die hybride Suche, die Text-, Vektor- und Geo-Abfragen kombiniert, und bietet standardmäßig Filter- und Sortierfunktionen. Es ist außerdem horizontal skalierbar, sodass man problemlos von einer Handvoll Fotos auf Tausende wachsen kann. Der offizielle <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/python">Elasticsearch Python-Client</a> hält die Infrastruktur einfach und lässt sich nahtlos in das Projekt integrieren. Schließlich benötigen wir noch ein schlankes Frontend, in dem wir Suchanfragen eingeben und Ergebnisse anzeigen können. Für eine schnelle, Python-basierte Demo ist Streamlit hervorragend geeignet. Es bietet die grundlegenden Funktionen, die wir benötigen – Datei-Upload, ein responsives Bildraster und Dropdown-Menüs zum Sortieren und Geofencing. Es lässt sich leicht klonen und lokal ausführen und funktioniert auch in einem Colab-Notebook.</p><h2>Implementierung</h2><h3>Elasticsearch-Indexierungsdesign und Indexierungsstrategie</h3><p>Für dieses Projekt verwenden wir zwei Indizes: <code>peaks_catalog</code> und <code>photos</code>.</p><h4>Peaks_Katalogindex</h4><p>Dieser Index dient als kompakter Katalog markanter Berggipfel, die während der Everest-Basislager-Trekkingtour sichtbar sind. Jedes Dokument in diesem Index entspricht einem einzelnen Berggipfel, wie zum Beispiel dem Mount Everest. Für jedes Berggipfeldokument speichern wir Namen/Aliasse, optionale Breiten- und Längengradkoordinaten sowie einen einzelnen Prototypvektor, der durch die Kombination von SigLIP-2-Texteingabeaufforderungen (+ optionalen Referenzbildern) erstellt wird.</p><p><strong>Indexzuordnung:</strong></p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Feld</td><td style="border: 1px solid #ccc; padding: 4px;">Typ</td><td style="border: 1px solid #ccc; padding: 4px;">Beispiel</td><td style="border: 1px solid #ccc; padding: 4px;">Zweck/Anmerkungen</td><td style="border: 1px solid #ccc; padding: 4px;">Vektor-/Indexierung</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Ausweis</td><td style="border: 1px solid #ccc; padding: 4px;">Stichwort</td><td style="border: 1px solid #ccc; padding: 4px;">ama-dablam</td><td style="border: 1px solid #ccc; padding: 4px;">Stabiler Slug/ID</td><td style="border: 1px solid #ccc; padding: 4px;">—</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Namen</td><td style="border: 1px solid #ccc; padding: 4px;">Text + Stichwort-Unterfeld</td><td style="border: 1px solid #ccc; padding: 4px;">["Ama Dablam","Amadablam"]</td><td style="border: 1px solid #ccc; padding: 4px;">Aliase / mehrsprachige Namen; names.raw für genaue Filter</td><td style="border: 1px solid #ccc; padding: 4px;">—</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Breitengrad</td><td style="border: 1px solid #ccc; padding: 4px;">Geopunkt</td><td style="border: 1px solid #ccc; padding: 4px;">{"lat":27.8617,"lon":86.8614}</td><td style="border: 1px solid #ccc; padding: 4px;">GPS-Koordinaten des Gipfels als Kombination aus Breitengrad und Längengrad (optional)</td><td style="border: 1px solid #ccc; padding: 4px;">—</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">elev_m</td><td style="border: 1px solid #ccc; padding: 4px;">ganze Zahl</td><td style="border: 1px solid #ccc; padding: 4px;">6812</td><td style="border: 1px solid #ccc; padding: 4px;">Höhenangabe (optional)</td><td style="border: 1px solid #ccc; padding: 4px;">—</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">text_embed</td><td style="border: 1px solid #ccc; padding: 4px;">dense_vector</td><td style="border: 1px solid #ccc; padding: 4px;">768</td><td style="border: 1px solid #ccc; padding: 4px;">Kombinierter Prototyp (Aufforderungen und optional 1–3 Referenzbilder) für diesen Peak</td><td style="border: 1px solid #ccc; padding: 4px;">index:true, similarity:"cosine", index_options:{type:"hnsw", m:16, ef_construction:128}</td>
                    </tr>
              </tbody>
            </table>
          <p>Dieser Index wird vor allem für Bild-zu-Bild-Suchen verwendet, beispielsweise zur Identifizierung von Berggipfeln anhand von Bildern. Wir verwenden diesen Index auch, um die Ergebnisse der Text-zu-Bild-Suche zu verbessern.</p><p>Zusammenfassend lässt sich sagen, dass die <code>peaks_catalog</code> die Frage „Welcher Berg ist das?“ in ein fokussiertes Nächste-Nachbar-Problem umwandelt und so das konzeptionelle Verständnis effektiv von der Komplexität der Bilddaten trennt.</p><p><strong>Indexierungsstrategie für den peaks_catalog-Index: </strong>Wir beginnen mit der Erstellung einer Liste der markantesten Gipfel, die während der EBC-Trekkingtour sichtbar sind. Für jeden Gipfel speichern wir seine geografische Position, seinen Namen, Synonyme und seine Höhe in einer <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/data/peaks.yaml">YAML-Datei</a>. Im nächsten Schritt wird für jeden Peak <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L351">die Einbettung generiert</a> und im Feld <code>text_embed</code> gespeichert. Um robuste Einbettungen zu erzeugen, verwenden wir die folgende Technik:</p><ul><li>Erstellen Sie einen Textprototyp mit folgendem Werkzeug:<ul><li>Namen der Gipfel</li><li><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L301">Prompt-Ensemble</a> (Verwendung mehrerer unterschiedlicher Prompts, um dieselbe Frage zu beantworten), zum Beispiel:<ul><li>„Ein Naturfoto des Berggipfels {name} im Himalaya, Nepal“</li><li>„ {name} markanter Gipfel in der Khumbu-Region, alpine Landschaft“</li><li>„ {name} Berggipfel, Schnee, felsiger Grat“</li></ul></li><li>optionales <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L333">Anti-Konzept</a> (sagt SigLIP-2, wonach nicht gematcht werden soll): einen kleinen Vektor für „Gemälde, Illustration, Poster, Karte, Logo“ abziehen, um eine Bevorzugung von echten Fotos zu erreichen.</li></ul></li><li>Optional <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L388C13-L388C29">kann ein Bildprototyp erstellt werden,</a> falls Referenzbilder des Gipfels vorhanden sind.</li></ul><p>Anschließend <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L392">verschmelzen wir den Text- und Bildprototyp</a> , um die endgültige Einbettung zu erzeugen. Abschließend wird das Dokument mit allen erforderlichen Feldern <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L396">indexiert</a> :</p><pre><code>def l2norm(v: np.ndarray) -> 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,
) -> 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 (<= 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 > 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]</code></pre><p>Beispieldokument aus dem Index <code>peaks_catalog</code> :</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/bc05fbd0c4f8d721d5170c28a3884a9eda80bb7d-1210x1132.png" style="width: 100%;" /><h4>Fotoindex</h4><p>Dieser Hauptindex speichert detaillierte Informationen über alle Fotos im Album. Jedes Dokument stellt ein einzelnes Foto dar und enthält folgende Informationen:</p><ul><li>Relativer Pfad zum Foto im Fotoalbum. Dies kann verwendet werden, um das passende Bild anzuzeigen oder das Bild in die Suchoberfläche zu laden.</li><li>GPS- und Zeitinformationen des Bildes.</li><li>Dichter Vektor für die Bildkodierung, generiert durch SigLIP-2.</li><li><code>predicted_peaks</code> Das ermöglicht es uns, nach Gipfelnamen zu filtern.<br/><br/><strong>Indexzuordnung</strong></li></ul>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Feld</td><td style="border: 1px solid #ccc; padding: 4px;">Typ</td><td style="border: 1px solid #ccc; padding: 4px;">Beispiel</td><td style="border: 1px solid #ccc; padding: 4px;">Zweck/Anmerkungen</td><td style="border: 1px solid #ccc; padding: 4px;">Vektor-/Indexierung</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Weg</td><td style="border: 1px solid #ccc; padding: 4px;">Stichwort</td><td style="border: 1px solid #ccc; padding: 4px;">data/images/IMG_1234.HEIC</td><td style="border: 1px solid #ccc; padding: 4px;">Wie die Benutzeroberfläche das Miniaturbild/Vollbild öffnet</td><td style="border: 1px solid #ccc; padding: 4px;">—</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Bildausschnitt</td><td style="border: 1px solid #ccc; padding: 4px;">dense_vector</td><td style="border: 1px solid #ccc; padding: 4px;">768</td><td style="border: 1px solid #ccc; padding: 4px;">SigLIP-2 Bildeinbettung</td><td style="border: 1px solid #ccc; padding: 4px;">index:true, similarity:"cosine", index_options:{type:"hnsw", m:16, ef_construction:128}</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">vorhergesagte_Spitzen</td><td style="border: 1px solid #ccc; padding: 4px;">Stichwort</td><td style="border: 1px solid #ccc; padding: 4px;">["ama-dablam","pumori"]</td><td style="border: 1px solid #ccc; padding: 4px;">Top-K-Vorhersagen zur Indexierungszeit (kostengünstiger UX-Filter / Facette)</td><td style="border: 1px solid #ccc; padding: 4px;">—</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">GPS</td><td style="border: 1px solid #ccc; padding: 4px;">Geopunkt</td><td style="border: 1px solid #ccc; padding: 4px;">{"lat":27.96,"lon":86.83}</td><td style="border: 1px solid #ccc; padding: 4px;">Aktiviert Geofilter</td><td style="border: 1px solid #ccc; padding: 4px;">—</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Schusszeit</td><td style="border: 1px solid #ccc; padding: 4px;">date</td><td style="border: 1px solid #ccc; padding: 4px;">2023-10-18T09:41:00Z</td><td style="border: 1px solid #ccc; padding: 4px;">Aufnahmezeit: Sortieren/Filtern</td><td style="border: 1px solid #ccc; padding: 4px;">—</td>
                    </tr>
              </tbody>
            </table>
          <p><strong>Indexierungsstrategie für den Fotoindex: </strong>Für jedes Foto im Album gehen wir wie folgt vor:<br/> Extrahieren Sie die Bildinformationen <code>shot_time</code> und <code>gps</code> <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L526">aus den Bildmetadaten</a>.</p><ul><li><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L511">SigLIP-2 Bildeinbettung</a>: Das Bild wird durch das Modell geleitet und der Vektor anschließend L2-normalisiert. Speichere die Einbettung im Feld <code>clip_image</code> .</li><li><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L519">Die Peaks werden vorhergesagt</a> und im Feld <code>predicted_peaks</code> gespeichert. Dazu nehmen wir zunächst den im vorherigen Schritt erzeugten Bildvektor des Fotos und führen dann eine schnelle kNN-Suche im Feld text_embed im Index <code>peaks_catalog</code> durch. Wir behalten die obersten 3-4 Spitzen bei und ignorieren den Rest.</li><li>Wir berechnen das Feld <code>_id</code> , indem wir einen <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L509">Hashwert</a> aus Bildname und Pfad erstellen. Dadurch wird sichergestellt, dass nach mehreren Durchläufen keine Duplikate entstehen.</li></ul><p>Sobald alle Felder für das Foto ermittelt wurden, werden die Fotodokumente mithilfe <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L530">der Massenindizierung</a> stapelweise indexiert:</p><pre><code>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",
) -> 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) >= 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")</code></pre><p>Beispieldokument aus dem Fotoindex:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1dc1406ac2a97440b6804838795b3c2205c4c6b2-1080x1234.png" style="width: 100%;" /><p>Zusammenfassend lässt sich sagen, dass der Fotoindex ein schneller, filterbarer und kNN-fähiger Speicher aller Fotos im Album ist. Die Kartierung ist bewusst minimalistisch gehalten – gerade so strukturiert, dass die Ergebnisse schnell abgerufen, übersichtlich dargestellt und nach Raum und Zeit unterteilt werden können. Dieser Index dient beiden Suchanwendungsfällen. Das Python-Skript zur Erstellung beider Indizes finden Sie <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/create_indices.py">hier</a>.</p><p>Die untenstehende Visualisierung der Kibana-Karten zeigt Dokumente aus dem Fotoalbum als grüne Punkte und Berggipfel ab dem Index <code>peaks_catalog</code> als rote Dreiecke an, wobei die grünen Punkte gut mit dem Wanderweg zum Everest-Basislager übereinstimmen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1c75d0ed0ce53d28a94bf2f47a354e25581d2baf-1600x1402.png" style="width: 100%;" /><h2>Suchanwendungsfälle</h2><p><strong>Namenssuche (Text-zu-Bild): Mit</strong> dieser Funktion können Benutzer mithilfe von Textanfragen Fotos von Berggipfeln (und sogar abstrakten Konzepten wie „Gebetsfahnen“) finden. Um dies zu erreichen, wird die Texteingabe mithilfe von SigLIP-2 <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L87C5-L87C20">in einen Textvektor umgewandelt</a> . Für eine robuste Textvektorgenerierung verwenden wir die gleiche Strategie wie für die Erstellung von Text-Embeddings im <code>peaks_catalog</code> -Index: <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L104">Kombination</a> der Texteingabe mit einem kleinen <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L100">Prompt-Ensemble</a>, Subtraktion eines minor<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L103"> Anti-Concept-Vektors</a> und Anwendung <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L104">der L2-Normalisierung</a> zur Erzeugung des endgültigen Abfragevektors. Anschließend wird eine kNN- <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L140">Abfrage</a> auf dem Feld <code>photos.clip_image</code> ausgeführt, um die am besten übereinstimmenden Peaks auf Basis der Kosinusähnlichkeit zu ermitteln und so die ähnlichsten Bilder zu finden. Optional können die Suchergebnisse relevanter gestaltet werden, indem Geo- und <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L152">Datumsfilter</a> und/oder ein <code>photos.predicted_peaks</code> -Termfilter als Teil der Abfrage angewendet werden (siehe Abfragebeispiele unten). Dadurch werden ähnlich aussehende Gipfel ausgeschlossen, die auf der Wanderung gar nicht sichtbar sind.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b5fac28ffdbedb820505365ca07df125cd01b939-946x370.png" style="width: 100%;" /><p><strong>Elasticsearch-Abfrage mit Geofilter:</strong></p><pre><code>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"
       }
     }
}</code></pre><p><strong>Bildsuche (Bild-zu-Bild): Mit</strong> dieser Funktion können wir einen Berg auf einem Bild identifizieren und weitere Bilder desselben Berges innerhalb des Fotoalbums finden. Beim Hochladen eines Bildes wird dieses vom SigLIP-2-Bildcodierer verarbeitet, um einen <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/identify_from_picture_find_similar_peaks.py#L228">Bildvektor</a> zu erzeugen. Anschließend wird eine <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/identify_from_picture_find_similar_peaks.py#L234">kNN-Suche</a> im Feld <code>peaks_catalog.text_embed</code> durchgeführt, um die am besten passenden Peaknamen zu identifizieren. Anschließend <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/identify_from_picture_find_similar_peaks.py#L257">wird aus diesen übereinstimmenden Peaknamen ein Textvektor generiert</a> und eine weitere <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/identify_from_picture_find_similar_peaks.py#L263">kNN-Suche</a> im Fotoindex durchgeführt, um entsprechende Bilder zu finden.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3a3d5635bee7a222b95529dd7f9fbee016381610-1226x550.png" style="width: 100%;" /><p><strong>Elasticsearch-Abfrage:</strong></p><p>Schritt 1: Finde die passenden Peaknamen</p><pre><code>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...
         ]
       }
     }
   ]
 }
}</code></pre><p>Schritt 2: Führen Sie eine Suche im Index <code>photos</code> durch, um die passenden Bilder zu finden (dieselbe Abfrage wie im Anwendungsfall der Text-zu-Bild-Suche):</p><pre><code>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"
       }
     }
}</code></pre><h2>Streamlit-Benutzeroberfläche</h2><p>Um alles zusammenzuführen, haben wir eine einfache Streamlit-Benutzeroberfläche entwickelt, die es uns ermöglicht, beide Suchanwendungsfälle durchzuführen. In der linken Spalte wird eine scrollbare Liste von Gipfeln (aggregiert aus <code>photos.predicted_peaks</code>) mit Kontrollkästchen und einem Mini-Karten-/Geofilter angezeigt. Ganz oben befinden sich ein <strong>Suchfeld für den Namen</strong> und eine Schaltfläche zum Hochladen <strong>eines Fotos zur Identifizierung</strong> . Im mittleren Bereich befindet sich ein responsives Miniaturraster mit kNN-Werten, vorhergesagten Spitzenwerten und Erfassungszeiten. Jedes Bild enthält eine Schaltfläche <strong>„Bild anzeigen“</strong> für Vorschauen in voller Auflösung.</p><p><strong>Suche durch Hochladen eines Bildes:</strong> Wir prognostizieren den Peak und finden übereinstimmende Peaks aus dem Fotoalbum.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/dca540cbf5279e6d6102c5a0c0351ddd4ac91cda-1600x1112.png" style="width: 100%;" /><p><strong>Suche nach Text</strong>: Finde die passenden Höhepunkte im Album anhand des Textes</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b1e8618db746cd49ea4962d3dc73031387b975dd-1600x1166.png" style="width: 100%;" /><h2>Fazit</h2><p>Was mit der Frage begann: <em>Können wir bitte die </em>Bilder<strong><em>von Ama Dablam</em></strong>sehen<em>?</em> entwickelte sich zu einem kleinen, funktionierenden <strong>multimodalen Suchsystem</strong> . Wir haben Rohdaten von Trekkingfotos aufgenommen, diese in <strong>SigLIP-2-Einbettungen</strong> umgewandelt und <strong>Elasticsearch</strong> verwendet, um ein schnelles <strong>kNN</strong> über Vektoren durchzuführen, sowie einfache Geo-/Zeitfilter, um die richtigen Bilder anhand ihrer <em>Bedeutung</em> anzuzeigen. Dabei haben wir die Belange mithilfe zweier Indizes getrennt: einem winzigen <code>peaks_catalog</code> Index von kombinierten Prototypen (zur Identifizierung) und einem skalierbaren <code>photos</code> Index von Bildvektoren und EXIF-Daten (zum Abruf). Es ist praktisch, reproduzierbar und leicht erweiterbar.</p><p>Falls Sie es feinabstimmen möchten, gibt es einige Einstellungen, mit denen Sie experimentieren können:</p><ul><li><strong>Einstellungen für die Abfragezeit:</strong> <code>k</code> (wie viele Nachbarn Sie zurückbekommen möchten) und <code>num_candidates</code> (wie breit die Suche vor der endgültigen Bewertung sein soll). Diese Einstellungen werden <a href="https://www.elastic.co/search-labs/blog/elasticsearch-knn-and-num-candidates-strategies">hier</a> im Blog besprochen.</li><li><strong>Indexzeiteinstellungen:</strong> <code>m</code> (Graphkonnektivität) und <code>ef_construction</code> (Genauigkeit der Build-Zeit vs. Speicher). Experimentieren Sie bei Abfragen auch mit <code>ef_search</code> – ein höherer Wert bedeutet in der Regel eine bessere Trefferquote, allerdings mit einem gewissen Nachteil bei der Latenz. Weitere Einzelheiten zu diesen Einstellungen finden Sie in <a href="https://www.elastic.co/search-labs/blog/hnsw-graph">diesem Blog</a> .</li></ul><p>Zukünftig werden native Modelle/Reranker für <strong>multimodale</strong> und <strong>mehrsprachige</strong> Suche in Kürze im Elastic-Ökosystem verfügbar sein. Dies dürfte die Bild-/Textsuche und das hybride Ranking von Haus aus noch weiter verbessern.<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>Wenn Sie das selbst ausprobieren möchten:</p><ul><li><strong>GitHub-Repository:</strong> <a href="https://github.com/navneet83/multimodal-mountain-peak-search"><em>https://github.com/navneet83/multimodal-mountain-peak-search</em></a></li><li><strong>Colab-Schnellstartanleitung:</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></li></ul><p>Damit ist unsere Reise zu Ende, und es ist Zeit, zurückzufliegen. Ich hoffe, das war hilfreich, und falls etwas kaputtgeht (oder verbessert wird), würde ich gerne erfahren, was Sie geändert haben.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d324d1e1472f1bfbd8f25747f57bdeeb9c7f16b2-1600x1200.png" style="width: 100%;" />]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Verbesserung der Relevanz mehrsprachiger Einbettungsmodelle durch hybrides Such-Reranking]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie die Relevanz der Suchergebnisse des E5-Multilingual-Embedding-Modells mithilfe des Cohere-Rerankers und der Hybridsuche in Elasticsearch verbessern können.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/multilingual-embedding-model-hybrid-search-reranking</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/multilingual-embedding-model-hybrid-search-reranking</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Quynh Nguyen]]></dc:creator>
            <pubDate>Mon, 03 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d341b04acecc8eeec321f5404e1643447ecc8526-720x420.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<h2>Einleitung</h2><p>Im <a href="https://www.elastic.co/search-labs/blog/multilingual-embedding-model-deployment-elasticsearch">letzten Teil dieser Serie</a> haben wir die Bereitstellung des vortrainierten E5-Modells von Elastic (sowie anderer mehrsprachiger Text-Embedding-Modelle von Hugging Face) erläutert und uns mit der Generierung dichter Vektor-Embeddings aus Ihren Textdaten mithilfe von Elasticsearch und Kibana befasst. In diesem Blogbeitrag werden wir die Ergebnisse dieser Einbettungen untersuchen und die wesentlichen Vorteile der Verwendung eines mehrsprachigen Modells hervorheben.</p><p>Nachdem wir nun unseren Index <code>coco_multilingual</code> haben, liefert die Suche Dokumente in mehreren Sprachen, wobei das Feld „en“ als Referenz dient:</p><pre><code># GET coco_multilingual/_search
    {
       "_index": "coco_multilingual",
       "_id": "WAiXQJYBgf6odR9bLohZ",
       "_score": 1,
       "_source": {
         "description": "Ein Parkmeßgerät auf einer Straße mit Autos",
         "en": "A row of parked cars sitting next to parking meters.",
         "language": "de",
         "vector_description": {...}
       }
     },
     . . .</code></pre><h2>Eine Suche auf Englisch durchführen</h2><p>Versuchen wir, die Suche auf Englisch durchzuführen und sehen wir, wie gut sie funktioniert:</p><pre><code>GET coco_multi/_search
{
"size": 10,
"_source": [
  "description", "language", "en"
],
"knn": {
  "field": "vector_description.predicted_value",
  "k": 10,
  "num_candidates": 100,
  "query_vector_builder": {
    "text_embedding": {
      "model_id": ".multilingual-e5-small_linux-x86_64_search",
      "model_text": "query: kitty"
    }
  }
}
}</code></pre><pre><code>{
       "_index": "coco_multi",
       "_id": "JQiXQJYBgf6odR9b6Yz0",
       "_score": 0.9334303,
       "_source": {
         "description": "Eine Katze, die in einem kleinen, gepackten Koffer sitzt.",
         "en": "A brown and white cat is in a suitcase.",
         "language": "de"
       }
     },
      {
       "_index": "coco_multi",
       "_id": "3AiXQJYBgf6odR9bFod6",
       "_score": 0.9281012,
       "_source": {
         "description": "Una bambina che tiene un gattino vicino a una recinzione blu.",
         "en": "A little girl holding a kitten next to a blue fence.",
         "language": "it"
       }
     },
     . . .</code></pre><p>Auch wenn die Anfrage täuschend einfach aussieht, suchen wir hier im Hintergrund nach den numerischen Einbettungen des Wortes „Kitty“ in allen Dokumenten und Sprachen. Und weil wir eine Vektorsuche durchführen, können wir semantisch nach allen Wörtern suchen, die mit „Kitty“ verwandt sein könnten: „Katze“, „Kätzchen“, „Katze“, „Gatto“ (Italienisch), „Mèo“ (Vietnamesisch), 고양이 (Koreanisch), 猫 (Chinesisch) usw. Das bedeutet, dass wir, selbst wenn meine Suchanfrage auf Englisch ist, auch Inhalte in allen anderen Sprachen suchen können. Wenn man beispielsweise nach „kitty l<code>ying on something</code> sucht, erhält man auch Dokumente in Italienisch, Niederländisch oder Vietnamesisch. Das nenne ich Effizienz!</p><h2>Suche nach Inhalten in anderen Sprachen</h2><pre><code>GET coco_multi/_search
{  
 "size": 100,
 "_source": [
   "description", "language", "en"
 ],
 "knn": {
   "field": "vector_description.predicted_value",
   "k": 50,
   "num_candidates": 1000,
   "query_vector_builder": {
     "text_embedding": {
       "model_id": ".multilingual-e5-small_linux-x86_64_search",
       "model_text": "query: kitty lying on something"
     }
   }
 }
}</code></pre><pre><code>{
 "description": "A black kitten lays on her side beside remote controls.",
 "en": "A black kitten lays on her side beside remote controls.",
 "language": "en"
},
{
 "description": "un gattino sdraiato su un letto accanto ad alcuni telefoni ",
 "en": "A black kitten lays on her side beside remote controls.",
 "language": "it"
},
{
 "description": "eine Katze legt sich auf ein ausgestopftes Tier",
 "en": "a cat lays down on a stuffed animal",
 "language": "de"
},
{
 "description": "Một chú mèo con màu đen nằm nghiêng bên cạnh điều khiển từ xa.",
 "en": "A black kitten lays on her side beside remote controls.",
 "language": "vi"
}
. . .</code></pre><p>Eine Suche nach dem koreanischen Stichwort „Katze“ („고양이“) liefert ebenfalls aussagekräftige Ergebnisse. Das Spektakuläre daran ist, dass wir in diesem Index nicht einmal Dokumente in koreanischer Sprache haben!</p><pre><code>GET coco_multi/_search
{
 "size": 100,
 "_source": [
   "description", "language", "en"
 ],
 "knn": {
   "field": "vector_description.predicted_value",
   "k": 50,
   "num_candidates": 1000,
   "query_vector_builder": {
     "text_embedding": {
       "model_id": ".multilingual-e5-small_linux-x86_64_search",
       "model_text": "query: 고양이"
     }
   }
 }
}</code></pre><pre><code> {
       {
         "description": "eine Katze legt sich auf ein ausgestopftes Tier",
         "en": "a cat lays down on a stuffed animal",
         "language": "de"
       }
     },
     {
       {
         "description": "Một con chó và con mèo đang ngủ với nhau trên một chiếc ghế dài màu cam.",
         "en": "A dog and cat lying  together on an orange couch. ",
         "language": "vi"
       }
     },</code></pre><p>Dies funktioniert, weil das Einbettungsmodell die Bedeutung in einem gemeinsamen semantischen Raum repräsentiert und somit das Auffinden relevanter Bilder auch bei einer Anfrage in einer anderen Sprache als den indizierten Bildunterschriften ermöglicht.</p><h2>Erhöhung der Relevanz der Suchergebnisse durch hybride Suche und Reranking</h2><p>Wir freuen uns, dass die entsprechenden Ergebnisse wie erwartet eingetreten sind. In der realen Welt, beispielsweise im E-Commerce oder bei RAG-Anwendungen, bei denen die Ergebnisse auf die 5 bis 10 relevantesten Ergebnisse eingegrenzt werden müssen, können wir ein Rerank-Modell verwenden, um die relevantesten Ergebnisse zu priorisieren.</p><p>Eine Suchanfrage wie „Welche Farbe hat die Katze?“ auf Vietnamesisch liefert hier zwar viele Ergebnisse, aber die ersten ein oder zwei Ergebnisse sind möglicherweise nicht die relevantesten.</p><pre><code>GET coco_multi/_search
{
 "size": 20,
 "_source": [
   "description",
   "language",
   "en"
 ],
 "knn": {
   "field": "vector_description.predicted_value",
   "k": 20,
   "num_candidates": 1000,
   "query_vector_builder": {
     "text_embedding": {
       "model_id": ".multilingual-e5-small_linux-x86_64_search",
       "model_text": "query: con mèo màu gì?"
     }
   }
 }
}</code></pre><p>In allen Ergebnissen wird die Katze oder irgendeine Farbe erwähnt:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/33e1e887dbbdd1066cfedc7375f5e3b46538529e-859x847.png" style="width: 100%;" /><p>Lasst uns das verbessern! Lasst uns das mehrsprachige Rerank-Modell von <a href="https://cohere.com/blog/rerank-3pt5">Cohere</a>integrieren, um die Argumentation in Bezug auf unsere Frage zu verbessern.</p><pre><code>PUT _inference/rerank/cohere_rerank
{
 "service": "cohere",
 "service_settings": {
   "api_key": "your_api_key",
   "model_id": "rerank-v3.5"
 },
 "task_settings": {
   "top_n": 10,
   "return_documents": true
 }
}


GET coco_multi/_search
{
"size": 10,
"_source": [
  "description",
  "language",
  "en"
],
"retriever": {
  "text_similarity_reranker": {
    "retriever": {
      "rrf": {
        "retrievers": [
          {
            "knn": {
              "field": "vector_description.predicted_value",
              "k": 50,
              "num_candidates": 100,
              "query_vector_builder": {
                "text_embedding": {
                  "model_id": ".multilingual-e5-small_linux-x86_64_search",
                  "model_text": "query: con mèo màu gì?" // English: What color is the cat?
                }
              }
            }
          }
        ],
        "rank_window_size": 100,
        "rank_constant": 0
      }
    },
    "field": "description",
    "inference_id": "cohere_rerank",
    "inference_text": "con mèo màu gì?"
  }
}
}</code></pre><pre><code> {
       "_index": "coco_multi",
       "_id": "rQiYQJYBgf6odR9bBYyH",
       "_score": 1.5501487,
       "_source": {
         "description": "Hai cái điện thoại được đặt trên một cái chăn cạnh một con mèo con màu đen.",
         "en": "A black kitten lays on her side beside remote controls.",
         "language": "vi"
       }
     },
     {
       "_index": "coco_multi",
       "_id": "swiXQJYBgf6odR9b04uf",
       "_score": 1.5427427,
       "_source": {
         "description": "Một con mèo sọc nâu nhìn vào máy quay.", // Real translation: A brown striped cat looks at the camera 
         "en": "This cat is sitting on a porch near a tire.",
         "language": "vi"
       }
     },</code></pre><p>Mit den besten Ergebnissen kann unsere Anwendung nun mit Sicherheit sagen, ob das Kätzchen schwarz oder braun mit Streifen ist. Was hierbei noch interessanter ist: Unsere Vektorsuche hat tatsächlich eine Auslassung in der englischen Bildunterschrift des ursprünglichen Datensatzes aufgedeckt. Es ist in der Lage, die braun gestreifte Katze zu finden, obwohl die englische Referenzübersetzung dieses Detail ausgelassen hat. Das ist die Stärke der Vektorsuche.</p><h2>Fazit</h2><p>In diesem Blog haben wir den Nutzen eines mehrsprachigen Einbettungsmodells erläutert und gezeigt, wie man Elasticsearch nutzen kann, um die Modelle zu integrieren, Einbettungen zu generieren und Relevanz und Genauigkeit mit einer hybriden Suche und einem Reranker effektiv zu verbessern. Sie können <a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;cta=cloud-registration&amp;tech=trial&amp;plcmt=article%20content&amp;pg=search-labs">einen eigenen Cloud-Cluster erstellen</a> , um <a href="https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-e5">die mehrsprachige semantische Suche mit unserem sofort einsatzbereiten E5-Modell</a> auf der Sprache und dem Datensatz Ihrer Wahl auszuprobieren.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Bereitstellung eines mehrsprachigen Einbettungsmodells in Elasticsearch]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie ein mehrsprachiges e5-Einbettungsmodell für die Vektorsuche und den sprachübergreifenden Abruf in Elasticsearch bereitstellen.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/multilingual-embedding-model-deployment-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/multilingual-embedding-model-deployment-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Quynh Nguyen]]></dc:creator>
            <pubDate>Wed, 22 Oct 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8f2aa7bebb6b2f701e274ba7282273f9ab4abed6-720x432.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<h2>Einleitung</h2><p>In einer Welt globaler Nutzer ist die sprachübergreifende Informationswiedergewinnung (CLIR) von entscheidender Bedeutung. Anstatt die Suche auf eine einzige Sprache zu beschränken, ermöglicht CLIR das Auffinden von Informationen in <em>jeder beliebigen</em> Sprache, verbessert so die Benutzerfreundlichkeit und optimiert die Abläufe. Stellen Sie sich einen globalen Markt vor, auf dem E-Commerce-Kunden in ihrer Sprache nach Artikeln suchen können und die passenden Ergebnisse angezeigt werden, ohne dass die Daten vorher lokalisiert werden müssen. Oder, um akademischen Forschern die Möglichkeit zu geben, in ihrer Muttersprache nach wissenschaftlichen Artikeln zu suchen, inklusive aller Nuancen und Komplexitäten, selbst wenn die Quelle in einer anderen Sprache verfasst ist.</p><p>Mehrsprachige Text-Embedding-Modelle ermöglichen uns genau das. Einbettungen sind eine Möglichkeit, die Bedeutung von Text als numerische Vektoren darzustellen. Diese Vektoren sind so konzipiert, dass Texte mit ähnlicher Bedeutung in einem hochdimensionalen Raum nahe beieinander liegen. Multilinguale Text-Embedding-Modelle sind speziell dafür entwickelt worden, Wörter und Phrasen mit der gleichen Bedeutung in verschiedenen Sprachen in einen ähnlichen Vektorraum abzubilden.</p><p>Modelle wie das Open-Source-Multilingual E5 werden mit riesigen Mengen an Textdaten trainiert, oft unter Verwendung von Techniken wie dem kontrastiven Lernen. Bei diesem Ansatz lernt das Modell, zwischen Textpaaren mit ähnlicher Bedeutung (positive Paare) und solchen mit unähnlicher Bedeutung (negative Paare) zu unterscheiden. Das Modell wird so trainiert, dass die von ihm erzeugten Vektoren so angepasst werden, dass die Ähnlichkeit zwischen positiven Paaren maximiert und die Ähnlichkeit zwischen negativen Paaren minimiert wird. Bei mehrsprachigen Modellen umfassen diese Trainingsdaten Textpaare in verschiedenen Sprachen, die Übersetzungen voneinander sind, wodurch das Modell einen gemeinsamen Repräsentationsraum für mehrere Sprachen erlernen kann. Die resultierenden Einbettungen können dann für verschiedene NLP-Aufgaben verwendet werden, darunter die sprachübergreifende Suche, bei der die Ähnlichkeit zwischen Texteinbettungen genutzt wird, um relevante Dokumente unabhängig von der Sprache der Anfrage zu finden.</p><h2>Vorteile der mehrsprachigen Vektorsuche</h2><ul><li><strong>Nuance</strong>: Die Vektorsuche zeichnet sich durch ihre Fähigkeit aus, semantische Bedeutungen zu erfassen und geht über die reine Stichwortsuche hinaus. Dies ist von entscheidender Bedeutung für Aufgaben, die das Verständnis von Kontext und sprachlichen Feinheiten erfordern.</li><li><strong>Sprachübergreifendes Verständnis</strong>: Ermöglicht einen effektiven Informationsabruf über verschiedene Sprachen hinweg, selbst wenn die Suchanfrage und die Dokumente unterschiedliches Vokabular verwenden.</li><li><strong>Relevanz</strong>: Liefert relevantere Ergebnisse durch Fokussierung auf die konzeptionelle Ähnlichkeit zwischen Suchanfragen und Dokumenten.</li></ul><p>Nehmen wir beispielsweise einen akademischen Forscher, der den „Einfluss sozialer Medien auf den politischen Diskurs“ in verschiedenen Ländern untersucht. Mit der Vektorsuche können sie Suchanfragen wie „l&#x27;impatto dei social media sul discorso politico“ (Italienisch) oder „ảnh hưởng của mạng xã hội đối với diễn ngôn chính trị“ (Vietnamesisch) eingeben und relevante Artikel auf Englisch, Spanisch oder einer anderen Sprache finden indizierte Sprache. Dies liegt daran, dass die Vektorsuche Artikel identifiziert, die das <em>Konzept</em> des Einflusses sozialer Medien auf die Politik diskutieren, und nicht nur solche, die die exakten Schlüsselwörter enthalten. Dies erweitert und vertieft die Bandbreite ihrer Forschung erheblich.</p><h2>Erste Schritte</h2><p>Hier erfahren Sie, wie Sie CLIR mit Elasticsearch einrichten – mit dem E5-Modell, das standardmäßig mitgeliefert wird. Wir verwenden den <a href="https://huggingface.co/datasets/romrawinjp/multilingual-coco">Open-Source-Datensatz COCO</a>, der Bildunterschriften in mehreren Sprachen enthält, um zwei Arten von Suchanfragen zu visualisieren:</p><ol><li>Suchanfragen und Suchbegriffe in anderen Sprachen für einen englischen Datensatz, und</li><li>Abfragen in mehreren Sprachen auf einem Datensatz, der Dokumente in mehreren Sprachen enthält.</li></ol><p>Anschließend werden wir die Leistungsfähigkeit der hybriden Suche und des Rerankings nutzen, um die Suchergebnisse noch weiter zu verbessern.</p><h2>Voraussetzungen</h2><ul><li>Python 3.6+</li><li>Elasticsearch 8+</li><li>Elasticsearch Python-Client: pip install elasticsearch</li></ul><h2>Datensatz</h2><p>Der <a href="https://huggingface.co/datasets/romrawinjp/multilingual-coco">COCO-Datensatz</a> ist ein umfangreicher Datensatz zur Untertitelung. Jedes Bild im Datensatz ist in mehreren verschiedenen Sprachen beschriftet, wobei für jede Sprache mehrere Übersetzungen verfügbar sind. Zur Veranschaulichung werden wir jede Übersetzung als einzelnes Dokument indexieren, zusammen mit der ersten verfügbaren englischen Übersetzung als Referenz.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d4f0632529c71a22fbdecf21c9f4f0bb64b8e69c-1600x567.png" style="width: 100%;" /><h3>Schritt 1: Laden Sie den mehrsprachigen COCO-Datensatz herunter</h3><p>Um den Blog zu vereinfachen und das Nachvollziehen zu erleichtern, laden wir hier die ersten 100 Zeilen der Restval-Tabelle mit einem einfachen API-Aufruf in eine lokale JSON-Datei. Alternativ können Sie die Datensätze der HuggingFace-Bibliothek verwenden, um den vollständigen Datensatz oder Teilmengen des Datensatzes zu laden.</p><pre><code>import requests
import json
import os
### Download multilingual coco dataset into a json file (for easy viewing)
### Here we are retrieving first 100 rows for this example
### Alternatively, you can use `datasets` library from Hugging Face
url = "https://datasets-server.huggingface.co/rows?dataset=romrawinjp%2Fmultilingual-coco&config=default&split=restval&offset=0&length=100"
response = requests.get(url)


if response.status_code == 200:
   data = response.json()
   output_file = "multilingual_coco_sample.json" 
   ### Loading the downloaded content into a json file locally
   with open(output_file, "w", encoding="utf-8") as f:
       json.dump(data, f, indent=4, ensure_ascii=False)
   print(f"Data successfully downloaded and saved to {output_file}")
else:
   print(f"Failed to download data: {response.status_code}")
   print(response.text)</code></pre><p>Wenn die Daten erfolgreich in eine JSON-Datei geladen wurden, sollte die Ausgabe in etwa so aussehen:</p><p><code>Data successfully downloaded and saved to multilingual_coco_sample.json</code></p><h3>Schritt 2: (Elasticsearch starten) und die Daten in Elasticsearch indizieren</h3><p>a) Starten Sie Ihren lokalen Elasticsearch-Server.</p><p>b) Starten Sie den Elasticsearch-Client.</p><pre><code>from elasticsearch import Elasticsearch
from getpass import getpass


# Initialize Elasticsearch client
es = Elasticsearch(getpass("Host: "), api_key=getpass("API Key: "))


index_name = "coco"


# Create the index if it doesn't exist
if not es.indices.exists(index=index_name):
   es.indices.create(index=index_name, body=mapping)</code></pre><p>c) Indexdaten</p><pre><code># Load the JSON data
with open('./multilingual_coco_sample.json', 'r') as f:
   data = json.load(f)


rows = data["rows"]
# List of languages to process
languages = ["en", "es", "de", "it", "vi", "th"]


# For each image, we will process each individual caption as its own document
bulk_data = []
for data in rows:
   row = data["row"]
   image = row.get("image")
   image_url = image["src"]


   # Process each language
   for lang in languages:
       # Skip if language not present in this row
       if lang not in row:
           continue


       # Get all descriptions for this language
 # along with first available English caption for reference
       descriptions = row[lang]
       first_eng_caption = row["en"][0]


       # Prepare bulk indexing data
       for description in descriptions:
           if description == "":
               continue
           # Add index operation
           bulk_data.append(
               {"index": {"_index": index_name}}
           )
           # Add document
           bulk_data.append({
               "language": lang,
               "description": description,
               "en": first_eng_caption,
               "image_url": image_url,
           })


# Perform bulk indexing
if bulk_data:
   try:
       response = es.bulk(operations=bulk_data)
       if response["errors"]:
           print("Some documents failed to index")
       else:
           print(f"Successfully bulk indexed {len(bulk_data)} documents")
   except Exception as e:
       print(f"Error during bulk indexing: {str(e)}")


print("Indexing complete!")</code></pre><p>Sobald die Daten indexiert sind, sollte etwa Folgendes angezeigt werden:</p><p><code>Successfully bulk indexed 4840 documents</code></p><p><code>Indexing complete!</code></p><h3>Schritt 3: Das mit E5 trainierte Modell bereitstellen</h3><p>Navigieren Sie in Kibana zur Seite „Stack-Verwaltung &gt; <strong>Trainierte Modelle“</strong> und klicken Sie für das Modell „.multilingual-e5-small_linux-x86_64“ <strong>auf „Bereitstellen“</strong> . Option. Dieses E5-Modell ist ein kleines, mehrsprachiges Gerät, das für linux-x86_64 optimiert ist und sofort einsatzbereit ist. Durch Klicken auf „Bereitstellen“ wird ein Bildschirm angezeigt, auf dem Sie die Bereitstellungseinstellungen oder vCPU-Konfigurationen anpassen können. Der Einfachheit halber verwenden wir die Standardoptionen mit der Auswahl adaptiver Ressourcen, wodurch unsere Bereitstellung je nach Nutzung automatisch skaliert wird.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/95cd8f352425d1db2d04b00c3c88d1e71d1ef19a-1600x440.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1599d99949dda8267acc58f400a403a3af5373ef-1600x655.png" style="width: 100%;" /><p>Optional können Sie auch andere Text-Embedding-Modelle verwenden. Um beispielsweise BGE-M3 zu verwenden, können Sie <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/eland/machine-learning#ml-nlp-pytorch">den Eland Python-Client von Elastic</a> verwenden, um das Modell von HuggingFace zu importieren.</p><pre><code>export MODEL_ID="bge-m3"
export HUB_MODEL_ID="BAAI/bge-m3"
export CLOUD_ID={{CLOUD_ID}}
export ES_API_KEY={{API_KEY}}
docker run -it --rm docker.elastic.co/eland/eland \
eland_import_hub_model --cloud-id $CLOUD_ID --es-api-key $ES_API_KEY --hub-model-id $HUB_MODEL_ID --es-model-id $MODEL_ID --task-type text_embedding --start</code></pre><p>Navigieren Sie anschließend zur Seite „Trainierte Modelle“, um das importierte Modell mit den gewünschten Konfigurationen bereitzustellen.</p><h3>Schritt 4: Vektorisieren oder Einbettungen für die Originaldaten mit dem bereitgestellten Modell erstellen</h3><p>Um die Einbettungen zu erstellen, müssen wir zunächst eine Ingest-Pipeline erstellen, die es uns ermöglicht, den Text zu nehmen und ihn durch das Inferenz-Texteinbettungsmodell laufen zu lassen. Dies ist über die Benutzeroberfläche von Kibana oder über die API von Elasticsearch möglich.</p><p><strong>Um dies über die Kibana-Oberfläche zu tun</strong>, klicken Sie nach dem Bereitstellen des trainierten Modells auf die Schaltfläche <strong>„Testen </strong> “. Dies ermöglicht es Ihnen, die generierten Einbettungen zu testen und eine Vorschau anzuzeigen. Erstellen Sie eine neue Datenansicht für <code>coco</code><strong> </strong>Index, Datenansicht auf die neu erstellte Coco-Datenansicht setzen und Feld auf <code>description</code> setzen, da dies das Feld ist, für das wir Einbettungen generieren möchten.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/cfe189e13dc118d325e7fb90bdace0c912e29f51-1088x1600.png" style="width: 100%;" /><p>Das funktioniert hervorragend! Nun können wir mit der Erstellung der Ingest-Pipeline fortfahren und unsere Originaldokumente neu indizieren, sie durch die Pipeline leiten und einen neuen Index mit den Einbettungen erstellen. Dies erreichen Sie durch Klicken auf <strong>„Pipeline erstellen“</strong>. Sie werden dann durch den Erstellungsprozess der Pipeline geführt, wobei automatisch die benötigten Prozessoren zur Erstellung der Einbettungen bereitgestellt werden.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1e043c1c3279b66fbdf19c06b41e76e613043998-1600x1126.png" style="width: 100%;" /><p>Der Assistent kann außerdem automatisch die Prozessoren eintragen, die zur Fehlerbehebung während der Datenerfassung und -verarbeitung benötigt werden.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8804d31b4f869078e3b2245040bbb0ab1720a94a-1600x1084.png" style="width: 100%;" /><p>Erstellen wir nun die Ingest-Pipeline. Ich nenne die Pipeline <code>coco_e5</code>. Sobald die Pipeline erfolgreich erstellt wurde, können Sie sie sofort verwenden, um die Einbettungen zu generieren, indem Sie die ursprünglich indizierten Daten im Assistenten in einen neuen Index umindizieren. Klicken Sie auf <strong>„Neu indizieren“</strong> , um den Vorgang zu starten.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e34b1b18f5b24420d4581fe4d657c569926c2023-1600x1126.png" style="width: 100%;" /><h2>Für komplexere Konfigurationen können wir die Elasticsearch-API verwenden.</h2><p>Bei einigen Modellen kann es aufgrund der Art und Weise, wie die Modelle trainiert wurden, erforderlich sein, bestimmte Texte vor oder nach dem eigentlichen Input einzufügen, bevor die Einbettungen generiert werden; andernfalls kommt es zu einer Leistungsverschlechterung.</p><p>Beim Modell e5 erwartet man beispielsweise, dass der Eingabetext dem Format „passage: {content of passage}“ folgt. Um das zu erreichen, nutzen wir die Ingest-Pipelines: Wir erstellen eine neue Ingest-Pipeline <strong>vectorize_descriptions</strong>. In dieser Pipeline erstellen wir ein neues temporäres Feld <code>temp_desc</code> , fügen dem Text <code>description</code> „passage: “ voran, führen <code>temp_desc</code> das Modell aus, um Text-Embeddings zu generieren, und löschen dann <code>temp_desc</code>.</p><pre><code>PUT _ingest/pipeline/vectorize_descriptions
{
"description": "Pipeline to run the descriptions text_field through our inference text embedding model",
"processors": [
 {
   "set": {
     "field": "temp_desc",
     "value": "passage: {{description}}"
   }
 },
 {
   "inference": {     
"field_map": {
       "temp_desc": "text_field"
     },
     "model_id": ".multilingual-e5-small_linux-x86_64_search",
     "target_field": "vector_description"
   }
 },
 {
   "remove": {
     "field": "temp_desc"
   }
 }
]
}</code></pre><p>Darüber hinaus möchten wir möglicherweise festlegen, welche <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/dense-vector#dense-vector-quantization">Art der Quantisierung</a> wir für den generierten Vektor verwenden möchten. Standardmäßig verwendet Elasticsearch <code>int8_hnsw</code>, aber hier möchte ich <a href="https://www.elastic.co/search-labs/blog/better-binary-quantization-lucene-elasticsearch">Better Binary Quantization</a> (oder <code>bqq_hnsw</code>), wodurch jede Dimension auf eine Einzelbitgenauigkeit reduziert wird. Dadurch wird der Speicherbedarf um 96 % (oder um das 32-Fache) reduziert, allerdings auf Kosten der Genauigkeit. Ich entscheide mich für diese Quantisierungsart, weil ich weiß, dass ich später einen Reranker verwenden werde, um den Genauigkeitsverlust zu verbessern.</p><p>Dazu erstellen wir einen neuen Index mit dem Namen <strong>coco_multi</strong> und legen die Zuordnungen fest. Die Magie liegt hier im Feld vector_description, wo wir den Typ von <strong>index_options</strong>auf <strong>bbq_hnsw</strong> festlegen.</p><pre><code>PUT coco_multi
{
 "mappings": {
   "properties": {
     "description": {
       "type": "text"
     },
     "en": {
       "type": "text"
     },
     "image_url": {
       "type": "keyword"
     },
     "language": {
       "type": "keyword"
     },
     "vector_description.predicted_value": {
       "type": "dense_vector",
       "dims": 384,
       "index": "true",
       "similarity": "cosine",
       "index_options": {
         "type": "bbq_hnsw" 
       }
     }
   }
 }
}</code></pre><p>Nun können wir die Originaldokumente in einem neuen Index neu indizieren, wobei unsere Ingest-Pipeline das Beschreibungsfeld „vektorisiert“ oder Einbettungen erstellt.</p><pre><code>POST _reindex?wait_for_completion=false
{
 "source": {
   "index": "coco"
 },
 "dest": {
   "index": "coco_multilingual",
   "pipeline": "vectorize_descriptions"
 }
}</code></pre><p>Und das war&#x27;s! Wir haben erfolgreich ein mehrsprachiges Modell mit Elasticsearch und Kibana implementiert und Schritt für Schritt gelernt, wie man mit Elastic Vektoreinbettungen für seine Daten erstellt, entweder über die Kibana-Benutzeroberfläche oder mit der Elasticsearch-API. Im zweiten Teil dieser Reihe werden wir die Ergebnisse und die Feinheiten der Verwendung eines mehrsprachigen Modells untersuchen. In der Zwischenzeit können Sie <a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;cta=cloud-registration&amp;tech=trial&amp;plcmt=article%20content&amp;pg=search-labs">einen eigenen Cloud-Cluster erstellen</a> , um <a href="https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-e5">die mehrsprachige semantische Suche mit unserem sofort einsatzbereiten E5-Modell</a> auf der Sprache und dem Datensatz Ihrer Wahl auszuprobieren.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Elastischer MCP-Server: Agent Builder-Tools für jeden KI-Agenten zugänglich machen]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie den integrierten Elastic MCP-Server im Agent Builder nutzen können, um jeden KI-Agenten sicher um Zugriff auf Ihre privaten Daten und benutzerdefinierten Tools zu erweitern.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/elastic-mcp-server-agent-builder-tools</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/elastic-mcp-server-agent-builder-tools</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jedr Blaszyk, Joe McElroy]]></dc:creator>
            <pubDate>Mon, 20 Oct 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ef5153551a1d14833c7f512fede554d1dfb31553-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Elastic Agent Builder ist eine Plattform zur Erstellung von Tools und Agents, die eng mit Ihren eigenen Daten in Elasticsearch integriert sind. Beispielsweise können Sie Tools erstellen, die eine semantische Suche in internen Dokumenten durchführen, Observability-Logs analysieren oder Sicherheitswarnungen abfragen.</p><p>Die wahre Magie entfaltet sich jedoch erst, wenn Sie diese maßgeschneiderten, datenorientierten Tools in die Umgebungen integrieren können, in denen Sie die meiste Zeit verbringen. Was wäre, wenn Ihr Code-Editor-Agent sicher auf die private Wissensdatenbank Ihres Unternehmens zugreifen könnte?</p><p>Hier kommt <strong>das Model Context Protocol (MCP)</strong> ins Spiel. Elastic Agent Builder wird mit einem integrierten MCP-Server ausgeliefert, der Zugriff auf die Tools der Plattform ermöglicht.</p><h2>Warum sollte man den Elastic Agent Builder MCP-Server verwenden?</h2><p>KI-Agenten sind unglaublich leistungsstark, aber ihr Wissen beschränkt sich typischerweise auf die Daten, mit denen sie trainiert wurden, und auf Informationen, die sie aktiv im öffentlichen Internet suchen können. Sie kennen weder die internen Designdokumente Ihres Unternehmens noch die spezifischen Bereitstellungs-Runbooks Ihres Teams oder die einzigartige Struktur Ihrer Anwendungsprotokolle.</p><p>Die Herausforderung besteht darin, Ihrem KI-Assistenten den benötigten spezialisierten Kontext zu liefern. Genau dieses Problem soll MCP lösen. <strong>MCP ist ein offener Standard, der es einem KI-Modell oder -Agenten ermöglicht, externe Tools zu erkennen und zu nutzen.</strong></p><p>Um dies zu ermöglichen, stellt der Elastic Agent Builder Ihre benutzerdefinierten Tools nativ über einen integrierten MCP-Server zur Verfügung. Das bedeutet, dass Sie jeden MCP-kompatiblen Client, wie <strong>Cursor</strong>, <strong>VS Code</strong> oder <strong>Claude Desktop</strong>, problemlos mit den spezialisierten, datenorientierten Tools verbinden können, die Sie mit Elastic Agent Builder erstellt haben.</p><h2>Wann sollte man MCP verwenden (und wann nicht)?</h2><p>Elastic Agent Builder beinhaltet mehrere Protokolle zur Unterstützung verschiedener Integrationsmuster. Die Wahl der richtigen Lösung ist der Schlüssel zum Aufbau effektiver KI-Workflows.</p><ul><li><strong>Verwenden Sie <a href="https://www.elastic.co/docs/solutions/search/agent-builder/mcp-server">MCP</a></strong> , um Ihren KI-Agenten (wie in <strong>Cursor</strong> oder <strong>VS Code</strong>) mit spezialisierten Werkzeugen zu erweitern. Es ist der Ansatz „Bring your own tools“, der den bereits verwendeten Assistenten um einen sicheren Zugriff auf Ihre privaten Daten erweitert. Lediglich die Tools werden über den MCP-Server bereitgestellt – die Agenten von Elastic sind davon getrennt.</li><li><strong>Mit dem <a href="https://www.elastic.co/docs/solutions/search/agent-builder/a2a-server">A2A-Protokoll</a></strong> können Sie Ihren vollständig benutzerdefinierten Elastic Agent mit anderen autonomen Agenten (wie in <a href="https://www.elastic.co/search-labs/blog/a2a-protocol-elastic-agent-builder-gemini-enterprise"><strong>Googles Gemini Enterprise</strong></a>) zusammenarbeiten lassen. Dies dient der Agenten-zu-Agenten-Delegierung, bei der jeder Agent als gleichberechtigter Partner an der Lösung eines Problems arbeitet.</li><li><strong>Nutzen Sie die <a href="https://www.elastic.co/docs/solutions/search/agent-builder/kibana-api">Agent Builder APIs</a></strong> für die vollständige programmatische Kontrolle beim Erstellen einer benutzerdefinierten Anwendung von Grund auf.</li></ul><p>Für Entwickler, die Antworten aus ihrer internen Dokumentation erhalten möchten, ohne ihre IDE zu verlassen, ist MCP die perfekte Lösung.</p><h2>Beispiel: Ihre benutzerdefinierten Tools in Cursor mit Agent Builder MCP-Server</h2><p>Lassen Sie uns ein praktisches Beispiel durchgehen, das ich täglich verwende. Zuerst habe ich unsere interne technische Dokumentation gecrawlt und in einen Elasticsearch-Index namens <code>elastic-dev-docs</code> indexiert. Obwohl wir die im Agent Builder verfügbaren generischen, integrierten Tools verwenden könnten, werden wir unser eigenes, benutzerdefiniertes Tool erstellen, um diese spezielle Wissensdatenbank abzufragen.</p><p>Der Grund für die Entwicklung eines maßgeschneiderten Werkzeugs ist einfach: <strong>Kontrolle und Präzision</strong>. Dieser Ansatz gibt uns die Möglichkeit, eine schnelle semantische Abfrage direkt gegen unseren <code>elastic-dev-docs</code> -Index auszuführen. Wir haben die volle Kontrolle darüber, welcher Index genau anvisiert wird und wie die Daten abgerufen werden.</p><p>Und so können wir diese benutzerdefinierte Wissensdatenbank in einem KI-gestützten Code-Editor wie Cursor verwenden.</p><h3>Schritt 1: Erstellen Sie ein benutzerdefiniertes Wissensdatenbank-Tool im Agent Builder.</h3><p>Zuerst erstellen Sie ein neues Tool im Agent Builder. Eine klare und präzise Werkzeugbeschreibung ist wichtig, denn sie ist die Grundlage dafür, wie jeder KI-Agent, sei es der interne Elastic Agent oder ein externes Werkzeug wie Cursor, das über MCP verbunden ist, Ihr Werkzeug für die richtige Aufgabe erkennt und auswählt.</p><p>Eine aussagekräftige Beschreibung sollte explizit sein. Zum Beispiel: „Führt eine semantische Suche im elastic-dev-docs-Index durch, um interne Entwicklungsdokumentationen, Runbooks und Release-Prozeduren zu finden.“</p><p>Damit ist das Tool so konfiguriert, dass es eine semantische Suche in unserem spezifischen Index durchführt. Nach dem Speichern steht es sofort zum Servieren bereit.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1eea079908fdf7cc72dbe81abd07ff51601a43d4-1472x1600.png" style="width: 100%;" /><p>Bevor Sie es mit der Außenwelt verbinden, können Sie es direkt in der Benutzeroberfläche testen. Klicken Sie einfach auf die Schaltfläche <strong>„Testen“</strong> , um die Parameter manuell einzugeben und so das Verhalten des LLM zu simulieren. Überprüfen Sie anschließend die Ergebnisse, um sicherzustellen, dass alles ordnungsgemäß funktioniert.</p><h3>Schritt 2: Cursor mit dem Elastic MCP-Server verbinden</h3><p>Elastic Agent Builder stellt automatisch alle verfügbaren Tools über einen sicheren MCP-Endpunkt bereit. Ihre individuelle Server-URL finden Sie in der Tools-Benutzeroberfläche von Kibana.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ba137be30f0eaa7f028b96bd8af4e2779c3f8a33-1600x589.png" style="width: 100%;" /><p>Um eine Verbindung zu Cursor herzustellen, fügen wir einfach diese URL zusammen mit einem Elastic API Key zur Authentifizierung in die Konfigurationsdatei ein (<a href="https://www.elastic.co/docs/deploy-manage/api-keys/elasticsearch-api-keys">hier erfahren Sie, wie Sie einen ES API Key erstellen</a>). Wir verwenden einen API-Schlüssel zur Autorisierung, da dieser sicherstellt, dass die Tools nur mit den von Ihnen erteilten Berechtigungen ausgeführt werden und alle Ihre Zugriffskontrollregeln eingehalten werden.</p><p>Die MCP-Konfiguration in Cursors <code>~/.cursor/mcp.json</code> sieht folgendermaßen aus:</p><pre><code>{
  "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 <ELASTIC_API_KEY>"
      }
    }
  }
}</code></pre><p>Sobald die Konfiguration gespeichert ist, sollte das Elastic Agent Builder MCP-Servertool in Cursor verfügbar sein.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d302c6d3609fbf14fd40e21b9e69e567bf12553f-1600x1002.png" style="width: 100%;" /><h3>Schritt 3: Fragen Sie einfach drauflos!</h3><p>Nachdem die Verbindung hergestellt wurde, können Cursor-Agenten nun Ihre benutzerdefinierten Tools aufrufen, um Ihre Fragen zu beantworten oder den Codegenerierungsprozess zu steuern.</p><p>Stellen wir eine konkrete Frage:</p><p><em>„Schritte zur Freigabe des Crawler-Dienstes in der internen Entwicklungsdokumentation der Elasticsearch-Organisation nachschlagen“</em></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/14f572730203c23615bb9dd38234bcb3b0f81155-1600x1468.png" style="width: 100%;" /><p>Hinter den Kulissen geschieht die Magie:</p><ol><li>Der Cursor-Agent entscheidet, wie Ihre Frage am besten beantwortet werden kann, und beschließt, den/die/das … anzurufen. <code>engineering_documentation_internal_search</code></li><li>Es ruft das Tool mit einer natürlichsprachlichen Anfrage auf.</li><li>Das Tool führt eine semantische Suche im Index <code>elastic-dev-docs</code> durch und gibt die relevantesten und aktuellsten Prozeduren zurück.</li></ol><p>Wir erhalten eine präzise und verlässliche Antwort auf Basis unserer internen Dokumentation, ohne jemals den Code-Editor verlassen zu müssen. Das Erlebnis ist nahtlos und beeindruckend.</p><h2>Jetzt bist du an der Reihe zu bauen</h2><p>Sie haben nun gesehen, wie Sie den integrierten MCP-Server im Elastic Agent Builder verwenden können, um Ihre KI-Assistenten um einen sicheren Zugriff auf Ihre privaten Daten zu erweitern. Die Modelle auf eigenen Informationen zu basieren, ist der Schlüssel zu ihrer tatsächlichen Nützlichkeit.</p><p>Zusammenfassend haben wir die wichtigsten Schritte behandelt:</p><ul><li>Die Wahl des richtigen Protokolls für Ihre Bedürfnisse (MCP).</li><li>Entwicklung eines maßgeschneiderten Wissensdatenbank-Tools.</li><li>Verbindung dieses Tools mit einem IDE-Assistenten wie Cursor.</li></ul><p>Ihre Agenten und Tools müssen nicht länger von ihrem wichtigsten Kontext getrennt werden. Wir hoffen, dass Ihnen dieser Leitfaden dabei hilft, effektivere, datenbasierte Arbeitsabläufe zu erstellen. Viel Spaß beim Bauen!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Training von LTR-Modellen in Elasticsearch mit Urteilslisten basierend auf Benutzerverhaltensdaten]]></title>
            <description><![CDATA[Lernen Sie, wie Sie UBI-Daten verwenden, um Beurteilungslisten zu erstellen und so das Training Ihrer Learning-to-Rank-Modelle (LTR) in Elasticsearch zu automatisieren.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/training-learning-to-rank-models-elasticsearch-ubi-data</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/training-learning-to-rank-models-elasticsearch-ubi-data</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Alexander Dávila]]></dc:creator>
            <pubDate>Wed, 15 Oct 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/762bf09c28829d626d42c2cfadc719e1dd618d1b-1536x1024.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Eine große Herausforderung bei der Verwendung von <a href="https://www.elastic.co/docs/solutions/search/ranking/learning-to-rank-ltr"><strong><em>Learning-to-Rank-</em></strong></a> Modellen besteht darin, eine qualitativ hochwertige <a href="https://www.elastic.co/search-labs/blog/judgment-lists"><strong><em>Beurteilungsliste</em></strong></a> zu erstellen, mit der das Modell trainiert werden kann. Traditionell beinhaltet dieser Prozess eine <strong><em>manuelle</em></strong> Bewertung der Relevanz von Suchanfrage und Dokument, um jedem Dokument eine Note zuzuweisen. Dies ist ein langsamer Prozess, der sich nicht gut skalieren lässt und schwer zu pflegen ist (stellen Sie sich vor, Sie müssten eine Liste mit Hunderten von Einträgen manuell aktualisieren).</p><p>Was wäre, wenn wir die Interaktionen realer Nutzer mit unserer Suchanwendung nutzen könnten, um diese Trainingsdaten zu erstellen? Die Nutzung <a href="https://www.elastic.co/search-labs/blog/elasticsearch-plugin-user-behavior-insights"><strong><em>von UBI</em></strong></a> -Daten ermöglicht uns genau das. Entwicklung eines automatischen Systems, das unsere Suchanfragen, Klicks und sonstige Interaktionen erfassen und nutzen kann, um eine Bewertungsliste zu erstellen. Dieser Prozess lässt sich viel einfacher skalieren und wiederholen als eine manuelle Interaktion und führt tendenziell zu besseren Ergebnissen. In diesem Blogbeitrag werden wir untersuchen, wie wir in Elasticsearch gespeicherte UBI-Daten abfragen können, um aussagekräftige Signale zu berechnen und so einen Trainingsdatensatz für ein <a href="https://www.elastic.co/search-labs/blog/elasticsearch-learning-to-rank-introduction"><strong><em>LTR-</em></strong></a> Modell zu generieren.</p><p><strong><em>Das vollständige Experiment finden Sie <a href="https://github.com/Alex1795/elastic-ltr-judgement_list-blog.git">hier</a>.</em></strong></p><h2>Warum UBI-Daten für das Training Ihres LTR-Modells nützlich sein können</h2><p>UBI-Daten bieten gegenüber einer manuellen Annotation mehrere Vorteile:</p><ul><li><strong>Volumen:</strong> Da die Daten zum bedingungslosen Grundeinkommen aus realen Interaktionen stammen, können wir viel mehr Daten sammeln, als wir manuell generieren könnten. Dies setzt natürlich voraus, dass wir über genügend Traffic verfügen, um diese Daten zu generieren.</li><li><strong>Tatsächliche Nutzerabsicht:</strong> Traditionell basiert eine manuelle Beurteilungsliste auf der Auswertung der verfügbaren Daten durch Experten. Andererseits spiegeln UBI-Daten das tatsächliche Nutzerverhalten wider. Das bedeutet, dass wir bessere Trainingsdaten generieren können, die die Genauigkeit unseres Suchsystems verbessern, da sie darauf basieren, wie Benutzer tatsächlich mit Ihren Inhalten interagieren und einen Nutzen darin finden, anstatt auf theoretischen Annahmen darüber, was relevant sein sollte.</li><li><strong>Kontinuierliche Aktualisierungen:</strong> Beurteilungslisten müssen von Zeit zu Zeit aktualisiert werden. Wenn wir sie aus UBI-Daten erstellen, erhalten wir aktuelle Daten, die zu aktualisierten Urteilslisten führen.</li><li><strong>Kosteneffizienz:</strong> Da keine manuelle Erstellung einer Beurteilungsliste erforderlich ist, kann der Prozess beliebig oft effizient wiederholt werden.</li><li><strong>Natürliche Abfrageverteilung</strong>: UBI-Daten repräsentieren reale Benutzerabfragen, die tiefgreifendere Veränderungen bewirken können. Nutzen unsere Nutzer beispielsweise natürliche Sprache, um in unserem System zu suchen? In diesem Fall sollten wir möglicherweise einen semantischen Suchansatz oder einen hybriden Suchansatz implementieren.</li></ul><p>Es gibt allerdings auch einige Warnhinweise:</p><ul><li><strong>Verzerrungsverstärkung: </strong>Beliebte Inhalte erhalten mit größerer Wahrscheinlichkeit Klicks, einfach weil sie mehr Aufmerksamkeit erregen. Dies könnte dazu führen, dass beliebte Artikel verstärkt werden und bessere Alternativen möglicherweise in den Hintergrund treten.</li><li><strong>Unvollständige Abdeckung: </strong>Neuen Inhalten fehlen jegliche Interaktionen, daher ist es schwierig für sie, in den Suchergebnissen weit oben zu erscheinen. Bei seltenen Anfragen können zudem nicht genügend Datenpunkte vorhanden sein, um aussagekräftige Trainingsdaten zu erzeugen.</li><li><strong>Saisonale Schwankungen:</strong> Wenn Sie erwarten, dass sich das Nutzerverhalten im Laufe der Zeit drastisch ändert, geben historische Daten möglicherweise nicht viel Aufschluss darüber, was ein gutes Ergebnis ist.</li><li><strong>Aufgabenunklarheit:</strong> Ein Klick garantiert nicht immer, dass der Nutzer gefunden hat, wonach er gesucht hat.</li></ul><h2>Notenberechnung</h2><h3>Noten für LTR-Schulung</h3><p>Um LTR-Modelle zu trainieren, benötigen wir eine numerische Darstellung, die angibt, wie relevant ein Dokument für eine Suchanfrage ist. In unserer Implementierung handelt es sich bei dieser Zahl um einen kontinuierlichen Wert von 0,0 bis 5,0+, wobei höhere Werte eine höhere Relevanz anzeigen.</p><p>Um zu veranschaulichen, wie dieses Bewertungssystem funktioniert, betrachten Sie folgendes manuell erstellte Beispiel:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Abfrage</td><td style="border: 1px solid #ccc; padding: 4px;">Dokumentinhalt</td><td style="border: 1px solid #ccc; padding: 4px;">Grad</td><td style="border: 1px solid #ccc; padding: 4px;">Erläuterung</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">"bestes Pizza-Rezept"</td><td style="border: 1px solid #ccc; padding: 4px;">"Authentisches italienisches Pizzateigrezept mit Schritt-für-Schritt-Fotos"</td><td style="border: 1px solid #ccc; padding: 4px;">4.0</td><td style="border: 1px solid #ccc; padding: 4px;">Äußerst relevant, genau das, wonach der Nutzer sucht.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">"bestes Pizza-Rezept"</td><td style="border: 1px solid #ccc; padding: 4px;">„Geschichte der Pizza in Italien“</td><td style="border: 1px solid #ccc; padding: 4px;">1.0</td><td style="border: 1px solid #ccc; padding: 4px;">Es passt zwar thematisch, es geht um Pizza, ist aber kein Rezept.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">"bestes Pizza-Rezept"</td><td style="border: 1px solid #ccc; padding: 4px;">"Schnelles 15-Minuten-Pizza-Rezept für Anfänger"</td><td style="border: 1px solid #ccc; padding: 4px;">3.0</td><td style="border: 1px solid #ccc; padding: 4px;">Relevant, ein gutes Ergebnis, aber es verfehlt vielleicht das Ziel, das „beste“ Rezept zu sein.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">"bestes Pizza-Rezept"</td><td style="border: 1px solid #ccc; padding: 4px;">"Autowartungsleitfaden"</td><td style="border: 1px solid #ccc; padding: 4px;">0,0</td><td style="border: 1px solid #ccc; padding: 4px;">Überhaupt nicht relevant, steht in keinem Zusammenhang mit der Anfrage.</td>
                    </tr>
              </tbody>
            </table>
          <p>Wie wir hier sehen können, ist die Bewertung eine numerische Darstellung der Relevanz eines Dokuments für unsere Beispielanfrage nach dem „besten Pizza-Rezept“. Anhand dieser Werte kann unser LTR-Modell lernen, welche Dokumente in den Ergebnissen weiter oben angezeigt werden sollten.</p><p>Die Berechnung der Noten ist der Kern unseres Trainingsdatensatzes. Hierfür gibt es <a href="https://www.elastic.co/search-labs/blog/judgment-lists">verschiedene Ansätze</a> , jeder mit seinen eigenen Stärken und Schwächen. Wir könnten beispielsweise eine binäre Bewertung vergeben: 1 für relevant, 0 für nicht relevant. Oder wir könnten einfach die Anzahl der Klicks in einem Ergebnisdokument für jede Suchanfrage zählen.</p><p>In diesem Blogbeitrag werden wir einen anderen Ansatz verfolgen, <strong><em>indem wir das Nutzerverhalten als Eingabe betrachten und eine Note als Ausgabe berechnen</em></strong>. Wir werden auch Verzerrungen korrigieren, die dadurch entstehen könnten, dass höhere Ergebnisse tendenziell häufiger angeklickt werden, unabhängig von der Relevanz des Dokuments.</p><h2>Notenberechnung – COEC-Algorithmus</h2><p>Der COEC-Algorithmus (<a href="https://www.wsdm-conference.org/2010/proceedings/docs/p351.pdf">Clicks over Expected Clicks</a>) ist eine Methode zur Berechnung von Beurteilungsnoten aus den Klicks der Nutzer.<br/>Wie bereits erwähnt, neigen Nutzer dazu, auf weiter oben positionierte Ergebnisse zu klicken, selbst wenn das Dokument nicht das relevanteste für die Suchanfrage ist; dies wird als <a href="https://eugeneyan.com/writing/position-bias/">Positionsbias</a> bezeichnet. Die Grundidee des COEC-Algorithmus besteht darin, dass nicht alle Klicks gleich wichtig sind; ein Klick auf ein Dokument an Position 10 deutet darauf hin, dass das Dokument für die Suchanfrage viel relevanter ist als ein Klick auf ein Dokument an Position 1. Um die Forschungsarbeit zum COEC-Algorithmus (siehe Link oben) zu zitieren:</p><p><em>„Es ist bekannt, dass die Klickrate (CTR) von Suchergebnissen oder Anzeigen je nach Position der Ergebnisse deutlich abnimmt.“</em></p><p>Mehr zum Thema Positionsbias können Sie <a href="https://www.researchgate.net/publication/200110550_An_experimental_comparison_of_click_position-bias_models">hier</a> lesen.</p><p>Um dies mit dem COEC-Algorithmus zu lösen, gehen wir wie folgt vor:</p><p><strong>1. Festlegung von Positionsbaselines:</strong> Wir berechnen die Klickrate (CTR) für jede Suchposition von 1 bis 10. Das bedeutet, wir ermitteln, welcher Prozentsatz der Nutzer typischerweise auf Position 1, Position 2 usw. klickt. Dieser Schritt erfasst die natürliche Positionsverzerrung der Nutzer.<br/><br/>Wir berechnen die CTR wie folgt:</p><div class="katex">CTRp=CpIp</div><p>Wo:</p><p>p = Position. Von 1 bis 10<br/> Cp = Gesamtzahl der Klicks (auf beliebige Dokumente) an Position p über alle Abfragen hinweg<br/> Ip = Gesamteindrücke: Wie oft ein Dokument an Position p über alle Suchanfragen hinweg erschienen ist.</p><p>Hier gehen wir davon aus, dass höhere Positionen mehr Klicks erhalten.</p><p><strong>2.</strong> <strong>Berechnung der erwarteten Klicks (EC)</strong>:</p><p>Diese Kennzahl legt fest, wie viele Klicks ein Dokument basierend auf seinen Platzierungspositionen und der Klickrate (CTR) für diese Positionen hätte erhalten sollen. Wir berechnen EC wie folgt:</p><div class="katex">EC (for a document) = qQdCTRpos(d,q)</div><p>Wo:</p><p>Qd = Alle Anfragen, bei denen das Dokument d vorkam<br/> pos(d,q) = Position des Dokuments d in den Abfrageergebnissen q</p><p>3. <strong>Tatsächliche Klicks zählen: </strong>Wir zählen die tatsächliche Gesamtzahl der Klicks, die ein Dokument über alle Suchanfragen hinweg erhalten hat, bei denen es erschien, im Folgenden <strong>A(d) genannt.</strong></p><p>4. <strong>Berechnen Sie den COEC-Wert:</strong> Dies ist das Verhältnis der tatsächlichen Klicks (A(d)) zu den erwarteten Klicks (EC(d)):</p><div class="katex">COEC = A(d)EC(d)</div><p>Diese Metrik normalisiert Positionsverzerrungen folgendermaßen:</p><ul><li>Ein Wert von 1,0 bedeutet, dass das Dokument angesichts seiner Positionen genau wie erwartet funktioniert hat.</li><li>Ein Wert über 1,0 bedeutet, dass das Dokument im Vergleich zu den bisherigen Ergebnissen besser abgeschnitten hat. Dieses Dokument ist daher für die Anfrage relevanter.</li><li>Ein Wert unter 1,0 bedeutet, dass das Dokument im Vergleich zu den bisherigen Ergebnissen schlechter abgeschnitten hat. Dieses Dokument ist daher für die Anfrage weniger relevant.</li></ul><p><strong><em>Das Endergebnis ist eine Bewertungszahl, die das widerspiegelt, wonach die Nutzer suchen, wobei positionsbezogene Erwartungen berücksichtigt werden, die aus realen Interaktionen mit unserem Suchsystem abgeleitet wurden.</em></strong></p><h2>Technische Umsetzung</h2><p>Wir werden ein Skript erstellen, um eine Beurteilungsliste zu generieren, mit der ein LTR-Modell trainiert werden kann.</p><p>Die Eingabe für dieses Skript sind die in Elastic indexierten UBI-Daten (Abfragen und Ereignisse).</p><p>Das Ergebnis ist eine Beurteilungsliste in einer CSV-Datei, die aus diesen UBI-Dokumenten mithilfe des COEC-Algorithmus generiert wird. Diese Beurteilungsliste kann mit <a href="https://www.elastic.co/search-labs/blog/elasticsearch-learning-to-rank-introduction">Eland</a> verwendet werden, um relevante Merkmale zu extrahieren und ein LTR-Modell zu trainieren.</p><h3>Schnellstart</h3><p>Um aus den Beispieldaten in diesem Blog eine Bewertungsliste zu erstellen, können Sie folgende Schritte befolgen:</p><p>1. Klonen Sie das Repository:</p><pre><code>git clone https://github.com/Alex1795/elastic-ltr-judgement_list-blog.git  
cd elastic-ltr-judgement_list-blog</code></pre><p>2. Installieren Sie die erforderlichen Bibliotheken</p><p>Für dieses Skript benötigen wir die folgenden Bibliotheken:</p><ul><li><em>pandas</em>: um die Urteilsliste zu speichern</li><li><em>elasticsearch</em>: Um die UBI-Daten aus unserer Elastic-Bereitstellung zu erhalten</li></ul><p>Wir benötigen außerdem Python 3.11.</p><pre><code>pip install -r requirements.txt</code></pre><p>3. Aktualisieren Sie die Umgebungsvariablen für Ihre Elastic-Bereitstellung in einer <a href="https://github.com/Alex1795/elastic-ltr-judgement_list-blog/blob/main/.env-example">.env-Datei.</a></p><ul><li>ES_HOST</li><li>API-Schlüssel</li></ul><p>Um die Umgebungsvariablen hinzuzufügen, verwenden Sie:</p><pre><code>source .env</code></pre><p>4. Erstellen Sie die Indizes ubi_queries und ubi_events und laden Sie die Beispieldaten hoch. Führen Sie die Datei setup.py aus:</p><pre><code>python setup.py</code></pre><p>5. Führen Sie das Python-Skript aus:</p><pre><code>python judgement_list-generator.py</code></pre><p>Wenn Sie diese Schritte befolgen, sollte eine neue Datei namens judgement_list.csv erscheinen, die folgendermaßen aussieht:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2531090131ac9fe3e4e1d79de9d156fc47a7825a-782x531.png" style="width: 100%;" /><p>Dieses Skript berechnet die Noten unter Anwendung des zuvor erläuterten COEC-Algorithmus mithilfe der unten gezeigten Funktion <strong>calculate_relevance_grade()</strong> .</p><h2>Datenarchitektur</h2><h3>Ubi-Anfragen</h3><p>Unser UBI-Abfrageindex enthält Informationen über die in unserem Suchsystem ausgeführten Abfragen. Dies ist ein Beispieldokument:</p><pre><code>{
          "client_id": "client_002",
          "query": "italian pasta recipes",
          "query_attributes": {
            "search_type": "recipe",
            "category": "food",
            "cuisine": "italian"
          },
          "query_id": "q002",
          "query_response_id": "qr002",
          "query_response_object_ids": [
            "doc_011",
            "doc_012",
            "doc_013",
            "doc_014",
            "doc_015",
            "doc_016",
            "doc_017",
            "doc_018",
            "doc_019",
            "doc_020"
          ],
          "timestamp": "2024-08-14T11:15:00Z",
          "user_query": "italian pasta recipes"
        }</code></pre><p>Hier sehen wir Daten vom Benutzer (client_id), aus den Ergebnissen der Abfrage (query_response_object_ids) und die Abfrage selbst (timestamp, user_query).</p><h3>Ubi-Klickereignisse</h3><p>Unser ubi_events-Index enthält Daten von jedem Klick eines Nutzers auf ein Dokument in den Suchergebnissen. Dies ist ein Beispieldokument:</p><pre><code>{
          "action_name": "click",
          "application": "recipe_search",
          "client_id": "client_001",
          "event_attributes": {
            "object": {
              "description": "Authentic Italian Pizza Dough Recipe with Step-by-Step Photos",
              "device": "desktop",
              "object_id": "doc_001",
              "position": {
                "ordinal": 1,
                "page_depth": 1
              },
              "user": {
                "city": "New York",
                "country": "USA",
                "ip": "192.168.1.100",
                "location": {
                  "lat": 40.7128,
                  "lon": -74.006
                },
                "region": "NY"
              }
            }
          },
          "message": "User clicked on document doc_001",
          "message_type": "click",
          "query_id": "q001",
          "timestamp": "2024-08-14T10:31:00Z",
          "user_query": "best pizza recipe"
        }</code></pre><h2>Skript zur Generierung der Urteilsliste</h2><h3>Allgemeine Skriptübersicht</h3><p>Dieses Skript automatisiert die Generierung der Beurteilungsliste mithilfe von UBI-Daten aus Abfragen und Klickereignissen, die in Elasticsearch gespeichert sind. Es führt folgende Aufgaben aus:</p><ul><li>Ruft die UBI-Daten in Elasticsearch ab und verarbeitet sie.</li><li>Korreliert UBI-Ereignisse mit seinen Abfragen.</li><li>Berechnet die Klickrate (CTR) für jede Position.</li><li>Berechnet die erwarteten Klicks (EC) für jedes Dokument.</li><li>Zählt die tatsächlichen Klicks für jedes Dokument.</li><li>Berechnet den COEC-Score für jedes Abfrage-Dokument-Paar.</li><li>Erstellt eine Bewertungsliste und speichert diese in einer CSV-Datei.</li></ul><p>Lassen Sie uns die einzelnen Funktionen durchgehen:</p><h3>connect_to_elasticsearch()</h3><pre><code>def connect_to_elasticsearch(host, api_key):
    """Create and return Elasticsearch client"""
    try:
        es = Elasticsearch(
            hosts=[host],
            api_key=api_key,
            request_timeout=60
        )
        # Test the connection
        if es.ping():
            print(f"✓ Successfully connected to Elasticsearch at {host}")
            return es
        else:
            print("✗ Failed to connect to Elasticsearch")
            return None
    except Exception as e:
        print(f"✗ Error connecting to Elasticsearch: {e}")
        return None</code></pre><p>Diese Funktion gibt ein Elasticsearch-Clientobjekt unter Verwendung des Hosts und des API-Schlüssels zurück.</p><h3>fetch_ubi_data()</h3><pre><code>def fetch_ubi_data(es_client: Elasticsearch, queries_index: str, events_index: str,
                   size: int = 10000) -> Tuple[List[Dict], List[Dict]]:
    """
    Fetch UBI queries and events data from Elasticsearch indices.

    Args:
        es_client: Elasticsearch client
        queries_index: Name of the UBI queries index
        events_index: Name of the UBI events index
        size: Maximum number of documents to fetch

    Returns:
        Tuple of (queries_data, events_data)
    """
    logger.info(f"Fetching data from {queries_index} and {events_index}")

    # Fetch queries with error handling
    try:
        queries_response = es_client.search(
            index=queries_index,
            body={
                "query": {"match_all": {}},
                "size": size
            }
        )
        queries_data = [hit['_source'] for hit in queries_response['hits']['hits']]
        logger.info(f"Fetched {len(queries_data)} queries")

    except Exception as e:
        logger.error(f"Error fetching queries from {queries_index}: {e}")
        raise

    # Fetch events (only click events for now) with error handling
    try:
        events_response = es_client.search(
            index=events_index,
            body={
                "query": {
                    "term": {"message_type.keyword": "CLICK_THROUGH"}
                },
                "size": size
            }
        )
        events_data = [hit['_source'] for hit in events_response['hits']['hits']]
        logger.info(f"Fetched {len(events_data)} click events")

    except Exception as e:
        logger.error(f"Error fetching events from {events_index}: {e}")
        raise

    logger.info(f"Data fetch completed successfully - Queries: {len(queries_data)}, Events: {len(events_data)}")

    return queries_data, events_data</code></pre><p>Diese Funktion ist die Datenextraktionsschicht; sie stellt eine Verbindung zu Elasticsearch her, um UBI-Abfragen mittels einer match_all-Abfrage abzurufen und filtert UBI-Ereignisse, um nur &#x27;CLICK_THROUGH&#x27;-Ereignisse zu erhalten.</p><h3>process_ubi_data()</h3><pre><code>def process_ubi_data(queries_data: List[Dict], events_data: List[Dict]) -> pd.DataFrame:
    """
    Process UBI data and generate judgment list.

    Args:
        queries_data: List of query documents from UBI queries index
        events_data: List of event documents from UBI events index

    Returns:
        DataFrame with judgment list (qid, docid, grade, keywords)
    """
    logger.info("Processing UBI data to generate judgment list")

    # Group events by query_id
    clicks_by_query = {}
    for event in events_data:
        query_id = event['query_id']
        if query_id not in clicks_by_query:
            clicks_by_query[query_id] = {}

        # Extract clicked document info
        object_id = event['event_attributes']['object']['object_id']
        position = event['event_attributes']['object']['position']['ordinal']

        clicks_by_query[query_id][object_id] = {
            'position': position,
            'timestamp': event['timestamp']
        }

    judgment_list = []

    # Process each query
    for query in queries_data:
        query_id = query['query_id']
        user_query = query['user_query']
        document_ids = query['query_response_object_ids']

        # Get clicks for this query
        query_clicks = clicks_by_query.get(query_id, {})

        # Generate judgment for each document shown
        for doc_id in document_ids:
            grade = calculate_relevance_grade(doc_id, query_clicks, document_ids, queries_data, events_data)

            judgment_list.append({
                'qid': query_id,
                'docid': doc_id,
                'grade': grade,
                'query': user_query
            })

    df = pd.DataFrame(judgment_list)
    logger.info(f"Generated {len(df)} judgment entries for {df['qid'].nunique()} unique queries")

    return df</code></pre><p>Diese Funktion ist für die Generierung der Urteilsliste zuständig. Die Verarbeitung der UBI-Daten beginnt mit der Verknüpfung von UBI-Ereignissen und -Abfragen. Anschließend wird für jedes Dokument-Abfrage-Paar die Funktion calculate_relevance_grade() aufgerufen, um die Einträge für die Bewertungsliste zu erhalten. Schließlich gibt es die resultierende Liste als Pandas-DataFrame zurück.</p><h3>calculate_relevance_grade()</h3><pre><code>def calculate_relevance_grade(document_id: str, clicks_data: Dict,
                              query_response_ids: List[str], all_queries_data: List[Dict] = None,
                              all_events_data: List[Dict] = None) -> float:
    """
    Calculate COEC (Click Over Expected Clicks) relevance score for a document.

    Args:
        document_id: ID of the document
        clicks_data: Dictionary of clicked documents with their positions for current query
        query_response_ids: List of document IDs shown in search results (ordered by position)
        all_queries_data: All queries data for calculating position CTR averages
        all_events_data: All events data for calculating position CTR averages

    Returns:
        COEC relevance score (continuous value, typically 0.0 to 5.0+)
    """

    # If no global data provided, fall back to simple position-based grading
    if all_queries_data is None or all_events_data is None:
        logger.warning("No global data provided, falling back to position-based grading")
        # Simple fallback logic
        if document_id in clicks_data:
            position = clicks_data[document_id]['position']
            if position > 3:
                return 4.0
            elif position >= 1 and position <= 3:
                return 3.0
        if document_id in query_response_ids:
            position = query_response_ids.index(document_id) + 1
            if position <= 5:
                return 2.0
            elif position >= 6 and position <= 10:
                return 1.0
        return 0.0

    # Calculate rank-aggregated click-through rates
    position_ctr_averages = {}
    position_impression_counts = {}
    position_click_counts = {}

    # Initialize counters
    for pos in range(1, 11):  # Positions 1-10
        position_impression_counts[pos] = 0
        position_click_counts[pos] = 0

    # Count impressions (every document shown contributes)
    for query in all_queries_data:
        for i, doc_id in enumerate(query['query_response_object_ids'][:10]):  # Top 10 positions
            position = i + 1
            position_impression_counts[position] += 1

    # Count clicks by position
    for event in all_events_data:
        if event.get('action_name') == 'click':
            position = event['event_attributes']['object']['position']['ordinal']
            if position <= 10:
                position_click_counts[position] += 1

    # Calculate average CTR per position
    for pos in range(1, 11):
        if position_impression_counts[pos] > 0:
            position_ctr_averages[pos] = position_click_counts[pos] / position_impression_counts[pos]
        else:
            position_ctr_averages[pos] = 0.0

    # Calculate expected clicks for this specific document
    expected_clicks = 0.0

    # Count how many times this document appeared at each position for any query
    for query in all_queries_data:
        if document_id in query['query_response_object_ids']:
            position = query['query_response_object_ids'].index(document_id) + 1
            if position <= 10:
                expected_clicks += position_ctr_averages[position]

    # Count total actual clicks for this document across all queries
    actual_clicks = 0
    for event in all_events_data:
        if (event.get('action_name') == 'click' and
                event['event_attributes']['object']['object_id'] == document_id):
            actual_clicks += 1

    # Calculate COEC score
    if expected_clicks > 0:
        coec_score = actual_clicks / expected_clicks
    else:
        coec_score = 0.0

    logger.debug(
        f"Document {document_id}: {actual_clicks} clicks / {expected_clicks:.3f} expected = {coec_score:.3f} COEC")

    return coec_score</code></pre><p>Dies ist die Funktion, die den COEC-Algorithmus implementiert. Es berechnet die Klickrate (CTR) für jede Position, vergleicht dann die tatsächlichen Klicks für ein Dokument-Abfrage-Paar und berechnet schließlich den tatsächlichen COEC-Wert für jedes Paar.</p><h3>generate_judgment_statistics()</h3><pre><code>def generate_judgment_statistics(df: pd.DataFrame) -> Dict:
    """Generate statistics about the judgment list."""
    stats = {
        'total_judgments': len(df),
        'unique_queries': df['qid'].nunique(),
        'unique_documents': df['docid'].nunique(),
        'grade_distribution': df['grade'].value_counts().to_dict(),
        'avg_judgments_per_query': len(df) / df['qid'].nunique() if df['qid'].nunique() > 0 else 0,
        'queries_with_clicks': len(df[df['grade'] > 1]['qid'].unique()),
        'click_through_rate': len(df[df['grade'] > 1]) / len(df) if len(df) > 0 else 0
    }
    return stats</code></pre><p>Es generiert nützliche Statistiken aus der Bewertungsliste, wie z. B. die Gesamtzahl der Anfragen, die Gesamtzahl der eindeutigen Dokumente oder die Notenverteilung. Dies dient lediglich der Information und hat keinen Einfluss auf die endgültige Urteilsliste.</p><h2>Ergebnisse und Auswirkungen</h2><p>Wenn Sie die Anweisungen im Abschnitt „Schnellstart“ befolgen, sollte eine CSV-Datei mit einer Urteilsliste mit 320 Einträgen angezeigt werden (ein <a href="https://github.com/Alex1795/elastic-ltr-judgement_list-blog/blob/main/judgment_list.csv">Beispiel</a> finden Sie im Repository). Mit diesen Feldern:</p><ul><li>qid: eindeutige ID der Abfrage</li><li>docid: eindeutige Kennung für ein resultierendes Dokument</li><li>Note: die berechnete Note für das Abfrage-Dokument-Paar</li><li>Anfrage: Die Benutzeranfrage</li></ul><p> Schauen wir uns die Ergebnisse der Suchanfrage „Italienische Rezepte“ an:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">qid</td><td style="border: 1px solid #ccc; padding: 4px;">docid</td><td style="border: 1px solid #ccc; padding: 4px;">Grad</td><td style="border: 1px solid #ccc; padding: 4px;">Abfrage</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">q1-italienische-rezepte</td><td style="border: 1px solid #ccc; padding: 4px;">Grundrezept für Pasta</td><td style="border: 1px solid #ccc; padding: 4px;">0,0</td><td style="border: 1px solid #ccc; padding: 4px;">Italienische Rezepte</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">q1-italienische-rezepte</td><td style="border: 1px solid #ccc; padding: 4px;">Rezept_Pizza_Margherita</td><td style="border: 1px solid #ccc; padding: 4px;">3,333333</td><td style="border: 1px solid #ccc; padding: 4px;">Italienische Rezepte</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">q1-italienische-rezepte</td><td style="border: 1px solid #ccc; padding: 4px;">Rezept-Risotto-Anleitung</td><td style="border: 1px solid #ccc; padding: 4px;">10.0</td><td style="border: 1px solid #ccc; padding: 4px;">Italienische Rezepte</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">q1-italienische-rezepte</td><td style="border: 1px solid #ccc; padding: 4px;">Rezept_französisches_Croissant</td><td style="border: 1px solid #ccc; padding: 4px;">0,0</td><td style="border: 1px solid #ccc; padding: 4px;">Italienische Rezepte</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">q1-italienische-rezepte</td><td style="border: 1px solid #ccc; padding: 4px;">Rezept_spanische_Paella</td><td style="border: 1px solid #ccc; padding: 4px;">0,0</td><td style="border: 1px solid #ccc; padding: 4px;">Italienische Rezepte</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">q1-italienische-rezepte</td><td style="border: 1px solid #ccc; padding: 4px;">Rezept_griechische_Moussaka</td><td style="border: 1px solid #ccc; padding: 4px;">1,875</td><td style="border: 1px solid #ccc; padding: 4px;">Italienische Rezepte</td>
                    </tr>
              </tbody>
            </table>
          <p>Aus den Ergebnissen geht hervor, dass für die Suchanfrage „Italienische Rezepte“ Folgendes gilt:</p><ul><li>Das Risotto-Rezept ist definitiv das beste Ergebnis der Suchanfrage und erhielt zehnmal mehr Klicks als erwartet.</li><li>Auch die Pizza Margherita ist ein hervorragendes Ergebnis.</li><li>Die griechische Moussaka erzielt (überraschenderweise) ebenfalls ein gutes Ergebnis und schneidet besser ab, als ihre Platzierung in der Ergebnisliste vermuten lässt. Das bedeutet, dass einige Nutzer, die nach italienischen Rezepten suchten, stattdessen an diesem Rezept interessiert waren. Vielleicht interessieren sich diese Nutzer generell für mediterrane Gerichte. Letztendlich bedeutet dies, dass es sich um ein gutes Ergebnis handeln könnte, das unter den beiden anderen, oben besprochenen, &quot;besseren&quot; Treffern angezeigt werden könnte.</li></ul><h2>Fazit</h2><p>Die Verwendung von UBI-Daten ermöglicht es uns, das Training von LTR-Modellen zu automatisieren und so qualitativ hochwertige Beurteilungslisten aus unseren eigenen Nutzern zu erstellen. Die UBI-Daten liefern einen großen Datensatz, der die Nutzung unseres Suchsystems widerspiegelt. Durch die Verwendung des COEC-Algorithmus zur Generierung der Noten berücksichtigen wir inhärente Verzerrungen und spiegeln gleichzeitig wider, was ein Benutzer als besseres Ergebnis ansieht. Die hier beschriebene Methode kann auf reale Anwendungsfälle angewendet werden, um ein besseres Sucherlebnis zu bieten, das sich mit den tatsächlichen Nutzungstrends weiterentwickelt.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Evaluierung von KI-Agenten: Wie Elastic agentenbasierte Frameworks testet]]></title>
            <description><![CDATA[Erfahren Sie, wie wir Änderungen an einem agentenbasierten System bewerten und testen, bevor wir sie für Elastic-Benutzer freigeben, um genaue und überprüfbare Ergebnisse zu gewährleisten.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/ai-agent-evaluation-elastic</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/ai-agent-evaluation-elastic</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Susan Chang, Abhimanyu Anand]]></dc:creator>
            <pubDate>Mon, 13 Oct 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e681aab6ee77dfef38dc95e36c8535dfe7bcc105-721x420.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<h2>Einleitung</h2><p>Im Elastic Stack gibt es viele LLM-gestützte agentenbasierte Anwendungen, wie zum Beispiel den kommenden Elastic AI Agent im<a href="https://www.elastic.co/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder"> Agent Builder</a> (derzeit in der technischen Vorschau) und <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> in 8.18 und 9.0+), und weitere sind in Arbeit. Während der Entwicklung und auch nach der Bereitstellung ist es wichtig, diese Fragen zu beantworten:</p><ul><li>Wie schätzen wir die Qualität der Antworten dieser KI-Anwendungen ein?</li><li>Wenn wir eine Änderung vornehmen, wie können wir garantieren, dass diese Änderung tatsächlich eine Verbesserung darstellt und keine Verschlechterung der Benutzererfahrung zur Folge hat?</li><li>Wie können wir diese Ergebnisse auf einfache und reproduzierbare Weise testen?</li></ul><p>Im Gegensatz zu herkömmlichen Softwaretests erfordert die Evaluierung von generativen KI-Anwendungen statistische Methoden, eine differenzierte qualitative Überprüfung und ein tiefes Verständnis der Ziele der Nutzer.</p><p>Dieser Artikel beschreibt detailliert den Prozess, den das Elastic-Entwicklerteam anwendet, um Evaluierungen durchzuführen, die Qualität der Änderungen vor der Bereitstellung sicherzustellen und die Systemleistung zu überwachen. Unser Ziel ist es, sicherzustellen, dass jede Änderung durch Beweise untermauert wird, was zu verlässlichen und nachvollziehbaren Ergebnissen führt. Ein Teil dieses Prozesses ist direkt in Kibana integriert und spiegelt damit unser Bekenntnis zu Transparenz als Teil unseres Open-Source-Ethos wider. Durch die offene Weitergabe von Teilen unserer Evaluierungsdaten und Kennzahlen wollen wir das Vertrauen der Community stärken und einen klaren Rahmen für alle bieten, die KI-Agenten entwickeln oder unsere Produkte nutzen.</p><h2>Produktbeispiele</h2><p>Die in diesem Dokument verwendeten Methoden bildeten die Grundlage für unsere iterative Weiterentwicklung und Verbesserung von Lösungen wie Attack Discovery und Elastic AI Agent. Eine kurze Vorstellung der beiden:</p><h3>Angriffserkennung von Elastic Security</h3><p>Attack Discovery verwendet LLMs, um Angriffssequenzen in Elastic zu identifizieren und zusammenzufassen. Anhand der Elastic Security-Warnmeldungen in einem bestimmten Zeitraum (standardmäßig 24 Stunden) ermittelt der agentenbasierte Workflow von Attack Discovery automatisch, ob ein oder mehrere Angriffe stattgefunden haben, sowie wichtige Informationen darüber, welcher Host oder welche Benutzer kompromittiert wurden und welche Warnmeldungen zu dieser Schlussfolgerung beigetragen haben.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/20fabb47642dad7b588daaaa8c3a98de860ad01d-1251x758.png" style="width: 100%;" /><p></p><p>Ziel ist es, dass die LLM-basierte Lösung ein Ergebnis liefert, das mindestens so gut ist wie das eines Menschen.</p><h3>Elastischer KI-Agent</h3><p>Der <strong>Elastic Agent Builder</strong> ist unsere neue Plattform zum Erstellen kontextsensitiver KI-Agenten, die alle unsere Suchfunktionen nutzen. Es beinhaltet den <strong>Elastic AI Agent</strong>, einen vorkonfigurierten, universell einsetzbaren Agenten, der Benutzern dabei hilft, ihre Daten durch dialogbasierte Interaktion zu verstehen und Antworten darauf zu erhalten.</p><p>Der Agent erreicht dies, indem er automatisch relevante Informationen innerhalb von Elasticsearch oder verbundenen Wissensdatenbanken identifiziert und eine Reihe vorgefertigter Tools nutzt, um mit diesen zu interagieren. Dies versetzt den Elastic AI Agent in die Lage, auf ein breites Spektrum von Benutzeranfragen zu reagieren, von einfachen Fragen und Antworten zu einem einzelnen Dokument bis hin zu komplexen Anfragen, die eine Aggregation und ein- oder mehrstufige Suchvorgänge über mehrere Indizes hinweg erfordern.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d29dee100bb8a17bb623acd745773a5164a1df4f-1600x1014.png" style="width: 100%;" /><h2>Verbesserungen durch Experimente messen</h2><p>Im Kontext von KI-Agenten ist ein Experiment eine strukturierte, testbare Änderung des Systems, die darauf abzielt, die Leistung in genau definierten Dimensionen (z. B. Hilfreichkeit, Korrektheit, Latenz) zu verbessern. Ziel ist es, die folgende Frage endgültig zu beantworten: „Wenn wir diese Änderung umsetzen, können wir dann garantieren, dass es sich um eine echte Verbesserung handelt und die Benutzererfahrung nicht verschlechtert wird?“</p><p>Die meisten unserer Experimente umfassen im Allgemeinen Folgendes:</p><ul><li><strong>Eine Hypothese:</strong> Eine spezifische und widerlegbare Behauptung. <em>Beispiel:</em> „Das Hinzufügen des Zugriffs auf ein Tool zur Angriffserkennung verbessert die Korrektheit sicherheitsrelevanter Abfragen.“</li><li><strong>Erfolgskriterien:</strong> Klare Schwellenwerte, die definieren, was „Erfolg“ bedeutet. <em>Beispiel:</em> „+5 % Verbesserung der Korrektheitsbewertung im Sicherheitsdatensatz, keine Verschlechterung in anderen Bereichen.“</li><li><strong>Evaluierungsplan:</strong> Wie wir den Erfolg messen (Kennzahlen, Datensätze, Vergleichsmethode)</li></ul><p>Ein erfolgreiches Experiment ist ein systematischer Forschungsprozess. Jede Änderung, von einer kleinen, spontanen Anpassung bis hin zu einer grundlegenden architektonischen Umgestaltung, durchläuft diese sieben Schritte, um sicherzustellen, dass die Ergebnisse aussagekräftig und umsetzbar sind:</p><ul><li>Schritt 1: Identifizieren Sie das Problem</li><li>Schritt 2: Kennzahlen definieren</li><li>Schritt 3: Formulieren Sie eine klare Hypothese</li><li>Schritt 4: Vorbereitung des Auswertungsdatensatzes</li><li>Schritt 5: Führen Sie das Experiment durch.</li><li>Schritt 6: Ergebnisse analysieren + wiederholen</li><li>Schritt 7: Treffen Sie eine Entscheidung und dokumentieren Sie diese.</li></ul><p>Ein Beispiel für diese Schritte ist in <em>Abbildung 1</em> dargestellt. In den folgenden Unterabschnitten werden die einzelnen Schritte erläutert, und die technischen Details der einzelnen Schritte werden wir in den folgenden Dokumenten genauer ausführen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3a9f5a3e92dd4922a795a19104c6e4ad8c98958d-2400x1352.png" style="width: 100%;" /><h2>Schrittweise Anleitung mit echten Elastic-Beispielen</h2><h3>Schritt 1: Identifizieren Sie das Problem</h3><p><em>Welches Problem soll durch diese Änderung genau gelöst werden?</em></p><p>Beispiel für die Angriffserkennung: Die Zusammenfassungen sind gelegentlich unvollständig, oder harmlose Aktivitäten werden fälschlicherweise als Angriff eingestuft (falsch positive Ergebnisse).</p><p>Beispiel für einen Elastic AI Agent: Die Werkzeugauswahl des Agenten, insbesondere bei analytischen Abfragen, ist suboptimal und inkonsistent, was häufig dazu führt, dass das falsche Werkzeug ausgewählt wird. Dies wiederum erhöht die Tokenkosten und die Latenz.</p><h3>Schritt 2: Kennzahlen definieren</h3><p><em>Das Problem muss messbar gemacht werden, damit wir eine Veränderung mit dem aktuellen Zustand vergleichen können.</em></p><p>Gängige Metriken sind <a href="https://developers.google.com/machine-learning/crash-course/classification/accuracy-precision-recall">Präzision und Trefferquote</a>, <a href="https://en.wikipedia.org/wiki/Semantic_similarity">semantische Ähnlichkeit</a>, Faktentreue usw. Je nach Anwendungsfall verwenden wir Code-Checks, um die Metriken zu berechnen, wie z. B. übereinstimmende Alarm-IDs oder korrekt abgerufene URLs, oder wir verwenden Techniken wie LLM-as-judge für freiere Antworten.</p><p>Nachfolgend sind einige (<em>nicht vollständige</em>) Beispielmetriken aufgeführt, die in den Experimenten verwendet wurden:</p><p><strong>Angriffserkennung</strong></p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Metrisch</td><td style="border: 1px solid #ccc; padding: 4px;">Beschreibung</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Präzision und Rückruf</td><td style="border: 1px solid #ccc; padding: 4px;">Vergleichen Sie die Alarm-IDs zwischen tatsächlichen und erwarteten Ausgaben, um die Erkennungsgenauigkeit zu messen.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Ähnlichkeit</td><td style="border: 1px solid #ccc; padding: 4px;">Verwenden Sie BERTScore, um die semantische Ähnlichkeit des Antworttextes zu vergleichen.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Faktentreue</td><td style="border: 1px solid #ccc; padding: 4px;">Sind wichtige IOCs (Indikatoren für eine Kompromittierung) vorhanden? Werden die MITRE-Taktiken (Branchenklassifizierung von Angriffen) korrekt abgebildet?</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Konsistenz der Angriffskette</td><td style="border: 1px solid #ccc; padding: 4px;">Vergleichen Sie die Anzahl der Entdeckungen, um festzustellen, ob der Angriff über- oder untererfasst gemeldet wurde.</td>
                    </tr>
              </tbody>
            </table>
          <p><strong>Elastischer KI-Agent</strong></p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Metrisch</td><td style="border: 1px solid #ccc; padding: 4px;">Beschreibung</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Präzision und Rückruf</td><td style="border: 1px solid #ccc; padding: 4px;">Um die Genauigkeit des Informationsabrufs zu messen, werden die vom Agenten abgerufenen Dokumente/Informationen mit den tatsächlich benötigten Informationen oder Dokumenten abgeglichen, die zur Beantwortung der Anfrage erforderlich sind.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Faktentreue</td><td style="border: 1px solid #ccc; padding: 4px;">Sind die zur Beantwortung der Benutzeranfrage erforderlichen Schlüsselinformationen vorhanden? Sind die Fakten in der richtigen Reihenfolge für verfahrenstechnische Anfragen?</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Relevanz der Antwort</td><td style="border: 1px solid #ccc; padding: 4px;">Enthält die Antwort Informationen, die für die Benutzeranfrage nebensächlich oder nicht relevant sind?</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Vollständigkeit der Antwort</td><td style="border: 1px solid #ccc; padding: 4px;">Beantwortet die Antwort alle Teile der Benutzeranfrage? Enthält die Antwort alle Informationen, die auch in den Referenzdaten vorhanden sind?</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">ES|QL-Validierung</td><td style="border: 1px solid #ccc; padding: 4px;">Ist der generierte ES|QL-Code syntaktisch korrekt? Ist es funktional identisch mit dem tatsächlichen ES|QL-Standard?</td>
                    </tr>
              </tbody>
            </table>
          <h3>Schritt 3: Formulieren Sie eine klare Hypothese</h3><p><em>Legen Sie anhand des Problems und der oben definierten Kennzahlen klare Erfolgskriterien fest.</em></p><p>Beispiel für einen elastischen KI-Agenten:</p><ol><li>Nehmen Sie <strong>Änderungen an den Beschreibungen der Tools relevance_search und nl_search vor, um deren spezifische Funktionen und Anwendungsfälle klar zu definieren</strong>.</li><li>Wir gehen davon aus, dass wir <strong>die</strong> <strong>Genauigkeit</strong> unserer Tool-Aufrufe um <strong>25 %</strong> verbessern werden.</li><li>Wir werden überprüfen, ob dies insgesamt positiv ist, indem wir sicherstellen, dass es keine negativen Auswirkungen auf andere Kennzahlen gibt, z. B. <strong>Faktentreue und Vollständigkeit</strong>.</li><li>Wir glauben, dass dies funktionieren wird, da <strong>präzise Werkzeugbeschreibungen dem Agenten helfen, das am besten geeignete Suchwerkzeug für verschiedene Anfragetypen genauer auszuwählen und anzuwenden, wodurch Fehlanwendungen reduziert und die Gesamteffektivität der Suche verbessert wird</strong>.</li></ol><h3>Schritt 4: Vorbereitung des Auswertungsdatensatzes</h3><p><em>Um die Leistungsfähigkeit des Systems zu messen, verwenden wir Datensätze, die realweltliche Szenarien abbilden.</em></p><p>Je nach Art der durchgeführten Evaluierung benötigen wir möglicherweise unterschiedliche Datenformate, wie z. B. Rohdaten, die einem LLM zugeführt werden (z. B. Angriffsszenarien für die Angriffserkennung) und erwartete Ergebnisse. Wenn es sich bei der Anwendung um einen Chatbot handelt, dann können die Eingaben Benutzeranfragen sein und die Ausgaben korrekte Chatbot-Antworten, korrekte Links, die er hätte abrufen sollen, und so weiter.</p><p>Beispiel für Angriffserkennung:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">10 neuartige Angriffsszenarien</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">8 Folgen von Oh My Malware (ohmymalware.com)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">4 Szenarien mit mehreren Angriffen (entstanden durch die Kombination von Angriffen aus den ersten beiden Kategorien)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">3 harmlose Szenarien</td>
                    </tr>
              </tbody>
            </table>
          <p>Beispiel eines Evaluierungsdatensatzes für Elastic AI-Agenten (<a href="https://github.com/elastic/kibana/blob/main/x-pack/platform/packages/shared/onechat/kbn-evals-suite-onechat/evals/kb/kb.spec.ts">Kibana-Datensatzlink</a>):</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">14 Indizes, die Open-Source-Datensätze verwenden, um mehrere Quellen in KB zu simulieren.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">5 Abfragetypen (analytisch, Textabfrage, hybrid…)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">7 Arten von Abfrageabsichten (prozedural, faktisch - Klassifizierung, investigativ; …)</td>
                    </tr>
              </tbody>
            </table>
          <h3>Schritt 5: Führen Sie das Experiment durch.</h3><p>Führen Sie das Experiment durch, indem Sie Antworten sowohl vom bestehenden Agenten als auch von der modifizierten Version gegen den Evaluierungsdatensatz generieren. Berechnen Sie Kennzahlen wie Faktentreue (siehe Schritt 2).</p><p>Wir kombinieren verschiedene Auswertungen auf Basis der in Schritt 2 geforderten Kennzahlen:</p><ul><li>Regelbasierte Auswertung (z. B. (mit Python/TypeScript prüfen, ob die .json-Datei gültig ist)</li><li>LLM als Richter (Befragung eines anderen LLM, ob eine Antwort sachlich mit einem Quelldokument übereinstimmt)</li><li>Menschliche Beteiligung an der Qualitätsprüfung zur Feinabstimmung</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8648e75818d3291f0ac66f069438a500d42b8225-1600x1099.png" style="width: 100%;" /><h3>Schritt 6: Ergebnisse analysieren + wiederholen</h3><p>Nachdem wir nun die Kennzahlen haben, analysieren wir die Ergebnisse. <em><span style="text-decoration:underline">Auch wenn die Ergebnisse die in Schritt 3 definierten Erfolgskriterien erfüllen, werden wir vor der Übernahme der Änderung in die Produktionsumgebung noch eine manuelle Überprüfung durchführen</span></em>; wenn die Ergebnisse die Kriterien nicht erfüllen, werden die Probleme behoben und anschließend die Auswertungen der neuen Änderung durchgeführt.</p><p>Wir gehen davon aus, dass einige Iterationen nötig sein werden, um die beste Änderung vor dem Zusammenführen zu finden. Ähnlich wie bei der Durchführung lokaler Softwaretests vor dem Pushen eines Commits können Offline-Evaluierungen mit lokalen Änderungen oder mehreren vorgeschlagenen Änderungen durchgeführt werden. Es ist hilfreich, das Speichern von Experimentergebnissen, Gesamtergebnissen und Visualisierungen zu automatisieren, um die Analyse zu optimieren.</p><h3>Schritt 7: Treffen Sie eine Entscheidung und dokumentieren Sie diese.</h3><p>Auf Basis eines Entscheidungsrahmens und Akzeptanzkriterien wird über die Übernahme der Änderung entschieden und das Experiment dokumentiert. Die Entscheidungsfindung ist vielschichtig und kann Faktoren berücksichtigen, die über den Auswertungsdatensatz hinausgehen, wie z. B. die Prüfung auf Regressionsszenarien in anderen Datensätzen oder die Abwägung des Kosten-Nutzen-Verhältnisses einer vorgeschlagenen Änderung.</p><p>Beispiel: Nach dem Testen und Vergleichen einiger Iterationen wählen Sie die Änderung mit der höchsten Punktzahl aus und senden sie zur Genehmigung an Produktmanager und andere relevante Stakeholder. Fügen Sie die Ergebnisse der vorherigen Schritte bei, um die Entscheidungsfindung zu erleichtern. Weitere Beispiele zum Thema Angriffserkennung finden Sie <a href="https://www.elastic.co/blog/elastic-security-generative-ai-features">unter Hinter den Kulissen der generativen KI-Funktionen von Elastic Security</a>.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/74c80b8f34dce8ddd20873ecb2f553873587ed35-1600x618.png" style="width: 100%;" /><h2>Fazit</h2><p>In diesem Blog haben wir den gesamten Ablauf eines Experiment-Workflows durchlaufen und veranschaulicht, wie wir Änderungen an einem agentenbasierten System bewerten und testen, bevor wir sie für Elastic-Benutzer freigeben. Wir haben auch einige Beispiele für die Verbesserung agentenbasierter Arbeitsabläufe in Elastic vorgestellt. In nachfolgenden Blogbeiträgen werden wir die Details verschiedener Schritte genauer erläutern, beispielsweise wie man einen guten Datensatz erstellt, wie man zuverlässige Metriken entwirft und wie man Entscheidungen trifft, wenn mehrere Metriken involviert sind.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Verbindung von Elastic Agents mit Gemini Enterprise über das A2A-Protokoll]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie mit Agent Builder Ihren benutzerdefinierten Elastic Agent externen Diensten wie Gemini Enterprise mithilfe des A2A-Protokolls zugänglich machen.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/a2a-protocol-elastic-agent-builder-gemini-enterprise</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/a2a-protocol-elastic-agent-builder-gemini-enterprise</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jedr Blaszyk, Valerio Arvizzigno, Joe McElroy]]></dc:creator>
            <pubDate>Thu, 09 Oct 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5be8a425fab55dca2f9717d2e50812b0450fa625-1440x840.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p><strong>Elastic Agent Builder</strong> ist eine Reihe von Funktionen zur Erstellung datengesteuerter KI-Agenten direkt in Elasticsearch. In früheren Beiträgen dieser <a href="https://www.elastic.co/search-labs/blog/series/context-aware-ai-agentic-workflows-with-elastic">Reihe</a> haben wir gezeigt, wie man benutzerdefinierte Agenten mit Werkzeugen ausstattet, um komplexe Aufgaben auszuführen, und ihnen eine Reihe benutzerdefinierter Anweisungen zur Verfügung stellt, um ihr Verhalten zu steuern.</p><p>Was aber, wenn Sie Ihre benutzerdefinierten Agenten mit den Anwendungen und Produktivitätstools verwenden möchten, auf die Sie bereits angewiesen sind?</p><p>Hier kommt das <strong>Agent-to-Agent (A2A)-Protokoll</strong> ins Spiel. A2A ist ein <a href="https://github.com/a2aproject/A2A">offener Standard</a> für Interoperabilität, der es Agenten verschiedener Plattformen ermöglicht, miteinander zu kommunizieren und zusammenzuarbeiten. Und wir haben es direkt in den Elastic Agent Builder integriert.</p><p>Heute zeigen wir Ihnen, wie Sie einen von Ihnen erstellten benutzerdefinierten Agenten anderen Diensten, insbesondere <strong>Gemini Enterprise </strong>(ehemals Agentspace), zugänglich machen können.</p><h2>Die Macht offener Standards: Warum A2A wichtig ist</h2><p>Im Blogbeitrag <a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch">„Ihr erster Elastic Agent“</a> haben wir gezeigt, wie man benutzerdefinierte Agenten erstellt, zum Beispiel einen <em>Finanzassistenten</em> -Agenten mit sicherem Zugriff auf Ihre Marktdaten. Sein Nutzen ist jedoch begrenzt, wenn Sie die gewonnenen Erkenntnisse nicht auch in anderen Umgebungen wie Gemini Enterprise nutzen können, ohne Ihre Arbeit neu aufbauen zu müssen.</p><p>Diese Herausforderung der Interoperabilität ist es, die agentenbasierte KI zurückhält. Agenten benötigen eine gemeinsame Sprache, um plattformübergreifend zu kommunizieren. Genau diese Rolle übernimmt das A2A-Protokoll. Es bietet eine standardisierte Kommunikationsschicht, die es Ihnen nicht nur ermöglicht, direkt mit Ihrem Agenten zu interagieren, sondern auch eine Zukunft eröffnet, in der spezialisierte Agenten in Ihrem gesamten Unternehmen zusammenarbeiten und Erkenntnisse austauschen können.</p><p>Um dies zu ermöglichen, unterstützt der Elastic Agent Builder das A2A-Protokoll nativ über zwei Standardendpunkte für alle Ihre Agenten:</p><ol><li><strong>Der Agent Card-Endpunkt (<code>GET {your-kibana-url}/api/agent_builder/a2a/{agentId}.json</code>) - </strong>Dieser dient als Visitenkarte Ihres individuellen Agenten. Es stellt Metadaten über Ihren Agenten (Name, Beschreibung, Fähigkeiten usw.) für jeden A2A-kompatiblen Dienst bereit.</li><li><strong>Der A2A-Protokollendpunkt (<code>POST {your-kibana-url}/api/agent_builder/a2a/{agentId}</code>)</strong> - Dies ist der Kommunikationskanal. Andere Agenten senden ihre Anfragen hierher, Ihr Agent verarbeitet sie und sendet eine Antwort zurück, alles gemäß der <a href="https://a2a-protocol.org/latest/specification/">A2A-Protokollspezifikation</a>.</li></ol><h2>Testen Sie Ihren Makler mit dem A2A-Inspektor.</h2><p>Bevor wir unseren Agenten mit einem Produktionssystem verbinden, sollten wir überprüfen, ob die Kommunikation korrekt funktioniert. Am einfachsten geht das mit dem <strong>A2A Inspector</strong>, einem Tool, das speziell für das Testen und Debuggen von A2A-Integrationen entwickelt wurde.</p><p>Die Inbetriebnahme des Inspektors ist unkompliziert. Sie können das <a href="https://github.com/a2aproject/a2a-inspector">a2a-inspector-</a> Repository klonen und den Anweisungen in der README-Datei folgen, um <a href="https://github.com/a2aproject/a2a-inspector?tab=readme-ov-file#3-run-the-application">die Anwendung auszuführen</a>. Nach dem Start ist die Benutzeroberfläche standardmäßig unter <code>http://localhost:5001/</code> verfügbar.</p><p>Um den A2A-Inspektor mit Ihrem Makler zu verbinden, müssen Sie zwei wichtige Informationen angeben:</p><ul><li>Agentenkarten-URL: Dies ist der Endpunkt, der Ihren Agenten beschreibt. Für den <a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch">Finanzassistenten-Agenten aus unserem vorherigen Beitrag</a> lautet diese URL <code>{your-kibana-url}/api/agent_builder/a2a/financial_assistant.json</code>.</li><li>Authentifizierungsheader: Wir verwenden einen Standard-API-Schlüssel zur Authentifizierung.</li></ul><p>Sobald Sie diese Angaben in der Benutzeroberfläche des Inspektors eingegeben haben, können Sie sich mit Ihrem Agenten verbinden und sofort mit ihm chatten.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7231c72bf30bed2a854f58658c1eca2843f43bfc-1600x1296.png" style="width: 100%;" /><p>Diese einfache Validierung gibt uns die Gewissheit, dass unser Agent korrekt konfiguriert ist und für den nächsten Schritt bereit ist.</p><h2>Geh live! Ihr persönlicher Kundenbetreuer in Gemini Enterprise</h2><p>Und nun zum spannenden Teil: die Umsetzung unseres maßgeschneiderten Finanzberateragenten in Gemini Enterprise (ehemals Agentspace). Diese Integration wird durch den <a href="https://console.cloud.google.com/marketplace/product/elastic-prod/elastic-ai-agent">Elastic AI Agent ermöglicht, der auf dem Google Cloud Marketplace verfügbar ist</a>.</p><p>Nach der Verbindungsherstellung nutzt Gemini Enterprise das A2A-Protokoll, um direkt mit Ihrem Agenten zu kommunizieren. Hier zeigt sich die wahre Stärke der Interoperabilität: Benutzer können nun auf die tiefgreifenden, datengestützten Erkenntnisse Ihres benutzerdefinierten Elasticsearch-Agenten zugreifen, ohne jemals ihre gewohnte Umgebung verlassen zu müssen. Sie können Ihren benutzerdefinierten Elastic Agent in der Agentenliste sehen:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/37a39e92ebf3d72c6c8014397cd8e846336173a4-1600x834.png" style="width: 100%;" /><p>Stellen Sie sich einen Benutzer in Gemini Enterprise vor, der Folgendes fragt:</p><p><em>„Ich bin besorgt über die Marktstimmung. Können Sie mir zeigen, welche unserer Kunden am stärksten von schlechten Nachrichten betroffen sein könnten?</em> “</p><p>Im Hintergrund leitet Gemini Enterprise diese Anfrage über das A2A-Protokoll an Ihren benutzerdefinierten Elastic Agent weiter. Ihr Agent verwendet dann seine spezialisierten Tools, um Ihre Daten abzufragen, eine Antwort zu formulieren und diese zurückzusenden. Für den Endnutzer ist das Erlebnis nahtlos.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e5f126c1a27a51c6e69a767aa87c9f746b62e39c-1600x1044.png" style="width: 100%;" /><p>Und das ist noch nicht alles! Die mit dem Elastic-Agenten erhaltene Antwort kann nun als Kontext für Ihre nächsten Fragen verwendet werden, die möglicherweise einen anderen spezialisierten Agenten auslösen (z. B. (Ihr Investmentplattform-Agent passt das Engagement in börsennotierten Unternehmen an.) Alles, ohne die Suchleiste zu verlassen.</p><p>Mit Ihren Elastic-Agenten, die auf Gemini Enterprise mit A2A bereitgestellt werden, können Sie Zugriff, Orchestrierung und Workflows vereinheitlichen und Reibungsverluste zwischen KI-, Such- und Unternehmenssystemen beseitigen, indem Sie eine einzige Benutzeroberfläche anbieten, über die Benutzer mit ihren Daten und Tools interagieren können – alles im Kontext. Für die Anwender bedeutet das weniger Werkzeugwechsel und intuitivere, leistungsfähigere KI-Assistenten. Für Organisationen bedeutet dies eine integrierte, kohärente Governance, Skalierbarkeit und Interoperabilität.</p><h2>Jetzt sind Sie am Zug.</h2><p>Sie verfügen nun über die Werkzeuge, um Ihre Elastic Agents überall verfügbar zu machen. Durch die Nutzung des offenen A2A-Protokolls können Sie die Reichweite Ihrer benutzerdefinierten, datenorientierten Agenten erweitern.</p><p>In diesem Beitrag haben wir Ihnen die wichtigsten Schritte erläutert:</p><ul><li>Bereitstellung Ihres Agenten über die A2A Agent Card und Protocol Endpunkte.</li><li>Testen der Verbindung mit dem A2A Inspector.</li><li>Integration Ihres Agenten live in einen externen Dienst wie Googles Gemini Enterprise.</li></ul><p>Ihre Agenten müssen nicht länger isoliert werden. Wir freuen uns schon sehr auf die leistungsstarken, vernetzten Systeme, die Sie entwickeln werden. Viel Spaß beim Bauen!</p><p>Am einfachsten gelingt der Einstieg mit Ihrer kostenlosen Elastic Cloud-Testversion auf <a href="https://console.cloud.google.com/marketplace/product/elastic-prod/elastic-cloud?pli=1">dem Google Cloud Marketplace.</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Experimente zur Verbesserung von Agentic AI-Tools für Elasticsearch]]></title>
            <description><![CDATA[Erfahren Sie, wie wir die Arbeitsabläufe von KI-Agenten für Elasticsearch durch iterative Experimente verbessert haben, indem wir lineare Retriever, hybride Suche und semantic_text für eine skalierbare RAG-Optimierung kombiniert haben.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/ai-agent-builder-experiments-performance</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/ai-agent-builder-experiments-performance</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Sean Story]]></dc:creator>
            <pubDate>Mon, 06 Oct 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/42903c869e034674b30bb36013345aaa97f6608b-1184x864.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Wie heutzutage alle anderen setzen auch wir bei Elastic voll auf Chat, Agenten und RAG. In der Suchabteilung haben wir kürzlich an einem Agent Builder und einer Tool Registry gearbeitet, alles mit dem Ziel, die Interaktion mit Ihren Daten in Elasticsearch so einfach wie möglich zu gestalten.</p><p>Lesen Sie den <a href="https://www.elastic.co/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder">Blogbeitrag „Building AI Agentic Workflows with Elasticsearch“,</a> um mehr über das Gesamtbild dieser Bemühungen zu erfahren, oder <a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch">„Your First Elastic Agent: From a Single Query to an AI-Powered Chat“</a> für eine praxisorientiertere Einführung.</p><p>In diesem Blogbeitrag wollen wir uns jedoch etwas genauer mit einem der ersten Dinge befassen, die beim Starten eines Chats passieren, und Ihnen einige der kürzlich vorgenommenen Verbesserungen vorstellen.</p><h2>Was geschieht hier?</h2><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/25a24055a166d7d6ba81d80aa35cb97163662e23-1600x443.png" style="width: 100%;" /><p>Wenn Sie mit Ihren Elasticsearch-Daten interagieren, durchläuft unser standardmäßiger KI-Agent diesen Standardablauf:</p><ol><li>Überprüfen Sie die Eingabeaufforderung.</li><li>Ermitteln Sie, welcher Index wahrscheinlich die Antworten auf diese Frage enthält.</li><li>Erstelle eine Abfrage für diesen Index basierend auf der Eingabeaufforderung.</li><li>Durchsuchen Sie diesen Index mit dieser Suchanfrage.</li><li>Die Ergebnisse zusammenfassen.</li><li>Können die Ergebnisse die Fragestellung beantworten? Falls ja, antworten Sie bitte. Wenn nicht, wiederholen Sie den Vorgang, aber versuchen Sie etwas anderes.</li></ol><p>Das sollte nicht allzu neuartig aussehen – es ist einfach nur Retrieval Augmented Generation (RAG). Wie zu erwarten, hängt die Qualität Ihrer Antworten stark von der Relevanz Ihrer ersten Suchergebnisse ab. Während wir an der Verbesserung unserer Antwortqualität gearbeitet haben, haben wir den Abfragen, die wir in Schritt 3 generiert und in Schritt 4 ausgeführt haben, sehr große Aufmerksamkeit geschenkt. Und wir haben ein interessantes Muster festgestellt.</p><p>Oftmals lag es bei unseren ersten, „schlechten“ Antworten nicht daran, dass wir eine fehlerhafte Abfrage ausgeführt hatten. Das lag daran, dass <em>wir den falschen Index für die Abfrage ausgewählt hatten</em> . Die Schritte 3 und 4 waren normalerweise nicht unser Problem – es war Schritt 2.</p><h2>Was haben wir gemacht?</h2><p>Unsere erste Implementierung war einfach. Wir hatten ein Tool (namens index_explorer) entwickelt, das effektiv eine <code>_cat/indices</code> -Auflistung aller verfügbaren Indizes durchführt und anschließend den LLM auffordert, denjenigen dieser Indizes zu ermitteln, der am besten zur Nachricht/Frage/Aufforderung des Benutzers passt. Die <a href="https://github.com/elastic/kibana/blob/0cc78184957fcd12110dabae50353392ea937508/x-pack/platform/packages/shared/onechat/onechat-genai-utils/tools/index_explorer.ts#L98-L113">ursprüngliche Implementierung können Sie hier</a> einsehen.</p><pre><code>You are an AI assistant for the Elasticsearch company.
based on a natural language query from the user, your task is to select up to ${limit} most relevant indices from a list of indices.

*The natural language query is:* ${nlQuery}

*List of indices:*
${indices.map((index) => `- ${index.index}`).join('\n')}

Based on those information, please return most relevant indices with your reasoning.
Remember, you should select at maximum ${limit} indices.</code></pre><p>Wie gut funktionierte das? Wir waren uns nicht sicher! Wir hatten klare Beispiele, wo es <em>nicht</em> gut funktionierte, aber unsere eigentliche erste Herausforderung bestand darin, unseren aktuellen Zustand zu quantifizieren.</p><h2>Festlegung einer Ausgangsbasis</h2><h3>Es beginnt mit Daten</h3><p>Was wir brauchten, war ein Referenzdatensatz, um die Effektivität eines Tools bei der Auswahl des richtigen Index anhand einer Benutzereingabe und einer bereits vorhandenen Menge von Indizes zu messen. Und wir hatten keinen solchen Datensatz zur Hand. Also haben wir einen generiert.</p><p>Hinweis: Wir wissen, dass dies nicht die „Best Practice“ ist. Manchmal ist es aber besser, vorwärts zu gehen, als auf der Stelle zu treten. <a href="https://www.elastic.co/about/our-source-code#progress-perfection">Fortschritt, schlichte Perfektion</a>.</p><p>Wir haben mithilfe <a href="https://gist.github.com/seanstory/a08db2e149897da656db3a1ca72e17ac">dieser Eingabeaufforderung</a> Startindizes für verschiedene Domänen generiert. Anschließend generierten wir für jede generierte Domäne mithilfe<a href="https://gist.github.com/seanstory/a280a85d067e61bfeb5911bf2654e6e2"> dieser Eingabeaufforderung</a> einige weitere Indizes (Ziel war es, das LLM mit schwierigen Negativen und schwer zu klassifizierenden Beispielen zu verwirren). Anschließend haben wir jeden generierten Index und seine Beschreibungen manuell bearbeitet. Abschließend generierten wir mithilfe <a href="https://gist.github.com/seanstory/44291b666c05a383136f6e36bb9106fa">dieser Eingabeaufforderung</a> Testabfragen. Dies ergab Beispieldaten wie:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9d96d87e286eddbc012402b1ecccd57419a99253-1600x782.png" style="width: 100%;" /><p>und Testfälle wie:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4c2e9ad941d98d7e66033bbc08c9b8060ec19097-1600x797.png" style="width: 100%;" /><h3>Anfertigung eines Testgeschirrs</h3><p>Der weitere Ablauf war von hier an sehr einfach. Entwerfen Sie ein Tool, das Folgendes kann:</p><ol><li>Erstellen Sie eine saubere Ausgangsbasis mit einem Elasticsearch-Zielcluster.</li><li>Erstelle alle im Zieldatensatz definierten Indizes.</li><li>Führen Sie für jedes Testszenario das Tool i<code>ndex_explorer</code> aus (praktischerweise verfügen wir über eine <a href="https://www.elastic.co/docs/api/doc/kibana/operation/operation-post-agent-builder-tools-execute">Execute Tool API</a>).</li><li>Vergleiche den Ergebnisindex mit dem erwarteten Index und speichere das Ergebnis.</li><li>Nachdem alle Testszenarien abgeschlossen sind, werden die Ergebnisse tabellarisch erfasst.</li></ol><h3>Laut einer Umfrage…</h3><p>Die ersten Ergebnisse waren wenig überraschend mittelmäßig.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9c10679bcd6291edfa2a9ba42e7dd922aa483f0b-1216x806.png" style="width: 100%;" /><p>Insgesamt lag die Trefferquote bei der Identifizierung des richtigen Index bei 77,14 %. Und das war ein „Best-Case“-Szenario, in dem alle Indizes gute, semantisch aussagekräftige Namen haben. Jeder, der schon einmal `PUT test2/_doc/foo {...}` ausgeführt hat, weiß, dass die Indizes nicht immer aussagekräftige Namen haben.</p><p>Wir haben also einen Ausgangspunkt, und dieser zeigt, dass es noch viel Raum für Verbesserungen gibt. Nun war es Zeit für etwas Wissenschaft! 🧪</p><h2>Experimentieren</h2><h3>Hypothese 1: Kartierungen werden helfen</h3><p>Das Ziel hierbei ist es, einen Index zu identifizieren, der Daten enthält, die für die ursprüngliche Fragestellung relevant sind. Und der Teil eines Index, der die darin enthaltenen Daten am besten beschreibt, sind die <em>Indexzuordnungen</em>. Selbst ohne Stichproben aus dem Indexinhalt zu entnehmen, lässt die Tatsache, dass der Index ein Preisfeld vom Typ double besitzt, darauf schließen, dass die Daten etwas darstellen, das verkauft werden soll. Ein Autorenfeld vom Typ Text impliziert unstrukturierte Sprachdaten. Die Kombination der beiden Begriffe könnte darauf hindeuten, dass es sich bei den Daten um Bücher/Geschichten/Gedichte handelt. Allein aus der Kenntnis der Eigenschaften eines Index lassen sich viele semantische Hinweise ableiten. In einem lokalen Branch habe ich also unsere `.index_explorer`-Methode angepasst. Tool zum Senden der vollständigen Zuordnungen eines Index (samt Namen) an das LLM zur Entscheidungsfindung. </p><p>Das Ergebnis (aus den Kibana-Protokollen):</p><pre><code>[2025-09-05T11:01:21.552-05:00][ERROR][plugins.onechat] Error: Error calling connector: event: error
data: {"error":{"code":"request_entity_too_large","message":"Received a content too large status code for request from inference entity id [.rainbow-sprinkles-elastic] status [413]","type":"error"}}


    at createInferenceProviderError (errors.ts:90:10)
    at convertUpstreamError (convert_upstream_error.ts:39:38)
    at handle_connector_response.ts:26:33
    at Observable.init [as _subscribe] (/Users/seanstory/Desktop/Dev/kibana/node_modules/rxjs/src/internal/observable/throwError.ts:123:68)...</code></pre><p>Die ursprünglichen Entwickler des Tools hatten dies vorhergesehen. Während die Zuordnung eines Index eine wahre Fundgrube an Informationen darstellt, ist sie gleichzeitig ein ziemlich umfangreicher JSON-Block. Und in einem realistischen Szenario, in dem man zahlreiche Indizes vergleicht (unser Evaluierungsdatensatz definiert 20), summieren sich diese JSON-Blobs. Wir möchten dem LLM also mehr Kontext für seine Entscheidung geben als nur Indexnamen für alle Optionen, aber nicht so sehr die vollständigen Zuordnungen jeder einzelnen.</p><h3>Hypothese 2: „Vereinfachte“ Zuordnungen (Feldlisten) als Kompromiss</h3><p>Wir gingen von der Annahme aus, dass Indexersteller semantisch aussagekräftige Indexnamen verwenden würden. Was wäre, wenn wir diese Annahme auch auf Feldnamen ausdehnen würden? Unser vorheriges Experiment scheiterte, weil Mapping-JSON eine Menge überflüssiger Metadaten und Boilerplate-Code enthält.</p><pre><code>     "description_text": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword"
            }
          },
          "copy_to": [
            "description_semantic"
          ]
        },</code></pre><p>Der obige Block beispielsweise umfasst 236 Zeichen und definiert lediglich ein einzelnes Feld in einem Elasticsearch-Mapping. Die Zeichenkette „description_text“ hingegen umfasst nur 16 Zeichen. Das entspricht einer fast 15-fachen Erhöhung der Zeichenanzahl, ohne dass sich die semantische Aussagekraft dieses Feldes hinsichtlich der verfügbaren Daten sinnvoll verbessert. Was wäre, wenn wir Zuordnungen für alle Indizes abrufen, diese aber vor dem Senden an das LLM zu einer Liste ihrer Feldnamen „vereinfachen“ würden?</p><p>Wir haben es versucht.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/112c2f447c11f154b5082725cd49b51d0a3c8a65-1214x804.png" style="width: 100%;" /><p>Das ist großartig! Durchweg Verbesserungen. Aber könnten wir es besser machen?</p><h3>Hypothese 3: Beschreibungen in der Mapping-Datei _meta</h3><p>Wenn schon Feldnamen ohne zusätzlichen Kontext einen so großen Sprung verursachen, wäre das Hinzufügen von substanziellem Kontext vermutlich noch besser! Es ist nicht unbedingt üblich, jedem Index eine Beschreibung beizufügen, aber es ist möglich, dem _meta-Objekt der Zuordnung Metadaten auf Indexebene jeglicher Art hinzuzufügen. Wir haben unsere generierten Indizes erneut aufgerufen und jedem Index in unserem Datensatz eine Beschreibung hinzugefügt. Solange die Beschreibungen nicht übermäßig lang sind, sollten sie weniger Tokens verwenden als die vollständige Zuordnung und einen deutlich besseren Einblick in die im Index enthaltenen Daten bieten. Unser Experiment bestätigte diese Hypothese.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/32d2692ad4479d0e52d8ee723dcc5710a6ec90f3-1208x806.png" style="width: 100%;" /><p>Eine kleine Verbesserung, und wir liegen jetzt durchweg bei über 90 % Genauigkeit.</p><h3>Hypothese 4: Das Ganze ist größer als seine Teile</h3><p>Feldnamen haben unsere Ergebnisse verbessert. Die Beschreibungen verbesserten unsere Ergebnisse. Die Verwendung <em>von </em>Beschreibungen UND Feldnamen sollte also noch bessere Ergebnisse liefern, richtig?</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/114cbb408ff16b136251d2265416bd5270380fe5-1208x794.png" style="width: 100%;" /><p>Die Daten ergaben „nein“ (keine Veränderung gegenüber dem vorherigen Experiment). Die vorherrschende Theorie hierbei war, dass, da die Beschreibungen von vornherein aus den Indexfeldern/Zuordnungen generiert wurden, zwischen diesen beiden Kontextelementen nicht genügend unterschiedliche Informationen vorhanden sind, um bei ihrer Kombination etwas „Neues“ hinzuzufügen. Darüber hinaus wird die Nutzlast, die wir für unsere 20 Testindizes senden, ziemlich groß. Der Gedankengang, dem wir bisher gefolgt sind, ist nicht skalierbar. Tatsächlich gibt es guten Grund zu der Annahme, dass keines unserer bisherigen Experimente auf Elasticsearch-Clustern funktionieren würde, bei denen Hunderte oder Tausende von Indizes zur Auswahl stehen. Ein Ansatz, der die Größe der an den LLM gesendeten Nachricht linear mit der Gesamtzahl der Indizes erhöht, dürfte wahrscheinlich keine allgemein anwendbare Strategie darstellen.</p><p>Was wir wirklich brauchen, ist ein Ansatz, der uns hilft, eine große Anzahl von Kandidaten auf die relevantesten Optionen zu reduzieren…</p><p>Wir haben es hier mit einem Suchproblem zu tun.</p><h3>Hypothese 5: Selektion durch semantische Suche</h3><p>Wenn der Name eines Indexes eine semantische Bedeutung hat, dann kann er als Vektor gespeichert und semantisch durchsucht werden.</p><p>Wenn die Feldnamen eines Index eine semantische Bedeutung haben, dann können sie als Vektoren gespeichert und semantisch durchsucht werden.</p><p>Wenn ein Index eine Beschreibung mit semantischer Bedeutung besitzt, kann auch diese als Vektor gespeichert und semantisch durchsucht werden.</p><p>Aktuell sind diese Informationen mit Elasticsearch-Indizes nicht durchsuchbar (vielleicht sollten wir das ändern!), aber es war relativ einfach, <a href="https://github.com/elastic/connectors/pull/3638"> etwas zusammenzubasteln</a> , das diese Lücke umgehen konnte. Mithilfe des Connector-Frameworks von Elastic habe ich einen Connector erstellt, der für jeden Index in einem Cluster ein Dokument ausgibt. Die Ausgabedokumente würden etwa so aussehen:</p><pre><code> doc = {
                "_id": index_name,
                "index_name": index_name,
			"meta_description”: description,
"field_descriptions" = field_descriptions,
                "mapping": json.dumps(mapping),  
                "source_cluster": self.es_client.configured_host,
            }</code></pre><p>Ich habe diese Dokumente in einen neuen Index verschoben, in dem ich die Zuordnung manuell wie folgt definiert habe:</p><pre><code>{
   "mappings": {
       "properties": {
           "semantic_content": {
               "type": "semantic_text"
           },
           "index_name": {
               "type": "text",
               "copy_to": "semantic_content"
           },
           "mapping": {
               "type": "keyword",
               "copy_to": "semantic_content"
           },
           "source_cluster": {
               "type": "keyword"
           },
           "meta_description": {
               "type": "text",
               "copy_to": "semantic_content"
           },
           "field_descriptions": {
               "type": "text",
               "copy_to": "semantic_content"
           }
       }
   }
}</code></pre><p>Dadurch entsteht ein einzelnes semantisches Inhaltsfeld, in dem alle anderen Felder mit semantischer Bedeutung zusammengefasst und indiziert werden. Die Suche in diesem Index wird trivial, mit lediglich:</p><pre><code>GET indexed-indices/_search
{
 "query": {
   "semantic": {
     "field": "semantic_content",
     "query": "$query"
   }
 }
}</code></pre><p>Das modifizierte <code>index_explorer</code> -Tool ist jetzt <em>wesentlich</em> schneller, da es keine Anfrage an ein LLM stellen muss, sondern stattdessen eine einzelne Einbettung für die gegebene Anfrage anfordern und eine effiziente Vektorsuche durchführen kann. Wenn wir den Spitzenreiter als unseren ausgewählten Index verwenden, erhalten wir folgende Ergebnisse:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/06ef5d78040d064d3444793f636d527d9e19a869-1214x800.png" style="width: 100%;" /><p>Dieser Ansatz ist skalierbar. Diese Vorgehensweise ist effizient. Dieser Ansatz ist aber kaum besser als unser Ausgangszustand. Das ist allerdings nicht überraschend; der Suchansatz ist hier unglaublich naiv. Da gibt es keine Nuancen. Es wird nicht anerkannt, dass der Name und die Beschreibung eines Index mehr Gewicht haben sollten als ein beliebiger Feldname, der im Index enthalten ist. Es besteht keine Möglichkeit, exakte lexikalische Übereinstimmungen gegenüber synonymen Übereinstimmungen zu gewichten. Allerdings müsste man für die Erstellung einer hochdifferenzierten Abfrage eine Menge Annahmen über die vorliegenden Daten treffen. Bis jetzt haben wir bereits einige große Annahmen darüber getroffen, dass Index- und Feldnamen eine semantische Bedeutung haben, aber wir müssten noch einen Schritt weiter gehen und anfangen anzunehmen, <em>wie viel</em> Bedeutung sie haben und wie sie zueinander in Beziehung stehen. Ohne dies zu tun, können wir wahrscheinlich nicht zuverlässig die beste Übereinstimmung als unser Top-Ergebnis identifizieren, sondern können eher sagen, dass die beste Übereinstimmung irgendwo unter den Top N Ergebnissen liegt. Wir benötigen etwas, das semantische Informationen in dem Kontext, in dem sie existieren, verarbeiten, mit einer anderen Entität vergleichen kann, die sich möglicherweise auf eine semantisch unterschiedliche Weise darstellt, und zwischen ihnen urteilen kann. Wie ein LLM.</p><h3>Hypothese 6: Reduktion der Kandidatenmenge</h3><p>Es gab noch einige weitere Experimente, die ich hier nur kurz erwähnen werde, aber der entscheidende Durchbruch bestand darin, den Wunsch aufzugeben, die beste Übereinstimmung ausschließlich anhand einer semantischen Suche auszuwählen, und stattdessen die semantische Suche als Filter zu nutzen, um irrelevante Indizes aus der Betrachtung des LLM auszusortieren. Wir kombinierten Linear Retrievers, Hybrid Search mit RRF und <code>semantic_text</code> für <a href="https://gist.github.com/seanstory/d704443120e20f6c844db10e30066860">unsere Suche</a> und beschränkten die Ergebnisse auf die Top 5 übereinstimmenden Indizes.</p><p>Anschließend haben wir für jede Übereinstimmung den Namen des Index, die Beschreibung und die Feldnamen zu einer Nachricht für das LLM hinzugefügt. Die Ergebnisse waren fantastisch:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8fcabd78f591f90d6bc7c0e087d31317e4eef791-1206x804.png" style="width: 100%;" /><p>Die höchste Genauigkeit, die je in einem Experiment erzielt wurde! Und weil bei diesem Ansatz die Nachrichtengröße nicht proportional zur Gesamtzahl der Indizes ansteigt, ist dieser Ansatz weitaus besser skalierbar.</p><h2>Ergebnisse</h2><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/04d630797213dbb8bf567da41d1cdd5c7b4586c9-1600x521.png" style="width: 100%;" /><p>Das erste eindeutige Ergebnis war, dass unsere Ausgangslage verbessert werden <em>kann</em> . Dies erscheint im Nachhinein offensichtlich, aber bevor die Experimente begannen, gab es ernsthafte Diskussionen darüber, ob wir unser <code>index_explorer</code> -Tool ganz aufgeben und uns auf eine explizite Konfiguration durch den Benutzer verlassen sollten, um den Suchraum einzuschränken. Auch wenn dies nach wie vor eine praktikable und gültige Option darstellt, zeigt diese Studie, dass es vielversprechende Wege zur Automatisierung der Indexauswahl gibt, wenn solche Benutzereingaben nicht verfügbar sind.</p><p>Das nächste eindeutige Ergebnis war, dass das bloße Hinzufügen weiterer beschreibender Zeichen zur Lösung des Problems immer weniger Nutzen bringt. Vor dieser Studie hatten wir darüber diskutiert, ob wir in den Ausbau der Elasticsearch-Funktionalität zur Speicherung <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-field-meta">von Metadaten auf Feldebene</a> investieren sollten. Heute sind diese <code>meta</code> -Werte auf 50 Zeichen begrenzt, und es gab die Annahme, dass wir diesen Wert erhöhen müssten, um ein semantisches Verständnis unserer Felder zu erlangen. Dies ist eindeutig nicht der Fall, und das LLM scheint mit reinen Feldnamen recht gut zurechtzukommen. Wir werden dies möglicherweise später noch genauer untersuchen, aber es erscheint uns momentan nicht dringlich.</p><p>Umgekehrt hat dies deutlich gezeigt, wie wichtig „durchsuchbare“ Indexmetadaten sind. Für diese Experimente haben wir einen Index von Indizes gehackt. Aber das ist etwas, was wir untersuchen könnten, indem wir es direkt in Elasticsearch integrieren, APIs zur Verwaltung erstellen oder zumindest eine Konvention dafür festlegen. Wir werden unsere Optionen abwägen und intern diskutieren, also bleiben Sie gespannt.</p><p>Letztendlich hat diese Anstrengung den Wert darin bestätigt, uns Zeit für Experimente zu nehmen und datengestützte Entscheidungen zu treffen. Tatsächlich hat es uns geholfen, erneut zu bestätigen, dass unser Agent Builder-Produkt robuste, im Produkt integrierte Evaluierungsfunktionen benötigt. Wenn wir ein komplettes Test-Framework nur für ein Tool entwickeln müssen, das Indizes auswählt, benötigen unsere Kunden unbedingt Möglichkeiten, ihre kundenspezifischen Tools qualitativ zu bewerten, während sie iterative Anpassungen vornehmen.</p><p>Ich bin gespannt, was wir bauen werden, und ich hoffe, Sie auch!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[So stellen Sie Elasticsearch auf dem AWS Marketplace bereit]]></title>
            <description><![CDATA[In dieser Schritt-für-Schritt-Anleitung erfahren Sie, wie Sie Elasticsearch mithilfe des Elastic Cloud Service auf dem AWS Marketplace einrichten und ausführen.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/aws-elasticsearch-service-set-up</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/aws-elasticsearch-service-set-up</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Eduard Martin]]></dc:creator>
            <pubDate>Fri, 03 Oct 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a80776d2ef85db26f850d932339fac2d26b90278-1086x620.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>In diesem Artikel erfahren Sie, wie Sie Elasticsearch mithilfe von Marketplace-Angeboten auf AWS bereitstellen.</p><p>Wir werden den Elastic Cloud Service auf AWS nutzen, den offiziellen verwalteten Elasticsearch-Service, der die Bereitstellung und Orchestrierung aller Elastic Stack-Komponenten über die native Infrastruktur von AWS vereinfacht.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f15814051b53b50bec38f9a9f515a1e6dc08a56c-884x440.png" style="width: 100%;" /><p>Wenn Sie erfahren möchten, wie Sie Elasticsearch auf AWS EC2 installieren und konfigurieren, schauen Sie sich <a href="https://www.elastic.co/search-labs/blog/elasticsearch-on-aws-ec2-deployment-guide">diesen Blog</a> an.<br/></p><h2>Was ist AWS Marketplace?</h2><p><a href="https://aws.amazon.com/marketplace"><strong>Elastic auf AWS Marketplace</strong></a> bietet ein vollständig verwaltetes Such- und Analyseerlebnis, bei dem AWS die Infrastrukturbereitstellung, Sicherheit und Skalierung übernimmt, während sich die Entwickler auf die Erstellung von Suchanwendungen konzentrieren. Dies ermöglicht es Teams, Elasticsearch-Cluster der Enterprise-Klasse innerhalb von Minuten mit integrierten AWS-Integrationen bereitzustellen.</p><h2>Wann sollte man Elastic auf dem AWS Marketplace verwenden?</h2><p>Elastic auf AWS Marketplace eignet sich am besten für Organisationen mit einer bestehenden AWS-Infrastruktur, die Elasticsearch mit Managed Services, integrierter Sicherheit und nahtloser AWS-Integration ohne operativen Mehraufwand bereitstellen möchten.</p><h2>So richten Sie Elastic Cloud auf dem AWS Marketplace ein</h2><h3>Schritt 1: Zugriff auf den AWS Marketplace</h3><p>1. Melden Sie sich bei <a href="https://console.aws.com/">AWS</a>an.</p><ul><li>Suchen Sie in der Suchleiste nach AWS Marketplace.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/fc9928f79482c2c01e33978c88d390a2bfa2a3bf-1600x340.png" style="width: 100%;" /><p>2. Klicken Sie im linken Navigationsbereich auf <strong>„Produkte entdecken“</strong> und suchen Sie dann nach Elasticsearch.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ca4232271cb13ebfe33de406ecaec085033ec8a0-1454x760.png" style="width: 100%;" /><p>3. Klicken Sie auf <strong>Elastic Cloud (Elasticsearch-Dienst).</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e59aa276e55532f2ac3461d0ca983af4d41ad7a6-1600x611.png" style="width: 100%;" /><h3>Schritt 2: Abonnieren Sie den Dienst</h3><p>1. Wählen Sie <strong>Ihre Kaufoptionen</strong> oder klicken Sie auf <strong>„Kostenlos testen“.</strong></p><p>2. Überprüfen Sie <strong>die Preisdetails</strong>, <strong>die Allgemeinen Geschäftsbedingungen</strong> und <strong>die Kaufdetails</strong> .</p><p>3. Klicken Sie auf die Schaltfläche <strong>„Abonnieren“</strong> .</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1c30d12b6b1061e76771d518011e522285f939f1-1600x290.png" style="width: 100%;" /><p>4. Jetzt müssen wir das Elastic-Konto einrichten. Folgen Sie den Schritten von AWS.</p><p>a. Klicken Sie auf die Schaltfläche „Integration aktivieren“.</p><p>b. Klicken Sie auf die Schaltfläche „Anmelden oder ein Händlerkonto erstellen“.</p><p>c. Klicken Sie auf die Schaltfläche „Vorlage starten“.</p><p>d. Klicken Sie auf die Schaltfläche „Software starten“.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/879d9f0f01406e1955e1b38a2f6f2192ef040344-852x722.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/fc59be78cf776aa12867f40810598419576cbd39-1600x1143.png" style="width: 100%;" /><h3>Schritt 3. Konfigurieren Sie Ihr neues Konto in Elastic.</h3><p>1. Erstellen Sie Ihr Elastic-Konto</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5efeaf0737062a55470b17b67651f220e12183f2-986x905.png" style="width: 100%;" /><p>2. Bestätigen Sie Ihre E-Mail-Adresse</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e0cfaac278614e317ce278935040bfa5a58edd13-853x894.png" style="width: 100%;" /><p>3. Geben Sie Ihren Namen und Ihre Firmeninformationen ein.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d5658fe66c3b1bcced73e822eae006846f0ddd9e-997x903.png" style="width: 100%;" /><p>4. Füllen Sie eine kurze Elastic-Umfrage aus.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a3f53c00dadfd22f7d739a920c87d5f387182833-892x805.png" style="width: 100%;" /><p>5. Wählen Sie die Region aus, in der Sie Elastic Cloud hosten möchten. Standardmäßig wird Ihre tatsächliche AWS-Region ausgewählt.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c1dcdf3bf91c305821daaa25a60aa03be6454c1c-1207x1032.png" style="width: 100%;" /><p>6. Warten Sie, bis Elastic bereitgestellt ist.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/50903e57ebea7cc47bdfabf4750b4ba2a7a91148-1370x1266.png" style="width: 100%;" /><p>7. Ihre Bereitstellung ist mit Ihrem AWS Marketplace-Abonnement verbunden.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3bb731e2d5de5053ccecb77e45dbdbcdaf294dba-1600x1288.png" style="width: 100%;" /><h2>Kündigen Sie Ihr Abonnement</h2><p>Um Ihr Abonnement zu kündigen</p><p>1. Öffnen Sie die <a href="https://console.aws.com/">AWS-Konsole.</a></p><p>Suchen Sie in der Suchleiste nach AWS Marketplace. Klicken Sie auf <strong>AWS Marketplace.</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/aeed3d1e67b4cef91934de257a6fd6daa9737a12-1600x554.png" style="width: 100%;" /><p>2. Klicken Sie auf das <strong>Elastic Cloud-Abonnement.</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/04e6cc41850226df223dbe2d1b0e4b45265f6c39-1600x564.png" style="width: 100%;" /><p>3. Klicken Sie auf die Schaltfläche <strong>„Aktionen“</strong> und anschließend auf <strong>„Abonnement kündigen“.</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e33d334ea6541c637a223de3ebd6209def75a6d3-1600x1039.png" style="width: 100%;" /><p>4. Bestätigen Sie die Kündigung, indem Sie dann auf <strong>„Ja“</strong>und anschließend auf die Schaltfläche<strong> „Abonnement</strong> kündigen“ klicken.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b73b3187168adc7aefdd46f95be33c1bce3da1e4-1103x698.png" style="width: 100%;" /><p>5. Eine Bestätigungsmeldung erscheint oben auf der Seite.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c5ba66a23d535e866a8b458e5aca82c5f0b93037-1600x639.png" style="width: 100%;" /><h2>Wie geht es weiter?</h2><p>Starten Sie Ihre Elastic Cloud-Reise mit einer 7-tägigen kostenlosen Testversion, die eine einzelne Bereitstellung und drei Projekte umfasst<a href="https://aws.amazon.com/marketplace/pp/prodview-voru33wi6xs7k">. Elastic Cloud (Elasticsearch Service)</a>. Melden Sie sich einfach in Ihrem AWS-Konto an und klicken Sie auf „Kaufoptionen anzeigen“, um die Elastic Search AI Platform sofort auf Elastic<a href="https://aws.amazon.com/marketplace/pp/prodview-voru33wi6xs7k"> Cloud (Elasticsearch Service)</a> zu nutzen. Die Testversion bietet vollen Zugriff auf Such-, Sicherheits- und Überwachungslösungen ohne zusätzlichen Aufwand für die Infrastrukturverwaltung.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[AutoOps: Ein Weg zur Vereinfachung der selbstverwalteten Elasticsearch-Verwaltung]]></title>
            <description><![CDATA[Entdecken Sie AutoOps für selbstverwaltete Elasticsearch-Umgebungen (On-Prem oder privat gehostet) Wir stellen Ihnen den Mehrwert, die Einrichtung und die damit verbundenen Erkenntnisse vor.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/elastic-autoops-self-managed-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/elastic-autoops-self-managed-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Ori Shafir]]></dc:creator>
            <pubDate>Wed, 01 Oct 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4538ca4c6cdde99d88f1cead7515716fac217b92-1600x883.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Wir stellen AutoOps für selbstverwaltete Elasticsearch-Umgebungen (On-Prem oder privat gehostet) vor, das die Verwaltung von Elasticsearch vereinfacht. Anstelle einer <em>traditionellen technischen Funktionsübersicht</em> zeigt dieser Blog den Mehrwert, die Einrichtung und die damit verbundenen Erkenntnisse aus der Perspektive eines DevOps-Ingenieurs auf, da sich der wahre Wert von AutoOps am besten im Rahmen des Tagesgeschäfts beim Verwalten von Elasticsearch im großen Maßstab zeigt.</p><h2>Kapitel 1: Hintergrund – Die Komplexität der Selbstverwaltung im großen Maßstab</h2><p><strong>Der Betrieb einer groß angelegten, selbstverwalteten Datenplattform kann komplex sein.</strong></p><p>Einen Moment lang werden die Anfragen blitzschnell beantwortet. Im nächsten Schritt kommt es zu Verzögerungen bei der Datenaufnahme und zu einem sprunghaften Anstieg der Lagerkosten. Es ist im Grunde wie die Leitung eines Zoos, nur dass die Tiere dich um 3 Uhr morgens anrufen können.</p><p>Meine Umgebung ist nicht anders: mehrere Cluster, intensive clusterübergreifende Suche (CCS) und Hunderte von Benutzern aus verschiedenen Abteilungen.</p><p>Wir nutzen Stack Monitoring für den täglichen Betrieb. Es liefert zwar Grafiken und Kennzahlen, aber es bedarf immer noch viel Fachwissen und Zeit, um die Zusammenhänge herzustellen. Die Diagnose von Engpässen oder die Entscheidung, wann Shard-Strategien angepasst werden müssen, ist nach wie vor ein manueller und fehleranfälliger Prozess. In vielen Fällen bleiben Probleme unbemerkt, bis sie zu einem Ausfall, einem Leistungsabfall oder einem unerwarteten Anstieg des Speicherbedarfs führen.</p><h2>Kapitel 2: AutoOps entdecken</h2><p>Dann kam die Ankündigung: <strong><a href="https://www.elastic.co/platform/autoops">AutoOps</a> ist jetzt auch für selbstverwaltete Cluster verfügbar – sowohl für On-Premise- als auch für privat gehostete Umgebungen.</strong></p><p>AutoOps unterstützt Benutzer von Elastic Cloud seit langem bei der effizienteren Verwaltung von Deployments. Nun stehen dieselben Vorteile auch selbstverwalteten Clustern (ECK, ECE oder eigenständig) zur Verfügung, die in lokalen oder privaten Cloud-Umgebungen ausgeführt werden – kostenlos und unabhängig von Ihrer Lizenzstufe.</p><p>Das Angebot von AutoOps ist verlockend:</p><ul><li>Echtzeit-Problemerkennung für Engpässe bei der Datenerfassung, unausgeglichene Shards, langsame Abfragen und mehr</li><li>Konkrete Handlungsempfehlungen, die auf die Konfiguration Ihres Clusters zugeschnitten sind</li><li>Erkenntnisse zur Ressourcenoptimierung zur Steigerung der Effizienz und Reduzierung von Verschwendung</li><li>Einfache Einrichtung durch Installation eines schlanken Agenten – keine zusätzliche Infrastruktur erforderlich</li></ul><p>Ehrlich gesagt, alles, was „keine zusätzliche Infrastruktur“ versprach, hatte meine volle Aufmerksamkeit.</p><h2>Kapitel 3: Einrichtung in 5 Minuten (ja, wirklich)</h2><p>Ich habe mir den Nachmittag freigehalten, mich mit Kaffee eingedeckt und mich auf einen langen Aufbau eingestellt. Zu meiner Überraschung dauerte es nur fünf Minuten:</p><ol><li>Ich habe mich in mein <a href="https://cloud.elastic.co/registration?onboarding_service_type=ccm-autoops">Elastic Cloud-Konto</a>eingeloggt.</li><li>Entscheidung über die Ausführungsmethode der Agenten: Elastic Cloud Kubernetes (ECK), Kubernetes, Docker oder Linux.</li><li>Die Cluster-URL wurde eingegeben.</li><li>Es musste nur ein einziger Befehl ausgeführt werden, der einen schlanken Metricbeat-Agenten installierte.</li></ol><p>Das war’s. Mein Cluster war verbunden.</p><p>Es müssen keine dedizierten Überwachungscluster bereitgestellt werden. Und vor allem sendet AutoOps nur <strong>Metriken,</strong> was bedeutet, dass die Daten meines Unternehmens in meiner selbstverwalteten Umgebung verbleiben.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/cb8e1ebc0f23a6db6b0e4300c9cdcd165623ca39-1184x734.png" style="width: 100%;" /><p><em>Schritt 1: Registrieren Sie sich bei Elastic Cloud</em></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b208830db4d74feef700473ce07b052e0c72f48b-3446x1716.png" style="width: 100%;" /><p><em>Schritt 2: Wählen Sie aus, wo der Agent ausgeführt werden soll.</em></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/850d8c0d6b9e018408969aa05b67dbd865a26d30-1184x734.png" style="width: 100%;" /><p><em>Schritt 3: Geben Sie Ihren Elasticsearch-Endpunkt und die Authentifizierungsmethode ein.</em></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0a472889a277caabb9ecd3fb9cd57d3b3eac0100-1184x734.png" style="width: 100%;" /><p><em>Schritt 4: Einfacher Befehl zur Installation des Agenten</em></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6f9a7283469a321a9c472d655cea11d9d0ea7b1e-1600x883.png" style="width: 100%;" /><p><em>Das war&#x27;s: Nach wenigen Minuten zeigt AutoOps erste Erkenntnisse an.</em></p><p>Weitere Einzelheiten finden Sie in der <a href="https://www.elastic.co/docs/deploy-manage/monitor/autoops/cc-connect-self-managed-to-autoops">AutoOps-Onboarding-Dokumentation</a> und in den <a href="https://www.elastic.co/docs/deploy-manage/monitor/autoops/cc-cloud-connect-autoops-faq">FAQ</a>.</p><h2>Kapitel 4: Erste Erkenntnisse, erste Erfolge</h2><p>Innerhalb weniger Minuten lieferte AutoOps erste Erkenntnisse, analysierte die Ursachen und gab klare Anweisungen zur Behebung der Probleme.</p><p>Zu den Höhepunkten der ersten Woche gehörten:</p><ul><li>Gekennzeichnete Indizes, die keiner ILM-Richtlinie zugeordnet sind und zu groß geworden sind</li><li>Ein Cluster wies drei leere Knoten auf, die von einem früheren Wartungsauftrag übrig geblieben waren.</li><li>Einige Knoten überschritten Wasserzeichen, und bei einigen Indizes fehlten Replikate.</li><li>Ich habe eine fehlerhaft konfigurierte Vorlage erwischt.</li><li>Eine langwierige Suche wurde lokalisiert und der genaue Abbruchbefehl vorgeschlagen.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f7d16355938ffcb00127deaf833d7a4d0acdb188-1600x894.png" style="width: 100%;" /><p><em>AutoOps hat festgestellt, dass der Cluster die Indizierung ablehnt.</em></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9d6372bc489e9954e6f78d81047d7a25feef1703-1600x894.png" style="width: 100%;" /><p><em>AutoOps hat festgestellt, dass einige Indizes ohne Replik konfiguriert sind.</em><br/>Vor AutoOps hätten wir diese Probleme mit noch mehr Hardware gelöst. Stattdessen wies AutoOps direkt auf die Ursache hin, und die Behebung dauerte nur wenige Minuten.</p><p>Ausnahmsweise zeigte mir ein Überwachungssystem nicht nur Diagramme an, sondern erklärte mir auch, wie ich das Problem lösen konnte. Ich fragte mich, ob AutoOps auch mein Heim-WLAN diagnostizieren und mich endlich von der Rolle der IT-Abteilung für meine Familie befreien könnte…</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/eac09f1d730955453cd79900002b100bc741ad97-1600x894.png" style="width: 100%;" /><p><em>AutoOps überwachte die Shard-Größen und gab eine Warnung aus, wenn viele Shards leer waren.</em></p><h2>Kapitel 5: Unterstützung, die sieht, was ich sehe</h2><p>Als ich zum ersten Mal einen Supportfall eröffnete, erkannte ich einen weiteren Vorteil: Die Support-Ingenieure von Elastic konnten genau dieselben Daten und Empfehlungen sehen, die ich mir ansah.</p><p>Es wandelte die Unterstützung in eine Zusammenarbeit. Statt eines ständigen Hin und Her mit Support-Tickets fühlte es sich an, als würde man mit einem Teamkollegen zusammenarbeiten, der Elasticsearch in- und auswendig kennt.</p><h2>Kapitel 6: Betrieb im großen Maßstab</h2><p>Vor AutoOps fühlte sich die Skalierung von Elasticsearch wie eine Mischung aus Wissenschaft, Instinkt und Erfahrungswissen an.</p><p>Jetzt ist es datengesteuert, mit klarer Transparenz und Handlungsempfehlungen:</p><ul><li>Transparenz der Ressourcennutzung zur Vermeidung von Überdimensionierung</li><li>Intelligentere Shard-Zuweisung und Tiering-Empfehlungen für eine ausgewogene Leistung</li><li>Erkenntnisse zur Indexgrößenbestimmung, die unnötigen Speicherplatz und Hardwarekosten reduzieren</li><li>Schnellere Ursachenanalyse über mehrere Cluster hinweg</li></ul><h2>Kapitel 7: Der erste von vielen Cloud-verbundenen Diensten</h2><p>AutoOps ist mehr als ein eigenständiges Tool. Es handelt sich um den ersten Dienst einer neuen Reihe von <a href="https://www.elastic.co/docs/deploy-manage/cloud-connect"><strong>Cloud Connected Services</strong></a> für selbstverwaltete Kunden. Cloud Connect ermöglicht es selbstverwalteten Clustern, Elastic Cloud-Dienste zu nutzen, ohne den operativen Aufwand für die Installation und Verwaltung dieser Dienste in ihrer eigenen Umgebung tragen zu müssen. Die Funktionen werden automatisch ausgerollt, sodass Teams schneller von Verbesserungen profitieren und die Infrastruktur weniger komplex ist.</p><p>Als Nächstes: Elastic Inference Service (EIS).</p><h2>Abschließende Gedanken</h2><p>Die Verwaltung groß angelegter, selbstverwalteter Implementierungen muss nicht überfordernd sein.</p><p>Und wenn Sie die Abläufe noch einfacher gestalten möchten, können Sie jederzeit einige Workloads in die <a href="https://cloud.elastic.co/registration?pg=global&amp;plcmt=nav&amp;cta=205352-primary">Elastic Cloud</a> verlagern, egal ob Hosted oder Serverless, um Elasticsearch auf einfachste Weise auszuführen.</p><p>Wenn Sie weiterhin selbstverwaltet arbeiten möchten, verbinden Sie einen beliebigen selbstverwalteten Cluster mit <a href="https://cloud.elastic.co/registration?onboarding_service_type=ccm-autoops">AutoOps in Elastic Cloud</a> – für alle Elastic-Lizenzen kostenlos.</p><h2>TLDR</h2><p>Der Betrieb großer, selbstverwalteter Elasticsearch-Cluster ist komplex und zeitaufwändig. AutoOps bietet Echtzeit-Problemerkennung, umsetzbare Handlungsempfehlungen und gemeinsame Transparenz mit Elastic Support – ohne dass zusätzliche Infrastruktur verwaltet werden muss. Die Einrichtung dauert nur wenige Minuten, und die Erkenntnisse werden sofort angezeigt.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Ihr erster Elastic Agent: Von einer einzelnen Anfrage bis zum KI-gestützten Chat]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie mit dem AI Agent Builder von Elastic spezialisierte KI-Agenten erstellen können. In diesem Blogbeitrag entwickeln wir einen KI-gestützten Finanzagenten.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/ai-agent-builder-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/ai-agent-builder-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jeff Vestal]]></dc:creator>
            <pubDate>Thu, 25 Sep 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ca853555eaa213f10f1db8c0ab0a2bbacee97b88-1456x816.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Mit dem neuen <a href="https://www.elastic.co/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder">Agent Builder</a> von Elastic können Sie spezialisierte KI-Agenten erstellen, die als Experten für Ihre spezifischen Geschäftsbereiche fungieren. Diese Funktion geht über einfache Dashboards und Suchleisten hinaus und verwandelt Ihre Daten von einer passiven Ressource in einen aktiven, dialogfähigen Partner.</p><p>Stellen Sie sich einen Finanzmanager vor, der sich vor einem Kundengespräch schnell einarbeiten muss. Statt manuell Newsfeeds zu durchforsten und Portfolio-Dashboards abzugleichen, können sie ihrem individuell entwickelten Agenten jetzt einfach eine direkte Frage stellen. Das ist der Vorteil eines „Chat-First“-Ansatzes. Der Manager hat einen direkten, dialogorientierten Draht zu seinen Daten und fragt beispielsweise: „Was gibt es Neues zu ACME Corp und wie wirkt sich das auf die Anlagen meines Kunden aus?“ und innerhalb von Sekunden eine zusammengefasste Expertenantwort zu erhalten.</p><p>Während wir heute einen Finanzexperten aufbauen, sind die Anwendungsbereiche so vielfältig wie Ihre Daten. Mit der gleichen Macht kann ein Cybersicherheitsanalyst zur Suche nach Bedrohungen, ein Site Reliability Engineer zur Diagnose eines Ausfalls oder ein Marketingmanager zur Optimierung einer Kampagne geschaffen werden. Unabhängig vom Fachgebiet bleibt die Kernmission dieselbe: Ihre Daten in einen Spezialisten zu verwandeln, mit dem Sie sich unterhalten können.</p><h2>Schritt 0: Unser Datensatz</h2><p>Unser heutiger Datensatz ist ein synthetischer, auf Finanzdaten basierender Datensatz, der aus Finanzkonten, Vermögenspositionen, Nachrichten und Finanzberichten besteht. Er ist zwar synthetisch, repliziert aber eine vereinfachte Version eines realen Finanzdatensatzes.</p><p><code>financial_accounts</code>Kundenportfolios mit Risikoprofilen</p><p><code>financial_holdings</code>: Aktien-/ETF-/Anleihenpositionen mit Kaufhistorie</p><p><code>financial_asset_details</code>Details zur Aktie/zum ETF/zur Anleihe</p><p><code>financial_news</code>: KI-generierte Marktartikel mit Stimmungsanalyse</p><p><code>financial_reports</code>Unternehmensgewinne und Analystennotizen</p><p>Sie können diesen Datensatz selbst laden, indem Sie der beigefügten Anleitung in <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/your-first-elastic-agent/Your_First_Elastic_Agent.ipynb">diesem</a> Notebook folgen.</p><h2>Schritt 1: Die Grundlage – Ihre Geschäftslogik als ES|QL</h2><p>Jede KI-Fähigkeit beginnt mit einer soliden Logik. Unserem Financial Manager-Agenten müssen wir beibringen, wie er eine häufig gestellte Frage beantworten kann: „Ich bin besorgt über die Marktstimmung.“ Können Sie mir zeigen, welche unserer Kunden am stärksten von schlechten Nachrichten bedroht sind? Diese Frage geht über eine einfache Suche hinaus. Dies erfordert von uns, die Marktstimmung mit den Kundenportfolios in Zusammenhang zu bringen.</p><p>Wir müssen die in den negativen Artikeln erwähnten Vermögenswerte finden, jeden Kunden identifizieren, der diese Vermögenswerte hält, den aktuellen Marktwert seines Engagements berechnen und dann die Ergebnisse nach dem höchsten Risiko ordnen. Diese komplexe Analyse mit mehreren Verknüpfungen ist die perfekte Aufgabe für unser hochentwickeltes ES|QL-Tool.</p><p>Hier ist die vollständige Abfrage, die wir verwenden werden. Es sieht beeindruckend aus, aber die Konzepte sind einfach.</p><pre><code>FROM financial_news, financial_reports METADATA _index
        | WHERE sentiment == "negative"
        | WHERE coalesce(published_date, report_date) >= 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</code></pre><h2>Aufschlüsselung: Verbindungen und Leitplanken</h2><p>Bei dieser Abfrage spielen zwei wichtige Konzepte eine Rolle, die den Agent Builder ausmachen.</p><h3>1. Die LOOKUP JOIN-Funktion</h3><p>Seit Jahren gehört die Möglichkeit, Daten aus verschiedenen Indizes anhand eines gemeinsamen Schlüssels zu verknüpfen, zu den am häufigsten nachgefragten Funktionen von Elasticsearch. Mit ES|QL ist das jetzt mit <code>LOOKUP JOIN</code> möglich.</p><p>In unserer neuen Abfrage führen wir eine Kette von drei <code>LOOKUP JOIN</code> durch: Zuerst verbinden wir negative Nachrichten mit Vermögensdetails, dann verknüpfen wir diese Vermögenswerte mit den Kundenbeständen und schließlich fügen wir sie zu den Kontoinformationen des Kunden hinzu. Dadurch wird mit einer einzigen, effizienten Abfrage ein unglaublich reichhaltiges Ergebnis aus vier verschiedenen Indizes erzeugt. Das bedeutet, dass wir unterschiedliche Datensätze kombinieren können, um eine einzige, aussagekräftige Antwort zu erhalten, ohne vorher alle unsere Daten in einen einzigen riesigen Index denormalisieren zu müssen.</p><h3>2. Parameter als LLM-Leitplanken</h3><p>Sie werden feststellen, dass die Abfrage <code>?time_duration</code> verwendet. Das ist nicht nur eine Variable; es ist eine Leitplanke für die KI. Während große Sprachmodelle (LLMs) hervorragend darin sind, Abfragen zu generieren, kann es zu ineffizienten oder sogar falschen Abfragen führen, wenn man ihnen freie Hand bei der Daten lässt.</p><p>Durch die Erstellung einer parametrisierten Abfrage zwingen wir das LLM dazu, innerhalb der getesteten, effizienten und korrekten Geschäftslogik zu arbeiten, die ein menschlicher Experte bereits definiert hat. Das ist vergleichbar damit, wie Entwickler seit Jahren Suchvorlagen verwenden, um Abfragefunktionen sicher für Anwendungen bereitzustellen. Der Agent kann eine Benutzeranfrage wie &quot;diese Woche&quot; interpretieren, um den Parameter <code>time_duration</code> zu füllen, muss aber unsere Abfragestruktur verwenden, um die Antwort zu erhalten. Dadurch erhalten wir die perfekte Balance zwischen Flexibilität und Kontrolle.</p><p>Letztendlich ermöglicht diese Abfrage einem Experten, der die Daten versteht, sein Wissen in einem Werkzeug zu kapseln. Andere Personen – und KI-Agenten – können dieses Werkzeug dann nutzen, um korrelierte Ergebnisse zu erhalten, indem sie einfach einen einzigen Parameter angeben, ohne etwas über die zugrunde liegende Komplexität wissen zu müssen.</p><h2>Schritt 2: Die Fertigkeit – Eine Abfrage in ein wiederverwendbares Werkzeug umwandeln</h2><p>Eine ES|QL-Abfrage ist nur Text, bis wir sie als <strong>Werkzeug</strong> registrieren. Im Agent Builder ist ein Tool mehr als nur eine gespeicherte Abfrage; es ist eine „Fähigkeit“, die ein KI-Agent verstehen und einsetzen kann. Der Zauber liegt in der von uns bereitgestellten <strong>Beschreibung in natürlicher Sprache</strong> . Diese Beschreibung bildet die Brücke zwischen der Frage eines Benutzers und der zugrunde liegenden Abfragelogik. Registrieren wir nun die soeben erstellte Abfrage.</p><h3>Der UI-Pfad</h3><p>Das Erstellen eines Tools in Kibana ist ein unkomplizierter Prozess.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a29c53a73b99af5972273c51218ea9004a9b0abb-1600x812.png" style="width: 100%;" /><p>1. Navigieren Sie zu <strong>Agenten</strong></p><ul><li>Klicken Sie auf<strong> „Tools“</strong>oder <strong>„Tools verwalten“</strong> und klicken Sie dann auf die Schaltfläche <strong>„Neues Tool“</strong> .</li></ul><p>2. Füllen Sie das Formular mit folgenden Angaben aus:</p><ul><li><strong>Werkzeug-ID:</strong> <code>find_client_exposure_to_negative_news</code></li></ul><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ich. Dies ist die eindeutige ID des Tools.</p><ul><li><strong>Beschreibung:</strong> &quot;Ermittelt das Risiko negativer Nachrichten im Kundenportfolio.&quot; Dieses Tool durchsucht aktuelle Nachrichten und Berichte nach negativen Stimmungen, identifiziert den zugehörigen Vermögenswert und findet alle Kunden, die diesen Vermögenswert halten. Es liefert eine nach dem aktuellen Marktwert der Position sortierte Liste zurück, um das höchste potenzielle Risiko hervorzuheben.“</li></ul><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ich. Dies ist das, was der LLM liest, um zu entscheiden, ob dieses Werkzeug das richtige für die Aufgabe ist.</p><ul><li><strong>Labels</strong>: <code>retrieval</code> and <code>risk-analysis</code></li></ul><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Etiketten dienen dazu, mehrere Werkzeuge zu gruppieren.</p><ul><li><strong>Konfiguration:</strong> Fügen Sie die vollständige ES|QL-Abfrage aus Schritt 1 ein.</li></ul><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ich. Dies ist die Suche, die der Agent verwenden wird.</p><p>3. Klicken Sie auf <strong>„Parameter aus Abfrage ableiten“</strong>. Die Benutzeroberfläche wird <code>?time_duration</code> automatisch finden und unten auflisten. Fügen Sie für jedes Element eine kurze Beschreibung hinzu, damit der Agent (und andere Benutzer) dessen Zweck verstehen können.</p><ul><li><code>time_duration</code>Der Zeitraum, in dem nach negativen Nachrichten gesucht wird. Format ist &quot;X Stunden&quot;, Standardwert: 8760 Stunden</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/deb422d97863f78dbe08bfa2e3c708d1f75166ff-1600x938.png" style="width: 100%;" /><p>4. Probier es aus!</p><ul><li>Klicken Sie auf Speichern &amp; Testen.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/57e768b88327821e70bd616744822f98fa367362-732x136.png" style="width: 100%;" /><ul><li>Es wird ein neues Flyout angezeigt, in dem Sie die Abfrage testen können, um sicherzustellen, dass sie wie erwartet funktioniert.</li></ul><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ich. Geben Sie in <code>time_duration</code> den gewünschten Bereich ein, hier verwenden wir „8760 Stunden“.</p><ul><li>Klicken Sie auf „Absenden“, und wenn alles gut geht, erhalten Sie eine JSON-Antwort. Um sicherzustellen, dass es wie erwartet funktioniert, scrollen Sie nach unten und sehen Sie sich das Objekt <code>values</code> an. Dort werden die eigentlichen übereinstimmenden Dokumente zurückgegeben.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7e0c5171a4f7ffdfc1830f1a05a9acb987870b75-1600x722.png" style="width: 100%;" /><p>5. Klicken Sie auf das „X“ oben rechts, um das Testfenster zu schließen. Ihr neues Tool wird nun in der Liste angezeigt und kann einem Agenten zugewiesen werden.</p><h3>Der API-Pfad</h3><p>Für Entwickler, die Automatisierung bevorzugen oder Tools programmatisch verwalten müssen, lässt sich dasselbe Ergebnis mit einem einzigen API-Aufruf erzielen. Senden Sie einfach eine <code>POST</code> -Anfrage an den <code>/api/agent_builder/tools</code> -Endpunkt mit der Definition des Tools.</p><pre><code>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) >= 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"
  ]
}</code></pre><h2>Schritt 3: Das Gehirn – Ihren individuellen Agenten erstellen</h2><p>Wir haben eine wiederverwendbare Fähigkeit entwickelt (das Tool). Nun müssen wir den <strong>Agenten</strong> erstellen, die Persona, die es tatsächlich benutzen wird. Ein Agent ist die Kombination aus einem LLM, einem bestimmten Satz von Werkzeugen, zu denen Sie ihm Zugriff gewähren, und vor allem einer Reihe von <strong>benutzerdefinierten Anweisungen</strong> , die als seine Verfassung fungieren und seine Persönlichkeit, Regeln und seinen Zweck definieren.</p><h3>Die Kunst des Prompts</h3><p>Der wichtigste Aspekt bei der Schaffung eines zuverlässigen, spezialisierten Agenten ist die Pünktlichkeit. Eine gut ausgearbeitete Anleitung macht den Unterschied zwischen einem generischen Chatbot und einem zielgerichteten, professionellen Assistenten aus. Hier legen Sie die Leitplanken fest, definieren die Ausgabe und geben dem Agenten seine Mission.</p><p>Für unseren <code>Financial Manager</code> -Agenten verwenden wir die folgende Eingabeaufforderung.</p><pre><code>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.**</code></pre><p>Lassen Sie uns genauer betrachten, warum diese Aufforderung so effektiv ist:</p><ul><li><strong>Es definiert eine anspruchsvolle Persönlichkeit: </strong>Schon die erste Zeile stellt den Agenten als „spezialisierten Data Intelligence Assistant“ dar und vermittelt so einen professionellen und kompetenten Eindruck.</li><li><strong>Es bietet einen Denkrahmen: </strong>Indem wir dem Agenten sagen, er solle &quot;Verstehen, Planen, Ausführen und Synthetisieren&quot;, geben wir ihm eine Standardarbeitsanweisung. Dadurch wird seine Fähigkeit verbessert, komplexe, mehrstufige Fragen zu beantworten.</li><li><strong>Es fördert den interaktiven Dialog: </strong>Die Anweisung, „klärende Fragen zu stellen“, macht den Agenten widerstandsfähiger. Dadurch werden Fehlinterpretationen bei mehrdeutigen Anfragen minimiert, was zu genaueren Antworten führt.</li></ul><h3>Der UI-Pfad</h3><p>1. Navigieren Sie zu <strong>Agenten.</strong></p><ul><li>Klicken Sie auf<strong> „Tools“</strong>oder <strong>„Tools verwalten“</strong> und klicken Sie dann auf die Schaltfläche <strong>„Neues Tool“</strong> .</li></ul><p>2. Füllen Sie die grundlegenden Angaben aus:</p><ul><li><strong>Agenten-ID:</strong> <code>financial_assistant</code>.</li><li><strong>Anleitung: </strong>Kopieren Sie die obige Eingabeaufforderung.</li><li><strong>Labels</strong>: <code>Finance</code>.</li><li><strong>Anzeigename:</strong> <code>Financial Assistant</code>.</li><li><strong>Anzeigebeschreibung: </strong><code>An assistant for analyzing and understanding your financial data</code>.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/18ea73f1cae620129c0afa0e7ba9e2a3390224a7-1600x1189.png" style="width: 100%;" /><p>3. Klicken Sie oben auf <strong>„Tools“</strong>.</p><ul><li>Setzen Sie ein Häkchen neben unserem <code>find_client_exposure_to_negative_news</code> -Tool.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0c1e4ecbbd51d0dd10c6e861dbe9a9ccddeb35f6-1600x149.png" style="width: 100%;" /><p>4. Klicken Sie auf <strong>Speichern</strong>.</p><h3>Der API-Pfad</h3><p>Sie können denselben Agenten mit einer <code>POST</code> -Anfrage an den <code>/api/agent_builder/agents</code> -Endpunkt erstellen. Der Anfragetext enthält dieselben Informationen: die ID, den Namen, die Beschreibung, die vollständigen Anweisungen und eine Liste der Tools, die der Agent verwenden darf.</p><pre><code>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"
            ]
          }
        ]
      }
    }</code></pre><h2>Schritt 4: Der Lohn – Ein Gespräch führen</h2><p>Unsere Geschäftslogik ist in einem Tool gekapselt und ein &quot;Gehirn&quot; ist bereit, es in unserem Agenten zu verwenden. Jetzt wird es Zeit, dass alles zusammenkommt. Wir können nun mithilfe eines spezialisierten Agenten mit unseren Daten kommunizieren.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5414cb6b7c41365acb0356a8bfe1140751ffd8db-1600x1014.png" style="width: 100%;" /><h3>Der UI-Pfad</h3><ol><li>Navigieren Sie in Kibana zu <strong>„Agenten“</strong> .</li><li>Wechseln Sie mithilfe des Dropdown-Menüs unten rechts im Chatfenster vom standardmäßigen <strong>Elastic AI Agent</strong> zu unserem neu erstellten <strong>Financial Assistant </strong>Agent.</li><li>Stellen Sie eine Frage, die es dem Agenten ermöglicht, unser Spezialtool zu nutzen:<ol><li><em>Ich bin besorgt über die Marktstimmung. Können Sie mir zeigen, welche unserer Kunden am stärksten von schlechten Nachrichten betroffen sein könnten?</em></li></ol></li></ol><p>Nach kurzer Zeit liefert der Agent eine perfekt formatierte, vollständige Antwort. Aufgrund der Beschaffenheit von LLMs kann Ihre Antwort etwas anders formatiert sein, aber für diesen Durchlauf hat der Agent Folgendes zurückgegeben:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/17b4ed43d279f9e53ee9fe3d482d0b2ec359a083-1600x1088.png" style="width: 100%;" /><h3>Was ist gerade passiert? Die Argumentation des Agenten</h3><p>Der Agent kannte die Antwort nicht einfach nur. Es wurde ein mehrstufiger Plan umgesetzt, dessen Mittelpunkt die Auswahl des besten Werkzeugs für die jeweilige Aufgabe bildete. Hier ein Einblick in den Denkprozess:</p><ul><li><strong>Identifizierte Absicht:</strong> Es wurden Schlüsselwörter aus Ihrer Frage, wie „Risiko“ und „negative Nachrichten“, mit der Beschreibung des <code>find_client_exposure_to_negative_news</code> -Tools abgeglichen.</li><li><strong>Plan ausgeführt:</strong> Es hat den Zeitrahmen aus Ihrer Anfrage extrahiert und einen <strong>einzigen Aufruf</strong> an dieses spezialisierte Tool durchgeführt.</li><li><strong>Die Arbeit wurde delegiert:</strong> Das Tool übernahm dann die gesamte schwere Arbeit: die verketteten Joins, die Wertberechnungen und die Sortierung.</li><li><strong>Ergebnis zusammengefasst:</strong> Abschließend formatierte der Agent die Rohdaten des Tools gemäß den Vorgaben in eine klare, für Menschen lesbare Zusammenfassung.</li></ul><p>Und wir müssen nicht nur raten, wenn wir unser Denken erweitern und mehr Details betrachten.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6a4da9262d3f88c60bfd8f8bf9b67c3b84e961ba-1600x607.png" style="width: 100%;" /><h3>Der API-Pfad</h3><p>Sie können diese Konversation auch programmatisch starten. Senden Sie einfach die Eingabefrage an den <code>converse</code> API-Endpunkt und achten Sie darauf, die <code>agent_id</code> unseres <code>financial_manager</code> anzugeben.</p><pre><code>POST kbn://api/agent_builder/converse
{
  "input": "Show me our largest positions affected by negative news",
  "agent_id": "financial_assistant"
}</code></pre><h2>Für Entwickler: Integration mit der API</h2><p>Während die Kibana-Benutzeroberfläche ein fantastisches und intuitives Erlebnis beim Erstellen und Verwalten Ihrer Agenten bietet, kann alles, was Sie heute gesehen haben, auch programmatisch erreicht werden. Der Agent Builder basiert auf einer Reihe von APIs, die es Ihnen ermöglichen, diese Funktionalität direkt in Ihre eigenen Anwendungen, CI/CD-Pipelines oder Automatisierungsskripte zu integrieren.</p><p>Die drei wichtigsten Endpunkte, mit denen Sie arbeiten werden, sind:</p><ul><li><strong><code>/api/agent_builder/tools</code></strong>: Der Endpunkt zum Erstellen, Auflisten und Verwalten der wiederverwendbaren Fähigkeiten, die Ihre Agenten nutzen können.</li><li><strong><code>/api/agent_builder/agents</code></strong>Der Endpunkt zur Definition Ihrer Agenten-Personas, einschließlich ihrer wichtigen Anweisungen und Tool-Zuweisungen.</li><li><strong><code>/api/agent_builder/converse</code></strong>: Der Endpunkt für die Interaktion mit Ihren Agenten, den Start von Gesprächen und das Erhalten von Antworten.</li></ul><p>Eine vollständige, praktische Anleitung zur Verwendung dieser APIs für jeden Schritt dieses Tutorials finden Sie im zugehörigen <strong>Jupyter Notebook</strong> , das <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/your-first-elastic-agent/Your_First_Elastic_Agent.ipynb">hier</a> in unserem GitHub-Repository verfügbar ist.</p><h2>Fazit: Jetzt sind Sie am Bauen</h2><p>Wir begannen damit, eine ES|QL-Abfrage zu nehmen und sie in eine wiederverwendbare Fähigkeit umzuwandeln. Anschließend entwickelten wir einen spezialisierten KI-Agenten, gaben ihm eine klare Mission und klare Regeln und statteten ihn mit diesen Fähigkeiten aus. Das Ergebnis ist ein hochentwickelter Assistent, der eine komplexe Frage verstehen und eine mehrstufige Analyse durchführen kann, um eine präzise, datengestützte Antwort zu liefern.</p><p>Dieser Workflow ist das Herzstück des neuen <strong>Agent Builders</strong> in Elastic. Es ist so konzipiert, dass es einfach genug ist, damit auch technisch nicht versierte Benutzer Agenten über die Benutzeroberfläche erstellen können, gleichzeitig aber differenziert genug, damit Entwickler auf Basis unserer APIs maßgeschneiderte KI-gestützte Anwendungen entwickeln können. Am wichtigsten ist jedoch, dass Sie LLMs sicher und geschützt mit Ihren eigenen Daten verbinden können, die von der von Ihnen definierten Expertenlogik gesteuert werden, und mit Ihren Daten kommunizieren können.</p><h2>Sind Sie bereit, Agenten für die Kommunikation mit Ihren Daten einzusetzen?</h2><p>Am besten festigt man das Gelernte, indem man selbst Hand anlegt. Probieren Sie alles, was wir heute besprochen haben, in unserem <a href="https://www.elastic.co/training/elastic-ai-agents-mcp"><strong>kostenlosen, interaktiven Praxisworkshop</strong></a> aus. Sie werden diesen gesamten Ablauf und mehr in einer speziellen Sandbox-Umgebung durchlaufen.</p><p>In einem zukünftigen Blogbeitrag zeigen wir Ihnen, wie Sie eine eigenständige Anwendung verwenden, die mit unserem <code>Financial Assistant</code> -Agenten interagiert, und gehen näher auf das <strong>Model Context Protocol (MCP)</strong> ein, das dies alles ermöglicht. In einem separaten Blogbeitrag werden wir die Unterstützung des Agent Builders für das sich entwickelnde Agent2Agent- oder A2A-Protokoll besprechen.</p><p>Bleibt dran und viel Spaß beim Bauen!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Erstellung von KI-Agenten-Workflows mit Elasticsearch]]></title>
            <description><![CDATA[Lernen Sie Agent Builder kennen, eine neue KI-Ebene in Elasticsearch, die ein Framework für die Erstellung von KI-Agenten-Workflows bietet und dabei die hybride Suche nutzt, um den Agenten den Kontext zu liefern, den sie zum Denken und Handeln benötigen.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/ai-agentic-workflows-elastic-ai-agent-builder</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/ai-agentic-workflows-elastic-ai-agent-builder</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Anish Mathur, Dana Juratoni]]></dc:creator>
            <pubDate>Tue, 23 Sep 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d5d04bbcfc3925f48f3487ea4c7e0dd2205316d0-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Wir bei Elastic haben LLMs mit KI-Assistenten, fortschrittlichem RAG und Verbesserungen der Vektordatenbanken um Kontext erweitert und dialogorientierte Schnittstellen geschaffen. In jüngster Zeit, mit dem Aufstieg von KI-Agenten, haben wir den Bedarf an relevantem Kontext wachsen sehen und gelernt, dass leistungsstarke<strong> KI-Agenten eine hervorragende Suchfunktion benötigen</strong>. Deshalb haben wir neue native Funktionen im Elastic Stack entwickelt, die dabei helfen sollen, KI-Agenten zu entwickeln, die Ihre Daten in Elasticsearch nutzen. Wir möchten Sie über unsere Fortschritte auf diesem Weg informieren und Ihnen einen Ausblick darauf geben, wohin die Reise unserer Meinung nach als Nächstes gehen wird.</p><h2>Agent Builder: Eine Grundlage für die Entwicklung datengesteuerter KI-Agenten</h2><p>Das Versprechen eines KI-Agenten ist einfach: Man gibt ihm ein Ziel, und er erledigt die Aufgabe. Für Entwickler sieht die Realität jedoch anders aus: Sie steht vor einer Reihe komplexer Herausforderungen. Erstens ist ein Agent nur so gut wie seine Wahrnehmung seiner Umgebung und der ihm zur Verfügung stehenden Werkzeuge, um die Ziele des Benutzers zu erreichen. Dann stellt es eine enorme Herausforderung dar, aus einer Flut von unterschiedlichen Unternehmensdaten den richtigen Kontext herauszufiltern. Schließlich muss all dies von einer zuverlässigen Denkschleife orchestriert werden, die planen, ausführen und lernen kann.</p><p>Um dieses Problem zu lösen, müssen die Entwickler einen komplexen und fehleranfälligen Stack von Grund auf neu aufbauen. Die heutige Agentenarchitektur erfordert das Zusammenfügen mehrerer, voneinander unabhängiger Komponenten: ein LLM, eine Vektordatenbank, ein Metadatenspeicher, separate Systeme für Protokollierung und Tracing sowie eine Möglichkeit zur Überprüfung, ob das Ganze überhaupt funktioniert. Das ist nicht nur komplex, sondern auch kostspielig, fehleranfällig und erschwert den Aufbau der hochwertigen, vertrauenswürdigen KI-Systeme, die Ihre Nutzer fordern.</p><p>Wir wollen es also vereinfachen. Unser Ansatz hierfür besteht darin, die wesentlichen Bestandteile eines effektiven kontextgesteuerten Agenten zu nehmen und sie mit einer neuen Reihe von Funktionen namens <strong>Elastic AI Agent Builder</strong> direkt in den Kern von Elasticsearch zu integrieren. Diese neue Schicht bietet ein Framework mit allen wesentlichen Bausteinen für die Erstellung von KI-Agenten auf Basis von Elasticsearch: ein offenes Set an Primitiven, standardbasierte Protokolle und sicherer Datenzugriff – damit Sie agentenbasierte Systeme entwickeln können, die auf reale Daten und Anforderungen zugeschnitten sind:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1ee1e73dd3f485ce86294d39490c98ce2a3d9925-1238x1072.png" style="width: 100%;" /><p><strong>KI-Erlebnisse bereitstellen</strong>: Das ist das ultimative Ziel. Mit unserer Search AI Platform und Ihren Daten als Grundlage können Sie jede Art von generativer KI-Anwendung erstellen: von benutzerdefinierten Chat-Oberflächen bis hin zu Integrationen mit agentenbasierten Frameworks wie LangChain oder Geschäftsanwendungen wie Salesforce.</p><p><strong>Unterstützt von Agenten und Tools</strong>: Auf der Plattform selbst stellen wir eine saubere, einfache Abstraktionsschicht bereit. Sie interagieren direkt mit Agenten und Tools, die Sie an Ihre spezifischen Bedürfnisse anpassen können. Sie können die Funktionen der Plattform auch über robuste APIs und offene Standards wie MCP und A2A nutzen.</p><p><strong>Ermöglicht durch die Search AI Platform</strong>: Dies ist die Kern-Engine, in die wir die Komponenten integriert haben. Die hochentwickelte Vektordatenbank, die Agentenlogik, die Abfragekonstruktion, Sicherheitsfunktionen, das Tracing zur Auswertung – all das befindet sich hier und wird von Elastic verwaltet und optimiert.</p><p><strong>Das Potenzial Ihrer Daten freisetzen</strong>: Die Grundlage jedes erfolgreichen Agenten sind hervorragende Daten. Unsere Plattform beginnt mit der Fähigkeit, den Zugriff auf alle Ihre Unternehmensdaten zu erfassen oder zu föderieren.</p><h2>Agentenaufbau in der Plattform</h2><p>Der in die Search AI Platform integrierte Agent Builder bietet ein komplettes Framework für die Agentenentwicklung. Es basiert auf fünf zentralen Säulen, von denen jede einen kritischen Aspekt beim Aufbau und Einsatz produktionsreifer KI-Systeme adressiert. Lassen Sie uns aufschlüsseln, wie Agenten das Ziel definieren, Tools die Fähigkeiten bereitstellen, offene Standards Interoperabilität gewährleisten, Evaluierung Transparenz schafft und Sicherheit das Vertrauen schafft.</p><h3>Agenten</h3><p>Agenten sind die obersten Bausteine dieser neuen Ebene von Elasticsearch. Ein Agent definiert das zu erreichende Ziel, die zur Ausführung verfügbaren Werkzeuge und die Datenquellen, mit denen er arbeiten kann. Agenten sind nicht auf dialogbasierte Interaktionen beschränkt; sie können komplette Arbeitsabläufe, Aufgabenautomatisierung oder benutzerorientierte Erlebnisse ermöglichen.</p><p>Wenn eine Anfrage an einen Agenten gerichtet wird, durchläuft sie einen strukturierten Zyklus:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/627ad1744b629bbe27359325702f40d97e40d1f4-704x852.png" style="width: 100%;" /><ol><li>Interpretieren Sie Ihre Eingabe und Ihr Ziel.</li><li>Wählen Sie das richtige Werkzeug und die richtigen Argumente für die Ausführung aus.</li><li>Begründen Sie die Antwort des Tools.</li><li>Entscheiden Sie, ob ein Ergebnis zurückgegeben oder mit weiteren Toolaufrufen fortgefahren werden soll.</li></ol><p>Elastic übernimmt die Orchestrierung, den Kontext und die Ausführung dieses Zyklus. Die Entwickler konzentrieren sich darauf, festzulegen, <em>was</em> der Agent tun soll: Ziele, Werkzeuge und Daten, während das System <em>die</em> Durchführung der Schlussfolgerungen und Arbeitsabläufe steuert.</p><p><em>Der Standardagent</em></p><p>Unser erster Agent, der auf dieser Plattform basiert, ist ein nativer Dialogagent in Kibana, der Ihnen die Möglichkeit gibt, sofort mit Ihren Daten zu interagieren. Es bietet eine sofort einsatzbereite Benutzererfahrung und bleibt gleichzeitig vollständig erweiterbar, sodass Sie ohne zusätzliche Konfiguration sofort mit Ihren Daten interagieren können.</p><p>Sie können mit dieser Funktion direkt in Kibana über eine neue Chat-Benutzeroberfläche oder über eine API interagieren.</p><p>Die Abfrage des Standardagenten über die API erfordert nur einen einzigen Aufruf:</p><pre><code>POST kbn://api/agent_builder/converse
{
    "input": "what is our top portfolio account?"
}</code></pre><p>Da Konversationen zustandsbehaftet sind, können Sie die Interaktion mit einem Agenten mithilfe einer conversation_id fortsetzen oder den vollständigen Konversationsverlauf abrufen:</p><pre><code>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</code></pre><p><em>Zollagenten</em></p><p>Entwickler können über einfache APIs auch ihre eigenen benutzerdefinierten Agenten erstellen. Agenten kapseln Anweisungen, Werkzeuge und Datenzugriff und erstellen so maßgeschneiderte Schlussfolgerungsmaschinen.</p><p>Die Erstellung eines benutzerdefinierten Agenten ist so einfach wie ein einziger API-Aufruf. Das folgende Beispiel zeigt, dass das Feld „Konfiguration“ alle wichtigen Details enthält, wie z. B. Anweisungen oder verfügbare Tools:</p><pre><code>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": 
...
   }
}</code></pre><p>Sobald der Agent erstellt ist, kann er direkt abgefragt werden:</p><pre><code>POST kbn://api/agent_builder/converse
{
    "input": "What news about DIA?",
    "agent_id": "custom_agent"
}</code></pre><p>Dieser Ansatz wandelt den Agenten von einem komplexen, von Grund auf neu zu entwickelnden System in eine einfache, deklarative Einheit der Geschäftslogik um, wodurch Sie intelligente Automatisierung schneller realisieren können.</p><p>Eine detaillierte Anleitung zum Erstellen eines spezialisierten Agenten von Grund auf finden Sie in unserem ausführlichen Schritt-für-Schritt-Leitfaden: <a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch">Ihr erster elastischer Agent: Von einer einzelnen Abfrage zu einem KI-gestützten Chat</a>.</p><h3>Tools</h3><p>Wenn Agenten definieren, <em>was</em> erreicht werden soll, definieren Werkzeuge, <em>wie</em>.</p><p>Die Tools stellen Agenten spezifische Elastic-Kernfunktionen zur Verfügung, um Informationen abzurufen und auszuführen oder Aktionen durchzuführen. Tools können Kernfunktionen wie das Abrufen von Indizes oder Zuordnungen umfassen, aber auch fortgeschrittenere Funktionen wie die Verarbeitung natürlicher Sprache zu ES|QL.</p><p>Elasticsearch wird mit einer Reihe von Standardwerkzeugen ausgeliefert, die für gängige Anwendungsfälle optimiert sind. Die wahre Flexibilität ergibt sich jedoch daraus, eigene Lösungen zu entwickeln. Durch die Definition von Tools legen Sie genau fest, welche Abfragen, Indizes und Felder einem Agenten mit ES|QL zugänglich gemacht werden, und erhalten so eine präzise Kontrolle über Geschwindigkeit, Genauigkeit und Sicherheit.</p><p>Die Registrierung eines neuen Tools ist ebenfalls so einfach wie ein einziger API-Aufruf. Sie könnten ein Tool erstellen, das unsere <a href="https://www.elastic.co/search-labs/blog/esql-timeline-of-improvements">ES|QL (Elasticsearch Query Language)</a> nutzt, um Neuigkeiten über ein bestimmtes Finanzinstrument zu finden:</p><pre><code>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"
      }
    }
  ...
  }
...
}</code></pre><p>Nach der Registrierung können Sie das neue Tool Ihren benutzerdefinierten Agenten zuweisen und ihnen so eine kuratierte Auswahl an Funktionen zur Verfügung stellen, die sie bei Bedarf nutzen können.</p><p>Wir bieten eine Plattform zur Erstellung von maßgeschneiderten Tools für Ihre spezifischen Bedürfnisse, z. B. mit ES|QL, die den Agenten von einem Allzweckagenten in einen domänenspezifischen Experten verwandelt, der auf Ihren einzigartigen Daten und Ihrer Geschäftsdomäne basiert.</p><h3>Offene Standards und Interoperabilität</h3><p>Elasticsearch Agents und Tools werden über offene Standard-APIs bereitgestellt, wodurch sie sich leicht als grundlegende Bausteine in das breitere Ökosystem agentenbasierter Frameworks integrieren lassen. Unser Ansatz ist einfach: keine Blackboxes. Wir möchten, dass Sie die Kernkompetenz von Elastic im Bereich der Suche nutzen und sie mit komplementären Fähigkeiten und anderen agentenbasierten Systemen kombinieren können.</p><p>Um dies zu ermöglichen, stellen wir unsere Fähigkeiten über APIs, neue Protokolle und offene Standards zur Verfügung.</p><p><em>Model Context Protocol (MCP)</em></p><p><a href="https://www.elastic.co/search-labs/blog/model-context-protocol-elasticsearch">Das Model Context Protocol (MCP)</a> entwickelt sich schnell zum offenen Standard für die systemübergreifende Vernetzung von Tools. Durch die Unterstützung von MCP kann Elasticsearch dialogbasierte KI mit Ihren Datenbanken, Indizes und externen APIs verbinden. Da im Elastic Stack ein Remote-MCP-Server integriert ist, kann jeder MCP-kompatible Client auf die Tools von Elastic zugreifen und sie als Bausteine in seinen umfassenderen agentenbasierten Workflows verwenden.</p><p>Das ist keine Einbahnstraße. Sie können außerdem Tools von externen MCP-Servern importieren und diese in Elasticsearch verfügbar machen. Schon bald werden MCP-Server wahrscheinlich für nahezu alles verfügbar sein und weitaus umfassender sein als alles, was wir selbst entwickeln könnten. Elastic bietet Such- und Abruffunktionen in großem Umfang, und Sie können dies mit spezialisierten Funktionen anderer Plattformen kombinieren, um effektive Agenten zu erstellen.</p><p><em>Agent-to-Agent (A2A)</em></p><p>Wir arbeiten außerdem an der Unterstützung von Agent zu Agent (A2A). Bei MCP geht es um die Vernetzung von Tools, bei A2A hingegen um die Vernetzung von Agenten. Mit einem A2A-Server können die von Ihnen erstellten Elastic-Agenten direkt mit Agenten aus anderen Systemen kommunizieren: Kontext austauschen, Aufgaben delegieren und Arbeitsabläufe koordinieren.</p><p>Man kann es sich als Interoperabilität auf der Ebene der Argumentation vorstellen. Ihr Elastic-Agent könnte die Suche und den Abruf übernehmen, dann eine Aufgabe an einen spezialisierten Support- oder IT-Agenten weitergeben und das Ergebnis nahtlos zurückerhalten. Das Ergebnis ist ein Ökosystem kooperierender Akteure, von denen jeder das tut, was er am besten kann.</p><p>Die Einführung von MCP und A2A unterstreicht letztlich unser Bekenntnis zu Elasticsearch als vollwertigem Bestandteil und gewährleistet die offene Integration innerhalb des gesamten agentenbasierten Ökosystems.</p><h3>Rückverfolgung und Bewertung</h3><p>Mit der Integration von Suchfunktionen in Agenten wird die Herausforderung einer effektiven Evaluierung entscheidend. Um Agenten in realen Unternehmensumgebungen bedenkenlos einsetzen zu können, benötigen Sie die Gewissheit, dass sie nicht nur präzise, sondern auch effizient und zuverlässig sind. Wie misst man die Leistung, diagnostiziert eine schlechte Reaktion oder verbessert die Ausgangslage? Alles beginnt mit Transparenz.</p><p>Aus diesem Grund haben wir unsere Agenten-APIs von Grund auf auf Transparenz ausgelegt. Betrachten wir folgende einfache Agenteninteraktion:</p><pre><code>POST kbn://api/agent_builder/converse
{
    "input": "what is our top portfolio account?"
}</code></pre><p>Die Antwort enthält nicht nur das Endergebnis, sondern auch den vollständigen Ausführungsablauf, aus dem hervorgeht, welche Werkzeuge der Agent ausgewählt hat, welche Parameter er verwendet hat und welche Ergebnisse jeder Schritt erbracht hat.</p><pre><code>{
  "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...."
  }
}</code></pre><p>Eine umfassende Ablaufverfolgung und Protokollierung sind für einen kontinuierlichen Verbesserungsprozess unerlässlich, und schon bald können Sie diese Agenten-Traces direkt in Elasticsearch speichern und anzeigen. Noch besser: Diese Traces basieren auf dem OpenTelemetry-Protokoll, wodurch sichergestellt wird, dass sie standardisiert und portabel sind und sich in die Observability-Plattform Ihrer Wahl integrieren lassen.</p><p>Dieser Detaillierungsgrad ist die Grundlage für einen echten kontinuierlichen Verbesserungsprozess. Es ermöglicht Ihnen, eine umfassende Suite von Tests zu erstellen, Fehler zu beheben, Fehlermodi zu identifizieren, um Regressionen zu verhindern, und erfolgreiche Muster zu erfassen, um die Leistung feinabzustimmen. Letztendlich ist dieser datengetriebene Ansatz der Schlüssel zur Umwandlung eines vielversprechenden Prototyps in ein produktionsreifes, vertrauenswürdiges KI-System.</p><h3>Security</h3><p>Da Agenten und Tools immer leistungsfähiger werden, ist Sicherheit nicht mehr optional – sie ist grundlegend. Die Bereitstellung von APIs, die Automatisierung von Aufgaben und Arbeitsabläufen erfordert, dass Unternehmenssysteme vertrauenswürdig sind. Gerade weil Agenten immer mehr Arbeitsabläufe automatisieren, ist die Fähigkeit, diese zu sichern und sicherzustellen, dass sie den Unternehmensanforderungen entsprechen, von entscheidender Bedeutung.</p><p>Die genannten Funktionen übernehmen alle die in Elastic bereits heute verfügbaren Steuerelemente, einschließlich <a href="https://www.elastic.co/search-labs/blog/rag-and-rbac-integration">rollenbasierter Zugriffskontrolle (RBAC)</a> für API-Aufrufe und API-Schlüsselverwaltung. Wir dehnen die gleichen Kontrollen auch auf neue Protokolle wie MCP aus. Das bedeutet Unterstützung für Standards wie OAuth sowie die Möglichkeit, benutzerdefinierte Authentifizierungsmechanismen einzubinden.</p><p>Unser Ziel ist es, Ihnen die Flexibilität zu geben, mit Agenten und Tools zu experimentieren und gleichzeitig das von Ihrer Organisation geforderte Maß an Sicherheit, Compliance und Governance aufrechtzuerhalten.</p><h2>Was kommt als Nächstes?</h2><p>Wir fügen nicht nur Funktionen hinzu; wir erweitern Elasticsearch für agentenbasierte Kontextentwicklung. Wir planen, unsere zukünftige Entwicklung auf diesen Prinzipien aufzubauen:</p><p>1. Bekenntnis zu Open Source und Standards</p><p>Unser Bekenntnis zu Open Source und offenen Standards gewährleistet, dass diese Funktionen mit externen Agenten-Frameworks interoperabel bleiben. Sie können jederzeit Agenten in Ihrem gesamten Ökosystem verbinden, erweitern und zusammenstellen und behalten dabei die Kontrolle über Ihre Daten und Arbeitsabläufe.</p><p>2. Wert des Kontextes</p><p>Der Kontext ist das größte Kapital eines KI-Agenten. Die Verwaltung des Kontextes während der Suchvorgänge und Workflow-Operationen der Agenten kann eine anspruchsvolle Aufgabe sein. Wir nutzen die Kernkompetenzen von Elastic, um Kontext-Engineering zu realisieren und sicherzustellen, dass Ihrem Agenten stets die relevantesten Informationen zur Verfügung stehen.</p><p>3. Fokus auf agentenbasierte Datenströme</p><p>Zukünftig werden Agenten eine immer größere Datenquelle darstellen, einschließlich der Ergebnisse von Agenten (generierte Dokumente, Berichte, Visualisierungen) und der Ausführungsspur von Agenten (ihr Denken, Werkzeugaufrufe, Speicher/Kontext). Elastic eignet sich hervorragend für die Verarbeitung dieser Art von Daten, und wir arbeiten an Forschungsprojekten zur Durchführung von Analysen, Auswertungen und automatisierten Verbesserungen mithilfe dieser Daten.</p><p>4. Sicherheit durch Design</p><p>KI-Agenten bringen eine ganze Reihe neuer Sicherheitsherausforderungen mit sich. Elastic war schon immer ein Vorreiter für sichere Lösungen und wir bauen auch weiterhin auf Enterprise-Niveau Schutzmechanismen, Zugriffskontrollen und &quot;Zero-Trust&quot;-Prinzipien.</p><p>5. In die Plattform integriert</p><p>Die Funktionen zum Erstellen von KI-Agenten sind in die Elasticsearch-Plattform integriert. Dies bedeutet, dass Funktionen auf Plattformebene wie Tracing, Evaluation, Visualisierung und Analyse auch für Agenten anwendbar sind. Sie möchten Dashboards auf Basis von Agentenausführungen entwickeln? Das ist bereits integriert. Sie möchten die Leistung des KI-Agenten mithilfe von Stimmungsanalysen bewerten? Die Plattform ermöglicht das. Dies ermöglicht es, einen kompletten Lebenszyklus rund um Ihre KI-Erlebnisse aufzubauen.</p><p>Elastic hat sich zum Ziel gesetzt, Ihnen Schnittstellen zur Verfügung zu stellen, mit denen Sie dialogbasierte KI und automatisierte Arbeitsabläufe entwickeln können, die vollständig integriert, erweiterbar und auf Ihren Daten basieren. Weitere technische Details und Fortschritte werden in Kürze bekannt gegeben.</p><p>Agent Builder ist ab sofort als private Vorschau verfügbar. <a href="https://www.elastic.co/contact?pg=global&amp;plcmt=nav&amp;cta=205352">Nehmen Sie Kontakt mit uns auf</a> , um Zugang anzufordern. Haben Sie Fragen oder Feedback? Tauschen Sie sich mit unserer Entwickler-Community in unserem <a href="https://elasticstack.slack.com/archives/C09GRHEQ4AG"><strong>Slack-Workspace</strong></a> oder in unserem <a href="https://discuss.elastic.co/c/search/84"><strong>Diskussionsforum</strong></a> aus.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Elastic Open Web Crawler als Code]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie GitHub Actions verwenden, um Elastic Open Crawler-Konfigurationen zu verwalten, sodass Änderungen, die wir in das Repository übertragen, automatisch auf die bereitgestellte Instanz des Crawlers angewendet werden.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/elastic-open-crawler-config-as-code</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/elastic-open-crawler-config-as-code</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Gustavo Llermaly]]></dc:creator>
            <pubDate>Mon, 22 Sep 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0ac41f55c85ad3f647cb0e0d750ed80bacd397f3-1036x581.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Mit <a href="https://github.com/elastic/crawler">Elastic Open Web Crawler</a> und seiner CLI-gesteuerten Architektur lassen sich versionierte Crawler-Konfigurationen und eine CI/CD-Pipeline mit lokalen Tests jetzt recht einfach realisieren.</p><p>Traditionell war die Verwaltung von Webcrawlern ein manueller, fehleranfälliger Prozess. Dabei ging es um das direkte Bearbeiten von Konfigurationen in der Benutzeroberfläche sowie um das Klonen von Crawl-Konfigurationen, das Zurücksetzen von Einstellungen, die Versionsverwaltung und vieles mehr. Die Behandlung von Crawler-Konfigurationen als Code löst dieses Problem, indem sie die gleichen Vorteile bietet, die wir von der Softwareentwicklung erwarten: Wiederholbarkeit, Nachvollziehbarkeit und Automatisierung.</p><p>Dieser Workflow erleichtert es, den Open Web Crawler in Ihre CI/CD-Pipeline für Rollbacks, Backups und Migrationen einzubinden – Aufgaben, die mit früheren Elastic Crawlern wie dem Elastic Web Crawler oder dem App Search Crawler wesentlich schwieriger waren.</p><p>In diesem Artikel erfahren Sie, wie Sie:</p><ul><li>Verwalten Sie unsere Crawl-Konfigurationen mit GitHub.</li><li>Eine lokale Testumgebung für Pipelines vor der Bereitstellung einrichten</li><li>Wir erstellen eine Produktionsumgebung, um den Webcrawler jedes Mal mit neuen Einstellungen auszuführen, wenn wir Änderungen an unseren Hauptzweig übertragen.</li></ul><p>Das Projekt-Repository finden Sie <em><strong><a href="https://github.com/llermaly/elastic-open-crawler-as-code">hier</a>. </strong>Zum Zeitpunkt der Erstellung dieses Dokuments verwende ich Elasticsearch 9.1.3 und Open Web Crawler 0.4.2.</em></p><h2>Voraussetzungen</h2><ul><li>Docker Desktop</li><li>Elasticsearch-Instanz</li><li>Virtuelle Maschine mit SSH-Zugriff (z. B. AWS EC2) und installiertem Docker</li></ul><h2>Schritte</h2><ol><li>Ordnerstruktur</li><li>Raupenkonfiguration</li><li>Docker-Compose-Datei (lokale Umgebung)</li><li>GitHub Actions</li><li>Lokale Tests</li><li>Bereitstellung in der Produktionsumgebung</li><li>Änderungen vornehmen und erneut bereitstellen</li></ol><h2>Ordnerstruktur</h2><p>Für dieses Projekt werden wir folgende Dateistruktur verwenden:</p><pre><code>├── docker-compose.yml # Local elasticsearch + crawler
├── config/crawler-config.yml # Crawler config
├── .github/workflows/deploy.yml # GH Action to deploy changes
├── local.sh # Script to run our local crawler</code></pre><h2>Raupenkonfiguration</h2><p>Unter <code>crawler-config.yml,</code> wird Folgendes eingetragen:</p><pre><code>output_sink: elasticsearch
output_index: web-crawl-index
max_crawl_depth: 1

elasticsearch:
  host: ${ES_HOST}
  api_key: ${ES_API_KEY}
     
domains:
  - url: https://web-scraping.dev
    seed_urls:
      - https://web-scraping.dev/product/1
      - https://web-scraping.dev/product/2
      - https://web-scraping.dev/product/3</code></pre><p>Dies führt einen Crawl von <a href="https://web-scraping.dev/products">https://web-scraping.dev/products</a> durch, einer simulierten Website für Produkte. Wir werden nur die ersten drei Produktseiten durchsuchen. Die Einstellung <code>max_crawl_depth</code> verhindert, dass der Crawler mehr Seiten als die als <code>seed_urls</code> definierten Seiten entdeckt, indem er die darin enthaltenen Links nicht öffnet.</p><p>Elasticsearch <code>host</code> und <code>api_key</code> werden dynamisch befüllt, abhängig von der Umgebung, in der das Skript ausgeführt wird.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f9831034e1c4ccb554d37bdd188f2824338355a0-890x624.png" style="width: 100%;" /><h2>Docker-Compose-Datei (lokale Umgebung)</h2><p>Für die lokale Umgebung <code>docker-compose.yml,</code> werden wir den Crawler und einen einzelnen Elasticsearch-Cluster + Kibana bereitstellen, damit wir unsere Crawling-Ergebnisse <strong><em>vor</em></strong> der Bereitstellung in der Produktionsumgebung einfach visualisieren können.</p><pre><code>services:
  es01:
    image: docker.elastic.co/elasticsearch/elasticsearch:9.1.3
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
      - ES_JAVA_OPTS=-Xms1g -Xmx1g
    ports:
      - "9200:9200"
    networks: [esnet]
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9200"]
      interval: 5s
      timeout: 5s
      retries: 10

  kibana:
    image: docker.elastic.co/kibana/kibana:9.1.3
    environment:
      - ELASTICSEARCH_HOSTS=http://es01:9200
    ports:
      - "5601:5601"
    networks: [esnet]
    depends_on: [es01]

  crawler:
    image: docker.elastic.co/integrations/crawler:0.4.2
    environment:
      - ES_HOST=http://es01:9200
      - CRAWLER_JRUBY_OPTS=--server
    container_name: crawler
    volumes:
      - ./config:/home/app/config
    networks: [esnet]
    entrypoint: ["/home/app/bin/crawler", "crawl", "/home/app/config/crawl-config-final.yml"]
    stdin_open: true
    tty: true

networks:
  esnet:
    driver: bridge</code></pre><p>Beachten Sie, wie der Crawler wartet, bis Elasticsearch bereit ist, ausgeführt zu werden.</p><h2>GitHub Actions</h2><p>Nun müssen wir eine GitHub-Aktion erstellen, die die neuen Einstellungen kopiert und den Crawler bei jedem Push auf den Hauptzweig in unserer virtuellen Maschine ausführt. Dadurch wird sichergestellt, dass wir immer die aktuellste Konfiguration im Einsatz haben, ohne manuell in die virtuelle Maschine eingreifen zu müssen, um Dateien zu aktualisieren und den Crawler auszuführen. Wir werden AWS EC2 als Anbieter virtueller Maschinen verwenden.</p><p>Der erste Schritt besteht darin, den Host (<code>VM_HOST</code>), den Maschinenbenutzer (<code>VM_USER</code>), den SSH-RSA-Schlüssel (<code>VM_KEY</code>), den Elasticsearch-Host (<code>ES_HOST</code>) und den Elasticsearch-API-Schlüssel (<code>ES_API_KEY</code>) zu den GitHub Action Secrets hinzuzufügen:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e1075bc54151b4b94eac2a6bd2682e9997e6c709-1106x707.png" style="width: 100%;" /><p>Auf diese Weise kann die Aktion auf unseren Server zugreifen, um die neuen Dateien zu kopieren und den Crawl auszuführen.</p><p>Nun erstellen wir unsere <code>.github/workflows/deploy.yml</code> -Datei:</p><pre><code>name: Deploy

on:
  push:
    branches: [main]

jobs:
  Deploy:
    name: Deploy to EC2
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v5

      - name: Deploy crawler
        env:
          HOSTNAME: ${{ secrets.VM_HOST }}
          USER_NAME: ${{ secrets.VM_USER }}
          PRIVATE_KEY: ${{ secrets.VM_KEY }}
          ES_HOST: ${{ secrets.ES_HOST }}
          ES_API_KEY: ${{ secrets.ES_API_KEY }}
        run: |
          # Save private key
          echo "$PRIVATE_KEY" > private_key
          chmod 600 private_key

          # Generate final config locally
          envsubst < config/crawler-config.yml > config/crawl-config-final.yml

          # Copy the config folder to VM
          scp -o StrictHostKeyChecking=no -i private_key -r config ${USER_NAME}@${HOSTNAME}:~/config

          # SSH into VM and run crawler
          ssh -o StrictHostKeyChecking=no -i private_key ${USER_NAME}@${HOSTNAME} << EOF
            docker run --rm \
              -v ~/config:/config \
              docker.elastic.co/integrations/crawler:latest jruby \
              bin/crawler crawl /config/crawl-config-final.yml
          EOF</code></pre><p>Diese Aktion führt jedes Mal die folgenden Schritte aus, wenn wir Änderungen an der Crawler-Konfigurationsdatei vornehmen:</p><ol><li>Tragen Sie den Elasticsearch-Host und den API-Schlüssel in die YAML-Konfiguration ein.</li><li>Kopieren Sie den Konfigurationsordner auf unsere VM</li><li>Stellen Sie über SSH eine Verbindung zu unserer VM her.</li><li>Führen Sie den Crawl mit der Konfiguration aus, die wir gerade aus dem Repository kopiert haben.</li></ol><h2>Lokale Tests</h2><p>Um unseren Crawler lokal zu testen, haben wir ein Bash-Skript erstellt, das den Elasticsearch-Host mit dem lokalen Docker-Repository befüllt und einen Crawl startet. Sie können <code>./local.sh</code> eingeben, um es auszuführen.</p><pre><code>#!/bin/bash

# Exit on any error
set -e

# Load environment variables
export ES_HOST="http://es01:9200"

# Generate final crawler config
envsubst < ./config/crawler-config.yml > ./config/crawl-config-final.yml

# Bring everything up
docker compose up --build</code></pre><p>Schauen wir uns die Kibana DevTools an, um zu bestätigen, dass<code> web-crawler-index</code> korrekt befüllt wurde:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/18551635e8265866e389a9632c4e4540958e4468-990x723.png" style="width: 100%;" /><h2>Bereitstellung in der Produktionsumgebung</h2><p>Jetzt sind wir bereit, die Änderungen auf den Hauptzweig zu übertragen. Dadurch wird der Crawler in Ihrer virtuellen Maschine bereitgestellt und beginnt, Protokolle an Ihre Serverless Elasticsearch-Instanz zu senden.</p><pre><code>git add .
git commit -m "First commit"
git push</code></pre><p>Dadurch wird die GitHub-Aktion ausgelöst, die das Bereitstellungsskript innerhalb der virtuellen Maschine ausführt und mit dem Crawling beginnt.</p><p>Sie können die Ausführung der Aktion überprüfen, indem Sie zum GitHub-Repository gehen und den Tab „Aktionen“ aufrufen:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/67ba3a7164d7a8049fe5661264820826cb18ed64-667x325.png" style="width: 100%;" /><h2>Änderungen vornehmen und erneut bereitstellen</h2><p>Vielleicht ist Ihnen aufgefallen, dass die <code>price</code> jedes Produkts Teil des Body-Felds des Dokuments ist. Ideal wäre es, den Preis in einem separaten Feld zu speichern, damit wir Filter darauf anwenden können.</p><p>Fügen wir diese Änderung zur Datei <code>crawler.yml</code> hinzu, um <a href="https://github.com/elastic/crawler/blob/main/docs/features/EXTRACTION_RULES.md">Extraktionsregeln</a> zu verwenden, mit denen der Preis aus der CSS-Klasse <code>product-price</code> extrahiert werden kann:</p><pre><code>output_sink: elasticsearch
output_index: web-crawl-index
max_crawl_depth: 1

elasticsearch:
  host: ${ES_HOST}
  api_key: ${ES_API_KEY}
     
  # Index ingest pipeline to process documents before indexing          
  pipeline_enabled: true
  pipeline: pricing-pipeline

domains:
  - url: https://web-scraping.dev
    seed_urls:
      - https://web-scraping.dev/product/1
      - https://web-scraping.dev/product/2
      - https://web-scraping.dev/product/3
    extraction_rulesets:
      - url_filters:
          - type: ends
            pattern: /product/*
        rules:
          - action: extract
            field_name: price
            selector: .product-price
            join_as: string
            source: html</code></pre><p>Wir sehen auch, dass der Preis ein Dollarzeichen (<code>$</code>) enthält, das wir entfernen müssen, wenn wir Bereichsabfragen ausführen wollen. Dafür können wir eine Ingest-Pipeline verwenden. Beachten Sie, dass wir in unserer neuen Crawler-Konfigurationsdatei oben darauf verweisen:</p><pre><code>PUT _ingest/pipeline/pricing-pipeline
{
  "processors": [
    {
      "script": {
        "source": """
                ctx['price'] = ctx['price'].replace("$","")
            """
      }
    }
  ]
}</code></pre><p>Wir können diesen Befehl in unserem Elasticsearch-Produktionscluster ausführen. Da die Entwicklungsversion ephemer ist, können wir die Pipeline-Erstellung in die <code>docker-compose.yml</code> -Datei integrieren, indem wir den folgenden Dienst hinzufügen. Beachten Sie, dass wir dem Crawler-Dienst auch eine <code>depends_on</code> hinzugefügt haben, damit er erst startet, nachdem die Pipeline erfolgreich erstellt wurde.</p><pre><code> crawler:
    image: docker.elastic.co/integrations/crawler:0.4.2
    environment:
      - ES_HOST=http://es01:9200
      - CRAWLER_JRUBY_OPTS=--server
    container_name: crawler
    volumes:
      - ./config:/home/app/config
    networks: [esnet]
    entrypoint: ["/home/app/bin/crawler", "crawl", "/home/app/config/crawl-config-final.yml"]
    depends_on:
      pipeline-init:
        condition: service_completed_successfully
    stdin_open: true
    tty: true  


  pipeline-init:
    image: curlimages/curl:latest
    depends_on:
      es01:
        condition: service_healthy
    networks: [esnet]
    entrypoint: >
        sh -c "
        echo 'Creating ingest pipeline...';
        curl -s -X PUT http://es01:9200/_ingest/pipeline/pricing-pipeline \\
          -H 'Content-Type: application/json' \\
          -d '{\"processors\":[{\"script\":{\"source\":\"ctx.price = ctx.price.replace(\\\"$\\\", \\\"\\\")\"}}]}';
        echo 'Pipeline created!';
        "</code></pre><p>Führen wir nun <code>`./local.sh`</code> aus, um die Änderung lokal zu sehen:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/dc1801599344a9f69f072b07ff828c4ba3815d7b-738x473.png" style="width: 100%;" /><p>Großartig! Jetzt lasst uns die Veränderung vorantreiben:</p><pre><code>git add crawler-config.yml
git commit -m "added price CSS selector"
git push</code></pre><p>Um sicherzustellen, dass alles funktioniert, können Sie Ihre Produktions-Kibana-Datei überprüfen. Dort sollten die Änderungen sichtbar sein und der Preis als neues Feld ohne Dollarzeichen angezeigt werden.</p><h2>Fazit</h2><p>Mit dem Elastic Open Web Crawler können Sie Ihren Crawler als Code verwalten. Das bedeutet, dass Sie die gesamte Pipeline – von der Entwicklung bis zur Bereitstellung – automatisieren und beispielsweise ephemere lokale Umgebungen und Tests anhand der gecrawlten Daten programmatisch hinzufügen können.</p><p>Sie sind eingeladen, das offizielle Repository zu klonen und mit der Indizierung Ihrer eigenen Daten mithilfe dieses Workflows zu beginnen. In <a href="https://www.elastic.co/search-labs/blog/semantic-search-open-crawler">diesem Artikel</a> erfahren Sie auch, wie Sie eine semantische Suche auf den vom Crawler erzeugten Indizes durchführen.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Elasticsearchs ES|QL-Editor-Benutzererfahrung im Vergleich zum PPL-Ereignisanalysator von OpenSearch.]]></title>
            <description><![CDATA[Entdecken Sie, wie die erweiterten Funktionen des ES|QL Editors Ihren Workflow beschleunigen und sich damit deutlich vom manuellen Ansatz des PPL Event Analyzer von OpenSearch abheben. 
]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/opensearch-vs-elasticsearch-ppl-esql</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/opensearch-vs-elasticsearch-ppl-esql</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Libby Lin, George Kobar]]></dc:creator>
            <pubDate>Thu, 18 Sep 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f1ff4ff4a00b3e5b084d4116cea6cabc82a2d816-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Die <a href="https://www.elastic.co/blog/getting-started-elasticsearch-query-language">Elasticsearch Query Language</a> (ES|QL), die seit Version 8.14 allgemein verfügbar ist, stellt eine speziell entwickelte Abfragesprache und -engine dar, die für die Bereiche Suche, Beobachtbarkeit und Sicherheitsuntersuchungen konzipiert wurde. Im Gegensatz zur Piped Processing Language (PPL) von OpenSearch, die sich stark an bestehenden Piped-Sprachen orientiert, wurde ES|QL von Grund auf neu entwickelt, um den Fokus auf Eleganz, Benutzerfreundlichkeit und nahtlose Integration in die Kibana-Plattform zu legen.</p><p>In diesem Blogbeitrag werden wir die Entwicklererfahrung des ES|QL-Editors in Elasticsearch 9.1 untersuchen, indem wir sie mit PPL im Event Analyzer (kurz PPL) in OpenSearch 3.2 vergleichen.</p><p>Die Unterschiede werden schnell deutlich: Der ES|QL Editor bietet intelligente Autovervollständigung, kontextbezogene Hilfe, empfohlene Abfragen und clusterübergreifende Abfrageunterstützung, die nicht nur Anfängern, sondern auch Experten gleichermaßen zugutekommen. Das durchdachte Design für die ES|QL-Erstellung zeigt sich auch in der integrierten Abfrageprüfung und der ganzheitlichen Integration durch Kibana-Workflows, beispielsweise mit den zuletzt verwendeten Abfragen.</p><p>PPL hingegen bietet keine vergleichbare Unterstützung für Autovervollständigung, kontextbezogene Hilfestellungen und verteilte Abfragen, was zu einer steileren Lernkurve und mehr Versuch und Irrtum führt.</p><h2>ES|QL einfacher zu erlernen und anzuwenden</h2><p>Der Einstieg in eine neue Abfragesprache kann oft überwältigend sein. Der direkt in <strong>Kibana Discover</strong> integrierte ES|QL - Editor wurde entwickelt, um diesen Prozess zu vereinfachen, indem er nicht nur die Erstellung und das Debuggen von Abfragen unterstützt, sondern auch beschleunigt, wie schnell Sie sich mit der Sprache vertraut machen und sich damit wohlfühlen. Da der Editor dazu beiträgt, Reibungsverluste bei alltäglichen Aufgaben zu reduzieren, können Sie Ihren Fokus von Syntax und Versuch-und-Irrtum-Prinzip auf die Lösungsfindung verlagern. Mehr über diese Prinzipien und wie wir sie in den Editor integriert haben, können Sie <a href="https://www.elastic.co/search-labs/blog/improving-esql-editor-experience-in-kibana">hier</a> lesen.</p><p>Diese Editor-Oberfläche ist nicht auf Discover beschränkt; es handelt sich um ein wiederverwendbares Code-Modul, dessen <strong>Integration in andere Teile von Kibana</strong> wir gerade vorantreiben, wie zum Beispiel Dashboards, Kibana-Alerts und Kibana-Maps.</p><h3>Intelligente Autovervollständigung: Beschleunigt die Erstellung Ihrer Suchanfragen</h3><p>Die Autovervollständigung im ES|QL Editor ist umfassend und bietet Vorschläge für kompatible Funktionen, Argumente, Literale und sogar verschachtelte Funktionen – eine Fähigkeit, die in PPL deutlich fehlt. Tatsächlich wurde es von Grund auf neu aufgebaut, wie <a href="https://www.elastic.co/search-labs/blog/esql-autocomplete-rebuilt">hier</a> beschrieben.</p><p>Die Validierung läuft während der Eingabe durch den Benutzer, wie <a href="https://www.elastic.co/search-labs/blog/improving-esql-editor-experience-in-kibana">hier</a> beschrieben, und schlägt Felder vor sowie benachrichtigt den Benutzer über Fehler. Dies reduziert die mentale Belastung der Benutzer und hilft, Fehler frühzeitig im Abfrageerstellungsprozess zu vermeiden.</p><p>Beispiel: In dieser Verschachtelung werden Felder und kompatible Funktionen vorgeschlagen:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a4d7b2819c34fab31bced7873257b8932b623fba-1502x473.png" style="width: 100%;" /><p>Etwas, das PPL nicht unterstützt:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6f3a89b1255b8a3a762022a2704fdd1c2987e5f9-1013x335.png" style="width: 100%;" /><p>Selbst wenn eine intelligente Autovervollständigung Sie durch kompatible Funktionen, Argumente und verschachtelte Funktionen führt, möchten Sie vielleicht trotzdem ein tieferes Verständnis der verfügbaren Optionen. Genau hier erweist sich die kontextbezogene Hilfe des ES|QL Editors als unschätzbar wertvoll, da sie sofortige Unterstützung direkt im Editor bietet, um Ihre Abfrageentwicklung zu verdeutlichen und zu verbessern.</p><h3>Kontextbezogene Hilfe direkt zur Hand</h3><p>Zusätzliche Informationen zu einem durch die Autovervollständigung generierten Befehl erhalten Sie mit einem Klick Strg+Leertaste. Es erscheint sofort ein Fenster mit Details zu der betreffenden Funktion, dem Argument oder dem Feld. Diese unkomplizierte Interaktion hält die Entwickler im Arbeitsfluss und bietet ihnen bedarfsgerechte Hilfestellung, ohne dass sie den Editor verlassen oder externe Dokumentationen durchsuchen müssen. Dadurch wird der Zeitaufwand für Syntaxprüfungen reduziert und häufige Fehler werden vermieden, bevor sie überhaupt auftreten.</p><p>So sieht es in der Praxis aus:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2797f9b5e002dbd83c46475c4ed4dcdc86144a01-1343x522.gif" style="width: 100%;" /><p>PPL bietet diese Art von integrierter Anleitung nicht, sodass die Benutzer auf externe Dokumente oder das Ausprobieren angewiesen sind. Dieses Fehlen ist nicht nur ein fehlendes Merkmal; es verdeutlicht eine umfassendere Diskrepanz in der Designphilosophie. ES|QL legt Wert auf ein durchdachtes, kontextbezogenes Benutzererlebnis, das sich an die Daten und Arbeitsabläufe des Benutzers anpasst. Dieser Unterschied wird umso deutlicher, je komplexer die Abfragen werden, wodurch der ES|QL Editor eine effizientere und zuverlässigere Umgebung sowohl für Lernzwecke als auch für den Produktiveinsatz darstellt.</p><h3>Empfohlene Abfragen, die den Datenkontext berücksichtigen</h3><p>Der ES|QL-Editor bietet empfohlene Abfragen, die automatisch auf die Daten, mit denen Sie arbeiten, wie z. B. Protokolle, zugeschnitten sind. Statt eines leeren Editors werden die relevantesten Ausgangspunkte für gängige Anwendungsfälle angezeigt. Durch die Auswahl einer empfohlenen Abfrage wird eine kanonische Abfrage generiert, die sofort verwendbar ist und bei Bedarf weiter verfeinert werden kann. Dieser Ansatz beschleunigt die Abfrageentwicklung, insbesondere für neue Benutzer, die die vollständige Syntax möglicherweise noch nicht kennen.</p><p>Hier ist ein Beispiel, bei dem ein Benutzer die Abfrage „Änderungspunkt erkennen“ auswählt:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3284c9340d41298820fbf8c7702abad946b48248-925x370.gif" style="width: 100%;" /><p>Vergleichen Sie das mit der PPL-Erfahrung:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5c3e59dadc6210aede3366bdd081887bcbae7a54-969x798.png" style="width: 100%;" /><p>Im Gegensatz dazu bietet PPL hier nur eine einfache Autovervollständigung, sodass man Abfragen ohne Kontext oder Struktur selbst zusammensetzen muss. Dieser Mangel an Anleitung kann zu Frustration und dem Vorgehen nach dem Prinzip „Versuch und Irrtum“ führen.<br/>Mit den datenbasierten empfohlenen Abfragen des ES|QL-Editors können Sie vermeiden, bei Routineaufgaben von Grund auf neu zu beginnen oder sich Syntax einzuprägen. Der Editor reduziert die kognitive Belastung, hilft, Fehler zu vermeiden, und ermöglicht es Ihnen, sich auf die Problemlösung und übergeordnete Ziele wie die Durchführung clusterübergreifender Suchen zu konzentrieren, anstatt sich mit der Formulierung von Abfragen auseinanderzusetzen.</p><h2>Intuitive clusterübergreifende Abfragen</h2><p>Die Autovervollständigung des ES|QL-Editors ist auch bei der Arbeit mit mehreren Remote-Clustern <a href="https://elastic.aiops.work/search-labs/blog/esql-cross-cluster-search">mit CCS</a> weiterhin überlegen. Hier ist der Grund:</p><h3>Der ES|QL-Editor bietet nahtlose Autovervollständigung auch clusterübergreifend.</h3><p>Die Autovervollständigung im ES|QL-Editor unterstützt nicht nur Clusternamen, sondern auch<strong> lokale und Remote-Indizes</strong>. Wie <a href="https://www.elastic.co/search-labs/blog/esql-cross-cluster-search">hier</a> beschrieben, funktioniert dies dank einer Koordinatorknotenarchitektur, die dabei hilft, den Abfrageplan zu validieren und zu generieren, der an die lokalen Knoten gesendet wird, die Abfrage auszuführen und die Ergebnisse zu aggregieren, bevor sie an den Benutzer zurückgesendet werden. Ohne Eingabe des vollständigen Namens des Remote-Clusters startet die Eingabe von „:“ den Autovervollständigungsprozess für den Remote-Index. Und Sie sind nicht auf das Präfix beschränkt.</p><p>Dadurch wird es einfach, verteilte Datensätze zu finden und abzufragen, ohne sich Namenskonventionen merken oder den Kontext wechseln zu müssen.</p><p>Hier ist ein Beispiel, bei dem der Benutzer einfach „clu:g“ eingibt, um einen Remote-Index zu finden:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/bae1fbacb2320e4d07f41291ea57c9bcf15bf8a5-1092x523.gif" style="width: 100%;" /><p>Im krassen Gegensatz dazu bietet die PPL nur eine grundlegende Vervollständigung für lokale Indizes, wobei die Vorschläge auf Präfixübereinstimmungen beschränkt sind. Remote-Cluster müssen manuell eingegeben werden, was die Fehlerwahrscheinlichkeit erhöht und die Abfrageerstellung verlangsamt.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e38793c0cc7c6cc7dc0fd4779a3e24ffbb6e0838-1094x263.gif" style="width: 100%;" /><p>PPL bietet Vervollständigung nur für lokale Indizes und die Vorschläge sind auf das Präfix beschränkt:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5310f824942f94485cace2558ea72c56a0971e22-862x197.png" style="width: 100%;" /><p>ES|QL geht noch einen Schritt weiter, indem <a href="https://www.elastic.co/docs/solutions/search/cross-cluster-search#exclude-problematic-clusters">es Ausschlüsse direkt über ein negatives Vorzeichen ermöglicht</a> und Ihnen so eine detaillierte Kontrolle darüber gibt, welche Cluster in Ihre Untersuchung einbezogen werden. Diese Funktionalität ist besonders wertvoll bei der Arbeit mit hybriden Umgebungen, in denen Sie bei clusterübergreifenden Untersuchungen möglicherweise bestimmte Datensätze ein- oder ausschließen möchten.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f23ca17f58fbf8e6d27419c028274cb91f30a549-937x78.png" style="width: 100%;" /><p>Diese Verbesserungen spiegeln den umfassenderen Fokus von Elasticsearch auf die Reduzierung von Reibungsverlusten bei der clusterübergreifenden Suche wider. Durch die Vereinfachung der Erstellung und Verwaltung verteilter Abfragen ermöglicht der ES|QL Editor Analysten und Entwicklern, sich auf Erkenntnisse anstatt auf die Syntax zu konzentrieren, während PPL diese Last eher dem Benutzer überlässt. Und genau wie der ES|QL-Editor die Erstellung clusterübergreifender Abfragen vereinfacht, bietet er auch Werkzeuge zur Überprüfung der Ausführung dieser Abfragen und gewährleistet so Transparenz und Leistungsüberwachung über mehrere Cluster hinweg.</p><h3>Analyse der Details der clusterübergreifenden Suche mithilfe des Inspektionstools</h3><p>Das Inspektionstool, das über den ES|QL-Editor zugänglich ist, dient dazu, Metadaten mit expliziten Informationen über die Abfrageausführung in allen Clustern bereitzustellen. Diese Funktionalität ist in Kibana Discover aktiviert und direkt im Query Inspector zugänglich. Dadurch können Sie den Suchfortschritt und Details analysieren, was insbesondere für <strong>die Cross-Cluster Search</strong> (<a href="https://www.elastic.co/docs/reference/query-languages/esql/esql-cross-clusters">CCS</a>) von entscheidender Bedeutung ist. Diese Funktion hilft Ihnen, den Suchfortschritt zu überwachen und zu verstehen, wie Abfragen in verteilten Datensätzen abschneiden.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5d1c204f70171526fff924c30ea8ad08121a0f8d-919x523.gif" style="width: 100%;" /><p>Diese detaillierte Transparenz der Abfrageausführung, insbesondere bei komplexen verteilten Suchvorgängen, ermöglicht es Ihnen, optimale Leistung und Fehlerbehebung zu gewährleisten.</p><p>Über das Verständnis der Funktionsweise einzelner Abfragen hinaus verbessert der ES|QL Editor die Benutzererfahrung zusätzlich, indem er wesentliche Funktionalitäten tief in die gesamte Kibana-Plattform einbettet und so einen nahtlosen und unterbrechungsfreien Arbeitsablauf fördert.</p><h2>Einheitliche Abfrageerfahrung mit ES|QL und Kibana</h2><p>Eine der häufigsten Ursachen für Reibungsverluste bei abfragegesteuerter Analyse ist der Kontextwechsel. Oftmals müssen Sie sich an bereits formulierte Anfragen erinnern. Jede Unterbrechung stört die Konzentration und verlangsamt die Ermittlungen. Der ES|QL-Editor löst dieses Problem durch die Integration des Abfrageverlaufs in Kibana.</p><h3>Aktuelle Suchanfragen</h3><p>Die Funktion <a href="https://www.elastic.co/search-labs/blog/esql-piped-query-language-goes-ga">„Letzte Abfragen“</a> im ES|QL Editor hilft Ihnen, im Arbeitsfluss zu bleiben, indem sie frühere Arbeiten sofort zugänglich macht. Im ES|QL-Editor von Discover können Sie Ihre letzten 20 Abfragen anzeigen, erneut ausführen und mit einem Stern markieren. So ist sichergestellt, dass häufig verwendete oder komplexe Abfragen nur einen Klick entfernt sind. Diese gespeicherten Abfragen werden auch in Kibana übernommen und in Dashboards, Visualisierungen, Benachrichtigungen und Karten integriert, sodass Sie Ihren aktuellen Bildschirm nicht verlassen oder Befehle von Grund auf neu eingeben müssen. Dadurch werden sich wiederholende Arbeiten reduziert, die Ermittlungen beschleunigt und das Fehlerrisiko minimiert.</p><p>Ein Benutzer kann beispielsweise die zuletzt verwendeten Abfragen im ES|QL-Editor in Discover nutzen (und diese mit einem Stern markieren):</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/eb0f9fe37b92dec421c394d31ae7d90afebe062e-1421x793.png" style="width: 100%;" /><p>Die neuesten Suchanfragen sind im Dashboard integriert:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b84cd3a9bdec58812360d2aba4fc7713363ee3cc-1411x797.png" style="width: 100%;" /><p>PPL bietet keine vergleichbare Funktion, sodass Benutzer auf manuelles Kopieren und Einfügen oder externe Notizen angewiesen sind, um Abfragen wiederzuverwenden. Der Unterschied liegt nicht nur in der Bequemlichkeit; er spiegelt die Strategie von Elastic wider, ES|QL als eine wirklich integrierte Sprache innerhalb des Kibana-Ökosystems aufzubauen. Mit Funktionen wie „Letzte Abfragen“ optimiert der ES|QL Editor nicht nur die täglichen Arbeitsabläufe, sondern legt auch den Grundstein für fortgeschrittenere Funktionen, die sich derzeit in der technischen Vorschau befinden, und gewährleistet so eine kontinuierliche Weiterentwicklung des Benutzererlebnisses.</p><h2>Fazit</h2><p>ES|QL ist mehr als nur eine Syntax; es spiegelt die Strategie von Elastic wider, die Art und Weise zu verbessern, wie Benutzer Daten suchen, erkunden und analysieren. Mit intelligenter Autovervollständigung, kontextbezogenen empfohlenen Abfragen, Hilfestellungen im Editor und Tools wie Inspect beschleunigt der ES|QL Editor das Lernen, reduziert Fehler und vereinfacht komplexe Arbeitsabläufe wie die Clusterübergreifende Analyse. Die Integration in Kibana ermöglicht die nahtlose Verbindung von Abfragen mit Dashboards, Warnmeldungen und Visualisierungen für einen unterbrechungsfreien Workflow.</p><p>Zusammenfassend lässt sich sagen, dass ES|QL nicht einfach nur eine weitere Pipe-Sprache ist; es ist eine durchdacht entwickelte Abfrage-Engine in Verbindung mit einer intuitiven Benutzeroberfläche, die die Art und Weise, wie Sie mit Ihren Daten interagieren, grundlegend neu definiert und ein integriertes, intelligentes und sich ständig weiterentwickelndes Erlebnis bietet, das sich deutlich von der oft sequenziellen und weniger geführten Natur von OpenSearch PPL abhebt.</p><h2>Was kommt als Nächstes?</h2><p>Dieser Blog kratzt nur an der Oberfläche von ES|QL. Zukünftige Beiträge werden sich eingehender mit Vergleichen mit OpenSearch PPL befassen und Geodaten-, Visualisierungs- und kommende Editorfunktionen wie <a href="https://www.elastic.co/docs/explore-analyze/dashboards/add-controls">Controls</a> (bereits in Dashboards verfügbar), Registerkarten zur Erkundung mehrerer Daten, Hintergrundsuche, erweiterte Abfragehistorie und FUSE untersuchen.</p><h2>Testen Sie ES|QL noch heute!</h2><p>Sie können ES|QL in vollständig verwalteten Elasticsearch <a href="https://www.elastic.co/cloud/serverless">Serverless</a> -Projekten mit einer <a href="https://www.elastic.co/docs/deploy-manage/deploy/elastic-cloud/create-serverless-project">kostenlosen Testversion</a> ausprobieren. Es ist auch in Versionen ab 8.11 verfügbar, bietet aber das beste Nutzungserlebnis in den <a href="https://www.elastic.co/blog/whats-new-elastic-9-1-0">Versionen 8.19 und 9.1</a>.</p><p>Legen Sie in wenigen Minuten in Ihrer lokalen Umgebung mit einem einzigen Befehl los:</p><pre><code>curl -fsSL https://elastic.co/start-local | sh</code></pre>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Einführung des ES|QL-Abfragegenerators für den Elasticsearch Ruby Client]]></title>
            <description><![CDATA[Lernen Sie, wie Sie den kürzlich veröffentlichten ES|QL-Abfragegenerator für den Elasticsearch Ruby Client verwenden. Ein Tool, um ES|QL-Abfragen mit Ruby-Code einfacher zu erstellen.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/esql-query-builder-elasticsearch-ruby-client</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/esql-query-builder-elasticsearch-ruby-client</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Fernando Briano]]></dc:creator>
            <pubDate>Wed, 17 Sep 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f8e1263ab53d356824a4fc539084151be80899db-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Wir haben kürzlich <a href="https://github.com/elastic/esql-ruby/"><code>elastic-esql</code></a> veröffentlicht, ein Ruby-Gem, das unter der Apache 2-Lizenz veröffentlicht wurde. Dieses Juwel ermöglicht es Ihnen, Elastic <a href="https://www.elastic.co/docs/explore-analyze/query-filter/languages/esql">ES|QL-</a> Abfragen in idiomatischem Ruby zu erstellen, die Sie dann mit der ES|QL-Abfrage-API verwenden können. ES|QL ermöglicht es Entwicklern, in Elasticsearch gespeicherte Daten mittels Abfragen zu filtern, zu transformieren und zu analysieren. Es verwendet &quot;Pipes&quot; ( <code>|</code> ), um die Daten schrittweise zu verarbeiten. Das Gem verwendet stattdessen Ruby-Funktionen, die Sie an das ursprüngliche Objekt anhängen können, um komplexere Abfragen zu erstellen:</p><p><strong>ESQL:</strong></p><pre><code>FROM sample_data | LIMIT 2 | SORT @timestamp DESC</code></pre><p><strong>Rubin:</strong></p><pre><code>Elastic::ESQL.from('sample_data').limit(2).sort('@timestamp').descending</code></pre><h2>Installation</h2><p>Das Gem kann über RubyGems installiert werden mit:</p><pre><code>gem install elastic-esql</code></pre><p>Alternativ kann es der Gemfile-Datei eines Projekts hinzugefügt werden:</p><pre><code>gem 'elastic-esql'</code></pre><h2>Verwendung</h2><p>Sie können entweder eine vollständige Abfrage auf einmal erstellen oder ein Abfrageobjekt mit einem Quellbefehl wie <code>from</code> oder <code>row</code> erstellen und dann ES|QL-Methoden verketten, um darauf aufzubauen.</p><pre><code>query = Elastic::ESQL.from('sample_data')
query.limit(2).sort('@timestamp')</code></pre><p>Das Gem übersetzt den Code in ES|QL für die <code>to_s</code> -Methode, sodass es die ES|QL-Abfrage zurückgibt, wenn sie ausgegeben oder als String gecastet wird:</p><pre><code>query = Elastic::ESQL.from('sample_data').limit(2).sort('@timestamp').descending
query.to_s
# => "FROM sample_data | LIMIT 2 | SORT @timestamp DESC"</code></pre><p>Sie können ein Abfrageobjekt instanziieren und seinen Anfangszustand verändern, indem Sie die <code>!</code> -Äquivalente jeder Funktion verwenden:</p><pre><code>query = Elastic::ESQL.from('sample_data')
query.to_s
# => "FROM sample_data"
query.limit!(2).sort!('@timestamp')
query.to_s
# => "FROM sample_data | LIMIT 2 | SORT @timestamp"</code></pre><p>Das Tool bietet bequeme Möglichkeiten, zusätzliche Schritte an eine ES|QL-Funktion anzuhängen, wie z. B. <code>enrich</code> und <code>sort</code>. Sobald Sie <code>enrich</code> auf einem <code>Elastic::ESQL</code> -Objekt aufgerufen haben, können Sie <code>on</code> und <code>with</code> daran anhängen:</p><pre><code>esql.enrich!('policy').on('a').with({ name: 'language_name' })</code></pre><p>Sie können auch <code>desc</code>, <code>asc</code>, <code>nulls_first</code> und <code>nulls_last</code> an Ihre Abfrage anhängen, nachdem Sie <code>sort</code> verwendet haben:</p><pre><code>Elastic::ESQL.from('sample_data').sort('@timestamp').asc.to_s
# => 'FROM sample_data | SORT @timestamp ASC'

Elastic::ESQL.from('sample_data').sort('@timestamp').desc.nulls_first.to_s
# => 'FROM sample_data | SORT @timestamp DESC NULLS FIRST'</code></pre><p>Es unterstützt auch benutzerdefinierte Zeichenketten, falls Sie die ES|QL-Abfrage selbst schreiben oder eine Funktion nutzen möchten, die noch nicht in die Bibliothek aufgenommen wurde. <code>custom</code> wird die Zeichenketten am Ende der Abfrage zusammenfügen. Die Zeichen werden beim Senden an die Funktion hinzugefügt, ohne dabei Pipe-Zeichen einzufügen. Sie werden durch ein Leerzeichen mit dem Rest der Anfrage verbunden.</p><pre><code>esql = Elastic::ESQL.from('sample_data')
esql.custom('| MY_VALUE = "test value"').to_s
# => 'FROM sample_data | MY_VALUE = "test value"'</code></pre><p>Sie können auch <code>custom</code> -Funktionen verketten:</p><pre><code>esql.custom('| MY_VALUE = "test value"').custom('| ANOTHER, VALUE')
'FROM sample_data | MY_VALUE = "test value" | ANOTHER, VALUE'</code></pre><h2>Verwendung des ES|QL Query Builders mit dem Ruby-Client</h2><p>Sie können den Query Builder direkt mit <a href="https://github.com/elastic/elasticsearch-ruby">elasticsearch-ruby</a> und der <code>esql.query</code> API verwenden, indem Sie das Query-Objekt senden:</p><pre><code>require 'elasticsearch'
require 'elastic/esql'

client = Elasticsearch::Client.new
index = 'sample_data'

query = Elastic::ESQL.from(index)
                     .sort('@timestamp')
                     .desc
                     .where('event_duration > 5000000')
                     .limit(3)
                     .eval({ duration_ms: 'ROUND(event_duration/1000000.0, 1)' })
client.esql.query(body: { query: query })</code></pre><p>Sie können es auch mit dem ES|QL-Helper des Elasticsearch Ruby-Clients verwenden. <a href="https://www.elastic.co/search-labs/blog/esql-ruby-helper-elasticsearch">Weitere Informationen finden Sie hier</a>:</p><pre><code>require 'elasticsearch/helpers/esql_helper'

Elasticsearch::Helpers::ESQLHelper.query(client, query)</code></pre><h2>Als eigenständiges Werkzeug</h2><p>Das Gem ist als eigenständiges Werkzeug konzipiert, um ES|QL-Abfragen auf idiomatische Weise zu erstellen. Es hat keine Laufzeitabhängigkeiten; Sie können es mit dem offiziellen Elasticsearch Ruby-Client oder auch eigenständig verwenden.</p><p>Die generierte Abfrage kann mit der <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-esql-query"><code>esql.query</code></a> API auf jede Art und Weise verwendet werden, wie eine Anwendung mit der Elasticsearch API interagiert (Ruby oder nicht). Sobald eine Abfrage mit <code>elastic-esql</code> erstellt wurde, kann die generierte Zeichenkette als Parameter <code>query</code> im Anfragetext an die API gesendet werden. </p><p>Ich habe bereits über <a href="https://www.elastic.co/search-labs/blog/elasticsearch-ruby-tools">die Verwendung von Elasticsearch mit gängigen Ruby-Tools</a> geschrieben. Dieses Gem kann mit allen gängigen Ruby-Tools verwendet werden, um Elasticsearch mit ES|QL abzufragen.</p><h2>Fazit</h2><p>Diese Bibliothek befindet sich in aktiver Entwicklung, und die endgültige API ist noch nicht fertiggestellt. Es ist aktuell als technische Vorschauversion veröffentlicht. Sollten Sie Feedback zur aktuellen API oder zur allgemeinen Nutzung haben, zögern Sie bitte nicht <a href="https://github.com/elastic/esql-ruby/issues">, ein neues Issue zu eröffnen</a>. Weitere Informationen zum Ruby ES|QL Query Builder finden Sie in <a href="https://github.com/elastic/esql-ruby/?tab=readme-ov-file#ruby-esql-query-builder">der README-Datei</a> .</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Vektorsuchfilterung: Relevanz beibehalten]]></title>
            <description><![CDATA[Es reicht nicht aus, eine Vektorsuche durchzuführen, um die ähnlichsten Ergebnisse zu einer Suchanfrage zu finden. Um die Suchergebnisse einzugrenzen, ist häufig das Filtern erforderlich. Dieser Artikel erklärt, wie die Filterung bei der Vektorsuche in Elasticsearch und Apache Lucene funktioniert.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/vector-search-filtering</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/vector-search-filtering</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Carlos Delgado]]></dc:creator>
            <pubDate>Wed, 03 Sep 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/03b1dd4c7bda4fbabd8e374bc2e4f12d5be6ef5f-1600x1150.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Eine Vektorsuche reicht nicht aus, um relevante Ergebnisse zu finden. Es ist sehr üblich, Filterkriterien zu verwenden, die dabei helfen, die Suchergebnisse einzugrenzen und irrelevante Ergebnisse herauszufiltern.</p><p>Das Verständnis der Funktionsweise von Filtern bei der Vektorsuche hilft Ihnen, die Kompromisse zwischen Leistung und Trefferquote auszubalancieren und einige der Optimierungen zu entdecken, die verwendet werden, um die Vektorsuche bei Verwendung von Filtern leistungsfähig zu gestalten.</p><h2>Warum filtern?</h2><p>Die Vektorsuche hat die Art und Weise, wie wir relevante Informationen in großen Datensätzen finden, revolutioniert und ermöglicht es uns, Elemente zu entdecken, die einer Suchanfrage semantisch ähnlich sind.</p><p>Es genügt jedoch nicht, einfach nur ähnliche Artikel zu finden. Oftmals müssen wir die Suchergebnisse anhand spezifischer Kriterien oder Attribute eingrenzen.</p><p>Stellen Sie sich vor, Sie suchen in einem Online-Shop nach einem Produkt. Eine reine Vektorsuche zeigt Ihnen möglicherweise visuell ähnliche Artikel an, aber Sie möchten vielleicht auch nach Preisspanne, Marke, Verfügbarkeit oder Kundenbewertungen filtern. Ohne Filterfunktion würden Ihnen unzählige ähnliche Produkte präsentiert, was es schwierig macht, genau das zu finden, was Sie suchen.</p><p>Durch die Filterung wird eine präzise Kontrolle über die Suchergebnisse ermöglicht, sodass sichergestellt wird, dass die abgerufenen Elemente nicht nur semantisch übereinstimmen, sondern auch alle notwendigen Anforderungen erfüllen. Dies führt zu einem wesentlich genaueren, effizienteren und benutzerfreundlicheren Sucherlebnis.</p><p>Hier liegt die Stärke von Elasticsearch und Apache Lucene – die effektive Filterung über verschiedene Datentypen hinweg ist einer der Hauptunterschiede zu anderen Vektordatenbanken.</p><h2>Filterung für exakte Vektorsuche</h2><p>Es gibt zwei Hauptmethoden zur Durchführung exakter Vektorsuchen:</p><ul><li>Verwendung des Indextyps <code>flat</code> für Ihr dense_vector-Feld. Dies bewirkt, dass <code>knn</code> -Suchen eine exakte Suche anstelle einer approximativen Suche verwenden.</li><li>Die Punktzahl wird mithilfe einer <a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-script-score-query#vector-functions">script_score-Abfrage</a> berechnet, die Vektorfunktionen verwendet. Dies kann mit jedem Indextyp verwendet werden.</li></ul><p>Bei der Ausführung einer exakten Vektorsuche werden alle Vektoren mit der Suchanfrage verglichen. In diesem Szenario verbessert das Filtern die Leistung, da nur die Vektoren verglichen werden müssen, die den Filter passieren.</p><p>Dies hat keinen Einfluss auf die Ergebnisqualität, da ohnehin alle Vektoren berücksichtigt werden. Wir filtern bereits im Voraus die Ergebnisse heraus, die nicht interessant sind, um die Anzahl der Operationen zu reduzieren.</p><p>Dies ist sehr wichtig, da eine exakte Suche gegenüber einer approximativen Suche performanter sein kann, wenn die angewendeten Filter zu einer geringen Anzahl von Dokumenten führen.</p><p>Als Faustregel gilt: Verwenden Sie die exakte Suche, wenn weniger als 10.000 Dokumente den Filter passieren. <a href="https://www.elastic.co/search-labs/blog/better-binary-quantization-lucene-elasticsearch">BBQ</a> -Indizes sind für Vergleiche wesentlich schneller, daher ist es sinnvoll, bei weniger als 100.000 Einträgen für die Basisindizes die exakte Suche zu verwenden. Weitere Details finden Sie in <a href="https://www.elastic.co/search-labs/blog/knn-exact-vs-approximate-search">diesem Blogbeitrag</a> .</p><p>Falls Ihre Filter immer sehr restriktiv sind, könnten Sie erwägen, die Indizierung auf die exakte Suche anstatt auf die ungefähre Suche auszurichten, indem Sie einen <code>flat</code> -Indextyp anstelle eines HNSW-basierten Index verwenden. Weitere Details finden Sie in <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/dense-vector#dense-vector-params">den Eigenschaften von index_options</a>.</p><h2>Filterung für die approximative Vektorsuche</h2><p>Bei der Durchführung einer approximativen Vektorsuche tauschen wir Ergebnisgenauigkeit gegen Leistung ein. Vektorsuchdatenstrukturen wie HNSW suchen effizient nach ungefähren nächsten Nachbarn in Millionen von Vektoren. Sie konzentrieren sich darauf, die ähnlichsten Vektoren zu finden, indem sie die geringste Anzahl an Vektorvergleichen durchführen, deren Berechnung aufwändig ist.</p><p>Dies bedeutet, dass andere Filterattribute nicht Teil der Vektordaten sind. Verschiedene Datentypen verfügen über eigene Indexierungsstrukturen, die effizient zum Auffinden und Filtern dieser Daten sind, wie beispielsweise Termwörterbücher, Beitragslisten und Dokumentwerte.</p><p>Da diese Datenstrukturen vom Vektorsuchmechanismus getrennt sind, wie wenden wir Filter auf die Vektorsuche an? Es gibt zwei Möglichkeiten: Filter nach der Vektorsuche (Nachfilterung) oder vor der Vektorsuche (Vorfilterung) anwenden.</p><p>Jede dieser Optionen hat Vor- und Nachteile. Lasst uns tiefer in diese Materie eintauchen!</p><h3>Nachfilterung</h3><p>Die Nachfilterung wendet Filter an, nachdem die Vektorsuche durchgeführt wurde. Dies bedeutet, dass die Filter angewendet werden, nachdem die k ähnlichsten Vektorergebnisse gefunden wurden.</p><p>Offensichtlich können wir nach Anwendung der Filter auf die Ergebnisse unter Umständen weniger als k Ergebnisse erhalten. Wir könnten natürlich mehr Ergebnisse durch eine Vektorsuche erhalten (höherer k-Wert), aber wir können nicht sicher sein, dass wir nach Anwendung der Filter k oder mehr Ergebnisse erhalten.</p><p>Der Vorteil der Nachfilterung besteht darin, dass sie das Laufzeitverhalten der Vektorsuche nicht verändert – die Vektorsuche ist sich der Filterung nicht bewusst. Dies ändert jedoch die endgültige Anzahl der abgerufenen Ergebnisse.</p><p>Nachfolgend ein Beispiel für die Nachfilterung mithilfe der <a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-knn-query">knn-Abfrage</a>. Prüfen Sie, ob die Filterklausel von der knn-Abfrage getrennt ist:</p><pre><code>{
  "query": {
    "bool": {
      "must": {
        "knn": {
          "field": "image-vector",
          "query_vector": [54, 10, -2],
          "k": 5,
          "num_candidates": 50
        }
      },
      "filter": {
        "term": {
          "file-type": "png"
        }
      }
    }
  }
}</code></pre><p>Für die KNN-Suche ist auch eine Nachfilterung mit <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/filter-search-results#post-filter">dem Postfilter</a> möglich:</p><pre><code>{
  "knn": {
    "field": "image-vector",
    "query_vector": [54, 10, 2],
    "k": 5,
    "num_candidates": 50
  },
  "post_filter": {
    "term": {
      "file-type": "png"
    }
  }
}</code></pre><p>Beachten Sie, dass Sie bei der knn-Suche einen expliziten Post-Filter-Abschnitt verwenden müssen. Wenn Sie keinen Nachfilter verwenden, <a href="https://www.elastic.co/docs/solutions/search/vector/knn#_combine_approximate_knn_with_other_features">kombiniert die kNN-Suche die Ergebnisse der nächsten Nachbarn</a> mit anderen Abfragen oder Filtern, anstatt einen Nachfilter anzuwenden.</p><h3>Vorfilterung</h3><p>Durch das Anwenden von Filtern vor der Vektorsuche werden zunächst die Dokumente abgerufen, die den Filtern entsprechen, und diese Informationen werden dann an die Vektorsuche weitergegeben.</p><p>Lucene verwendet <a href="https://github.com/apache/lucene/blob/7a60d7ce92392181e137361336e5196bd486cdd9/lucene/core/src/java/org/apache/lucene/util/BitSet.java">BitSets</a> , um die Dokumente, die die Filterbedingung erfüllen, effizient zu speichern. Anschließend durchläuft die Vektorsuche den HNSW-Graphen und berücksichtigt dabei die Dokumente, die die Bedingung erfüllen. Bevor ein Kandidat zu den Ergebnissen hinzugefügt wird, wird geprüft, ob er im BitSet gültiger Dokumente enthalten ist.</p><p>Allerdings muss der Kandidat untersucht und mit der Anfrage verglichen werden, selbst wenn es sich nicht um ein gültiges Dokument handelt. Die Effektivität von HNSW beruht auf der Verbindung zwischen den Vektoren im Graphen – wenn wir die Untersuchung eines Kandidaten abbrechen würden, hieße das, dass wir möglicherweise auch seine Nachbarn überspringen würden.</p><p>Stellen Sie es sich so vor, als würden Sie zu einer Tankstelle fahren. Wenn Sie alle Straßen ausschließen, auf denen sich keine Tankstelle befindet, ist es unwahrscheinlich, dass Sie Ihr Ziel erreichen. Andere Straßen sind vielleicht nicht das, was Sie brauchen, aber sie <em>verbinden</em> Sie mit Ihrem Ziel. Gleiches gilt für Vektoren in einem HNSW-Diagramm!</p><p>Daraus folgt, dass die Anwendung von Vorfiltern weniger effizient ist als der Verzicht auf Filter. Wir müssen die Arbeit an <em>allen</em> Vektoren durchführen, die wir bei unserer Suche besuchen, und diejenigen verwerfen, die nicht dem Filter entsprechen. Wir investieren mehr Arbeit und nehmen uns mehr Zeit, um unsere Top-K-Ergebnisse zu erzielen.</p><p>Nachfolgend ein Beispiel für Vorfilterung in der Elasticsearch Query DSL. Prüfen Sie, ob die Filterklausel nun Teil des knn-Abschnitts ist:</p><pre><code>{
  "knn": {
    "field": "image-vector",
    "query_vector": [54, 10, -2],
    "k": 5,
    "num_candidates": 50,
    "filter": {
      "term": {
        "file-type": "png"
      }
    }
  }
}</code></pre><p>Eine Vorfilterung ist sowohl für <a href="https://www.elastic.co/docs/solutions/search/vector/knn#knn-search-filter-example">die KNN-Suche</a> als auch für <a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-knn-query#knn-query-filtering">die KNN-Abfrage</a> verfügbar:</p><pre><code>{
  "query": {
    "knn": {
      "field": "image-vector",
      "query_vector": [-5, 9, -12],
      "k": 5,
      "filter": {
        "term": {
          "file-type": "png"
        }
      }
    }
  }
}</code></pre><h4>Vorfilteroptimierungen</h4><p>Es gibt einige Optimierungen, die wir anwenden können, um eine effiziente Vorfilterung zu gewährleisten.</p><p>Wir können auf die exakte Suche umschalten, wenn der Filter sehr restriktiv ist. Wenn nur wenige Vektoren zu vergleichen sind, ist es schneller, eine exakte Suche in den wenigen Dokumenten durchzuführen, die dem Filter entsprechen.</p><p>Dies ist eine Optimierung, die in <a href="https://github.com/apache/lucene/blob/eb876b618da5d04c1ad14b04a48321638318493a/lucene/core/src/java/org/apache/lucene/search/AbstractKnnVectorQuery.java#L218">Lucene</a> und Elasticsearch automatisch angewendet wird.</p><p>Eine weitere Optimierungsmethode besteht darin, die Vektoren zu ignorieren, die den Filter nicht erfüllen. Stattdessen prüft diese Methode die Nachbarn der gefilterten Vektoren, die den Filter passieren. Dieser Ansatz reduziert effektiv die Anzahl der Vergleiche, da die gefilterten Vektoren nicht berücksichtigt werden, und untersucht weiterhin Vektoren, die mit dem aktuellen Pfad verbunden sind.</p><p>Dieser Algorithmus heißt ACORN-1, und der Prozess wird in <a href="https://www.elastic.co/search-labs/blog/filtered-hnsw-knn-search">diesem Blogbeitrag</a> ausführlich beschrieben.</p><h2>Filtern mithilfe der Dokumentensicherheit</h2><p><a href="https://www.elastic.co/docs/deploy-manage/users-roles/cluster-or-deployment-auth/controlling-access-at-document-field-level#document-level-security">Document Level Security (DLS)</a> ist eine Elasticsearch-Funktion, die festlegt, welche Dokumente Benutzerrollen abrufen können.</p><p>DLS wird mittels Abfragen durchgeführt. Einer Rolle kann eine Abfrage zugeordnet sein, die die Dokumente einschränkt, die ein Benutzer dieser Rolle aus den Indizes abrufen kann.</p><p>Die Rollenabfrage dient als Filter, um <a href="https://github.com/elastic/elasticsearch/blob/c3a1cb34294e902a9f46d7e840ea09965019f456/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/accesscontrol/SecurityIndexReaderWrapper.java#L92">die Dokumente abzurufen, die ihr entsprechen</a>, und wird als BitSet zwischengespeichert. Dieses BitSet wird dann verwendet, um den zugrunde liegenden Lucene-Reader zu umschließen, sodass nur die Dokumente, die von der Abfrage zurückgegeben wurden, als <em>aktiv</em>gelten – das heißt, sie existieren im Index und wurden nicht gelöscht.</p><p>Da die Live-Dokumente <a href="https://github.com/apache/lucene/blob/a211d30097a8e3264d3ef073a054bd31eb847231/lucene/core/src/java/org/apache/lucene/search/AbstractKnnVectorQuery.java#L196">vom Reader abgerufen</a> werden, um die kNN-Abfrage durchzuführen, werden nur die dem Benutzer zur Verfügung stehenden Dokumente berücksichtigt. Falls ein Vorfilter vorhanden ist, werden die DLS-Dokumente <a href="https://github.com/apache/lucene/blob/a211d30097a8e3264d3ef073a054bd31eb847231/lucene/core/src/java/org/apache/lucene/search/AbstractKnnVectorQuery.java#L204">diesem hinzugefügt</a>.</p><p>Dies bedeutet, dass die DLS-Filterung als Vorfilter für die approximative Vektorsuche fungiert, mit denselben Auswirkungen auf die Leistung und den gleichen Optimierungen.</p><p>Die DLS-Suche mit exakter Suche bietet die gleichen Vorteile wie die Anwendung eines beliebigen Filters – je weniger Dokumente aus der DLS abgerufen werden, desto effizienter ist die exakte Suche. Berücksichtigen Sie auch die Anzahl der von DLS zurückgegebenen Dokumente – wenn die DLS-Rollen sehr restriktiv sind, sollten Sie die Verwendung einer exakten Suche anstelle einer ungefähren Suche in Betracht ziehen.</p><h2>Benchmarking</h2><p>Wir bei Elasticsearch möchten sicherstellen, dass die Vektorsuchfilterung effizient ist. Wir haben <a href="https://elasticsearch-benchmarks.elastic.co/#tracks/so_vector/nightly/default/90d">einen speziellen Benchmark für die Vektorfilterung</a> , der approximative Vektorsuchen mit unterschiedlichen Filtern durchführt, um sicherzustellen, dass die Vektorsuche relevante Ergebnisse so schnell wie möglich liefert.</p><p>Prüfen Sie die <a href="https://elasticsearch-benchmark-analytics.elastic.co/app/dashboards#/view/43b63e80-5ba2-11ed-aede-a742809feed4?_g=(refreshInterval:(pause:!t,value:60000),time:(from:&#x27;2025-05-28T01:27:58.456Z&#x27;,to:&#x27;2025-06-30T13:53:26.430Z&#x27;))&amp;_a=()">Verbesserungen,</a> die mit der Einführung von ACORN-1 einhergingen. Bei Tests, bei denen nur 2 % der Vektoren den Filter passieren, reduziert sich die Abfragelatenz auf 55 % der ursprünglichen Dauer:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3eac3748a33376fc97d957364a5c1f5108d5c58b-1023x896.png" style="width: 100%;" /><h2>Fazit</h2><p>Filtern ist ein integraler Bestandteil der Suche. Die Gewährleistung einer leistungsfähigen Filterung bei der Vektorsuche sowie das Verständnis der damit verbundenen Kompromisse und Optimierungsmöglichkeiten entscheiden darüber, ob eine effiziente und genaue Suche gelingt oder scheitert.</p><p>Die Filterung beeinflusst die Leistung bei der Vektorsuche:</p><ul><li>Die exakte Suche ist schneller, wenn Filter verwendet werden. Sie sollten die Verwendung einer exakten Suche anstelle einer ungefähren Suche in Betracht ziehen, wenn Ihre Filterkriterien ausreichend restriktiv sind. Dies ist eine automatische Optimierung in Elasticsearch.</li><li>Die ungefähre Suche ist langsamer, wenn Vorfilter verwendet werden. Durch die Vorfilterung erhalten wir die ersten k Ergebnisse, die dem Filter entsprechen, allerdings auf Kosten einer langsameren Suche.</li><li>Die Nachfilterung liefert nicht unbedingt die obersten k Ergebnisse, da diese bereits beim Anwenden des Filters herausgefiltert worden sein können.</li></ul><p>Viel Spaß beim Filtern!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Elasticsearch-Shards und -Replikate: Ein praktischer Leitfaden]]></title>
            <description><![CDATA[Machen Sie sich mit den Konzepten von Elasticsearch-Shards und -Replikaten vertraut und lernen Sie, wie Sie diese optimieren können.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/elasticsearch-shards-and-replicas-guide</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/elasticsearch-shards-and-replicas-guide</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Piotr Przybyl]]></dc:creator>
            <pubDate>Thu, 14 Aug 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7775c44f01f2516c4ff4cce6d6bbe9e7b2c38908-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Elasticsearch erweitert die Leistungsfähigkeit von Lucene, indem es ein verteiltes System darauf aufbaut, wodurch die Probleme der Skalierbarkeit und Fehlertoleranz gelöst werden. Es stellt außerdem eine JSON-basierte REST-API bereit, wodurch die Interoperabilität mit anderen Systemen sehr einfach ist.</p><p>Verteilte Systeme wie Elasticsearch können sehr komplex sein, und es gibt viele Faktoren, die ihre Leistungsfähigkeit und Stabilität beeinflussen können. <strong>Shards</strong> gehören zu den grundlegendsten Konzepten in Elasticsearch, und das Verständnis ihrer Funktionsweise ermöglicht Ihnen die effektive Verwaltung eines Elasticsearch-Clusters.</p><p>Dieser Artikel erklärt, was primäre und Replikat-Shards sind, welche Auswirkungen sie auf einen Elasticsearch-Cluster haben und welche Tools es gibt, um sie an unterschiedliche Anforderungen anzupassen.</p><h2>Scherben verstehen</h2><p>Die Datenmenge in einem Elasticsearch-Index kann enorm anwachsen. Um die Verwaltung überschaubar zu halten, wird jedes Datenelement in einem Index gespeichert, und Indizes sind ein Index, der in eine Anzahl von <strong>Shards</strong> aufgeteilt ist. Jeder Elasticsearch-Shard ist ein Apache Lucene-Index, wobei jeder einzelne Lucene-Index eine Teilmenge der Dokumente im Elasticsearch-Index enthält. Durch die Aufteilung der Indizes auf diese Weise bleibt die Ressourcennutzung unter Kontrolle. Ein Apache Lucene-Index hat eine Begrenzung von 2.147.483.519 (2³¹ - 129) Dokumenten.</p><p>Manchmal müssen Indizes zum Zweck der Neuausrichtung zwischen Knoten verschoben werden. Da dieser Prozess sowohl zeit- als auch ressourcenintensiv sein kann, sollten die Indizes nicht zu groß werden, was dazu beiträgt, die Wiederherstellungszeit überschaubar zu halten. Da Indizes aus Lucene-Segmenten bestehen, die ständig zusammengeführt werden müssen, ist es außerdem wichtig, dass die Segmente nicht zu groß werden. Aus diesen Gründen teilt Elasticsearch die Indexdaten in kleinere, besser handhabbare Teile auf, sogenannte <strong>primäre Shards</strong>, die sich leichter auf mehrere Maschinen verteilen lassen. <strong>Replikat-</strong> Shards sind einfach eine exakte Kopie eines entsprechenden primären Shards. Ihre Funktion werden wir später in diesem Artikel erläutern.</p><p>Die richtige Anzahl an Shards ist wichtig für die Performance. Daher ist es ratsam, im Voraus zu planen. Wenn Abfragen parallel über verschiedene Shards ausgeführt werden, sind sie schneller als ein Index, der aus einem einzigen Shard besteht, allerdings nur dann, wenn sich jeder Shard auf einem anderen Knoten befindet und genügend Knoten im Cluster vorhanden sind. Gleichzeitig verbrauchen Shards jedoch Arbeitsspeicher und Festplattenspeicher, sowohl im Hinblick auf indizierte Daten als auch auf Cluster-Metadaten. Zu viele Shards (auch als Oversharding bezeichnet) können Abfragen, Indexierungsanforderungen und Verwaltungsvorgänge verlangsamen, daher ist die Aufrechterhaltung des richtigen Gleichgewichts von entscheidender Bedeutung.</p><p>Die Anzahl der primären Shards wird zum Zeitpunkt der Indexerstellung <strong>für die jeweilige Indexinstanz</strong> festgelegt. Falls Sie später eine andere Anzahl primärer Shards benötigen, können Sie die<strong> Resize-APIs</strong>verwenden –<a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-indices-split">split</a> (mehr primäre Shards), <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-indices-shrink">shrink</a> (weniger primäre Shards) oder <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-indices-clone">clone</a> (die gleiche Anzahl primärer Shards mit neuen Einstellungen für Replikate). Diese Operationen kopieren Lucene-Segmente und <strong>vermeiden eine vollständige Neuindizierung aller Dokumente</strong>. Beim Erstellen eines Index können Sie die Anzahl der primären und Replikat-Shards als Indexeinstellungen festlegen:</p><pre><code>PUT /sensor
{
   "settings" : {
       "index" : {
           "number_of_shards" : 6,
           "number_of_replicas" : 2
       }
   }
}</code></pre><p>(Wenn Sie die Anzahl der Shards oder Replikate nicht angeben, ist der Standardwert für beides 1 (Stand: Elasticsearch 7.0). Die ideale Anzahl an Shards sollte anhand der Datenmenge im Index bestimmt werden. Im Allgemeinen <a href="https://www.elastic.co/docs/deploy-manage/production-guidance/optimize-performance/size-shards">sollte ein optimaler Shard 10-50 GB an Daten enthalten</a>, mit weniger als 200 Millionen Dokumenten pro Shard. Wenn Sie beispielsweise erwarten, dass sich täglich etwa 300 GB an Anwendungsprotokollen ansammeln, wären etwa 10 Shards in diesem Index angemessen, vorausgesetzt, Sie verfügen über eine ausreichende Anzahl von Knoten, um diese zu hosten.</p><p>Während ihrer Lebensdauer können Scherben verschiedene Zustände durchlaufen, darunter:</p><ul><li><strong>Initialisierung:</strong> Ein Anfangszustand, bevor der Shard verwendet werden kann.</li><li><strong>Gestartet:</strong> Ein Zustand, in dem der Shard aktiv ist und Anfragen empfangen kann.</li><li><strong>Verschieben:</strong> Ein Zustand, der eintritt, wenn Shards gerade auf einen anderen Knoten verschoben werden. Dies kann unter bestimmten Bedingungen erforderlich sein, beispielsweise wenn auf dem Knoten, auf dem sie sich befinden, der Speicherplatz knapp wird.</li><li><strong>Nicht zugewiesen:</strong> Der Status eines Shards, der nicht zugewiesen werden konnte. Wenn dies geschieht, wird ein Grund angegeben, beispielsweise wenn sich der Knoten, auf dem der Shard gehostet wird, nicht mehr im Cluster befindet <em>(NODE_LEFT)</em> oder wenn die Wiederherstellung in einen geschlossenen Index erfolgt <em>(EXISTING_INDEX_RESTORED).</em></li></ul><p>Um alle Shards, deren Zustände und weitere Metadaten anzuzeigen, können Sie die folgende Anfrage verwenden:</p><pre><code>GET _cat/shards</code></pre><p>Um Shards für einen bestimmten Index anzuzeigen, können Sie den Namen des Index an die URL anhängen, zum Beispiel sensor:</p><pre><code>GET _cat/shards/sensor</code></pre><p>Dieser Befehl erzeugt eine Ausgabe, wie im folgenden Beispiel. Standardmäßig enthalten die angezeigten Spalten den Namen des Index, den Namen (d. h. Nummer) des Shards, ob es sich um einen primären Shard oder eine Replik handelt, sein Status, die Anzahl der Dokumente, die Größe auf der Festplatte sowie die IP-Adresse und die Knoten-ID des Knotens, auf dem sich der Shard befindet.</p><pre><code>sensor 5 p STARTED    0  283b 127.0.0.1 ziap
sensor 5 r UNASSIGNED                  
sensor 2 p STARTED    1 3.7kb 127.0.0.1 ziap
sensor 2 r UNASSIGNED                  
sensor 3 p STARTED    3 7.2kb 127.0.0.1 ziap
sensor 3 r UNASSIGNED                  
sensor 1 p STARTED    1 3.7kb 127.0.0.1 ziap
sensor 1 r UNASSIGNED                  
sensor 4 p STARTED    2 3.8kb 127.0.0.1 ziap
sensor 4 r UNASSIGNED                  
sensor 0 p STARTED    0  283b 127.0.0.1 ziap
sensor 0 r UNASSIGNED</code></pre><h2>Repliken verstehen</h2><p>Während jeder Shard nur eine einzige Kopie der Daten enthält, kann ein Index mehrere Kopien des Shards enthalten. Es gibt also zwei Arten von Shards, den <strong>primären Shard</strong> und eine Kopie oder <strong>Replik</strong>. Jede Replik eines primären Shards befindet sich immer auf einem anderen Knoten, was eine hohe Verfügbarkeit Ihrer Daten im Falle eines Knotenausfalls gewährleistet. Neben der Redundanz und ihrer Rolle bei der Vermeidung von Datenverlust und Ausfallzeiten können Replikate auch zur Steigerung der Suchleistung beitragen, indem sie die parallele Verarbeitung von Abfragen mit dem primären Shard und damit eine schnellere Verarbeitung ermöglichen.</p><p>Es gibt einige wichtige Unterschiede im Verhalten von primären und Replikat-Shards. Beide können zwar Anfragen verarbeiten, Indexierungsanfragen (d. h. Daten, die dem Index hinzugefügt werden, müssen zuerst die primären Shards durchlaufen, bevor sie auf die Replikat-Shards repliziert werden können. Wie bereits erwähnt, wird, wenn ein primärer Shard nicht mehr verfügbar ist – beispielsweise aufgrund einer Knotenunterbrechung oder eines Hardwareausfalls –, eine Replik zum Nachfolger befördert, um dessen Rolle zu übernehmen.</p><p>Replikate können zwar im Falle eines Knotenausfalls hilfreich sein, es ist jedoch wichtig, nicht zu viele davon zu haben, da sie beim Indizieren Speicherplatz, Festplattenspeicher und Rechenleistung verbrauchen. Ein weiterer Unterschied zwischen den primären Shards und Replikaten besteht darin, dass die Anzahl der primären Shards nach der Erstellung des Index nicht mehr geändert werden kann, die Anzahl der Replikate jedoch jederzeit dynamisch durch Aktualisieren der Indexeinstellungen angepasst werden kann.</p><p>Ein weiterer Faktor, der bei Replikaten zu berücksichtigen ist, ist die Anzahl der verfügbaren Knoten. Replikate werden immer auf anderen Knoten als dem primären Shard platziert, da zwei Kopien derselben Daten auf demselben Knoten keinen Schutz bieten würden, wenn der Knoten ausfallen sollte. Damit ein System <em>n</em> Replikate unterstützen kann, müssen daher mindestens <em>n + 1</em> Knoten im Cluster vorhanden sein. Wenn beispielsweise ein Cluster aus zwei Knoten besteht und ein Index mit sechs Replikaten konfiguriert ist, wird nur ein Replikat zugewiesen. Ein System mit sieben Knoten hingegen ist durchaus in der Lage, einen primären Shard und sechs Replikate zu verwalten.</p><h2>Optimierung von Shards und Replikaten</h2><p>Auch nachdem ein Index mit dem richtigen Verhältnis von primären und Replikat-Shards erstellt wurde, müssen diese überwacht werden, da sich die Dynamik eines Index im Laufe der Zeit ändert. Beispielsweise sind bei der Analyse von Zeitreihendaten Indizes mit aktuellen Daten im Allgemeinen aktiver als ältere. Ohne eine Anpassung dieser Indizes würden sie alle die gleiche Menge an Ressourcen verbrauchen, trotz ihrer sehr unterschiedlichen Anforderungen.</p><p>Mithilfe der Rollover-Index-API lassen sich neuere und ältere Indizes trennen. Es kann so eingestellt werden, dass automatisch ein neuer Index erstellt wird, sobald ein bestimmter Schwellenwert erreicht ist – die Größe des Index auf der Festplatte, die Anzahl der Dokumente oder sein Alter. Diese API ist auch nützlich, um die Shard-Größen unter Kontrolle zu halten. Da die Anzahl der Shards nach der Indexerstellung nicht ohne Weiteres geändert werden kann, sammeln sich weiterhin Daten in den Shards an, wenn keine Rollover-Bedingungen erfüllt sind. Bei älteren Indizes, auf die nur selten zugegriffen werden muss, sind das Verkleinern und das erzwungene Zusammenführen eines Index zwei verschiedene Möglichkeiten, den Speicher- und Festplattenbedarf zu reduzieren. Ersteres reduziert die Anzahl der Shards in einem Index, während letzteres die Anzahl der Lucene-Segmente reduziert und Speicherplatz freigibt, der von gelöschten Dokumenten belegt wurde.</p><h2>Primäre und Replikat-Shards als Grundlage von Elasticsearch</h2><p>Elasticsearch hat sich einen hervorragenden Ruf als verteilte Speicher-, Such- und Analyseplattform für riesige Datenmengen erworben. Bei einem Betrieb in einem solchen Umfang werden jedoch unweigerlich Herausforderungen auftreten. Deshalb ist es für Elasticsearch so wichtig und grundlegend zu verstehen, wie primäre und Replikat-Shards funktionieren, da dies zur Optimierung der Zuverlässigkeit und Leistung der Plattform beitragen kann.</p><p>Zu wissen, wie sie funktionieren und wie man sie optimiert, ist entscheidend für die Erreichung eines robusteren und leistungsfähigeren Elasticsearch-Clusters. Wenn Sie regelmäßig mit langsamen Antwortzeiten auf Anfragen oder Ausfällen konfrontiert sind, könnte dieses Wissen der Schlüssel zur Überwindung dieser Hindernisse sein.</p><p>In der offiziellen Dokumentation von Elasticsearch erfahren Sie mehr über <a href="https://www.elastic.co/docs/deploy-manage/distributed-architecture/clusters-nodes-shards">Cluster, Knoten und Shards</a>, <a href="https://www.elastic.co/docs/deploy-manage/production-guidance/optimize-performance/size-shards">die Dimensionierung Ihrer Shards</a>, <a href="https://www.elastic.co/docs/deploy-manage/distributed-architecture/shard-allocation-relocation-recovery">die Shard-Zuweisung und die Wiederherstellung</a>.</p><p>Dieses Thema ist auch als Einführungskurs auf dem <a href="https://youtu.be/sAySPSyL2qE">YouTube-Kanal der Elastic Community verfügbar.</a></p><p>Zu guter Letzt: Wenn Sie sich keine Gedanken über Nodes, Shards oder Replikate machen möchten, können Sie <a href="https://www.elastic.co/docs/deploy-manage/deploy/elastic-cloud/serverless">Elastic Cloud Serverless</a> ausprobieren. Dieses Elastic Cloud-Angebot wird vollständig von Elastic verwaltet und ist so automatisiert, dass es mit Ihrer Arbeitslast skaliert. Eine kostenlose Testversion kann Ihnen helfen, sich mit weiteren Vorteilen des serverlosen Ansatzes vertraut zu machen.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Wie man Felder eines Elasticsearch-Index anzeigt]]></title>
            <description><![CDATA[Lernen Sie, wie Sie Felder eines Elasticsearch-Index mithilfe der _mapping- und _search-APIs, Unterfeldern, synthetischen _source-Daten und Laufzeitfeldern anzeigen können.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/elasticsearch-index-show-fields</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/elasticsearch-index-show-fields</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[JD Armada]]></dc:creator>
            <pubDate>Wed, 06 Aug 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/23b96aaa1a38b1f4747b4a87695d816f24c0cf70-720x421.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>In diesem Artikel werden wir erläutern, wie man Felder eines Elasticsearch-Index anzeigt. Dies kann hilfreich sein, um die Struktur Ihrer Daten zu verstehen, bestimmte Felder zu identifizieren und Probleme zu beheben. Wir werden folgende Themen behandeln:</p><ol><li>Verwendung der <code>_mapping</code> -API zum Abrufen von Feldinformationen</li><li>Verwendung der <code>_search</code> API zum Anzeigen von Feldwerten</li><li>Unterfelder anzeigen</li><li>Synthetisches Feld „_source“</li><li>Laufzeitfelder</li></ol><h2>1. Verwendung der _mapping-API zum Abrufen von Feldinformationen</h2><p>Die <code>_mapping</code> API ermöglicht es Ihnen, die Mapping-Definition für einen oder mehrere Indizes abzurufen. Dies umfasst Informationen über die Felder, ihre Datentypen und weitere Eigenschaften. Um die Zuordnung für einen bestimmten Index abzurufen, verwenden Sie die folgende Anfrage:</p><pre><code>GET /<index_name>/_mapping</code></pre><p>Wenn Sie beispielsweise einen Index mit dem Namen <code>my_index</code> haben, können Sie dessen Zuordnung mit der folgenden Anfrage abrufen:</p><pre><code>GET /my_index/_mapping</code></pre><p>Die Antwort enthält die Mapping-Definition für den Index, die Informationen über die Felder und deren Eigenschaften enthält.</p><p>Es ist auch möglich, die Zuordnung eines bestimmten Feldes abzurufen. Dies kann nützlich sein, wenn Ihre Kartierung recht umfangreich ist und Sie sich nur auf ein bestimmtes Feld konzentrieren möchten. Um die Zuordnung eines bestimmten Feldes abzurufen, verwenden Sie die folgende Anfrage:</p><pre><code>GET /my_index/_mapping/field/my_field</code></pre><p>Sie können die Zuordnungen mehrerer Felder auch abrufen, indem Sie deren Namen durch Kommas trennen, wie in der folgenden Anfrage:</p><pre><code>GET /my_index/_mapping/field/my_field_1,my_field_2,my_field_3</code></pre><h2>2. Verwenden der _search-API zum Anzeigen von Feldwerten</h2><p>Um die Werte von Feldern in einem Elasticsearch-Index anzuzeigen, können Sie die <code>_search</code> API verwenden. Die <code>_search</code> API bietet Ihnen mehrere Möglichkeiten, die zurückgegebenen Felder zu steuern; die beiden wichtigsten sind:</p><ol><li><strong><code>_source</code></strong>Das Feld <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-source-field"><code>_source</code></a> enthält den ursprünglichen JSON-Dokumentkörper genau so, wie er indexiert wurde, einschließlich aller Änderungen, die durch Ingestionspipelines oder Vorverarbeitungsschritte vorgenommen wurden. Um bestimmte Felder aus dem Quelldokument anzuzeigen, implementieren Sie eine Quellfilterung, wie wir im Folgenden sehen werden.</li><li><strong><code>fields</code></strong>Mit dem Parameter <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrieve-selected-fields"><code>fields</code></a> können Sie beim Durchführen einer Suche bestimmte Felder aus Ihren Dokumenten auf Basis der Indexzuordnung abrufen. Im Gegensatz zu <code>_source</code> kann <code>fields</code> auch Werte aus gespeicherten Feldern, Dokumentwerten oder Laufzeitfeldern zurückgeben, ohne auf <code>_source</code> zu verweisen. Für Standardfelder ohne Dokumentwerte oder gespeicherte Einstellungen wird jedoch auf <code>_source</code> zurückgegriffen. Dies kann viele Vorteile mit sich bringen, wie zum Beispiel eine höhere Leistungsfähigkeit und mehr, wie wir im Folgenden sehen werden.</li></ol><h3>Verwendung des Feldes _source<code> </code></h3><p>Standardmäßig gibt die<code> _search</code> -API das Feld <code>_source</code> zurück, welches das ursprüngliche, indizierte JSON-Dokument enthält. Um bestimmte Felder anzuzeigen, können Sie Filter im Parameter <code>_source </code>der Suchanfrage hinzufügen; dies wird als Quellfilterung bezeichnet.</p><p>Hier ist ein Beispiel für eine Suchanfrage, die die Werte der Felder <code>title </code>und <code>author</code> für Dokumente im Index <code>my_index</code> zurückgibt:</p><pre><code>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "_source": ["title", "author"]
}</code></pre><p>In diesem Beispiel gibt der Parameter <code>_source</code> die zurückzugebenden Felder an.</p><p>Falls Sie noch mehr Kontrolle benötigen, können Sie die Eigenschaften <code>includes</code> und <code>excludes </code>des Objekts <code>_source</code> verwenden. Beispielsweise gibt die folgende Abfrage das Feld der obersten Ebene <code>title</code> und alle Unterfelder von <code>author</code> außer <code>author.description</code> zurück.</p><pre><code>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "_source": {
     “includes”: [“title”, “author.*],
     “excludes”: [“author.description”]
  }
}</code></pre><p>In diesem Beispiel verwenden wir das Muster <code>author.* </code> , um jedes direkte Unterfeld des Objekts <code>author </code>abzurufen. Dann schließen wir <code>author.description </code>explizit aus, sodass nur die übrigen Autorenfelder zurückgegeben werden. Beachten Sie, dass dies keine Leistungsverbesserungen mit sich bringt, da das Quell-JSON weiterhin geladen und analysiert werden muss, aber es kann die Größe der über das Netzwerk gesendeten Antwort verringern.</p><h3>Verwendung des Parameters „fields“</h3><p>Mit dem Parameter <code>fields</code> können Sie die in der Suchergebnisseinduktion zurückgegebenen Felder filtern. Die Verwendung <code>fields</code> anstelle von <code>_source</code> bietet mehrere Vorteile, darunter:</p><ul><li><strong>Verbesserte Performance: </strong><code>fields </code>kann Werte direkt aus <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-store">gespeicherten Feldern</a> oder <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/doc-values">Dokumentwerten</a> zurückgeben, ohne die vollständige <code>_source</code> laden zu müssen, wodurch die Größe der Antwortnutzlast kleiner wird.</li><li><strong>Formatierte Ausgabe:</strong> Bei Standardfeldern kann <code> fields</code> auf <code>_source</code> zurückgreifen, um die Werte zu erfassen. Dabei wird jedoch die Indexzuordnung herangezogen, um die Ausgabe korrekt zu formatieren, z. B. formatierte Datumsangaben, sodass sie mit den für Aggregationen und Sortierungen verwendeten Formaten konsistent sind.</li><li><strong>Zugriff auf Laufzeitfelder:</strong> <code>fields</code> kann Laufzeitfelder zurückgeben, die im ursprünglichen <code>_source</code> nicht existieren.</li><li>Weitere Vorteile finden Sie <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrieve-selected-fields#search-fields-param">hier</a>.</li></ul><p>Um beispielsweise nur die Felder <code>title</code> und <code>author</code> im Index <code>my_index</code> zurückzugeben, können Sie die folgende Suchanfrage verwenden:</p><pre><code>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "fields": ["title", "author"],
  "_source": false
}</code></pre><p>In der obigen Abfrage setzen wir das Feld <code>_source </code>auf false, damit wir das Quelldokument nicht zurückgeben. Dadurch kann die Nutzlastgröße der Antwort drastisch reduziert werden. Beachten Sie jedoch, dass dies nur funktioniert, weil die Felder <code>title</code> und <code>author</code> vom Feldtyp <code>keyword </code>sind, bei dem standardmäßig <code>doc_values</code> aktiviert ist. Wenn das Feld <code>doc_values</code> nicht aktiviert hat und <code>_source</code> auf false gesetzt ist, hat Elasticsearch keine Möglichkeit, diese abzurufen, und sie werden in der Antwort übersprungen.</p><p>Wichtig zu beachten ist, dass die <code>fields</code> -Antwort immer ein Array von Werten für jedes Feld zurückgibt, selbst wenn es nur einen einzigen Wert gibt. Dies liegt daran, dass Elasticsearch keinen dedizierten Array-Typ besitzt und jedes Feld mehrere Werte haben kann. Für weitere Informationen zu Arrays in Elasticsearch klicken Sie <a href="http://elastic.co/docs/reference/elasticsearch/mapping-reference/array">hier</a>.</p><h3>Weitere Möglichkeiten zum Abrufen von Feldern</h3><p>Obwohl das Abrufen von Feldern mit <code>_source</code> oder <code>fields</code> die empfohlenen Methoden sind, stehen für bestimmte Anwendungsfälle verschiedene Methoden zur Verfügung, wie zum Beispiel:</p><p><strong>Doc-Wertfelder:</strong> Wenn Sie <code>_source</code> komplett vermeiden möchten, können Sie mit dem Parameter <code><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrieve-selected-fields#docvalue-fields">docvalue_fields</a> </code>suchen. Doc-Werte speichern die gleichen Feldwerte wie <code>_source</code> , jedoch in einer auf der Festplatte gespeicherten Datenstruktur, die für Sortierung und Aggregation optimiert ist.</p><p>Da es sich um separate Werte handelt, die nicht mit <code>_source</code> gespeichert sind, können Sie bestimmte Felder anfordern, ohne das gesamte <code>_source</code> zu laden. Dies ist nützlich, wenn Sie große Dokumente abfragen, aber nur wenige kleine Felder benötigen, die Dokumentwerte unterstützen. Ein weiterer Anwendungsfall für <code>docvalue_fields </code>besteht darin, dass Sie eine benutzerdefinierte Formatierung für die Felder <code>date</code> und <code>numeric</code> verwenden möchten, wie wir im folgenden Beispiel sehen werden.</p><p>Beachten Sie, dass dies nur für Felder funktioniert, für die Sie <code>doc_values</code> aktivieren, oder für Feldtypen, bei denen dies standardmäßig aktiviert ist, wie z. B. <code>keyword</code>, <code>date</code>, numerische Typen und <code>boolean</code>, nicht für <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/text"><code>text</code></a> oder <a href="https://www.elastic.co/docs/reference/elasticsearch/plugins/mapper-annotated-text-usage"><code>annotated_text</code></a>.</p><p>In diesem Beispiel verwenden wir den Parameter <code>docvalue_fields</code> , um die Felder <code>title</code>, <code>author</code> und <code>published</code> abzurufen, ohne das vollständige Dokument <code>_source</code> zu laden:</p><pre><code>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "docvalue_fields": [
    "title",
    "author",
    {
      "field": "published",
      "format": "epoch_millis"
    }
  ],
  "_source": false
}</code></pre><p>Wenn diese Abfrage ausgeführt wird, greift Elasticsearch direkt auf die Werte in seinem spaltenorientierten Speicher auf der Festplatte zu, anstatt für jedes Dokument auf <code>_source </code>zu verweisen. Das Feld <code>published</code> wird dank des in der Abfrage angegebenen Parameters <code>format</code> im Format <code>epoch_millis</code> anstatt im Standardformat zurückgegeben.</p><p><strong>Gespeicherte Felder:</strong> Wenn Sie in der Zuordnung explizit bestimmte Felder als <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-store">gespeichert</a> markiert haben, können Sie mit dem Parameter <code>stored_fields</code> nach diesen Feldern filtern. Dies ist nützlich, wenn Sie kurze Antworten nur mit diesen spezifischen Feldern wünschen oder Felder, die Sie absichtlich zum späteren Abruf gespeichert haben. Es wird separat von <code>_source</code> gespeichert, daher ist diese Methode auch nützlich, um das Laden von <code>_source</code> zu vermeiden.</p><p>Wichtig zu beachten ist, dass diese Option standardmäßig deaktiviert und generell nicht empfehlenswert ist. Verwenden Sie stattdessen Quellfilter, um bestimmte Teilmengen des ursprünglichen Quelldokuments zurückzugeben.</p><p>In der folgenden Beispielabfrage verwenden wir den Parameter <code>stored_fields</code> , um das Feld <code>summary</code> abzurufen, das die Indexzuordnungskonfiguration ”<code>store”: true</code> hat.</p><pre><code>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "stored_fields": ["summary"]
}</code></pre><p>Wenn diese Abfrage ausgeführt wird, prüft Elasticsearch, ob dieses Feld mit <code>”store”: true</code> markiert wurde. Falls dies nicht der Fall ist, wird das Feld vollständig übersprungen.</p><h2>3. Unterfelder anzeigen</h2><p>Wenn Ihr Index Unterfelder enthält, können Sie die Punktnotation verwenden, um den Feldpfad im Parameter <code>fields</code> anzugeben. Beachten Sie, dass Unterfelder sich vom <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/nested">verschachtelten Feldtyp</a> unterscheiden. Wenn Sie beispielsweise ein Unterfeld mit dem Namen <code>address.city</code> haben, können Sie es wie folgt in die Suchergebnisseinlösung einbinden:</p><pre><code>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "fields": ["title", "author", "address.city"],
  "_source": false
}</code></pre><p>In diesem Beispiel enthält die Suchergebnisseinsendung die Werte der Felder <code>title</code>, <code>author</code> und <code>address.city</code> .</p><h2>4. Synthetische Quelle</h2><p>Wenn Sie die Funktionalität der Verwendung von<code> _source</code> beibehalten, aber gleichzeitig Speicherplatz sparen möchten, haben Sie die Möglichkeit, in Ihrer Indexzuordnung synthetisches <code>_source</code> zu verwenden. Die Funktion <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-source-field#synthetic-source">Synthetic <code>_source</code></a> ermöglicht es Elasticsearch, die <code>_source</code> aus vorhandenen Daten wie gespeicherten Feldern und Dokumentwerten zu rekonstruieren, selbst wenn <code>_source</code> deaktiviert ist. Dadurch lässt sich viel Speicherplatz sparen, allerdings auf Kosten etwas geringerer Abfragegeschwindigkeiten, da die Rekonstruktion in Echtzeit erfolgt. Aktivieren Sie diese Funktion, indem Sie die folgenden Werte in Ihren Indexeinstellungen verwenden:</p><pre><code>PUT idx
{
  "settings": {
    "index": {
      "mapping": {
        "source": {
          "mode": "synthetic"
        }
      }
    }
  }
}</code></pre><p>Zu den Vorteilen der Verwendung von synthetischem <code>_source </code>gehören: vollständige Dokumentanzeige bei Verwendung der <code>_search</code> API, Quellfilterung und Kompatibilität mit anderen Funktionen und Tools wie Kibana, die die Verfügbarkeit <code>_source</code> voraussetzen, und das alles, ohne dass das vollständige <code>_source</code> Dokument gespeichert werden muss.</p><h2>5. Laufzeitfelder</h2><p><a href="https://www.elastic.co/docs/manage-data/data-store/mapping/runtime-fields">Mit Laufzeitfeldern</a> können Sie skriptgesteuerte Felder zur Abfragezeit oder in Ihrer Indexzuordnung unter einem Laufzeitblock definieren. Diese Felder werden nie indiziert, daher erhöht das Hinzufügen eines Laufzeitfelds nicht die Indexgröße, es wird aber niemals in <code>_source</code> angezeigt. Die in der Zuordnung definierten Laufzeitfelder sind persistent und für alle Abfragen verfügbar, während die zur Abfragezeit definierten Laufzeitfelder temporär sind und nur in dieser Suchanfrage verfügbar sind.</p><p>Der Hauptvorteil der Verwendung von Laufzeitfeldern besteht darin, dass man Felder zu Dokumenten hinzufügen kann, nachdem man sie bereits importiert hat, was die Zuordnungsentscheidungen vereinfacht. Laufzeitfelder eignen sich auch hervorragend, um Ihre Dokumente mit Werten anzureichern, die im Originaldokument nicht vorhanden sind, sondern mithilfe eines Skripts generiert werden, z. B. durch Formatieren einer Zeichenkette oder Berechnen einer Punktzahl.</p><p>Es ist außerdem zu beachten, dass Laufzeitfelder die Leistung beeinträchtigen können, da für jedes Dokument im Ergebnissatz ein Skript ausgeführt werden muss. Um <a href="https://www.elastic.co/docs/manage-data/data-store/mapping/retrieve-runtime-field">ein Laufzeitfeld abzurufen</a>, können Sie auch den Parameter <code>fields</code> der API <code>_search</code> verwenden.</p><h2>Fazit</h2><p>Die Anzeige von Feldern eines Elasticsearch-Index kann von der einfachen Abfrage von Werten mithilfe der Indexzuordnung oder <code>_source</code> bis hin zu fortgeschritteneren Methoden mit <code>fields</code>, <code>docvalue_fields</code> oder Laufzeitfeldern für mehr Kontrolle und Effizienz reichen. Das Verständnis der Vor- und Nachteile verschiedener Methoden ist der Schlüssel zur Optimierung Ihrer Sucherfahrung. Egal ob Sie Nutzdaten optimieren, Dokumente anreichern oder synthetische Daten <code>_source</code> verwenden, um Speicherplatz zu sparen, Elasticsearch bietet Ihnen zahlreiche Tools und Funktionen, um die benötigten Daten so zu finden, wie Sie sie benötigen. Mithilfe dieser Techniken können Sie die Struktur Ihrer Daten verstehen, bestimmte Felder identifizieren und Probleme beheben.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[KI-gestützte Dashboards: Von der Vision zu Kibana]]></title>
            <description><![CDATA[Generieren Sie ein Dashboard mithilfe eines LLM, um ein Bild zu verarbeiten und in ein Kibana-Dashboard umzuwandeln.
]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/ai-powered-dashboards</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/ai-powered-dashboards</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jeffrey Rengifo, Tomás Murúa]]></dc:creator>
            <pubDate>Wed, 16 Jul 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/eb60ceb2fbc3941745b21ae3357cbb6ea8fab18c-1443x811.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p><a href="https://www.elastic.co/kibana/kibana-lens">Kibana Lens</a> macht das Erstellen von Dashboards per Drag &amp; Drop sehr einfach, aber wenn man Dutzende von Panels benötigt, summieren sich die Klicks. Was wäre, wenn Sie ein Dashboard skizzieren, einen Screenshot davon machen und einen LLM den gesamten Prozess für Sie abschließen lassen könnten?</p><p>In diesem Artikel werden wir genau das tun. Wir werden eine Anwendung erstellen, die ein Bild eines Dashboards aufnimmt, unsere Mappings analysiert und anschließend ein Dashboard generiert, ohne dass wir Kibana überhaupt berühren müssen!</p><p><strong>Schritte</strong>:</p><ol><li><a href="https://www.elastic.co/search-labs/blog/ai-powered-dashboards#background-&amp;-application-workflow">Hintergrund und Anwendungsablauf</a></li><li><a href="https://www.elastic.co/search-labs/blog/ai-powered-dashboards#prepare-data">Daten vorbereiten</a></li><li><a href="https://www.elastic.co/search-labs/blog/ai-powered-dashboards#llm-configuration">LLM-Konfiguration</a></li><li><a href="https://www.elastic.co/search-labs/blog/ai-powered-dashboards#application-functions">Anwendungsfunktionen</a></li></ol><h2>Hintergrund und Anwendungsablauf</h2><p>Mein erster Gedanke war, den LLM das gesamte NDJSON-Format der in Kibana <a href="https://www.elastic.co/docs/explore-analyze/find-and-organize/saved-objects">gespeicherten Objekte</a> generieren zu lassen und sie dann in Kibana zu importieren.</p><p>Wir haben eine Handvoll Modelle ausprobiert:</p><ul><li>Gemini 2.5 Pro</li><li>GPT o3 / o4-mini-hoch / 4.1</li><li>Claude 4 Sonett</li><li>Grok 3</li><li>Deepseek (Deepthink R1)</li></ul><p>Und als Anregungen begannen wir mit ganz einfachen Dingen:</p><pre><code>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</code></pre><p>Trotz der Durchsicht <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.">einiger weniger Beispiele</a> und detaillierter Erklärungen zum Aufbau der einzelnen Visualisierungen hatten wir keinen Erfolg. Wenn Sie an diesem Experiment interessiert sind, finden Sie <a href="https://gist.github.com/TomasMurua/a78dc283e115624731beffc98984b70b">hier</a> weitere Informationen.</p><p>Das Ergebnis dieser Vorgehensweise war, dass beim Versuch, die vom LLM erzeugten Dateien in Kibana hochzuladen, diese Meldungen angezeigt wurden:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2b599443b5613c9f0fc3235581614add5b4b3900-891x98.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d87ccfc033bc00ee8188c5cae18043fbca22784c-741x233.png" style="width: 100%;" /><p>Das bedeutet, dass das generierte JSON ungültig oder schlecht formatiert ist. Die häufigsten Probleme waren, dass der LLM unvollständiges NDJSON erzeugte, Parameter falsch interpretierte oder normales JSON anstelle von NDJSON zurückgab, egal wie sehr wir uns bemühten, das Gegenteil zu erzwingen.</p><p>Inspiriert von <a href="https://www.elastic.co/search-labs/blog/llm-functions-elasticsearch-intelligent-query">diesem Artikel</a> – in dem <a href="https://www.elastic.co/docs/solutions/search/search-templates">Suchvorlagen</a> besser funktionierten als LLM Freestyle – entschieden wir uns, dem LLM Vorlagen zu übergeben, anstatt die vollständige NDJSON-Datei generieren zu lassen und anschließend im Code die vom LLM bereitgestellten Parameter zur Erstellung der Visualisierungen zu verwenden. Dieser Ansatz hat sich bewährt und ist vorhersehbar und erweiterbar, da nun der Code und nicht mehr das LLM die Hauptarbeit übernimmt.</p><p>Der Bewerbungsprozess wird wie folgt ablaufen:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/52c587cf0cf3517fdd4ee7ab95581dd4f2bce030-725x668.png" style="width: 100%;" /><p></p><p><em>Der Einfachheit halber lassen wir einige Codeabschnitte weg, aber den vollständigen, lauffähigen Code der Anwendung finden Sie in <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"><strong>diesem</strong></a> Notebook.</em></p><h2>Voraussetzungen</h2><p>Bevor Sie mit der Entwicklung beginnen, benötigen Sie Folgendes:</p><ol><li>Python 3.8 oder höher</li><li>Eine <a href="https://docs.python.org/3/library/venv.html">Venv</a> Python-Umgebung</li><li>Eine laufende Elasticsearch-Instanz, zusammen mit ihrem Endpunkt und API-Schlüssel</li><li>Ein OpenAI-API-Schlüssel, der unter dem Umgebungsvariablennamen OPENAI_API_KEY gespeichert ist:</li></ol><pre><code>export OPENAI_API_KEY="your-openai-api-key"</code></pre><h2>Daten vorbereiten</h2><p>Für die Daten halten wir es einfach und verwenden Elastic-Beispiel-Weblogs. <a href="https://www.elastic.co/docs/manage-data/ingest/sample-data#add-sample-data-sets">Hier</a> erfahren Sie, wie Sie diese Daten in Ihren Cluster importieren.</p><p>Jedes Dokument enthält Angaben zum Host, der die Anfragen an die Anwendung gestellt hat, sowie Informationen zur Anfrage selbst und deren Antwortstatus. Nachfolgend finden Sie ein Beispieldokument:</p><pre><code>{
    "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
}</code></pre><p>Nun holen wir uns die Zuordnungen des soeben geladenen Index, <code>kibana_sample_data_logs</code>:</p><pre><code>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"]</code></pre><p>Wir werden die Zuordnungen zusammen mit dem Bild übergeben, das wir später laden werden.</p><h2>LLM-Konfiguration</h2><p>Konfigurieren wir das LLM so, dass es <a href="https://python.langchain.com/docs/concepts/structured_outputs/">strukturierte Ausgabe</a> verwendet, um ein Bild einzugeben und ein JSON mit den Informationen zu erhalten, die wir an unsere Funktion übergeben müssen, um die JSON-Objekte zu erzeugen.</p><p>Wir installieren die Abhängigkeiten:</p><pre><code>pip install elasticsearch pydantic langchain langchain-openai -q</code></pre><p>Elasticsearch wird uns dabei helfen, die <a href="https://www.elastic.co/docs/manage-data/data-store/mapping">Indexzuordnungen</a> abzurufen. Pydantic ermöglicht es uns, Schemata in Python zu definieren, denen der LLM dann folgen soll, und <a href="https://www.elastic.co/search-labs/integrations/langchain">LangChain</a> ist das Framework, das den Aufruf von LLMs und KI-Tools vereinfacht.</p><p>Wir werden ein Pydantic-Schema erstellen, um die gewünschte Ausgabe des LLM zu definieren. Aus dem Bild müssen wir den Diagrammtyp, das Feld, den Visualisierungstitel und den Dashboard-Titel ablesen:</p><pre><code>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]</code></pre><p>Als Bildeingabe senden wir ein Dashboard, das ich gerade gezeichnet habe:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/36441d7b5dc1f3ff2ac2a30710208d57ad41c716-1600x898.jpg" style="width: 100%;" /><p>Nun deklarieren wir den LLM-Modellaufruf und das Laden des Bildes. Diese Funktion erhält die Zuordnungen des Elasticsearch-Index und ein Bild des Dashboards, das wir generieren möchten.</p><p>Mit <code>with_structured_output</code> können wir unser Pydantic <code>Dashboard</code> Schema als Antwortobjekt verwenden, das der LLM erzeugen wird. Mit <a href="https://docs.pydantic.dev/latest/">Pydantic</a> können wir Datenmodelle mit Validierung definieren, wodurch sichergestellt wird, dass die LLM-Ausgabe der erwarteten Struktur entspricht.</p><p>Um das Bild in Base64 zu konvertieren und als Eingabe zu senden, können Sie einen <a href="https://www.base64-image.de/">Online-Konverter</a> verwenden oder dies <a href="https://www.geeksforgeeks.org/python-convert-image-to-string-and-vice-versa/">im Code</a> erledigen.</p><pre><code>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)}")</code></pre><p>Der LLM verfügt bereits über Kontextinformationen zu Kibana-Dashboards, daher müssen wir nicht alles in der Eingabeaufforderung erklären, sondern nur einige Details, um sicherzustellen, dass er nicht vergisst, dass er mit Elasticsearch und Kibana arbeitet.</p><p>Lassen Sie uns die Aufgabenstellung aufschlüsseln:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Abschnitt</td><td style="border: 1px solid #ccc; padding: 4px;">Grund</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Sie sind Experte in der Analyse von Kibana-Dashboards anhand von Images für die Version 9.0.0 von Kibana.</td><td style="border: 1px solid #ccc; padding: 4px;">Durch die Verstärkung dieser Funktion wird Elasticsearch und die Verwendung der Elasticsearch-Version unterstützt, wodurch die Wahrscheinlichkeit verringert wird, dass das LLM alte/ungültige Parameter fälschlicherweise annimmt.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Sie erhalten ein Dashboard-Bild und eine Elasticsearch-Indexzuordnung.</td><td style="border: 1px solid #ccc; padding: 4px;">Wir erklären, dass es sich bei dem Bild um Dashboards handelt, um Fehlinterpretationen seitens des LLM zu vermeiden.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Nachfolgend finden Sie die Indexzuordnungen für den Index, auf dem das Dashboard basiert. Nutzen Sie diese, um die Daten und die verfügbaren Felder besser zu verstehen. Indexzuordnungen: {index_mappings}</td><td style="border: 1px solid #ccc; padding: 4px;">Es ist entscheidend, die Zuordnungen bereitzustellen, damit das LLM dynamisch gültige Felder auswählen kann. Andernfalls könnten wir die Zuordnungen hier fest codieren, was zu starr wäre, oder uns darauf verlassen, dass das Bild die richtigen Feldnamen enthält, was nicht zuverlässig ist.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Beschränken Sie sich auf die Felder, die für die jeweilige Visualisierung relevant sind, basierend auf dem, was im Bild sichtbar ist.</td><td style="border: 1px solid #ccc; padding: 4px;">Wir mussten diese Verstärkung hinzufügen, weil manchmal versucht wird, Felder hinzuzufügen, die nicht zum Bild gehören.</td>
                    </tr>
              </tbody>
            </table>
          <p>Dies gibt ein Objekt mit einem Array von anzuzeigenden Visualisierungen zurück:</p><pre><code>"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"")"
]</code></pre><h2>Verarbeitung der LLM-Antwort</h2><p>Wir<strong> </strong>Wir haben ein Beispiel-Dashboard mit 2x2-Panels erstellt und es dann mithilfe der <a href="https://www.elastic.co/docs/api/doc/kibana/operation/operation-get-dashboards-dashboard">Get a dashboard API</a> im JSON-Format exportiert. Anschließend haben wir die Panels als Visualisierungsvorlagen (Kreisdiagramm, Balkendiagramm, Metrikdiagramm) gespeichert, in denen wir einige der Parameter ersetzen können, um je nach Fragestellung neue Visualisierungen mit anderen Feldern zu erstellen.</p><p>Die JSON-Vorlagedateien können Sie <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>hier</strong></a> einsehen. Beachten Sie, wie wir die Objektwerte, die wir später ersetzen möchten, durch {<code>variable_name</code>}ersetzt haben.<br/></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ec7e1ac0cd8b470df13e60940162b56778acb386-315x234.png" style="width: 100%;" /><p>Anhand der von LLM bereitgestellten Informationen können wir entscheiden, welche Vorlage wir verwenden und welche Werte wir ersetzen.</p><p><code>fill_template_with_analysis</code> empfängt die Parameter für ein einzelnes Panel, einschließlich der JSON-Vorlage der Visualisierung, eines Titels, eines Feldes und der Koordinaten der Visualisierung im Raster.</p><p>Anschließend werden die Werte der Vorlage ersetzt und die endgültige JSON-Visualisierung zurückgegeben.</p><pre><code>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)</code></pre><p>Um es einfach zu halten, verwenden wir statische Koordinaten, die wir den vom LLM erstellten Panels zuweisen, und erzeugen so ein 2x2-Raster-Dashboard wie in der obigen Abbildung dargestellt.</p><pre><code># 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</code></pre><p>Je nach dem vom LLM festgelegten Visualisierungstyp wählen wir eine JSON-Dateivorlage aus und ersetzen die relevanten Informationen durch <code>fill_template_with_analysis</code> . Anschließend fügen wir das neue Panel einem Array hinzu, das wir später zum Erstellen des Dashboards verwenden.</p><p>Sobald das Dashboard fertig ist, verwenden wir die <a href="https://www.elastic.co/docs/api/doc/kibana/operation/operation-post-dashboards-dashboard-id">„Create a dashboard“-API</a> , um die neue JSON-Datei an Kibana zu übertragen und das Dashboard zu generieren:<br/></p><pre><code>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)}")</code></pre><p>Um das Skript auszuführen und das Dashboard zu generieren, führen Sie folgenden Befehl in der Konsole aus:</p><pre><code>python <file_name>.py</code></pre><p>Das Endergebnis wird folgendermaßen aussehen:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e909afbf0e47d9a6e0f7bd07dfb2efcfa5cf06ac-921x715.png" style="width: 100%;" /><h2>Fazit</h2><p>LLMs zeigen ihre ausgeprägten visuellen Fähigkeiten bei der Umwandlung von Text in Code oder der Umwandlung von Bildern in Code. Die Dashboards-API ermöglicht es auch, JSON-Dateien in Dashboards umzuwandeln, und mit einem LLM und etwas Code können wir Bilder in ein Kibana-Dashboard umwandeln.</p><p>Der nächste Schritt besteht darin, die Flexibilität der Dashboard-Visualisierungen durch die Verwendung unterschiedlicher Rastereinstellungen, Dashboard-Größen und -Positionen zu verbessern. Darüber hinaus wäre die Unterstützung komplexerer Visualisierungen und Visualisierungstypen eine sinnvolle Ergänzung dieser Anwendung.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Einzigartige Muster aufdecken: Ein Leitfaden zur Aggregation aussagekräftiger Begriffe in Elasticsearch]]></title>
            <description><![CDATA[Lernen Sie, wie Sie mithilfe der wichtigen Begriffe der Aggregation Erkenntnisse aus Ihren Daten gewinnen können.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/significant-terms-aggregation-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/significant-terms-aggregation-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Alexander Dávila]]></dc:creator>
            <pubDate>Mon, 07 Jul 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/13adbc901837835bb56abf15e377127b017cfac8-1536x1024.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>In Elasticsearch geht die <a href="https://www.elastic.co/docs/reference/aggregations/search-aggregations-bucket-significantterms-aggregation">signifikante Termaggregation</a> über die <a href="https://www.elastic.co/docs/reference/aggregations/search-aggregations-bucket-terms-aggregation">häufigsten Begriffe</a> hinaus, um statistisch ungewöhnliche Werte in einem Datensatz zu finden. Dies ermöglicht es uns, wertvolle Erkenntnisse und nicht offensichtliche Muster zu entdecken. Eine signifikante Termaggregation liefert eine Antwort mit zwei nützlichen Parametern:</p><ul><li><strong>bg_count (Hintergrundzählung): </strong>Anzahl der im übergeordneten Datensatz gefundenen Dokumente</li><li><strong>doc_count:</strong> Anzahl der im Ergebnisdatensatz gefundenen Dokumente</li></ul><p>In einem Datensatz zu Handyverkäufen können wir beispielsweise nach relevanten Begriffen im Zusammenhang mit den iPhone 16-Verkäufen suchen, etwa so:</p><pre><code>GET phone_sales_analysis/_search
{
 "size": 0,
 "query": {
   "term": {
     "phone_model": {
       "value": "iPhone 16"
     }
   }
 },
 "aggs": {
   "significant_cities": {
     "significant_terms": {
       "field": "city_region",
       "size": 1
     }
   }
 }
}</code></pre><p>Die Antwort lautet dann:</p><pre><code>{
 "aggregations": {
   "significant_cities": {
     "doc_count": 122,
     "bg_count": 424,
     "buckets": [
       {
         "key": "Houston",
         "doc_count": 12,
         "score": 0.1946481360617346,
         "bg_count": 14
       }

     ]
   }
 }
}</code></pre><p>Houston gehört weder zu den Top 10 Städten im gesamten Datensatz noch ist Houston die Top-Stadt für das iPhone 16. Die Auswertung der signifikanten Terme ergab jedoch, dass das<strong><em> iPhone 16 in dieser Stadt im Vergleich zu den übrigen Daten überproportional häufig gekauft wird</em></strong> . Lassen Sie uns die Zahlen genauer betrachten:</p><ul><li><strong>Auf höchster Ebene:</strong><ul><li><strong>Dokumentenanzahl: 122 – </strong>Die Abfrage ergab insgesamt 122 Treffer.</li><li><strong>bg_count: 424 — </strong>Der Hintergrundsatz (alle Verkaufsbelege) enthält 424 Belege</li></ul></li><li><strong>Im Houston-Eimer:</strong><ul><li><strong>Dokumentanzahl: 12 — </strong>Houston erscheint in 12 der 122 Suchergebnisse</li><li><strong>bg_count: 14 — </strong>Houston erscheint in 14 der insgesamt 424 Dokumente im Hintergrunddatensatz</li></ul></li></ul><p>Dies bedeutet, dass von insgesamt 424 Käufen nur 14 in Houston stattfanden; das sind 3,3 % aller Käufe. Betrachtet man jedoch nur die Verkaufszahlen des iPhone 16, so stellt man fest, dass 12 von 122 Verkäufen in Houston stattfanden, was 9,8 % entspricht – das Dreifache des Wertes im gesamten Datensatz; das ist bemerkenswert!</p><p>So sieht das in einer Visualisierung aus: Gesamtumsatz pro Stadtregion.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f31335b0b7793650025f941820f238dd35bfb09f-1486x1066.png" style="width: 100%;" /><p>Wir sehen, dass es in Houston 14 Verkäufe gibt, womit Houston gemessen an den Verkäufen die 14. höchste Stadt im Datensatz ist.</p><p>Wenn wir nun einen Filter anwenden, der nur die Verkaufszahlen des iPhone 16 berücksichtigt, verzeichnen wir 12 Verkäufe in Houston. Damit ist Houston die zweitmeisten Städte mit den Verkaufszahlen für dieses spezielle Modell:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6516040db77e6c62af5541a74c723b18008ad3c6-1472x1038.png" style="width: 100%;" /><h2>Die wichtigsten Begriffe der Aggregation verstehen</h2><p>Laut Elastic-Dokumentation sind die <a href="https://www.elastic.co/docs/reference/aggregations/search-aggregations-bucket-significantterms-aggregation">wichtigsten Begriffe Aggregation</a>:</p><p><em>„(Findet) Begriffe, deren Popularität sich im Vergleich zwischen einem Vordergrund- und einem Hintergrunddatensatz signifikant verändert hat.“</em></p><p>Das bedeutet, dass statistische Kennzahlen verwendet werden, um die Häufigkeit eines Begriffs in einer Teilmenge der Daten (der Vordergrundmenge) mit der Häufigkeit desselben Begriffs in der übergeordneten Datenmenge (der Hintergrundmenge) zu vergleichen. Auf diese Weise spiegelt die Bewertung die statistische Signifikanz wider und nicht, wie häufig ein Begriff in den Daten vorkommt.</p><p>Die Hauptunterschiede zwischen einer Aggregation signifikanter Terme und einer normalen Termaggregation sind:</p><ul><li>Signifikante Terme vergleichen eine Teilmenge der Daten, während eine Termaggregation nur mit dem aus der Abfrage resultierenden Datensatz arbeitet.</li><li>Die Ergebnisse einer Termaggregation sind die häufigsten Terme im Datensatz, während die Ergebnisse einer signifikanten Termaggregation die häufigen Terme ignorieren, um herauszufinden, was den Datensatz einzigartig macht.</li><li>Signifikante Terme können einen größeren Einfluss auf die Leistung haben, da sie Daten von der Festplatte und nicht aus dem Arbeitsspeicher abrufen müssen, wie es bei der Termaggregation der Fall ist.</li></ul><h2>Praktische Anwendung (Verbraucherverhaltensanalyse)</h2><h3>Datenaufbereitung für die Analyse</h3><p>Für diese Analyse haben wir einen synthetischen Datensatz über Handyverkäufe erstellt, der Preis, technische Daten des Telefons, demografische Daten des Käufers und Kundenfeedback enthält. Wir haben außerdem Einbettungen aus dem Feedback der Nutzer generiert, um später eine semantische Abfrage durchführen zu können. Wir verwendeten das <a href="https://huggingface.co/intfloat/multilingual-e5-small">mehrsprachige e5 small model</a>, das auf Elasticsearch standardmäßig verfügbar ist.</p><p></p><p>So verwenden Sie diesen Datensatz in Elasticsearch:</p><ol><li>Laden Sie die CSV-Datei ( <a href="https://github.com/Alex1795/significant_terms_blog_dataset/blob/main/phone_sales_analysis_dataset.csv">hier</a> herunterladbar) mit der Kibana-Funktion <a href="https://www.elastic.co/docs/manage-data/ingest/upload-data-files">„Datendateien hochladen“</a> hoch.</li><li>Richten Sie ein semantisches Feld namens „Einbettung“ ein, wie in <a href="https://www.elastic.co/search-labs/blog/chat-with-pdf-elastic-playground#upload-pdfs-to-kibana">diesem Blog</a> beschrieben. <code>multilingual-e5-small model</code></li><li>Schließen Sie den Import mit den Feldtyp-Standardwerten ab (Schlüsselwort für jedes Feld außer <code>purchase_date</code> und <code>user_feedback)</code>. Um die hier präsentierten Abfragen ausführen zu können, müssen Sie unbedingt den Indexnamen <code>phone_sales_analysis</code> hinzufügen.</li></ol><p>Der Schwerpunkt dieser Analyse liegt auf der Frage <strong><em>: „Was unterscheidet die Käufer des iPhone 16 von anderen Bevölkerungsgruppen?</em></strong>“ und darauf, eine Segmentierung der Käufer für Marketingzwecke zu erstellen. </p><p>Dies ist ein Beispieldokument aus dem Datensatz:</p><pre><code>{
         "customer_type": "Returning",
         "user_feedback": "I have to say, quality is great for the price. The battery life is really good.",
         "upgrade_frequency": "2 years",
         "storage_capacity": "256GB",
         "occupation": "Technology & Data",
         "color": "Phantom Black",
         "gender": "Male",
         "price_paid": 899,
         "previous_brand_loyalty": "Mixed",
         "location_type": "Urban",
         "phone_model": "Samsung Galaxy S24",
         "city_region": "San Francisco Bay Area",
         "@timestamp": "2024-03-15T00:00:00.000-05:00",
         "income_bracket": "75000-100000",
         "purchase_channel": "Online",
         "feedback_sentiment": "positive",
         "education_level": "Bachelor",
         "embedding": "I have to say, quality is great for the price. The battery life is really good.",
         "customer_id": "C001",
         "purchase_date": "2024-03-15",
         "age": 34,
         "trade_in_model": "iPhone 13"
}</code></pre><h3>demografische Muster verstehen</h3><p>Hier werden wir eine Analyse der Gesamtbevölkerung durchführen und sie mit interessanten Erkenntnissen aus den signifikanten Begriffsaggregationen für iPhone 16-Nutzer vergleichen.</p><h4>Normale Muster</h4><p>Um normale Kaufmuster zu verstehen, können wir Daten aus allen Dokumenten und verschiedenen Bereichen aggregieren. Der Einfachheit halber konzentrieren wir uns auf die Berufe der Personen, die ein Telefon gekauft haben. Das können wir mit einer Anfrage an Elasticsearch erreichen.</p><pre><code>GET phone_sales_analysis/_search
{
 "aggs": {
   "occupation_distribution": {
     "terms": {
       "size": 5,
       "field": "occupation"
     }
   }
 },
 "size": 0
}</code></pre><p>Dies zeigt uns, dass die häufigsten Berufe im Datensatz (nach Anzahl der Datensätze) folgende sind:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/99136ddddd7abad5d74481158a04501b6915441b-1518x480.png" style="width: 100%;" /><h4>Verhaltensmuster von iPhone 16-Nutzern</h4><p>Um zu verstehen, was die Käufer eines iPhone 16 auszeichnet, führen wir eine Termaggregation auf demselben Feld mit einem Filter durch, um diese Personen in der Abfrage zu finden, etwa so:</p><pre><code>GET phone_sales_analysis/_search
{
  "query": {
    "term": {
      "phone_model": "iPhone 16"
    }
  },
  "aggs": {
    "occupation_distribution": {
      "terms": {
        "size": 5,
        "field": "occupation"
      }
    }
  },
  "size": 0
}</code></pre><p>Die häufigsten Berufe für iPhone 16-Nutzer sind also:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/36ce86475beb03810c6ad81d7c776d1eec736654-1500x484.png" style="width: 100%;" /><p>Wir können sehen, dass iPhone 16-Nutzer im Vergleich zu Nutzern anderer Telefonmodelle unterschiedliche Beschäftigungsmuster aufweisen. Nutzen wir Kibana, um die Ergebnisse einfach zu visualisieren:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b70ab05bc962a274e1617b6caf20575c489a62d8-1448x1128.png" style="width: 100%;" /><p></p><p>In dieser Grafik können wir sehen, dass der Trend beim iPhone 16 vom Trend der Gesamtbevölkerung abweicht.</p><p>Wir können diese gesamte Analyse überspringen und direkt zu den Unterschieden zwischen iPhone 16-Nutzern und der Gesamtbevölkerung gelangen, indem wir eine Aggregation eines einzigen signifikanten Begriffs durchführen:</p><pre><code>GET phone_sales_analysis/_search
{
  "query": {
    "term": {
      "phone_model": "iPhone 16"
    }
  },
  "aggs": {
    "occupation_distribution": {
      "significant_terms": {
        "size": 5,
        "field": "occupation"
      }
    }
  },
  "size": 0
}</code></pre><p>Kurz gesagt, erhalten wir folgende Antwort:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Werte der Berufe für das iPhone 16</td><td style="border: 1px solid #ccc; padding: 4px;">doc_count</td><td style="border: 1px solid #ccc; padding: 4px;">bg_count</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Berufsverteilung (oberste Ebene)</td><td style="border: 1px solid #ccc; padding: 4px;">122</td><td style="border: 1px solid #ccc; padding: 4px;">424</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Medizin- und Gesundheitsbereich</td><td style="border: 1px solid #ccc; padding: 4px;">45</td><td style="border: 1px solid #ccc; padding: 4px;">57</td>
                    </tr>
              </tbody>
            </table>
          <p>Die Reaktion lässt eindeutig darauf schließen, dass iPhone 16-Nutzer ein ungewöhnliches (sprich: signifikantes!) Problem haben. Anzahl der Beschäftigten im medizinischen und Gesundheitsbereich im Vergleich zur Gesamtbevölkerung. Mal sehen, was die Zahlen in der Antwort bedeuten:</p><ul><li><strong>Auf höchster Ebene:</strong><ul><li><strong>Dokumentenanzahl: 122 – </strong>Die Abfrage ergab insgesamt 122 Treffer.</li><li><strong>bg_count: 424 — </strong>Der Hintergrundsatz (alle Verkaufsbelege) enthält 424 Belege</li></ul></li><li><strong>Im Bereich Medizin &amp; Gesundheitswesen:</strong><ul><li><strong>Dokumentanzahl: 45 — </strong>„Medizin &amp; Gesundheitswesen“ erscheint in 45 der 122 Suchergebnisse</li><li><strong>bg_count: 57 — </strong>&quot;Medizin &amp; Gesundheitswesen&quot; erscheint in 57 der insgesamt 424 Dokumente im Hintergrunddatensatz</li></ul></li></ul><p>Von 424 Käufern arbeiten 57 im medizinischen und Gesundheitsbereich – das entspricht 13,44 %. Betrachtet man jedoch die Käufer des iPhone 16, so arbeiten 45 von 122 im medizinischen und Gesundheitsbereich – das entspricht 36,88 %. Das bedeutet, dass die Wahrscheinlichkeit, jemanden aus dem medizinischen oder Gesundheitssektor unter den iPhone 16-Nutzern zu finden, doppelt so hoch ist!</p><p>Wir können diese Analyse auch auf andere Bereiche (Alter, Standort, Einkommensklasse usw.) anwenden, um mehr Informationen darüber zu erhalten, was die Nutzer des iPhone 16 auszeichnet. </p><h3>Kundensegmentierung</h3><p>Mithilfe der Aggregation signifikanter Begriffe können wir Erkenntnisse über die Beziehungen zwischen Produkten, Kategorien und Kundensegmenten gewinnen. Hierfür erstellen wir eine übergeordnete Aggregation für die Kategorie, die wir genauer untersuchen möchten. Wir verwenden außerdem eine Unteraggregation von signifikanten Begriffen und normalen Begriffen, um interessante Erkenntnisse über diese Kategorie zu gewinnen und sie mit dem zu vergleichen, was die meisten Menschen in diesem Beruf verwenden.</p><p>Schauen wir uns beispielsweise an, was Menschen in verschiedenen Berufsfeldern bevorzugen:</p><ol><li>Um die Analyse übersichtlicher zu gestalten, beschränken wir unsere Suche auf drei Arbeitsbereiche: [&quot;Verwaltung &amp; Support&quot;, &quot;Technologie &amp; Daten&quot;, &quot;Medizin &amp; Gesundheitswesen&quot;]</li><li>Auf der Seite der Aggregationen beginnen wir mit einer Termaggregation nach Berufsbezeichnung.</li><li>Fügen Sie eine Unteraggregation hinzu: Begriffe nach Telefonmodell – um herauszufinden, welche Modelle Nutzer kaufen, die in den jeweiligen Bereichen arbeiten.</li><li>Fügen Sie eine zweite Unteraggregation hinzu: signifikante Begriffe nach Telefonmodellen – um herauszufinden, welche Modelle in den einzelnen Arbeitsbereichen besonders sind.</li></ol><pre><code>GET phone_sales_analysis/_search
{
 "query": {
   "terms": {
     "occupation": [
       "Administrative & Support",
       "Technology & Data",
       "Medical & Healthcare"
     ]
   }
 },
 "aggs": {
   "occupations": {
     "terms": {
       "size": 15,
       "field": "occupation"
     },
     "aggs": {
       "general_models": {
         "terms": {
           "field": "phone_model"
         }
       },
       "significant_models": {
         "significant_terms": {
           "field": "phone_model"
         }
       }
     }
   }
 },
 "size": 0
}</code></pre><p>Lassen Sie uns die Aggregationsergebnisse im Detail betrachten:</p><p><strong>Beruf</strong>: Verwaltung &amp; Unterstützung</p><p><strong>Termaggregation</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a4ad519c9013867a3f4cee032160eadd8a47804a-1506x398.png" style="width: 100%;" /><p><strong>Aggregation signifikanter Terme</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e5604fa8036667bdfe733576a5e7c6153760dd3a-306x220.png" style="width: 100%;" /><p>Aus dieser Tabelle lässt sich schließen, dass es keine signifikanten Unterschiede zwischen dem Trend für diesen Beruf und dem Trend für die Gesamtbevölkerung gibt.</p><p><strong>Beruf</strong>: Technologie &amp; Daten</p><p><strong>Termaggregation</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/13b09039bb7d183276451007d2d69dc190b1d3c0-1508x836.png" style="width: 100%;" /><p></p><p><strong>Aggregation signifikanter Terme</strong></p><p>Gesamtzahl der Dokumente: 424</p><p>Dokumente in diesem Beruf: 71</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Telefonmodell</td><td style="border: 1px solid #ccc; padding: 4px;">doc_count (dieses Modell in diesem Beruf)</td><td style="border: 1px solid #ccc; padding: 4px;">bg_count (Dieses Modell ist in allen Dokumenten enthalten)</td><td style="border: 1px solid #ccc; padding: 4px;">% in allen Dokumenten</td><td style="border: 1px solid #ccc; padding: 4px;">% in diesem Beruf</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Google Pixel 8</td><td style="border: 1px solid #ccc; padding: 4px;">12</td><td style="border: 1px solid #ccc; padding: 4px;">22</td><td style="border: 1px solid #ccc; padding: 4px;">5,19 %</td><td style="border: 1px solid #ccc; padding: 4px;">16,90 %</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OnePlus 11</td><td style="border: 1px solid #ccc; padding: 4px;">9</td><td style="border: 1px solid #ccc; padding: 4px;">14</td><td style="border: 1px solid #ccc; padding: 4px;">3,30 %</td><td style="border: 1px solid #ccc; padding: 4px;">12,68 %</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OnePlus 12 Pro</td><td style="border: 1px solid #ccc; padding: 4px;">3</td><td style="border: 1px solid #ccc; padding: 4px;">3</td><td style="border: 1px solid #ccc; padding: 4px;">0,71 %</td><td style="border: 1px solid #ccc; padding: 4px;">4,23 %</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Google Pixel 8 Pro</td><td style="border: 1px solid #ccc; padding: 4px;">9</td><td style="border: 1px solid #ccc; padding: 4px;">21</td><td style="border: 1px solid #ccc; padding: 4px;">4,95 %</td><td style="border: 1px solid #ccc; padding: 4px;">12,68 %</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Nichts Telefon 2</td><td style="border: 1px solid #ccc; padding: 4px;">5</td><td style="border: 1px solid #ccc; padding: 4px;">8</td><td style="border: 1px solid #ccc; padding: 4px;">1,89 %</td><td style="border: 1px solid #ccc; padding: 4px;">7,04 %</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Samsung Galaxy Z Fold5</td><td style="border: 1px solid #ccc; padding: 4px;">4</td><td style="border: 1px solid #ccc; padding: 4px;">6</td><td style="border: 1px solid #ccc; padding: 4px;">1,42 %</td><td style="border: 1px solid #ccc; padding: 4px;">5,63 %</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OnePlus 12</td><td style="border: 1px solid #ccc; padding: 4px;">8</td><td style="border: 1px solid #ccc; padding: 4px;">20</td><td style="border: 1px solid #ccc; padding: 4px;">4,72 %</td><td style="border: 1px solid #ccc; padding: 4px;">11,27 %</td>
                    </tr>
              </tbody>
            </table>
          <p><strong>Beruf</strong>: Medizin &amp; Gesundheitswesen</p><p><strong>Termaggregation</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b008e996742fc0bb48dc6bacff17cfbc56cf0d73-1492x398.png" style="width: 100%;" /><p><strong>Aggregation signifikanter Terme</strong></p><p>Gesamtzahl der Dokumente: 424</p><p>Dokumente in diesem Beruf: 57</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Telefonmodell</td><td style="border: 1px solid #ccc; padding: 4px;">doc_count (dieses Modell in diesem Beruf)</td><td style="border: 1px solid #ccc; padding: 4px;">bg_count (Dieses Modell ist in allen Dokumenten enthalten)</td><td style="border: 1px solid #ccc; padding: 4px;">% in allen Dokumenten</td><td style="border: 1px solid #ccc; padding: 4px;">% in diesem Beruf</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">iPhone 16</td><td style="border: 1px solid #ccc; padding: 4px;">45</td><td style="border: 1px solid #ccc; padding: 4px;">122</td><td style="border: 1px solid #ccc; padding: 4px;">28,77 %</td><td style="border: 1px solid #ccc; padding: 4px;">78,95 %</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">iPhone 15 Pro Max</td><td style="border: 1px solid #ccc; padding: 4px;">3</td><td style="border: 1px solid #ccc; padding: 4px;">13</td><td style="border: 1px solid #ccc; padding: 4px;">3,07 %</td><td style="border: 1px solid #ccc; padding: 4px;">5,26 %</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">iPhone 15</td><td style="border: 1px solid #ccc; padding: 4px;">7</td><td style="border: 1px solid #ccc; padding: 4px;">40</td><td style="border: 1px solid #ccc; padding: 4px;">9,43 %</td><td style="border: 1px solid #ccc; padding: 4px;">12,28 %</td>
                    </tr>
              </tbody>
            </table>
          <p>Mal sehen, welche Geschichte uns diese Daten erzählen:</p><ul><li>Medizinisches Fachpersonal und Angehörige von Gesundheitsberufen bevorzugen das iPhone 16 und neigen generell sehr dazu, Apple-Handys zu benutzen.</li><li>Technologie- und Datenexperten bevorzugen High-End-Android-Smartphones, greifen aber nicht unbedingt auf die Marke Samsung zurück. Auch in dieser Kategorie ist ein deutlicher Trend zu iPhones zu beobachten.</li><li>Bei Verwaltungs- und Supportmitarbeitern sind Samsung- und Google-Handys beliebt, es gibt jedoch keinen ausgeprägten und eindeutigen Trend.</li></ul><h3>Aggregation signifikanter Begriffe und Hybridsuche</h3><p>Die Hybridsuche kombiniert Textsuche und semantische Ergebnisse, um ein verbessertes Sucherlebnis zu bieten. In diesem Kontext kann eine aussagekräftige Termaggregation Aufschluss über die Ergebnisse einer kontextbezogenen Suche geben, indem sie die Frage beantwortet: <strong>Was ist das Besondere an diesem Datensatz im Vergleich zu allen Dokumenten?</strong>Um diese Funktion zu veranschaulichen, sehen wir uns an, welche Modelle überrepräsentiert sind, wenn Nutzer von guter Leistung sprechen: </p><ul><li>Wir erstellen eine semantische Abfrage, bei der wir das beste Nutzerfeedback finden, das dem Eingabetext „gute Leistung“ im Feld „Einbettung“ am nächsten kommt.</li><li>Wir werden außerdem eine Textsuche mit denselben Begriffen im Textfeld user_feedback durchführen.</li><li>Wir werden außerdem eine Abfrage mit aussagekräftigen Begriffen hinzufügen, um Telefonmodelle zu finden, die in diesen Ergebnissen häufiger vorkommen als im gesamten Datensatz.<br/></li></ul><pre><code>GET phone_sales_analysis/_search
{
 "retriever": {
   "rrf": {
     "retrievers": [
       {
         "standard": {
           "query": {
             "bool": {
               "must": [
                 {
                   "match": {
                     "user_feedback": {
                       "query": "good performance",
                       "operator": "and"
                     }
                   }
                 }
               ]
             }
           }
         }
       },
       {
         "standard": {
           "query": {
             "semantic": {
               "field": "embedding",
               "query": "good performance"
             }
           }
         }
       }
     ],
    "rank_window_size": 20
   }
 },
 "aggs": {
   "Models": {
     "significant_terms": {
       "field": "phone_model"
     }
   }
 }
}</code></pre><p>Betrachten wir ein Beispiel für die übereinstimmenden Dokumente:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4cb488097a382f0c28c21540db4f593d23633473-1600x162.png" style="width: 100%;" /><p>Das ist die Antwort, die wir erhalten:</p><pre><code>{
  "took": 388,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 20,
      "relation": "eq"
    },
    "max_score": 0.016393442,
    "hits": [...]
  },
  "aggregations": {
    "Models": {
      "doc_count": 20,
      "bg_count": 424,
      "buckets": [
        {
          "key": "iPhone 15",
          "doc_count": 5,
          "score": 0.4125,
          "bg_count": 40
        }
      ]
    }
  }
}</code></pre><p></p><p>Dies bedeutet, dass ein iPhone 15 zwar 40 Mal in insgesamt 424 Dokumenten vorkommt (9,4 % der Dokumente), es aber 5 Mal in den 20 Dokumenten zu finden ist, die der semantischen Suche „gute Leistung“ entsprechen (25 % der Dokumente). Daraus lässt sich schließen: Die Wahrscheinlichkeit, ein iPhone 15 zu finden, ist 2,7-mal höher, wenn es um gute Leistung geht, als durch Zufall.</p><h2>Fazit</h2><p>Durch die Aggregation signifikanter Terme lassen sich einzigartige Details eines Datensatzes aufdecken, indem man ihn mit der Gesamtheit aller Dokumente vergleicht. Dadurch können unerwartete Zusammenhänge in unseren Daten aufgedeckt werden, die über die reine Anzahl der Vorkommen hinausgehen. Wir können in verschiedenen Anwendungsfällen aussagekräftige Begriffe einsetzen, die sehr interessante Funktionen ermöglichen, zum Beispiel:</p><ul><li><a href="https://www.elastic.co/blog/significant-terms-aggregation#credit">Bei der Betrugserkennung sollten Sie Muster erkennen </a>– identifizieren Sie typische Transaktionen gestohlener Kreditkarten.</li><li>Markenqualitätseinblicke aus Nutzerbewertungen – Marken mit einer unverhältnismäßig hohen Anzahl schlechter Bewertungen erkennen.</li><li><a href="https://www.elastic.co/docs/reference/aggregations/search-aggregations-bucket-significantterms-aggregation#_use_on_free_text_fields">Aufspüren </a>falsch klassifizierter Dokumente – Aufspüren von Dokumenten, die zu einer Kategorie gehören (Termfilter), die in einer Beschreibung ungewöhnliche Wörter für die Kategorie verwenden (Aggregation signifikanter Begriffe).</li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Entwicklung eines agentenbasierten RAG-Assistenten mit JavaScript und Mastra und Elasticsearch]]></title>
            <description><![CDATA[Lerne, wie du KI-Agenten im JavaScript-Ökosystem entwickelst.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/agentic-rag</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/agentic-rag</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[JD Armada]]></dc:creator>
            <pubDate>Tue, 01 Jul 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8132ed781c1ea5d46ca244182f421ed5c721f23b-1200x628.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Diese Idee kam mir mitten in einer hitzigen Fantasy-Basketball-Liga mit hohem Einsatz. Ich fragte mich: <em>Könnte ich einen KI-Agenten entwickeln, der mir hilft, meine wöchentlichen Begegnungen zu dominieren? Absolut!</em></p><p>In diesem Beitrag zeigen wir Ihnen, wie Sie mit <a href="https://mastra.ai/en/docs">Mastra</a> einen agentenbasierten RAG-Assistenten und eine leichtgewichtige JavaScript-Webanwendung zur Interaktion mit diesem erstellen. Durch die Anbindung dieses Agenten an Elasticsearch erhalten Sie Zugriff auf strukturierte Spielerdaten und die Möglichkeit, statistische Aggregationen in Echtzeit durchzuführen, um Ihnen auf Spielerstatistiken basierende Empfehlungen geben zu können. Besuchen Sie das GitHub- <a href="https://github.com/jdarmada/nba-ai-assistant-js.git">Repository</a> , um die Schritte nachzuvollziehen; die <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/README.md">README-Datei</a> enthält Anweisungen zum Klonen und Ausführen der Anwendung. </p><p>So sollte es aussehen, wenn alles zusammengebaut ist:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1c73bd1dc1b5fe54f025c7a2b7c322acc9122f3a-1999x1393.png" style="width: 100%;" /><p>Hinweis: Dieser Blogbeitrag baut auf „ <a href="https://www.elastic.co/search-labs/blog/ai-agents-ai-sdk-elasticsearch">Entwicklung von KI-Agenten mit AI SDK und Elastic</a>“ auf. Wenn Sie sich mit KI-Agenten im Allgemeinen noch nicht auskennen und nicht wissen, wofür sie eingesetzt werden könnten, fangen Sie dort an.<br/></p><h2><strong>Architekturübersicht</strong></h2><p>Kernstück des Systems ist ein großes Sprachmodell (LLM), das als Denkmaschine (das Gehirn) des Agenten fungiert. Es interpretiert die Benutzereingaben, entscheidet, welche Tools aufgerufen werden sollen, und koordiniert die notwendigen Schritte, um eine relevante Antwort zu generieren.</p><p>Der Agent selbst wird von Mastra, einem Agenten-Framework im JavaScript-Ökosystem, bereitgestellt. Mastra umschließt das LLM mit einer Backend-Infrastruktur, stellt es als API-Endpunkt bereit und bietet eine Schnittstelle zur Definition von Tools, Systemaufforderungen und Agentenverhalten.</p><p>Im Frontend verwenden wir <a href="https://vite.dev/guide/">Vite</a> , um schnell eine React-Webanwendung zu erstellen, die eine Chat-Oberfläche zum Senden von Anfragen an den Agenten und zum Empfangen seiner Antworten bereitstellt.</p><p>Schließlich gibt es noch Elasticsearch, das Spielerstatistiken und Matchup-Daten speichert, die der Agent abfragen und aggregieren kann.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/443bdc00d84ed1dd49e9f9e431e86ca4b0892563-1999x977.png" style="width: 100%;" /><h2><strong>Hintergrund</strong></h2><p>Lassen Sie uns einige grundlegende Konzepte durchgehen:</p><h3><strong>Was ist agentic RAG?</strong></h3><p>KI-Agenten können mit anderen Systemen interagieren, unabhängig agieren und Aktionen auf Basis ihrer definierten Parameter ausführen. Agentic RAG kombiniert die Autonomie eines KI-Agenten mit den Prinzipien der abrufverstärkten Generierung und ermöglicht es einem LLM, auszuwählen, welche Werkzeuge aufgerufen und welche Daten als Kontext verwendet werden sollen, um eine Antwort zu generieren. Lesen Sie <a href="https://www.elastic.co/search-labs/blog/retrieval-augmented-generation-rag">hier</a> mehr über RAG.</p><h3><strong>Warum sollte man bei der Wahl eines Frameworks über das AI-SDK hinausgehen?</strong></h3><p>Es gibt viele KI-Agenten-Frameworks, und Sie haben wahrscheinlich schon von den bekannteren wie <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> und <a href="https://www.elastic.co/search-labs/blog/build-rag-workflow-langgraph-elasticsearch">LangGraph</a> gehört. Die meisten dieser Frameworks verfügen über eine gemeinsame Funktionspalette, darunter die Unterstützung verschiedener Modelle, die Verwendung von Werkzeugen und das Speichermanagement.</p><p>Hier finden Sie eine <a href="https://docs.google.com/spreadsheets/d/1B37VxTBuGLeTSPVWtz7UMsCdtXrqV5hCjWkbHN8tfAo/edit?gid=0#gid=0">Vergleichstabelle</a> der verschiedenen Frameworks von Harrison Chase (CEO von LangChain).</p><p>Was mich an Mastra besonders interessiert hat, ist, dass es sich um ein JavaScript-basiertes Framework handelt, das für Full-Stack-Entwickler entwickelt wurde, um Agenten einfach in ihr Ökosystem zu integrieren. Das AI-SDK von Vercel kann das meiste davon auch, aber Mastra glänzt vor allem dann, wenn Ihre Projekte komplexere Agenten-Workflows beinhalten. Mastra erweitert die vom AI-SDK festgelegten Basismuster, und in diesem Projekt werden wir sie gemeinsam verwenden.</p><h3><strong>Rahmenwerke und Überlegungen zur Modellwahl</strong></h3><p>Diese Frameworks können zwar dabei helfen, schnell KI-Agenten zu entwickeln, es gibt jedoch einige Nachteile zu beachten. Wenn man beispielsweise andere Frameworks als KI-Agenten oder Abstraktionsschichten im Allgemeinen verwendet, verliert man ein Stück weit die Kontrolle. Wenn das LLM die Werkzeuge nicht korrekt verwendet oder etwas tut, was Sie nicht möchten, erschwert die Abstraktion die Fehlersuche. Dennoch ist dieser Kompromiss meiner Meinung nach den Komfort und die Geschwindigkeit wert, die man beim Erstellen von Projekten erhält, insbesondere weil diese Frameworks immer beliebter werden und ständig weiterentwickelt werden.</p><p>Auch diese Frameworks sind modellagnostisch, das heißt, Sie können verschiedene Modelle einsetzen und verwenden. Bedenken Sie jedoch, dass sich die Modelle in den Datensätzen unterscheiden, mit denen sie trainiert wurden, und dass sich dadurch auch die Ergebnisse unterscheiden. Manche Modelle unterstützen nicht einmal den Aufruf von Tools. Es ist also möglich, zwischen verschiedenen Modellen zu wechseln und diese auszuprobieren, um zu sehen, welches die besten Ergebnisse liefert. Bedenken Sie jedoch, dass Sie höchstwahrscheinlich für jedes Modell die Systemabfrage neu schreiben müssen. Zum Beispiel mit Llama3.3 Im Vergleich zu GPT-4o sind deutlich mehr Aufforderungen und spezifische Anweisungen erforderlich, um die gewünschte Antwort zu erhalten.</p><h3><strong>NBA Fantasy Basketball</strong></h3><p>Beim Fantasy-Basketball geht es darum, mit einer Gruppe von Freunden eine Liga zu gründen (Achtung: Je nachdem, wie wettbewerbsorientiert eure Gruppe ist, könnte dies den Status eurer Freundschaften beeinträchtigen), wobei in der Regel auch Geld auf dem Spiel steht. Jeder von euch stellt dann ein Team aus 10 Spielern zusammen, die abwechselnd wöchentlich gegen die 10 Spieler eines anderen Freundes antreten. Die Punkte, die zu Ihrer Gesamtpunktzahl beitragen, ergeben sich aus der Leistung jedes Ihrer Spieler gegen seine Gegner in einer bestimmten Woche.</p><p>Wenn ein Spieler Ihres Teams verletzt, gesperrt usw. wird, steht Ihnen eine Liste von Free Agents zur Verfügung, die Sie Ihrem Team hinzufügen können. Hier findet ein Großteil des schwierigen Denkens im Fantasy-Sport statt, denn man hat nur eine begrenzte Anzahl an Spielern zur Auswahl und jeder ist ständig auf der Suche nach dem besten Spieler.</p><p>Hier wird unser NBA-KI-Assistent seine Stärken ausspielen, insbesondere in Situationen, in denen Sie schnell entscheiden müssen, welchen Spieler Sie auswählen. Anstatt manuell nachschlagen zu müssen, wie ein Spieler gegen einen bestimmten Gegner abschneidet, kann der Assistent diese Daten schnell finden und Durchschnittswerte vergleichen, um Ihnen eine fundierte Empfehlung zu geben.</p><p>Nachdem Sie nun einige Grundlagen zu Agentic RAG und NBA Fantasy Basketball kennengelernt haben, sehen wir uns das Ganze mal in der Praxis an.</p><h2><strong>Aufbau des Projekts</strong></h2><p>Falls Sie an irgendeiner Stelle nicht weiterkommen oder es nicht von Grund auf neu erstellen möchten, schauen Sie bitte im <a href="https://github.com/jdarmada/nba-ai-assistant-js.git">Repository</a> nach.</p><h3><strong>Was wir behandeln werden</strong></h3><ol><li><strong>Gerüstbau für das Projekt:</strong><ol><li><strong>Backend (Mastra):</strong> Verwenden Sie npx create mastra@latest, um das Backend zu erstellen und die Agentenlogik zu definieren.</li><li><strong>Frontend (Vite + React):</strong> Verwenden Sie npm create vite@latest, um die Frontend-Chat-Oberfläche für die Interaktion mit dem Agenten zu erstellen.</li></ol></li><li><strong>Einrichten von Umgebungsvariablen</strong><ol><li>Installieren Sie dotenv, um Umgebungsvariablen zu verwalten.</li><li>Erstellen Sie eine .env-Datei Datei und geben Sie die erforderlichen Variablen an.</li></ol></li><li><strong>Elasticsearch einrichten</strong><ol><li>Erstellen Sie einen Elasticsearch-Cluster (entweder lokal oder in der Cloud).</li><li>Installieren Sie den offiziellen Elasticsearch-Client.</li><li>Stellen Sie sicher, dass Umgebungsvariablen zugänglich sind.</li><li>Verbindung zum Client herstellen.</li></ol></li><li><strong>Massenhaftes Einlesen von NBA-Daten in Elasticsearch</strong><ol><li>Erstellen Sie einen Index mit den entsprechenden Zuordnungen, um Aggregationen zu ermöglichen.</li><li>Spielerstatistiken aus einer CSV-Datei massenhaft in einen Elasticsearch-Index importieren.</li></ol></li><li><strong>Elasticsearch-Aggregationen definieren</strong><ol><li>Abfrage zur Berechnung historischer Durchschnittswerte gegen einen bestimmten Gegner.</li><li>Abfrage zur Berechnung der Saison-Durchschnittswerte gegen einen bestimmten Gegner.</li></ol></li><li><strong>Datei zum Spielervergleich</strong><ol><li>Konsolidiert Hilfsfunktionen und Elasticsearch-Aggregationen.</li></ol></li><li><strong>Agentenaufbau</strong><ol><li>Fügen Sie die Agentendefinition und die Systemeingabeaufforderung hinzu.</li><li>Installieren Sie zod und definieren Sie Tools.</li><li>Fügen Sie eine Middleware-Konfiguration zur Behandlung von CORS hinzu.</li></ol></li><li><strong>Integration des Frontends</strong><ol><li>Die Interaktion mit dem Agenten erfolgt über die useChat-Funktion des AI-SDK.</li><li>Erstellen Sie eine Benutzeroberfläche, die ordnungsgemäß formatierte Konversationen ermöglicht.</li></ol></li><li><strong>Die Anwendung ausführen</strong><ol><li>Starten Sie sowohl das Backend (Mastra-Server) als auch das Frontend (React-App).</li><li>Beispielabfragen und Anwendungsbeispiele.</li></ol></li><li><strong>Was kommt als Nächstes: Den Agenten intelligenter machen.</strong><ol><li>Durch die Integration semantischer Suchfunktionen können aussagekräftigere Empfehlungen ermöglicht werden.</li><li>Aktivieren Sie dynamische Abfragen, indem Sie die Suchlogik auf den Elasticsearch MCP (Model Context Protocol)-Server verlagern.</li></ol></li></ol><h3><strong>Voraussetzungen</strong></h3><ul><li><strong>Node.js und npm</strong>: Sowohl das Backend als auch das Frontend laufen auf Node.js. Stellen Sie sicher, dass Sie Node 18+ und npm v9+ installiert haben (das in Node 18+ standardmäßig enthalten ist).</li><li><strong>Elasticsearch-Cluster:</strong> Ein aktiver Elasticsearch-Cluster, entweder lokal oder in der Cloud.</li><li><strong>OpenAI API-Schlüssel</strong>: Generieren Sie einen auf der Seite „API-Schlüssel“ im <a href="https://platform.openai.com/api-keys">Entwicklerportal von OpenAI</a>.</li></ul><p></p><h3><strong>Projektstruktur</strong></h3><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1c0bde11ad0eead523a95e03b9b905aa776e3fd1-1420x934.png" style="width: 100%;" /><h4><strong>Schritt 1: Das Projektgerüst erstellen</strong></h4><ol><li>Erstellen Sie zunächst das Verzeichnis nba-ai-assistant-js und navigieren Sie darin mit folgendem Befehl: </li></ol><pre><code>mkdir nba-ai-assistant-js && cd nba-ai-assistant-js</code></pre><p><strong>Backend:</strong></p><ol><li>Verwenden Sie das Mastra-Erstellungstool mit folgendem Befehl: </li></ol><pre><code>npx create-mastra@latest</code></pre><p>2. Sie sollten nun einige Eingabeaufforderungen in Ihrem Terminal erhalten. Bei der ersten nennen wir das Projekt-Backend:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/de2725031ed6837db99a979efcdd0ece1e197dbb-608x84.png" style="width: 100%;" /><p>3. Als nächstes behalten wir die Standardstruktur für die Speicherung der Mastra-Dateien bei, also Eingabe <code>src/</code>.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/88919d9ff1852126395e1fcd700ecb1b59aac63c-866x116.png" style="width: 100%;" /><p>4. Dann wählen wir OpenAI als unseren Standard-LLM-Anbieter.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2328761e769f3ded134e5a21e8a0bf8f41e88f68-404x210.png" style="width: 100%;" /><p>5. Abschließend werden Sie nach Ihrem OpenAI-API-Schlüssel gefragt. Fürs Erste wählen wir die Option, dies zu überspringen und es später in einer<code> .env</code> -Datei bereitzustellen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0662de9bd28758e377e4c63df8d08b479068ce63-444x120.png" style="width: 100%;" /><p><strong>Frontend:</strong></p><ol><li>Wechseln Sie zurück in das Stammverzeichnis und führen Sie das <a href="https://vite.dev/guide/">Vite-Erstellungstool</a> mit folgendem Befehl aus: <code>npm create vite@latest frontend -- --template react</code></li></ol><p>Dadurch sollte eine leichtgewichtige React-App mit dem Namen <code>frontend</code> und einer spezifischen Vorlage für React erstellt werden.</p><p>Wenn alles gut geht, sollten Sie in Ihrem Projektverzeichnis ein Backend-Verzeichnis sehen, das den Mastra-Code enthält, und ein <code>frontend</code> -Verzeichnis mit Ihrer React-App.</p><p></p><h4><strong>Schritt 2: Umgebungsvariablen einrichten</strong></h4><ol><li>Zur Verwaltung sensibler Schlüssel verwenden wir das Paket <code>dotenv</code> , um unsere Umgebungsvariablen aus der .env-Datei zu laden. Datei. Navigieren Sie zum Backend-Verzeichnis und installieren Sie <code>dotenv</code>:</li></ol><pre><code>cd backend
npm install dotenv --save</code></pre><p>2. Im Backend-Verzeichnis befindet sich eine example.env-Datei mit den entsprechenden Variablen zum Ausfüllen. Wenn Sie Ihre eigene Version erstellen, achten Sie darauf, die folgenden Variablen einzubeziehen:</p><pre><code># 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
</code></pre><p></p><p>Hinweis: Stellen Sie sicher, dass diese Datei von Ihrer Versionskontrolle ausgeschlossen wird, indem Sie <code>.env</code> zu <code>.gitignore</code> hinzufügen.</p><h4><strong>Schritt 3: Elasticsearch einrichten</strong></h4><p>Als Erstes benötigen Sie einen aktiven Elasticsearch-Cluster. Es gibt zwei Möglichkeiten:</p><ul><li><strong>Option A: Elasticsearch Cloud verwenden</strong><ul><li>Registrieren Sie sich für <a href="https://cloud.elastic.co/registration">Elastic Cloud</a></li><li>Erstellen Sie eine neue Bereitstellung</li><li>Rufen Sie Ihre Endpunkt-URL und Ihren API-Schlüssel (kodiert) ab.</li></ul></li><li><strong>Option B: Elasticsearch lokal ausführen</strong><ul><li>Elasticsearch lokal installieren und ausführen</li><li>Verwenden Sie http://localhost:9200 als Endpunkt.</li><li>API-Schlüssel generieren</li></ul></li></ul><p></p><p><strong>Installation des Elasticsearch-Clients auf dem Backend:</strong></p><ol><li>Installieren Sie zunächst den offiziellen Elasticsearch-Client in Ihrem Backend-Verzeichnis:</li></ol><pre><code>npm install @elastic/elasticsearch</code></pre><p>2. Erstellen Sie anschließend ein Verzeichnis „lib“, um wiederverwendbare Funktionen zu speichern, und wechseln Sie in dieses Verzeichnis:</p><pre><code>mkdir lib && cd lib</code></pre><p>3. Erstellen Sie darin eine neue Datei namens <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/lib/elasticClient.js">elasticClient.js</a>. Diese Datei initialisiert den Elasticsearch-Client und stellt ihn für die Verwendung in Ihrem gesamten Projekt zur Verfügung.</p><p>4. Da wir ECMAScript-Module (ESM) verwenden, sind __dirname and __-Dateinamen nicht verfügbar. Um sicherzustellen, dass Ihre Umgebungsvariablen korrekt aus der .env-Datei geladen werden Fügen Sie diese Konfiguration am Anfang der Datei im Backend-Ordner hinzu:</p><pre><code>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 });</code></pre><p>5. Initialisieren Sie nun den Elasticsearch-Client mithilfe Ihrer Umgebungsvariablen und überprüfen Sie die Verbindung:</p><pre><code>//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();
</code></pre><p>Nun können wir diese Clientinstanz in jede Datei importieren, die mit Ihrem Elasticsearch-Cluster interagieren muss.</p><p></p><h4><strong>Schritt 4: Massenhaftes Einlesen von NBA-Daten in Elasticsearch</strong></h4><p><strong>Datensatz:</strong></p><p>Für dieses Projekt werden wir auf die Datensätze im Verzeichnis <a href="https://github.com/jdarmada/nba-ai-assistant-js/tree/main/backend">backend/data</a> des Repos zurückgreifen. Unser NBA-Assistent wird diese Daten als Wissensbasis für statistische Vergleiche und die Generierung von Empfehlungen nutzen.</p><ul><li><a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/data/sample_nba_data.csv">sample_player_game_stats.csv</a> - Beispielhafte Spielstatistiken eines Spielers (z. B. Punkte, Rebounds, Steals usw.) pro Spiel und Spieler während seiner gesamten NBA-Karriere. Wir werden diesen Datensatz für Aggregationen verwenden. (Hinweis: Dies sind Beispieldaten, die zu Demonstrationszwecken generiert wurden und nicht aus offiziellen NBA-Quellen stammen.)</li><li><a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/data/playerAndTeamInfo.js">playerAndTeamInfo.js</a> - Ersetzt die Spieler- und Team-Metadaten, die normalerweise über einen API-Aufruf bereitgestellt werden, damit der Agent Spieler- und Teamnamen IDs zuordnen kann. Da wir Beispieldaten verwenden, möchten wir den Aufwand des Abrufens von einer externen API vermeiden. Daher haben wir einige Werte fest codiert, auf die der Agent zugreifen kann.</li></ul><p></p><p><strong>Durchführung:</strong></p><ol><li>Erstellen Sie im Verzeichnis <code>backend/lib</code> eine Datei mit dem Namen <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/lib/playerDataIngestion.js">playerDataIngestion.js</a>.</li><li>Importe einrichten, den CSV-Dateipfad auflösen und das Parsen einrichten. Da wir ESM verwenden, müssen wir <code>__dirname</code> rekonstruieren, um den Pfad zur Beispiel-CSV-Datei aufzulösen. Außerdem importieren wir <a href="http://node.js/">Node.js.</a> eingebaute Module <code>fs</code> und <code>readline</code>, um die gegebene CSV-Datei Zeile für Zeile zu analysieren.</li></ol><pre><code>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');</code></pre><p>Damit sind Sie bestens gerüstet, um die CSV-Datei effizient zu lesen und zu analysieren, wenn wir zum Schritt der Massenverarbeitung übergehen.</p><p>3. Erstellen Sie einen Index mit der entsprechenden Zuordnung. Obwohl Elasticsearch Feldtypen mithilfe von <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/dynamic">Dynamic Mapping</a> automatisch ableiten kann, möchten wir hier explizit vorgehen, damit jede Statistik als numerisches Feld behandelt wird. Dies ist wichtig, da wir diese Felder später für Aggregationen verwenden werden. Wir möchten außerdem den Typ <code>float </code>für Statistiken wie Punkte, Rebounds usw. verwenden, um sicherzustellen, dass wir Dezimalwerte einbeziehen. Zum Schluss möchten wir die Mapping-Eigenschaft <code>dynamic: &#x27;strict&#x27;</code> hinzufügen, damit Elasticsearch nicht dynamisch nicht erkannte Felder zuordnet. <br/></p><pre><code>// 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;
    }
}
</code></pre><p>4. Fügen Sie die Funktion hinzu, um die CSV-Daten in Ihren Elasticsearch-Index zu importieren. Innerhalb des Codeblocks überspringen wir die Kopfzeile. Anschließend werden die einzelnen Zeilen durch Kommas getrennt und in das Dokumentobjekt eingefügt. Dieser Schritt reinigt sie außerdem und stellt sicher, dass es sich um den richtigen Typ handelt. Als nächstes fügen wir die Dokumente zusammen mit den Indexinformationen in das bulkBody-Array ein, welches als Nutzlast für die Massenaufnahme in Elasticsearch dient.</p><pre><code>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`);
</code></pre><p>5. Anschließend können wir <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-bulk">die Bulk-API</a> von Elasticsearch mit <code>elasticClient.bulk()</code> verwenden, um mehrere Dokumente in einer einzigen Anfrage zu erfassen. Die unten beschriebene Fehlerbehandlung ist so aufgebaut, dass sie Ihnen eine Übersicht darüber gibt, wie viele Dokumente nicht eingelesen werden konnten und wie viele erfolgreich eingelesen wurden.</p><pre><code>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) => {
                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);
    }
}
</code></pre><p>6. Führen Sie die unten stehende Funktion <code>main()</code> aus, um die Funktionen <code>createIndex()</code> und <code>bulkIngestCsv()</code> nacheinander auszuführen.</p><pre><code>// 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();
</code></pre><p>Wenn in der Konsole eine Meldung erscheint, dass die Massenaufnahme erfolgreich war, überprüfen Sie kurz Ihren Elasticsearch-Index, um festzustellen, ob die Dokumente tatsächlich erfolgreich aufgenommen wurden.</p><h4><strong>Schritt 5: Elasticsearch-Aggregationen definieren und konsolidieren</strong></h4><p>Dies sind die Hauptfunktionen, die wir verwenden werden, wenn wir die Werkzeuge für den KI-Agenten definieren, um die Statistiken der Spieler miteinander zu vergleichen.</p><p>1. Navigieren Sie zum Verzeichnis <code>backend/lib</code> und erstellen Sie eine Datei namens <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/lib/elasticAggs.js">elasticAggs.js</a>.</p><p>2. Fügen Sie die unten stehende Abfrage hinzu, um historische Durchschnittswerte für einen Spieler gegen einen bestimmten Gegner zu berechnen. Diese Abfrage verwendet einen <code>bool</code> <a href="https://www.elastic.co/search-labs/tutorials/search-tutorial/full-text-search/filters">-Filter</a> mit 2 Bedingungen: eine, die <code>player_id</code> entspricht, und eine weitere, die <code>opponent_team_id</code> entspricht, um nur die relevanten Spiele abzurufen. Wir müssen keine Dokumente zurückgeben, uns geht es nur um die Aggregationen, deshalb setzen wir <code>size:0</code>. Im Block <code>aggs</code> führen wir mehrere <a href="https://www.elastic.co/docs/explore-analyze/query-filter/aggregations">Metrikaggregationen</a> parallel auf Feldern wie <code>points, rebounds, assists, steals, blocks</code> und <code>fg_percentage</code> durch, um deren Durchschnittswerte zu berechnen. LLMs können bei Berechnungen ungenau sein, und dies lagert diesen Prozess an Elasticsearch aus, um sicherzustellen, dass unser NBA-KI-Assistent Zugriff auf genaue Daten hat.</p><pre><code>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.' };
    }
}
</code></pre><p>3. Um die Saisondurchschnittswerte eines Spielers gegen einen bestimmten Gegner zu berechnen, verwenden wir praktisch die gleiche Abfrage wie für die historischen Ergebnisse. Der einzige Unterschied bei dieser Abfrage besteht darin, dass der <code>bool</code> -Filter eine zusätzliche Bedingung für <code>game_date</code> enthält. Das Feld <code>game_date</code> muss innerhalb des Bereichs der aktuellen NBA-Saison liegen. In diesem Fall liegt der Bereich zwischen <code>2024-10-01</code> und <code>2025-06-30</code>. Die unten stehende zusätzliche Bedingung stellt sicher, dass die nachfolgenden Aggregationen nur die Spiele dieser Saison berücksichtigen.<br/></p><pre><code>        {
                            range: {
                    //Range for this season, change to match current season
                                game_date: {
                                    gte: '2024-10-01',
                                    lte: '2025-06-30',
                                },
                            },
</code></pre><h4><strong>Schritt 6: Spielervergleichstool</strong></h4><p>Um unseren Code modular und wartbar zu halten, erstellen wir eine Hilfsdatei, die Metadaten-Hilfsfunktionen und Elasticsearch-Aggregationen zusammenfasst. Dies wird das Hauptwerkzeug des Agenten mit Strom versorgen. Mehr dazu später:</p><p>1. Erstellen Sie eine neue Datei <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/lib/comparePlayers.js">comparePlayers.js</a> im Verzeichnis <code>backend/lib</code> .</p><p>2. Fügen Sie die unten stehende Funktion hinzu, um Metadaten-Helfer und Elasticsearch-Aggregationslogik in einer einzigen Funktion zu konsolidieren, die das Hauptwerkzeug des Agenten antreibt.<br/></p><pre><code>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];
}
</code></pre><h4><strong>Schritt 7: Erstellung des Agenten</strong></h4><p>Nachdem Sie nun das Frontend- und Backend-Gerüst erstellt, NBA-Spieldaten eingespielt und eine Verbindung zu Elasticsearch hergestellt haben, können wir damit beginnen, alle Teile zusammenzufügen, um den Agenten zu entwickeln.</p><p><strong>Definition des Agenten</strong></p><p>1. Navigieren Sie zur Datei <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/src/mastra/agents/index.ts">index.ts</a> im Verzeichnis <code>backend/src/mastra/agents</code> und fügen Sie die Agentendefinition hinzu. Sie können Felder wie die folgenden angeben:</p><ul><li><strong>Name:</strong> Geben Sie Ihrem Agenten einen Namen, der als Referenz verwendet wird, wenn er im Frontend aufgerufen wird.</li><li><strong>Anweisungen/Systemaufforderung: </strong>Eine Systemaufforderung gibt dem LLM den anfänglichen Kontext und die Regeln vor, die während der Interaktion zu befolgen sind. Es ähnelt der Aufforderung, die Benutzer über das Chatfenster senden, diese wird jedoch vor jeglicher Benutzereingabe angezeigt. Auch dies hängt vom gewählten Modell ab.</li><li><strong>Modell:</strong> Welches LLM soll verwendet werden (Mastra unterstützt OpenAI, Anthropic, lokale Modelle usw.)?</li><li><strong>Tools:</strong> Eine Liste der Tool-Funktionen, die der Agent aufrufen kann.</li><li><strong>Speicher:</strong> (Optional) Wenn der Agent sich den Gesprächsverlauf usw. merken soll. Der Einfachheit halber können wir ohne persistenten Speicher beginnen, obwohl Mastra diesen unterstützt.</li></ul><p></p><pre><code>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 },
});
</code></pre><p><strong><br/>Werkzeuge definieren</strong></p><ol><li>Navigieren Sie zur Datei <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/src/mastra/tools/index.ts">index.ts</a> im Verzeichnis <code>backend/src/mastra/tools</code> .</li><li>Installieren Sie Zod mit folgendem Befehl:</li></ol><pre><code>npm install zod</code></pre><p>3. Werkzeugdefinitionen hinzufügen. Beachten Sie, dass wir die Funktion innerhalb der Datei <code>comparePlayers.js</code> als Hauptfunktion importieren, die der Agent beim Aufruf dieses Tools verwenden wird. Mit der Funktion <code>createTool()</code> von Mastra registrieren wir unser <code>playerComparisonTool</code>. Zu den Feldern gehören:</p><ul><li><code>id</code>Dies ist eine Beschreibung in natürlicher Sprache, die dem Agenten hilft zu verstehen, was das Tool leistet.</li><li><code>input schema</code>Um die Form der Eingabe für das Tool zu definieren, verwendet Mastra das <a href="https://zod.dev/">Zod</a> -Schema, eine TypeScript-Bibliothek zur Schema-Validierung. Zod hilft dabei, indem es sicherstellt, dass der Agent korrekt strukturierte Eingaben vornimmt und die Ausführung des Tools verhindert, wenn die Eingabestruktur nicht übereinstimmt.</li><li><code>description</code>Dies ist eine Beschreibung in natürlicher Sprache, die dem Agenten helfen soll zu verstehen, wann er anrufen und das Tool verwenden soll.</li><li><code>execute</code>Die Logik, die beim Aufruf des Tools ausgeführt wird. In unserem Fall verwenden wir eine importierte Hilfsfunktion, um Leistungsstatistiken zurückzugeben.</li></ul><pre><code>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 } }) => {
        return await comparePlayersForNextMatchup(player1, player2);
      },
})</code></pre><p><strong>Hinzufügen von Middleware zur Behandlung von CORS</strong></p><p>Fügen Sie Middleware auf dem Mastra-Server hinzu, um <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS">CORS</a> zu behandeln. Man sagt, es gäbe drei Dinge im Leben, denen man nicht entkommen kann: den Tod, die Steuern und für Webentwickler zusätzlich CORS. Kurz gesagt, Cross-Origin Resource Sharing ist eine Browser-Sicherheitsfunktion, die verhindert, dass das Frontend Anfragen an ein Backend sendet, das auf einer anderen Domain oder einem anderen Port läuft. Obwohl wir sowohl das Backend als auch das Frontend auf localhost betreiben, verwenden sie unterschiedliche Ports, wodurch die CORS-Richtlinie ausgelöst wird. Wir müssen die in der <a href="https://mastra.ai/en/docs/server-db/middleware">Mastra-Dokumentation</a> beschriebene Middleware hinzufügen, damit unser Backend diese Anfragen vom Frontend zulässt.</p><p>1. Navigieren Sie zur Datei <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/src/mastra/index.ts">index.ts</a> im Verzeichnis <code>backend/src/mastra</code> und fügen Sie die Konfiguration für CORS hinzu:</p><ul><li><code>origin: [&#x27;http://localhost:5173&#x27;]</code><ul><li>Erlaubt Anfragen nur von dieser Adresse (Vite-Standardadresse)</li></ul></li><li><code>allowMethods: [&quot;GET&quot;, &quot;POST&quot;]</code><ul><li>Zulässige HTTP-Methoden. In den meisten Fällen wird POST verwendet.</li></ul></li><li><code>allowHeaders: [&quot;Content-Type&quot;, &quot;Authorization&quot;, &quot;x-mastra-client-type, &quot;x-highlight-request&quot;, &quot;traceparent&quot;],</code><ul><li>Diese legen fest, welche benutzerdefinierten Header in Anfragen verwendet werden können.</li></ul></li></ul><p></p><pre><code>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
</code></pre><h4><strong>Schritt 8: Integration des Frontends</strong></h4><p>Diese React-Komponente stellt eine einfache Chat-Oberfläche bereit, die über den <a href="https://mastra.ai/en/docs/frameworks/agentic-uis/ai-sdk#using-the-usechat-hook">useChat()</a> -Hook aus <code>@ai-sdk/react</code> eine Verbindung zum Mastra AI-Agenten herstellt. Wir werden diesen Hook auch verwenden, um die Tokenverwendung und Toolaufrufe anzuzeigen sowie die Konversation darzustellen. In der obigen Systemabfrage bitten wir den Agenten außerdem, die Antwort im Markdown-Format auszugeben. Daher verwenden wir <code>react-markdown</code> , um die Antwort korrekt zu formatieren.</p><p></p><p>1. Installieren Sie im Frontend-Verzeichnis das Paket @ai-sdk/react, um den useChat()-Hook verwenden zu können.</p><pre><code>npm install @ai-sdk/react</code></pre><p>2. Installieren Sie im selben Verzeichnis React Markdown, damit wir die vom Agenten generierte Antwort richtig formatieren können.</p><pre><code>npm install react-markdown</code></pre><p>3. Implementiere <code>useChat()</code>. Dieser Hook steuert die Interaktion zwischen Ihrem Frontend und Ihrem KI-Agenten-Backend. Es verarbeitet Nachrichtenstatus, Benutzereingaben und Statusinformationen und bietet Lebenszyklus-Hooks zur Überwachung. Zu den Optionen, die wir übergeben, gehören:</p><ul><li><code>api:</code> Dies definiert den Endpunkt Ihres Mastra AI-Agenten. Standardmäßig wird Port 4111 verwendet, und wir möchten außerdem die Route hinzufügen, die Streaming-Antworten unterstützt.</li><li><code>onToolCall</code>Dies wird immer dann ausgeführt, wenn der Agent ein Tool aufruft; wir verwenden es, um zu verfolgen, welche Tools unser Agent aufruft.</li><li><code>onFinish</code>Dies wird ausgeführt, nachdem der Agent eine vollständige Antwort abgegeben hat. Auch wenn wir Streaming aktiviert haben, wird <code>onFinish</code> erst ausgeführt, nachdem die gesamte Nachricht empfangen wurde, und nicht nach jedem einzelnen Datenblock. Hier verwenden wir es, um unsere Token-Nutzung zu verfolgen. Dies kann hilfreich sein, um die LLM-Kosten zu überwachen und zu optimieren.</li></ul><p>4. Zum Schluss gehen Sie zur Komponente <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/frontend/components/ChatUI.jsx">ChatUI.jsx</a> im Verzeichnis <code>frontend/components</code> , um die Benutzeroberfläche für unsere Konversation zu erstellen. Als Nächstes muss die Antwort in eine <code>ReactMarkdown</code> -Komponente eingeschlossen werden, um die Antwort des Agenten korrekt zu formatieren.</p><pre><code>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 }) => {
            setToolsCalled((prev) => [...prev, toolCall.toolName]);
        },

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

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

    return (
        <div>
            <div className="agent-info">
                <h4 className="stats-title">What's My Agent Doing?</h4>

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

                    <div className="usage-stats">
                        <p>Prompt Token Usage: {promptTokenUsage}</p>
                        <p>Completion Token Usage: {completionTokenUsage}</p>
                        <p>Total Token Usage: {totalTokenUsage}</p>
                    </div>
                </div>
            </div>

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

            <form onSubmit={handleSubmit}>
                <input
                    type="text"
                    value={input}
                    onChange={handleInputChange}
                    placeholder="Input two players you want to compare."
                    className="input-box"
                />
                <button type="submit" disabled={status === 'streaming'}>
                    {status === 'streaming' ? 'Thinking...' : 'Send'}
                </button>
            </form>
        </div>
    );
}</code></pre><h4><strong>Schritt 9: Ausführen der Anwendung</strong></h4><p>Glückwunsch! Sie können die Anwendung nun ausführen. Folgen Sie diesen Schritten, um sowohl das Backend als auch das Frontend zu starten.</p><ol><li>Öffnen Sie ein Terminalfenster, beginnend im Stammverzeichnis, und navigieren Sie zum Backend-Verzeichnis. Starten Sie dort den Mastra-Server:</li></ol><pre><code>cd backend

npm run dev</code></pre><p>2. Öffnen Sie ein weiteres Terminalfenster, beginnend mit dem Stammverzeichnis, und navigieren Sie zum Frontend-Verzeichnis. Starten Sie dort die React-App:</p><p></p><pre><code>cd frontend

npm run dev</code></pre><p></p><p>3. Öffnen Sie Ihren Browser und navigieren Sie zu:</p><p></p><p><a href="http://localhost:5173/">http://localhost:5173</a></p><p></p><p>Sie sollten die Chat-Oberfläche sehen können. Probieren Sie diese Beispielaufforderungen aus:</p><ul><li>„Vergleiche LeBron James und Stephen Curry“</li><li>&quot;Wen soll ich wählen, Jayson Tatum oder Luka Doncic?&quot;</li></ul><p></p><h3><strong>Was kommt als Nächstes: Den Agenten intelligenter machen.</strong></h3><p>Um den Assistenten handlungsfähiger und die Empfehlungen aussagekräftiger zu gestalten, werde ich in der nächsten Version einige wichtige Verbesserungen vornehmen.</p><p></p><p><strong>Semantische Suche nach NBA-Nachrichten</strong></p><p>Es gibt unzählige Faktoren, die die Leistung eines Spielers beeinflussen können, viele davon spiegeln sich nicht in den reinen Statistiken wider. Dinge wie Verletzungsberichte, Aufstellungsänderungen oder auch eine Spielanalyse nach dem Spiel findet man nur in Nachrichtenartikeln. Um diesen zusätzlichen Kontext zu erfassen, werde ich semantische Suchfunktionen hinzufügen, damit der Agent relevante NBA-Artikel abrufen und diese Erzählung in seine Empfehlungen einbeziehen kann.</p><p></p><p><strong>Dynamische Suche mit dem Elasticsearch MCP-Server</strong></p><p>MCP (Model Context Protocol) entwickelt sich schnell zum Standard für die Art und Weise, wie Agenten Verbindungen zu Datenquellen herstellen. Ich werde die Suchlogik auf den Elasticsearch MCP-Server migrieren, wodurch der Agent Abfragen dynamisch erstellen kann, anstatt sich auf vordefinierte Suchfunktionen zu verlassen, die wir bereitstellen. Dies ermöglicht uns die Nutzung von Workflows in natürlicher Sprache und reduziert den Aufwand, jede einzelne Suchanfrage manuell zu formulieren. Erfahren Sie <a href="https://www.elastic.co/search-labs/blog/mcp-current-state">hier</a> mehr über den Elasticsearch MCP-Server und den aktuellen Stand des Ökosystems.</p><p></p><p>Diese Änderungen sind bereits im Gange, bleiben Sie gespannt!</p><h3><strong>Fazit</strong></h3><p></p><p>In diesem Blog haben wir einen agentenbasierten RAG-Assistenten entwickelt, der mithilfe von JavaScript, Mastra und Elasticsearch maßgeschneiderte Empfehlungen für Ihr Fantasy-Basketballteam liefert. Wir behandelten Folgendes:</p><ul><li><strong>Grundlagen von Agentic RAG</strong> und wie die Kombination der Autonomie eines KI-Agenten mit den Werkzeugen zur effektiven Nutzung von RAG zu differenzierteren und dynamischeren Agenten führen kann.</li><li><strong>Elasticsearch </strong>und wie seine Datenspeicherfunktionen und leistungsstarken nativen Aggregationen es zu einem großartigen Partner als Wissensbasis für ein LLM machen.</li><li><strong>Das Mastra- </strong>Framework und wie es die Entwicklung dieser Agenten für Entwickler im JavaScript-Ökosystem vereinfacht.</li></ul><p>Egal, ob Sie ein Basketballfanatiker sind, sich damit beschäftigen, wie man KI-Agenten entwickelt, oder beides wie ich, ich hoffe, dieser Blog hat Ihnen einige Bausteine für den Einstieg geliefert. Das vollständige Repository ist auf <a href="https://github.com/jdarmada/nba-ai-assistant-js">GitHub</a> verfügbar, Sie können es gerne klonen und damit experimentieren. Jetzt hol dir den Sieg in der Fantasy-Liga!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Ruby-Skripting in Logstash]]></title>
            <description><![CDATA[Erfahren Sie mehr über das Logstash Ruby Filter-Plugin für die erweiterte Datentransformation in Ihrer Logstash-Pipeline.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/ruby-scripting-logstash</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/ruby-scripting-logstash</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Dai Sugimori]]></dc:creator>
            <pubDate>Tue, 24 Jun 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b1bcd63c033ccbde102c20ba3085f165f9289a71-1600x1000.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Logstash ist eine Datenverarbeitungspipeline, die Daten aus verschiedenen Quellen aufnimmt, transformiert und an die von Ihnen gewählten Ziele sendet. Filter-Plugins sind für diesen Prozess unerlässlich; sie führen spezifische Operationen an Ihren Daten durch, während diese die Datenpipeline durchlaufen.</p><p>Logstash enthält mehrere integrierte Filter für häufige Aufgaben wie das Parsen, Anreichern und Modifizieren von Daten. Manchmal stößt man jedoch auf Szenarien, die eine benutzerdefinierte Logik erfordern, die über das hinausgeht, was diese Standardfilter bieten können. Hier kommt das <a href="https://www.elastic.co/docs/reference/logstash/plugins/plugins-filters-ruby">Ruby-Filter-Plugin</a> ins Spiel.</p><p><strong>Mit dem Ruby-Filter-Plugin können Sie benutzerdefinierten Ruby-Code direkt in Ihrer Logstash-Pipeline ausführen.</strong> Wenn Standardfilter nicht ausreichen, ermöglicht Ihnen der Ruby-Filter die Durchführung komplexer Datentransformationen, die Implementierung benutzerdefinierter Geschäftslogik oder die Integration mit externen Systemen.</p><p>In diesem Blogbeitrag zeigen wir Ihnen, wie Sie Ruby-Filter verwenden – von den Grundlagen bis hin zu fortgeschrittenen Anwendungsfällen.</p><h2>Wann sollte man den Ruby-Filter verwenden?</h2><p>Als beratender Architekt bei Elastic sehe ich oft, dass Kunden Logstash für die Datenverarbeitungspipeline verwenden, obwohl es heutzutage keine hochmoderne Datenverarbeitungs-Engine mehr ist. Bei komplexen Datenmanipulationen oder benutzerdefinierter Logik stoßen sie häufig an die Grenzen von Standardfiltern. In diesen Fällen kann der Ruby-Filter dazu beitragen, diese Herausforderungen zu bewältigen.</p><p>Der Ruby-Filter ist nützlich, wenn die Standard-Logstash-Filter Ihre spezifischen Anforderungen nicht erfüllen können. Hier einige typische Anwendungsfälle:</p><ul><li><strong>Tief verschachtelte Datenmanipulation</strong>: Komplexe JSON-Strukturen, Arrays innerhalb von Arrays modifizieren oder Daten dynamisch anhand ihres Inhalts umstrukturieren.</li><li><strong>Erweiterte Stringverarbeitung</strong>: Strukturierte Daten aus unstrukturiertem Text analysieren und extrahieren</li><li><strong>Implementierung komplexer Geschäftslogik</strong>: Erstellen benutzerdefinierter Transformationen, die bedingte Logik, Schleifen oder komplexe Berechnungen erfordern.</li></ul><h2>Grundlegende Verwendung</h2><p>Beginnen wir mit einem einfachen Beispiel, um zu verstehen, wie der Ruby-Filter funktioniert.</p><h3>Konfigurieren des Ruby-Filters</h3><p>Wenn Sie eine Logstash-Pipeline erstellen, sollten Sie die Konfigurationsdatei im Verzeichnis <code>/etc/logstash/conf.d</code> ablegen. Alternativ können Sie die Option <code>-f</code> verwenden, um beim manuellen Starten von Logstash den Pfad zur Konfigurationsdatei anzugeben, sodass Sie problemlos mit Ihren Pipelines experimentieren können.</p><pre><code>$ ./bin/logstash -f /path/to/your_pipeline.conf</code></pre><p>Die Konfigurationsdatei sollte die Dateiendung <code>.conf</code> haben.</p><p>Um den Ruby-Filter zu verwenden, definieren Sie einen <code>ruby</code> -Filter im Filterabschnitt Ihrer Logstash-Pipeline-Konfigurationsdatei (*.conf). Hier ein einfaches Beispiel:</p><pre><code>filter {
  ruby {
    code => "
      event.set('new_field', 'Hello from Ruby!')
    "
  }
}</code></pre><p>Dieser Inline-Ruby-Filter definiert eine Ruby-Filterinstanz innerhalb Ihrer Logstash-Konfiguration. Der Parameter <code>code</code> stellt das Inline-Ruby-Skript bereit, das Logstash für jedes von diesem Filter verarbeitete Ereignis ausführt. Innerhalb dieses Skripts gibt es eine <code>event</code> Variable, die das Ereignis selbst repräsentiert. Das Ereignisobjekt enthält die Originaldaten, die an Logstash gesendet wurden, sowie alle zusätzlichen Felder, die während der Filterphasen von Logstash erstellt wurden. Sie können auf diese Felder über die Logstash Event API zugreifen, zum Beispiel über <code>event.get()</code> und <code>event.set()</code>. In diesem Beispielcode hat <code>event.set(&#x27;new_field&#x27;, &#x27;Hello from Ruby!&#x27;)</code> ein neues Feld mit dem Namen <code>new_field</code> auf den Zeichenkettenwert <code>Hello from Ruby!</code> gesetzt. Sie können bei Bedarf weiteren Code in diesen <code>code</code> -Block einfügen.</p><p>Beachten Sie, dass dieses <code>event</code> -Objekt kein gewöhnliches Ruby-Hash-Objekt ist, obwohl es als Datencontainer vom Typ Schlüssel-Wert fungiert. In <a href="https://www.elastic.co/docs/reference/logstash/event-api">dieser offiziellen Dokumentation</a> erfahren Sie mehr über die Event-API.</p><h3>Ruby-Skript auslagern</h3><p>Für einfache Transformationen ist Inline-Ruby-Code praktisch. Für komplexe Logik oder wiederverwendbare Funktionen empfiehlt es sich jedoch, den Code in ein externes Ruby-Skript auszulagern. Dies verbessert die Wartbarkeit und sorgt für eine saubere Logstash-Pipeline-Konfiguration.</p><p>Zuerst erstellen Sie ein Ruby-Skript und speichern es unter dem Namen <code>my_ruby_script.rb</code>. Das Skript muss eine <code>filter</code> Methode definieren, die das Ereignis verarbeitet. Es benötigt ein Ereignisobjekt als Argument, das das aktuell verarbeitete Ereignis repräsentiert. Die Methode <code>filter</code> muss ein Array von Ereignissen zurückgeben, die ausgelöst werden sollen. Um das Ereignis zu verwerfen, geben Sie ein leeres Array zurück.</p><p>Das folgende Skript liest beispielsweise das Feld <code>message</code> , berechnet seine Länge und speichert das Ergebnis in einem neuen Feld namens <code>message_length</code>.</p><pre><code>def register(params)
  # This method is called when the plugin is loaded.
  # You can use it to initialize any instance variables or perform setup tasks.
end

def filter(event)
  message = event.get('message')

  if message
    event.set('message_length', message.length)
  end

  return [event]
end</code></pre><p>Als Nächstes muss die Ruby-Filterkonfiguration so eingestellt werden, dass sie mit der Option <code>path</code> auf das Skript verweist. Dies weist Logstash an, das externe Skript zu laden und auszuführen. Bei der Verwendung externer Skripte muss sichergestellt werden, dass die Datei existiert und über die korrekten Berechtigungen verfügt.</p><pre><code>filter {
  ruby {
    path => "/path/to/my_ruby_script.rb"
  }
}</code></pre><p>Nun wird jedes Ereignis an die Methode <code>filter</code> in <code>my_ruby_script.rb</code> übergeben und von dieser verarbeitet.</p><p>Dieser Ansatz hilft Ihnen, komplexe Logik effektiver zu handhaben und erleichtert so das Testen, Debuggen und Wiederverwenden Ihres Ruby-Codes.</p><h2>Erweiterte Nutzung</h2><p>In diesem Abschnitt werden wir einige fortgeschrittene Beispiele für die Verwendung des Ruby-Filters in Logstash untersuchen. Anhand dieser Beispiele wird demonstriert, wie man Datentransformationen durchführt, Ereignisse anreichert und benutzerdefinierte Logik mit Ruby implementiert.</p><h3>Manipulation verschachtelter Datenstrukturen</h3><p>Ein Logstash-Ereignis ist die zentrale Datenstruktur, die von Logstash verarbeitet wird. Es kann verschiedene Felder enthalten, darunter auch verschachtelte Datenstrukturen wie Arrays und Hashes. Der Ruby-Filter ermöglicht Ihnen die einfache Bearbeitung dieser verschachtelten Strukturen.</p><p>Der Ruby-Filter kann verschachtelte Datenstrukturen wie Hashes und Arrays verarbeiten und ermöglicht es Ihnen, Felder innerhalb dieser Strukturen zu ändern oder hinzuzufügen. Dies ist hilfreich beim Umgang mit komplexen Datenformaten wie JSON.</p><pre><code>input {
  generator {
    lines => [
      '{"nested": {"key1": "value1", "key2": "value2"}}'
    ]
    count => 1
    codec => "json"
    ecs_compatibility => "disabled"
  }
}

filter {
  ruby {
    code => "
      nested_data = event.get('nested')

      if nested_data.is_a?(Hash)
        nested_data['key3'] = 'value3'
        event.set('nested', nested_data)
      end
    "
  }
}

output {
  stdout { codec => rubydebug }
}</code></pre><p>Dieses Beispiel enthält ein verschachteltes JSON-Objekt in den Eingabedaten. Der Ruby-Filter modifiziert die verschachtelten Daten, indem er ein neues Schlüssel-Wert-Paar hinzufügt. Diese Art der Manipulation von verschachtelten Daten ist mit den Standard-Logstash-Filtern nicht möglich, wodurch der Ruby-Filter eine praktische Option für komplexe Datenstrukturen darstellt.</p><h3>Teile ein einzelnes Ereignis in mehrere Ereignisse auf</h3><p>Ruby-Filter können auch verwendet werden, um ein einzelnes Ereignis in mehrere Ereignisse aufzuteilen. Dies ist nützlich, wenn Sie ein einzelnes Ereignis haben, das ein Array von Elementen enthält, und Sie für jedes Element ein separates Ereignis erstellen möchten.</p><p>Beachten Sie, dass weder die Ingest-Pipeline von Elasticsearch noch die Prozessoren von Beats/Elastic Agent das Aufteilen von Ereignissen unterstützen. Dies ist einer der überzeugendsten Anwendungsfälle für Logstash.</p><h4>Mit geteiltem Filter</h4><p>Mit dem Filter <code>split</code> können Sie ein Ereignis anhand eines bestimmten Feldes in mehrere Ereignisse aufteilen. Wenn Sie jedoch während der Aufteilung zusätzliche Transformationen oder Logik durchführen müssen, können Sie den Ruby-Filter in Kombination mit dem Split-Filter verwenden.</p><p>Im folgenden Beispiel haben wir einen RSS-Feed als einzeiligen XML-Text. Es enthält mehrere <code>&lt;item&gt;</code> -Elemente. Der Ruby-Filter dient dazu, die <code>&lt;item&gt;</code> -Elemente aus dem XML zu extrahieren und in einem neuen Feld namens <code>items</code> zu speichern. Anschließend wird der Split-Filter verwendet, um das Ereignis anhand des Feldes <code>items</code> in mehrere Ereignisse aufzuteilen.</p><pre><code>input {
  generator {
    lines => [
      '<rss version="2.0"><channel><title>Sample RSS</title><item><title>Article 1</title><link>http://example.com/1</link><description>Desc 1</description></item><item><title>Article 2</title><link>http://example.com/2</link><description>Desc 2</description></item></channel></rss>'
    ]
    count => 1
    codec => "plain"
    ecs_compatibility => "disabled"
  }
}

filter {
  xml {
    source => "message"
    target => "rss"
    store_xml => true
    force_array => false
  }
  ruby {
    code => "event.set('items', event.get('[rss][channel][item]')) if event.get('[rss][channel][item]')"
  }
  split {
    field => "items"
  }
  ruby {
    code => "
      item = event.get('items')
      event.set('title', item['title']) if item['title']
      event.set('link', item['link']) if item['link']
      event.set('description', item['description']) if item['description']
    "
  }
  mutate {
    remove_field => ["@timestamp", "@version", "sequence", "host", "event", "message", "rss", "items"]
  }
}

output {
  stdout { codec => rubydebug }
}</code></pre><p>Die Ausgabe lautet wie folgt:</p><pre><code>{
          "title" => "Article 1",
           "link" => "http://example.com/1",
    "description" => "Desc 1"
}
{
          "title" => "Article 2",
           "link" => "http://example.com/2",
    "description" => "Desc 2"
}</code></pre><p>Wie Sie vielleicht bemerkt haben, ist der <code>ruby</code> -Filter in diesem Fall nicht unbedingt erforderlich. Mit dem Filter <code>split</code> kann das Ereignis anhand des Feldes <code>items</code> in mehrere Ereignisse aufgeteilt werden, und mit dem Filter <code>mutate</code> können unnötige Felder entfernt werden. Wenn Sie jedoch während der Aufteilung zusätzliche Transformationen oder Logik durchführen müssen, können Sie den Ruby-Filter verwenden.</p><h4>Verwenden Sie das Inline-Ruby-Skript.</h4><p>Sie können auch ein Inline-Ruby-Skript verwenden, um ein einzelnes Ereignis in mehrere Ereignisse aufzuteilen, indem Sie die Methode <code>event.clone</code> und die Methode <code>new_event_block variable</code> verwenden, z. B. <code>new_event_block.call(new_event)</code>. Dies ermöglicht es Ihnen, neue Ereignisse auf Basis des ursprünglichen Ereignisses zu erstellen und dabei dessen Daten zu erhalten.</p><p>Hier ist ein Beispiel, wie man den Ruby-Filter verwendet, um ein einzelnes Ereignis in mehrere Ereignisse aufzuteilen. Eingabe und Ausgabe sind die gleichen wie im vorherigen Beispiel.</p><pre><code>filter {
  xml {
    source => "message"
    target => "rss"
    store_xml => true
    force_array => false
  }
  ruby {
    code => "
      items = event.get('[rss][channel][item]')
      if items.is_a?(Array)
        items.each do |item|
          new_event = event.clone
          new_event.set('title', item['title'])
          new_event.set('link', item['link'])
          new_event.set('description', item['description'])
          new_event_block.call new_event
        end
        event.cancel
      elsif items.is_a?(Hash)
        event.set('title', items['title'])
        event.set('link', items['link'])
        event.set('description', items['description'])
      end
    "
  }
  mutate {
    remove_field => ["@timestamp", "@version", "sequence", "host", "event", "message", "rss", "items"]
  }
}</code></pre><h4>Externes Ruby-Skript verwenden</h4><p>Alternativ können Sie ein externes Ruby-Skript verwenden, um ein einzelnes Ereignis in mehrere Ereignisse aufzuteilen.</p><p>Konfigurationsdatei:</p><pre><code>filter {
  xml {
    source => "message"
    target => "rss"
    store_xml => true
    force_array => false
  }
  ruby {
    path => "path/to/ruby/split_event.rb"
  }
  mutate {
    remove_field => ["@timestamp", "@version", "sequence", "host", "event", "message", "rss", "items"]
  }
}</code></pre><p>Das Ruby-Skript muss als <code>split_event.rb</code> externalisiert werden:</p><pre><code>def filter(event)
  items = event.get('[rss][channel][item]')
  events = []
  if items.is_a?(Array)
    items.each do |item|
      new_event = event.clone
      new_event.set('title', item['title'])
      new_event.set('link', item['link'])
      new_event.set('description', item['description'])
      events << new_event
    end
    return events
  elsif items.is_a?(Hash)
    event.set('title', items['title'])
    event.set('link', items['link'])
    event.set('description', items['description'])
    return [event]
  else
    return []
  end
end</code></pre><p>Denken Sie daran, dass die Methode <code>filter</code> ein Array von Ereignissen zurückgeben muss. Sie können mehrere Ereignisse zurückgeben, indem Sie ein eingehendes Ereignisobjekt klonen und die Ergebnisse dem Array hinzufügen, oder Sie können ein einzelnes Ereignis als Array mit einem Element zurückgeben.</p><pre><code>return events
# or
# return [event]</code></pre><p>Dies ermöglicht es Ihnen, ein einzelnes Ereignis in mehrere Ereignisse aufzuteilen.</p><h3>Führe externe Befehle aus und analysiere deren Ausgabe.</h3><p>Das Logstash exec input-Plugin ermöglicht die Ausführung externer Befehle, deren Ausgabe ein Logstash-Ereignis darstellt. Die Ausgabe des Befehls wird im Feld <code>message</code> des Ereignisses gespeichert.</p><p>Normalerweise sind die Ausgaben von Systembefehlen für Menschen lesbar, aber nicht als JSON oder in anderen Formaten strukturiert, die Logstash problemlos parsen kann. Um dies zu handhaben, können Sie den Ruby-Filter verwenden, um die Ausgabe zu analysieren und die Informationen daraus zu extrahieren.</p><p>Hier ist ein Beispiel für die Verwendung des <code>exec</code> -Input-Plugins zur Ausführung des <code>ps -ef</code> -Befehls, der alle laufenden Prozesse auf einem Unix-ähnlichen System auflistet. Die Ausgabe wird vom Ruby-Filter analysiert, um relevante Informationen über jeden Prozess zu extrahieren.</p><pre><code>input {
  exec {
    command => "ps -ef"
    interval => 60
  }
}

filter {
  ruby {
    code => '
      processes = []
      lines = event.get("message").split("\n")  
      lines.each_with_index do |line, index|
        # Skip header line and empty lines
        next if index == 0 || line.strip.empty?
        entry = nil
        
        # Use regex to match the ps -ef output format more flexibly
        # This pattern accounts for variable spacing and different time formats
        if line =~ /^\s*(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(\S+)\s+([\d:]+\.?\d*)\s+(.+)$/
          uid, pid, ppid, c, stime, tty, time, cmd = $1, $2, $3, $4, $5, $6, $7, $8
          
          entry = {
            "UID" => uid,
            "PID" => pid,
            "PPID" => ppid,
            "C" => c,
            "STIME" => stime,
            "TTY" => tty,
            "TIME" => time,
            "CMD" => cmd.strip
          }
        elsif line =~ /^\s*(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(.+)$/
          # Fallback pattern for lines that might not match the exact format
          # Split the remaining part more carefully
          uid, pid, ppid, c, remainder = $1, $2, $3, $4, $5
          
          # Split remainder into STIME, TTY, TIME, CMD
          parts = remainder.strip.split(/\s+/, 4)
          if parts.length >= 4
            stime, tty, time, cmd = parts[0], parts[1], parts[2], parts[3]
            
            entry = {
              "UID" => uid,
              "PID" => pid,
              "PPID" => ppid,
              "C" => c,
              "STIME" => stime,
              "TTY" => tty,
              "TIME" => time,
              "CMD" => cmd
            }
          end
        end
        if entry && entry["UID"] == "0"
          original_line = line.strip
          entry["original_line"] = original_line if original_line.length > 0
          processes.push(entry)
        end
      end
      event.set("processes", processes)
      event.remove("message")
      event.remove("event")
    '
  }
}

output {
  stdout { codec => rubydebug }
}</code></pre><p>Dieses Beispiel verwendet das <code>exec</code> -Input-Plugin, um den <code>ps -ef</code> -Befehl alle 60 Sekunden auszuführen. Der Ruby-Filter verarbeitet die Ausgabe und extrahiert relevante Felder wie UID, PID, PPID, CPU-Auslastung (C), Startzeit (STIME), TTY, Gesamt-CPU-Zeit (TIME) und den ausgeführten Befehl (CMD). Auf meiner macOS-Umgebung funktioniert es einwandfrei, aber möglicherweise müssen Sie die Regex-Muster an das Ausgabeformat des Befehls <code>ps -ef</code> auf Ihrem System anpassen.</p><h3>Verwenden Sie integrierte Bibliotheken</h3><p>Das Ruby-Filter-Plugin ermöglicht die Verwendung integrierter Ruby-Bibliotheken, was für verschiedene Aufgaben sehr nützlich sein kann. Beispielsweise können Sie die <code>json</code> -Bibliothek zum Parsen von JSON-Zeichenketten oder die <code>date</code> -Bibliothek zum Bearbeiten von Datumsangaben verwenden.</p><p>Hier ist ein Beispiel für die Verwendung der <code>json</code> -Bibliothek zum Parsen einer in einem Feld gespeicherten JSON-Zeichenkette:</p><pre><code>require 'json'

def filter(event)
  json_string = event.get('message')
  parsed_json = JSON.parse(json_string)
  event.set('parsed_json', parsed_json)
  return [event]
end</code></pre><p>Um zu vermeiden, dass die Bibliothek jedes Mal neu geladen werden muss, sollten Sie Ihren Ruby-Code auslagern, damit Sie die <code>require</code> -Anweisung am Anfang Ihres Ruby-Filterskripts verwenden können. Dadurch wird die Bibliothek einmalig geladen und steht Ihnen anschließend in Ihrem Skript zur Verfügung.</p><p>Um zu überprüfen, welche Bibliotheken in Ihrer Umgebung verfügbar sind, können Sie die integrierten Bibliotheken auflisten, indem Sie den folgenden Code im Ruby-Filter ausführen:</p><pre><code>Gem.loaded_specs.sort_by { |name, _| name }.each do |name, spec|
  puts "#{name}: #{spec.version}"
end</code></pre><p><strong>Hinweis: </strong>Die integrierten Bibliotheken werden von Logstash nicht offiziell unterstützt, und ihr Verhalten kann sich ändern oder sie sind in zukünftigen Versionen möglicherweise nicht mehr verfügbar. Die Benutzung erfolgt auf eigene Gefahr.</p><h2>Fazit</h2><p>Mit dem Logstash Ruby-Filter können Sie die Funktionen Ihrer Logstash-Pipelines anpassen und erweitern. In diesem Beitrag haben wir die Grundlagen der Verwendung des Ruby-Filters behandelt und fortgeschrittene Anwendungsbeispiele vorgestellt.</p><p>Durch die Nutzung des Ruby-Filters können Sie komplexe Datenverarbeitungsaufgaben bewältigen, die benutzerdefinierte Logik oder fortgeschrittene Manipulationen erfordern. Egal ob Sie mit verschachtelten Datenstrukturen arbeiten, Ereignisse aufteilen oder komplexen/unstrukturierten Text in strukturiertes JSON parsen und konvertieren, der Ruby-Filter bietet die Flexibilität, Ihre spezifischen Anforderungen zu erfüllen.</p><p>Wir hoffen, dass Ihnen dieser Leitfaden das Wissen und die Inspiration vermittelt hat, das volle Potenzial des Logstash Ruby-Filters auszuschöpfen. Viel Spaß beim Skripten!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Bereitstellung von Elasticsearch auf GCP GKE Autopilot]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie einen Elasticsearch-Cluster auf der Google Cloud Platform mit GKE Autopilot und ECK für eine teilweise verwaltete Elasticsearch-Konfiguration bereitstellen können.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/eck-gke-autopilot</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/eck-gke-autopilot</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Eduard Martin]]></dc:creator>
            <pubDate>Thu, 19 Jun 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d9c462fe63011356671479ccfedd435eec1ede52-1200x628.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>In diesem Artikel lernen wir, wie Elasticsearch mithilfe von Autopilot auf Google Cloud Kubernetes (GKE) bereitgestellt wird.</p><p>Für Elasticsearch werden wir <a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-on-k8s">Elastic Cloud on Kubernetes </a>(ECK) verwenden, den offiziellen Elasticsearch Kubernetes-Operator, der die Orchestrierung von Kubernetes-Deployments aller Elastic Stack-Komponenten vereinfacht.</p><p>Um mehr darüber zu erfahren, wie man Elasticsearch-Cluster auf verschiedenen Google-Cloud-Platform-Infrastrukturen bereitstellt, können Sie unsere Einstiegsartikel für <a href="https://www.elastic.co/search-labs/blog/elasticsearch-gpc-google-compute-engine">Google Cloud Compute</a> und <a href="https://www.elastic.co/search-labs/blog/deploy-elastic-gcp-marketplace">Google Cloud Marketplace</a> lesen.</p><h2>Aufwand für die Bereitstellung von Elasticsearch</h2><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/56b54d718dcff9af9050873c41fdf738074851da-1428x582.png" style="width: 100%;" /><h3>Was ist GKE Autopilot?</h3><p><a href="https://cloud.google.com/kubernetes-engine/docs/concepts/autopilot-overview?hl=es-419"><strong>Google Kubernetes Engine (GKE) Autopilot</strong></a> bietet ein vollständig verwaltetes Kubernetes-Erlebnis, bei dem Google die Clusterkonfiguration, das Node-Management, die Security und das Skalieren übernimmt, während Entwickler sich auf die Bereitstellung von Anwendungen konzentrieren, sodass Teams mit integrierten Best Practices in wenigen Minuten vom Code zur Produktion gelangen können.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/bbf6de63fa0a199326352f521cb22654818799f6-1600x958.png" style="width: 100%;" /><h2>Wann sollte man ECK in Google Cloud nutzen?</h2><p>Elastic Cloud on Kubernetes (ECK) eignet sich besonders für Unternehmen mit einer bestehenden Kubernetes-Infrastruktur, die Elasticsearch mit erweiterten Funktionen wie dedizierten Knotenrollen, hoher Verfügbarkeit und Automatisierung bereitstellen möchten.</p><h2>Wie richtet man ECK in Google Cloud ein?</h2><p>1. Melden Sie sich in der <a href="https://console.cloud.google.com">Google Cloud Console</a> an.</p><p>2. Klicken Sie <strong>oben rechts  </strong>auf die <strong>Cloud-Shell-Taste</strong>, um auf die Konsole zuzugreifen, und stellen Sie von dort aus den GKE-Cluster bereit. Alternativ können Sie auch <a href="https://cloud.google.com/cli">gcloud CLI</a> verwenden.</p><p><strong><em>Denken Sie daran, die Projekt-ID während des Tutorials durch Ihre eigene zu ersetzen.</em></strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/999b03861d4fe44f360ab4c7e2616e1dc10cf182-1558x1248.png" style="width: 100%;" /><p>3. Aktivieren Sie die <a href="https://console.cloud.google.com/flows/enableapi?apiid=container.googleapis.com">Google Kubernetes Engine API</a>.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/03827d3dc0e987c019e7747d33e7c01920047beb-911x246.png" style="width: 100%;" /><p>Klicken Sie auf <strong><em>Next (Weiter).</em></strong></p><p>Nun sollte die Kubernetes Engine API als aktiviert angezeigt werden, wenn nach der Kubernetes Engine API gesucht wird.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d5cd46f0333086bcb31b80cf9c08a469b449ec0f-640x250.png" style="width: 100%;" /><p>4. In der Cloud Shell einen Autopilot-Cluster erstellen. Wir werden es „autopilot-cluster-1“ benennen und außerdem „autopilot-test“ durch die ID Ihres Projekts ersetzen.</p><pre><code>gcloud beta container --project "autopilot-test-457216" clusters create-auto "autopilot-cluster-1" --region "us-central1" --release-channel "regular" --tier "standard" --enable-ip-access --no-enable-google-cloud-access --network "projects/autopilot-test-457216/global/networks/default" --subnetwork "projects/autopilot-test-457216/regions/us-central1/subnetworks/default" --cluster-ipv4-cidr "/17" --binauthz-evaluation-mode=DISABLED</code></pre><p>5. Warten Sie, bis es bereit ist. Die Erstellung dauert etwa 10 Minuten.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/81fbc45ba56d0f16ba42724cb8ae45e60b327dbc-1581x258.png" style="width: 100%;" /><p>Nach der korrekten Einrichtung des Clusters wird eine Bestätigungsmeldung angezeigt.</p><p>6. Konfigurieren Sie den Zugriff auf die „kubectl“-Befehlszeile.</p><pre><code>gcloud container clusters get-credentials autopilot-cluster-1 --region us-central1 --project autopilot-test-457216</code></pre><p>Sie sollten sehen:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b0beba1ee00ce9029f586ee32693fc2aa58c7f65-3442x142.png" style="width: 100%;" /><p><em>Für autopilot-cluster-1 wurde ein „kubeconfig“-Eintrag generiert.</em></p><p>7. Installieren Sie den <a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-on-k8s">Elastic Cloud on Kubernetes</a> (ECK)-Operator.</p><pre><code># Install ECK Custom Resource Definitions
kubectl create -f https://download.elastic.co/downloads/eck/2.16.1/crds.yaml

# Install the ECK operator
kubectl apply -f https://download.elastic.co/downloads/eck/2.16.1/operator.yaml</code></pre><p>8. Lassen Sie uns eine Elasticsearch-Instanz mit einem einzelnen Knoten und den Standardwerten erstellen.</p><p>Wenn Sie einige Rezepte für verschiedene Setups prüfen möchten, können Sie <a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-on-k8s/recipes">diesen Link</a> besuchen.</p><p>Bitte beachten Sie, dass ECK, wenn Sie kein <code>storageClass</code> angeben, den standardmäßig festgelegten Wert verwendet, der für GKE <code>standard-rwo</code> ist und den <a href="https://cloud.google.com/kubernetes-engine/docs/how-to/persistent-volumes/gce-pd-csi-driver?cloudshell=true">Compute Engine Persistent Disk CSI Driver</a> nutzt, und damit ein 1-GB-Volume erstellt.</p><pre><code>cat <<EOF | kubectl apply -f -
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
spec:
  version: 9.0.0
  nodeSets:
  - name: default
    count: 1
    config:
      node.store.allow_mmap: false
EOF</code></pre><p>Wir haben <code>nmap</code> deaktiviert, da die Standard-GKE-Maschine einen zu niedrigen Wert für <code>vm.max_map_count</code> hat. Für den Produktivbetrieb wird von einer Deaktivierung abgeraten; stattdessen sollte der Wert von <code>vm.max_map_count</code> erhöht werden. Mehr darüber, wie Sie das machen, können Sie <a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-on-k8s/virtual-memory">hier</a> lesen.</p><p>9. Lassen Sie uns außerdem einen Kibana-Cluster mit einem einzelnen Knoten bereitstellen. Für Kibana fügen wir einen LoadBalancer hinzu, der uns eine externe IP-Adresse bereitstellt, über die wir Kibana von unserem Gerät aus erreichen können.</p><pre><code>cat <<EOF | kubectl apply -f -
apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
  name: quickstart
spec:
  version: 9.0.0
  http:
    service:
      metadata:
        annotations:
          cloud.google.com/l4-rbs: "enabled"
      spec:
        type: LoadBalancer
  count: 1
  elasticsearchRef:
    name: quickstart
EOF</code></pre><p>Beachten Sie die Anmerkung: </p><p><code>cloud.google.com/l4-rbs: &quot;enabled&quot;</code></p><p><strong><em>Das ist sehr wichtig, weil es Autopilot anweist, einen öffentlich zugänglichen LoadBalancer bereitzustellen. Falls nicht festgelegt, wird der LoadBalancer intern verwendet.</em></strong></p><p>10. Prüfen Sie, ob Ihre Pods laufen</p><pre><code>kubectl get pods</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/99da1270581a137683770efdb9c6e1577ec9fc01-3150x442.png" style="width: 100%;" /><p>11. Sie können auch <code>run kubectl get elasticsearch</code> und <code>kubectl get kibana</code> für spezifischere Statistiken wie Elasticsearch-Version, Nodes und Health verwenden.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/86234f307970fd5f78b8acd41496e8cc89ff82d3-3414x326.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/29160286ccc88928734c8ea11b1923db8e85d49d-3142x318.png" style="width: 100%;" /><p>12. Greifen Sie auf Ihre Dienste zu.</p><pre><code>kubectl get svc</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ed1a07314b84a99b4aa1fec3db4b9badeb9587ee-3446x610.png" style="width: 100%;" /><p>Hier wird Ihnen die externe URL für Kibana unter EXTERNAL-IP angezeigt. Es kann einige Minuten dauern, bis der LoadBalancer bereitgestellt wird. <strong><em>Kopieren Sie den Wert von EXTERNAL-IP.</em></strong></p><p>13. Ermitteln Sie das Elasticsearch-Passwort für den Nutzer „elastic“:</p><pre><code>kubectl get secret quickstart-es-elastic-user -o=jsonpath='{.data.elastic}' | base64 --decode</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ab5054219216ebc15fc0d96e27605aaf13b720c6-3448x210.png" style="width: 100%;" /><p>14. <strong>Greifen Sie über Ihren Browser auf Kibana zu:</strong></p><ul><li>URL: https://&lt;EXTERNAL_IP&gt;:5601</li><li>Benutzername:elastic</li><li>Passwort: 28Pao50lr2GpyguX470L2uj5 (aus dem vorherigen Schritt)</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/47cbe88dc14db64db3a256f3f7504cc86a843475-463x503.png" style="width: 100%;" /><p>15. Wenn Sie von Ihrem Browser aus zugreifen, wird der Willkommensbildschirm angezeigt.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a75c151c0144b7efe2b730698c0ed0156fa9b16a-1600x1005.png" style="width: 100%;" /><p>Wenn Sie die Spezifikationen des Elasticsearch-Clusters ändern möchten, z. B. Knoten ändern oder die Größe anpassen, können Sie das yml-Manifest mit den neuen Einstellungen erneut anwenden:</p><pre><code>cat <<EOF | kubectl apply -f -
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
spec:
  version: 9.0.0
  nodeSets:
    - name: default
      count: 2
      config:
        node.store.allow_mmap: false
      podTemplate:
        spec:
          containers:
            - name: elasticsearch
              resources:
                requests:
                  memory: 1.5Gi
                  cpu: 2
                limits:
                  memory: 1.5Gi
                  cpu: 2
EOF</code></pre><p>In diesem Beispiel fügen wir einen weiteren Node hinzu und ändern RAM sowie CPU. Wie Sie sehen, zeigt <code>kubectl get elasticsearch</code> jetzt 2 Knoten:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/48b475622cc48890bff8105d151f2cbde28d7021-3418x298.png" style="width: 100%;" /><p>Das Gleiche gilt für Kibana:</p><pre><code>cat <<EOF | kubectl apply -f -
apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
  name: quickstart
spec:
  version: 9.0.0
  http:
    service:
      metadata:
        annotations:
          cloud.google.com/l4-rbs: "enabled"
      spec:
        type: LoadBalancer
  count: 1
  elasticsearchRef:
    name: quickstart
  podTemplate:
    spec:
      containers:
        - name: kibana
          env:
            - name: NODE_OPTIONS
              value: "--max-old-space-size=1024"
          resources:
            requests:
              memory: 0.5Gi
              cpu: 0.5
            limits:
              memory: 1Gi
              cpu: 1
EOF</code></pre><p>Wir können die CPU/RAM des Containers sowie die Speichernutzung von <a href="https://nodejs.org/">Node.js </a>(<a href="https://nodejs.org/api/cli.html#--max-old-space-sizesize-in-mib">max-old-space-size</a>) anpassen.</p><p>Denken Sie daran, dass <a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-on-k8s/volume-claim-templates">bestehende Volumenbehauptungen nicht verkleinert werden können</a>. Nach der Installation des Updates wird der Betreiber die Änderungen mit minimaler Ausfallzeit vornehmen.</p><p>Denken Sie daran, den Cluster nach dem Testen zu löschen, um unnötige Kosten zu vermeiden.</p><pre><code>gcloud container clusters delete autopilot-cluster-1</code></pre><h2>Wie geht es weiter?</h2><p>Wenn Sie mehr über Kubernetes und die Google Kubernetes Engine erfahren möchten, lesen Sie diese Artikel:</p><ul><li><a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-on-k8s">Elastic Cloud auf Kubernetes | Elastic Docs</a></li><li><a href="https://cloud.google.com/blog/products/containers-kubernetes/introducing-gke-autopilot">Einführung von GKE Autopilot | Google Cloud Blog</a></li><li><a href="https://cloud.google.com/kubernetes-engine/docs/concepts/autopilot-overview">Autopilot-Überblick | Google Kubernetes Engine (GKE)</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Verwenden von Azure LLM-Funktionen mit Elasticsearch für intelligentere Abfrageerlebnisse]]></title>
            <description><![CDATA[Entdecken Sie eine Beispiel-Immobilien-Such-App, die Azure Gen AI LLM Functions mit Elasticsearch nutzt, um flexible hybride Suchergebnisse zu liefern. Eine Schritt-für-Schritt-Anleitung zur Konfiguration und Ausführung der Beispiel-App in GitHub Codespaces finden Sie hier.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/azure-llm-functions-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/azure-llm-functions-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jonathan Simon]]></dc:creator>
            <pubDate>Fri, 13 Jun 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1431b90c7e00de06574c1e33c44a2e89296c824e-1200x628.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Präzision. Wenn es darauf ankommt, ist es sehr wichtig. Wenn Sie nach etwas Bestimmtem suchen, ist Präzision äußerst wichtig. Manchmal liefert eine zu präzise Abfrage jedoch keine Ergebnisse. Daher ist es von Vorteil, den Umfang einer Abfrage flexibel erweitern zu können, um zusätzliche potenziell relevante Daten zu finden.</p><p>In diesem Blogbeitrag wird die Verwendung von Elasticsearch und Azure Open AI zum Erstellen einer Beispiel-App beschrieben, die zeigt, wie bei der Suche nach sehr spezifischen Immobilienobjekten genaue Ergebnisse erzielt werden und gleichzeitig relevante Ergebnisse bereitgestellt werden, wenn keine spezifische Übereinstimmung verfügbar ist. Wir gehen alle erforderlichen Schritte durch, um einen Elasticsearch-Index zusammen mit einer Suchvorlage zu erstellen. Anschließend erläutern wir den gesamten Prozess der Erstellung einer App, die Azure OpenAI verwendet, um Benutzerabfragen entgegenzunehmen und in Elasticsearch-Suchvorlagenabfragen umzuwandeln, die erstaunlich benutzerdefinierte Ergebnisse liefern können.</p><p>Hier ist eine Liste aller Ressourcen, die wir zum Erstellen unserer Beispiel-App zur Immobiliensuche verwenden werden:</p><ul><li>Elasticsearch-Index und Suchvorlage</li><li>Azure OpenAI</li><li>Azure Maps-API</li><li><a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb">Codespaces Jupyter Notebook</a></li><li>Semantischer Kernel</li><li>C#-App mit Blazor-Frontend</li></ul><h2>Intelligenter Abfrage-Workflow</h2><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0c4a7c835e06c514f158c00ab1055a7ba719a35f-1600x765.png" style="width: 100%;" /><p>Dieser Workflow kombiniert LLM, LLM-Tools und Suche, um Abfragen in natürlicher Sprache in strukturierte, relevante Suchergebnisse umzuwandeln:</p><ul><li><strong>LLM (Large Language Model)</strong> – Interpretiert komplexe Benutzerabfragen und orchestriert Tools, um die Suchabsicht zu extrahieren und den Kontext anzureichern.</li><li><strong>LLM-Tools</strong> – Jedes LLM-Tool ist ein C#-Programm, das wir für diesen Beitrag erstellt haben. Es gibt drei Werkzeuge:<ul><li><em>Tool zur Parameterextraktion</em>: zieht wichtige Attribute wie Schlafzimmer, Badezimmer, Ausstattung und Preis aus der Abfrage.</li><li><em>GeoCode-Tool</em>: Konvertiert Ortsnamen in Breiten-/Längengrade zur räumlichen Filterung.</li><li><em>Suchtool</em>: füllt eine Elasticsearch-Suchvorlage mit Abfrageparametern und führt eine Suche aus. <strong>Hybridsuche</strong> – Führt eine Hybridsuche (Volltext + dichter Vektor) mit integrierter ML-Inferenz aus. Dieser mehrschichtige Ansatz gewährleistet intelligentere, kontextbezogene Abfrageerlebnisse für den Endbenutzer.</li></ul></li></ul><h2>Anwendungsarchitektur</h2><p>Hier ist ein Systemarchitekturdiagramm der Beispiel-App. Wir werden ein Codespaces Jupyter Notebook verwenden, um mit Elastic Cloud zu interagieren.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2880afee184cd9270c0eb4310e51418e2339784d-936x452.png" style="width: 100%;" /><h2>Voraussetzungen</h2><div style="display:none">Unknown block type "block", specify a component for it in the `components.types` option</div><h2>Klonen Sie das Beispiel-App-Repository in Codespaces</h2><p>Beginnen Sie mit dem Klonen des Codes für die Beispielanwendung. Sie können dies in <a href="https://github.com/codespaces/">GitHub Codespaces</a> tun, das eine Möglichkeit zum Klonen und Ausführen von Anwendungen bietet. Klicken Sie auf <strong>„Neuer Codespace“.</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b89cbec491659b6c8a0bb9551ed2629f7a37f9fd-1600x427.png" style="width: 100%;" /><p>Wählen Sie dann das Repository <a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo">jwilliams-elastic/msbuild-intelligent-query-demo</a> im Dropdown-Menü <strong>„Repository“</strong> aus und klicken Sie auf <strong>„Codespace erstellen“</strong>.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/43ea377554527af9578400f16cd2342bf8fff3a2-1600x1049.png" style="width: 100%;" /><h2>Erstellen Sie .env Datei</h2><p>Wir verwenden ein Python Jupyter Notebook, um auf Elastic Cloud zuzugreifen und mit ihr zu interagieren. Dabei werden die Konfigurationseinstellungen verwendet, die in einer Konfigurationsdatei gespeichert sind. Die Konfigurationsdatei für das Notebook sollte den Dateinamen <strong><em>.env</em></strong> haben und Sie werden es jetzt erstellen.</p><ol><li>Klicken Sie in GitHub Codespaces auf die Schaltfläche <strong>„Neue Datei“</strong>und fügen Sie eine Datei mit dem Namen <strong><em>.env</em></strong>hinzu.</li><li>Fügen Sie der neu erstellten <strong><em>.env</em></strong> -Datei den folgenden Inhalt hinzu Datei</li></ol><pre><code>ELASTIC_URL=
ELASTIC_API_KEY=</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/84d4f327948858ba61db0001dd8cf780d42fe0a7-1600x875.gif" style="width: 100%;" /><p>Wie Sie sehen, fehlen uns einige Werte, <strong>ELASTIC_URL</strong> und <strong>ELASTIC_API_KEY,</strong> die der <em>.env</em> hinzugefügt werden müssen. Datei. Lassen Sie uns diese jetzt erhalten, indem wir ein serverloses Elasticsearch-Projekt erstellen, das als Backend dient und die Suchfunktion für unsere Beispiel-App bereitstellt.</p><h2>Erstellen eines Elastic Serverless-Projekts</h2><ol><li>Gehen Sie zu <a href="http://cloud.elastic.co">cloud.elastic.co</a> und klicken Sie auf <strong>„Neues serverloses Projekt erstellen“</strong></li><li>Klicken Sie auf <strong>„Weiter“</strong> , um zur <strong>Elasticsearch-</strong> Lösung zu gelangen.</li><li>Wählen Sie <strong>„Optimiert für Vektoren“</strong></li><li>Legen Sie <strong>Azure</strong> als <strong>Cloud-Anbieter fest</strong></li><li>Klicken Sie auf <strong>„Serverloses Projekt erstellen“</strong></li><li>Klicken Sie im Hauptnavigationsmenü auf <strong>Erste Schritte</strong> und scrollen Sie nach unten, um die <strong>Verbindungsdetails</strong>zu kopieren</li><li>Klicken Sie auf die Schaltfläche <strong>„Kopieren“</strong> , um den <strong>Elasticsearch-Endpunkt</strong> aus den <strong>Verbindungsdetails</strong>zu kopieren</li><li>Aktualisieren Sie <strong><em>.env</em></strong> Datei, um die <strong>ELASTIC_URL</strong> als kopierten <strong>Elasticsearch-Endpunkt</strong>festzulegen</li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/77a35e55446d396066b68cfd132d1543a07b81cc-1600x875.gif" style="width: 100%;" /><h2>Erstellen eines Elastic-API-Schlüssels</h2><ol><li>Öffnen Sie die Seite <strong>„Erste Schritte“ von</strong> <strong>Elasticsearch und klicken Sie im</strong> Abschnitt „API-Schlüssel hinzufügen“ auf <strong>„Neu“ .</strong></li><li>Geben Sie einen <strong>Schlüsselnamen</strong>ein</li><li>Klicken Sie auf<strong> API-Schlüssel erstellen</strong></li><li>Klicken Sie auf die Schaltfläche „Kopieren“, um den API-Schlüsselwert zu kopieren</li><li>Zurück in <strong>Codespaces,</strong> wo wir die <strong><em>.env</em></strong>haben<strong> </strong>Öffnen Sie die Datei zum Bearbeiten und fügen Sie den kopierten Wert ein, um den<strong> ELASTIC_API_KEY</strong>festzulegen.</li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/20168cba493d8e2c0d9ae7704eb0ae707df58e4c-1600x875.gif" style="width: 100%;" /><h2>Öffnen Sie das Codespaces-Notebook und installieren Sie die Bibliotheksabhängigkeiten</h2><p>Wählen Sie im Datei-Explorer die Datei <strong><em>VectorDBSetup.ipynb</em></strong> aus, um das Notebook zu öffnen. Suchen Sie nach dem Laden des Notebooks den Notebook <a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L40-L52">-Abschnitt mit dem Titel <strong>„Bibliotheken installieren“</strong></a><strong>. </strong>Klicken Sie auf die Schaltfläche „Abspielen“ des Abschnitts.</p><p>Wenn Sie zum ersten Mal ein Notebook in GitHub Codespaces ausführen, werden Sie aufgefordert, einen Codespaces-Kernel auszuwählen und die Python-Umgebung zu konfigurieren.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2272f70615dfb9dcbeb91f39b6dd5076213e24a5-1600x875.gif" style="width: 100%;" /><h2>Definieren Sie Importe und laden Sie Umgebungsvariablen mit Codespaces Notebook</h2><p>Gehen Sie zum nächsten Abschnitt im Notebook <a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L58-L104">mit dem Titel <strong>„Importe definieren und Umgebungsvariablen laden“</strong></a>. Klicken Sie auf die Schaltfläche „Abspielen“ des Abschnitts.</p><p>Dieser Code importiert die vom Notebook verwendeten Python-Bibliotheken und lädt die Umgebungsvariablen aus der <em>.env </em>die Sie zuvor erstellt haben.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/aa9f9198ff452ac0c4ce33b00f253731dbee22c5-1600x875.gif" style="width: 100%;" /><h2>Erstellen Sie einen Elastic ML-Inferenzendpunkt mit dem Codespaces-Notebook</h2><p>Gehen Sie zum nächsten Notebook <a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L112-L157">-Abschnitt mit dem Titel <strong>„ML-Inferenz-Endpunkt erstellen“</strong></a>. Klicken Sie auf die Schaltfläche „Abspielen“ des Abschnitts.</p><p>Dadurch wird ein neuer ML-Inferenz-Endpunkt im Elasticsearch-Projekt erstellt, den wir zum Generieren von Text-Embeddings aus unseren Daten verwenden. Text-Embeddings sind Vektordarstellungen von Text, die in Elasticsearch gespeichert werden, um die semantische Suche zu unterstützen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/97c1afab3e64027ee5ae77f377d56ba406ae1765-1600x875.gif" style="width: 100%;" /><h2>Erstellen Sie einen Elasticsearch-Index mit Codespaces Notebook</h2><p>Gehen Sie zum nächsten Notebook <a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L165-L224">-Abschnitt mit dem Titel <strong>„Elasticsearch-Index erstellen“</strong></a>. Klicken Sie auf die Schaltfläche „Abspielen“ des Abschnitts.</p><p>Dadurch wird der Elasticsearch-Index erstellt, in dem unsere Beispieldaten und alle zugehörigen Vektordaten gespeichert werden, die über den ML-Inferenz-Endpunkt generiert werden.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/23a66283ee41239e24fb8455c3cd95641982ca6b-1600x875.gif" style="width: 100%;" /><h2>Erstellen Sie eine Elasticsearch-Suchvorlage mit Codespaces Notebook</h2><p>Fahren Sie mit dem nächsten Notebook <a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L232-L384">-Abschnitt mit dem Titel <strong>„Suchvorlage“</strong></a> fort. Klicken Sie auf die Schaltfläche „Abspielen“ des Abschnitts.</p><p>Dadurch wird eine <a href="https://www.elastic.co/de/docs/solutions/search/search-templates">Suchvorlage</a> erstellt, die unsere Beispiel-App als Vorlage verwendet und mit den Wörtern gefüllt, die aus der Suchanfrage eines Benutzers analysiert wurden. Dadurch können wir konfigurieren und steuern, wie spezifisch wir beim Abfragen von Daten im Elasticsearch-Index sind.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4c001a3e4d1cca4cfb5c043fea92c7ccaf9cb64a-1600x875.gif" style="width: 100%;" /><h2>Daten mithilfe des Codespaces Notebooks in den Elasticsearch-Index aufnehmen</h2><p>Gehen Sie zum nächsten Abschnitt des Notebooks <a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L392-L450">mit dem Titel „ <strong>Eigenschaftsdaten aufnehmen“</strong></a>. Klicken Sie auf die Schaltfläche „Abschnitt ausführen“.</p><p>Durch Ausführen dieses Codeabschnitts werden die in der Datei <em>„properties.jsonl“</em>enthaltenen Beispieldaten in großen Mengen geladen. Nach einigen Minuten sollten Sie eine Bestätigung sehen, dass der Vorgang erfolgreich abgeschlossen wurde. Sie können bestätigen, dass der Index den erwarteten Datensatz enthält, indem Sie in Elastic Cloud zum Abschnitt <strong>Indexverwaltung</strong> gehen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e9698c798541ccfc08143a939846597028e3c566-1600x875.gif" style="width: 100%;" /><h2>Erstellen Sie appsetting.json, um die C#-App zu konfigurieren</h2><p>Nachdem unser Elasticsearch-Index erstellt und mit Daten gefüllt wurde, können wir nun die Beispiel-App für die Arbeit mit Elastic und Azure Cloud konfigurieren. Die C#-Beispiel-App verwendet eine Datei namens <em>appsettings.json</em> , um ihre Zugriffsinformationen wie API-Schlüssel zu speichern und zu laden. Sie erstellen die Datei <em>appsettings.json</em> jetzt mit dem Editor in Codespaces.</p><p>1. Erstellen Sie <em>appsettings.json</em> im <strong>HomeFinderApp </strong>-Ordner</p><p>2. Fügen Sie den folgenden Code in die Datei <em>appsettings.json</em> ein</p><pre><code>{
 "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": "*"
}
</code></pre><p>3. Suchen Sie im Abschnitt <strong>„ElasticSettings“</strong> <strong></strong> <strong>nach den URL- </strong>und API -Schlüsselwerten. Legen Sie sie auf die gleichen Werte fest, die Sie in der <em>.env-Datei</em> festgelegt haben Datei in einem früheren Schritt.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/00dad25bacdea2adcbd1e6eca7658867a49b0d8c-1600x875.gif" style="width: 100%;" /><h2>Erstellen des Azure OpenAI-Diensts</h2><p>Unsere Beispiel-App verwendet Azure OpenAI, um die Abfrage des App-Benutzers zu analysieren und dann eine Anfrage an Elasticsearch zu senden, indem sie die Suchvorlage ausfüllt, um zu versuchen, flexibel mitzuteilen, wonach der Benutzer sucht.</p><ol><li>Öffnen Sie einen neuen Browser-Tab und gehen Sie im Azure-Portal zu <a href="https://portal.azure.com/#blade/Microsoft_Azure_ProjectOxford/CognitiveServicesHub/OpenAI">AI Foundry | Azure OpenAI</a> . Klicken Sie auf +<strong>Erstellen</strong></li><li>Wählen Sie im Erstellungsformular eine <strong>Ressourcengruppe</strong> aus.</li><li>Geben Sie einen <strong>Namen</strong>ein</li><li>Wählen Sie eine <strong>Preisstufe</strong></li><li>Klicken Sie <strong>auf Weiter</strong></li><li>Klicken Sie auf der Registerkarte <strong>„Netzwerk“</strong> auf<strong> „Weiter“</strong></li><li>Klicken Sie auf der Registerkarte <strong>Tags</strong> <strong>auf Weiter</strong></li><li>Klicken Sie auf der Registerkarte <strong>„Überprüfen und senden“</strong> <strong>auf „Erstellen“</strong> .</li><li>Sobald die Erstellung abgeschlossen ist, klicken Sie auf <strong>„Zur Ressource gehen“.</strong></li><li>Wählen Sie im linken Navigationsmenü <strong>„Schlüssel und Endpunkt“</strong> aus.</li><li>Kopieren Sie den <strong>Endpunkt</strong> und fügen Sie ihn in die von Ihnen erstellte Datei <em>appsettings.json</em> in Ihrem Browser-Tab mit dem geöffneten Codespaces-Editor ein.</li><li>Kehren Sie dann zu Ihrem Browser-Tab zurück, der den Azure OpenAI <strong>-Schlüssel und die Endpunktseite</strong> enthält. Klicken Sie auf die Schaltfläche „Kopieren“ für <strong>Schlüssel 1</strong> und fügen Sie den kopierten Wert in die Datei <em>„appsettings.json“</em> ein, zurück in Ihrem Browser-Tab, in dem der Codespaces-Editor geöffnet ist.</li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6d49197302d110410dca0a53b6ae90237cf2dfd6-1600x875.gif" style="width: 100%;" /><h2>Fügen Sie dem Azure Open AI-Dienst die Bereitstellung des GPT-4O-Modells hinzu</h2><p>Großartig, wir haben jetzt einen laufenden Azure OpenAI-Dienst, aber es ist noch eine Modellbereitstellung erforderlich, um uns die LLM-Funktionen bereitzustellen, die unsere Beispiel-App benötigt. Es stehen unzählige Modelle zur Auswahl. Lassen Sie uns <em>gpt-4o</em> bereitstellen, da es bereits in der von Ihnen erstellten Datei <em>appsettings.json</em> angegeben ist.</p><p></p><ol><li>Gehen Sie zu <a href="https://ai.azure.com/resource/playground">Azure AI Foundry</a> und klicken Sie auf <strong>„Bereitstellung erstellen“</strong></li><li>Suchen Sie nach <em>gpt-4o</em> und wählen Sie es aus den Ergebnissen aus</li><li>Klicken Sie auf <strong>„Bestätigen“</strong> , um es auszuwählen</li><li>Klicken Sie auf <strong>„Bereitstellen“</strong> , um das Modell bereitzustellen</li></ol><p>Nachdem Sie das <em>gpt-4o-</em> Modell erfolgreich bereitgestellt haben, können Sie im linken Navigationsmenü „ <strong>Bereitstellungen</strong> “ auswählen und bestätigen, dass die <strong><em>gpt-4o-</em></strong> Bereitstellung mit dem <strong>Status</strong> <strong>„Erfolgreich“</strong> aufgeführt ist.<br/></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1b817ab67c05634e9c72777593b4d1a2c6c28191-1600x875.gif" style="width: 100%;" /><h2>Erstellen eines Azure Maps-Kontos</h2><p>Wir möchten, dass die Nutzer unserer Beispiel-App in bestimmten Gebieten nach Immobilien suchen können, ohne dabei zu spezifisch sein zu müssen. Wenn jemand nach einem Grundstück in der Nähe des örtlichen Bauernmarkts suchen möchte, ist Azure Maps ein Dienst, mit dem das OpenAI LLM die Breiten- und Längengradkoordinaten für den Markt abrufen kann. Die Koordinaten können dann in die auf Suchvorlagen basierenden Anfragen aufgenommen werden, die für Benutzerabfragen, die bestimmte Standorte und geografische Entfernungen berücksichtigen, an Elasticsearch gesendet werden.</p><ol><li><strong>Klicken</strong> Sie in <a href="https://portal.azure.com/#browse/Microsoft.Maps%2Faccounts">Azure Maps-Konten auf Erstellen</a></li><li>Auswählen einer <strong>Ressourcengruppe</strong></li><li>Geben Sie einen <strong>Namen</strong>ein</li><li>Stimmen Sie der Lizenz- und Datenschutzerklärung zu</li><li>Klicken Sie auf <strong>„Überprüfen und erstellen“</strong></li><li>Klicken Sie <strong>auf Erstellen</strong></li><li>Sobald die Kontoerstellung abgeschlossen ist, klicken Sie auf <strong>„Zur Ressource gehen“.</strong></li><li>Klicken Sie im linken Navigationsmenü auf <strong>„Authentifizierung“</strong></li><li>Kopieren Sie den <strong>Primärschlüsselwert</strong> und fügen Sie ihn als Wert des <strong>API-Schlüssels</strong> im Abschnitt <strong>„AzureMapsSettings“</strong> der Datei <em>„appsettings.json“</em> ein, zurück auf der Registerkarte Ihres Browsers mit dem Codespaces-Editor.</li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/90fadd48e366682e2bad91e32988f93c6354e126-1600x875.gif" style="width: 100%;" /><h2>Probieren Sie die Beispiel-App aus</h2><p>Jetzt kommt der spaßige Teil. Es ist Zeit, die Beispiel-App auszuführen. Wir verfügen über alle Konfigurationsdetails sowie die Elastic Cloud- und Azure Cloud-Ressourcen, die wir zum Betrieb der App benötigen.</p><p>1. Öffnen Sie ein Terminalfenster im Codespaces-Editor.</p><p>2. Verwenden Sie den folgenden Befehl, um das aktive Verzeichnis in den Beispiel-App-Ordner zu ändern.<br/></p><pre><code>cd HomeFinderApp</code></pre><p>3. Verwenden Sie den folgenden <em>Dotnet</em> -Befehl, um die App auszuführen.</p><pre><code>dotnet run</code></pre><p>4. Klicken Sie auf die Schaltfläche <strong>„Im Browser öffnen“,</strong>wenn sie angezeigt wird.</p><p>5. Testen Sie die Standardsuche und probieren Sie dann einige Ihrer eigenen benutzerdefinierten Suchen aus. Wenn Sie weitere Details dazu sehen möchten, was im Backend zur Generierung der Suchergebnisse ausgeführt wird, können Sie auf den Link <strong>„Anzeigen“</strong>neben <strong>„Toolaufrufe“ klicken.</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/821fe7b9446de5ed646d938cc9484a7ddad21030-1600x875.gif" style="width: 100%;" /><p><strong>Bonus: </strong>Wenn Sie GPT-4o wirklich testen möchten, versuchen Sie die folgende Suche: <em>Ich suche nach einer Immobilie in der Nähe von Disney World, Florida, mit über 30 Schlafzimmern und über 20 Badezimmern, einem Pool und einer Garage und in Strandnähe für weniger als 200.000. </em>Diese Abfrage gibt nach mehreren Aufrufen des Suchtools Ergebnisse zurück.</p><h2>Elastic ist Ihre Lösung für Search AI</h2><p>Die laufende App ist ein Beispiel für die geführte Suche von Gen AI LLM unter Verwendung von Elasticsearch über Suchvorlagen als grundlegende Datenquelle. Experimentieren Sie ruhig und passen Sie die Beispiel-App an, um ein präzises und dennoch flexibles Sucherlebnis zu schaffen, das Ihren Benutzern hilft, das zu finden, wonach sie suchen.</p><p>Vielen Dank fürs Lesen. Probieren Sie <a href="https://cloud.elastic.co/registration">Elastic Cloud</a> aus.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Der aktuelle Stand von MCP (Model Context Protocol)]]></title>
            <description><![CDATA[Informieren Sie sich über MCP, Projektaktualisierungen, Funktionen, Sicherheitsherausforderungen, neue Anwendungsfälle und wie Sie mit dem Elasticsearch MCP-Server von Elastic herumbasteln können.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/mcp-current-state</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/mcp-current-state</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[JD Armada]]></dc:creator>
            <pubDate>Thu, 12 Jun 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/02b8c8672ffa129e0ed91a92d6cab612a01d27f2-1200x628.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Ich habe vor Kurzem am <a href="https://mcpdevsummit.ai/">MCP Developers Summit</a> in San Francisco teilgenommen und es ist klar, dass das Model Context Protocol (MCP) schnell zu einem grundlegenden Baustein für KI-Agenten und kontextreiche KI-Anwendungen wird. Bei Elastic gehen wir diesen Weg, indem wir MCP-Server direkt aus <a href="https://www.elastic.co/de/elasticsearch/agent-builder">dem Agent Builder</a> heraus zugänglich machen und Elasticsearch so zu einem erstklassigen Kontext- und Werkzeuganbieter für jeden MCP-kompatiblen Agenten machen. In diesem Beitrag werde ich die wichtigsten Neuerungen der Veranstaltung, neue Anwendungsfälle, die Zukunftsaussichten von MCP und die Verwendung des Agent Builders zur Bereitstellung von Elasticsearch für Agenten über MCP erläutern.</p><h2>Was ist das Model Context Protocol (MCP)?</h2><p>Für diejenigen, die es nicht kennen: <a href="https://modelcontextprotocol.io/introduction">Model Context Protocol</a> ist ein offener Standard, der eine strukturierte, bidirektionale Möglichkeit bietet, KI-Modelle mit verschiedenen Datenquellen und Tools zu verbinden, sodass sie relevantere und fundiertere Antworten generieren können. Er wird allgemein als „ <a href="https://modelcontextprotocol.io/introduction">USB-C-Anschluss für KI-Anwendungen</a>“ bezeichnet.</p><p>Hier ist ein Architekturdiagramm, das seine bidirektionale Natur hervorhebt:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5eba1e59514eb58a5220bb92bb49e6328ee83cd7-674x466.png" style="width: 100%;" /><p>Dies stellt für KI-Praktiker eine bedeutende Veränderung dar, da eine der größten Herausforderungen bei der Skalierung von KI-Anwendungen darin besteht, für jede neue Datenquelle benutzerdefinierte Integrationen erstellen zu müssen. MCP bietet eine nachhaltige, wiederverwendbare Architektur zum Verwalten und Bereitstellen von Kontext für Modelle. Es ist modellunabhängig, serverunabhängig und vollständig Open Source.</p><p>MCP ist die neueste Version einer Reihe von API-Spezifikationen, die die Integration zwischen Anwendungen standardisieren sollen. In der Vergangenheit hatten wir OpenAPI für RESTful-Dienste, GraphQL für Datenabfragen und gRPC für die Mikrodienstkommunikation. MCP teilt nicht nur die strukturierte Genauigkeit dieser älteren Spezifikationen, sondern bringt sie auch in eine generative KI-Umgebung ein, wodurch es einfacher wird, Agenten ohne benutzerdefinierte Konnektoren in verschiedene Systeme einzubinden. In vielerlei Hinsicht zielt MCP darauf ab, für KI-Agenten das zu tun, was HTTP für das Web getan hat. So wie HTTP die Kommunikation zwischen Browsern und Websites standardisiert, versucht MCP, die Art und Weise zu standardisieren, wie KI-Agenten mit der sie umgebenden Datenwelt interagieren.</p><h2>MCP im Vergleich zu anderen Agentenprotokollen</h2><p>Die Landschaft der Agentenprotokolle wächst rasant, und über ein Dutzend neuer Standards konkurrieren darum, die Interaktion der Agenten zu definieren. <a href="https://x.com/seldo">Laurie Voss</a> von LlamaIndex beschreibt, dass die meisten in zwei Typen eingeteilt werden können: Inter-Agent-Protokolle, bei denen der Schwerpunkt auf der Kommunikation der Agenten untereinander liegt, und kontextorientierte Protokolle wie MCP, bei denen der Schwerpunkt auf der Bereitstellung eines strukturierten Kontexts für LLMs liegt.</p><p>Andere beliebte Protokolle wie <a href="https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/">A2A</a> (Agent to Agent) von Google, <a href="https://agentcommunicationprotocol.dev/introduction/welcome">ACP</a> (Agent Communication Protocol) von Cisco und IBM sowie <a href="https://agoraprotocol.org/">Agora</a> zielen darauf ab, Verhandlungen zwischen Agenten, den Aufbau von Koalitionen und sogar dezentrale Identitätssysteme zu ermöglichen. MCP verfolgt dabei einen etwas pragmatischeren Ansatz, da es sich darauf konzentriert, wie Agenten auf Tools und Daten zugreifen und nicht unbedingt darauf, wie sie miteinander kommunizieren (obwohl MCP dies in Zukunft auch auf andere Weise ermöglichen könnte).</p><p>Was MCP derzeit auszeichnet, ist seine Zugkraft und Dynamik. Wie React in den Anfängen der Frontend-Frameworks begann MCP mit einem Nischenproblem und ist heute in der Praxis eines der am häufigsten verwendeten und erweiterbarsten Agentenprotokolle.</p><h2>Gipfel-Rückblick: Neue Prioritäten für MCP</h2><p>Auf dem Gipfel sprachen Redner von Mitwirkenden bei Anthropic, Okta, OpenAI, AWS, GitHub und vielen anderen. Die Gespräche reichten von Verbesserungen des Kernprotokolls bis hin zu Implementierungen in der realen Welt und skizzierten sowohl unmittelbare als auch langfristige Prioritäten. Diese Gespräche spiegelten eine Abkehr von frühen Experimenten und einfachen Tool-Aufrufen hin zum Aufbau vertrauenswürdiger, skalierbarer und modularer KI-Systeme auf der Grundlage von MCP wider.</p><p>Mehrere Redner deuteten eine Zukunft an, in der MCP mehr als nur eine Protokoll-Infrastruktur ist und die Grundlage eines KI-nativen Webs werden kann. So wie JavaScript es Benutzern ermöglicht, auf Webseiten zu klicken und mit ihnen zu interagieren, könnte MCP es Agenten ermöglichen, dieselben Aktionen in unserem Namen auszuführen. Im E-Commerce beispielsweise könnten Benutzer, anstatt manuell zu einer Website zu navigieren, um einzukaufen, einfach einen Agenten anweisen, sich anzumelden, ein bestimmtes Produkt zu suchen, es in den Einkaufswagen zu legen und zur Kasse zu gehen.</p><p>Und das ist nicht bloß reine Spekulation und Hype; PayPal hat auf dem Gipfel sein neues Agenten-Toolkit und seinen MCP-Server vorgestellt, die genau dieses Agenten-Commerce-Erlebnis ermöglichen. Da MCP einen sicheren und zuverlässigen Zugriff auf Tools und Datenquellen bietet, können Agenten das Web nicht nur lesen, sondern auch darauf reagieren. MCP ist heute bereits ein leistungsstarker Standard mit großer Dynamik und könnte sich in Zukunft zum Standard für KI-gestützte Benutzerinteraktionen im gesamten Web entwickeln.</p><h2>MCP-Projekt-Updates: Transport, Erhebung und strukturierte Werkzeuge</h2><p><a href="https://x.com/JeromeSwannack">Jerome Swannack</a>, ein Hauptbeitragender zu MCP, hat einige Aktualisierungen der Protokollspezifikation der letzten 6 Monate mitgeteilt. Die Hauptziele dieser Änderungen sind:</p><ol><li>So aktivieren Sie Remote-MCP durch Hinzufügen von Streamable HTTP</li><li>Um umfangreichere Agenteninteraktionsmodelle durch die Ergänzung von Elicitation- und Tool-Output-Schemata zu ermöglichen</li></ol><p>Da MCP Open Source ist, stehen Änderungen wie Streamable HTTP den Entwicklern bereits zur Implementierung zur Verfügung. Die Schemata zur Ermittlung und Tool-Ausgabe sind derzeit noch nicht veröffentlicht. Sie befinden sich im Entwurfsstadium und können sich weiterentwickeln.</p><p><strong>Streamable HTTP </strong>(<a href="https://modelcontextprotocol.io/specification/2025-03-26/basic/transports">veröffentlicht am 26.03.2025</a>)<strong>:</strong> Ein wirkungsvolles technisches Update war die Einführung von Streamable HTTP als neuer Transportmechanismus. Dadurch werden serverseitig gesendete Ereignisse (SSE) durch ein skalierbareres, bidirektionales Modell ersetzt, das Chunked-Transfer-Encoding und progressive Nachrichtenübermittlung über eine einzelne HTTP-Verbindung unterstützt. Dadurch können Sie MCP-Server auf Cloud-Infrastrukturen wie AWS Lambda bereitstellen und Unternehmensnetzwerkbeschränkungen ohne langlebige Verbindungen oder die Notwendigkeit von Polling unterstützen.</p><p><strong>Elicitation </strong>(<a href="https://modelcontextprotocol.io/specification/2025-06-18/client/elicitation">veröffentlicht am 18.06.2025</a>)<strong>:</strong> Elicitation ermöglicht es Servern, ein Schema für die gewünschte Kontextstruktur eines Clients zu definieren. Im Wesentlichen kann der Server beschreiben, was er benötigt und welche Art von Eingabe er erwartet. Dies hat einige Auswirkungen: Server-Builder können komplexere Agenteninteraktionen erstellen. Client-Builder können dynamische Benutzeroberflächen implementieren, die sich an diese Schemata anpassen. Die Erhebung von Daten sollte jedoch nicht dazu verwendet werden, vertrauliche oder persönlich identifizierbare Informationen von Benutzern zu erhalten. Entwickler sollten <a href="https://modelcontextprotocol.io/specification/draft/client/elicitation#security-considerations">bewährte Methoden</a> befolgen, um sicherzustellen, dass die Abfrageaufforderungen sicher und angemessen bleiben, insbesondere wenn MCP weiterentwickelt wird. Dies hängt mit allgemeineren Sicherheitsbedenken zusammen, die wir später in diesem Beitrag erörtern werden.</p><p><strong>Tool-Ausgabeschemata </strong>(<a href="https://modelcontextprotocol.io/specification/draft/server/tools#output-schema">veröffentlicht am 18.06.2025</a>)<strong>: </strong>Dieses Konzept ermöglicht es dem Client und dem LLM, die Formen der Tool-Ausgabe im Voraus zu kennen. Mithilfe von Tool-Ausgabeschemata können Entwickler beschreiben, was ein Tool voraussichtlich zurückgeben wird. Diese Schemata beheben eine der Haupteinschränkungen des direkten Toolaufrufs, nämlich die ineffiziente Verwendung des Kontextfensters. Das Kontextfenster gilt als eine der wichtigsten Ressourcen bei der Arbeit mit LLMs. Wenn Sie ein Tool direkt aufrufen, gibt es Rohinhalte zurück, die vollständig in den Kontext des LLMs verschoben werden. Mithilfe von Tool-Ausgabeschemata können Sie Ihre Token und das Kontextfenster besser nutzen, indem Sie dem MCP-Server die Bereitstellung strukturierter Daten ermöglichen. Hier sind einige <a href="https://modelcontextprotocol.io/specification/draft/server/tools#security-considerations">Best Practices</a> zu Tools im Allgemeinen.</p><p>Zusammen werden diese neuen Updates und zukünftigen Ergänzungen dazu beitragen, dass MCP zu einem modulareren, typisierteren und produktionsbereiteren Agentenprotokoll wird.</p><h2>Untergenutzte Power-Funktionen: Sampling und Wurzeln</h2><p>Obwohl die MCP-Spezifikation nichts Neues ist, wurden in der Keynote sowohl Sampling als auch Roots hervorgehoben. Diese beiden Grundelemente werden derzeit übersehen und sind noch nicht ausreichend erforscht, können jedoch erheblich zu umfassenderen und sichereren Interaktionen zwischen Agenten beitragen.</p><p><strong>Sampling – Server können Vervollständigungen vom Client anfordern: </strong><a href="https://modelcontextprotocol.io/docs/concepts/sampling">Durch Sampling</a> können MCP-Server Vervollständigungen vom clientseitigen LLM anfordern. Dies trägt zur bidirektionalen Natur des Protokolls bei, bei dem der Server nicht nur auf Anfragen antwortet, sondern das Modell des Clients auffordern und bitten kann, eine Antwort zu generieren. Dadurch behält der Kunde die volle Kontrolle über die Kosten, die Sicherheit und das vom MCP-Server verwendete Modell. Wenn Sie also einen externen MCP-Server mit einem vorkonfigurierten Modell verwenden, müssen Sie weder Ihre eigenen API-Schlüssel angeben noch Ihr eigenes Abonnement für dieses Modell konfigurieren, da der Server einfach das bereits mit dem Client verbundene Modell aufrufen kann. Dies ermöglicht komplexere und interaktivere Agentenverhalten.</p><p><strong>Roots – eingeschränkter Zugriff auf Ressourcen: </strong><a href="https://modelcontextprotocol.io/docs/concepts/roots">Roots</a> wurden entwickelt, um Clients die Möglichkeit zu bieten, Server über relevante Ressourcen und Arbeitsbereiche zu informieren, auf die sie sich konzentrieren sollen. Dies ist leistungsstark, um den Umfang festzulegen, in dem Server arbeiten. Es ist wichtig zu beachten, dass Roots „ <a href="https://modelcontextprotocol.io/docs/concepts/roots#how-roots-work">informativ und nicht strikt erzwingend</a>“ sind, d. h., sie definieren keine Berechtigungen oder Berechtigungen für MCP-Server oder -Agenten. Mit anderen Worten: Sie können sich nicht allein auf Roots verlassen, um einen Server oder Agenten daran zu hindern, bestimmte Tools auszuführen oder Schreibaktionen durchzuführen. Bei Roots sollten Berechtigungen weiterhin clientseitig mit Mechanismen für Benutzergenehmigungen gehandhabt werden. Darüber hinaus sollten Entwickler weiterhin darauf achten, Server zu verwenden, die so konzipiert sind, dass sie die von den Roots gesetzten Grenzen respektieren und <a href="https://modelcontextprotocol.io/docs/concepts/roots#best-practices">bewährte Methoden</a> verwenden.</p><h2>Authentifizierung für Agenten: OAuth 2.1 und geschützte Metadaten</h2><p>Dieser Abschnitt konzentriert sich auf OAuth 2.1, die neueste Version von OAuth 2.0, die unsichere Abläufe beseitigt und bewährte Methoden konsolidiert.</p><p>Die OAuth-Unterstützung war ein mit Spannung erwartetes Thema, insbesondere da Sicherheit und Skalierbarkeit als die größten Hindernisse gelten, die verhindern, dass MCP zum Standard für die Verbindung von Agenten mit Tools wird. <a href="https://x.com/aaronpk">Aaron Parecki</a> (OAuth 2.1-Redakteur und Experte für Identitätsstandards bei Okta) erläuterte, wie MCP einen sauberen, skalierbaren OAuth-Flow übernehmen kann, der den Serverentwicklern den Großteil der Komplexität abnimmt. Die offizielle Autorisierungsspezifikation OAuth 2.1 wurde kürzlich am <a href="https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization">18.06.2025</a> in der neuesten Protokollrevision veröffentlicht.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2ea159116fe5e03ff800f077adf16d6ca9f1c1d1-1594x1280.png" style="width: 100%;" /><p>In dieser Implementierung können die OAuth-Verantwortlichkeiten zwischen dem MCP-Client und dem Server aufgeteilt werden. Der Großteil des Authentifizierungsflusses wird vom MCP-Client initiiert und verarbeitet. Der Server ist nur am Ende beteiligt, um das sichere Token zu empfangen und zu überprüfen. Diese Aufteilung hilft bei der Lösung eines kritischen Skalierungsproblems bei der Authentifizierung über viele Tools hinweg, ohne dass Entwickler jede einzelne Verbindung konfigurieren müssen, und stellt sicher, dass MCP-Serverentwickler keine OAuth-Experten werden müssen.</p><p>Zwei wichtige Highlights aus dem Vortrag:</p><ol><li><a href="https://datatracker.ietf.org/doc/rfc9728/"><strong>Metadaten geschützter Ressourcen</strong></a>: MCP-Server können eine JSON-Datei veröffentlichen, die ihren Zweck, ihre Endpunkte und ihre Authentifizierungsmethoden beschreibt. Dadurch können Clients OAuth-Flows nur mit der Server-URL starten, was den Verbindungsprozess vereinfacht. Mehr erfahren: <a href="https://aaronparecki.com/2025/04/03/15/oauth-for-model-context-protocol">OAuth in MCP reparieren</a></li><li><a href="https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13"><strong>Unterstützung für IDPs und SSO</strong></a>: Unternehmen können Identitätsanbieter integrieren, um den Zugriff zentral zu verwalten. Dies ist sowohl für das Benutzererlebnis als auch für die Sicherheit ein Gewinn. Benutzer müssten nicht durch zehn verschiedene Zustimmungsbildschirme klicken und Sicherheitsteams könnten jede Verbindung beobachten.</li></ol><p>Indem die OAuth-Logik an den Client übertragen wird und auf Metadaten von Servern zurückgegriffen wird, vermeidet das MCP-Ökosystem einen großen Engpass. Dadurch wird MCP besser an die Art und Weise angepasst, wie moderne APIs in heutigen Produktionsumgebungen gesichert werden.</p><p>Zusätzliche Lektüre: <a href="https://aaronparecki.com/oauth-2-simplified/">OAuth 2 Simplified</a>.</p><h2>Sicherheitsherausforderungen in einem zusammensetzbaren Ökosystem</h2><p>Neue Entwicklungen bringen auch neue Angriffsflächen mit sich. Arjun Sambamoorthy von Cisco listet mehrere wichtige Bedrohungen in der MCP-Landschaft auf, darunter:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Bedrohung</td><td style="border: 1px solid #ccc; padding: 4px;">Beschreibung</td><td style="border: 1px solid #ccc; padding: 4px;">Abhilfemaßnahmen und bewährte Methoden</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Sofortige Injektion & Werkzeugvergiftung</td><td style="border: 1px solid #ccc; padding: 4px;">Eine Möglichkeit, eine bösartige Eingabeaufforderung in den LLM-Systemkontext oder die Toolbeschreibung einzufügen, wodurch das LLM unbeabsichtigte Aktionen wie das Lesen von Dateien oder das Durchsickern von Daten ausführt.</td><td style="border: 1px solid #ccc; padding: 4px;">Verwenden Sie Tools wie MCP Scan, um Überprüfungen der Tool-Metadaten durchzuführen. Überprüfen Sie Beschreibungen und Parameter, bevor Sie sie in Eingabeaufforderungen aufnehmen. Erwägen Sie abschließend die Implementierung von Benutzergenehmigungen für Hochrisikotools. Weitere Einzelheiten finden Sie im OWASP Prompt Injection-Handbuch in der zusätzlichen Leseliste unter der Tabelle.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Sampling-Angriffe</td><td style="border: 1px solid #ccc; padding: 4px;">Im Kontext von MCP öffnet das Sampling dem MCP-Server die Tür für sofortige Injektionsangriffe auf das LLM.</td><td style="border: 1px solid #ccc; padding: 4px;">Deaktivieren Sie die Stichprobenentnahme für nicht vertrauenswürdige Server und ziehen Sie in Erwägung, menschliche Genehmigungen für Stichprobenanfragen hinzuzufügen.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Bösartige MCP-Server</td><td style="border: 1px solid #ccc; padding: 4px;">Bei den aktuellen Sammlungen von MCP-Servern ist es schwierig, jeden einzelnen zu überprüfen, um die Sicherheit zu gewährleisten. Betrügerische Server können Ihre Daten unbemerkt sammeln und böswilligen Akteuren preisgeben.</td><td style="border: 1px solid #ccc; padding: 4px;">Stellen Sie nur Verbindungen zu MCP-Servern aus vertrauenswürdigen Registern oder internen Listen her. Führen Sie Server von Drittanbietern in Containern mit Sandboxing aus.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Schädliche MCP-Installationstools</td><td style="border: 1px solid #ccc; padding: 4px;">Befehlszeileninstallationsprogramme und -skripte sind praktisch, um MCP-Server oder -Tools schnell zu implementieren, aber Sie könnten am Ende ungeprüften, kompromittierten Code installieren.</td><td style="border: 1px solid #ccc; padding: 4px;">Installieren Sie es in Sandbox-Umgebungen und validieren Sie Paketsignaturen. Führen Sie niemals automatische Updates aus nicht verifizierten Quellen durch.</td>
                    </tr>
              </tbody>
            </table>
          <p>Um dem noch weiter entgegenzuwirken, schlägt Arjun ein vertrauenswürdiges MCP-Register vor, das alle Überprüfungen übernimmt (ein Thema, das im Mittelpunkt stand – weitere Einzelheiten finden Sie in den ersten beiden Punkten der Leseliste unten) und außerdem die Verwendung dieser <a href="https://github.com/slowmist/MCP-Security-Checklist">Sicherheitscheckliste</a>.</p><p>Weiterführende Literatur:</p><ul><li><a href="https://modelcontextprotocol.io/specification/2025-06-18/basic/security_best_practices">Offizielle Best Practices für MCP-Sicherheit</a></li><li><a href="https://owasp.org/www-project-top-10-for-large-language-model-applications/">OWASP LLM-Bewerbung Top 10</a></li><li><a href="https://hiddenlayer.com/innovation-hub/">HiddenLayer-Bedrohungsforschung</a></li><li><a href="https://github.com/invariantlabs-ai/mcp-scan">MCP-Scan</a></li><li><a href="https://genai.owasp.org/llmrisk/llm01-prompt-injection/">OWASP Prompt Injection-Leitfaden</a></li></ul><h2>Was kommt als Nächstes: Register, Governance und Ökosystem</h2><p>Ein zentrales MCP-Register ist in der Entwicklung und war eines der am häufigsten diskutierten Themen auf dem Gipfel. Das aktuelle Server-Ökosystem leidet unter Fragmentierung, geringem Vertrauen und mangelnder Auffindbarkeit. Für Entwickler ist es schwierig, MCP-Server zu finden, ihre Funktion zu überprüfen und sie sicher zu installieren, insbesondere in einem dezentralen Ökosystem, in dem Metadaten unvollständig oder gefälscht sein können.</p><p>Ein zentrales Register behebt diese Schwachstellen direkt, indem es als vertrauenswürdige Quelle der Wahrheit fungiert, die Auffindbarkeit verbessert, die Integrität der Servermetadaten sicherstellt und das Risiko der Installation bösartiger Tools verringert.</p><p>Die Ziele des MCP-Registers sind:</p><ul><li>Bereitstellung einer einzigen zuverlässigen Quelle für Servermetadaten (was ein Server tut, wie er authentifiziert, installiert und aufgerufen wird)</li><li>Beseitigung unvollständiger Registrierungen von Drittanbietern und Fragmentierung, sodass ein Server bei der Registrierung nicht jede einzelne andere Registrierung im Internet aktualisieren muss.</li><li>Bereitstellung eines Serverregistrierungsflusses, der ein CLI-Tool und eine server.json-Datei umfasst, die die zuvor erwähnten Metadaten enthält.</li></ul><p>Die allgemeinere Hoffnung besteht darin, dass ein vertrauenswürdiges Register dabei hilft, das Ökosystem sicher zu skalieren und es Entwicklern ermöglicht, neue Tools selbstbewusst zu erstellen und zu teilen.</p><p>Ein weiteres wichtiges Thema für Anthropic war die Regierungsführung. Sie machten deutlich, dass MCP offen und von der Community gesteuert bleiben sollte, die Skalierung dieses Governance-Modells jedoch noch in Arbeit ist. Sie suchen derzeit in diesem Bereich nach Unterstützung und bitten jeden, der Erfahrung mit der Governance in Open-Source-Protokollen hat, sich zu melden. Dies führt zu dem anderen Thema, das ich erwähnen wollte. Während der gesamten Veranstaltung betonten die Redner, dass das Ökosystem nur durch die Beiträge der Entwickler wachsen kann. Es bedarf konzentrierter Anstrengungen, um MCP zum neuen Webstandard zu machen und es von den anderen gängigen Agentenprotokollen abzuheben.</p><h2>MCP in der Praxis: Fallstudien und Demos</h2><p>Mehrere Organisationen berichteten, wie MCP bereits in praktischen Anwendungen eingesetzt wird:</p><ul><li><strong>PayPal – MCP-Server für Agentic Commerce: </strong>PayPal stellte sein neues <a href="https://github.com/paypal/agent-toolkit/">Agent-Toolkit</a> und seinen MCP-Server vor, die das Einkaufserlebnis eines Benutzers grundlegend verändern können. Anstatt soziale Medien nach Artikeln zu durchsuchen, Preise zu vergleichen und zur Kasse zu gehen, können Benutzer mit einem Agenten chatten, der eine Verbindung zum PayPal-MCP-Server herstellt und alle diese Aktionen abwickelt.<br/></li><li><strong>EpicAI.pro – Jarvis:</strong> Die Entwicklungen in MCP bringen uns einem echten Assistenten vom Typ Jarvis immer näher. Für diejenigen, die die Iron Man-Filme nicht kennen: Jarvis ist ein KI-Assistent, der natürliche Sprache verwendet, auf multimodale Eingaben reagiert, beim Antworten keine Latenz aufweist, die Bedürfnisse des Benutzers proaktiv antizipiert, Integrationen automatisch verwaltet und zwischen Geräten und Standorten im Kontext wechseln kann. Wenn wir uns Jarvis als physischen Roboterassistenten vorstellen, verleiht MCP Jarvis „Hände“ oder die Fähigkeit, komplexe Aufgaben zu bewältigen.<br/></li><li><strong>Postman – <a href="https://www.postman.com/explore/mcp-generator">MCP-Server-Generator</a>: </strong>Bietet eine Warenkorb-Erfahrung für API-Anfragen, bei der Sie verschiedene API-Anfragen auswählen, in einen Korb legen und den gesamten Korb als MCP-Server herunterladen können.<br/></li><li><strong>Bloomberg – </strong>Bloomberg hat einen wichtigen Engpass bei der GenAI-Entwicklung für Unternehmen gelöst. Mit fast 10.000 Ingenieuren benötigten sie eine standardisierte Möglichkeit, Tools und Agenten teamübergreifend zu integrieren. Mit MCP haben sie ihre internen Tools in modulare Remote-First-Komponenten umgewandelt, die Agenten problemlos über eine einheitliche Schnittstelle aufrufen können. Dadurch konnten ihre Ingenieure Tools für die gesamte Organisation bereitstellen, während sich die KI-Teams auf die Erstellung von Agenten statt auf benutzerdefinierte Integrationen konzentrierten. Bloomberg unterstützt jetzt skalierbare, sichere Agenten-Workflows, die die vollständige Interoperabilität mit dem MCP-Ökosystem ermöglichen. Bloomberg hat keine öffentlichen Quellen verlinkt, aber dies ist, was sie auf dem Gipfel öffentlich präsentiert haben.<br/></li><li><strong>Block – </strong>Block verwendet MCP, um <a href="https://github.com/block/goose?tab=readme-ov-file">Goose</a> zu betreiben, einen internen KI-Agenten, der es Mitarbeitern ermöglicht, Aufgaben in den Bereichen Technik, Vertrieb, Marketing und mehr zu automatisieren. Sie haben über 60 MCP-Server für Tools wie Git, Snowflake, Jira und Google Workspace erstellt, um die Interaktion in natürlicher Sprache mit den Systemen zu ermöglichen, die sie täglich verwenden. Die Mitarbeiter von Block verwenden Goose jetzt, um Daten abzufragen, Betrug zu erkennen, Vorfälle zu verwalten, interne Prozesse zu steuern und vieles mehr – und das alles, ohne Code schreiben zu müssen. MCP hat Block dabei geholfen, die Einführung von KI in vielen Berufsfunktionen in nur zwei Monaten zu skalieren.<br/></li><li><strong>AWS – <a href="https://github.com/awslabs/mcp">AWS MCP-Server</a>: </strong>AWS hat einen unterhaltsamen MCP-Server mit Dungeons-and-Dragons-Thema vorgestellt, der das Würfeln simuliert, vergangene Würfe verfolgt und Ergebnisse über Streamable HTTP zurückgibt. Dieses einfache Beispiel verdeutlicht, wie einfach es ist, MCP-Server mit AWS-Tools und -Infrastrukturen wie Lambda und Fargate zu erstellen und bereitzustellen. Sie stellten außerdem <a href="https://aws.amazon.com/blogs/opensource/introducing-strands-agents-an-open-source-ai-agents-sdk/">Strands SDK</a> vor, ein Open-Source-Toolkit zum Erstellen multimodaler Agenten, die mit MCP-Servern interagieren.</li></ul><h2>MCP-Unterstützung im Elastic Agent Builder</h2><p>Sie können noch heute mit MCP experimentieren und dabei <a href="https://www.elastic.co/de/search-labs/blog/elastic-ai-agent-builder-context-engineering-introduction">den Elastic Agent Builder verwenden.</a> Dies ist die einfachste Möglichkeit, Agenten direkt auf Basis Ihrer Daten zu erstellen. Mit Agent Builder können Sie Elasticsearch-basierte Tools für MCP-kompatible Agenten bereitstellen. Es enthält bereits einige leistungsstarke integrierte Tools, darunter:</p><ul><li><code>platform.core.search</code> - Führt Suchvorgänge unter Verwendung der vollständigen Elasticsearch Query DSL aus.</li><li><code>platform.core.list_indices</code> - Listet alle verfügbaren Indizes in Elasticsearch auf (hilft Agenten, die vorhandenen Daten zu ermitteln)</li><li><code>platform.core.get_index_mapping</code> - Ruft Feldzuordnungen für einen bestimmten Index ab (hilft Agenten, die Struktur und die Typen Ihrer Daten zu verstehen)</li><li><code>platform.core.get_document_by_id</code> - Ruft ein bestimmtes Dokument anhand seiner ID ab (für präzisen Abruf)</li></ul><p>Mit diesen Tools allein können Sie Ihren Agenten mit Such- und Relevanzfunktionen auf Unternehmensebene ausstatten, was für die Entwicklung zuverlässiger KI-Agenten unerlässlich ist.</p><p>Was Agent Builder noch leistungsfähiger macht, ist die Möglichkeit, eigene, auf die Bedürfnisse Ihrer Anwendung zugeschnittene Tools zu definieren und bereitzustellen. Dies ist besonders nützlich für vordefinierte oder wiederholbare Arbeitsabläufe, bei denen der Agent eine bestimmte Art von Suche in einem bestimmten Index durchführen soll, ohne dass diese Logik jedes Mal neu ermittelt werden muss. Anstatt Tokens für Planung und Schlussfolgerung auszugeben, um zum selben Ergebnis zu gelangen, können Sie diese Absicht direkt in ein Tool einprogrammieren, wodurch Ihre Agenten schneller, zuverlässiger und kosteneffizienter werden.</p><p>Innerhalb der Agent Builder-Benutzeroberfläche finden Sie hier ein Beispiel für eine benutzerdefinierte Tool-Definition, die ES|QL verwendet:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c1f6405a374b707e8e6fa36b9e21db5f3c7cd127-1376x864.png" style="width: 100%;" /><p>Sobald Sie Ihre benutzerdefinierten Tools definiert haben, können Sie diese (sowie die integrierten nativen Tools) mithilfe von MCP verfügbar machen, indem Sie auf das Dropdown-Menü für <code>Manage MCP</code> klicken und die MCP-Server-URL kopieren.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9f39588525ca2643475de557ea54a6bcf5c150f6-1282x616.png" style="width: 100%;" /><p>Sie können diesen MCP-Endpunkt nun in jeden Client importieren, der MCP verwendet, um ihn mit Agent Builder zu verbinden und ihm Zugriff auf alle verfügbaren Tools zu gewähren. Weitere Informationen finden Sie in dieser Einführung zu <a href="https://www.elastic.co/de/search-labs/blog/elastic-ai-agent-builder-context-engineering-introduction">Agent Builder</a>.</p><h2>Fazit</h2><p>Der MCP Dev Summit machte deutlich, dass MCP die Art und Weise prägt, wie diese KI-Agenten miteinander und mit der sie umgebenden Datenwelt interagieren. Unabhängig davon, ob Sie einen Agenten mit Unternehmensdaten verbinden oder vollständig autonome Agenten entwerfen, bietet MCP eine standardisierte, zusammensetzbare Möglichkeit zur Integration, die im großen Maßstab schnell nützlich wird. Von Transportprotokollen und Sicherheitsmustern bis hin zu Registern und Governance reift das MCP-Ökosystem schnell. MCP wird weiterhin offen und von der Community gesteuert sein, sodass Entwickler heute die Möglichkeit haben, seine Entwicklung mitzugestalten.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Hybride Suche neu betrachtet: Einführung des linearen Retrievers in Elasticsearch!]]></title>
            <description><![CDATA[Entdecken Sie, wie der lineare Retriever die hybride Suche durch die Nutzung gewichteter Scores und MinMax-Normalisierung für präzisere und konsistentere Rangfolgen verbessert, und lernen Sie, wie Sie ihn verwenden.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/linear-retriever-hybrid-search</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/linear-retriever-hybrid-search</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Wed, 28 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7a07e27e3095463ff93b4cb7f8a0cf3b8e44eab0-1777x1000.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>In unserem <a href="https://www.elastic.co/de/search-labs/blog/elasticsearch-retrievers-ga-8.16.0">vorherigen Blogbeitrag</a> haben wir das von Grund auf neu gestaltete Retriever-Framework vorgestellt, das die Erstellung komplexer Ranking-Pipelines ermöglicht. Wir haben auch untersucht, wie der Reciprocal Rank Fusion (RRF)-Retriever eine hybride Suche ermöglicht, indem er Ergebnisse aus verschiedenen Abfragen zusammenführt. Obwohl RRF einfach zu implementieren ist, weist es eine bemerkenswerte Einschränkung auf: Es konzentriert sich ausschließlich auf relative Ränge und ignoriert tatsächliche Punktzahlen. Dies macht die Feinabstimmung und Optimierung zu einer Herausforderung.</p><h2>Lernen Sie den Linear Retriever kennen!</h2><p>In diesem Beitrag stellen wir den <a href="https://www.elastic.co/de/docs/solutions/search/retrievers-overview#retrievers-overview-types"><code>linear</code></a> <a href="https://www.elastic.co/de/docs/solutions/search/retrievers-overview#retrievers-overview-types">Retriever</a> vor, unsere neueste Ergänzung zur Unterstützung der Hybridsuche! Im Gegensatz zu <code>rrf</code> berechnet der <code>linear</code> -Retriever eine gewichtete Summe aller Abfragen, die mit einem Dokument übereinstimmen. Dieser Ansatz bewahrt die relative Bedeutung jedes Dokuments innerhalb eines Ergebnissatzes und ermöglicht gleichzeitig eine präzise Kontrolle über den Einfluss jeder Abfrage auf das Endergebnis. Dadurch bietet es eine intuitivere und flexiblere Möglichkeit zur Feinabstimmung der Hybridsuche.</p><p>Definieren eines linearen Retrievers, bei dem die endgültige Punktzahl wie folgt berechnet wird:</p><div class="katex">score = 5 * knn + 1.5 * bm25</div><p>Es ist so einfach wie:</p><pre><code>GET linear_retriever_blog/_search
{
   "retriever": {
       "linear": {
           "retrievers": [
               {
                   "retriever": {
                       "knn": {
                          ...
                        }
                    },
                   "weight": 5
               },
                  {
                   "retriever": {
                       "standard": {
                          ...
                        }
                    },
                   "weight": 1.5
               },


           ]
        }
     }
}</code></pre><p>Merken Sie, wie einfach und intuitiv es ist? (und sehr ähnlich zu <code>rrf</code>!) Mit dieser Konfiguration können Sie genau steuern, wie viel jeder Abfragetyp zum endgültigen Ranking beiträgt, im Gegensatz zu <code>rrf</code>, das sich ausschließlich auf relative Ränge stützt.</p><p>Ein Vorbehalt bleibt bestehen: <code>knn</code> -Wertungen können je nach verwendeter Ähnlichkeitsmetrik streng begrenzt sein. Beispielsweise liegen die Werte bei der Kosinusähnlichkeit oder dem Skalarprodukt einheitsnormalisierter Vektoren immer im Bereich <code>[0, 1]</code> . Im Gegensatz dazu sind <code>bm25</code> -Werte weniger vorhersehbar und haben keine klar definierten Grenzen.</p><h2>Skalierung der Ergebnisse: kNN vs. BM25</h2><p>Eine Herausforderung bei der Hybridsuche besteht darin, dass verschiedene Retriever Ergebnisse auf unterschiedlichen Skalen liefern. Stellen Sie sich beispielsweise das folgende Szenario vor:</p><p>Abfrage A-Ergebnisse:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;">Dokument 1</td><td style="border: 1px solid #ccc; padding: 4px;">doc2</td><td style="border: 1px solid #ccc; padding: 4px;">doc3</td><td style="border: 1px solid #ccc; padding: 4px;">doc4</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">knn</td><td style="border: 1px solid #ccc; padding: 4px;">0,347</td><td style="border: 1px solid #ccc; padding: 4px;">0,35</td><td style="border: 1px solid #ccc; padding: 4px;">0,348</td><td style="border: 1px solid #ccc; padding: 4px;">0,346</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">bm25</td><td style="border: 1px solid #ccc; padding: 4px;">100</td><td style="border: 1px solid #ccc; padding: 4px;">1,5</td><td style="border: 1px solid #ccc; padding: 4px;">1</td><td style="border: 1px solid #ccc; padding: 4px;">0,5</td>
                    </tr>
              </tbody>
            </table>
          <p>Abfrage B-Ergebnisse:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;">Dokument 1</td><td style="border: 1px solid #ccc; padding: 4px;">doc2</td><td style="border: 1px solid #ccc; padding: 4px;">doc3</td><td style="border: 1px solid #ccc; padding: 4px;">doc4</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">knn</td><td style="border: 1px solid #ccc; padding: 4px;">0,347</td><td style="border: 1px solid #ccc; padding: 4px;">0,35</td><td style="border: 1px solid #ccc; padding: 4px;">0,348</td><td style="border: 1px solid #ccc; padding: 4px;">0,346</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">bm25</td><td style="border: 1px solid #ccc; padding: 4px;">0,63</td><td style="border: 1px solid #ccc; padding: 4px;">0,01</td><td style="border: 1px solid #ccc; padding: 4px;">0,3</td><td style="border: 1px solid #ccc; padding: 4px;">0,4</td>
                    </tr>
              </tbody>
            </table>
          <p>Sie können die Ungleichheit oben sehen: <code>kNN</code> -Wertungen liegen zwischen 0 und 1, während <code>bm25</code> -Wertungen stark variieren können. Dieser Unterschied macht es schwierig, statische optimale Gewichte für die Kombination der Ergebnisse festzulegen.</p><h2>Normalisierung zur Rettung: der MinMax-Normalisierer</h2><p>Um dieses Problem zu beheben, haben wir einen optionalen <code>minmax</code> -Normalisierer eingeführt, der die Punktzahlen unabhängig für jede Abfrage mithilfe der folgenden Formel auf den <code>[0, 1]</code> -Bereich skaliert:</p><div class="katex">normalized_score = (score - min) / (max - min)</div><p>Dadurch bleibt die relative Wichtigkeit jedes Dokuments innerhalb des Ergebnissatzes einer Abfrage erhalten, was die Kombination von Bewertungen verschiedener Abrufer erleichtert. Durch die Normalisierung ergeben sich folgende Werte:</p><p>Abfrage A-Ergebnisse:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;">Dokument 1</td><td style="border: 1px solid #ccc; padding: 4px;">doc2</td><td style="border: 1px solid #ccc; padding: 4px;">doc3</td><td style="border: 1px solid #ccc; padding: 4px;">doc4</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">knn</td><td style="border: 1px solid #ccc; padding: 4px;">0,347</td><td style="border: 1px solid #ccc; padding: 4px;">0,35</td><td style="border: 1px solid #ccc; padding: 4px;">0,348</td><td style="border: 1px solid #ccc; padding: 4px;">0,346</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">bm25</td><td style="border: 1px solid #ccc; padding: 4px;">1,00</td><td style="border: 1px solid #ccc; padding: 4px;">0,01</td><td style="border: 1px solid #ccc; padding: 4px;">0,005</td><td style="border: 1px solid #ccc; padding: 4px;">0,000</td>
                    </tr>
              </tbody>
            </table>
          <p>Abfrage B-Ergebnisse:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;">Dokument 1</td><td style="border: 1px solid #ccc; padding: 4px;">doc2</td><td style="border: 1px solid #ccc; padding: 4px;">doc3</td><td style="border: 1px solid #ccc; padding: 4px;">doc4</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">knn</td><td style="border: 1px solid #ccc; padding: 4px;">0,347</td><td style="border: 1px solid #ccc; padding: 4px;">0,35</td><td style="border: 1px solid #ccc; padding: 4px;">0,348</td><td style="border: 1px solid #ccc; padding: 4px;">0,346</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">bm25</td><td style="border: 1px solid #ccc; padding: 4px;">1,00</td><td style="border: 1px solid #ccc; padding: 4px;">0,000</td><td style="border: 1px solid #ccc; padding: 4px;">0,465</td><td style="border: 1px solid #ccc; padding: 4px;">0,645</td>
                    </tr>
              </tbody>
            </table>
          <p>Alle Punktzahlen liegen jetzt im Bereich <code>[0, 1]</code> und die Optimierung der gewichteten Summe ist viel einfacher, da wir jetzt die (im Verhältnis zur Abfrage) Wichtigkeit eines Ergebnisses anstelle seiner absoluten Punktzahl erfassen und die Konsistenz über alle Abfragen hinweg aufrechterhalten.</p><h2>Beispiel für einen linearen Retriever </h2><p>Sehen wir uns nun ein Beispiel an, um zu zeigen, wie das oben genannte aussieht und wie der <code>linear</code> -Retriever einige der Mängel von <code>rrf</code> behebt. RRF basiert ausschließlich auf relativen Rängen und berücksichtigt keine tatsächlichen Punkteunterschiede. Beispielsweise bei diesen Ergebnissen:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;">Dokument 1</td><td style="border: 1px solid #ccc; padding: 4px;">doc2</td><td style="border: 1px solid #ccc; padding: 4px;">doc3</td><td style="border: 1px solid #ccc; padding: 4px;">doc4</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">knn</td><td style="border: 1px solid #ccc; padding: 4px;">0,347</td><td style="border: 1px solid #ccc; padding: 4px;">0,35</td><td style="border: 1px solid #ccc; padding: 4px;">0,348</td><td style="border: 1px solid #ccc; padding: 4px;">0,346</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">bm25</td><td style="border: 1px solid #ccc; padding: 4px;">100</td><td style="border: 1px solid #ccc; padding: 4px;">1,5</td><td style="border: 1px solid #ccc; padding: 4px;">1</td><td style="border: 1px solid #ccc; padding: 4px;">0,5</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">RRF-Score</td><td style="border: 1px solid #ccc; padding: 4px;">0,03226</td><td style="border: 1px solid #ccc; padding: 4px;">0,03252</td><td style="border: 1px solid #ccc; padding: 4px;">0,03200</td><td style="border: 1px solid #ccc; padding: 4px;">0,03125</td>
                    </tr>
              </tbody>
            </table>
          <p>rrf würde die Dokumente wie folgt einstufen:</p><div class="katex">doc2 > doc1 > doc3 > doc4</div><p>Allerdings weist doc1 einen deutlich höheren <code>bm25</code> -Score als die anderen auf, den <code>rrf</code> nicht erfasst, da nur die relativen Ränge berücksichtigt werden. Der <code>linear</code> -Retriever berücksichtigt in Kombination mit der Normalisierung sowohl die Punktzahlen als auch ihre Unterschiede korrekt und erzeugt so eine aussagekräftigere Rangfolge:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;">Dokument 1</td><td style="border: 1px solid #ccc; padding: 4px;">doc2</td><td style="border: 1px solid #ccc; padding: 4px;">doc3</td><td style="border: 1px solid #ccc; padding: 4px;">doc4</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">knn</td><td style="border: 1px solid #ccc; padding: 4px;">0,347</td><td style="border: 1px solid #ccc; padding: 4px;">0,35</td><td style="border: 1px solid #ccc; padding: 4px;">0,348</td><td style="border: 1px solid #ccc; padding: 4px;">0,346</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">bm25</td><td style="border: 1px solid #ccc; padding: 4px;">1</td><td style="border: 1px solid #ccc; padding: 4px;">0,01</td><td style="border: 1px solid #ccc; padding: 4px;">0,005</td><td style="border: 1px solid #ccc; padding: 4px;">0</td>
                    </tr>
              </tbody>
            </table>
          <p>Wie wir oben sehen können, wird das großartige Ranking von doc1 und <code>score</code> für <code>bm25</code> richtig berücksichtigt und in den endgültigen Ergebnissen widergespiegelt. Darüber hinaus liegen jetzt alle Ergebnisse im Bereich <code>[0, 1]</code> , sodass wir sie viel intuitiver vergleichen und kombinieren können (und sogar Offline-Optimierungsprozesse erstellen können).</p><h2>Alles zusammenfügen</h2><p>Um den <code>linear</code> -Retriever mit Normalisierung optimal zu nutzen, würde die Suchanfrage folgendermaßen aussehen:</p><pre><code>GET linear_retriever_blog/_search
{
   "retriever": {
       "linear": {
           "retrievers": [
               {
                   "retriever": {
                       "knn": {
                          ...
                        }
                    },
                   "weight": 5
               },
                  {
                   "retriever": {
                       "standard": {
                          ...
                        }
                    },
                   "weight": 1.5,
                   "normalizer": "minmax"
               },


           ]
       }
   }
}</code></pre><p>Dieser Ansatz kombiniert das Beste aus beiden Welten: Er behält die Flexibilität und intuitive Bewertung des <code>linear</code> -Retrievers bei und gewährleistet gleichzeitig eine konsistente Bewertungsskalierung mit MinMax-Normalisierung.</p><p>Wie alle unsere Retriever kann der <code>linear</code> -Retriever in jede Ebene eines hierarchischen Retrieverbaums integriert werden und bietet Unterstützung für Erklärbarkeit, Hervorhebung von Übereinstimmungen, Ausblenden von Feldern und mehr.</p><h2>Wann Sie sich für den Linear Retriever entscheiden sollten und warum das einen Unterschied macht</h2><p>Der <code>linear</code> -Retriever:</p><ul><li>Bewahrt die relative Bedeutung durch die Nutzung tatsächlicher Punktzahlen und nicht nur von Rängen.</li><li>Ermöglicht eine Feinabstimmung mit gewichteten Beiträgen aus verschiedenen Abfragen.</li><li>Verbessert die Konsistenz durch Normalisierung und macht die Hybridsuche robuster und vorhersehbarer.</li></ul><h2>Fazit</h2><p>Der <code>linear</code> -Retriever ist bereits auf Elasticsearch Serverless und den Versionen 8.18 und 9.0 verfügbar! Weitere Beispiele und Konfigurationsparameter finden Sie auch in unserer Dokumentation. Probieren Sie es aus und sehen Sie, wie es Ihr Hybridsucherlebnis verbessern kann – wir freuen uns auf Ihr Feedback. Viel Spaß beim Suchen!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Erstellung von Bewertungslisten mit Quepid]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie in Quepid mithilfe eines kollaborativen menschlichen Bewertungsprozesses Bewertungslisten erstellen und anhand der Benchmarks Ihre Relevanz optimieren können.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/quepid-judgement-lists</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/quepid-judgement-lists</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Daniel Wrigley]]></dc:creator>
            <pubDate>Mon, 26 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/307c1774bd31f92bb4aa7b69e1a6796240465100-1600x914.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Die Erstellung von <a href="https://www.elastic.co/search-labs/blog/judgment-lists">Bewertungslisten</a> ist ein entscheidender Schritt zur Optimierung der Suchergebnisse, kann aber eine komplizierte und schwierige Aufgabe sein. Eine Beurteilungsliste ist eine zusammengestellte Sammlung von Suchanfragen, denen Relevanzbewertungen für die entsprechenden Ergebnisse zugeordnet sind; sie wird auch als Testsammlung bezeichnet. Die anhand dieser Liste berechneten Kennzahlen dienen als Vergleichsmaßstab für die Leistungsfähigkeit einer Suchmaschine. Um den Prozess der Erstellung von Beurteilungslisten zu vereinfachen, entwickelte das <a href="https://opensourceconnections.com/">OpenSource Connections-</a> Team <a href="https://quepidapp.com/">Quepid</a>. Die Beurteilung kann entweder explizit erfolgen oder auf implizitem Feedback von Nutzern basieren. Dieser Blog führt Sie durch die Einrichtung einer kollaborativen Umgebung in Quepid, um menschlichen Bewertern die Möglichkeit zu geben, explizite Beurteilungen vorzunehmen, was die Grundlage jeder Beurteilungsliste bildet.</p><p>Quepid unterstützt Suchteams im Prozess der Bewertung der Suchqualität:</p><ul><li>Abfragesätze erstellen</li><li>Erstellen Sie Beurteilungslisten</li><li>Suchqualitätsmetriken berechnen</li><li>Vergleichen Sie verschiedene Suchalgorithmen/Ranking-Systeme anhand berechneter Suchqualitätsmetriken.</li></ul><p>Nehmen wir für unseren Blog an, wir betreiben eine Videothek und haben das Ziel, die Qualität unserer Suchergebnisse zu verbessern.</p><h2>Voraussetzungen</h2><p>Dieser Blog verwendet die Daten und Zuordnungen aus dem <a href="https://github.com/o19s/es-tmdb">es-tmdb-Repository</a>. Die Daten stammen von <a href="https://www.themoviedb.org/">The Movie Database</a>. Um dem Beispiel zu folgen, erstellen Sie einen Index namens tmdb mit den entsprechenden Zuordnungen und indizieren Sie die Daten. Ob Sie hierfür eine lokale Instanz einrichten oder eine Elastic Cloud-Bereitstellung verwenden, spielt keine Rolle – beides funktioniert einwandfrei. Für diesen Blog gehen wir von einer Elastic Cloud-Bereitstellung aus. Informationen zur Indizierung der Daten finden Sie in der <a href="https://github.com/o19s/es-tmdb/blob/master/README.md">README-Datei des es-tmdb-Repositorys</a>.</p><p>Führen Sie eine einfache Suchabfrage im Titelfeld nach <code>rocky</code> durch, um zu bestätigen, dass Daten zum Durchsuchen vorhanden sind:</p><pre><code>GET tmdb/_search
{
 "query": {
   "match": {
     "title": "rocky"
   }
 }
}</code></pre><p>Es sollten 8 Ergebnisse angezeigt werden.</p><pre><code>{
 "took": 2,
 "timed_out": false,
 "_shards": {
   "total": 1,
   "successful": 1,
   "skipped": 0,
   "failed": 0
 },
 "hits": {
   "total": {
     "value": 8,
     "relation": "eq"
   }
…
}</code></pre><h2>Melden Sie sich bei Quepid an.</h2><p><a href="https://github.com/o19s/quepid">Quepid</a> ist ein Tool, mit dem Benutzer die Qualität von Suchergebnissen messen und Offline-Experimente durchführen können, um diese zu verbessern.</p><p>Sie können Quepid auf zwei Arten nutzen: entweder die kostenlose, öffentlich verfügbare Version unter <a href="https://app.quepid.com">https://app.quepid.com</a>, oder richten Sie Quepid auf einem Rechner ein, auf den Sie Zugriff haben. Dieser Beitrag geht davon aus, dass Sie die kostenlose gehostete Version verwenden. Wenn Sie eine Quepid-Instanz in Ihrer Umgebung einrichten möchten, folgen Sie der <a href="https://github.com/o19s/quepid/wiki/Installation-Guide">Installationsanleitung</a>.</p><p>Egal für welche Variante Sie sich entscheiden, Sie müssen ein Konto erstellen, falls Sie noch keins besitzen.</p><h2>So richten Sie ein Quepid-Ticket ein</h2><p>Quepid ist nach dem Prinzip „Fälle“ organisiert. Ein Case speichert Suchanfragen zusammen mit Relevanzeinstellungen und Informationen zur Herstellung einer Verbindung zu Ihrer Suchmaschine.</p><ul><li>Für Erstnutzer: Wählen Sie <strong>„Ersten Relevanzfall erstellen“</strong>.</li><li>Wiederkehrende Benutzer können im Hauptmenü <strong>„Relevanzfälle“</strong> auswählen und auf <strong>„+ Fall erstellen“</strong> klicken.</li></ul><p>Geben Sie Ihrem Fall einen beschreibenden Namen, z. B. „Filmsuche-Baseline“, da wir mit der Messung und Verbesserung unserer Baseline-Suche beginnen möchten.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8f9e480d9aae0d706cfc5371e41f19c706dd452a-594x251.png" style="width: 100%;" /><p>Bestätigen Sie den Namen, indem Sie <strong>„Weiter“</strong> auswählen.</p><p>Als nächstes stellen wir eine Verbindung von Quepid zur Suchmaschine her. Quepid kann Verbindungen zu einer Vielzahl von Suchmaschinen herstellen, darunter Elasticsearch.</p><p>Die Konfiguration hängt von Ihrer Elasticsearch- und Quepid-Konfiguration ab. Um Quepid mit einer Elastic Cloud-Bereitstellung zu verbinden, müssen wir CORS für unsere Elastic Cloud-Bereitstellung aktivieren und konfigurieren und einen API-Schlüssel bereithalten. Eine detaillierte Anleitung finden Sie in der entsprechenden <a href="https://quepid-docs.dev.o19s.com/2/quepid/49/how-to-connect-quepid-to-elastic-cloud">Anleitung in der Quepid-Dokumentation</a>.</p><p>Geben Sie Ihre Elasticsearch-Endpunktinformationen (<code>https://YOUR_ES_HOST:PORT/tmdb/_search</code>) und alle weiteren Informationen ein, die für die Verbindung erforderlich sind (im Falle einer Elastic Cloud-Bereitstellung den API-Schlüssel in den <strong>erweiterten</strong> Konfigurationsoptionen), testen Sie die Verbindung, indem Sie auf <strong>„pingen“</strong> klicken, und wählen Sie <strong>„Weiter“</strong> , um zum nächsten Schritt zu gelangen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/267339dfaecae2740eb2ee2739bdc971608bdb5f-588x1169.png" style="width: 100%;" /><p>Nun legen wir fest, welche Felder im Fall angezeigt werden sollen. Wählen Sie alle Optionen aus, die unseren menschlichen Gutachtern später helfen, die Relevanz eines Dokuments für eine bestimmte Suchanfrage zu beurteilen.</p><p>Setzen Sie <code>title</code> als <em>Titelfeld</em>, lassen Sie <code>_id</code> als <em>ID-Feld</em> und fügen Sie <code>overview, tagline, cast, vote_average, thumb:poster_path</code> als <em>zusätzliches Anzeigefeld</em> hinzu. Im letzten Eintrag werden kleine Vorschaubilder der Filme in unseren Ergebnissen angezeigt, um uns und den menschlichen Gutachtern eine visuelle Orientierung zu geben.</p><p>Bestätigen Sie die Anzeigeeinstellungen durch Auswahl der Schaltfläche <strong>„Weiter“</strong> .</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/02c58aae8c2ebb6d31f538b27462b4c65428fdc3-594x493.png" style="width: 100%;" /><p>Der letzte Schritt besteht darin, Suchanfragen zum Fall hinzuzufügen. Fügen Sie die drei Suchbegriffe <em>„Star Wars“</em>, <em>„Harrison Ford“</em> und <em>„Bester Actionfilm“</em> nacheinander über das Eingabefeld hinzu und <strong>klicken Sie auf „Weiter“</strong>.</p><p>Idealerweise enthält ein Fallbeispiel Abfragen, die reale Benutzerabfragen repräsentieren und verschiedene Abfragetypen veranschaulichen. Fürs Erste können wir uns vorstellen, dass <em>„Star Wars“</em> eine Suchanfrage ist, die alle Suchanfragen nach Filmtiteln repräsentiert, <em>„Harrison Ford“</em> eine Suchanfrage, die alle Suchanfragen nach Darstellern repräsentiert, und <em>„Bester Actionfilm“</em> eine Suchanfrage, die alle Suchanfragen repräsentiert, die nach Filmen eines bestimmten Genres suchen. Dies wird üblicherweise als Query-Set bezeichnet.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/baccfe96766319aa7255e9bff08913ac87d1517f-595x326.png" style="width: 100%;" /><p>In einem Produktionsszenario würden wir Anfragen aus Ereignisverfolgungsdaten stichprobenartig entnehmen, indem wir statistische Verfahren wie <a href="https://opensourceconnections.com/blog/2022/10/13/how-to-succeed-with-explicit-relevance-evaluation-using-probability-proportional-to-size-sampling/">die Wahrscheinlichkeits-proportional-zur-Größe-Stichprobe</a> anwenden, und diese Stichprobenanfragen in Quepid importieren, um Anfragen vom Anfang (häufige Anfragen) und vom Ende (seltene Anfragen) relativ zu ihrer Häufigkeit einzubeziehen, was bedeutet, dass wir häufigere Anfragen bevorzugen, ohne seltene auszuschließen.</p><p>Wählen Sie abschließend <strong>„Fertigstellen“</strong> aus. Sie werden dann zur Falloberfläche weitergeleitet, wo Sie die drei definierten Abfragen sehen.</p><h2>Anfragen und Informationsbedarf</h2><p>Um zu unserem übergeordneten Ziel einer Bewertungsliste zu gelangen, müssen menschliche Gutachter ein Suchergebnis (in der Regel ein Dokument) für eine bestimmte Suchanfrage bewerten. Dies wird als Abfrage-/Dokumentpaar bezeichnet.</p><p>Manchmal scheint es einfach zu sein, anhand der Suchanfrage zu erkennen, was ein Benutzer wollte. Die Anfrage <code>harrison ford</code> zielt darauf ab, Filme zu finden, in denen der Schauspieler Harrison Ford die Hauptrolle spielt. Und die Anfrage <code>action</code>? Ich weiß, ich wäre versucht zu sagen, dass der Nutzer Filme aus dem Action-Genre sucht. Aber welche? Die neuesten, die beliebtesten, die laut Nutzerbewertungen besten? Oder möchte der Nutzer vielleicht alle Filme finden, die als „Action“ bezeichnet werden? <a href="https://www.themoviedb.org/search/movie?query=Action">In der Movie Database gibt es mindestens 12 (!) Filme mit dem Titel „Action“</a> , die sich hauptsächlich durch die Anzahl der Ausrufezeichen im Titel unterscheiden.</p><p>Zwei menschliche Gutachter können bei der Interpretation einer Anfrage, deren Intention unklar ist, zu unterschiedlichen Ergebnissen kommen. Das Informationsbedürfnis: Ein <a href="https://en.wikipedia.org/wiki/Information_needs">Informationsbedürfnis</a> ist ein bewusstes oder unbewusstes Verlangen nach Informationen. Die Definition eines Informationsbedarfs hilft menschlichen Gutachtern bei der Beurteilung von Dokumenten im Hinblick auf eine Anfrage und spielt daher eine wichtige Rolle beim Aufbau von Beurteilungslisten. Erfahrene Anwender oder Fachexperten eignen sich gut zur Spezifizierung des Informationsbedarfs. Es ist eine gute Praxis, Informationsbedürfnisse aus der Perspektive des Nutzers zu definieren, da die Suchergebnisse dessen Bedürfnisse erfüllen sollten.</p><p>Informationsbedarf für die Suchanfragen unseres „Filmsuche-Baseline“-Szenarios:</p><ol><li><strong>Star Wars</strong>: Der Nutzer möchte Filme oder Serien aus dem Star Wars-Franchise finden. Möglicherweise relevant sind Dokumentarfilme über Star Wars.</li><li><strong>Harrison Ford</strong>: Der Nutzer möchte Filme finden, in denen der Schauspieler Harrison Ford mitspielt. Möglicherweise relevant sind Filme, in denen Harrison Ford eine andere Rolle spielt, zum Beispiel die des Erzählers.</li><li><strong>bester Actionfilm</strong>: Der Nutzer möchte Actionfilme finden, vorzugsweise solche mit einer hohen durchschnittlichen Nutzerbewertung.</li></ol><h2>So legen Sie den Informationsbedarf in Quepid fest</h2><p>Um einen Informationsbedarf in Quepid zu definieren, greifen Sie auf die Fallschnittstelle zu:</p><p>1. Öffnen Sie eine Abfrage (z. B. <em>Star Wars</em>) und wählen Sie <em>„Notizen umschalten“.</em></p><p>2. Tragen Sie im ersten Feld den Informationsbedarf und im zweiten Feld etwaige zusätzliche Anmerkungen ein:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e01d3d5242a350d8797faa665eb3170039f5dfa2-1483x559.png" style="width: 100%;" /><p>3. Klicken Sie auf <strong>Speichern</strong>.</p><p>Für eine Handvoll Anfragen ist dieses Vorgehen in Ordnung. Wenn Sie Ihren Fall jedoch von drei auf 100 Anfragen erweitern (Quepid-Fälle liegen oft im Bereich von 50 bis 100 Anfragen), sollten Sie die Informationsbedürfnisse außerhalb von Quepid definieren (z. B. in einer Tabellenkalkulation) und diese dann über <strong>Import</strong> hochladen und <strong>Informationsbedürfnisse</strong> auswählen.</p><h2>Erstellen Sie ein Team in Quepid und teilen Sie Ihr Ticket mit anderen</h2><p>Gemeinsame Beurteilungen verbessern die Qualität von Relevanzbewertungen. So stellen Sie ein Team zusammen:</p><p>1. Navigieren Sie im Hauptmenü zu <strong>Teams</strong> .</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/797706e8d130b474a95d30b6fa22ecaf36f98c03-613x58.png" style="width: 100%;" /><p>2. Klicken Sie auf <strong>+ Neu hinzufügen</strong>, geben Sie einen Teamnamen ein (z. B. &quot;Suchrelevanzbewerter&quot;) und klicken Sie auf <strong>Erstellen</strong>.</p><p>3. Fügen Sie Mitglieder hinzu, indem Sie deren E-Mail-Adressen eingeben und <strong>auf Benutzer hinzufügen</strong> klicken.</p><p>4. Wählen Sie in der Falloberfläche <strong>die Option „Fall teilen“</strong>.</p><p>5. Wählen Sie das passende Team aus und bestätigen Sie die Auswahl.</p><h2>Erstellen Sie ein Bewertungsbuch in Quepid</h2><p>Ein Buch in Quepid ermöglicht es mehreren Bewertern, Anfrage-/Dokumentpaare systematisch zu bewerten. Um einen zu erstellen:</p><p>1. Gehen Sie in der Falloberfläche zu <strong>„Urteile“</strong> und klicken Sie auf <strong>„+ Buch erstellen“</strong>.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6a077f26225961150b7414463d7db04f090b68d6-896x365.png" style="width: 100%;" /><p>2. Konfigurieren Sie das Buch mit einem aussagekräftigen Namen, weisen Sie es Ihrem Team zu, wählen Sie eine Bewertungsmethode (z. B. DCG@10) und legen Sie die Auswahlstrategie fest (einzelner oder mehrere Bewerter). Verwenden Sie für das Buch die folgenden Einstellungen:</p><ul><li><strong>Name</strong>: „Filmsuche Skala 0-3“</li><li><strong>Teams, mit denen Sie dieses Buch teilen möchten</strong>: Markieren Sie das Kästchen neben dem Team, das Sie erstellt haben.</li><li><strong>Torschütze</strong>: DCG@10</li></ul><p>3. Klicken Sie auf <strong>„Buch erstellen“.</strong></p><p>Der Name ist beschreibend und enthält Informationen darüber, wonach gesucht wird („Filme“) sowie über die Skala der Bewertungen („0-3“). Der ausgewählte Scorer DCG@10 definiert die Berechnungsmethode der Suchmetrik. „DCG“ ist die Abkürzung für <a href="https://en.wikipedia.org/wiki/Discounted_cumulative_gain">Discounted Cumulative Gain (diskontierter kumulativer Gewinn)</a> und „@10“ ist die Anzahl der Ergebnisse von oben, die bei der Berechnung der Kennzahl berücksichtigt werden.</p><p>In diesem Fall verwenden wir eine Metrik, die den Informationsgewinn misst und ihn mit der Positionsgewichtung kombiniert. Möglicherweise gibt es andere Suchmetriken, die für Ihren Anwendungsfall besser geeignet sind, und <a href="https://opensourceconnections.com/blog/2020/02/28/choosing-your-search-relevance-metric">die Auswahl der richtigen Metrik ist schon eine Herausforderung für sich</a>.</p><h2>Füllen Sie das Buch mit Abfrage-/Dokumentpaaren.</h2><p>Um Abfrage-/Dokumentpaare für die Relevanzbewertung hinzuzufügen, befolgen Sie diese Schritte:</p><p>1. Navigieren Sie in der Fallübersicht zu „Urteile“.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d717c5b06ae6cb42ed2b9e771486a12f738a9890-1041x218.png" style="width: 100%;" /><p>2. Wählen Sie Ihr erstelltes Buch aus.</p><p>3. Klicken Sie auf „Buch füllen“ und bestätigen Sie mit der Auswahl von „Abfrage-/Dokumentpaare für Buch aktualisieren“.</p><p>Diese Aktion generiert Paare basierend auf den Top-Suchergebnissen für jede Suchanfrage, die dann von Ihrem Team ausgewertet werden können.</p><h2>Lassen Sie Ihr Team aus menschlichen Bewertern urteilen </h2><p>Die bisher abgeschlossenen Schritte waren überwiegend technischer und administrativer Natur. Nachdem diese notwendigen Vorbereitungen nun abgeschlossen sind, können wir unser Richterteam seine Arbeit machen lassen. Im Wesentlichen besteht die Aufgabe des Richters darin, die Relevanz eines bestimmten Dokuments für eine gegebene Anfrage zu bewerten. Das Ergebnis dieses Prozesses ist die Bewertungsliste, die alle Relevanzbezeichnungen für die bewerteten Abfragedokumentpaare enthält. Im Folgenden werden dieser Prozess und die zugehörige Benutzeroberfläche genauer erläutert.</p><h3>Übersicht über die Schnittstelle von Human Rating</h3><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/be3016091b49655dab3354d84e6dc638f3468390-1283x664.png" style="width: 100%;" /><p>Die Human Rating-Schnittstelle von Quepid ist für effiziente Bewertungen konzipiert:</p><ul><li><strong>Suchanfrage:</strong> Zeigt den Suchbegriff an.</li><li><strong>Informationsbedarf:</strong> Zeigt die Absicht des Nutzers.</li><li><strong>Bewertungsrichtlinien:</strong> Enthält Anweisungen für eine einheitliche Bewertung.</li><li><strong>Dokumentmetadaten:</strong> Enthält relevante Details zum Dokument.</li><li><strong>Bewertungsbuttons:</strong> Ermöglicht es den Bewertenden, Beurteilungen mithilfe entsprechender Tastenkombinationen zuzuweisen.</li></ul><h3>Verwendung der Human Rating-Schnittstelle</h3><p>Als menschlicher Bewerter greife ich über die Buchübersicht auf die Benutzeroberfläche zu:</p><p>1. Navigieren Sie zur Falloberfläche und klicken Sie auf <strong>Urteile</strong>.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d717c5b06ae6cb42ed2b9e771486a12f738a9890-1041x218.png" style="width: 100%;" /><p>2. Klicken Sie auf <strong>„Weitere Urteile sind erforderlich!“</strong>.</p><p>Das System präsentiert ein Anfrage-/Dokumentpaar, das noch nicht bewertet wurde und weitere Beurteilungen erfordert. Dies wird durch die Auswahlstrategie des Buches bestimmt:</p><ul><li><em>Einzelbewerter</em>: Eine einzige Bewertung pro Anfrage/Dokumentenpaar.</li><li><em>Mehrere Gutachter</em>: Bis zu drei Beurteilungen pro Anfrage/Dokumentenpaar.</li></ul><h3>Bewertungsanfrage/Dokumentpaare</h3><p>Schauen wir uns ein paar Beispiele an. Wenn Sie dieser Anleitung folgen, werden Ihnen höchstwahrscheinlich verschiedene Filme angezeigt. Die Bewertungsgrundsätze bleiben jedoch unverändert.</p><p>Unser erstes Beispiel ist der Film „Heroes“ für die Suchanfrage <em>Harrison Ford</em>:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c1e14b0d8b04dd579471932dbe4ff72ae5692a02-981x571.png" style="width: 100%;" /><p>Wir betrachten zunächst die Suchanfrage, dann den Informationsbedarf und beurteilen anschließend den Film anhand der angegebenen Metadaten.</p><p>Dieser Film ist ein relevantes Ergebnis für unsere Suchanfrage, da Harrison Ford zur Besetzung gehört. Wir mögen neuere Filme subjektiv als relevanter einstufen, aber dies entspricht nicht unserem Informationsbedürfnis. Daher bewerten wir dieses Dokument mit „Perfekt“, was einer 3 auf unserer Bewertungsskala entspricht.</p><p>Unser nächstes Beispiel ist der Film „Ford v Ferrari“ für die Suchanfrage <em>Harrison Ford</em>:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/052af7894506d7a765af156ba8e26ceec3559973-981x789.png" style="width: 100%;" /><p>In Anlehnung an diese Vorgehensweise beurteilen wir diese Anfrage/dieses Dokument, indem wir die Anfrage, den Informationsbedarf und anschließend prüfen, wie gut die Metadaten des Dokuments dem Informationsbedarf entsprechen.</p><p>Das ist ein schlechtes Ergebnis. Dieses Ergebnis sehen wir wahrscheinlich deshalb, weil einer unserer Suchbegriffe, „ford“, im Titel vorkommt. Harrison Ford spielt jedoch weder in diesem Film noch in irgendeiner anderen Rolle eine Rolle. Daher bewerten wir dieses Dokument mit „Mangelhaft“, was einer 0 auf unserer Bewertungsskala entspricht.</p><p>Unser drittes Beispiel ist der Film „Action Jackson“ für die Suchanfrage <em>„bester Actionfilm“</em>:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/247ab862fbc7435537709f8c96619cb331133d09-985x606.png" style="width: 100%;" /><p>Das sieht nach einem Actionfilm aus, das Informationsbedürfnis ist also zumindest teilweise befriedigt. Der durchschnittliche Stimmenanteil liegt jedoch bei 5,4 von 10 Punkten. Und das macht diesen Film wahrscheinlich nicht zum besten Actionfilm unserer Sammlung. Dies würde mich als Richter dazu veranlassen, dieses Dokument mit „Mittelmäßig“ zu bewerten, was einer 1 auf unserer Bewertungsskala entspricht.</p><p>Diese Beispiele veranschaulichen den Prozess der Bewertung von Anfrage-/Dokumentpaaren mit Quepid im Besonderen, auf einer hohen Ebene und auch im Allgemeinen.</p><h2>Best Practices für menschliche Bewerter</h2><p>Die gezeigten Beispiele könnten den Eindruck erwecken, es sei unkompliziert, zu eindeutigen Urteilen zu gelangen. Doch die Einrichtung eines zuverlässigen menschlichen Bewertungsprogramms ist keine leichte Aufgabe. Es handelt sich um einen Prozess voller Herausforderungen, die die Qualität Ihrer Daten leicht beeinträchtigen können:</p><ul><li>Menschliche Bewerter können durch sich wiederholende Aufgaben ermüden.</li><li>Persönliche Vorlieben können Urteile verzerren.</li><li>Der Grad an Fachwissen variiert von Richter zu Richter.</li><li>Bewerter jonglieren oft mit mehreren Aufgaben gleichzeitig.</li><li>Die wahrgenommene Relevanz eines Dokuments entspricht möglicherweise nicht seiner tatsächlichen Relevanz für eine Suchanfrage.</li></ul><p>Diese Faktoren können zu uneinheitlichen und qualitativ minderwertigen Beurteilungen führen. Aber keine Sorge – es gibt bewährte Best Practices, die Ihnen helfen können, diese Probleme zu minimieren und einen robusteren und zuverlässigeren Evaluierungsprozess aufzubauen:</p><ul><li><strong>Konsequente Bewertung:</strong> Überprüfen Sie die Anfrage, den Informationsbedarf und die Dokumentenmetadaten der Reihe nach.</li><li><strong>Beachten Sie die Richtlinien:</strong> Verwenden Sie die Bewertungsrichtlinien, um eine einheitliche Bewertung zu gewährleisten. Die Bewertungsrichtlinien können Beispiele enthalten, wann welche Note zu vergeben ist, wodurch der Beurteilungsprozess veranschaulicht wird. Die Rücksprache mit menschlichen Gutachtern nach der ersten Bewertungsrunde erwies sich als gute Vorgehensweise, um schwierige Grenzfälle zu erkennen und herauszufinden, wo zusätzliche Unterstützung benötigt wird.</li><li><strong>Nutzen Sie die Antwortmöglichkeiten:</strong> Wenn Sie unsicher sind, verwenden Sie „Ich werde später urteilen“ oder „Ich kann es nicht sagen“ und geben Sie gegebenenfalls Erklärungen an.</li><li><strong>Machen Sie Pausen:</strong> Regelmäßige Pausen tragen dazu bei, die Urteilsfähigkeit aufrechtzuerhalten. Quepid sorgt für regelmäßige Pausen, indem es Konfetti knallen lässt, sobald ein menschlicher Bewerter eine Reihe von Beurteilungen abgeschlossen hat.</li></ul><p>Durch Befolgen dieser Schritte etablieren Sie einen strukturierten und kollaborativen Ansatz zur Erstellung von Beurteilungslisten in Quepid und steigern so die Effektivität Ihrer Bemühungen zur Optimierung der Suchrelevanz.</p><h2>Wie geht es weiter?</h2><p>Wie geht es nun weiter? Bewertungslisten sind nur ein grundlegender Schritt zur Verbesserung der Qualität der Suchergebnisse. Hier die nächsten Schritte:</p><h3>Berechnen Sie Metriken und beginnen Sie mit dem Experimentieren</h3><p>Sobald die Bewertungslisten verfügbar sind, ist die Nutzung der Bewertungen und die Berechnung <a href="https://opensourceconnections.com/blog/2020/02/28/choosing-your-search-relevance-metric/">von Kennzahlen zur Suchqualität</a> ein logischer nächster Schritt. Quepid berechnet die konfigurierte Metrik für den aktuellen Fall automatisch, sobald Urteile vorliegen. Metriken werden als „Scorer“ implementiert, und Sie können Ihre eigenen angeben, wenn die unterstützten Metriken Ihre bevorzugten nicht enthalten!</p><p>Gehen Sie zur Falloberfläche, navigieren Sie zu <strong>„Scorer auswählen“</strong>, wählen Sie <em>DCG@10</em> und bestätigen Sie mit einem Klick auf <strong>„Scorer auswählen“</strong>. Quepid berechnet nun DCG@10 pro Abfrage und mittelt außerdem die Gesamtzahl der Abfragen, um die Qualität der Suchergebnisse für Ihren Fall zu quantifizieren.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0ff3b9918342071744d681a43d542102e927abd3-1163x551.png" style="width: 100%;" /><p>Nachdem die Qualität Ihrer Suchergebnisse nun quantifiziert wurde, können Sie erste Experimente durchführen. Experimente beginnen mit der Aufstellung von Hypothesen. Ein Blick auf die drei Suchanfragen im Screenshot nach der Bewertung macht deutlich, dass die drei Suchanfragen hinsichtlich ihrer Suchqualitätsmetrik sehr unterschiedlich abschneiden: <em>Star Wars</em> schneidet ziemlich gut ab, <em>Harrison Ford</em> sieht in Ordnung aus, aber das größte Potenzial liegt bei <em>Best Action Movie</em>.</p><p>Durch die Erweiterung dieser Abfrage sehen wir ihre Ergebnisse und können in die Details eintauchen und untersuchen, warum Dokumente übereinstimmten und was ihre Punktzahl beeinflusst:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c45c6cae085a492198c0f8b7060a1a7204e3724e-1131x691.png" style="width: 100%;" /><p>Durch Klicken auf „Abfrage erläutern“ und Aufrufen der Registerkarte „Parsing“ sehen wir, dass es sich bei der Abfrage um eine DisjunctionMaxxQuery handelt, die drei Felder durchsucht: <em>cast</em>, <em>overview</em> und <em>title</em>:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4a1e1bb2a9cd28e9c48e0ba16357d17ed9d3a5cf-894x557.png" style="width: 100%;" /><p>Als Suchmaschinenentwickler kennen wir in der Regel einige domänenspezifische Details unserer Suchplattform. In diesem Fall wissen wir möglicherweise, dass wir ein <em>Genre-</em> Feld haben. Fügen wir das der Suchanfrage hinzu und schauen wir, ob sich die Suchqualität verbessert.</p><p>Wir verwenden die <strong>Abfrage-Sandbox</strong> , die sich öffnet, wenn man in der Fallschnittstelle <strong>„Relevanz optimieren“</strong> auswählt. Probieren Sie es aus, indem Sie das Suchfeld <em>„Genres“</em> hinzufügen:</p><pre><code>{
  "query": {
    "multi_match": {
      "query": "#$query##",
      "type": "best_fields",
      "fields": [
        "title^10",
        "overview",
        "cast",
        "genres"
      ]
    }
  }
}</code></pre><p>Klicken Sie auf „Meine Suchanfragen erneut ausführen“! Und sehen Sie sich die Ergebnisse an. Haben sie sich verändert? Leider nein. Wir haben nun viele Möglichkeiten zur Erkundung, im Grunde alle Abfrageoptionen, die Elasticsearch bietet:</p><ul><li>Wir könnten die Gewichtung des Feldes „Genres“ erhöhen.</li><li>Wir könnten eine Funktion hinzufügen, die Dokumente anhand ihres durchschnittlichen Abstimmungsergebnisses höher priorisiert.</li><li>Wir könnten eine komplexere Abfrage erstellen, die Dokumente nur dann nach ihrem Stimmendurchschnitt priorisiert, wenn eine starke Übereinstimmung der Genres vorliegt.</li><li>…</li></ul><p>Das Beste daran, all diese Optionen zu haben und sie in Quepid zu erkunden, ist, dass wir die Möglichkeit haben, die Auswirkungen nicht nur auf die eine Abfrage zu quantifizieren, die wir verbessern wollen, sondern auf alle Abfragen in unserem Fall. Das hindert uns daran, eine leistungsschwache Suchanfrage zu verbessern, indem wir die Qualität der Suchergebnisse für andere opfern. Wir können schnell und kostengünstig iterieren und den Wert unserer Hypothese ohne Risiko validieren, wodurch Offline-Experimente zu einer grundlegenden Fähigkeit aller Suchteams werden.</p><h3>Messung der Interrater-Zuverlässigkeit</h3><p>Selbst bei Aufgabenbeschreibungen, Informationsbedarfsdefinitionen und einer Benutzeroberfläche für menschliche Bewerter, wie sie Quepid bietet, können menschliche Bewerter unterschiedlicher Meinung sein.</p><p>Meinungsverschiedenheiten an sich sind nichts Schlechtes, ganz im Gegenteil: Die Messung von Meinungsverschiedenheiten kann Probleme aufdecken, die man angehen möchte. Relevanz kann subjektiv sein, Anfragen können mehrdeutig sein und Daten können unvollständig oder fehlerhaft sein. <a href="https://en.wikipedia.org/wiki/Fleiss%27_kappa">Fleiss&#x27; Kappa</a> ist ein statistisches Maß für die Übereinstimmung zwischen Beurteilern. In Quepid gibt es ein Beispiel-Notebook, das Sie verwenden können. Um es zu finden, wählen Sie in der Hauptnavigation <strong>„Notebooks“</strong> aus und wählen Sie das Notebook <strong>Fleiss Kappa.ipynb</strong> im Ordner <strong>„examples“</strong> aus.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f0669ae96371368ef4d84bb28669560ef09d755c-624x61.png" style="width: 100%;" /><h2>Fazit</h2><p>Mit Quepid können Sie selbst die komplexesten Herausforderungen im Bereich der Suchrelevanz bewältigen und es wird ständig weiterentwickelt: <a href="https://github.com/o19s/quepid/blob/main/CHANGELOG.md#800----2024-02-14">Ab Version 8 unterstützt Quepid KI-generierte Beurteilungen</a>, was besonders für Teams nützlich ist, die ihren Beurteilungsgenerierungsprozess skalieren möchten.</p><p>Mit Quepid-Workflows können Sie effizient skalierbare Beurteilungslisten erstellen – was letztendlich zu Suchergebnissen führt, die den Bedürfnissen der Nutzer wirklich gerecht werden. Mit den erstellten Bewertungslisten verfügen Sie über eine solide Grundlage, um die Relevanz der Suchergebnisse zu messen, Verbesserungen iterativ umzusetzen und ein besseres Nutzererlebnis zu schaffen.</p><p>Denken Sie bei Ihrem weiteren Vorgehen daran, dass die Relevanzoptimierung ein fortlaufender Prozess ist. Beurteilungslisten ermöglichen es Ihnen, Ihren Fortschritt systematisch zu bewerten, ihre größte Wirkung entfalten sie jedoch in Kombination mit Experimenten, Metrikanalysen und iterativen Verbesserungen.</p><h2>Weitere Lektüre</h2><ul><li>Quepid-Dokumentation:<ul><li><a href="https://quepid-docs.dev.o19s.com/2/quepid/32/relevancy-is-a-team-sport">Relevanz ist ein Teamsport</a></li><li><a href="https://quepid-docs.dev.o19s.com/2/quepid/18/quepid-for-human-raters">Quepid für menschliche Bewerter</a></li><li><a href="https://quepid-docs.dev.o19s.com/2/quepid/49/how-to-connect-quepid-to-elastic-cloud">Wie man Quepid mit Elastic Cloud verbindet</a></li></ul></li><li><a href="https://github.com/o19s/quepid">Quepid GitHub-Repository</a></li><li><a href="https://opensourceconnections.com/blog/2020/07/07/meet-pete-the-e-commerce-search-product-manager/">Lernen Sie Pete kennen, eine Blogserie zur Verbesserung der E-Commerce-Suche</a></li><li><a href="https://opensourceconnections.com/slack">Relevanz Slack</a>: Treten Sie dem Kanal #quepid bei</li></ul><p><strong>Arbeiten Sie mit <a href="https://opensourceconnections.com/">Open Source Connections</a></strong> zusammen, um Ihre Such- und KI-Fähigkeiten zu transformieren und Ihr Team zu befähigen, diese kontinuierlich weiterzuentwickeln. Unsere Erfolgsbilanz erstreckt sich über den gesamten Globus, wobei unsere Kunden durchweg dramatische Verbesserungen in der Suchqualität, der Teamleistung und der Geschäftsperformance erzielen. <a href="https://opensourceconnections.com/contact/">Kontaktieren Sie uns noch heute,</a> um mehr zu erfahren.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Anzeigen von Feldern in einem Elasticsearch-Index]]></title>
            <description><![CDATA[Untersuchung von Techniken zur Darstellung von Feldern in einem Elasticsearch-Index.
]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/displaying-fields-in-an-elasticsearch-index</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/displaying-fields-in-an-elasticsearch-index</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Kofi Bartlett]]></dc:creator>
            <pubDate>Mon, 26 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/fa386d7bbaeab6855e62897ace8d7dca91a060b4-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>In diesem Artikel werden wir erläutern, wie Felder in einem Elasticsearch-Index angezeigt werden. Dies kann hilfreich sein, um die Struktur Ihrer Daten zu verstehen, bestimmte Felder zu identifizieren und Probleme zu beheben. Wir werden folgende Themen behandeln:</p><ol><li><a href="https://www.elastic.co/search-labs/blog/displaying-fields-in-an-elasticsearch-index#1.-using-the--mapping-api-to-retrieve-field-information">Verwendung der <code>_mapping</code> API zum Abrufen von Feldinformationen</a></li><li><a href="https://www.elastic.co/search-labs/blog/displaying-fields-in-an-elasticsearch-index#2.-using-the--search-api-to-display-field-values">Verwendung der <code>_search</code> API zum Anzeigen von Feldwerten</a></li><li><a href="https://www.elastic.co/search-labs/blog/displaying-fields-in-an-elasticsearch-index#3.-filtering-fields-using-the-fields-parameter">Filtern von Feldern mithilfe des  Parameters<code>fields</code></a></li><li><a href="https://www.elastic.co/search-labs/blog/displaying-fields-in-an-elasticsearch-index#4.-displaying-nested-fields">Verschachtelte Felder anzeigen</a></li></ol><h2>1. Verwendung der _mapping-API zum Abrufen von Feldinformationen</h2><p>Die <code>_mapping</code> API ermöglicht es Ihnen, die Mapping-Definition für einen oder mehrere <a href="https://opster.com/guides/elasticsearch/glossary/elasticsearch-index/">Indizes</a> abzurufen. Dies umfasst Informationen über die Felder, ihre Datentypen und weitere Eigenschaften. Um die Zuordnung für einen bestimmten Index abzurufen, verwenden Sie die folgende Anfrage:</p><pre><code>GET /<index_name>/_mapping</code></pre><p>Wenn Sie beispielsweise einen Index mit dem Namen <code>my_index</code> haben, können Sie dessen Zuordnung mit der folgenden Anfrage abrufen:</p><pre><code>GET /my_index/_mapping</code></pre><p>Die Antwort enthält die Mapping-Definition für den Index, die Informationen über die Felder und deren Eigenschaften enthält.</p><p>Es ist auch möglich, die Zuordnung eines bestimmten Feldes abzurufen. Dies kann nützlich sein, wenn Ihre Kartierung recht umfangreich ist und Sie sich nur auf ein bestimmtes Feld konzentrieren möchten. Um die Zuordnung eines bestimmten Feldes abzurufen, verwenden Sie die folgende Anfrage:</p><pre><code>GET /my_index/_mapping/field/my_field</code></pre><p>Sie können die Zuordnungen mehrerer Felder auch abrufen, indem Sie deren Namen durch Kommas trennen, wie in der folgenden Anfrage:</p><pre><code>GET /my_index/_mapping/field/my_field_1,my_field_2,my_field_3</code></pre><h2>2. Verwenden der _search-API zum Anzeigen von Feldwerten</h2><p>Um die Werte von Feldern in einem Elasticsearch-Index anzuzeigen, können Sie die <code>_search</code> API verwenden. Standardmäßig gibt die <code>_search</code> API das Feld <code>_source</code> zurück, welches das ursprüngliche JSON-Dokument enthält, das indiziert wurde. Um nur bestimmte Felder anzuzeigen, können Sie den Parameter <code>_source</code> in der Suchanfrage verwenden.</p><p>Hier ist ein Beispiel für eine Suchanfrage, die die Werte der Felder <code>title</code> und <code>author</code> für Dokumente im Index <code>my_index</code> zurückgibt:</p><pre><code>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "_source": ["title", "author"]
}</code></pre><p>In diesem Beispiel gibt der Parameter <code>_source</code> die zurückzugebenden Felder an.</p><h2>3. Filtern von Feldern mithilfe des Parameters „fields“</h2><p>Sie können auch den Parameter <code>fields</code> verwenden, um die in der Suchantwort zurückgegebenen Felder zu filtern. Dies kann nützlich sein, wenn Sie nur bestimmte Felder benötigen und die Größe der Antwort reduzieren möchten. Der Parameter <code>fields</code> akzeptiert ein Array von Feldnamen oder Platzhaltermustern.</p><p>Um beispielsweise nur die Felder <code>title</code> und <code>author</code> für Dokumente im Index <code>my_index</code> zurückzugeben, können Sie die folgende Suchanfrage verwenden:</p><pre><code>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "fields": ["title", "author"],
  "_source": false
}</code></pre><p>Beachten Sie, dass der Parameter <code>_source</code> auf false gesetzt ist, um das Quelldokument nicht zurückzugeben.</p><p>Um alle Felder mit dem Datentyp <code>text</code> zurückzugeben, können Sie ein Wildcard-Muster wie dieses verwenden:</p><pre><code>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "fields": ["*.text"],
  "_source": false
}</code></pre><h2>4. Anzeigen verschachtelter Felder</h2><p>Wenn Ihr Index verschachtelte Felder enthält, können Sie die Punktnotation verwenden, um den Pfad des verschachtelten Feldes im Parameter <code>fields</code> anzugeben. Wenn Sie beispielsweise ein verschachteltes Feld mit dem Namen <code>address.city</code> haben, können Sie es wie folgt in die Suchergebnisseinlösung einbinden:</p><pre><code>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "fields": ["title", "author", "address.city"],
  "_source": false
}</code></pre><p>In diesem Beispiel enthält die Suchergebnisseinsendung die Werte der Felder <code>title</code>, <code>author</code> und <code>address.city</code> .</p><h2>Fazit</h2><p>Zusammenfassend lässt sich sagen, dass die Anzeige von Feldern in einem Elasticsearch-Index durch die Verwendung der <code>_mapping</code> -API zum Abrufen von Feldinformationen und der <code>_search</code> -API zum Anzeigen von Feldwerten erreicht werden kann. Sie können die in der Suchantwort zurückgegebenen Felder entweder mit den Parametern <code>_source</code> oder <code>fields</code> filtern und verschachtelte Felder mit der Punktnotation anzeigen. Mithilfe dieser Techniken können Sie die Struktur Ihrer Daten verstehen, bestimmte Felder identifizieren und Probleme beheben.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Spring AI und Elasticsearch als Ihre Vektordatenbank]]></title>
            <description><![CDATA[Lernen Sie, wie Sie eine produktionsreife RAG-App mit Spring AI und Elasticsearch erstellen und LLMs mit Ihren proprietären Daten mithilfe einer Vektordatenbank integrieren.
]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/spring-ai-elasticsearch-application</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/spring-ai-elasticsearch-application</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Philipp Krenn, Laura Trotta]]></dc:creator>
            <pubDate>Tue, 20 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0771fb5b3d9234697cb868cd7d9d1b840000bf29-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p><strong>Spring AI</strong> ist jetzt allgemein verfügbar, die erste <a href="https://spring.io/blog/2025/05/20/spring-ai-1-0-GA-released">stabile Version 1.0</a> steht auf <a href="https://mvnrepository.com/artifact/org.springframework.ai/spring-ai-core">Maven Central</a> zum Download bereit. Lass es uns gleich nutzen, um eine vollständige KI-Anwendung zu erstellen, mit deinem bevorzugten <a href="https://www.elastic.co/what-is/large-language-models">LLM</a> und unserer bevorzugten <a href="https://www.elastic.co/elasticsearch/vector-database">Vektordatenbank</a>. Oder stürzen Sie sich direkt in das <a href="https://github.com/xeraa/rag-with-java-springai-elasticsearch">Repository</a> mit der fertigen Anwendung.</p><a href="https://www.youtube.com/watch/ms2xRO8B1YU">Watch video on YouTube</a><h2>Was ist Spring AI?</h2><p><strong>Spring AI 1.0</strong>, eine umfassende Lösung für KI-Engineering in Java, ist nach einer langen Entwicklungsphase, die von rasanten Fortschritten im Bereich der KI geprägt war, nun verfügbar. Die Version enthält zahlreiche wichtige neue Funktionen für KI-Ingenieure.</p><p>Java und Spring sind bestens positioniert, um auf diesen KI-Zug aufzuspringen. Unzählige Unternehmen setzen auf Spring Boot, um ihre Systeme zu betreiben. Dadurch ist es extrem einfach, KI in ihre bestehenden Prozesse zu integrieren. Sie können Ihre Geschäftslogik und Daten im Prinzip ohne großen Aufwand direkt mit diesen KI-Modellen verknüpfen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/328d7c51e1c145e94ea1e73ee9ff91836d3b180e-1600x773.png" style="width: 100%;" /><p>Spring AI bietet Unterstützung für <a href="https://docs.spring.io/spring-ai/reference/api/index.html">verschiedene KI-Modelle und -Technologien</a>, wie zum Beispiel:</p><ul><li><strong>Bildmodelle</strong>: Generieren von Bildern anhand von Textvorgaben.</li><li><strong>Transkriptionsmodelle</strong>: Sie nehmen Audioquellen und wandeln sie in Text um.</li><li><strong>Einbettungsmodelle: </strong>Beliebige Daten werden in <a href="https://www.elastic.co/what-is/vector-embedding">Vektoren</a> umgewandelt, die für die semantische Ähnlichkeitssuche optimiert sind.</li><li><strong>Chat-Modelle: </strong>diese<strong> </strong>Sollte bekannt sein! Sie haben zweifellos schon einmal ein kurzes Gespräch mit einem geführt.</li></ul><p>Chatmodelle stehen im Bereich der KI derzeit im Mittelpunkt des Interesses, und das völlig zu Recht – sie sind fantastisch! Sie können sie bitten, Ihnen bei der Korrektur eines Dokuments zu helfen oder ein Gedicht zu schreiben. (Bitten Sie sie nur noch nicht, einen Witz zu erzählen.) Sie sind großartig, aber sie haben auch einige Schwächen.</p><h2>Spring AI-Lösungen für KI-Herausforderungen</h2><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2ebd68a90ebc73847df6ef7325936d4d06b35c8c-1600x900.jpg" style="width: 100%;" /><p>Lassen Sie uns einige dieser Probleme und ihre Lösungen in Spring AI durchgehen.</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;">Problem</td><td style="border: 1px solid #ccc; padding: 4px;">Lösung</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Konsistenz</td><td style="border: 1px solid #ccc; padding: 4px;">Chat-Models sind aufgeschlossen und lassen sich leicht ablenken.</td><td style="border: 1px solid #ccc; padding: 4px;">Sie können ihnen eine Systemvorgabe geben, um ihre Gesamtform und Struktur zu steuern.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Erinnerung</td><td style="border: 1px solid #ccc; padding: 4px;">KI-Modelle haben kein Gedächtnis, daher können sie keine Nachrichten von verschiedenen Nutzern miteinander verknüpfen.</td><td style="border: 1px solid #ccc; padding: 4px;">Sie können ihnen ein Speichersystem zur Verfügung stellen, um die relevanten Teile des Gesprächs zu speichern.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Isolierung</td><td style="border: 1px solid #ccc; padding: 4px;">KI-Modelle leben in isolierten kleinen Umgebungen, aber sie können wirklich Erstaunliches leisten, wenn man ihnen Zugriff auf Werkzeuge gibt – Funktionen, die sie bei Bedarf aufrufen können.</td><td style="border: 1px solid #ccc; padding: 4px;">Spring AI unterstützt Tool Calling, wodurch Sie dem KI-Modell Tools in seiner Umgebung mitteilen können, die es dann aufrufen soll. Diese mehrstufige Interaktion wird vollständig transparent für Sie abgewickelt.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Private Daten</td><td style="border: 1px solid #ccc; padding: 4px;">KI-Modelle sind intelligent, aber nicht allwissend! Sie wissen nicht, was sich in Ihren firmeneigenen Datenbanken befindet – und wir glauben auch nicht, dass Sie das möchten!</td><td style="border: 1px solid #ccc; padding: 4px;">Sie müssen die Antworten der Nutzer durch das Einfügen von Eingabeaufforderungen beeinflussen – im Grunde genommen verwenden Sie den allmächtigen String-Verkettungsoperator, um Text in die Anfrage einzufügen, bevor das Modell die gestellte Frage analysiert. Hintergrundinformationen, wenn Sie möchten. Wie entscheidet man, was verschickt werden soll und was nicht? Verwenden Sie einen Vektorspeicher, um nur die relevanten Daten auszuwählen und diese weiterzuleiten. Dies wird als Retrieval Augmented Generation oder RAG bezeichnet.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Halluzination</td><td style="border: 1px solid #ccc; padding: 4px;">KI-Chatmodelle unterhalten sich gerne! Und manchmal tun sie das so selbstsicher, dass sie sich Dinge ausdenken können.</td><td style="border: 1px solid #ccc; padding: 4px;">Sie müssen eine Evaluierung durchführen – also ein Modell verwenden, um die Ausgabe eines anderen zu validieren –, um plausible Ergebnisse zu bestätigen.</td>
                    </tr>
              </tbody>
            </table>
          <p></p><p>Und natürlich ist keine KI-Anwendung eine Insel. Moderne KI-Systeme und -Dienste funktionieren heutzutage am besten, wenn sie mit anderen Systemen und Diensten integriert sind. <strong><a href="https://modelcontextprotocol.io/introduction">Modellkontextprotokoll</a> </strong>(MCP) ermöglicht es, Ihre KI-Anwendungen mit anderen MCP-basierten Diensten zu verbinden, unabhängig davon, in welcher Sprache sie geschrieben sind. All dies lässt sich in <strong>agentenbasierten </strong>Arbeitsabläufen zusammenfassen, die auf ein übergeordnetes Ziel hinarbeiten.</p><p>Das Beste daran? All dies gelingt Ihnen, indem Sie auf die vertrauten Idiome und Abstraktionen zurückgreifen, die jeder Spring Boot-Entwickler erwartet: Praktische Starter-Abhängigkeiten für praktisch alles sind auf <a href="https://start.spring.io"><strong>Spring Initializr</strong></a>verfügbar<strong>.</strong></p><p>Spring AI bietet komfortable Spring Boot-Autokonfigurationen, die Ihnen das von Ihnen bekannte und erwartete Konvention-vor-Konfigurations-Setup bieten. Und Spring AI unterstützt Observability mit Spring Boot&#x27;s Actuator und dem Micrometer-Projekt. Es harmoniert auch hervorragend mit GraalVM und virtuellen Threads, sodass Sie superschnelle und effiziente KI-Anwendungen erstellen können, die skalierbar sind.</p><h2>Warum Elasticsearch?</h2><p>Elasticsearch ist eine Volltextsuchmaschine, das wissen Sie wahrscheinlich schon. Warum verwenden wir es also für dieses Projekt? Nun ja, es ist <em>auch</em> ein Vektorshop! Und zwar eine ziemlich gute, bei der die Daten direkt neben dem vollständigen Text stehen. Weitere bemerkenswerte Vorteile:</p><ul><li>Super einfach einzurichten</li><li>Opensource</li><li>Horizontal skalierbar</li><li>Die meisten Freitextdaten Ihrer Organisation befinden sich wahrscheinlich bereits in einem Elasticsearch-Cluster.</li><li>Vollständige Suchmaschinenfunktion</li><li>Vollständig <a href="https://docs.spring.io/spring-ai/reference/api/vectordbs/elasticsearch.html">in Spring AI integriert</a>!</li></ul><p>Alles in allem erfüllt Elasticsearch alle Anforderungen an einen hervorragenden Vektorspeicher. Also lasst uns ihn einrichten und mit der Entwicklung unserer Anwendung beginnen!</p><h2>Erste Schritte mit Elasticsearch</h2><p>Wir benötigen sowohl Elasticsearch als auch Kibana, die Benutzeroberfläche, mit der Sie mit den in der Datenbank gespeicherten Daten interagieren werden.</p><p>Dank der praktischen Docker-Images und der <a href="http://elastic.co">Elastic.co-Homepage</a> können Sie alles auf Ihrem lokalen Rechner ausprobieren. Gehe dorthin, scrolle nach unten, um den Befehl <code>curl</code> zu finden, führe ihn aus und leite ihn direkt in deine Shell weiter:</p><pre><code> 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
➜  ~ </code></pre><p>Dadurch werden einfach Docker-Images für Elasticsearch und Kibana heruntergeladen und konfiguriert, und nach wenigen Minuten sind sie auf Ihrem lokalen Rechner einsatzbereit, inklusive der Zugangsdaten.</p><p>Außerdem stehen Ihnen zwei verschiedene URLs zur Verfügung, über die Sie mit Ihrer Elasticsearch-Instanz interagieren können. Folgen Sie den Anweisungen und rufen Sie in Ihrem Browser <a href="http://localhost:5601">http://localhost:5601</a> auf.</p><p>Beachten Sie auch den Benutzernamen <code>elastic</code> und das Passwort, die in der Konsole ausgegeben werden: Sie benötigen diese zum Anmelden (in der obigen Beispielausgabe sind dies <code>elastic</code> bzw. <code>w1GB15uQ</code>).</p><p></p><h2>Die App zusammenführen</h2><p>Rufen Sie die <a href="https://start.spring.io">Spring Initializr-</a> Seite auf und generieren Sie ein neues Spring AI-Projekt mit den folgenden Abhängigkeiten:</p><ul><li><code>Elasticsearch Vector Store</code></li><li><code>Spring Boot Actuator</code></li><li><code>GraalVM</code></li><li><code>OpenAI</code></li><li><code>Web</code></li></ul><p>Achten Sie darauf, die neueste und beste Version von Java zu wählen (idealerweise Java 24 – zum Zeitpunkt der Erstellung dieses Dokuments – oder höher) und das Build-Tool Ihrer Wahl. In diesem Beispiel verwenden wir Apache Maven.</p><p>Klicken Sie auf <code>Generate</code> und entpacken Sie anschließend das Projekt und importieren Sie es in Ihre bevorzugte IDE. (Wir verwenden IntelliJ IDEA.)</p><p>Das Wichtigste zuerst: Legen wir die Verbindungsdetails für Ihre Spring Boot-Anwendung fest. Schreiben Sie in <code>application.properties,</code> Folgendes:</p><pre><code>spring.elasticsearch.uris=http://localhost:9200
spring.elasticsearch.username=elastic
spring.elasticsearch.password=w1GB15uQ</code></pre><p>Wir werden außerdem die Vektorspeicherfunktion von Spring AI nutzen, um alle benötigten Datenstrukturen auf der Elasticsearch-Seite zu initialisieren. Geben Sie dazu Folgendes an:</p><pre><code>spring.ai.vectorstore.elasticsearch.initialize-schema=true</code></pre><p>In dieser Demo verwenden wir <strong>OpenAI</strong> , genauer gesagt das <strong>Embedding Model</strong> und <strong>das Chat Model </strong>(Sie können gerne den Dienst Ihrer Wahl verwenden, solange <a href="https://docs.spring.io/spring-ai/reference/api/embeddings.html#available-implementations">Spring AI ihn unterstützt</a>).</p><p>Das Embedding-Modell wird benötigt, um Einbettungen der Daten zu erstellen, bevor wir sie in Elasticsearch speichern. Damit OpenAI funktioniert, müssen wir <code>API key</code> angeben:</p><pre><code>spring.ai.openai.api-key=...</code></pre><p>Sie können sie als Umgebungsvariable wie <code>SPRING_AI_OPENAI_API_KEY</code> definieren, um zu vermeiden, dass die Anmeldeinformationen in Ihrem Quellcode gespeichert werden.</p><p>Wir werden Dateien hochladen. Stellen Sie daher sicher, dass Sie anpassen, wie viele Daten in den Servlet-Container hochgeladen werden können:</p><pre><code>spring.servlet.multipart.max-file-size=20MB
spring.servlet.multipart.max-request-size=20MB</code></pre><p>Wir haben es fast geschafft! Bevor wir uns ans Programmieren machen, werfen wir einen Blick darauf, wie das Ganze funktionieren wird.</p><p>Auf unserem Rechner haben wir die <a href="https://images-cdn.fantasyflightgames.com/filer_public/9f/aa/9faa23a3-9f71-4c77-865f-bba4aac8a258/runewars-revised-_rulebook.pdf">folgende Datei</a> heruntergeladen (eine Liste der Regeln für ein Brettspiel), sie in <code>test.pdf</code> umbenannt und in <code>~/Downloads/test.pdf</code> abgelegt.</p><p>Die Datei wird an den Endpunkt <code>/rag/ingest</code> gesendet (ersetzen Sie den Pfad entsprechend Ihrer lokalen Konfiguration):</p><pre><code>http --form POST http://localhost:8080/rag/ingest path@/Users/jlong/Downloads/test.pdf</code></pre><p>Das könnte ein paar Sekunden dauern…</p><p>Im Hintergrund werden die Daten an OpenAI gesendet, das daraus Einbettungen erstellt; diese Daten werden dann in Elasticsearch geschrieben, sowohl die Vektoren als auch der Originaltext.</p><p>In diesen Daten und allen darin enthaltenen Einbettungen geschieht die Magie. Anschließend können wir Elasticsearch über die <code>VectorStore</code> -Schnittstelle abfragen.</p><p>Der vollständige Ablauf sieht folgendermaßen aus:</p><ul><li>Der HTTP-Client lädt Ihre ausgewählte PDF-Datei in die Spring-Anwendung hoch.</li><li>Spring AI übernimmt die Textextraktion aus unserem PDF und unterteilt jede Seite in 800 Zeichen lange Abschnitte.</li><li>OpenAI generiert die Vektordarstellung für jeden Chunk.</li><li>Sowohl der segmentierte Text als auch die Einbettung werden anschließend in Elasticsearch gespeichert.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9734adb2d7128e61c515d5855dfad6d3a326a4a1-1454x706.png" style="width: 100%;" /><p>Zum Schluss stellen wir eine Anfrage:</p><pre><code>http :8080/rag/query question=="where do you place the reward card after obtaining it?" </code></pre><p>Und wir erhalten eine relevante Antwort:</p><pre><code>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</code></pre><p>Toll! Wie funktioniert das alles?</p><ul><li>Der HTTP-Client übermittelt die Frage an die Spring-Anwendung.</li><li>Spring AI erhält die Vektordarstellung der Frage von OpenAI.</li><li>Durch diese Einbettung sucht es in den gespeicherten Elasticsearch-Chunks nach ähnlichen Dokumenten und ruft die ähnlichsten Dokumente ab.</li><li>Spring AI sendet dann die Frage und den abgerufenen Kontext an OpenAI, um eine LLM-Antwort zu generieren.</li><li>Schließlich gibt es die generierte Antwort und einen Verweis auf den abgerufenen Kontext zurück.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3799de6e8cb13ce49b9e136cfe593263030231a8-1464x1050.png" style="width: 100%;" /><p>Lasst uns einen Blick in den Java-Code werfen, um zu sehen, wie er wirklich funktioniert.</p><p>Zunächst einmal zur <strong>Main-</strong> Klasse: Es handelt sich um eine standardmäßige Hauptklasse für jede beliebige Spring Boot-Anwendung.</p><pre><code>@SpringBootApplication
public class DemoApplication {
 	public static void main(String[] args) { 
     		SpringApplication.run(DemoApplication.class, args);
 	}
}</code></pre><p>Dort gibt es nichts zu sehen. Weiter geht’s…</p><p>Als nächstes ein einfacher HTTP-Controller:</p><pre><code>@RestController
class RagController {

   private final RagService rag;

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

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

   @GetMapping("/rag/query")
   ResponseEntity<?> query(@RequestParam String question) {
       String response = rag.directRag(question);
       return ResponseEntity.ok().body(response);
   }
}</code></pre><p>Der Controller ruft lediglich einen von uns entwickelten Dienst auf, der Dateien aufnimmt und in den Elasticsearch-Vektorspeicher schreibt und anschließend Abfragen gegen denselben Vektorspeicher ermöglicht.</p><p>Schauen wir uns den Service an:</p><pre><code>@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<Document> batch = new TokenTextSplitter().apply(pdfReader.read());
       vectorStore.add(batch);
   }

  // TBD
}</code></pre><p>Dieser Code übernimmt die gesamte Datenaufnahme: Ausgehend von einem Spring Framework <code>Resource</code>, das ein Container um Bytes ist, lesen wir die PDF-Daten (vermutlich eine <code>.PDF</code> -Datei - stellen Sie sicher, dass Sie dies überprüfen, bevor Sie beliebige Eingaben akzeptieren!) mit Spring AIs <code>PagePdfDocumentReader</code> und tokenisieren sie dann mit Spring AIs <code>TokenTextSplitter</code>. Schließlich fügen wir die resultierenden <code>List&lt;Document&gt;</code>-Objekte der <code>VectorStore</code> -Implementierung <code>ElasticsearchVectorStore</code> hinzu.</p><p>Sie können dies mit Kibana bestätigen: Nachdem Sie eine Datei an den Endpunkt <code>/rag/ingest</code> gesendet haben, öffnen Sie Ihren Browser unter <code>localhost:5601</code> und navigieren Sie im Seitenmenü auf der linken Seite zu <code>Dev Tools</code>. Dort können Sie Abfragen stellen, um mit den Daten in der Elasticsearch-Instanz zu interagieren.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c85e522f02f8b2da7462cd428dc7e952c9692542-1600x1040.png" style="width: 100%;" /><p>Stellen Sie eine Anfrage wie diese:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/00974a176cbce11e70fcab24fb4b3f9c6e205982-1600x1040.png" style="width: 100%;" /><p>Und nun zum spannenden Teil: Wie erhalten wir diese Daten wieder als Antwort auf Benutzeranfragen?</p><p>Hier ist ein erster Entwurf für die Implementierung der Abfrage in einer Methode namens <code>directRag</code>.</p><pre><code>String directRag(String question) {
   // Query the vector store for documents related to the question
   List<Document> 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";

}</code></pre><p>Der Code ist recht einfach, aber wir werden ihn in mehrere Schritte unterteilen:</p><ol><li>Verwenden Sie <code>VectorStore</code> , um eine Ähnlichkeitssuche durchzuführen.</li><li>Ausgehend von allen Ergebnissen werden die zugrunde liegenden Spring AI <code>Document</code>s ermittelt und deren Text extrahiert, der anschließend zu einem einzigen Ergebnis zusammengefügt wird.</li><li>Sende die Ergebnisse von <code>VectorStore</code> an das Modell, zusammen mit einer Aufforderung, die dem Modell erklärt, was damit zu tun ist, und der Frage des Benutzers. Warten Sie die Antwort ab und senden Sie sie zurück.</li></ol><p></p><p>Dies ist <strong>RAG</strong> – Retrieval Augmented Generation. Es geht um die Idee, dass wir Daten aus einem Vektorspeicher verwenden, um die vom Modell durchgeführte Verarbeitung und Analyse zu unterstützen. Jetzt, wo Sie wissen, wie es geht, hoffen wir, dass Sie es nie tun müssen! So jedenfalls nicht: <a href="https://docs.spring.io/spring-ai/reference/api/advisors.html">Die Berater</a> von Spring AI sind dazu da, diesen Prozess noch weiter zu vereinfachen.</p><p>Advisors ermöglicht die Vor- und Nachbearbeitung einer Anfrage an ein bestimmtes Modell und stellt darüber hinaus eine Abstraktionsschicht zwischen Ihrer Anwendung und dem Vektorspeicher bereit. Fügen Sie Ihrem Build die folgende Abhängigkeit hinzu:<br/></p><pre><code><dependency>
   <groupId>org.springframework.ai</groupId>
   <artifactId>spring-ai-advisors-vector-store</artifactId>
</dependency></code></pre><p>Füge der Klasse eine weitere Methode namens <code>advisedRag(String question)</code> hinzu:</p><pre><code>String advisedRag(String question) {
   return this.ai
           .prompt()
           .user(question)
           .advisors(new QuestionAnswerAdvisor(vectorStore))
           .call()
           .content();
}</code></pre><p>Die gesamte RAG-Musterlogik ist in <code>QuestionAnswerAdvisor</code> gekapselt. Alles andere ist genau so, wie jede andere Anfrage an <code>ChatModel</code> wäre! Hübsch!</p><p><a href="https://github.com/xeraa/rag-with-java-springai-elasticsearch">Den vollständigen Code finden Sie auf GitHub</a>.</p><h2>Fazit</h2><p>In dieser Demo verwendeten wir Docker-Images und führten alles auf unserem lokalen Rechner durch, das Ziel hier ist jedoch die Entwicklung produktionsreifer KI-Systeme und -Dienste. Es gibt verschiedene Möglichkeiten, dies zu verwirklichen.</p><p>Zunächst können Sie <a href="https://docs.spring.io/spring-boot/reference/actuator/index.html#actuator">den Spring Boot Actuator</a> hinzufügen, um den Tokenverbrauch zu überwachen. Tokens dienen als Stellvertreter für die Komplexität (und manchmal auch die Kosten in Dollar und Cent) einer bestimmten Anfrage an das Modell.</p><p>Der Spring Boot Actuator befindet sich bereits im Klassenpfad. Geben Sie daher einfach die folgenden Eigenschaften an, um alle Metriken anzuzeigen (die vom großartigen Projekt <a href="http://micrometer.io">Micrometer.io</a> erfasst werden):</p><pre><code>management.endpoints.web.exposure.include=*</code></pre><p>Starten Sie Ihre Anwendung neu. Führe eine Abfrage durch und gehe dann zu: <a href="http://localhost:8080/actuator/metrics">http://localhost:8080/actuator/metrics</a>. Suchen Sie nach „ <code>token</code>“ und Sie erhalten Informationen über die von der Anwendung verwendeten Token. Behalten Sie das bitte im Auge. Sie können natürlich auch die Micrometer- <a href="https://docs.micrometer.io/micrometer/reference/implementations/elastic.html">Integration für Elasticsearch</a> nutzen, um diese Metriken zu übertragen und Elasticsearch als Ihre bevorzugte Zeitreihendatenbank zu verwenden!</p><p>Man sollte dann bedenken, dass wir jedes Mal, wenn wir eine Anfrage an einen Datenspeicher wie Elasticsearch, an OpenAI oder an andere Netzwerkdienste stellen, E/A-Operationen durchführen und dass diese E/A-Operationen häufig die Threads blockieren, auf denen sie ausgeführt werden. Java 21 und spätere Versionen werden mit nicht-blockierenden <strong>virtuellen Threads</strong> ausgeliefert, die die Skalierbarkeit deutlich verbessern. Aktivieren Sie es mit:<br/></p><pre><code>spring.threads.virtual.enabled=true</code></pre><p>Und schließlich sollten Sie Ihre Anwendung und Ihre Daten an einem Ort hosten, an dem sie optimal funktionieren und skalieren können. Sie haben sich sicher schon Gedanken darüber gemacht, wo Sie Ihre Anwendung ausführen möchten, aber wo werden Sie Ihre Daten hosten? Dürfen wir Ihnen die <a href="https://cloud.elastic.co/">Elastic Cloud</a> empfehlen? Es ist sicher, privat, skalierbar und voller Funktionen. Unser Lieblingsteil? Wenn Sie möchten, können Sie die Serverless-Edition wählen, bei der Elastic den Pager trägt, nicht Sie!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Elasticsearch in JavaScript richtig anwenden, Teil II]]></title>
            <description><![CDATA[Erfahren Sie mehr über Best Practices für die Produktion und darüber, wie Sie den Elasticsearch Node.js-Client in serverlosen Umgebungen ausführen können, um Programmierfehler zu vermeiden. ]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/how-to-use-elasticsearch-in-javascript-part-ii</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/how-to-use-elasticsearch-in-javascript-part-ii</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
            <pubDate>Mon, 19 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/70fb0ff949f6db9ac9b8a28ecb4329ab915ebf46-720x420.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Dies ist der zweite Teil unserer Elasticsearch-in-JavaScript-Serie. Im<a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i"> ersten Teil haben wir gelernt,</a> wie wir unsere Umgebung richtig einrichten, den Node.js-Client konfigurieren, Daten indizieren und suchen. Im zweiten Teil lernen wir, wie man Best Practices für die Produktion implementiert und den Elasticsearch <a href="http://node.js">Node.js</a> -Client in serverlosen Umgebungen ausführt.</p><p>Wir werden Folgendes überprüfen:</p><ul><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-ii#production-best-practices">Best Practices für die Produktion</a><ul><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-ii#error-handling">Fehlerbehandlung</a></li><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-ii#testing">Tests</a></li></ul></li><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-ii#serverless-environments">Serverlose Umgebungen</a><ul><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-ii#running-the-client-on-elastic-serverless">Den Client auf Elastic Serverless ausführen</a></li><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-ii#running-the-client-on-function-as-a-service-environment">Ausführen des Clients in einer Function-as-a-Service-Umgebung</a></li></ul></li></ul><p><em>Den Quellcode mit den Beispielen finden Sie <strong><a href="https://github.com/Delacrobix/JS-client-best-practices_article">hier</a>.</strong></em></p><h2>Best Practices für die Produktion</h2><h3>Fehlerbehandlung in Elasticsearch</h3><p>Ein nützliches Feature des Elasticsearch-Clients in Node.js ist, dass er Objekte für die möglichen Fehler in Elasticsearch bereitstellt, sodass man diese auf verschiedene Weise validieren und behandeln kann.</p><p>Um <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/javascript/connecting#client-error-handling">sie alle zu sehen</a>, führen Sie folgenden Befehl aus: </p><pre><code>const { errors } = require('@elastic/elasticsearch')
console.log(errors)</code></pre><p>Kehren wir zum Suchbeispiel zurück und behandeln wir einige der möglichen Fehler:</p><pre><code>app.get("/search/lexic", async (req, res) => {
 ....
  } catch (error) {
    if (error instanceof errors.ResponseError) {
      let errorMessage =
        "Response error!, query malformed or server down, contact the administrator!";

      if (error.body.error.type === "parsing_exception") {
        errorMessage = "Query malformed, make sure mappings are set correctly";
      }

      res.status(error.meta.statusCode).json({
        erroStatus: error.meta.statusCode,
        success: false,
        results: null,
        error: errorMessage,
      });
    }

    res.status(500).json({
      success: false,
      results: null,
      error: error.message,
    });
  }
});</code></pre><p><code>ResponseError</code> Dies tritt insbesondere dann auf, wenn die Antwort <code>4xx</code> oder <code>5xx</code> lautet, was bedeutet, dass die Anfrage fehlerhaft ist oder der Server nicht verfügbar ist.</p><p>Wir können diese Art von Fehler testen, indem wir fehlerhafte Abfragen generieren, beispielsweise den Versuch, <strong>eine Termabfrage für ein Textfeld durchzuführen:</strong></p><p>Standardfehler:</p><pre><code> {
    "success": false,
    "results": null,
    "error": "parsing_exception\n\tRoot causes:\n\t\tparsing_exception: [terms] query does not support [visit_details]"
}</code></pre><p>Benutzerdefinierter Fehler: </p><pre><code>{
    "erroStatus": 400,
    "success": false,
    "results": null,
    "error": "Response error!, query malformed or server down; contact the administrator!"
}</code></pre><p>Wir können auch jeden Fehlertyp auf eine bestimmte Weise erfassen und behandeln. Zum Beispiel können wir in einem <code>TimeoutError</code> eine Wiederholungslogik hinzufügen.</p><pre><code>app.get("/search/semantic", async (req, res) => {
    try {
  ...
  } catch (error) {
    if (error instanceof errors.TimeoutError) {


     // Retry logic...

      res.status(error.meta.statusCode).json({
        erroStatus: error.meta.statusCode,
        success: false,
        results: null,
        error:
          "The request took more than 10s after 3 retries. Try again later.",
      });
    }
  }
});</code></pre><h3>Tests</h3><p>Tests sind entscheidend, um die Stabilität der App zu gewährleisten. Um den Code auf eine von Elasticsearch isolierte Weise zu testen, können wir die Bibliothek <a href="https://github.com/elastic/elasticsearch-js-mock">elasticsearch-js-mock</a> beim Erstellen unseres Clusters verwenden.</p><p>Mithilfe dieser Bibliothek können wir einen Client instanziieren, der dem realen Client sehr ähnlich ist, aber auf unsere Konfiguration reagiert, indem wir lediglich die HTTP-Schicht des Clients durch eine simulierte Schicht ersetzen, während der Rest mit dem Original übereinstimmt.</p><p>Wir werden die Mock-Bibliothek und <a href="https://github.com/avajs/ava">AVA</a> für automatisierte Tests installieren.</p><p><code>npm install @elastic/elasticsearch-mock</code></p><p><code>npm install --save-dev ava</code></p><p>Wir werden die Datei <code>package.json</code> so konfigurieren, dass die Tests ausgeführt werden. Stellen Sie sicher, dass es so aussieht:</p><pre><code>"type": "module",
	"scripts": {
		"test": "ava"
	},
	"devDependencies": {
		"ava": "^5.0.0"
	}</code></pre><p>Erstellen wir nun eine <code>test.js</code> -Datei und installieren wir unseren Mock-Client:</p><pre><code>const { Client } = require('@elastic/elasticsearch')
const Mock = require('@elastic/elasticsearch-mock')

const mock = new Mock()
const client = new Client({
  node: 'http://localhost:9200',
  Connection: mock.getConnection()
})</code></pre><p>Fügen Sie nun ein Mock-Objekt für die semantische Suche hinzu:</p><pre><code>function createSemanticSearchMock(query, indexName) {
  mock.add(
    {
      method: "POST",
      path: `/${indexName}/_search`,
      body: {
        query: {
          semantic: {
            field: "semantic_field",
            query: query,
          },
        },
      },
    },
    () => {
      return {
        hits: {
          total: { value: 2, relation: "eq" },
          hits: [
            {
              _id: "1",
              _score: 0.9,
              _source: {
                owner_name: "Alice Johnson",
                pet_name: "Buddy",
                species: "Dog",
                breed: "Golden Retriever",
                vaccination_history: ["Rabies", "Parvovirus", "Distemper"],
                visit_details:
                  "Annual check-up and nail trimming. Healthy and active.",
              },
            },
            {
              _id: "2",
              _score: 0.7,
              _source: {
                owner_name: "Daniel Kim",
                pet_name: "Mochi",
                species: "Rabbit",
                breed: "Mixed",
                vaccination_history: [],
                visit_details:
                  "Nail trimming and general health check. No issues.",
              },
            },
          ],
        },
      };
    }
  );
}</code></pre><p>Wir können nun einen Test für unseren Code erstellen, um sicherzustellen, dass der Elasticsearch-Teil immer die gleichen Ergebnisse liefert:</p><pre><code>import test from 'ava';

test("performSemanticSearch must return formatted results correctly", async (t) => {
  const indexName = "vet-visits";
  const query = "Which pets had nail trimming?";

  createSemanticSearchMock(query, indexName);

  async function performSemanticSearch(esClient, q, indexName = "vet-visits") {
    try {
      const result = await esClient.search({
        index: indexName,
        body: {
          query: {
            semantic: {
              field: "semantic_field",
              query: q,
            },
          },
        },
      });

      return {
        success: true,
        results: result.hits.hits,
      };
    } catch (error) {
      if (error instanceof errors.TimeoutError) {
        return {
          success: false,
          results: null,
          error: error.body.error.reason,
        };
      }

      return {
        success: false,
        results: null,
        error: error.message,
      };
    }
  }

  const result = await performSemanticSearch(esClient, query, indexName);

  t.true(result.success, "The search must be successful");
  t.true(Array.isArray(result.results), "The results must be an array");

  if (result.results.length > 0) {
    t.true(
      "_source" in result.results[0],
      "Each result must have a _source property"
    );
    t.true(
      "pet_name" in result.results[0]._source,
      "Results must include the pet_name field"
    );
    t.true(
      "visit_details" in result.results[0]._source,
      "Results must include the visit_details field"
    );
  }
});</code></pre><p>Lasst uns die Tests durchführen.</p><p><code>npm run test</code></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/42feae845ae8eae03c37ad7ad114e8db35984812-1186x302.png" style="width: 100%;" /><p>Erledigt! Ab sofort können wir unsere App testen und uns dabei zu 100 % auf den Code konzentrieren, ohne uns von externen Faktoren beeinflussen zu lassen.</p><h2>Serverlose Umgebungen</h2><h3>Wie Sie den Client auf Elastic Serverless ausführen</h3><p>Wir haben die Ausführung von Elasticsearch in der Cloud oder lokal behandelt; der Node.js-Client unterstützt jedoch auch Verbindungen zu <a href="https://www.elastic.co/guide/en/serverless/current/intro.html">Elastic Cloud Serverless</a>.</p><p>Mit Elastic Cloud Serverless können Sie ein Projekt erstellen, bei dem Sie sich keine Gedanken um die Infrastruktur machen müssen, da Elastic dies intern übernimmt. Sie müssen sich nur um die Daten kümmern, die Sie indizieren möchten, und darum, wie lange Sie darauf zugreifen möchten.</p><p>Aus Nutzersicht entkoppelt Serverless Rechenleistung von Speicher und bietet automatische Skalierungsfunktionen sowohl für <a href="https://www.elastic.co/search-labs/blog/elasticsearch-serverless-tier-autoscaling">die Suche</a> als auch <a href="https://www.elastic.co/search-labs/blog/elasticsearch-ingest-autoscaling">für die Indizierung</a>. Dadurch können Sie nur die Ressourcen anbauen, die Sie tatsächlich benötigen.</p><p>Der Client nimmt folgende Anpassungen vor, um eine Verbindung zu Serverless herzustellen:</p><ul><li>Deaktiviert das Sniffing und ignoriert alle Sniffing-bezogenen Optionen.</li><li>Ignoriert alle in der Konfiguration übergebenen Knoten außer dem ersten und ignoriert jegliche Knotenfilter- und Auswahloptionen.</li><li>Aktiviert Komprimierung und `TLSv1_2_method` (genau wie bei der Konfiguration für Elastic Cloud).</li><li>Fügt allen Anfragen einen HTTP-Header `elastic-api-version` hinzu</li><li>Verwendet standardmäßig `CloudConnectionPool` anstelle von `WeightedConnectionPool`.</li><li>Deaktiviert die vom Anbieter bereitgestellten `content-type`- und `accept`-Header zugunsten der Standard-MIME-Typen.</li></ul><p>Um Ihr serverloses Projekt zu verbinden, müssen Sie den Parameter serverMode: serverless verwenden.</p><pre><code>const { Client } = require('@elastic/elasticsearch')
const client = new Client({
  node: 'ELASTICSEARCH_ENDPOINT',
  auth: { apiKey: 'ELASTICSEARCH_API_KEY' },
  serverMode: "serverless",
});</code></pre><h3>Wie Sie den Client in einer Function-as-a-Service-Umgebung ausführen</h3><p>In diesem Beispiel haben wir einen Node.js-Server verwendet, aber Sie können auch über eine Function-as-a-Service-Umgebung mit Funktionen wie AWS Lambda, GCP Run usw. eine Verbindung herstellen.</p><pre><code>'use strict'

const { Client } = require('@elastic/elasticsearch')

const client = new Client({
  // client initialisation
})

exports.handler = async function (event, context) {
  // use the client
}</code></pre><p>Ein weiteres Beispiel ist die Anbindung an Dienste wie Vercel, das ebenfalls serverlos ist. Sie können sich dieses <a href="https://github.com/elastic/elasticsearch-js/blob/main/docs/examples/proxy/README.md">vollständige Beispiel</a> ansehen, wie das geht, aber der relevanteste Teil des <a href="https://github.com/elastic/elasticsearch-js/blob/main/docs/examples/proxy/api/search.js">Suchendpunkts</a> sieht folgendermaßen aus:</p><pre><code>const response = await client.search(
  {
    index: INDEX,
    // You could directly send from the browser
    // the Elasticsearch's query DSL, but it will
    // expose you to the risk that a malicious user
    // could overload your cluster by crafting
    // expensive queries.
    query: {
      match: { field: req.body.text },
    },
  },
  {
    headers: {
      Authorization: `ApiKey ${token}`,
    },
  }
);</code></pre><p>Dieser Endpunkt befindet sich im Ordner /api und wird serverseitig ausgeführt, sodass der Client nur über den Parameter „text“ verfügt, der dem Suchbegriff entspricht.</p><p>Die Konsequenz aus der Verwendung von Function-as-a-Service ist, dass Funktionen, im Gegensatz zu einem Server, der rund um die Uhr läuft, nur die Maschine starten, auf der die Funktion ausgeführt wird, und dass die Maschine nach Beendigung der Funktion in den Ruhemodus wechselt, um weniger Ressourcen zu verbrauchen.</p><p>Diese Konfiguration kann praktisch sein, wenn die Anwendung nicht zu viele Anfragen erhält; andernfalls können die Kosten hoch sein. Sie müssen auch den <a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtime-environment.html">Lebenszyklus der Funktionen</a> und die Laufzeiten berücksichtigen (die in manchen Fällen nur Sekunden betragen können).</p><h2>Fazit</h2><p>In diesem Artikel haben wir gelernt, wie man mit Fehlern umgeht, was in Produktionsumgebungen von entscheidender Bedeutung ist. Wir haben auch das Testen unserer Anwendung unter Verwendung von Mocking für den Elasticsearch-Dienst behandelt. Dies ermöglicht zuverlässige Tests unabhängig vom Zustand des Clusters und erlaubt es uns, uns auf unseren Code zu konzentrieren.</p><p>Zum Schluss demonstrierten wir, wie man einen vollständig serverlosen Stack einrichtet, indem man sowohl Elastic Cloud Serverless als auch eine Vercel-Anwendung bereitstellt.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Wie man den Speicherplatz und die Nutzung von Elasticsearch optimiert]]></title>
            <description><![CDATA[Lernen Sie, wie Sie Fälle vermeiden und behandeln können, in denen die Elasticsearch-Festplatte zu voll ist (Überauslastung) und in denen die Festplattenkapazität nicht ausgelastet ist, um die Clusterkosten zu optimieren.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/optimize-elasticsearch-disk-space-and-usage</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/optimize-elasticsearch-disk-space-and-usage</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Fri, 16 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5d41a078dd03e4480a0ff4e9591c8618b9bab4d0-720x420.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Die Verwaltung von Datenträgern ist in jeder Datenbank wichtig, und Elasticsearch bildet da keine Ausnahme. Wenn nicht genügend Speicherplatz zur Verfügung steht, stellt Elasticsearch die Zuweisung von Shards an den Knoten ein. Dies wird Sie letztendlich daran hindern, Daten in den Cluster zu schreiben, wodurch das Risiko eines Datenverlusts in Ihrer Anwendung entsteht. Wenn Sie hingegen zu viel Speicherplatz haben, bezahlen Sie für mehr Ressourcen, als Sie benötigen.</p><h2>Hintergrundinformationen zu Wasserzeichen</h2><p>In Ihrem Elasticsearch-Cluster gibt es verschiedene „Wasserzeichen“-Schwellenwerte, die Ihnen helfen, den verfügbaren Speicherplatz zu überwachen. Wenn der Speicherplatz auf einem Knoten fast aufgebraucht ist, wird als erstes der Schwellenwert „niedriger Speicherplatz“ überschritten. Der zweite Schwellenwert wird dann der „hohe Festplatten-Wasserzeichen-Schwellenwert“ sein. Schließlich wird das Stadium der „Scheibenflutung“ erreicht. Sobald dieser Schwellenwert überschritten ist, blockiert der Cluster das Schreiben in ALLE Indizes, die einen Shard (primär oder Replikat) auf dem Knoten haben, der den Schwellenwert überschritten hat. Lesevorgänge (Suchanfragen) bleiben weiterhin möglich.</p><h2>Wie man Fälle von zu voller Festplatte (Überauslastung) verhindert und damit umgeht</h2><p>Es gibt verschiedene Methoden, um mit Fällen umzugehen, in denen Ihre Elasticsearch-Festplatte zu voll ist:</p><ol><li><strong>Alte Daten</strong> <strong>löschen</strong> : Daten sollten in der Regel nicht unbegrenzt aufbewahrt werden. Eine Möglichkeit, einer zu vollen Festplatte vorzubeugen und das Problem zu lösen, besteht darin, sicherzustellen, dass Daten, sobald sie ein bestimmtes Alter erreichen, zuverlässig archiviert und gelöscht werden. Eine Möglichkeit hierfür ist die Verwendung <a href="https://www.elastic.co/docs/manage-data/lifecycle/index-lifecycle-management">von ILM</a>.</li><li><strong>Speicherkapazität hinzufügen:</strong> Wenn Sie die Daten nicht löschen können, sollten Sie möglicherweise weitere Datenknoten hinzufügen oder die Festplattengrößen erhöhen, um alle Daten zu erhalten, ohne die Leistung negativ zu beeinflussen. Wenn Sie die Speicherkapazität des Clusters erhöhen müssen, sollten Sie überlegen, ob Sie nur die Speicherkapazität allein oder auch RAM- und CPU-Ressourcen im entsprechenden Verhältnis hinzufügen müssen (siehe Abschnitt zum <a href="https://www.elastic.co/search-labs/blog/optimize-elasticsearch-disk-space-and-usage#the-relationship-between-disk-size,-ram-and-cpu">Verhältnis von Festplattengröße, RAM und CPU</a> weiter unten).</li></ol><h2>So fügen Sie Ihrem Elasticsearch-Cluster Speicherkapazität hinzu</h2><ol><li><strong>Erhöhen Sie die Anzahl der Datenknoten: </strong>Denken Sie daran, dass die neuen Knoten die gleiche Größe wie die vorhandenen Knoten und die gleiche Elasticsearch-Version haben sollten.</li><li><strong>Vergrößerung der vorhandenen Knoten: </strong>In Cloud-basierten Umgebungen ist es in der Regel einfach, die Festplattengröße und den Arbeitsspeicher/die CPU auf vorhandenen Knoten zu erhöhen.</li><li><strong>Erhöhen Sie nur die Festplattengröße: </strong>In Cloud-basierten Umgebungen ist es oft relativ einfach, die Festplattengröße zu erhöhen.</li><li><a href="https://www.elastic.co/docs/deploy-manage/tools/snapshot-and-restore"><strong>Schnappschuss</strong> <strong>Und</strong> <strong>Wiederherstellung</strong></a><strong>:</strong> Wenn Sie es zulassen, dass alte Daten auf Anfrage in einem automatisierten Prozess aus Backups wiederhergestellt werden, können Sie alte Indizes als Snapshots speichern, diese löschen und die Daten auf Anfrage temporär aus den Snapshots wiederherstellen. </li><li><strong>Reduzierung der Replikate pro Shard:</strong> Eine weitere Möglichkeit zur Datenreduzierung besteht darin, die Anzahl der Replikate jedes Shards zu verringern. Für eine hohe Verfügbarkeit ist es wünschenswert, pro Shard eine Replik zu haben. Wenn die Daten jedoch älter werden, kann man unter Umständen auch ohne Replikate arbeiten. Das funktioniert in der Regel, wenn die Daten persistent sind oder Sie über eine Datensicherung verfügen, die Sie bei Bedarf wiederherstellen können.</li><li><strong>Benachrichtigungen erstellen:</strong> Um zu verhindern, dass die Festplatte in Zukunft voll wird und um proaktiv handeln zu können, sollten Sie Benachrichtigungen auf Basis der Festplattennutzung erstellen, die Sie benachrichtigen, wenn die Festplatte sich zu füllen beginnt. </li></ol><h2>Wie man Fälle von unzureichender Festplattenauslastung verhindert und behandelt</h2><p>Wenn Ihre Festplattenkapazität nicht voll ausgelastet ist, gibt es verschiedene Möglichkeiten, das Speichervolumen in Ihrem Cluster zu reduzieren.</p><h3>Wie man das Speichervolumen eines Elasticsearch-Clusters reduziert</h3><p>Es gibt verschiedene Methoden, um das Speichervolumen eines Clusters zu reduzieren.</p><p><strong>1. Die Anzahl der Datenknoten reduzieren</strong></p><p>Wenn Sie den Datenspeicherbedarf reduzieren und gleichzeitig RAM- und CPU-Ressourcen im gleichen Verhältnis einsparen möchten, dann ist dies die einfachste Strategie. Die Stilllegung nicht benötigter Knotenpunkte dürfte die größten Kosteneinsparungen ermöglichen.</p><p>Vor der Außerbetriebnahme des Knotens sollten Sie Folgendes beachten:</p><ul><li>Stellen Sie sicher, dass der außer Betrieb zu nehmende Knoten nicht als MASTER-Knoten benötigt wird. Sie sollten immer mindestens drei Knoten mit der MASTER-Knotenrolle haben.</li><li>Die Datenfragmente müssen von dem außer Betrieb zu nehmenden Knoten migriert werden.</li></ul><p><strong>2. Ersetzen Sie die vorhandenen Knoten durch kleinere Knoten.</strong></p><p>Wenn Sie die Anzahl der Knoten nicht weiter reduzieren können (normalerweise wären 3 die Mindestkonfiguration), dann sollten Sie die vorhandenen Knoten verkleinern. Denken Sie daran, dass es ratsam ist, sicherzustellen, dass alle Datenknoten über den gleichen Arbeitsspeicher und die gleiche Festplattengröße verfügen, da der Shard-Ausgleich auf der Grundlage der Anzahl der Shards pro Knoten erfolgt.</p><p>Der Ablauf wäre wie folgt:</p><ul><li>Fügen Sie dem Cluster neue, kleinere Knoten hinzu.</li><li>Die Shards von den außer Betrieb zu nehmenden Knoten migrieren</li><li>Schalten Sie die alten Knoten ab.</li></ul><p><strong>3. Verringern Sie die Festplattengröße auf den Knoten.</strong></p><p>Wenn Sie NUR die Festplattengröße auf den Knoten reduzieren möchten, ohne den gesamten RAM oder die CPU des Clusters zu verändern, dann können Sie die Festplattengröße für jeden Knoten reduzieren. Die Reduzierung der Festplattengröße auf einem Elasticsearch-Knoten ist kein trivialer Prozess.</p><p>Am einfachsten ginge das in der Regel so:</p><ul><li>Shards vom Knoten migrieren</li><li>Stoppe den Knoten</li><li>Hängen Sie ein neues Datenvolume mit geeigneter Größe an den Knoten an.</li><li>Kopieren Sie alle Daten vom alten Datenträger auf den neuen Datenträger.</li><li>Altes Volume A abtrennen</li><li>Startknoten und Shards zurück zum Knoten migrieren</li></ul><p>Dies setzt voraus, dass auf den anderen Knoten ausreichend Kapazität vorhanden ist, um die zusätzlichen Shards des Knotens während dieses Prozesses vorübergehend zu speichern. In vielen Fällen können die Kosten für die Verwaltung dieses Prozesses die potenziellen Einsparungen bei der Festplattennutzung übersteigen. Aus diesem Grund ist es unter Umständen einfacher, den Knoten komplett durch einen neuen Knoten mit der gewünschten Festplattengröße zu ersetzen (siehe oben „Ersetzen vorhandener Knoten durch kleinere Knoten“).</p><p>Wenn man für unnötige Ressourcen bezahlt, können die Kosten offensichtlich durch eine Optimierung der Ressourcennutzung reduziert werden.</p><h2>Das Verhältnis zwischen Festplattengröße, RAM und CPU</h2><p>Das ideale Verhältnis von Festplattenkapazität zu Arbeitsspeicher in Ihrem Cluster hängt von Ihrem jeweiligen Anwendungsfall ab. Aus diesem Grund sollten Sie bei der Überlegung von Änderungen Ihrer Speicherkapazität auch berücksichtigen, ob Ihre aktuellen Verhältnisse von Festplatte, Arbeitsspeicher und CPU angemessen ausbalanciert sind und ob Sie infolgedessen auch Arbeitsspeicher und CPU im gleichen Verhältnis hinzufügen oder reduzieren müssen.</p><p>Der Bedarf an RAM und CPU hängt vom Umfang der <a href="https://opster.com/guides/elasticsearch/glossary/elasticsearch-indexing/">Indizierungsaktivität</a> , der Anzahl und Art der Abfragen sowie der Menge der zu durchsuchenden und zu aggregierenden Daten ab. Dies steht oft im Verhältnis zur Menge der auf dem Cluster gespeicherten Daten und sollte daher auch mit der Festplattengröße in Zusammenhang stehen.</p><p>Das Verhältnis zwischen Festplattenkapazität und Arbeitsspeicher kann je nach Anwendungsfall variieren. Hier einige Beispiele:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;">Indexaktivität</td><td style="border: 1px solid #ccc; padding: 4px;">Aufbewahrung</td><td style="border: 1px solid #ccc; padding: 4px;">Suchaktivität</td><td style="border: 1px solid #ccc; padding: 4px;">Festplattenkapazität</td><td style="border: 1px solid #ccc; padding: 4px;">RAM</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Unternehmenssuch-App</td><td style="border: 1px solid #ccc; padding: 4px;">Mäßige Holzaufnahme</td><td style="border: 1px solid #ccc; padding: 4px;">Lang</td><td style="border: 1px solid #ccc; padding: 4px;">Licht</td><td style="border: 1px solid #ccc; padding: 4px;">2 TB</td><td style="border: 1px solid #ccc; padding: 4px;">32 GB</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">App-Überwachung</td><td style="border: 1px solid #ccc; padding: 4px;">Intensive Holzverarbeitung</td><td style="border: 1px solid #ccc; padding: 4px;">Kurz</td><td style="border: 1px solid #ccc; padding: 4px;">Licht</td><td style="border: 1px solid #ccc; padding: 4px;">1 TB</td><td style="border: 1px solid #ccc; padding: 4px;">32 GB</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">E-Commerce</td><td style="border: 1px solid #ccc; padding: 4px;">Light-Datenindexierung</td><td style="border: 1px solid #ccc; padding: 4px;">Unbestimmt</td><td style="border: 1px solid #ccc; padding: 4px;">Schwer</td><td style="border: 1px solid #ccc; padding: 4px;">500 GB</td><td style="border: 1px solid #ccc; padding: 4px;">32 GB</td>
                    </tr>
              </tbody>
            </table>
          <p><em>Denken Sie daran, dass Änderungen an der Konfiguration von Node-Maschinen mit Vorsicht vorgenommen werden müssen, da dies zu Ausfallzeiten der Nodes führen kann und Sie sicherstellen müssen, dass Shards nicht auf Ihre anderen, bereits überlasteten Nodes migriert werden.</em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Elasticsearch in JavaScript richtig anwenden, Teil 1]]></title>
            <description><![CDATA[Hier wird erklärt, wie man ein produktionsreifes Elasticsearch-Backend in JavaScript erstellt.  

Erkunden Sie, wie Sie Elasticsearch mit JavaScript verwenden, um einen Server mit verschiedenen Such-Endpunkten zu erstellen, der Elasticsearch-Dokumente gemäß den Best Practices für Client/Server abfragt.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/how-to-use-elasticsearch-in-javascript-part-i</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/how-to-use-elasticsearch-in-javascript-part-i</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
            <pubDate>Thu, 15 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/72200540ed258779d87e53a72ea189f8a138540c-1600x901.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Dies ist der erste Artikel einer Reihe, die die Verwendung von Elasticsearch mit JavaScript behandelt. In dieser Reihe lernen Sie die Grundlagen der Verwendung von Elasticsearch in einer JavaScript-Umgebung kennen und erhalten einen Überblick über die wichtigsten Funktionen und Best Practices zur Erstellung einer Such-App. Am Ende dieses Kurses werden Sie alles wissen, was Sie benötigen, um Elasticsearch mit JavaScript auszuführen.</p><p>In diesem ersten Teil werden wir Folgendes besprechen:</p><ul><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i#environment">Umfeld</a><ul><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i#frontend,-backend,-or-serverless?">Frontend, Backend oder serverlos?</a></li><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i#connecting-the-client">Verbindung des Clients</a></li></ul></li><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i#indexing-documents">Dokumente indizieren</a><ul><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i#elasticsearch-client">Elasticsearch-Client</a></li><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i#semantic-mappings">Semantische Zuordnungen</a></li><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i#bulk-helper">Schüttguthelfer</a></li></ul></li><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i#searching-data">Datensuche</a><ul><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i#lexical-query-(/search/lexic?q=%3Cquery-term%3E)">Lexikalische Abfrage</a></li><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i#semantic-query-(/search/semantic?q=%3Cquery-term%3E)">Semantische Anfrage</a></li><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i#hybrid-query-(/search/hybrid?q=%3Cquery-term%3E)">Hybridabfrage</a></li></ul></li></ul><p><em>Den Quellcode mit den Beispielen finden Sie <strong><a href="https://github.com/Delacrobix/JS-client-best-practices_article">hier</a>.</strong></em></p><h3>Was ist der Elasticsearch Node.js-Client?</h3><p>Der <a href="https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/index.html">Elasticsearch Node.js-Client</a> ist eine JavaScript-Bibliothek, die die HTTP-REST-Aufrufe der Elasticsearch-API in JavaScript umsetzt. Dadurch wird die Handhabung einfacher und es stehen Hilfsfunktionen zur Verfügung, die Aufgaben wie das Indizieren von Dokumenten in Stapeln vereinfachen.</p><h2>Umfeld</h2><h3>Frontend, Backend oder serverlos?</h3><p>Um unsere Such-App mit dem JavaScript-Client zu erstellen, benötigen wir mindestens zwei Komponenten: einen Elasticsearch-Cluster und eine JavaScript-Laufzeitumgebung zum Ausführen des Clients.</p><p>Der JavaScript-Client unterstützt alle Elasticsearch-Lösungen (Cloud, On-Premise und Serverless), und es gibt keine wesentlichen Unterschiede zwischen ihnen, da der Client alle Varianten intern handhabt, sodass Sie sich keine Gedanken darüber machen müssen, welche Sie verwenden sollen.</p><p>Die JavaScript-Laufzeitumgebung muss jedoch vom <strong>Server</strong> und <strong>nicht direkt vom Browser ausgeführt werden.</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/92ce6cfd923c8008fa44f617a58193642d9d5879-661x410.png" style="width: 100%;" /><p>Dies liegt daran, dass beim Aufruf von Elasticsearch über den Browser sensible Informationen wie der Cluster-API-Schlüssel, der Host oder die Abfrage selbst abgerufen werden können. Elasticsearch empfiehlt, <strong>den Cluster niemals direkt dem Internet auszusetzen </strong>und stattdessen eine Zwischenschicht zu verwenden, die all diese Informationen abstrahiert, sodass der Benutzer nur die Parameter sehen kann. <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/es-security-principles.html#security-protect-cluster-traffic">Hier</a> können Sie mehr zu diesem Thema lesen.</p><p>Wir schlagen vor, folgendes Schema zu verwenden:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a08769f08ec73fe57bf2e961cfdfbb1cdd57919d-972x429.png" style="width: 100%;" /><p>In diesem Fall sendet der Client lediglich die Suchbegriffe und einen Authentifizierungsschlüssel an Ihren Server, während Ihr Server die vollständige Kontrolle über die Abfrage und die Kommunikation mit Elasticsearch behält.</p><h3>Verbindung des Clients</h3><p>Erstellen Sie zunächst einen API-Schlüssel gemäß <a href="https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud">diesen Schritten</a>.</p><p>Im Anschluss an das vorherige Beispiel erstellen wir einen einfachen Express-Server und stellen über einen Client von einem Node.JS-Server aus eine Verbindung zu diesem her.</p><p>Wir werden das Projekt mit NPM initialisieren und den Elasticsearch-Client sowie <a href="https://expressjs.com/">Express installieren.</a> Letzteres ist eine Bibliothek zum Starten von Servern in Node.js. Mit Express können wir über HTTP mit unserem Backend interagieren.</p><p>Lasst uns das Projekt initialisieren:</p><p><code>npm init -y</code></p><p>Abhängigkeiten installieren:</p><p><code>npm install @elastic/elasticsearch express split2 dotenv</code></p><p>Ich erkläre es Ihnen genauer:</p><ul><li><a href="https://www.npmjs.com/package/@elastic/elasticsearch"><strong><em>@elastic/elasticsearch</em></strong></a>: Es handelt sich um den offiziellen Node.js-Client.</li><li><a href="https://www.npmjs.com/package/express"><strong><em>Express</em></strong></a>: Damit können wir einen schlanken Node.js-Server starten, um Elasticsearch bereitzustellen.</li><li><a href="https://www.npmjs.com/package/split2"><strong><em>split2</em></strong></a>: Teilt Textzeilen in einen Datenstrom auf. Nützlich, um unsere ndjson-Dateien zeilenweise zu verarbeiten.</li><li><a href="https://www.npmjs.com/package/dotenv"><strong><em>dotenv</em></strong></a>: Ermöglicht die Verwaltung von Umgebungsvariablen mithilfe einer .env-Datei. Datei</li></ul><p>Erstelle eine .env-Datei Fügen Sie in der Datei im Stammverzeichnis des Projekts die folgenden Zeilen hinzu:</p><pre><code>ELASTICSEARCH_ENDPOINT="Your Elasticsearch endpoint"
ELASTICSEARCH_API_KEY="Your Elasticssearch API"</code></pre><p>Auf diese Weise können wir diese Variablen mithilfe des Pakets <code>dotenv</code> importieren.</p><p>Erstelle eine <code>server.js</code> -Datei:</p><pre><code>const express = require("express");
const bodyParser = require("body-parser");
const { Client } = require("@elastic/elasticsearch");
 
require("dotenv").config(); //environment variables setup

const ELASTICSEARCH_ENDPOINT = process.env.ELASTICSEARCH_ENDPOINT;
const ELASTICSEARCH_API_KEY = process.env.ELASTICSEARCH_API_KEY;
const PORT = 3000;


const app = express();

app.listen(PORT, () => {
  console.log("Server running on port", PORT);
});
app.use(bodyParser.json());


let esClient = new Client({
  node: ELASTICSEARCH_ENDPOINT,
  auth: { apiKey: ELASTICSEARCH_API_KEY },  
});

app.get("/ping", async (req, res) => {
  try {
    const result = await esClient.info();

    res.status(200).json({
      success: true,
      clusterInfo: result,
    });
  } catch (error) {
    console.error("Error getting Elasticsearch info:", error);

    res.status(500).json({
      success: false,
      clusterInfo: null,
      error: error.message,
    });
  }
});</code></pre><p>Dieser Code richtet einen einfachen Express.js-Server ein, der auf Port 3000 lauscht und sich über einen API-Schlüssel zur Authentifizierung mit einem Elasticsearch-Cluster verbindet. Es beinhaltet einen /ping-Endpunkt, der bei einem GET-Aufruf mithilfe der <code>.info()</code> -Methode des Elasticsearch-Clients grundlegende Informationen vom Elasticsearch-Cluster abfragt. </p><p>Bei erfolgreicher Abfrage werden die Clusterinformationen im JSON-Format zurückgegeben; andernfalls wird eine Fehlermeldung zurückgegeben. Der Server verwendet außerdem die Middleware body-parser, um JSON-Anfragetexte zu verarbeiten.</p><p>Führen Sie die Datei aus, um den Server zu starten:</p><p><code>node server.js</code></p><p>Die Antwort sollte folgendermaßen aussehen:</p><pre><code>Server running on port 3000</code></pre><p>Und nun konsultieren wir den Endpunkt <code>/ping</code> , um den Status unseres Elasticsearch-Clusters zu überprüfen.</p><pre><code>curl http://localhost:3000/ping
{
    "success": true,
    "clusterInfo": {
        "name": "instance-0000000000",
        "cluster_name": "61b7e19eec204d59855f5e019acd2689",
        "cluster_uuid": "BIfvfLM0RJWRK_bDCY5ldg",
        "version": {
            "number": "9.0.0",
            "build_flavor": "default",
            "build_type": "docker",
            "build_hash": "112859b85d50de2a7e63f73c8fc70b99eea24291",
            "build_date": "2025-04-08T15:13:46.049795831Z",
            "build_snapshot": false,
            "lucene_version": "10.1.0",
            "minimum_wire_compatibility_version": "8.18.0",
            "minimum_index_compatibility_version": "8.0.0"
        },
        "tagline": "You Know, for Search"
    }
}</code></pre><h2>Dokumente indizieren</h2><p>Sobald die Verbindung hergestellt ist, können wir Dokumente mithilfe von Zuordnungen wie <a href="https://www.elastic.co/search-labs/blog/semantic-search-simplified-semantic-text">semantic_text</a> für die semantische Suche und text für Volltextabfragen indizieren. Mit diesen beiden Feldtypen können wir auch <a href="https://www.elastic.co/what-is/hybrid-search">eine hybride Suche</a> durchführen.</p><p>Wir erstellen eine neue <code>load.js</code> -Datei, um die Zuordnungen zu generieren und die Dokumente hochzuladen.</p><h3>Elasticsearch-Client</h3><p>Zuerst müssen wir den Client instanziieren und authentifizieren:</p><pre><code>const { Client } = require("@elastic/elasticsearch");

const ELASTICSEARCH_ENDPOINT = "cluster/project_endpoint";
const ELASTICSEARCH_API_KEY = "apiKey";

const esClient = new Client({
  node: ELASTICSEARCH_ENDPOINT,
  auth: { apiKey: ELASTICSEARCH_API_KEY },
});</code></pre><h3>Semantische Zuordnungen</h3><p>Wir erstellen einen Index mit Daten über Tierkliniken. Wir speichern die Informationen über den Besitzer, das Haustier und die Details des Besuchs.</p><p>Die Daten, in denen wir eine Volltextsuche durchführen möchten, wie zum Beispiel Namen und Beschreibungen, werden als Text gespeichert. Die Daten aus Kategorien, wie zum Beispiel die Tierart oder -rasse, werden als Schlüsselwörter gespeichert.</p><p>Zusätzlich kopieren wir die Werte aller Felder in ein semantisches Textfeld, um auch mit diesen Informationen eine semantische Suche durchführen zu können.</p><pre><code>const INDEX_NAME = "vet-visits";

const createMappings = async (indexName, mapping) => {
  try {
    const body = await esClient.indices.create({
      index: indexName,
      body: {
        mappings: mapping,
      },
    });

    console.log("Index created successfully:", body);
  } catch (error) {
    console.error("Error creating mapping:", error);
  }
};

await createMappings(INDEX_NAME, {
  properties: {
    owner_name: {
      type: "text",
      copy_to: "semantic_field",
    },
    pet_name: {
      type: "text",
      copy_to: "semantic_field",
    },
    species: {
      type: "keyword",
      copy_to: "semantic_field",
    },
    breed: {
      type: "keyword",
      copy_to: "semantic_field",
    },
    vaccination_history: {
      type: "keyword",
      copy_to: "semantic_field",
    },
    visit_details: {
      type: "text",
      copy_to: "semantic_field",
    },
    semantic_field: {
      type: "semantic_text",
    },
  },
});</code></pre><h3>Schüttguthelfer</h3><p>Ein weiterer Vorteil des Clients besteht darin, dass wir den <a href="https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/client-helpers.html#bulk-helper">Bulk-Helper</a> verwenden können, um die Indizierung in Batches durchzuführen. Der Bulk-Helper ermöglicht es uns, Dinge wie Parallelverarbeitung, Wiederholungsversuche und die Behandlung jedes Dokuments, das die Funktion durchläuft und erfolgreich oder fehlschlägt, einfach zu handhaben.</p><p>Ein attraktives Merkmal dieses Helfers ist, dass man mit Datenströmen arbeiten kann. Mit dieser Funktion können Sie eine Datei zeilenweise senden, anstatt die gesamte Datei im Speicher zu speichern und sie dann auf einmal an Elasticsearch zu senden.</p><p>Um die Daten in Elasticsearch hochzuladen, erstellen Sie eine Datei namens data.ndjson im Stammverzeichnis des Projekts und fügen Sie die folgenden Informationen hinzu (alternativ können Sie die Datei mit dem Datensatz <a href="https://github.com/Delacrobix/JS-client-best-practices_article/blob/main/data.ndjson">hier</a> herunterladen):</p><pre><code>{"owner_name":"Alice Johnson","pet_name":"Buddy","species":"Dog","breed":"Golden Retriever","vaccination_history":["Rabies","Parvovirus","Distemper"],"visit_details":"Annual check-up and nail trimming. Healthy and active."}
{"owner_name":"Marco Rivera","pet_name":"Milo","species":"Cat","breed":"Siamese","vaccination_history":["Rabies","Feline Leukemia"],"visit_details":"Slight eye irritation, prescribed eye drops."}
{"owner_name":"Sandra Lee","pet_name":"Pickles","species":"Guinea Pig","breed":"Mixed","vaccination_history":[],"visit_details":"Loss of appetite, recommended dietary changes."}
{"owner_name":"Jake Thompson","pet_name":"Luna","species":"Dog","breed":"Labrador Mix","vaccination_history":["Rabies","Bordetella"],"visit_details":"Mild ear infection, cleaning and antibiotics given."}
{"owner_name":"Emily Chen","pet_name":"Ziggy","species":"Cat","breed":"Mixed","vaccination_history":["Rabies","Feline Calicivirus"],"visit_details":"Vaccination update and routine physical."}
{"owner_name":"Tomás Herrera","pet_name":"Rex","species":"Dog","breed":"German Shepherd","vaccination_history":["Rabies","Parvovirus","Leptospirosis"],"visit_details":"Follow-up for previous leg strain, improving well."}
{"owner_name":"Nina Park","pet_name":"Coco","species":"Ferret","breed":"Mixed","vaccination_history":["Rabies"],"visit_details":"Slight weight loss; advised new diet."}
{"owner_name":"Leo Martínez","pet_name":"Simba","species":"Cat","breed":"Maine Coon","vaccination_history":["Rabies","Feline Panleukopenia"],"visit_details":"Dental cleaning. Minor tartar buildup removed."}
{"owner_name":"Rachel Green","pet_name":"Rocky","species":"Dog","breed":"Bulldog Mix","vaccination_history":["Rabies","Parvovirus"],"visit_details":"Skin rash, antihistamines prescribed."}
{"owner_name":"Daniel Kim","pet_name":"Mochi","species":"Rabbit","breed":"Mixed","vaccination_history":[],"visit_details":"Nail trimming and general health check. No issues."}</code></pre><p>Wir verwenden split2, um die Dateizeilen zu streamen, während der Bulk-Helper sie an Elasticsearch sendet.</p><pre><code>const { createReadStream } = require("fs");
const split = require("split2");
 
const indexData = async (filePath, indexName) => {
  try {
    console.log(`Indexing data from ${filePath} into ${indexName}...`);

    const result = await esClient.helpers.bulk({
      datasource: createReadStream(filePath).pipe(split()),

      onDocument: () => {
        return {
          index: { _index: indexName },
        };
      },
      onDrop(doc) {
        console.error("Error processing document:", doc);
      },
    });

    console.log("Bulk indexing successful elements:", result.items.length);
  } catch (error) {
    console.error("Error indexing data:", error);
    throw error;
  }
};

await indexData("./data.ndjson", INDEX_NAME);</code></pre><p>Der obige Code liest eine .ndjson-Datei. Mit der Methode <code>helpers.bulk</code> werden alle JSON-Objekte zeilenweise und in einem Massenindex in einen angegebenen Elasticsearch-Index indiziert. Es streamt die Datei mit <code>createReadStream</code> und <code>split2</code>, richtet Indexierungsmetadaten für jedes Dokument ein und protokolliert alle Dokumente, deren Verarbeitung fehlschlägt. Nach Abschluss des Vorgangs wird die Anzahl der erfolgreich indizierten Elemente protokolliert.</p><p>Alternativ zur <code>indexData</code> -Funktion können Sie die Datei auch direkt über die Benutzeroberfläche von Kibana hochladen und dabei die <a href="https://www.elastic.co/docs/manage-data/ingest/upload-data-files">Funktion „Datendateien hochladen“ verwenden.</a></p><p>Wir führen die Datei aus, um die Dokumente in unseren Elasticsearch-Cluster hochzuladen.</p><p><code>node load.js</code></p><pre><code>Creating mappings for index vet-visits...
Index created successfully: { acknowledged: true, shards_acknowledged: true, index: 'vet-visits' }
Indexing data from ./data.ndjson into vet-visits...
Bulk indexing completed. Total documents: 10, Failed: 0</code></pre><h2>Datensuche in Elasticsearch</h2><p>Wir kehren zu unserer <code>server.js</code> -Datei zurück und erstellen verschiedene Endpunkte, um lexikalische, semantische oder hybride Suchen durchzuführen.</p><p>Kurz gesagt, schließen sich diese Sucharten nicht gegenseitig aus, sondern hängen von der Art der Frage ab, die Sie beantworten möchten.</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Abfragetyp</td><td style="border: 1px solid #ccc; padding: 4px;">Anwendungsfall</td><td style="border: 1px solid #ccc; padding: 4px;">Beispielaufgabe</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Lexikalische Abfrage</td><td style="border: 1px solid #ccc; padding: 4px;">Die in der Frage vorkommenden Wörter oder Wortstämme tauchen wahrscheinlich in den Indexdokumenten auf. Tokenische Ähnlichkeit zwischen Frage und Dokumenten.</td><td style="border: 1px solid #ccc; padding: 4px;">Ich suche ein blaues Sport-T-Shirt.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Semantische Anfrage</td><td style="border: 1px solid #ccc; padding: 4px;">Die in der Frage enthaltenen Wörter werden in den Dokumenten wahrscheinlich nicht vorkommen. Konzeptionelle Ähnlichkeit zwischen Frage und Dokumenten.</td><td style="border: 1px solid #ccc; padding: 4px;">Ich suche Kleidung für kaltes Wetter.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Hybrid Search</td><td style="border: 1px solid #ccc; padding: 4px;">Die Frage enthält lexikalische und/oder semantische Komponenten. Token- und semantische Ähnlichkeit zwischen Frage und Dokumenten.</td><td style="border: 1px solid #ccc; padding: 4px;">Ich suche ein Kleid in Größe S für eine Strandhochzeit.</td>
                    </tr>
              </tbody>
            </table>
          <p>Die <strong><em>lexikalischen </em></strong>Bestandteile der Frage sind wahrscheinlich Teil von Titeln und Beschreibungen oder Kategorienamen, während die <strong><em>semantischen </em></strong>Bestandteile Konzepte sind, die mit diesen Bereichen in Zusammenhang stehen. <strong><em>Blau</em></strong> wird wahrscheinlich ein Kategoriename oder Teil einer Beschreibung sein, <strong><em>Strandhochzeit</em></strong> hingegen eher nicht, kann aber semantisch mit Leinenkleidung in Verbindung gebracht werden.</p><h3>Lexikalische Abfrage (/search/lexic?q=)&lt;query_term&gt;</h3><p>Die lexikalische Suche, auch Volltextsuche genannt, bedeutet die Suche auf der Grundlage der Ähnlichkeit von Wörtern; das heißt, nach einer Analyse werden die Dokumente zurückgegeben, die die gesuchten Wörter enthalten.</p><p><a href="https://www.elastic.co/demo-gallery/lexical-search">Hier</a> finden Sie unser praktisches Tutorial zur lexikalischen Suche.</p><pre><code>app.get("/search/lexic", async (req, res) => {
  const { q } = req.query;

  const INDEX_NAME = "vet-visits";

  try {
    const result = await esClient.search({
      index: INDEX_NAME,
      size: 5,
      body: {
        query: {
          multi_match: {
            query: q,
            fields: ["owner_name", "pet_name", "visit_details"],
          },
        },
      },
    });

    res.status(200).json({
      success: true,
      results: result.hits.hits
    });
  } catch (error) {
    console.error("Error performing search:", error);

    res.status(500).json({
      success: false,
      results: null,
      error: error.message,
    });
  }
});</code></pre><p>Wir testen mit: <strong><em>Krallenschneiden</em></strong></p><pre><code>curl http://localhost:3000/search/lexic?q=nail%20trimming</code></pre><p>Antwort:</p><pre><code>{
    "success": true,
    "results": [
        {
            "_index": "vet-visits",
            "_id": "-RY6RJYBLe2GoFQ6-9n9",
            "_score": 2.7075968,
            "_source": {
                "pet_name": "Mochi",
                "owner_name": "Daniel Kim",
                "species": "Rabbit",
                "visit_details": "Nail trimming and general health check. No issues.",
                "breed": "Mixed",
                "vaccination_history": []
            }
        },
        {
            "_index": "vet-visits",
            "_id": "8BY6RJYBLe2GoFQ6-9n9",
            "_score": 2.560356,
            "_source": {
                "pet_name": "Buddy",
                "owner_name": "Alice Johnson",
                "species": "Dog",
                "visit_details": "Annual check-up and nail trimming. Healthy and active.",
                "breed": "Golden Retriever",
                "vaccination_history": [
                    "Rabies",
                    "Parvovirus",
                    "Distemper"
                ]
            }
        }
    ]
}</code></pre><h3>Semantische Abfrage (/search/semantic?q=)&lt;query_term&gt;</h3><p>Die semantische Suche findet, anders als die lexikalische Suche, Ergebnisse, die der Bedeutung der Suchbegriffe ähneln, durch Vektorsuche.</p><p><a href="https://www.elastic.co/demo-gallery/semantic-search">Hier</a> finden Sie unser praktisches Tutorial zur semantischen Suche.</p><pre><code>app.get("/search/semantic", async (req, res) => {
  const { q } = req.query;

  const INDEX_NAME = "vet-visits";

  try {
    const result = await esClient.search({
      index: INDEX_NAME,
      size: 5,
      body: {
        query: {
          semantic: {
            field: "semantic_field",
            query: q
          },
        },
      },
    });

    res.status(200).json({
      success: true,
      results: result.hits.hits,
    });
  } catch (error) {
    console.error("Error performing search:", error);

    res.status(500).json({
      success: false,
      results: null,
      error: error.message,
    });
  }
});</code></pre><p>Wir testen mit: <strong><em>Wer hat sich eine Pediküre machen lassen?</em></strong></p><pre><code>curl http://localhost:3000/search/semantic?q=Who%20got%20a%20pedicure?</code></pre><p>Antwort:</p><pre><code>{
    "success": true,
    "results": [
        {
            "_index": "vet-visits",
            "_id": "-RY6RJYBLe2GoFQ6-9n9",
            "_score": 4.861466,
            "_source": {
                "owner_name": "Daniel Kim",
                "pet_name": "Mochi",
                "species": "Rabbit",
                "breed": "Mixed",
                "vaccination_history": [],
                "visit_details": "Nail trimming and general health check. No issues."
            }
        },
        {
            "_index": "vet-visits",
            "_id": "8BY6RJYBLe2GoFQ6-9n9",
            "_score": 4.7152824,
            "_source": {
                "pet_name": "Buddy",
                "owner_name": "Alice Johnson",
                "species": "Dog",
                "visit_details": "Annual check-up and nail trimming. Healthy and active.",
                "breed": "Golden Retriever",
                "vaccination_history": [
                    "Rabies",
                    "Parvovirus",
                    "Distemper"
                ]
            }
        },
        {
            "_index": "vet-visits",
            "_id": "9RY6RJYBLe2GoFQ6-9n9",
            "_score": 1.6717153,
            "_source": {
                "pet_name": "Rex",
                "owner_name": "Tomás Herrera",
                "species": "Dog",
                "visit_details": "Follow-up for previous leg strain, improving well.",
                "breed": "German Shepherd",
                "vaccination_history": [
                    "Rabies",
                    "Parvovirus",
                    "Leptospirosis"
                ]
            }
        },
        {
            "_index": "vet-visits",
            "_id": "9xY6RJYBLe2GoFQ6-9n9",
            "_score": 1.5600781,
            "_source": {
                "pet_name": "Simba",
                "owner_name": "Leo Martínez",
                "species": "Cat",
                "visit_details": "Dental cleaning. Minor tartar buildup removed.",
                "breed": "Maine Coon",
                "vaccination_history": [
                    "Rabies",
                    "Feline Panleukopenia"
                ]
            }
        },
        {
            "_index": "vet-visits",
            "_id": "-BY6RJYBLe2GoFQ6-9n9",
            "_score": 1.2696637,
            "_source": {
                "pet_name": "Rocky",
                "owner_name": "Rachel Green",
                "species": "Dog",
                "visit_details": "Skin rash, antihistamines prescribed.",
                "breed": "Bulldog Mix",
                "vaccination_history": [
                    "Rabies",
                    "Parvovirus"
                ]
            }
        }
    ]
}</code></pre><h3>Hybrid-Suchanfrage (/search/hybrid?q=)&lt;query_term&gt;</h3><p>Die Hybridsuche ermöglicht es uns, semantische und lexikalische Suche zu kombinieren und so das Beste aus beiden Welten zu erhalten: Sie erhalten die Präzision der Token-Suche zusammen mit der Bedeutungsnähe der semantischen Suche.</p><pre><code>app.get("/search/hybrid", async (req, res) => {
  const { q } = req.query;

  const INDEX_NAME = "vet-visits";

  try {
    const result = await esClient.search({
      index: INDEX_NAME,
      body: {
        retriever: {
          rrf: {
            retrievers: [
              {
                standard: {
                  query: {
                    bool: {
                      must: {
                         multi_match: {
             query: q,
            fields: ["owner_name", "pet_name", "visit_details"],
          },
                      },
                    },
                  },
                },
              },
              {
                standard: {
                  query: {
                    bool: {
                      must: {
                        semantic: {
                          field: "semantic_field",
                          query: q,
                        },
                      },
                    },
                  },
                },
              },
            ],
          },
        },
        size: 5,
      },
    });

    res.status(200).json({
      success: true,
      results: result.hits.hits,
    });
  } catch (error) {
    console.error("Error performing search:", error);

    res.status(500).json({
      success: false,
      results: null,
      error: error.message,
    });
  }
});</code></pre><p>Wir testen mit der Frage: „ <strong><em>Wer hat sich eine Pediküre oder eine Zahnbehandlung gegönnt?“</em></strong></p><pre><code>curl http://localhost:3000/search/hybrid?q=who%20got%20a%20pedicure%20or%20dental%20treatment</code></pre><p>Abwehr:</p><pre><code>{
    "success": true,
    "results": [
        {
            "_index": "vet-visits",
            "_id": "9xY6RJYBLe2GoFQ6-9n9",
            "_score": 0.032522473,
            "_source": {
                "pet_name": "Simba",
                "owner_name": "Leo Martínez",
                "species": "Cat",
                "visit_details": "Dental cleaning. Minor tartar buildup removed.",
                "breed": "Maine Coon",
                "vaccination_history": [
                    "Rabies",
                    "Feline Panleukopenia"
                ]
            }
        },
        {
            "_index": "vet-visits",
            "_id": "-RY6RJYBLe2GoFQ6-9n9",
            "_score": 0.016393442,
            "_source": {
                "pet_name": "Mochi",
                "owner_name": "Daniel Kim",
                "species": "Rabbit",
                "visit_details": "Nail trimming and general health check. No issues.",
                "breed": "Mixed",
                "vaccination_history": []
            }
        },
        {
            "_index": "vet-visits",
            "_id": "8BY6RJYBLe2GoFQ6-9n9",
            "_score": 0.015873017,
            "_source": {
                "pet_name": "Buddy",
                "owner_name": "Alice Johnson",
                "species": "Dog",
                "visit_details": "Annual check-up and nail trimming. Healthy and active.",
                "breed": "Golden Retriever",
                "vaccination_history": [
                    "Rabies",
                    "Parvovirus",
                    "Distemper"
                ]
            }
        },
        {
            "_index": "vet-visits",
            "_id": "9RY6RJYBLe2GoFQ6-9n9",
            "_score": 0.015625,
            "_source": {
                "pet_name": "Rex",
                "owner_name": "Tomás Herrera",
                "species": "Dog",
                "visit_details": "Follow-up for previous leg strain, improving well.",
                "breed": "German Shepherd",
                "vaccination_history": [
                    "Rabies",
                    "Parvovirus",
                    "Leptospirosis"
                ]
            }
        },
        {
            "_index": "vet-visits",
            "_id": "8xY6RJYBLe2GoFQ6-9n9",
            "_score": 0.015384615,
            "_source": {
                "pet_name": "Luna",
                "owner_name": "Jake Thompson",
                "species": "Dog",
                "visit_details": "Mild ear infection, cleaning and antibiotics given.",
                "breed": "Labrador Mix",
                "vaccination_history": [
                    "Rabies",
                    "Bordetella"
                ]
            }
        }
    ]
}</code></pre><h2>Fazit</h2><p>Im ersten Teil unserer Serie haben wir erklärt, wie man die Umgebung einrichtet und einen Server mit verschiedenen Suchendpunkten erstellt, um die Elasticsearch-Dokumente gemäß den Best Practices für Client/Server abzufragen. Schauen Sie sich <a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i">den zweiten Teil</a> unserer Serie an, in dem Sie Best Practices für die Produktion kennenlernen und erfahren, wie Sie den Elasticsearch Node.js-Client in serverlosen Umgebungen ausführen.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Wie konfiguriert man die Anzahl der Replikate in einem Elasticsearch-Index?]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie die number_of_replicas in einem Elasticsearch-Index konfigurieren, um die Suchleistung zu verbessern und eine Widerstandsfähigkeit gegen Node-Ausfälle zu gewährleisten. 
]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/elasticsearch-index-number-of_replicas</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/elasticsearch-index-number-of_replicas</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Wed, 14 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/23b96aaa1a38b1f4747b4a87695d816f24c0cf70-720x421.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Elasticsearch ist als verteiltes System konzipiert, das große Datenmengen verarbeiten und eine hohe Verfügbarkeit gewährleisten kann. Eine der wichtigsten Funktionen, die dies ermöglichen, ist das Konzept der Indexreplikation, das durch die Einstellung <code>number_of_replicas</code> gesteuert wird. Dieser Artikel befasst sich detailliert mit dieser Einstellung, ihren Auswirkungen und wie man sie richtig konfiguriert.</p><h2>Die Rolle von Replikaten in Elasticsearch</h2><p>In Elasticsearch ist ein Index eine Sammlung von Dokumenten, die auf mehrere primäre Shards verteilt sind. Jeder primäre Shard ist ein in sich geschlossener Apache Lucene-Index, und die Dokumente innerhalb eines Index werden auf alle primären Shards verteilt. Um eine hohe Verfügbarkeit und Datenredundanz zu gewährleisten, ermöglicht Elasticsearch, dass jeder Shard eine oder mehrere Kopien, sogenannte Replikate, besitzt.<br/><br/>Die Einstellung <code>number_of_replicas</code> steuert die Anzahl der Replikat-Shards (Kopien), die Elasticsearch für jeden primären Shard in einem Index erstellt. Standardmäßig erstellt Elasticsearch für jeden primären Shard eine Replik, dies kann jedoch je nach den Anforderungen Ihres Systems geändert werden.</p><h2>Konfigurieren der Anzahl der Replikate</h2><p>Die Einstellung <code>number_of_replicas</code> kann bei der Indexerstellung konfiguriert oder später aktualisiert werden. So können Sie dies bei der Indexerstellung festlegen:</p><pre><code>PUT /my_index
{
  "settings": {
    "number_of_replicas": 2
  }
}</code></pre><p>In diesem Beispiel erstellt Elasticsearch zwei Replikate für jeden primären Shard im Index <code>my_index</code> .</p><p>Um die <code>number_of_replicas</code> -Einstellung für einen bestehenden Index zu aktualisieren, können Sie die <code>_settings</code> -API verwenden:</p><pre><code>PUT /my_index/_settings
{
  "number_of_replicas": 3
}</code></pre><p>Dieser Befehl aktualisiert den Index <code>my_index</code> , sodass für jeden primären Shard drei Replikate vorhanden sind.</p><h2>Auswirkungen der Einstellung number_of_replicas</h2><p>Die Einstellung <code>number_of_replicas</code> hat einen erheblichen Einfluss auf die Leistungsfähigkeit und Stabilität Ihres Elasticsearch- <a href="https://opster.com/guides/elasticsearch/glossary/elasticsearch-cluster/">Clusters</a>. Hier einige wichtige Punkte, die Sie beachten sollten:</p><ol><li><strong>Datenredundanz und Verfügbarkeit:</strong> Durch die Erhöhung von <code>number_of_replicas</code> wird die Verfügbarkeit Ihrer Daten verbessert, indem mehr Kopien jedes Shards erstellt werden. Wenn ein Knoten ausfällt, kann Elasticsearch weiterhin Daten von den Replikat-Shards auf den verbleibenden <a href="https://opster.com/guides/elasticsearch/glossary/elasticsearch-node/">Knoten</a> bereitstellen.</li><li><strong>Suchleistung:</strong> Replikat-Shards können Leseanfragen bedienen, daher kann eine größere Anzahl von Replikaten die Suchleistung verbessern, indem die Last auf mehr Shards verteilt wird.</li></ol><div style="display:none">Unknown block type "block", specify a component for it in the `components.types` option</div><ol><li><strong>Speicherbedarf:</strong> Mehr Replikate bedeuten mehr Speicherplatz. Sie sollten sicherstellen, dass Ihr Cluster über genügend Kapazität verfügt, um die zusätzlichen Replikate zu speichern.</li><li><strong>Ausfallsicherheit bei Knoten:</strong> Der <code>number_of_replicas</code> sollte unter Berücksichtigung der Anzahl der Knoten in Ihrem Cluster festgelegt werden. Wenn <code>number_of_replicas</code> gleich oder größer als die Anzahl der Knoten ist, kann Ihr Cluster den Ausfall mehrerer Knoten ohne Datenverlust tolerieren.</li></ol><h2>Bewährte Vorgehensweisen zum Festlegen der Anzahl der Replikate</h2><p>Die optimale Einstellung <code>number_of_replicas</code> hängt von den spezifischen Anforderungen Ihres Systems ab. Hier sind jedoch einige allgemeine Best Practices:</p><ul><li>Bei einem Cluster mit nur einem Knoten sollte <code>number_of_replicas</code> auf 0 gesetzt werden, da keine weiteren Knoten vorhanden sind, die Replikate aufnehmen könnten.</li><li>Bei einem Multi-Node-Cluster sollte <code>number_of_replicas</code> mindestens auf 1 gesetzt werden, um Datenredundanz und hohe Verfügbarkeit zu gewährleisten.</li><li>Wenn die Suchleistung Priorität hat, sollten Sie die Anzahl der Einträge <code>number_of_replicas</code> erhöhen. Man sollte jedoch den Zielkonflikt zwischen Schreibleistung und Speicherbedarf berücksichtigen.</li><li>Stellen Sie stets sicher, dass Ihr Cluster über ausreichend Kapazität zur Speicherung der zusätzlichen Replikate verfügt.</li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Zuordnung von Einbettungen zu Elasticsearch-Feldtypen: semantic_text, dense_vector, sparse_vector]]></title>
            <description><![CDATA[Erörterung, wie und wann semantic_text, dense_vector oder sparse_vector verwendet werden und wie diese mit der Einbettungsgenerierung zusammenhängen.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/mapping-embeddings-to-elasticsearch-field-types</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/mapping-embeddings-to-elasticsearch-field-types</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Tue, 13 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f98fdff837db55b466780c0bae672aa6f6c3a966-1200x628.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Die Verwendung von Einbettungen zur Verbesserung der Relevanz und Genauigkeit der Informationswiedergewinnung hat im Laufe der Jahre deutlich zugenommen. Tools wie Elasticsearch wurden weiterentwickelt, um diese Art von Daten durch spezialisierte Feldtypen wie dichte Vektoren, dünnbesetzte Vektoren und semantischen Text zu unterstützen. Um jedoch gute Ergebnisse zu erzielen, ist es unerlässlich zu verstehen, wie man Embeddings den verfügbaren Elasticsearch-Feldtypen <code>semantic_text</code>, <code>dense_vector</code> und <code>sparse_vector</code> korrekt zuordnet.</p><p>In diesem Artikel werden wir diese Feldtypen, ihre jeweiligen Anwendungsbereiche und ihren Zusammenhang mit Strategien zur Einbettungsgenerierung und -nutzung sowohl bei der Indizierung als auch bei der Abfrage besprechen.</p><h2>Dichter Vektortyp</h2><p>Der Feldtyp <code>dense_vector</code> in Elasticsearch dient zur Speicherung dichter Vektoren, die numerische Darstellungen von Daten wie Text, Bildern und Audio darstellen, wobei nahezu alle Dimensionen relevant sind. Diese Vektoren werden mithilfe von Einbettungsmodellen generiert, die von Plattformen wie OpenAI, Cohere oder Hugging Face bereitgestellt werden, und sind so konzipiert, dass sie die Gesamtsemantik der Daten erfassen, auch wenn diese keine exakten Begriffe mit anderen Dokumenten teilen.</p><p>In Elasticsearch können dichte Vektoren je nach verwendetem Modell bis zu 4096 Dimensionen haben. Das Modell all-MiniLM-L6-v2 erzeugt beispielsweise Vektoren mit 384 Dimensionen, während das Modell text-embedding-ada-002 von OpenAI Vektoren mit 1536 Dimensionen erzeugt.</p><p>Das Feld <code>dense_vector</code> wird üblicherweise als Standardtyp für die Speicherung dieser Art von Einbettungen verwendet, wenn eine größere Kontrolle erforderlich ist, z. B. durch die Verwendung vorab generierter Vektoren, die Anwendung benutzerdefinierter Ähnlichkeitsfunktionen oder die Integration mit externen Modellen.</p><h3>Wann und warum sollte man dense_vector-Typ verwenden?</h3><p>Dichte Vektoren eignen sich hervorragend, um semantische Ähnlichkeiten zwischen Sätzen, Absätzen oder ganzen Dokumenten zu erfassen. Sie eignen sich hervorragend, wenn es darum geht, die Gesamtbedeutung von Texten zu vergleichen, selbst wenn diese nicht dieselben Begriffe verwenden.</p><p>Das dichte Vektorfeld ist ideal, wenn Sie bereits eine externe Embedding-Generierungspipeline mit Modellen von Plattformen wie OpenAI, Cohere oder Hugging Face verwenden und diese Vektoren nur manuell speichern und abfragen möchten. Dieser Feldtyp bietet eine hohe Kompatibilität mit Einbettungsmodellen und volle Flexibilität bei der Generierung und Abfrage, sodass Sie steuern können, wie die Vektoren erzeugt, indiziert und während der Suche verwendet werden.</p><p>Darüber hinaus unterstützt es verschiedene Formen der semantischen Suche, mit Abfragen wie k-NN oder script_score für Fälle, in denen es notwendig ist, die Ranking-Logik anzupassen. Diese Möglichkeiten machen den dichten Vektor ideal für Anwendungen wie RAG (Retrieval-Augmented Generation), Empfehlungssysteme und personalisierte Suchen auf der Grundlage von Ähnlichkeit.</p><p>Schließlich ermöglicht Ihnen das Feld, die Relevanzlogik anzupassen, indem Sie Funktionen wie <code>cosineSimilarity</code>, <code>dotProduct</code> oder <code>l2norm</code> verwenden, um das Ranking an die Bedürfnisse Ihres Anwendungsfalls anzupassen. </p><p>Dichte Vektoren bleiben die beste Option für diejenigen, die Flexibilität, Anpassungsmöglichkeiten und Kompatibilität mit fortgeschrittenen Anwendungsfällen wie den oben genannten benötigen.</p><h3>Wie verwendet man die Abfrage für den Datentyp „dichter Vektor“?</h3><p>Bei Suchen in Feldern, die als <strong><code>dense_vector</code></strong> definiert sind, wird die k-nächste-Nachbarn-Abfrage verwendet. Diese Abfrage dient dazu, Dokumente zu finden, deren Dichtevektor dem Abfragevektor am nächsten kommt. Nachfolgend ein Beispiel für die Anwendung einer k-NN-Abfrage auf ein dichtes Vektorfeld:</p><pre><code>{
  "knn": {
    "field": "my_dense_vector",
    "k": 10,
    "num_candidates": 50,
    "query_vector": [/* vector generated by model */]
  }
}</code></pre><p>Zusätzlich zur k-NN-Abfrage besteht die Möglichkeit, bei Bedarf die Dokumentenbewertung anzupassen, indem man die script_score-Abfrage verwendet und sie mit Vektorvergleichsfunktionen wie <strong>cosineSimilarity, dotProduct oder l2norm</strong> kombiniert, um die Relevanz kontrollierter zu berechnen. Siehe das Beispiel:</p><pre><code>{
"script_score": {
    "query": { "match_all": {} },
    "script": {
      "source": "cosineSimilarity(params.query_vector,
'my_dense_vector') + 1.0",
      "params": {
        "query_vector": [/* vector */]
      }
    }
  }
}</code></pre><p>Wer tiefer in die Materie einsteigen möchte, dem empfehle ich den Artikel <a href="https://www.elastic.co/search-labs/blog/vector-search-set-up-elasticsearch">„Wie man die Vektorsuche in Elasticsearch einrichtet“.</a></p><p></p><h2>Sparse Vektortyp</h2><p>Der Feldtyp <strong><code>sparse_vector</code></strong> dient zum Speichern von dünnbesetzten Vektoren. Dabei handelt es sich um numerische Darstellungen, bei denen die meisten Werte null sind und nur wenige Terme ein signifikantes Gewicht haben. Dieser Vektortyp ist in termbasierten Modellen wie SPLADE oder ELSER (Elastic Learned Sparse EncodeR) üblich.</p><h3>Wann und warum sollte man den Sparse-Vektor-Typ verwenden?</h3><p>Sparse Vektoren sind ideal, wenn Sie eine präzisere Suche in lexikalischen Begriffen benötigen, ohne dabei semantische Intelligenz einzubüßen. Sie stellen den Text als Token/Wert-Paare dar und heben nur die relevantesten Begriffe mit zugehörigen Gewichtungen hervor, was für Klarheit, Kontrolle und Effizienz sorgt.</p><p>Dieser Feldtyp ist besonders nützlich, wenn Sie Vektoren auf der Grundlage von Begriffen generieren, wie beispielsweise in den Modellen ELSER oder SPLADE, die jedem Token unterschiedliche Gewichte zuweisen, basierend auf seiner relativen Bedeutung im Text.</p><p>Für den Fall, dass Sie den Einfluss bestimmter Wörter in der Suchanfrage steuern möchten, ermöglichen Ihnen Sparse-Vektor-Typen, die Gewichtung der Begriffe manuell anzupassen, um die Rangfolge der Ergebnisse zu optimieren.</p><p>Zu den wichtigsten Vorteilen zählen die Transparenz bei der Suche, da klar ersichtlich ist, warum ein Dokument als relevant eingestuft wurde, und die Speichereffizienz, da nur Token mit einem Wert ungleich Null gespeichert werden, im Gegensatz zu dichten Vektoren, die alle Dimensionen speichern.</p><p>Darüber hinaus sind dünnbesetzte Vektoren die ideale Ergänzung in hybriden Suchstrategien und können sogar mit dichtbesetzten Vektoren kombiniert werden, um lexikalische Präzision mit semantischem Verständnis zu verbinden.</p><h3>Wie verwendet man die Abfrage für den Datentyp „dünnbesetzter Vektor“?</h3><p>Mit der <strong><code>sparse_vector</code></strong> -Abfrage können Sie nach Dokumenten auf Basis eines Abfragevektors im Token/Wert-Format suchen. Nachfolgend finden Sie ein Beispiel für die Abfrage:</p><pre><code>{
  "query": {
    "sparse_vector": {
      "field": "field_sparse",
      "query_vector": {
        "token1": 0.6,
        "token2": 0.2,
        "token3": 0.9
      }
    }
  }
}</code></pre><p>Falls Sie ein trainiertes Modell bevorzugen, können Sie einen Inferenzendpunkt verwenden, der den Abfragetext automatisch in einen Sparse-Vektor umwandelt:</p><pre><code>{
  "query": {
    "sparse_vector": {
      "field": "field_sparse",
      "inference_id": "the inference ID to produce the token/weights",
      "query": "search text"
    }
  }
}</code></pre><p>Um dieses Thema weiter zu vertiefen, empfehle ich die Lektüre von <a href="https://www.elastic.co/search-labs/blog/sparse-vector-embedding">„Understanding sparse vector embeddings with trained ML models“</a>.</p><h2>Semantischer Texttyp</h2><p>Der Feldtyp <strong><code>semantic_text</code></strong> ist die einfachste und unkomplizierteste Möglichkeit, die semantische Suche in Elasticsearch zu nutzen. Die Generierung von Einbettungen erfolgt automatisch sowohl zur Indexierungs- als auch zur Abfragezeit über einen Inferenz-Endpunkt. Das bedeutet, dass Sie sich keine Gedanken über das manuelle Generieren oder Speichern von Vektoren machen müssen.</p><h3>Wann und warum sollte man semantischen Text verwenden?</h3><p>Das Feld <code>semantic_text</code> ist ideal für diejenigen, die mit minimalem technischem Aufwand und ohne manuelle Vektorbearbeitung beginnen möchten. Dieses Feld automatisiert Schritte wie die Generierung von Einbettungen und die Vektorsuche, wodurch die Einrichtung schneller und bequemer wird.</p><p>Sie sollten die Verwendung <code>semantic_text</code> in Betracht ziehen, wenn Sie Wert auf <strong>Einfachheit und Abstraktion</strong> legen, da dadurch <strong>die Komplexität der manuellen Konfiguration von Mappings, Embedding-Generierung und Ingestion-Pipelines entfällt</strong>. Wählen Sie einfach das Inferenzmodell aus, und Elasticsearch kümmert sich um den Rest.</p><p>Zu den wichtigsten Vorteilen gehören <strong>die automatische Generierung von Einbettungen,</strong> die sowohl während der Indizierung als auch während der Abfrage erfolgt, und <strong>das sofort einsatzbereite Mapping</strong>, das vorkonfiguriert ist, um das ausgewählte Inferenzmodell zu unterstützen.</p><p>Darüber hinaus bietet das Feld <strong>native Unterstützung für die automatische Aufteilung langer Texte (Text Chunking)</strong>, wodurch große Texte in kleinere Abschnitte unterteilt werden können, von denen jeder seine eigene Einbettung hat, was die Suchgenauigkeit verbessert. Dies steigert die Produktivität enorm, insbesondere für Teams, die schnell Mehrwert liefern wollen, ohne sich mit der zugrundeliegenden Technik der semantischen Suche auseinandersetzen zu müssen.</p><p>Allerdings bietet <code>semantic_text</code> zwar Geschwindigkeit und Einfachheit, hat aber auch einige Einschränkungen. Es ermöglicht die Verwendung marktüblicher Modelle, sofern diese als Inferenzendpunkte in Elasticsearch verfügbar sind. <strong>Es unterstützt jedoch keine extern generierten Einbettungen</strong>, wie es mit dem Feld <code>dense_vector</code> möglich ist.</p><p>Wenn Sie mehr Kontrolle darüber benötigen, wie Vektoren generiert werden, Ihre eigenen Einbettungen verwenden möchten oder mehrere Felder für fortgeschrittene Strategien kombinieren müssen, bieten die Felder <code>dense_vector</code> und <code>sparse_vector</code> die Flexibilität, die für individuellere oder domänenspezifische Szenarien erforderlich ist.</p><h3>Wie man die Abfrage für semantischen Texttyp verwendet</h3><p>Vor <strong><code>semantic_text</code></strong> war es notwendig, je nach Art der Einbettung (dicht oder dünn) eine andere Abfrage zu verwenden. Für dünn besetzte Felder wurde eine <code>sparse_vector</code> -Abfrage verwendet, während für <code>dense_vector</code> -Felder KNN-Abfragen erforderlich waren.</p><p>Bei der semantischen Textsuche wird die Suche mithilfe der <a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-semantic-query">semantischen Abfrage</a> durchgeführt, die automatisch den Abfragevektor generiert und ihn mit den Einbettungen der indizierten Dokumente vergleicht. Mit dem Typ <strong><code>semantic_text</code></strong> können Sie einen Inferenzendpunkt definieren, um die Abfrage einzubetten. Wenn jedoch kein Endpunkt angegeben wird, wird derselbe Endpunkt, der während der Indizierung verwendet wurde, auf die Abfrage angewendet.</p><pre><code>{
  "query": {
    "semantic": {
      "field": "semantic_text_field",
      "query": "search text"
    }
  }
}</code></pre><p>Um mehr zu erfahren, empfehle ich die Lektüre des Artikels <a href="https://www.elastic.co/search-labs/blog/semantic-search-simplified-semantic-text">Elasticsearch new semantic_text mapping: Simplifying semantic search</a>.</p><h2>Fazit</h2><p>Bei der Auswahl der Methode zum Mapping von Embeddings in Elasticsearch ist es wichtig zu verstehen, wie Sie die Vektoren generieren möchten und welchen Grad an Kontrolle Sie darüber benötigen. Wenn Sie Wert auf Einfachheit legen, ermöglicht das semantische Textfeld eine automatische und skalierbare semantische Suche und ist damit ideal für viele erste Anwendungsfälle. Wenn mehr Kontrolle, feinabgestimmte Leistung oder die Integration mit benutzerdefinierten Modellen erforderlich ist, bieten die dichten Vektorfelder und die dünnbesetzten Vektorfelder die notwendige Flexibilität.</p><p>Der ideale Feldtyp hängt von Ihrem Anwendungsfall, der verfügbaren Infrastruktur und dem Reifegrad Ihres Machine-Learning-Stacks ab. Am wichtigsten ist jedoch, dass Elastic die Werkzeuge bietet, um moderne und hochgradig anpassungsfähige Suchsysteme zu entwickeln.</p><h2>Referenzen</h2><ul><li><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-text.html">Semantischer Textfeldtyp</a></li><li><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/sparse-vector.html">Sparse Vektorfeldtyp</a></li><li><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/dense-vector.html">Dichtes Vektorfeld</a></li><li><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-semantic-query.html">Semantische Anfrage</a></li><li><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-sparse-vector-query.html">Sparse Vektorabfrage</a></li><li><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html">kNN-Suche</a></li><li><a href="https://www.elastic.co/search-labs/blog/semantic-search-simplified-semantic-text">Elasticsearch: Neues semantisches Textmapping: Vereinfachung der semantischen Suche</a></li><li><a href="https://www.elastic.co/search-labs/blog/sparse-vector-embedding">Verständnis von spärlichen Vektoreinbettungen mit trainierten ML-Modellen</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Elasticsearch-Felder von der Indizierung ausschließen]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie Elasticsearch konfigurieren, um Felder auszuschließen, die Hauptgründe für das Ausschließen von Feldern aus dem Indizieren und die zu befolgenden Best Practices.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/excluding-elasticsearch-fields-from-indexing</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/excluding-elasticsearch-fields-from-indexing</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Mon, 12 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3aa0b481994d2445ba979d3c79fff64c5ee6676a-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>In Elasticsearch bezeichnet Indexierung den Prozess des Speicherns und Organisierens von Daten, sodass diese leicht durchsuchbar sind. Während die Indizierung aller Felder eines Dokuments in manchen Fällen sinnvoll sein kann, gibt es Situationen, in denen man bestimmte Felder von der Indizierung ausschließen möchte. Dies kann dazu beitragen, die Leistung zu verbessern, die Speicherkosten zu senken und die Gesamtgröße Ihres Elasticsearch-Index zu minimieren.</p><p>In diesem Artikel werden wir die Gründe für den Ausschluss von Feldern aus der Indizierung, die Konfiguration von Elasticsearch zum Ausschluss bestimmter Felder und einige bewährte Vorgehensweisen dabei erläutern.</p><h2>Gründe für den Ausschluss von Feldern aus der Indizierung</h2><ol><li><strong>Leistung: </strong>Die Indizierung aller Felder in einem Dokument kann zu längeren Indizierungszeiten und einer langsameren Suchleistung führen. Durch den Ausschluss von Feldern, die für die Suche oder Aggregation nicht erforderlich sind, können Sie die Gesamtleistung Ihres Elasticsearch-Clusters verbessern.</li><li><strong>Speicherplatz: </strong>Das Indizieren von Feldern benötigt Speicherplatz. Durch das Ausschließen von Feldern, die für die Suche oder Aggregation nicht benötigt werden, können die Speicheranforderungen Ihres Elasticsearch-Clusters reduziert werden.</li><li><strong>Indexgröße: </strong>Die Größe eines Elasticsearch-Index steht in direktem Zusammenhang mit der Anzahl der indizierten Felder. Durch den Ausschluss unnötiger Felder können Sie die Größe Ihres Index minimieren, was zu einer schnelleren Such- und Indexierungsleistung führen kann.</li></ol><h2>Elasticsearch so konfigurieren, dass Felder ausgeschlossen werden</h2><p>Um ein Feld von der Indizierung in Elasticsearch auszuschließen, können Sie die „index“-Eigenschaft in der Feldzuordnung verwenden. Wenn die Eigenschaft „index“ auf „false“ gesetzt wird, wird Elasticsearch das Feld nicht indizieren, und es ist weder durchsuchbar noch für Aggregationen verfügbar.</p><p>Hier ist ein Beispiel dafür, wie man ein Feld mithilfe des Elasticsearch-Mappings von der Indizierung ausschließt:</p><pre><code>PUT /my_index
{
  "mappings": {
    "properties": {
      "field_to_exclude": {
        "type": "text",
        "index": false
      }
    }
  }
}</code></pre><p>In diesem Beispiel erstellen wir einen neuen Index namens „my_index“ mit einem einzigen Feld namens „field_to_exclude“. Indem wir die Eigenschaft „index“ auf „false“ setzen, weisen wir Elasticsearch an, dieses Feld nicht zu indizieren. Das Feld wird jedoch im Quelldokument weiterhin verfügbar sein.</p><h2>Bewährte Verfahren zum Ausschließen von Feldern von der Indizierung</h2><ol><li><strong>Analysieren Sie Ihre Daten: </strong>Bevor Sie Felder von der Indizierung ausschließen, ist es unerlässlich, Ihre Daten zu analysieren und zu verstehen, welche Felder für die Suche und Aggregation notwendig sind. Dies wird Ihnen helfen, fundierte Entscheidungen darüber zu treffen, welche Felder ausgeschlossen werden sollen.</li><li><strong>Testen Sie Ihre Änderungen: </strong>Wenn Sie Felder von der Indizierung ausschließen, ist es unerlässlich, Ihre Änderungen zu testen, um sicherzustellen, dass Ihre Such- und Aggregationsfunktionen weiterhin wie erwartet funktionieren. Dies kann Ihnen helfen, unerwartete Probleme oder Leistungsstörungen zu vermeiden.</li><li><strong>Leistungsüberwachung:</strong> Nachdem Sie Felder von der Indizierung ausgeschlossen haben, sollten Sie die Leistung Ihres Elasticsearch-Clusters überwachen, um sicherzustellen, dass Ihre Änderungen den gewünschten Effekt erzielt haben. Dies kann Ihnen dabei helfen, eventuell erforderliche zusätzliche Optimierungen zu identifizieren.</li><li><strong>Quellfilterung nutzen:</strong> Wenn Sie ein Feld in Elasticsearch speichern müssen, es aber nicht durchsuchbar oder für Aggregationen verfügbar sein soll, sollten Sie die Quellfilterung in Betracht ziehen. Dies ermöglicht es Ihnen, das Feld im Feld _source zu speichern, es aber vom Index auszuschließen.</li></ol><h2>Fazit</h2><p>Das Ausschließen von Feldern von der Indizierung in Elasticsearch kann die Leistung verbessern, die Speicherkosten senken und die Gesamtgröße Ihres Index minimieren. Durch eine sorgfältige Analyse Ihrer Daten und das Verständnis, welche Felder für die Suche und Aggregation notwendig sind, können Sie fundierte Entscheidungen darüber treffen, welche Felder ausgeschlossen werden sollten. Testen Sie Ihre Änderungen stets und überwachen Sie die Leistung Ihres Elasticsearch-Clusters, um sicherzustellen, dass Ihre Optimierungen den gewünschten Effekt erzielen.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Löschen eines Feldes aus einem Dokument in Elasticsearch]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie Felder aus Elasticsearch-Dokumenten mithilfe der Update-API, von Skripten oder durch Neuindizierung für einzelne und massenhafte Entfernungen löschen können.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/elasticsearch-delete-field-from-document</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/elasticsearch-delete-field-from-document</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Fri, 09 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/89278eb7309b7f3018c61be2b514d1fd25b9564d-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>In Elasticsearch ist das Löschen eines Feldes aus einem Dokument eine häufige Anforderung. Dies kann nützlich sein, wenn Sie unnötige oder veraltete Informationen aus Ihrem Index entfernen möchten. In diesem Artikel werden wir verschiedene Methoden zum Löschen eines Feldes aus einem Dokument in Elasticsearch besprechen und Beispiele sowie Schritt-für-Schritt-Anleitungen geben. </p><h2>Methode 1: Verwendung der Update-API</h2><p>Die <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/update-document">Update-API</a> ermöglicht es Ihnen, ein Dokument zu aktualisieren, indem Sie ein Skript bereitstellen, das die Quelle des Dokuments ändert. Mit dieser API können Sie ein Feld aus einem Dokument löschen, indem Sie das Feld auf Null setzen. Hier ist eine Schritt-für-Schritt-Anleitung dazu:</p><p>1. Ermitteln Sie den Index, den Dokumenttyp (falls Sie Elasticsearch 6.x oder eine ältere Version verwenden) und die Dokument-ID des Dokuments, das Sie aktualisieren möchten.</p><p>2. Verwenden Sie die Update-API mit einem Skript, das das Feld auf null setzt oder, noch besser, es aus dem Quelldokument entfernt. Das folgende Beispiel zeigt, wie das Feld „field_to_delete“ aus einem Dokument mit der ID „1“ im Index „my_index“ gelöscht wird:</p><pre><code>POST /my_index/_update/1
{
  "script": "ctx._source.remove('field_to_delete')"
}</code></pre><p>3. Führe die Anfrage aus. Im Erfolgsfall gibt Elasticsearch eine Antwort zurück, die anzeigt, dass das Dokument aktualisiert wurde.</p><p>Hinweis: Diese Methode entfernt das Feld nur aus dem angegebenen Dokument. Das Feld wird weiterhin in der Zuordnung und in anderen Dokumenten im Index vorhanden sein.</p><h2>Methode 2: Reindexierung mit einer modifizierten Quelle</h2><p>Wenn Sie ein Feld aus allen Dokumenten in einem Index löschen möchten, können Sie die <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-reindex">Reindex-API</a> verwenden, um einen neuen Index mit der modifizierten Quelle zu erstellen. So machen Sie das:</p><p>1. Erstellen Sie einen neuen Index mit den gleichen Einstellungen und Zuordnungen wie der ursprüngliche Index. Mit der Get Index API können Sie die Einstellungen und Zuordnungen des ursprünglichen Index abrufen.</p><p>2. Verwenden Sie die Reindex-API, um Dokumente vom ursprünglichen Index in den neuen Index zu kopieren und dabei das Feld aus der Quelle zu entfernen. Das folgende Beispiel zeigt, wie das Feld „field_to_delete“ aus allen Dokumenten im Index „my_index“ gelöscht wird:</p><pre><code>POST /_reindex
{
  "source": {
    "index": "my_index"
  },
  "dest": {
    "index": "new_index"
  },
  "script": {
    "source": "ctx._source.remove('field_to_delete')"
  }
}</code></pre><p><br/>3. Überprüfen Sie, ob der neue Index die korrekten Dokumente enthält, nachdem das Feld entfernt wurde.</p><p>4. Wenn alles in Ordnung ist, können Sie den ursprünglichen Index löschen und gegebenenfalls einen Alias für den neuen Index hinzufügen, der den Namen des ursprünglichen Index trägt.</p><h2>Methode 3: Aktualisierung des Mappings und der Neuindexierung</h2><p>Wenn Sie ein Feld aus der Zuordnung und alle Dokumente in einem Index löschen möchten, können Sie die Zuordnung aktualisieren und anschließend die Dokumente neu indizieren. So geht&#x27;s:</p><p>1. Erstellen Sie einen neuen Index mit den gleichen Einstellungen wie der ursprüngliche Index.</p><p>2. Rufen Sie die Zuordnungen des ursprünglichen Index mithilfe der Get Mapping API ab.</p><p>3. Ändern Sie die Zuordnungen, indem Sie das Feld entfernen, das Sie löschen möchten.</p><p>4. Wenden Sie die geänderten Zuordnungen mithilfe der Put Mapping API auf den neuen Index an.</p><p>5. Verwenden Sie die Reindex-API, um Dokumente vom ursprünglichen Index in den neuen Index zu kopieren, wie in Methode 2 beschrieben.</p><p>6. Überprüfen Sie, ob der neue Index die korrekten Dokumente enthält, wobei das Feld entfernt wurde, und ob das Feld in der Zuordnung nicht vorhanden ist.</p><p>7. Wenn alles gut aussieht, können Sie den ursprünglichen Index löschen und gegebenenfalls einen Alias mit dem Namen des ursprünglichen Index zum neuen Index hinzufügen.</p><h2>Fazit</h2><p>In diesem Artikel haben wir drei Methoden zum Löschen eines Feldes aus einem Dokument in Elasticsearch besprochen: die Verwendung der Update-API, die Neuindizierung mit einer geänderten Quelle und die Aktualisierung des Mappings mit anschließender Neuindizierung. Jede Methode hat ihre eigenen Anwendungsfälle und Vor- und Nachteile. Wählen Sie daher diejenige, die am besten zu Ihren Anforderungen passt. Denken Sie immer daran, Ihre Änderungen zu testen und die Ergebnisse zu überprüfen, bevor Sie sie in Produktionsumgebungen anwenden.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[So verbinden Sie zwei Indizes in Elasticsearch]]></title>
            <description><![CDATA[Erläuterung der Verwendung der Begriffe Abfrage, Logstash Elasticsearch-Filter, Anreicherungsprozessor und ES|QL zum Verbinden zweier Indizes in Elasticsearch.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/elasticsearch-join-two-indexes</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/elasticsearch-join-two-indexes</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Carly Richmond]]></dc:creator>
            <pubDate>Wed, 07 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0d4736d10fa3e12e6233cd59993299c7bd48911b-680x450.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>In Elasticsearch ist das Verbinden zweier Indizes nicht so einfach wie in herkömmlichen relationalen SQL-Datenbanken. Mit bestimmten Techniken und Funktionen von Elasticsearch können jedoch ähnliche Ergebnisse erzielt werden.</p><p>In der Vergangenheit haben viele Leute den<a href="https://www.elastic.co/de/docs/reference/elasticsearch/mapping-reference/nested"> Feldtyp</a> <a href="https://www.elastic.co/de/docs/reference/elasticsearch/mapping-reference/nested"><code>nested</code></a>als Mechanismus verwendet, um verschiedene Indizes miteinander zu verbinden. Allerdings war dies aufgrund teurer Abfragen und unvollständiger Unterstützung in Kibana, insbesondere bei Lens-Visualisierungen, eingeschränkt.</p><p>Dieser Artikel befasst sich eingehend mit dem Zusammenführen zweier Indizes in Elasticsearch und konzentriert sich dabei auf die folgenden Ansätze: </p><ol><li>Verwenden der <code>terms</code> -Abfrage</li><li>Verwenden des <code>enrich</code> -Prozessors in Aufnahmepipelines</li><li>Logstash <code>elasticsearch</code> Filter-Plugin</li><li>ES|QL <code>ENRICH</code></li><li>ES|QL <code>LOOKUP JOIN</code></li></ol><h2>Verwenden der Begriffsabfrage</h2><p>Die <a href="https://www.elastic.co/de/docs/reference/query-languages/query-dsl/query-dsl-terms-query">Termabfrage</a> ist eine der effektivsten Möglichkeiten, zwei Indizes in Elasticsearch zu verbinden. Mit dieser Abfrage können Sie Dokumente abrufen, die einen oder mehrere genaue Begriffe in einem bestimmten Feld enthalten. Hier besprechen wir, wie man es zum Verbinden zweier Indizes verwendet.</p><p>Zuerst müssen Sie die erforderlichen Daten aus dem ersten Index abrufen. Dies kann mithilfe einer einfachen GET-Anfrage und dem Abrufen der Werte aus dem Attribut <code>_source</code> erfolgen.</p><pre><code># Simple GET request
GET first_index/_search</code></pre><p>Sobald Sie die Daten aus dem ersten Index haben, können Sie diese zum Abfragen des zweiten Index verwenden. Dies geschieht mithilfe der <code>terms</code> -Abfrage, bei der Sie das Feld und die Werte angeben, die Sie abgleichen möchten.</p><p>Hier ist ein Beispiel:</p><pre><code>GET second_index/_search
{
  "query": {
    "terms": {
      "field_in_second_index": ["value1_from_first_index", "value2_from_first_index"]
    }
  }
}</code></pre><p><br/>In diesem Beispiel ist <code>field_in_second_index</code> das Feld im zweiten Index, das Sie mit den Werten aus dem ersten Index abgleichen möchten. <code>value1_from_first_index</code> und <code>value2_from_first_index</code> sind die Werte aus dem ersten Index, die im zweiten Index abgeglichen werden sollen.</p><p>Die Begriffsabfrage bietet außerdem Unterstützung bei der Ausführung der beiden oben genannten Schritte in einem Durchgang mithilfe einer Technik namens <a href="https://www.elastic.co/de/docs/reference/query-languages/query-dsl/query-dsl-terms-query#query-dsl-terms-lookup">„Begriffssuche“</a>. Elasticsearch kümmert sich um das transparente Abrufen der abzugleichenden Werte aus einem anderen Index. Wenn Sie beispielsweise einen Teamindex mit einer Spielerliste haben:</p><pre><code>PUT teams/_doc/team1
{
  "players":   ["john", "bill", "michael"]
}
PUT teams/_doc/team2
{
  "players":   ["aaron", "joe", "donald"]
}</code></pre><p>Es ist möglich, einen Personenindex für alle Personen abzufragen, die in Team1 spielen, wie unten gezeigt:</p><pre><code>GET people/_search?pretty
{
  "query": {
    "terms": {
        "name" : {
            "index" : "teams",
            "id" : "team1",
            "path" : "players"
        }
    }
  }
}</code></pre><p>Im obigen Beispiel ruft Elasticsearch die Spielernamen transparent aus dem Dokument mit der ID team1 im Teamindex ab (d. h. „John“, „Bill“ und „Michael“) und suchen Sie alle Dokumente im Personenindex, die einen dieser Werte in ihrem Namensfeld enthalten.</p><p>Für alle, die es interessiert: Die entsprechende SQL-Abfrage wäre:</p><pre><code>SELECT p.* FROM people p
INNER JOIN teams t ON p.name = t.players</code></pre><h2>Verwenden des Anreicherungsprozessors</h2><p>Der <a href="https://www.elastic.co/de/docs/reference/enrich-processor/enrich-processor"><code>enrich</code> -Prozessor</a> ist ein weiteres leistungsstarkes Tool, mit dem zwei Indizes in Elasticsearch verbunden werden können. Dieser Prozessor bereichert die Daten eingehender Dokumente, indem er Daten aus einem vordefinierten Anreicherungsindex hinzufügt.</p><p>So können Sie den Anreicherungsprozessor verwenden, um zwei Indizes zu verknüpfen:</p><p>1. Zuerst müssen Sie eine Anreicherungsrichtlinie erstellen. Diese Richtlinie definiert, welcher Index für die Anreicherung verwendet werden soll, welches Feld abgeglichen werden soll und welche Felder für die Anreicherung eingehender Dokumente verwendet werden sollen.</p><p>Hier ist ein Beispiel:</p><pre><code>PUT _enrich/policy/my_enrich_policy
{
  "match": {
    "indices": "first_index",
    "match_field": "field_in_first_index",
    "enrich_fields": ["field_to_enrich"]
  }
}</code></pre><p>2. Sobald die Richtlinie erstellt ist, müssen Sie sie ausführen, um den angereicherten Index aus Ihrer neu erstellten Richtlinie zu erstellen:</p><pre><code>PUT _enrich/policy/my_enrich_policy/_execute</code></pre><p>Dadurch wird ein neuer versteckter angereicherter Index erstellt, der während der Anreicherung verwendet wird. Abhängig von der Größe des Quellindex kann dieser Vorgang einige Zeit dauern. Stellen Sie sicher, dass die Anreicherungsrichtlinie vollständig erstellt ist, bevor Sie mit dem nächsten Schritt fortfahren.</p><p>3. Nachdem die Anreicherungsrichtlinie erstellt wurde, können Sie den Anreicherungsprozessor in einer Aufnahmepipeline verwenden, um die Daten eingehender Dokumente anzureichern:</p><pre><code>PUT _ingest/pipeline/my_pipeline
{
  "processors": [
    {
      "enrich": {
        "policy_name": "my_enrich_policy",
        "field": "field_in_second_index",
        "target_field": "enriched_field"
      }
    }
  ]
}</code></pre><p>In diesem Beispiel ist <code>field_in_second_index</code> das Feld im zweiten Index, das mit <code>match_field</code> aus dem ersten Index übereinstimmen muss. <code>enriched_field</code> ist das neue Feld im zweiten Index, das die angereicherten Daten aus <code>enrich_fields</code> im ersten Index enthalten wird.</p><p>Ein Nachteil dieses Ansatzes besteht darin, dass die Anreicherungsrichtlinie erneut ausgeführt werden muss, wenn sich die Daten in <code>first_index</code> ändern. Der angereicherte Index wird nicht automatisch mit dem Quellindex aktualisiert oder synchronisiert, aus dem er erstellt wurde. Wenn <code>first_index</code> jedoch relativ stabil ist, funktioniert dieser Ansatz gut.</p><h2>Logstash Elasticsearch-Filter-Plugin</h2><p>Wenn Sie Logstash verwenden, besteht eine weitere Option, die dem oben beschriebenen <code>enrich</code> -Prozessor ähnelt, darin, das <code>elasticsearch</code> -Filter-Plugin zu verwenden, um dem Ereignis basierend auf einer angegebenen Abfrage relevante Felder hinzuzufügen. Die Konfiguration für unsere Logstash-Pipeline würde sich in einer <code>.conf</code> -Datei befinden, beispielsweise <code>my-pipeline.conf</code>.</p><p>Stellen wir uns vor, unsere Pipeline zieht Protokolle aus Elasticsearch mithilfe des <a href="https://www.elastic.co/de/docs/reference/logstash/plugins/plugins-inputs-elasticsearch"><code>elasticsearch</code> -Eingabe-Plugins</a> und verwendet eine Abfrage, um die Auswahl einzugrenzen:</p><pre><code>input {
  # Read all documents from Elasticsearch matching the given query
  elasticsearch {
    hosts => "localhost"
    query => '{ "query": { "match": { "statuscode": 200 } }, "sort": [ "_doc" ] }'
  }
}</code></pre><p>Wenn wir diese Nachrichten mit Informationen aus einem bestimmten Index anreichern möchten, können wir das<a href="https://www.elastic.co/de/docs/reference/logstash/plugins/plugins-filters-elasticsearch"> Filter-Plugin</a> <a href="https://www.elastic.co/de/docs/reference/logstash/plugins/plugins-filters-elasticsearch"><code>elasticsearch</code></a>im Abschnitt <code>filter</code> verwenden, um unsere Protokolle anzureichern:</p><pre><code>filter {
   elasticsearch {
      hosts => ["localhost"]
      index => "index_name"
      query => "type:start AND operation:%{[opid]}"
      fields => { "@timestamp" => "started" }
   }
}</code></pre><p>Der obige Code findet die Dokumente ab Index <code>index_name</code> , wobei <code>type</code> der Start ist und das Operationsfeld mit dem angegebenen <code>opid</code> übereinstimmt, und kopiert dann den Wert des Felds <code>@timestamp</code> in ein neues Feld mit dem Namen <code>started</code>.</p><p>Die angereicherten Dokumente würden dann an die entsprechende Ausgabequelle gesendet, in diesem Fall an Elasticsearch unter Verwendung des <a href="https://www.elastic.co/de/docs/reference/logstash/plugins/plugins-outputs-elasticsearch"><code>elasticsearch</code> -Ausgabe-Plugins</a>:</p><pre><code>output {
    elasticsearch {
        hosts => "localhost"
        data_stream => "true"
    }
}</code></pre><p>Wenn Sie Logstash bereits verwenden, kann diese Option nützlich sein, um Ihre Anreicherungslogik an einem einzigen Ort zu konsolidieren und bei eingehenden neuen Ereignissen zu verarbeiten. Wenn dies jedoch nicht der Fall ist, wird Ihre Lösung komplexer und Sie müssen eine weitere Komponente ausführen und warten.</p><h2>ES|QL ENRICH</h2><p>Die Einführung von <a href="https://www.elastic.co/de/docs/explore-analyze/query-filter/languages/esql">ES|QL</a>, das in Version 8.14 allgemein verfügbar wurde, ist eine von Elasticsearch unterstützte Piped-Abfragesprache, die das Filtern, Transformieren und Analysieren von Daten ermöglicht. Mithilfe des Verarbeitungsbefehls ENRICH können wir mithilfe einer Anreicherungsrichtlinie Daten aus vorhandenen Indizes hinzufügen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/03aadddc08afffff3f6526c9c052999c97fa09dd-1600x989.png" style="width: 100%;" /><p>Wenn man dieselbe Richtlinie <code>my_enrich_policy</code> aus dem ursprünglichen Beispiel des Anreicherungsprozessors verwendet, würde das ES|QL-Beispiel folgendermaßen aussehen:</p><pre><code>FROM first_index
| WHERE field_in_first_index IS NOT NULL
| ENRICH my_enrich_policy</code></pre><p>Es ist auch möglich, die Übereinstimmungs- und Anreicherungsfelder zu überschreiben, die in unserem Beispiel <code>field_in_first_index</code> bzw. <code>field_to_enrich</code> sind:</p><pre><code>FROM first_index
| WHERE field_in_first_index IS NOT NULL
| ENRICH my_enrich_policy ON another_field_in_first_index WITH different_field_to_enrich</code></pre><p>Die offensichtliche Einschränkung besteht darin, dass Sie zuerst eine Anreicherungsrichtlinie angeben müssen, ES|QL bietet jedoch die Flexibilität, die Felder nach Bedarf anzupassen.</p><h2>ES|QL LOOKUP JOIN</h2><p>Elasticsearch 8.18 führt eine neue Möglichkeit zum Verbinden von Indizes in Elasticsearch ein, nämlich den Befehl <code>LOOKUP JOIN</code> . Dieser Befehl funktioniert als LEFT OUTER JOIN im SQL-Stil und verwendet den neuen <a href="https://www.elastic.co/de/docs/reference/elasticsearch/index-settings/index-modules#index-mode-setting">Lookup-Indexmodus</a> auf der rechten Seite des Joins.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1d73495979c4d6bb675c4c966ea86d9a72dc1c48-510x605.png" style="width: 100%;" /><p>Wenn wir unser vorheriges Beispiel noch einmal betrachten, lautet die neue Abfrage wie folgt, wobei <code>match_field</code> sowohl in <code>first_index</code> als auch in <code>second_index</code> vorhanden sein muss:</p><pre><code>FROM first_index
| WHERE field_in_first_index IS NOT NULL
| LOOKUP JOIN second_index ON match_field</code></pre><p>Der Vorteil von LOOKUP JOIN gegenüber den anderen Ansätzen besteht darin, dass keine <code>enrich</code> -Richtlinie erforderlich ist und daher auch keine zusätzliche Verarbeitung im Zusammenhang mit der Einrichtung der Richtlinie erforderlich ist. Im Gegensatz zu den anderen in diesem Artikel besprochenen Ansätzen ist es nützlich, wenn mit sich häufig ändernden Anreicherungsdaten gearbeitet wird.</p><h2>Fazit</h2><p>Zusammenfassend lässt sich sagen, dass Elasticsearch zwar keine herkömmlichen Join-Operationen unterstützt, aber verschiedene Funktionen bietet, mit denen ähnliche Ergebnisse erzielt werden können. Insbesondere haben wir erläutert, wie Sie Join-Operationen mithilfe der folgenden Methoden durchführen können:</p><ol><li>Die <code>terms</code> -Abfrage</li><li>Der <code>enrich</code> -Prozessor in Ingest-Pipelines</li><li>Logstash <code>elasticsearch</code> Filter-Plugin</li><li>ES|QL <code>ENRICH</code></li><li>ES|QL <code>LOOKUP JOIN</code></li></ol><p>Es ist wichtig zu beachten, dass diese Methoden ihre Grenzen haben und je nach den spezifischen Anforderungen und der Art der Daten mit Bedacht eingesetzt werden sollten.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Elasticsearch-Scoring und die Explain-API verstehen]]></title>
            <description><![CDATA[Lernen Sie die Bewertungsmechanismen von Elasticsearch und die praktische Bewertungsfunktion kennen, um mit der Explain API die Suchrelevanz zu prüfen und das Dokumentenranking zu verbessern.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/elasticsearch-scoring-and-explain-api</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/elasticsearch-scoring-and-explain-api</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Mon, 05 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a70c5403064d5bbceff66a17373332362227f13c-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Elasticsearch ist eine leistungsstarke Suchmaschine, die schnelle und relevante Suchergebnisse liefert, indem sie für jedes Dokument im Index eine Punktzahl berechnet. Diese Punktzahl ist ein entscheidender Faktor für die Reihenfolge der Suchergebnisse. In diesem Artikel werden wir uns mit dem Scoring-Mechanismus von Elasticsearch befassen und die Explain API untersuchen, die zum Verständnis des Scoring-Prozesses beiträgt.</p><h2>Bewertungsmechanismen in Elasticsearch</h2><p>Elasticsearch verwendet standardmäßig ein Bewertungsmodell namens Practical Scoring Function (BM25). Dieses Modell basiert auf der probabilistischen Information-Retrieval-Theorie und berücksichtigt Faktoren wie Termfrequenz, inverse Dokumentfrequenz und Feldlängennormalisierung. Lassen Sie uns diese Faktoren kurz besprechen:</p><ol><li><strong>Termfrequenz (TF):</strong> Dies gibt an, wie oft ein Begriff in einem Dokument vorkommt. Eine höhere Termfrequenz deutet auf eine stärkere Beziehung zwischen dem Begriff und dem Dokument hin.</li><li><strong>Inverse Dokumenthäufigkeit (IDF):</strong> Dieser Faktor misst die Bedeutung eines Begriffs innerhalb der gesamten Dokumentensammlung. Ein Begriff, der in vielen Dokumenten vorkommt, gilt als weniger wichtig, während ein Begriff, der in weniger Dokumenten vorkommt, als wichtiger gilt.</li><li><strong>Feldlängennormalisierung</strong>: Dieser Faktor berücksichtigt die Länge des Feldes, in dem der Term vorkommt. Kürzere Felder werden stärker gewichtet, da der Begriff in einem kürzeren Feld als bedeutsamer angesehen wird.</li></ol><h2>Verwendung der Explain-API</h2><p>Die Explain API in Elasticsearch ist ein wertvolles Werkzeug zum Verständnis des Scoring-Prozesses. Es bietet eine detaillierte Erklärung, wie die Punktzahl für ein bestimmtes Dokument berechnet wurde. Um die Explain-API zu verwenden, müssen Sie eine GET-Anfrage an den folgenden Endpunkt senden:</p><pre><code>GET /<index>/_explain/<document_id></code></pre><p>Im Anfragetext müssen Sie die Abfrage angeben, für die Sie die Bewertung verstehen möchten. Hier ein Beispiel:</p><pre><code>{
  "query": {
    "match": {
      "title": "elasticsearch"
    }
  }
}</code></pre><p>Die Antwort der Explain API enthält eine detaillierte Aufschlüsselung des Bewertungsprozesses, einschließlich der einzelnen Faktoren (TF, IDF und Feldlängennormalisierung) und deren Beitrag zur Endwertung. Hier ist eine Beispielantwort:</p><pre><code>{
  "_index": "example_index",
  "_type": "_doc",
  "_id": "1",
  "matched": true,
  "explanation": {
    "value": 1.2,
    "description": "weight(title:elasticsearch in 0) [PerFieldSimilarity], result of:",
    "details": [
      {
        "value": 1.2,
        "description": "score(doc=0,freq=1.0 = termFreq=1.0\n), product of:",
        "details": [
          {
            "value": 2.2,
            "description": "idf, computed as log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5)) from:",
            "details": [
              {
                "value": 1,
                "description": "docFreq",
                "details": []
              },
              {
                "value": 1,
                "description": "docCount",
                "details": []
              }
            ]
          },
          {
            "value": 0.5,
            "description": "tfNorm, computed as (freq * (k1 + 1)) / (freq + k1 * (1 - b + b * fieldLength / avgFieldLength)) from:",
            "details": [
              {
                "value": 1,
                "description": "termFreq=1.0",
                "details": []
              },
              {
                "value": 1.2,
                "description": "parameter k1",
                "details": []
              },
              {
                "value": 0.75,
                "description": "parameter b",
                "details": []
              },
              {
                "value": 1,
                "description": "avgFieldLength",
                "details": []
              },
              {
                "value": 1,
                "description": "fieldLength",
                "details": []
              }
            ]
          }
        ]
      }
    ]
  }
}</code></pre><p>In diesem Beispiel zeigt die Antwort, dass der Wert von 1,2 das Produkt aus dem IDF-Wert (2,2) und dem tfNorm-Wert (0,5) ist. Die detaillierte Erklärung hilft, die Faktoren zu verstehen, die zur Bewertung beitragen, und kann für die Feinabstimmung der Suchrelevanz nützlich sein.</p><h2>Fazit</h2><p>Die Bewertung durch Elasticsearch ist ein entscheidender Aspekt bei der Bereitstellung relevanter Suchergebnisse. Indem Sie die Bewertungsmechanismen verstehen und die Explain API nutzen, können Sie Einblicke in die Faktoren gewinnen, die die Suchergebnisse beeinflussen, und Ihre Suchanfragen im Hinblick auf bessere Relevanz und Leistung optimieren.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Indexvorlagen in Elasticsearch: Wie man zusammensetzbare Vorlagen verwendet]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie in Elasticsearch zusammensetzbare und komponentenbasierte Indexvorlagen erstellen, um konsistente Mappings sicherzustellen und die Indexkonfiguration zu automatisieren.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/index-composable-templates</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/index-composable-templates</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Fri, 02 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/510750708df50bf79463586a1bbf35bf94acfa30-1200x628.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Ein Elasticsearch-Index kann über Mapping, Einstellungen und Aliase konfiguriert werden: </p><ul><li>Mapping-Definitionen legen das Datenschema fest.</li><li>In den Einstellungen werden die Shard-Größe und die Aktualisierungsraten festgelegt. </li><li>Aliase werden verwendet, um dem Index alternative Namen zu geben.</li></ul><p>Wenn wir ein Dokument zum ersten Mal indexieren oder einen leeren Index mithilfe der Create Index API erstellen, wird der Index mit Standardeinstellungen, ohne Datenschema und ohne Aliase erstellt. Diese Standardeinstellungen funktionieren in Entwicklungs- und Testumgebungen recht gut, aber für Produktionsumgebungen müssen wir unsere Indizes möglicherweise anpassen.</p><p>Die Verwendung der Standardzuordnungen und -einstellungen in der Produktionsumgebung kann zu einer schlechten Indexierungs- und Suchleistung führen. Das manuelle Erstellen von Indizes ist ein mühsamer und zeitaufwändiger Prozess. Die Neuerstellung solcher Indizes in jeder Umgebung ist besonders unpraktisch, wenn wir ein aufwendiges Mapping-Schema sowie benutzerdefinierte Einstellungen und Aliase haben.</p><p>Glücklicherweise stellt Elasticsearch uns ein Werkzeug zur Verfügung, mit dem wir beim Erstellen von Indizes automatisch eine vordefinierte Konfiguration in Form von <em>Indexvorlagen</em> anwenden können <em>.</em></p><h2>Indexvorlagen</h2><p>Indexvorlagen ermöglichen es uns, Indizes mit benutzerdefinierter Konfiguration zu erstellen. Ein Index kann die Konfiguration aus diesen Vorlagen abrufen, beispielsweise eine festgelegte Anzahl von Shards und Replikaten oder Feldzuordnungen, während seiner Instanziierung. Es wird eine Vorlage mit einem Namensmuster und einigen Konfigurationseinstellungen definiert. Wenn der Name des Index mit dem Namensmuster der Vorlage übereinstimmt, wird der neue Index mit der in der Vorlage definierten Konfiguration erstellt.</p><p>Elasticsearch hat in Version 7.8 seine Template-Funktionalität mit zusammensetzbaren Templates verbessert. Diese neuere Version bietet wesentlich mehr wiederverwendbare Indexvorlagen, wie in diesem Artikel gezeigt wird.</p><h3>Arten von Indexvorlagen</h3><p>Indexvorlagen lassen sich in zwei Kategorien einteilen:</p><ul><li><strong>Indexvorlagen (oder zusammensetzbare Indexvorlagen)</strong>: Die zusammensetzbaren Indexvorlagen können entweder allein existieren oder aus keiner oder mehreren Komponentenvorlagen zusammengesetzt sein (siehe die zweite Kategorie).</li><li><strong>Komponentenvorlagen:</strong> Die Komponentenvorlage ist eine <em>wiederverwendbare</em> Vorlage, die die erforderliche Konfiguration definiert. Üblicherweise wird erwartet, dass die Komponentenvorlage mit einer Indexvorlage verknüpft ist. Jede der Komponentenvorlagen kann mit einer oder mehreren Indexvorlagen verknüpft werden. </li></ul><p>Wie Sie im Bild unten sehen können, teilen sich die Indexvorlagen A und B Komponentenvorlagen (in diesem Fall nur eine – Vorlage 3). Eine Indexvorlage kann aus keiner oder mehreren Komponentenvorlagen bestehen, und jede der Komponentenvorlagen kann keiner oder mehreren Indexvorlagen zugeordnet sein. Beide Arten von Vorlagen können eigenständig existieren, jedoch sind Komponentenvorlagen nur dann nützlich, wenn sie an eine Indexvorlage angehängt werden.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/96c0aac29d3992e54a79be34e14cf909e0ca2ea9-1202x556.png" style="width: 100%;" /><p>Die Grundidee besteht darin, einen Katalog von Komponentenvorlagen zu entwickeln, die eine Organisation für verschiedene Zwecke nutzen kann (z. B. die verschiedenen Komponentenvorlagen für einzelne Umgebungen festlegen) und diese über die zusammensetzbaren Indexvorlagen verschiedenen Indizes zuzuordnen.</p><h2>Wie man zusammensetzbare (Index-)Vorlagen erstellt</h2><p>Elasticsearch bietet einen _index_template-Endpunkt zur Verwaltung von Indexvorlagen. Der Benutzer gibt in dieser Vorlage alle erforderlichen Zuordnungen, Einstellungen und Aliase sowie ein Indexnamensmuster an. Betrachten wir ein Beispiel für die Erstellung einer Vorlage für eine Microservice-Anwendung <em>namens customer-order-service</em> , die für die Logik zur Auftragsgenerierung zuständig ist. </p><p>Nehmen wir an, unsere Anforderung ist es, eine Vorlage für Kundenbestellungen zu erstellen, die durch ein Muster mit Platzhaltern dargestellt wird: *orders. Diese Vorlage muss bestimmte Zuordnungen und Einstellungen enthalten, wie zum Beispiel das Feld order_date sowie Shard- und Replikatnummern.</p><p>Jeder Index, der bei seiner Erstellung mit dieser Vorlage übereinstimmt, erbt die in dieser Vorlage definierten Konfigurationen. Ein Index namens black_friday_orders beispielsweise hat das Feld order_date, die Anzahl der Shards ist auf 5 und die Anzahl der Replikate auf 2 festgelegt. Darüber hinaus erben <em>alle</em> aus dieser Vorlage erstellten Indizes auch einen einzigen <a href="https://opster.com/guides/elasticsearch/glossary/elasticsearch-alias/">Aliasnamen</a> ! Erstellen wir nun diese orders_template mit einem Indexmuster, das als *orders definiert ist, und einem Mapping-Schema, das aus einem einzigen Feld der_date mit dem vordefinierten Datumsformat dd-MM-yyyy besteht. Der folgende Code zeigt, wie diese Indexvorlage erstellt wird.</p><pre><code>PUT _index_template/orders_template
{
  "index_patterns": ["*orders"],
  "priority": 300,
  "template": {
    "mappings": {
      "properties": {
        "order_date": {
          "type": "date",
          "format":"dd-MM-yyyy"
        }
      }
    },
    "settings":{
      "number_of_shards":5,
      "number_of_replicas":2
    },
    "aliases":{
      "all_orders":{}
    }
  }
}</code></pre><p>Wenn Sie diese Abfrage in den Kibana DevTools ausführen, wird die Vorlage mit dem Indexmuster *orders zusammen mit dem vordefinierten Mapping, den Einstellungen und einem Alias erstellt. index_patterns ist ein Array von Übereinstimmungsmustern; jeder Index, der diesem Muster entspricht, leitet die Vorlagenkonfiguration ab. Sie können folgenden Befehl ausführen, um die gespeicherte Vorlage abzurufen, die unsere Aktionen wiederholen sollte:</p><pre><code>GET _index_template/orders_template </code></pre><p>Außerdem wird beim Erstellen des Vorlagenattributs, das in der Vorlage definiert ist, eine Priorität, eine positive Zahl, festgelegt: Jede Vorlage wird mit einer Priorität definiert, sodass etwaige Konflikte zwischen Änderungen aus verschiedenen Vorlagen durch Verwendung dieses Wertes aufgelöst werden, wobei dem Wert mit der höheren Priorität Vorrang eingeräumt wird. Wir werden uns weiter unten eingehender mit der Priorisierung von Vorlagen befassen.</p><h2>Erstellen eines Index mit der Vorlage</h2><p>Jetzt haben wir eine Vorlage – einen Bauplan für die Erstellung von Indizes – der nächste Schritt ist die Erstellung eines Indexes. Wenn der Name des Index mit dem angegebenen Muster übereinstimmt, werden die Vorlagenkonfigurationen automatisch angewendet. Um dies zu beweisen, erstellen wir, wie der folgende Code zeigt, einen brandneuen Index mit dem Namen: blackfriday_orders:</p><pre><code>PUT blackfriday_orders</code></pre><p>Da der Name des Index (blackfriday_orders) mit dem im Template definierten Namensmuster übereinstimmt (d. h. *orders), sollte der Index alle aus der Vorlage abgeleiteten Konfigurationen erhalten. Lassen Sie uns diesen neu erstellten Index abrufen und überprüfen, ob dies tatsächlich zutrifft, indem wir den folgenden Code ausführen:</p><pre><code>GET blackfriday_orders</code></pre><p>Dies sollte folgendes Ergebnis liefern:</p><pre><code>{
  "blackfriday_orders" : {
    "aliases" : {
      "all_orders" : { }
    },
    "mappings" : {
      "properties" : {
        "order_date" : {
          "type" : "date",
          "format" : "dd-MM-yyyy"
        }
      }
    },
    "settings" : {
      "index" : {
         ...
        "number_of_shards" : "5",
        "number_of_replicas" : "2"
      }
    }
  }
}</code></pre><p>Wie die Antwort zeigt, wurde die Konfiguration von blackfriday_orders aus der Vorlage übernommen. Wir können verschiedene Kombinationen der Indizes ausprobieren, die die Vorlagenkonfiguration erfolgreich übernehmen:</p><pre><code>PUT blackfriday_orders
PUT americaorders
PUT cancelled--orders
PUT undefined101orders</code></pre><p>Die folgenden Indizes werden die Konfiguration jedoch nicht übernehmen, da ihr Name nicht dem Muster entspricht:</p><pre><code>PUT blackfriday_orders2
PUT open_orders_
PUT allorders_total</code></pre><p>Wichtig zu beachten ist, dass alle von einer Vorlage abgeleiteten Indizes in diesem Fall denselben Alias haben – all_orders. Ein solcher Alias hat den Vorteil, dass wir einfach über diesen einen Alias anstatt über mehrere Indizes abfragen können.</p><pre><code>GET blackfriday_orders,americaorders,undefined101orders/_search
GET all_orders/_search 
{
  "query": {
    "range": {
      "order_date": {
        "gte": "01-12-2021",
        "lte": "31-12-2021"
      }
    }
  }
}</code></pre><p>Während wir eine Vorlage für *orders erstellen, wird erwartet, dass jeder übereinstimmende Index die Konfiguration der Vorlage übernimmt. In der Regel erstellen Teams, bewusst oder unbewusst, aus verschiedenen Gründen einige zusätzliche Vorlagen. Das bedeutet, dass der Indexname unter Umständen mit zwei verschiedenen Vorlagenmustern übereinstimmen kann! Elasticsearch muss entscheiden, welche der Konfigurationen aus diesen Vorlagen angewendet werden soll. Glücklicherweise lässt sich dieses Dilemma durch die Verwendung der Vorlagenpriorität lösen.</p><h2>Erstellung von Komponentenvorlagen</h2><p>Wir haben im vorangegangenen Teil dieses Artikels etwas über Indexvorlagen gelernt. Es gibt ein paar Nachteile bei der Erstellung der Vorlagen mit integrierter Konfiguration – einer dieser Nachteile ist, dass die Konfiguration nicht für andere Vorlagen exportiert werden kann. Wenn wir eine ähnliche Konfiguration wünschen, beispielsweise für kundenbezogene Vorlagen (*customers), müssen wir möglicherweise die gesamte Vorlage neu erstellen. Das bedeutet, dass wir in einer typischen Organisation Dutzende davon erstellen (und je nach Umgebung können noch einige weitere hinzukommen).</p><p>Da wir stets auf Wiederverwendbarkeit Wert legen, hat Elasticsearch die Templates unter Berücksichtigung der Wiederverwendbarkeit neu gestaltet. Die Komponentenvorlagen erfüllen diese Anforderung. Wenn Sie aus dem DevOps-Bereich kommen, besteht für Sie höchstwahrscheinlich die Anforderung, Indizes mit einer vordefinierten Konfiguration für jede Umgebung zu erstellen. Anstatt jede dieser Konfigurationen mühsam manuell anzuwenden, können Sie für jede Umgebung eine Komponentenvorlage erstellen.</p><p>Eine Komponentenvorlage ist nichts anderes als ein wiederverwendbarer Konfigurationsblock, mit dem wir weitere Indexvorlagen erstellen können. Beachten Sie, dass die Komponentenvorlagen nur dann einen Nutzen haben, wenn sie mit Indexvorlagen kombiniert werden. Sie werden über einen _component_template-Endpunkt bereitgestellt. Mal sehen, wie das alles zusammenpasst.</p><h3>Einstellungen in einer Indexvorlage</h3><p>Lassen Sie uns die Einstellungen, die wir zuvor in unserer Indexvorlage definiert haben, extrahieren und daraus eine Komponentenvorlage erstellen. Es wird erwartet, dass die settings_component_template fünf primäre Shards mit jeweils zwei Replikaten pro primärem Shard besitzt. Der erste Schritt besteht, wie die untenstehende Codeauflistung zeigt, darin, eine Komponentenvorlage mit dieser Konfiguration zu deklarieren und auszuführen.</p><pre><code>PUT _component_template/settings_component_template
{
  "template":{
    "settings":{
      "number_of_shards":5,
      "number_of_replicas":2
    }
  }
}</code></pre><p>Wie der obige Code zeigt, verwenden wir den Endpunkt _component_template, um eine Komponentenvorlage zu erstellen. Der Anfragetext enthält die Vorlageninformationen in einem Vorlagenobjekt. Die settings_component_template kann nun auch an anderer Stelle in den Indexvorlagen verwendet werden. Ein wesentlicher Unterschied besteht darin, dass diese Vorlage kein Indexmuster definiert; es handelt sich lediglich um einen Codeblock, der einige Eigenschaften für uns konfiguriert.</p><h3>Mapping-Vorlage</h3><p>Erstellen wir auf die gleiche Weise eine weitere Vorlage. Dieses Mal extrahieren wir das Mapping-Schema, das wir zuvor in den eigenständigen Indexvorlagen definiert hatten. Der folgende Code zeigt das Skript:</p><pre><code>PUT _component_template/mappings_component_template
{
  "template": {
    "mappings": {
      "properties": {
        "order_date": {
          "type": "date",
          "format":"dd-MM-yyyy"
        }
      }
    }
  }
}</code></pre><h3>Aliasvorlage</h3><p>Im gleichen Sinne können wir auch eine Komponentenvorlage mit den Aliasen haben – zwei Aliase (all_orders und sales_orders):</p><pre><code>PUT _component_template/aliases_component_template
{
  "template": {
    "aliases": {
      "all_orders": {},
      "sales_orders":{}
    }
  }
}</code></pre><h3>Vorlage für einen zusammensetzbaren Index</h3><p>Nachdem wir nun diese drei Komponentenvorlagen haben, besteht der nächste Schritt darin, sie einzusetzen. Wir können dies erreichen, indem wir eine Indexvorlage, beispielsweise für christmas_orders, diese verwenden lassen:</p><pre><code>PUT _index_template/composed_orders_template
{
  "index_patterns": [
    "*orders"
  ],
  "priority": 500,
  "composed_of": [
    "settings_component_template",
    "mappings_component_template",
    "aliases_component_template"
  ]
}</code></pre><p>Das `composed_of`-Tag ist eine Sammlung aller Komponentenvorlagen, aus denen diese Vorlage besteht. In diesem Fall wählen wir die Komponentenvorlagen für Einstellungen, Zuordnungen und Aliase aus. Wir erhöhen außerdem die Priorität, sodass diese Vorlage alle anderen übertrifft. Sobald die Vorlage fertig ist, erben alle Indizes, die dem Muster *orders entsprechen, die Konfiguration von diesen drei Komponentenvorlagen.</p><p>Wenn wir nun eine neue Vorlage, beispielsweise für Kunden, mit nur einer der bestehenden Vorlagen (settings_component_template) und einer neu erstellten Aliasvorlage (aliases_component_template – siehe unten) erstellen möchten, können wir dies wie folgt tun:</p><pre><code>PUT _component_template/aliases_component_template2
{
  "template": {
    "aliases": {
      "all_customers": {}
    }
  }
}</code></pre><p>Die Indexvorlage sieht folgendermaßen aus:</p><pre><code>PUT _index_template/composed_customers_template
{
  "index_patterns": [
    "*customers*"
  ],
  "priority": 200,
  "composed_of": [
    "settings_component_template",
    "aliases_component_template2"
  ]
}</code></pre><p>Ist Ihnen aufgefallen, dass die settings_component_template in zwei verschiedenen Templates (wieder)verwendet wurde? Das ist die Stärke von Komponentenvorlagen.</p><h2>Indexvorlagenpriorität</h2><p>Es besteht die Möglichkeit, dass Entwickler mehrere Indexvorlagen erstellen, ohne den vorhandenen Bestand zu berücksichtigen. Es ist wichtig, jeder dieser Vorlagen eine Priorität zuzuweisen, damit diejenige mit der höheren Priorität verwendet wird. Beispielsweise überschreibt my_orders_template_1 im folgenden Codeausschnitt my_orders_template_2:</p><pre><code>PUT _index_template/my_orders_template_1
{
  "index_patterns": ["*orders"],
  "priority": 1000,
  "template": { ... }
}
PUT _index_template/my_orders_template2
{
  "index_patterns": ["*orders"],
  "priority": 300,
  "template": { ... }
}</code></pre><p>Wenn mehrere Vorlagen zu den zu erstellenden Indizes passen, wendet Elasticsearch alle Konfigurationen aus allen passenden Vorlagen an, überschreibt aber alle Konfigurationen mit höherer Priorität.</p><h2>Priorität der Vorlagen</h2><p>Abschließend stellt sich vielleicht die Frage nach der Priorität der Vorlagen – überschreibt die in der Komponentenvorlage definierte Konfiguration diejenige, die in der Hauptindexvorlage selbst definiert ist? Oder umgekehrt? Nun ja, es gibt einige Regeln:</p><ul><li>Ein Index, der mit expliziten Konfigurationen erstellt wurde, hat Vorrang vor allem anderen – das bedeutet, wenn Sie einen Index mit expliziten Konfigurationen erstellen, können Sie nicht davon ausgehen, dass diese von den Vorlagen überschrieben werden.</li><li>Legacy-Vorlagen (Vorlagen, die vor Version 7.8 erstellt wurden) haben eine niedrigere Priorität als die zusammensetzbaren Vorlagen.</li></ul><h2>Zusammenfassung</h2><ul><li>Ein Index enthält Mappings, Einstellungen und Aliase: Die Mappings definieren das Feldschema, die Einstellungen legen die Indexparameter wie die Anzahl der Shards und Replikate fest, und die Aliase geben dem Index alternative Namen.</li><li>Mithilfe von Vorlagen können wir Indizes mit vordefinierten Konfigurationen erstellen. Wenn ein Index mit einem Namen benannt wird, der dem in einer bestimmten Vorlage definierten Indexmuster entspricht, wird dieser Index automatisch gemäß der Vorlage konfiguriert.</li><li>Elasticsearch führte in Version 7.8 zusammensetzbare Indexvorlagen ein. Zusammensetzbare Indexvorlagen ermöglichen Modularität und Versionierung von Vorlagen.</li><li>Die zusammensetzbaren Vorlagen bestehen aus keiner oder mehreren Komponentenvorlagen.</li><li>Eine Indexvorlage kann auch über eine eigene Konfiguration verfügen.</li><li>Eine Komponentenvorlage ist eine wiederverwendbare Vorlage mit vordefinierter Konfiguration, genau wie eine zusammensetzbare Indexvorlage.</li><li>Allerdings wird von Komponentenvorlagen erwartet, dass sie Teil einer Indexvorlage sind; sie sind nutzlos, wenn sie nicht in eine Indexvorlage „zusammengesetzt“ werden.</li><li>Komponentenvorlagen haben kein definiertes Indexmuster – was ein weiterer Grund dafür ist, dass sie „erwartet“ werden, Teil einer Indexvorlage zu sein.</li><li>Jede der Vorlagen hat eine Priorität – eine positive Zahl. Je höher die Zahl, desto höher ist die Priorität für die Anwendung dieser Vorlage.</li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Elasticsearch-Suche anhand zweier Felder]]></title>
            <description><![CDATA[Entdecken Sie Techniken zur Suche in zwei Feldern, darunter Multi-Match-Abfragen, Bool-Abfragen und Feld-Boosting zur Abfragezeit.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/elasticsearch-search-by-two-fields</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/elasticsearch-search-by-two-fields</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Wed, 30 Apr 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d5d04bbcfc3925f48f3487ea4c7e0dd2205316d0-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Die Suche über mehrere Felder hinweg in Elasticsearch ist in vielen Anwendungen eine gängige Anforderung. In diesem Artikel werden wir fortgeschrittene Techniken zur Durchführung von Suchen anhand zweier Felder untersuchen, darunter Mehrfachübereinstimmungsabfragen, Boolesche Abfragen und die Feld-Boosting-Methode zur Abfragezeit. Diese Techniken helfen Ihnen dabei, genauere und relevantere Suchergebnisse für Ihre Nutzer zu erstellen.</p><h2>Erweiterte Techniken zur Durchführung von Suchen in zwei Feldern</h2><h3>1. Mehrfachabfrage</h3><p>Eine Mehrfachabfrage ermöglicht es Ihnen, in mehreren Feldern nach einer einzelnen Suchzeichenfolge zu suchen. Dies ist nützlich, wenn Sie Dokumente finden möchten, die die angegebene Suchanfrage in einem der beiden Felder enthalten. Hier ist ein Beispiel für eine Mehrfachabfrage, die nach dem Begriff „Beispiel“ in den Feldern „Titel“ oder „Beschreibung“ sucht:</p><pre><code>{
  "query": {
    "multi_match": {
      "query": "example",
      "fields": ["title", "description"]
    }
  }
}</code></pre><h3>2. Bool-Abfrage</h3><p>Eine Bool-Abfrage ermöglicht es Ihnen, mehrere Abfragen mithilfe boolescher Logik zu kombinieren. Sie können die „should“-Klausel verwenden, um nach Dokumenten zu suchen, die der Suchanfrage in einem der beiden Felder entsprechen. Hier ist ein Beispiel für eine Boolesche Abfrage, die nach dem Begriff „Beispiel“ in den Feldern „Titel“ und „Beschreibung“ sucht:</p><pre><code>{
  "query": {
    "bool": {
      "should": [
        {"match": {"title": "example"}},
        {"match": {"description": "example"}}
      ]
    }
  }
}</code></pre><h3>3. Feld-Boosting zur Abfragezeit</h3><p>Manchmal möchten Sie bei der Suche einem Feld mehr Gewicht beimessen als einem anderen. Dies lässt sich erreichen, indem man dem Feld zur Abfragezeit einen Boost-Faktor zuweist. Ein höherer Boost-Wert gewichtet das Feld stärker, wodurch es mit größerer Wahrscheinlichkeit das endgültige Suchergebnis beeinflusst. Hier ist ein Beispiel für eine Mehrfachabfrage mit einem Boost-Faktor für das Feld „Titel“:</p><pre><code>{
  "query": {
    "multi_match": {
      "query": "example",
      "fields": ["title^3", "description"]
    }
  }
}</code></pre><p>In diesem Beispiel hat das Feld „Titel“ einen Boost-Faktor von 3, wodurch es bei der Bestimmung des Such-Scores dreimal wichtiger ist als das Feld „Beschreibung“.</p><h3>4. Kombinieren von Abfragen mit unterschiedlichen Gewichtungsfaktoren</h3><p>Sie können auch mehrere Abfragen mit unterschiedlichen Gewichtungsfaktoren mithilfe einer booleschen Abfrage kombinieren. Dies ermöglicht es Ihnen, die Wichtigkeit jedes Feldes in den Suchergebnissen feinabzustimmen. Hier ist ein Beispiel für eine Boolesche Abfrage mit unterschiedlichen Gewichtungsfaktoren für die Felder „Titel“ und „Beschreibung“:</p><pre><code>{
  "query": {
    "bool": {
      "should": [
        {"match": {"title": {"query": "example", "boost": 3}}},
        {"match": {"description": {"query": "example", "boost": 1}}}
      ]
    }
  }
}</code></pre><p>In diesem Beispiel hat das Feld „Titel“ einen Boost-Faktor von 3, während das Feld „Beschreibung“ einen Boost-Faktor von 1 hat.</p><h2>Fazit</h2><p>Die Suche anhand zweier Felder in Elasticsearch kann mithilfe fortgeschrittener Techniken wie Multi-Match-Abfragen, Booleschen Abfragen und Query-Time Field Boosting erreicht werden. Durch die Kombination dieser Techniken können Sie genauere und relevantere Suchergebnisse für Ihre Nutzer erstellen. Experimentieren Sie mit verschiedenen Abfragekombinationen und Gewichtungsfaktoren, um die optimale Suchkonfiguration für Ihren spezifischen Anwendungsfall zu finden.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[​​Erstellung eines RAG-Workflows mit LangGraph und Elasticsearch]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie eine LangGraph Retrieval Agent Template mit Elasticsearch konfigurieren und anpassen, um einen RAG-Workflow für effizienten Datenabruf und KI-gesteuerte Antworten zu erstellen.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/build-rag-workflow-langgraph-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/build-rag-workflow-langgraph-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Fri, 25 Apr 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/17b7e7b336f73e232375d1add582ae5f6c52a279-1440x840.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Die <a href="https://github.com/langchain-ai/retrieval-agent-template">LangGraph Retrieval Agent-Vorlage</a> ist ein von LangChain entwickeltes Starterprojekt, das die Erstellung abrufbasierter Frage-Antwort-Systeme mit LangGraph in LangGraph Studio erleichtern soll. Diese Vorlage ist für die nahtlose Integration mit Elasticsearch vorkonfiguriert und ermöglicht Entwicklern die schnelle Erstellung von Agenten, die Dokumente effizient indizieren und abrufen können.</p><p>In diesem Blog geht es um das Ausführen und Anpassen der LangChain Retrieval Agent-Vorlage mit LangGraph Studio und LangGraph CLI. Die Vorlage bietet ein Framework zum Erstellen von Retrieval-Augmented Generation (RAG)-Anwendungen und nutzt verschiedene Retrieval-Backends wie Elasticsearch.</p><p>Wir führen Sie durch die Einrichtung, Konfiguration der Umgebung und effiziente Ausführung der Vorlage mit Elastic, während wir den Agentenfluss anpassen.</p><h2>Voraussetzungen</h2><p>Bevor Sie fortfahren, stellen Sie sicher, dass Folgendes installiert ist:</p><ul><li>Elasticsearch Cloud-Bereitstellung oder lokale Elasticsearch-Bereitstellung (oder erstellen Sie eine 14-tägige <a href="https://www.elastic.co/de/cloud/cloud-trial-overview">kostenlose Testversion </a>auf Elastic Cloud) – Version 8.0.0 oder höher</li><li>Python 3.9+</li><li>Zugriff auf einen LLM-Anbieter wie <a href="https://cohere.com/">Cohere</a> (in diesem Handbuch verwendet), <a href="https://openai.com/">OpenAI</a> oder <a href="https://www.anthropic.com/claude">Anthropic/Claude</a></li></ul><h2>Erstellen der LangGraph-App</h2><h3>1. Installieren Sie die LangGraph CLI</h3><pre><code>pip install --upgrade "langgraph-cli[inmem]"</code></pre><h3>2. Erstellen Sie eine LangGraph-App aus der Retrieval-Agent-Vorlage</h3><pre><code>mkdir lg-agent-demo
cd lg-agent-demo
langgraph new lg-agent-demo </code></pre><p><em>Ihnen wird ein interaktives Menü angezeigt, in dem Sie aus einer Liste verfügbarer Vorlagen auswählen können. </em>Wählen Sie 4 für Retrieval Agent und 1 für Python, wie unten gezeigt:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6a41a41f95c2477c67810adc7be46d91faf06878-1600x407.png" style="width: 100%;" /><ul><li><strong>Fehlerbehebung</strong>: Wenn der Fehler „urllib.error.URLError: &lt;urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1000)&gt;“ auftritt, „</li></ul><p>Führen Sie den Python-Befehl „Zertifikat installieren“ aus, um das Problem wie unten gezeigt zu beheben.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/83ec238136c41738457299fd42c83aff32eb5b97-1407x75.png" style="width: 100%;" /><h3>3. Abhängigkeiten installieren</h3><p>Erstellen Sie im Stammverzeichnis Ihrer neuen LangGraph-App eine virtuelle Umgebung und installieren Sie die Abhängigkeiten im Modus <code>edit</code> , damit Ihre lokalen Änderungen vom Server verwendet werden:</p><pre><code>#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 .</code></pre><h2>Einrichten der Umgebung</h2><h3>1. Erstellen Sie eine .environment-Umgebung Datei</h3><p>Die Datei <code>.env</code> enthält API-Schlüssel und Konfigurationen, damit die App eine Verbindung mit dem von Ihnen gewählten LLM- und Abrufanbieter herstellen kann. Generieren Sie eine neue <code>.env</code> -Datei, indem Sie die Beispielkonfiguration duplizieren:</p><pre><code>cp .env.example .env</code></pre><h3>2. Konfigurieren Sie die .env-Datei. Datei</h3><p>Die Datei <code>.env</code> enthält eine Reihe von Standardkonfigurationen. Sie können es aktualisieren, indem Sie basierend auf Ihrem Setup die erforderlichen API-Schlüssel und -Werte hinzufügen. Alle Schlüssel, die für Ihren Anwendungsfall nicht relevant sind, können unverändert bleiben oder entfernt werden.</p><pre><code># 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</code></pre><ul><li>Beispieldatei <code>.env</code> (unter Verwendung von Elastic Cloud und Cohere)</li></ul><p>Nachfolgend finden Sie eine Beispielkonfiguration <code>.env</code> für die Verwendung <strong>von Elastic Cloud</strong> als Abrufanbieter und <strong>Cohere</strong> als LLM, wie in diesem Blog gezeigt:</p><pre><code># 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</code></pre><p><em>Hinweis: In dieser Anleitung wird Cohere sowohl für die Antwortgenerierung als auch für die Einbettung verwendet. Sie können je nach Anwendungsfall jedoch auch andere LLM-Anbieter wie <strong>OpenAI,</strong> <strong>Claude</strong> oder sogar ein lokales LLM-Modell verwenden. Stellen Sie sicher, dass alle <code>.env</code> Schlüssel, die Sie verwenden möchten, in der Datei vorhanden und korrekt festgelegt sind.</em></p><h3>3. Aktualisieren Sie die Konfigurationsdatei -configuration.py </h3><p>Nachdem Sie Ihre <code>.env</code> -Datei mit den entsprechenden API-Schlüsseln eingerichtet haben, besteht der nächste Schritt darin, die Standardmodellkonfiguration Ihrer Anwendung zu aktualisieren. Durch die Aktualisierung der Konfiguration wird sichergestellt, dass das System die Dienste und Modelle verwendet, die Sie in Ihrer <code>.env</code> -Datei angegeben haben.</p><p>Navigieren Sie zur Konfigurationsdatei:</p><pre><code> cd src/retrieval_graph</code></pre><p>Die Datei <code>configuration.py</code> enthält die Standardmodelleinstellungen, die vom Abrufagenten für drei Hauptaufgaben verwendet werden:</p><ul><li><strong>Einbettungsmodell</strong> – konvertiert Dokumente in Vektordarstellungen</li><li><strong>Abfragemodell</strong> – verarbeitet die Abfrage des Benutzers in einen Vektor</li><li><strong>Antwortmodell</strong> – generiert die endgültige Antwort</li></ul><p>Standardmäßig verwendet der Code Modelle von <strong>OpenAI</strong> (z. B. <code>openai/text-embedding-3-small</code>) und <strong>Anthropic</strong> (z. <code>anthropic/claude-3-5-sonnet-20240620 and anthropic/claude-3-haiku-20240307</code>).<br/><br/>In diesem Blog wechseln wir zur Verwendung von Cohere-Modellen. Wenn Sie bereits OpenAI oder Anthropic verwenden, sind keine Änderungen erforderlich.</p><h4>Beispieländerungen (mit Cohere):</h4><p>Öffnen Sie <code>configuration.py</code> und ändern Sie die Modellstandards wie unten gezeigt:</p><pre><code>…
 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={</code></pre><h2>Ausführen des Abrufagenten mit der LangGraph-Befehlszeilenschnittstelle</h2><h3>1. Starten Sie den LangGraph-Server</h3><pre><code>cd lg-agent-demo
langgraph dev</code></pre><p>Dadurch wird der LangGraph-API-Server lokal gestartet. Wenn dies erfolgreich ausgeführt wird, sollten Sie etwa Folgendes sehen:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e3c3344b24651067e2d0892d870feca505b3be35-1494x542.png" style="width: 100%;" /><p>Öffnen Sie die URL der Studio-Benutzeroberfläche.</p><p>Es stehen zwei Diagramme zur Verfügung:</p><ul><li><strong>Retrieval-Graph</strong>: Ruft Daten aus Elasticsearch ab und beantwortet die Anfrage mithilfe eines LLM.</li><li><strong>Indexer-Graph</strong>: Indiziert Dokumente in Elasticsearch und generiert Einbettungen mithilfe eines LLM.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/99b579ca44b2d13f61da7e1de5886ea5b9a5e16f-1600x1009.png" style="max-width:100%; margin-bottom:1em;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f54d182d2789b6a4d113b0c88063531ea0b6de5d-1600x1009.png" style="max-width:100%; margin-bottom:1em;" /><h3>2. Konfigurieren des Indexergraphen</h3><ul><li>Öffnen Sie den Indexergraphen.</li><li>Klicken Sie auf „Assistenten verwalten“.<ul><li>Klicken Sie auf <strong>„Neuen Assistenten hinzufügen</strong>“, geben Sie die Benutzerdaten wie angegeben ein und schließen Sie anschließend das Fenster.</li></ul></li></ul><pre><code>{"user_id": "101"}</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6f87958e40760ac9671639f4995962bcea090991-1600x1139.png" style="max-width:100%; margin-bottom:1em;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8f802f6b65b3f06d7d1a521c3f40385c6006413b-1422x1212.png" style="max-width:100%; margin-bottom:1em;" /><h3>3. Beispieldokumente indexieren</h3><ul><li>Indizieren Sie die folgenden Beispieldokumente, die einen hypothetischen Quartalsbericht für die Organisation NoveTech darstellen:</li></ul><pre><code>[
  {    "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&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.'"
  }
]</code></pre><p>Sobald die Dokumente indiziert sind, wird im Thread eine Löschmeldung angezeigt, wie unten dargestellt.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/fd3a1efd64cb54d54ea56ef5055249dd066d5708-1600x854.png" style="width: 100%;" /><h3>4. Ausführen des Abrufgraphen</h3><ul><li>Wechseln Sie zum Abrufgraphen.</li><li>Geben Sie die folgende Suchanfrage ein:</li></ul><pre><code>What was NovaTech Solutions total revenue in Q1 2025?</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/eb4d8ddfe0effd7e1868fba921b8ef13f7baf27a-1600x755.png" style="width: 100%;" /><p>Das System gibt relevante Dokumente zurück und liefert eine genaue Antwort basierend auf den indizierten Daten.</p><h2>Passen Sie den Abrufagenten an.</h2><p>Um das Benutzererlebnis zu verbessern, führen wir einen Anpassungsschritt im Abrufgraphen ein, um die nächsten drei Fragen vorherzusagen, die ein Benutzer stellen könnte. Diese Vorhersage basiert auf Folgendem:</p><ul><li>Kontext aus den abgerufenen Dokumenten</li><li>Vorherige Benutzerinteraktionen</li><li>Letzte Benutzerabfrage</li></ul><p>Zur Implementierung der Abfragevorhersagefunktion sind die folgenden Codeänderungen erforderlich:</p><h3>1. Aktualisiere graph.py</h3><ul><li>Funktion <code>predict_query</code> hinzufügen:</li></ul><pre><code>async def predict_query(
   state: State, *, config: RunnableConfig
) -> 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]}</code></pre><ul><li>Ändern Sie die Funktion <code>respond</code> , um das Objekt <strong><code>response</code></strong> anstelle der Nachricht zurückzugeben:</li></ul><pre><code>async def respond(
   state: State, *, config: RunnableConfig
) -> 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]}</code></pre><ul><li>Aktualisieren Sie die Graphstruktur, um einen neuen Knoten und eine neue Kante für predict_query hinzuzufügen:</li></ul><pre><code>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")</code></pre><h3>2. Aktualisiere prompts.py</h3><ul><li>Erstelle eine Eingabeaufforderung für eine Vorhersage in <code>prompts.py</code>:</li></ul><pre><code>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}"""</code></pre><h3>3. Aktualisieren Sie die Datei configuration.py</h3><ul><li>Fügen Sie <code>predict_next_question_prompt</code> hinzu:</li></ul><pre><code>predict_next_question_prompt: str = field(
       default=prompts.PREDICT_NEXT_QUESTION_PROMPT,
       metadata={"description": "The system prompt used for generating responses."},
   )</code></pre><h3>4. Aktualisiere state.py</h3><ul><li>Fügen Sie die folgenden Attribute hinzu:</li></ul><pre><code>response: Annotated[Sequence[AnyMessage], add_messages]
next_question : Annotated[Sequence[AnyMessage], add_messages]</code></pre><h3>5. Führen Sie den Abrufgraphen erneut aus.</h3><ul><li>Geben Sie die folgende Suchanfrage erneut ein:</li></ul><pre><code>What was NovaTech Solutions total revenue in Q1 2025?</code></pre><p>Das System verarbeitet die Eingabe und sagt drei verwandte Fragen voraus, die Benutzer stellen könnten, wie unten gezeigt.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/70e855a2e4edc0ba5a147588df0de30eb081d053-1600x777.png" style="width: 100%;" /><h2>Fazit</h2><p>Die Integration der Retrieval Agent-Vorlage in LangGraph Studio und CLI bietet mehrere wichtige Vorteile:</p><ul><li><strong>Beschleunigte Entwicklung</strong>: Die Vorlagen- und Visualisierungstools optimieren die Erstellung und das Debuggen von Abruf-Workflows und verkürzen so die Entwicklungszeit.</li><li><strong>Nahtlose Bereitstellung</strong>: Integrierte Unterstützung für APIs und automatische Skalierung gewährleistet eine reibungslose Bereitstellung in allen Umgebungen.</li><li><strong>Einfache Updates:</strong> Das Ändern von Arbeitsabläufen, das Hinzufügen neuer Funktionen und die Integration zusätzlicher Knoten ist einfach, wodurch der Abrufprozess leichter skaliert und verbessert werden kann.</li><li><strong>Permanenter Speicher</strong>: Das System behält Agentenzustände und -wissen bei und verbessert so Konsistenz und Zuverlässigkeit.</li><li><strong>Flexible Workflow-Modellierung</strong>: Entwickler können Abruflogik und Kommunikationsregeln für bestimmte Anwendungsfälle anpassen.</li><li><strong>Interaktion und Debugging in Echtzeit</strong>: Die Möglichkeit zur Interaktion mit laufenden Agenten ermöglicht effizientes Testen und Problemlösen.</li></ul><p>Durch die Nutzung dieser Funktionen können Unternehmen leistungsstarke, effiziente und skalierbare Abrufsysteme erstellen, die die Datenzugänglichkeit und das Benutzererlebnis verbessern.</p><p>Der vollständige Quellcode für dieses Projekt ist auf <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/langraph-retrieval-agent-template-demo">GitHub</a> verfügbar.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Wie Sie Better Binary Quantization (BBQ) in Ihren Anwendungsfall implementieren können]]></title>
            <description><![CDATA[Erfahren Sie, warum Sie Better Binary Quantization (BBQ) in Ihrem Anwendungsfall einsetzen sollten und wie Sie dabei vorgehen.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/bbq-implementation-into-use-case</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/bbq-implementation-into-use-case</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Sachin Frayne]]></dc:creator>
            <pubDate>Wed, 23 Apr 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/66842055367cdd795532b01c167f2a4b03dc65e3-1200x628.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Die Vektorsuche bildet die Grundlage für die Implementierung der semantischen Suche nach Text oder der Ähnlichkeitssuche nach Bildern, Videos oder Audio. Bei der Vektorsuche handelt es sich bei den Vektoren um mathematische Darstellungen von Daten, die sehr groß und manchmal langsam sein können. Die bessere binäre Quantisierung (im Folgenden als BBQ bezeichnet) funktioniert als Komprimierungsmethode für Vektoren. Es ermöglicht Ihnen, die richtigen Übereinstimmungen zu finden und gleichzeitig die Vektoren zu verkleinern, damit sie schneller durchsucht und verarbeitet werden können. Dieser Artikel behandelt BBQ und rescore_vector, ein Feld, das nur für quantisierte Indizes verfügbar ist und Vektoren automatisch neu bewertet.</p><p>Alle vollständigen Abfragen und Ausgaben, die in diesem Artikel erwähnt werden, finden Sie in unserem <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/how-and-why-bbq">Elasticsearch Labs-Code-Repository</a>.</p><h2>Warum sollten Sie Better Binary Quantization (BBQ) in Ihrem Anwendungsfall einsetzen?</h2><blockquote>Hinweis: Um ein tieferes Verständnis der Mathematik hinter BBQ zu erhalten, lesen Sie bitte den <a href="https://www.elastic.co/de/search-labs/blog/bbq-implementation-into-use-case#further-learning">Abschnitt „Weiterführende Informationen“</a> weiter unten. Für die Zwecke dieses Blogs liegt der Schwerpunkt auf der Implementierung.</blockquote><p>Die mathematischen Hintergründe sind zwar faszinierend, aber unerlässlich, wenn Sie vollständig verstehen wollen, warum Ihre Vektorsuchen präzise bleiben. Letztendlich dreht sich alles um Komprimierung, da sich herausgestellt hat, dass man mit den aktuellen Vektorsuchalgorithmen durch die Lesegeschwindigkeit der Daten begrenzt ist. Wenn Sie also all diese Daten im Arbeitsspeicher unterbringen können, erzielen Sie im Vergleich zum Lesen vom Speicher eine erhebliche Geschwindigkeitssteigerung (<a href="https://sre.google/static/pdf/rule-of-thumb-latency-numbers-letter.pdf">der Arbeitsspeicher ist etwa 200-mal schneller als SSDs</a>).</p><p>Es gibt ein paar Dinge zu beachten:</p><ul><li>Graphenbasierte Indizes wie <a href="https://arxiv.org/pdf/1603.09320">HNSW</a> (Hierarchical Navigable Small World) sind für die Vektorabfrage am schnellsten.<ul><li>HNSW: Ein ungefährer Suchalgorithmus für den nächsten Nachbarn, der eine mehrschichtige Graphstruktur erstellt, um effiziente hochdimensionale Ähnlichkeitssuchen zu ermöglichen.</li></ul></li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/590f3b3c72a76023a38a0436cd9ff90a9f80e936-1964x1262.png" style="width: 100%;" /><ul><li>Die Geschwindigkeit von HNSW wird grundsätzlich durch die Datenlesegeschwindigkeit aus dem Speicher oder im schlimmsten Fall aus dem Speicher begrenzt.<ul><li>Idealerweise möchten Sie alle Ihre gespeicherten Vektoren in den Speicher laden können.</li></ul></li><li>Einbettungsmodelle erzeugen im Allgemeinen Vektoren mit Float32-Präzision, 4 Bytes pro Gleitkommazahl.</li><li>Und schließlich kann es, je nachdem, wie viele Vektoren und/oder Dimensionen Sie haben, sehr schnell passieren, dass Ihnen nicht genügend Speicher zur Verfügung steht, um alle Ihre Vektoren zu speichern.</li></ul><p>Wenn man dies als gegeben voraussetzt, erkennt man, dass schnell ein Problem entsteht, wenn man Millionen oder sogar Milliarden von Vektoren mit jeweils potenziell Hunderten oder sogar Tausenden von Dimensionen verarbeitet. Der Abschnitt „ <a href="https://www.elastic.co/de/search-labs/blog/bbq-implementation-into-use-case#approximate-numbers-on-the-compression-ratios">Ungefähre Zahlen zu den Kompressionsverhältnissen</a>“ enthält einige grobe Zahlen.</p><h2>Was brauchen Sie für den Anfang?</h2><p>Für den Anfang benötigen Sie Folgendes:</p><ul><li>Wenn Sie Elastic Cloud oder vor Ort verwenden, benötigen Sie eine höhere Version von Elasticsearch als 8.18. Während BBQ in 8.16 eingeführt wurde, verwenden Sie in diesem Artikel <code>vector_rescore</code>, das in 8.18 eingeführt wurde.</li><li>Darüber hinaus müssen Sie sicherstellen, dass in Ihrem Cluster ein <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/8.18/ml-settings.html">Knoten für maschinelles Lernen (ML)</a> vorhanden ist. (Hinweis: Zum Laden des Modells ist ein ML-Knoten mit mindestens 4 GB erforderlich, für die vollständige Produktionsarbeitslast werden Sie jedoch wahrscheinlich viel größere Knoten benötigen.)</li><li>Wenn Sie Serverless verwenden, müssen Sie eine Instanz auswählen, die für Vektoren optimiert ist.</li><li>Darüber hinaus benötigen Sie Grundkenntnisse im Umgang mit Vektordatenbanken. Wenn Sie mit den Konzepten der Vektorsuche in Elastic noch nicht vertraut sind, sollten Sie sich zunächst die folgenden Ressourcen ansehen:<ul><li><a href="https://www.elastic.co/de/search-labs/blog/elastic-vector-database-practical-example">Navigieren in einer Elastic Vector-Datenbank</a></li><li><a href="https://www.elastic.co/de/blog/retrieval-augmented-generation-explained">Die großen Ideen hinter der Retrieval Augmented Generation</a></li></ul></li></ul><h2>Verbesserte Binärquantisierung (BBQ)-Implementierung</h2><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4d388078495566f0527e931e0c2e38facdce83c6-1503x748.png" style="width: 100%;" /><p>Um diesen Blog einfach zu halten, verwenden Sie integrierte Funktionen, wenn diese verfügbar sind. In diesem Fall verfügen Sie über das Vektor-Einbettungsmodell <a href="https://www.elastic.co/de/guide/en/machine-learning/8.17/ml-nlp-e5.html"><code>.multilingual-e5-small</code></a> , das direkt in Elasticsearch auf einem Machine-Learning-Knoten ausgeführt wird. Beachten Sie, dass Sie das Modell <code>text_embedding</code> durch den Embedder Ihrer Wahl ersetzen können (<a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/8.18/infer-service-openai.html">OpenAI</a>, <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/8.18/infer-service-google-ai-studio.html">Google AI Studio</a>, <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/8.18/infer-service-cohere.html">Cohere</a> und viele mehr). Wenn Ihr bevorzugtes Modell noch nicht integriert ist, können Sie auch <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/8.18/bring-your-own-vectors.html">Ihre eigenen dichten Vektoreinbettungen mitbringen</a>.)</p><p>Zuerst müssen Sie einen Inferenzendpunkt erstellen, um Vektoren für einen bestimmten Textabschnitt zu generieren. Sie führen alle diese Befehle von der Kibana <a href="https://www.elastic.co/de/guide/en/kibana/8.18/console-kibana.html">Dev Tools-Konsole</a> aus. Dieser Befehl lädt <code>.multilingual-e5-small</code> herunter. Wenn es noch nicht vorhanden ist, wird Ihr Endpunkt eingerichtet. Dies kann eine Minute dauern. Sie können die erwartete Ausgabe in der Datei <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/how-and-why-bbq/Outputs/01-create-an-inference-endpoint-output.json">01-create-an-inference-endpoint-output.json</a> im Ordner „Outputs“ sehen. </p><pre><code>PUT _inference/text_embedding/my_e5_model
{
  "service": "elasticsearch",
  "service_settings": {
    "num_threads": 1,
    "model_id": ".multilingual-e5-small",
    "adaptive_allocations": {
      "enabled": true,
      "min_number_of_allocations": 1
    }
  }
}</code></pre><p>Sobald dies zurückgegeben wurde, ist Ihr Modell eingerichtet und Sie können mit dem folgenden Befehl testen, ob das Modell wie erwartet funktioniert. Sie können die erwartete Ausgabe in der Datei <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/how-and-why-bbq/Outputs/02-embed-text-output.json">02-embed-text-output.json</a> im Ordner „Outputs“ sehen.</p><pre><code>POST _inference/text_embedding/my_e5_model
{
  "input": "my awesome piece of text"
}</code></pre><p>Wenn bei Ihnen Probleme auftreten, weil Ihr trainiertes Modell keinem Knoten zugewiesen wird, müssen Sie Ihr Modell möglicherweise manuell starten.</p><pre><code>POST _ml/trained_models/.multilingual-e5-small/deployment/_start</code></pre><p>Erstellen wir nun eine neue Zuordnung mit 2 Eigenschaften, einem Standardtextfeld (<code>my_field</code>) und einem dichten Vektorfeld (<code>my_vector</code>) mit 384 Dimensionen, um der Ausgabe des Einbettungsmodells zu entsprechen. Sie werden auch <code>index_options.type to bbq_hnsw</code> überschreiben. Sie können die erwartete Ausgabe in der Datei <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/how-and-why-bbq/Outputs/03-create-byte-qauntized-index-output.json">03-create-byte-qauntized-index-output.json</a> im Ordner „Outputs“ sehen.</p><pre><code>PUT bbq-my-byte-quantized-index
{
  "mappings": {
    "properties": {
      "my_field": {
        "type": "text"
      },
      "my_vector": {
        "type": "dense_vector",
        "dims": 384,
        "index_options": {
          "type": "bbq_hnsw"
        }
      }
    }
  }
}</code></pre><p>Um sicherzustellen, dass Elasticsearch Ihre Vektoren generiert, können Sie eine <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/8.18/ingest.html">Ingest-Pipeline</a> verwenden. Diese Pipeline erfordert drei Dinge: den Endpunkt (<code>model_id</code>), die <code>input_field</code> , für die Sie Vektoren erstellen möchten, und die <code>output_field</code> , in der diese Vektoren gespeichert werden. Der erste Befehl unten erstellt eine Inferenz-Ingest-Pipeline, die den <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/inference-apis.html">Inferenzdienst </a>im Hintergrund verwendet, und der zweite testet, ob die Pipeline ordnungsgemäß funktioniert. Sie können die erwartete Ausgabe in der Datei <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/how-and-why-bbq/Outputs/04-create-and-simulate-ingest-pipeline-output.json">04-create-and-simulate-ingest-pipeline-output.json</a> im Ordner „Outputs“ sehen. </p><pre><code>PUT _ingest/pipeline/my_inference_pipeline
{
  "processors": [
    {
      "inference": {
        "model_id": "my_e5_model",
        "input_output": [
          {
            "input_field": "my_field",
            "output_field": "my_vector"
          }
        ]
      }
    }
  ]
}

POST _ingest/pipeline/my_inference_pipeline/_simulate
{
  "docs": [
    {
      "_source": {
        "my_field": "my awesome text field"
      }
    }
  ]
}</code></pre><p>Sie können nun mit den ersten beiden Befehlen unten einige Dokumente hinzufügen und mit dem dritten Befehl testen, ob Ihre Suchvorgänge funktionieren. Sie können die erwartete Ausgabe in der Datei <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/how-and-why-bbq/Outputs/05-bbq-index-output.json">05-bbq-index-output.json</a> im Ordner „Outputs“ überprüfen. </p><pre><code>PUT bbq-my-byte-quantized-index/_doc/1?pipeline=my_inference_pipeline
{
    "my_field": "my awesome text field"
}

PUT bbq-my-byte-quantized-index/_doc/2?pipeline=my_inference_pipeline
{
    "my_field": "some other sentence"
}

GET bbq-my-byte-quantized-index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "knn": {
            "field": "my_vector",
            "query_vector_builder": {
              "text_embedding": {
                "model_id": "my_e5_model",
                "model_text": "my awesome search field"
              }
            },
            "k": 10,
            "num_candidates": 100
          }
        }
      ]
    }
  },
  "_source": [
    "my_field"
  ]
}</code></pre><p>Wie in <a href="https://www.elastic.co/de/search-labs/blog/better-binary-quantization-lucene-elasticsearch#lucene-benchmarking">diesem Beitrag</a> empfohlen, sind Neubewertung und Überabtastung ratsam, wenn Sie auf nicht triviale Datenmengen skalieren, da sie dazu beitragen, eine hohe Rückrufgenauigkeit aufrechtzuerhalten und gleichzeitig von den Komprimierungsvorteilen zu profitieren. Ab Elasticsearch Version 8.18 können Sie dies auf diese Weise mit <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/8.18/knn-search.html#dense-vector-knn-search-rescoring">rescore_vector</a> tun. Die erwartete Ausgabe befindet sich in der Datei <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/how-and-why-bbq/Outputs/06-bbq-search-8-18-output.json">06-bbq-search-8-18-output.json</a> im Ordner „Outputs“.</p><pre><code>GET bbq-my-byte-quantized-index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "knn": {
            "field": "my_vector",
            "query_vector_builder": {
              "text_embedding": {
                "model_id": "my_e5_model",
                "model_text": "my awesome search field"
              }
            },
            "rescore_vector": {
              "oversample": 3
            },
            "k": 10,
            "num_candidates": 100
          }
        }
      ]
    }
  },
  "_source": [
    "my_field"
  ]
}</code></pre><p>Wie schneiden diese Werte im Vergleich zu denen ab, die Sie für Rohdaten erhalten würden? Wenn Sie alles oben Gesagte noch einmal machen, aber mit <code>index_options.type: hnsw</code>, werden Sie sehen, dass die Ergebnisse sehr vergleichbar sind. Sie können die erwartete Ausgabe in der Datei <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/how-and-why-bbq/Outputs/07-raw-vector-output.json">07-raw-vector-output.json</a> im Ordner „Outputs“ sehen.</p><pre><code>PUT my-raw-vector-index
{
  "mappings": {
    "properties": {
      "my_field": {
        "type": "text"
      },
      "my_vector": {
        "type": "dense_vector",
        "dims": 384,
        "index_options": {
          "type": "hnsw"
        }
      }
    }
  }
}

PUT my-raw-vector-index/_doc/1?pipeline=my_inference_pipeline
{
    "my_field": "my awesome text field"
}

PUT my-raw-vector-index/_doc/2?pipeline=my_inference_pipeline
{
    "my_field": "some other sentence"
}

GET my-raw-vector-index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "knn": {
            "field": "my_vector",
            "query_vector_builder": {
              "text_embedding": {
                "model_id": "my_e5_model",
                "model_text": "my awesome search field"
              }
            },
            "k": 10,
            "num_candidates": 100
          }
        }
      ]
    }
  },
  "_source": [
    "my_field"
  ]
}</code></pre><h2>Ungefähre Zahlen zu den Kompressionsverhältnissen</h2><p>Bei der Arbeit mit der Vektorsuche können Speicher- und Arbeitsspeicheranforderungen schnell zu einer erheblichen Herausforderung werden. Die folgende Aufschlüsselung veranschaulicht, wie verschiedene Quantisierungstechniken den Speicherbedarf von Vektordaten drastisch reduzieren.</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Vektoren (V)</td><td style="border: 1px solid #ccc; padding: 4px;">Abmessungen (D)</td><td style="border: 1px solid #ccc; padding: 4px;">roh (V x D x 4)</td><td style="border: 1px solid #ccc; padding: 4px;">int8 (V x (D x 1 + 4))</td><td style="border: 1px solid #ccc; padding: 4px;">int4 (V x (D x 0,5 + 4))</td><td style="border: 1px solid #ccc; padding: 4px;">Grill (V x (D x 0,125 + 4))</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">10.000.000</td><td style="border: 1px solid #ccc; padding: 4px;">384</td><td style="border: 1px solid #ccc; padding: 4px;">14,31 GB</td><td style="border: 1px solid #ccc; padding: 4px;">3,61 GB</td><td style="border: 1px solid #ccc; padding: 4px;">1,83 GB</td><td style="border: 1px solid #ccc; padding: 4px;">0,58 GB</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">50.000.000</td><td style="border: 1px solid #ccc; padding: 4px;">384</td><td style="border: 1px solid #ccc; padding: 4px;">71,53 GB</td><td style="border: 1px solid #ccc; padding: 4px;">18,07 GB</td><td style="border: 1px solid #ccc; padding: 4px;">9,13 GB</td><td style="border: 1px solid #ccc; padding: 4px;">2,89 GB</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">100.000.000</td><td style="border: 1px solid #ccc; padding: 4px;">384</td><td style="border: 1px solid #ccc; padding: 4px;">143,05 GB</td><td style="border: 1px solid #ccc; padding: 4px;">36,14 GB</td><td style="border: 1px solid #ccc; padding: 4px;">18,25 GB</td><td style="border: 1px solid #ccc; padding: 4px;">5,77 GB</td>
                    </tr>
              </tbody>
            </table>
          <h2>Fazit</h2><p>BBQ ist eine Optimierung, die Sie zur Komprimierung Ihrer Vektordaten anwenden können, ohne die Genauigkeit zu beeinträchtigen. Dabei werden Vektoren in Bits umgewandelt, sodass Sie die Daten effektiv durchsuchen und Ihre KI-Workflows skalieren können, um die Suche zu beschleunigen und die Datenspeicherung zu optimieren.</p><h2>Weiterführendes Lernen</h2><p>Wenn Sie mehr über BBQ erfahren möchten, sehen Sie sich unbedingt die folgenden Ressourcen an:</p><ul><li><a href="https://www.elastic.co/de/search-labs/blog/better-binary-quantization-lucene-elasticsearch">Binäre Quantisierung (BBQ) in Lucene und Elasticsearch</a></li><li><a href="https://www.elastic.co/de/search-labs/blog/bit-vectors-elasticsearch-bbq-vs-pq">Bessere binäre Quantisierung (BBQ) vs. Produktquantisierung</a></li><li><a href="https://www.elastic.co/de/search-labs/blog/optimized-scalar-quantization-elasticsearch">Optimierte skalare Quantisierung: Noch bessere binäre Quantisierung</a></li><li><a href="https://www.youtube.com/watch?v=04NzMt2Nigc">Bessere binäre Quantisierung (BBQ): Von Bytes bis BBQ, das Geheimnis einer besseren Vektorsuche von Ben Trent</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Elasticsearch-Heap-Speichernutzung und JVM-Garbage-Collection]]></title>
            <description><![CDATA[Untersuchung der Heap-Speichernutzung von Elasticsearch und der JVM-Garbage-Collection, einschließlich Best Practices und Lösungsansätzen für Probleme, wenn die Heap-Speichernutzung zu hoch ist oder die JVM-Leistung nicht optimal ist.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/elasticsearch-heap-size-jvm-garbage-collection</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/elasticsearch-heap-size-jvm-garbage-collection</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Tue, 22 Apr 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b162c28623b9070fd1980bcd891b9dd1e868f2f0-720x421.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Die Heap-Größe ist die Menge an RAM, die der Java Virtual Machine eines Elasticsearch-Knotens zugewiesen wird.</p><p>Ab Version 7.11 passt Elasticsearch die JVM-Heap-Größe standardmäßig automatisch an die Rollen und den Gesamtspeicher eines Knotens an. Für die meisten Produktionsumgebungen wird die Verwendung der Standarddimensionierung empfohlen. Wenn Sie jedoch die Größe Ihres JVM-Heaps manuell festlegen möchten, sollten Sie in der Regel -Xms und -Xmx auf den gleichen Wert setzen, der 50 % Ihres gesamten verfügbaren RAMs betragen sollte, maximal jedoch (ungefähr) 31 GB.</p><p>Eine größere Heap-Größe stellt Ihrem Knoten mehr Speicher für Indizierungs- und Suchvorgänge zur Verfügung. Allerdings benötigt Ihr Knoten auch Speicher für das Caching, daher sorgt die Verwendung von 50 % für ein gesundes Gleichgewicht zwischen den beiden. Aus demselben Grund sollten Sie im Produktivbetrieb vermeiden, andere speicherintensive Prozesse auf demselben Knoten wie Elasticsearch auszuführen.</p><p>Typischerweise folgt die Haufennutzung einem Sägezahnmuster und schwankt zwischen etwa 30 und 70 % der maximal genutzten Haufengröße. Dies liegt daran, dass die JVM den Heap-Nutzungsprozentsatz stetig erhöht, bis der Garbage-Collection-Prozess wieder Speicher freigibt. Eine hohe Heap-Speicherbelegung tritt auf, wenn der Garbage-Collection-Prozess nicht hinterherkommt. Ein Indikator für eine hohe Heap-Speicherbelegung ist, wenn die Garbage Collection nicht in der Lage ist, die Heap-Speicherbelegung auf etwa 30 % zu reduzieren.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0a17a67cc589a3c1fbf9e918eadc119df7bd7619-858x278.png" style="width: 100%;" /><p>Im obigen Bild ist ein normales Sägezahnmuster des JVM-Heaps zu sehen.</p><p>Sie werden auch feststellen, dass es zwei Arten von Müllabfuhr gibt, die junge und die alte Müllabfuhr.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8df5c24c4894404de4617be7a13683c9027d607d-875x281.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e01eb2537310b052580411153b8eddc187d97687-890x264.png" style="width: 100%;" /><p>In einer gesunden JVM sollte die Speicherbereinigung idealerweise die folgenden Bedingungen erfüllen:</p><ul><li>Young GC wird schnell verarbeitet (innerhalb von 50 ms).</li><li>Young GC wird nicht häufig ausgeführt (etwa alle 10 Sekunden).</li><li>Der alte GC wird schnell verarbeitet (innerhalb von 1 Sekunde).</li><li>Die alte Garbage Collection wird nicht häufig ausgeführt (einmal alle 10 Minuten oder seltener).</li></ul><h3><strong>Wie lässt sich das Problem beheben, wenn die Heap-Speichernutzung zu hoch ist oder die JVM-Leistung nicht optimal ist?</strong></h3><p>Es kann verschiedene Gründe geben, warum die Heap-Speicherbelegung ansteigen kann:</p><h4><strong>Übersharding</strong></h4><p>Das Dokument zum Thema Oversharding finden Sie <a href="https://www.elastic.co/docs/deploy-manage/production-guidance/optimize-performance/size-shards#sizing-shard-guidelines">hier</a>.</p><h4><strong>Große Aggregationsgrößen</strong></h4><p>Um große Aggregationsgrößen zu vermeiden, halten Sie die Anzahl der Aggregations-Buckets (Größe) in Ihren Abfragen so gering wie möglich.</p><pre><code>GET /_search
{
   "aggs" : {
       "products" : {
           "terms" : {
               "field" : "product",
               "size" : 5
                          }
       }
   }
}</code></pre><p>Sie können die Protokollierung langsamer Abfragen (Slow Logs) verwenden und sie auf einem bestimmten Index mit den folgenden Mitteln implementieren.</p><pre><code>PUT /my_index/_settings
{
   "index.search.slowlog.threshold.query.warn": "10s",
   "index.search.slowlog.threshold.query.info": "5s",
   "index.search.slowlog.threshold.query.debug": "2s",
   "index.search.slowlog.threshold.query.trace": "500ms",
   "index.search.slowlog.threshold.fetch.warn": "1s",
   "index.search.slowlog.threshold.fetch.info": "800ms",
   "index.search.slowlog.threshold.fetch.debug": "500ms",
   "index.search.slowlog.threshold.fetch.trace": "200ms",
   "index.search.slowlog.level": "info"
}</code></pre><p>Anfragen, deren Beantwortung lange dauert, sind wahrscheinlich ressourcenintensiv.</p><h4><strong>Übermäßige Größe des Massenindex</strong></h4><p>Wenn Sie große Anfragen senden, kann dies zu einem hohen Heap-Verbrauch führen. Versuchen Sie, die Größe der Massenindexierungsanfragen zu reduzieren.</p><h4><strong>Kartierungsprobleme</strong></h4><p>Insbesondere wenn Sie „fielddata: true“ verwenden, kann dies einen erheblichen Teil Ihres JVM-Heaps beanspruchen.</p><h4><strong>Die Haufengröße wurde falsch eingestellt.</strong></h4><p>Die Heap-Größe kann manuell definiert werden durch:</p><p>Festlegen der Umgebungsvariablen:</p><pre><code>ES_JAVA_OPTS="-Xms2g -Xmx2g"</code></pre><p>Bearbeiten der Datei jvm.options in Ihrem Elasticsearch-Konfigurationsverzeichnis:</p><pre><code>-Xms2g
-Xmx2g</code></pre><p>Die Einstellung der Umgebungsvariablen hat Vorrang vor der Dateieinstellung.</p><p>Der Knoten muss neu gestartet werden, damit die Einstellung übernommen wird.</p><h4><strong>Das neue JVM-Verhältnis wurde falsch eingestellt</strong></h4><p>Im Allgemeinen ist es NICHT notwendig, dies festzulegen, da Elasticsearch diesen Wert standardmäßig setzt. Dieser Parameter definiert das Verhältnis des verfügbaren Speicherplatzes für Objekte der „neuen Generation“ und der „alten Generation“ in der JVM.</p><p>Wenn Sie feststellen, dass old GC sehr häufig vorkommt, können Sie versuchen, diesen Wert in der Datei jvm.options in Ihrem Elasticsearch-Konfigurationsverzeichnis explizit festzulegen.</p><pre><code>-XX:NewRatio=3</code></pre><h3><strong>Was sind die besten Vorgehensweisen für die Verwaltung der Heap-Speicherbelegung und der JVM-Garbage-Collection in einem großen Elasticsearch-Cluster?</strong></h3><p>Die besten Vorgehensweisen für die Verwaltung der Heap-Speicherbelegung und der JVM-Garbage-Collection in einem großen Elasticsearch-Cluster bestehen darin, sicherzustellen, dass die Heap-Größe auf maximal 50 % des verfügbaren RAMs eingestellt ist und dass die JVM-Garbage-Collection-Einstellungen für den jeweiligen Anwendungsfall optimiert sind. Es ist wichtig, die Heap-Größe und die Garbage-Collection-Metriken zu überwachen, um sicherzustellen, dass der Cluster optimal läuft. Insbesondere ist es wichtig, die JVM-Heap-Größe, die Garbage-Collection-Zeit und die Garbage-Collection-Pausen zu überwachen. Darüber hinaus ist es wichtig, die Anzahl der Müllabfuhrzyklen und die für die Müllabfuhr aufgewendete Zeit zu überwachen. Durch die Überwachung dieser Kennzahlen können potenzielle Probleme mit der Heap-Größe oder den Einstellungen für die Speicherbereinigung erkannt und gegebenenfalls Korrekturmaßnahmen ergriffen werden.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Wie man die Anzahl der primären Shards in Elasticsearch erhöht]]></title>
            <description><![CDATA[Erfahren Sie, wie Sie die Anzahl der primären Shards in Elasticsearch mithilfe der Split- und Reindex-APIs für ein optimales Shard-Skalieren erhöhen können.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/elasticsearch-increase-primary-shard-count</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/elasticsearch-increase-primary-shard-count</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Thu, 17 Apr 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7034b76019a0cba52c25eda29fceb18afc96ed0b-720x420.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Es ist nicht möglich, die Anzahl der primären Shards eines bestehenden Index zu erhöhen. Das bedeutet, dass ein Index neu erstellt werden muss, wenn Sie die Anzahl der primären Shards erhöhen möchten. In solchen Situationen werden üblicherweise zwei Methoden verwendet: die _reindex API und die _split API.</p><p>Die _split-API ist oft eine schnellere Methode als die _reindex-API. <strong>Die Indizierung</strong> <strong>muss vor beiden Operationen gestoppt werden</strong> , da sich sonst die Dokumentanzahlen in source_index und target_index unterscheiden.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/aa0ae010c2f5691ca00440fb453ed6b47bacd24f-1200x628.png" style="width: 100%;" /><h2>Methode 1 – Verwendung der Split-API</h2><p>Die Split-API wird verwendet, um einen neuen Index mit der gewünschten Anzahl primärer Shards zu erstellen, indem die Einstellungen kopiert und ein vorhandener Index zugeordnet werden. Die gewünschte Anzahl primärer Shards kann bei der Erstellung festgelegt werden. Vor der Implementierung der Split-API sollten folgende Einstellungen überprüft werden:</p><ol><li>Der Quellindex muss schreibgeschützt sein. Dies bedeutet, dass der Indexierungsprozess gestoppt werden muss.</li><li>Die Anzahl der primären Shards im Zielindex muss ein Vielfaches der Anzahl der primären Shards im Quellindex sein. Wenn der Quellindex beispielsweise 5 primäre Shards hat, können die primären Shards des Zielindex auf 10, 15, 20 usw. festgelegt werden.</li></ol><p>Hinweis: Wenn nur die Nummer des primären Shards geändert werden muss, ist die Split-API vorzuziehen, da sie wesentlich schneller ist als die Reindex-API.</p><h3>Implementierung der Split-API</h3><p>Erstellen Sie einen Testindex:</p><pre><code>POST test_split_source/_doc
{
  "test": "test"
}</code></pre><p>Der Quellindex muss schreibgeschützt sein, um aufgeteilt werden zu können:</p><pre><code>PUT test_split_source/_settings
{
  "index.blocks.write": true
}</code></pre><p>Einstellungen und Zuordnungen werden automatisch aus dem Quellindex kopiert:</p><pre><code>POST /test_split_source/_split/test_split_target
{
  "settings": {
    "index.number_of_shards": 3
  }
}</code></pre><p>Den Fortschritt können Sie mit folgendem Link überprüfen:</p><pre><code>GET _cat/recovery/test_split_target?v&h=index,shard,time,stage,files_percent,files_total</code></pre><p>Da Einstellungen und Zuordnungen aus den Quellindizes kopiert werden, ist der Zielindex schreibgeschützt. Aktivieren wir nun den Schreibvorgang für den Zielindex:</p><pre><code>PUT test_split_target/_settings
{
    "index.blocks.write": null
}</code></pre><p>Prüfen Sie die Anzahl der Dokumente (docs.count) im Quell- und Zielindex, bevor Sie den ursprünglichen Index löschen:</p><pre><code>GET _cat/indices/test_split*?v&h=index,pri,rep,docs.count</code></pre><p>Indexname und Aliasname dürfen nicht identisch sein. Sie müssen den Quellindex löschen und den Namen des Quellindex als Alias zum Zielindex hinzufügen:</p><pre><code>DELETE test_split_source
PUT /test_split_target/_alias/test_split_source</code></pre><p>Nachdem Sie den Alias <strong>test_split_source</strong> zum Index <strong>test_split_target</strong> hinzugefügt haben, sollten Sie ihn wie folgt testen:</p><pre><code>GET test_split_source
POST test_split_source/_doc
{
  "test": "test"
}</code></pre><h2>Methode 2 – Verwendung der Reindex-API</h2><p>Durch die Erstellung eines neuen Index mit der Reindex-API kann eine beliebige Anzahl primärer Shards angegeben werden. Nach der Erstellung eines neuen Index mit der gewünschten Anzahl primärer Shards können alle Daten im Quellindex in diesen neuen Index neu indiziert werden.</p><p>Zusätzlich zu den Split-API-Funktionen können die Daten mithilfe der ingest_pipeline im Reindex-AP manipuliert werden. Bei der Ingest-Pipeline werden nur die angegebenen Felder, die dem Filter entsprechen, mithilfe der Abfrage in den Zielindex indiziert. Der Dateninhalt kann mithilfe eines einfachen Skripts geändert werden, und mehrere Indizes können zu einem einzigen Index zusammengeführt werden.</p><h3>Implementierung der Reindex-API</h3><p>Erstellen Sie einen Test-Reindex:</p><pre><code>POST test_reindex_source/_doc
{
    "test": "test"
}</code></pre><p>Kopieren Sie die Einstellungen und Zuordnungen aus dem Quellindex:</p><pre><code>GET test_reindex_source</code></pre><p>Erstellen Sie einen Zielindex mit Einstellungen, Zuordnungen und der gewünschten Shard-Anzahl:</p><pre><code>PUT test_reindex_target
{
  "mappings" : {},
  "settings": {
    "number_of_shards": 10,
    "number_of_replicas": 0,
    "refresh_interval": -1
  }
}</code></pre><p>*Hinweis: Die Einstellung number_of_replicas: 0 und refresh_interval: -1 erhöht die Geschwindigkeit der Neuindizierung.</p><p>Starten Sie den Reindexierungsprozess. Durch die Einstellung requests_per_second=-1 und slices=auto wird die Reindexierungsgeschwindigkeit angepasst.</p><pre><code>POST _reindex?requests_per_second=-1&slices=auto&wait_for_completion=false
{
  "source": {
    "index": "test_reindex_source"
  },
  "dest": {
    "index": "test_reindex_target"
  }
}</code></pre><p>Die task_id wird Ihnen beim Ausführen der Reindex-API angezeigt. Kopiere das und überprüfe es mit der _tasks-API:</p><pre><code>GET _tasks/<task_id></code></pre><p>Aktualisieren Sie die Einstellungen, nachdem die Neuindizierung abgeschlossen ist:</p><pre><code>PUT test_reindex_target/_settings
{
  "number_of_replicas": 1,
  "refresh_interval": "1s"
}</code></pre><p>Prüfen Sie vor dem Löschen des ursprünglichen Index die Anzahl der Dokumente (docs.count) im Quell- und Zielindex; sie sollte identisch sein.</p><pre><code>GET _cat/indices/test_reindex_*?v&h=index,pri,rep,docs.count</code></pre><p>Der Indexname und der Aliasname dürfen nicht identisch sein. Löschen Sie den Quellindex und fügen Sie den Namen des Quellindex als Alias zum Zielindex hinzu:</p><pre><code>DELETE test_reindex_source
PUT /test_reindex_target/_alias/test_reindex_source</code></pre><p>Nachdem Sie den Alias test_split_source zum Index test_split_target hinzugefügt haben, testen Sie ihn mit folgendem Befehl:</p><pre><code>GET test_reindex_source</code></pre><h2>Zusammenfassung</h2><p>Wenn Sie die Anzahl der primären Shards eines bestehenden Index erhöhen möchten, müssen Sie die Einstellungen und Zuordnungen für einen neuen Index neu erstellen. Hierfür gibt es zwei Hauptmethoden: die Reindex-API und die Split-API. Die aktive Indizierung muss vor Anwendung beider Methoden beendet werden.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Elasticsearch BBQ vs. OpenSearch FAISS: Leistungsvergleich der Vektorsuche]]></title>
            <description><![CDATA[Ein Leistungsvergleich zwischen Elasticsearch BBQ und OpenSearch FAISS.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/elasticsearch-bbq-vs-opensearch-faiss</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/elasticsearch-bbq-vs-opensearch-faiss</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Ugo Sangiorgi]]></dc:creator>
            <pubDate>Tue, 15 Apr 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/796de4829e29566f1f3efa2482f5c3e54b31b1d6-1536x1024.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p><strong>Vektorsuche mit binärer Quantisierung: Elasticsearch mit BBQ ist 5x schneller als OpenSearch mit FAISS</strong>. Elastic hat von unserer Community Anfragen erhalten, die Leistungsunterschiede zwischen Elasticsearch und OpenSearch zu klären, insbesondere im Bereich der semantischen Suche/Vektorsuche. Daher haben wir diese Leistungstests durchgeführt, um klare, datenbasierte Vergleiche zu ermöglichen.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7f4f8d08f7bee188423e4e65f0caefc7e34f0355-1600x681.png" style="width: 100%;" /><h2>Showdown der binären Quantisierung</h2><p>Das Speichern hochdimensionaler Vektoren in ihrer ursprünglichen Form kann speicherintensiv sein. Quantisierungstechniken komprimieren diese Vektoren in eine kompakte Darstellung und reduzieren so den Speicherbedarf drastisch. Die Suche erfolgt dann im komprimierten Raum, was den Rechenaufwand reduziert und die Suche insbesondere bei großen Datensätzen beschleunigt.</p><p>Elastic hat sich zum Ziel gesetzt, Lucene zu einer leistungsstarken Vektor-Engine zu machen. Wir haben <a href="https://www.elastic.co/de/search-labs/blog/better-binary-quantization-lucene-elasticsearch">Better Binary Quantization</a> (BBQ) in Elasticsearch 8.16 auf Basis von Lucene eingeführt und in den Versionen 8.18 und 9.0 weiterentwickelt. BBQ basiert auf einem neuen Ansatz der <a href="https://www.elastic.co/de/search-labs/blog/optimized-scalar-quantization-elasticsearch">Skalarquantisierung</a> , der die float32-Dimensionen auf Bits reduziert und so eine Speicherreduzierung von ca. 95 % bei gleichzeitig hoher Ranking-Qualität ermöglicht.</p><p>OpenSearch hingegen verwendet mehrere Vektor-Engines: nmslib (jetzt veraltet), Lucene und FAISS. In einem <a href="https://www.elastic.co/de/search-labs/blog/elasticsearch-opensearch-vector-search-performance-comparison">früheren Blog</a> haben wir Elasticsearch und OpenSearch für die Vektorsuche verglichen. Wir haben drei verschiedene Datensätze verwendet und unterschiedliche Kombinationen von Engines und Konfigurationen auf beiden Produkten getestet.</p><p>In diesem Blog geht es um die derzeit in beiden Produkten verfügbaren binären Quantisierungsalgorithmen. Wir haben Elasticsearch mit BBQ und OpenSearch mit <a href="https://opensearch.org/docs/latest/search-plugins/knn/knn-vector-quantization/#binary-quantization">der binären Quantisierung von FAISS</a> unter Verwendung des <a href="https://github.com/elastic/rally-tracks/edit/master/openai_vector">openai_vector</a> Rally-Tracks getestet.</p><p>Das Hauptziel bestand darin, die Leistung beider Lösungen bei gleichem Erinnerungsniveau zu bewerten. Was bedeutet <em>Rückruf</em> ? Die Rückrufquote ist eine Kennzahl, die misst, wie viele relevante Ergebnisse erfolgreich von einem Suchsystem abgerufen werden.</p><p>Bei dieser Auswertung ist insbesondere der Recall@k wichtig, wobei <em>k</em> die Anzahl der berücksichtigten Top-Ergebnisse darstellt. <strong>Recall@10</strong>, <strong>Recall@50 und Recall@100</strong> messen daher, wie viele der wirklich relevanten Ergebnisse in den ersten 10, 50 bzw. 100 abgerufenen Elementen erscheinen. Die Rückrufquote wird auf einer Skala von 0 bis 1 (oder 0 % bis 100 % Präzision) ausgedrückt. Und das ist wichtig, weil wir über ungefähres KNN (ANN) und nicht über exaktes KNN sprechen, bei dem die Rückrufrate immer 1 (100 %) beträgt.</p><p>Für jeden Wert von <em>k</em> haben wir auch <em>n angegeben, </em>also die Anzahl der Kandidaten, die vor der endgültigen Rangfolge berücksichtigt wurden. Dies bedeutet, dass das System für Recall@10, Recall@50 und Recall@100 zunächst <em>n</em> Kandidaten mithilfe des binären Quantisierungsalgorithmus abruft und sie dann in eine Rangfolge bringt, um zu bestimmen, ob die obersten <em>k</em> Ergebnisse die erwarteten relevanten Elemente enthalten.</p><p>Durch die Steuerung <em>von n</em> können wir den Kompromiss zwischen Effizienz und Genauigkeit analysieren. Ein höherer <em>n-Wert</em> <strong>erhöht</strong> normalerweise die Rückrufquote, da mehr Kandidaten für die Rangfolge zur Verfügung stehen, <strong>erhöht</strong> aber auch die Latenz und<strong> verringert </strong>den Durchsatz. Umgekehrt beschleunigt ein niedrigerer <em>n-</em> Wert zwar die Abfrage, kann aber die Trefferquote verringern, wenn zu wenige relevante Kandidaten im anfänglichen Satz enthalten sind.</p><p>In diesem Vergleich zeigte Elasticsearch bei identischen Setups eine geringere Latenz und einen höheren Durchsatz als OpenSearch.</p><h2>Methodik</h2><p>Die vollständige Konfiguration sowie Terraform-Skripte, Kubernetes-Manifeste und der spezifische Rally-Track sind in diesem <a href="https://github.com/elastic/elasticsearch-opensearch-vector-performance/tree/bbq">Repository</a> unter <a href="https://github.com/elastic/elasticsearch-opensearch-vector-performance/tree/bbq/rally-custom/custom_tracks/elasticsearch/openai_vector_bq"><em>openai_vector_bq</em></a> verfügbar.</p><p>Wie bei vorherigen Benchmarks haben wir einen Kubernetes-Cluster verwendet, der aus Folgendem besteht:</p><ul><li>1 Knotenpool für Elasticsearch 9.0 mit 3 <code>e2-standard-32</code> Maschinen (128 GB RAM und 32 CPUs)</li><li>1 Knotenpool für OpenSearch 2.19 mit 3 <code>e2-standard-32</code> Maschinen (128 GB RAM und 32 CPUs)</li><li>1 Knotenpool für Rally mit 2 <code>e2-standard-4</code> Maschinen (16 GB RAM und 4 CPUs)</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/169bb6033f6eebfd1b177b3446bf916fde4ee5c5-1600x856.png" style="width: 100%;" /><p>Wir haben einen Elasticsearch-Cluster Version 9.0 und einen OpenSearch-Cluster Version 2.19 eingerichtet.</p><p>Sowohl Elasticsearch als auch OpenSearch wurden mit genau demselben Setup getestet: Wir haben <a href="https://github.com/elastic/rally-tracks/edit/master/openai_vector">openai_vector</a> Rally Track mit <a href="https://github.com/elastic/elasticsearch-opensearch-vector-performance/commit/b97d5d95c22c8cf862f2030964524bdd156a5da3">einigen Modifikationen</a> verwendet, das 2,5 Millionen Dokumente aus dem <a href="https://huggingface.co/datasets/BeIR/nq">NQ-Datensatz</a> verwendet, angereichert mit Einbettungen, die mit <a href="https://openai.com/blog/new-and-improved-embedding-model">dem Modell text-embedding-ada-002</a> von OpenAI generiert wurden.</p><pre><code>{
  "source-file": "open_ai_corpus-initial-indexing.json.bz2",
  "document-count": 2580961,
  "compressed-bytes": 32076749416,
  "uncompressed-bytes": 90263571686
}</code></pre><p>Die Ergebnisse berichten über die gemessene Latenz und den Durchsatz bei verschiedenen Rückrufstufen (Recall@10, Recall@50 und Recall@100) unter Verwendung von 8 gleichzeitigen Clients zur Durchführung von Suchvorgängen. Wir haben einen einzelnen Shard und keine Replikate verwendet.</p><p>Wir haben die folgenden Kombinationen von kn-rescore ausgeführt, zB 10-2000-2000 oder <em>k:10</em>, <em>n:2000</em> und <em>rescore:2000</em> würden die besten k (10) von n Kandidaten (2000) abrufen, indem ein Rescore auf 2000 Ergebnisse angewendet wird (was einem „Oversample-Faktor“ von 1 entspricht). Jede Suche wurde 10.000 Mal ausgeführt, mit 1.000 Suchvorgängen als Aufwärmphase:</p><p></p><p><strong><span style="text-decoration:underline">Rückruf@10</span></strong></p><ul><li>10-40-40</li><li>10-50-50</li><li>10-100-100</li><li>10-200-200</li><li>10-500-500</li><li>10-750-750</li><li>10-1000-1000</li><li>10-1500-1500</li><li>10-2000-2000</li></ul><p><strong><span style="text-decoration:underline">Rückruf@50</span></strong></p><ul><li>50-150-150</li><li>50-200-200</li><li>50-250-250</li><li>50-500-500</li><li>50-750-750</li><li>50-1000-1000</li><li>50-1200-1200</li><li>50-1500-1500</li><li>50-2000-2000</li></ul><p><strong><span style="text-decoration:underline">Rückruf@100</span></strong></p><ul><li>100-200-200</li><li>100-250-250</li><li>100-300-300</li><li>100-500-500</li><li>100-750-750</li><li>100-1000-1000</li><li>100-1200-1200</li><li>100-1500-1500</li><li>100-2000-2000</li></ul><p>Um den Benchmark zu replizieren, sind in den Kubernetes-Manifesten für Rally-Elasticsearch und Rally-Opensearch alle relevanten Variablen in einer ConfigMap externalisiert, die <a href="https://github.com/elastic/elasticsearch-opensearch-vector-performance/blob/bbq/k8s/rally-openai_vector-es-bq.yml">hier</a> (ES) und <a href="https://github.com/elastic/elasticsearch-opensearch-vector-performance/blob/bbq/k8s/rally-openai_vector-os-bq.yml">hier</a> (OS) verfügbar ist. Der Parameter <em>search_ops</em> kann angepasst werden, um jede Kombination aus k, n und Rescore zu testen.</p><h3>OpenSearch Rally-Konfiguration</h3><p><code>/k8s/rally-openai_vector-os-bq.yml</code></p><pre><code>apiVersion: v1
kind: ConfigMap
metadata:
  name: rally-params-os
  labels:
    app: rally-opensearch
data:
  user-tags.json: |
    {
      "product": "OpenSearch",
      "product-version": "OpenSearch-2.19.0",
      "product-label": "OpenSearch-2.19-faiss",
      "benchmark-run": "19-feb-recall@100"
    }
  track-params.json: |
    {
      "mapping_type": "vectors-only-mapping-with-docid",
      "standalone_search_clients": 8,
      "standalone_search_iterations": 5000,
      "ann_threshold": 0,
      "vector_mode": "on_disk",
      "compression_level": "32x",
      "vector_method_name": "hnsw",
      "vector_method_engine": "faiss",
      "search_ops": [
        [100, 200, 200],
        [100, 250, 250],
        [100, 300, 300],
        [100, 500, 500],
        [100, 750, 750],
        [100, 1000, 1000],
        [100, 1200, 1200],
        [100, 1500, 1500],
        [100, 2000, 2000]
      ]
    }</code></pre><h3>Opensearch-Indexkonfiguration</h3><p>Die Variablen aus der ConfigMap werden dann auf die Indexkonfiguration angewendet, einige Parameter bleiben unverändert. Die 1-Bit-Quantisierung in OpenSearch wird durch <a href="https://opensearch.org/docs/latest/search-plugins/knn/knn-vector-quantization/#binary-quantization">Einstellen der Komprimierungsstufe auf „32x“</a> konfiguriert.</p><p><code>index-vectors-only-mapping-with-docid-mapping.json</code></p><pre><code>{
  "settings": {
    {% if preload_pagecache %}
    "index.store.preload": [
      "vec", "vex", "vem", "veq", "veqm", "veb", "vebm"
    ],
    {% endif %}
    "index.number_of_shards": {{ number_of_shards | default(1) }},
    "index.number_of_replicas": {{ number_of_replicas | default(0) }},
    "index.knn": true,
    "index.knn.advanced.approximate_threshold": {{ ann_threshold | default(15000) }}
  },
  "mappings": {
    "dynamic": false,
    "properties": {
      "docid": {
        "type": "keyword"
      },
      "emb": {
        "type": "knn_vector",
        "dimension": 1536,
        "space_type": "innerproduct",
        "data_type": "float",
        "mode": {{ vector_mode | default("in_memory") | tojson }},
        "compression_level": {{ compression_level | default("32x") | tojson }},
        "method": {
          "name": {{ vector_method_name | default("hnsw") | tojson }},
          "engine": {{ vector_method_engine | default("faiss") | tojson }},
          "parameters": {
            "ef_construction": 100,
            "m": 16
          }
        }
      }
    }
  }
}</code></pre><h3>Elasticsearch Rally-Konfiguration</h3><p><code>/k8s/rally-openai_vector-es-bq.yml</code></p><pre><code>apiVersion: v1
kind: ConfigMap
metadata:
  name: rally-params-es
  labels:
    app: rally-elasticsearch
data:
  user-tags.json: |
    {
      "product": "Elasticsearch",
      "product-version": "Elasticsearch-9.0.0-ade01164",
      "product-label": "Elasticsearch-9.0-BBQ",
      "benchmark-run": "19-feb-recall@100"
    }
  track-params.json: |
    {
      "mapping_type": "vectors-only-mapping-with-docid",
      "standalone_search_clients": 8,
      "standalone_search_iterations": 5000,
      "vector_index_type": "bbq_hnsw",
      "search_ops": [
        [100, 200, 200],
        [100, 250, 250],
        [100, 300, 300],
        [100, 500, 500],
        [100, 750, 750],
        [100, 1000, 1000],
        [100, 1200, 1200],
        [100, 1500, 1500],
        [100, 2000, 2000]
      ]
    }</code></pre><h3>Elasticsearch-Indexkonfiguration</h3><p><code>index-vectors-only-mapping-with-docid-mapping.json</code></p><pre><code>{
  "settings": {
    {# non-serverless-index-settings-marker-start #}
    {%- if build_flavor != "serverless" or serverless_operator == true -%}
    {% if preload_pagecache %}
    "index.store.preload": [ "vec", "vex", "vem", "veq", "veqm", "veb", "vebm" ],
    {% endif %}
    "index.number_of_shards": {{ number_of_shards | default(1) }},
    "index.number_of_replicas": {{ number_of_replicas | default(0) }}
    {%- endif -%}
    {# non-serverless-index-settings-marker-end #}
  },
  "mappings": {
    "dynamic": false,
    "properties": {
      "docid": {
        "type": "keyword"
      },
      "emb": {
        "type": "dense_vector",
        "element_type": "float",
        "dims": 1536,
        "index": true,
        "similarity": "dot_product",
        "index_options": {
          "type": {{ vector_index_type | default("bbq_hnsw") | tojson }},
          "ef_construction": 100,
          "m": 16
        }
      }
    }
  }
}</code></pre><h2>Ergebnisse</h2><p>Es gibt mehrere Möglichkeiten, die Ergebnisse zu interpretieren. Sowohl für die Latenz als auch für den Durchsatz haben wir auf jeder Rückrufebene ein vereinfachtes und ein detailliertes Diagramm erstellt. Es ist leicht, Unterschiede zu erkennen, wenn wir bei jeder Metrik das Prinzip „höher ist besser“ berücksichtigen. Allerdings ist die Latenz ein negativer Faktor (je niedriger, desto besser), während der Durchsatz ein positiver Faktor ist. Für die vereinfachten Diagramme haben wir <strong>(Rückruf / Latenz) * 10000 </strong>(einfach „Geschwindigkeit“ genannt) und<strong> Rückruf * Durchsatz</strong> verwendet. Beide Messwerte bedeuten also, dass mehr Geschwindigkeit und mehr Durchsatz besser sind. Lasst uns loslegen.</p><h3>Rückruf @ 10 - vereinfacht</h3><p>Bei dieser Rückrufebene ist Elasticsearch BBQ bis zu <strong>5-mal schneller </strong>(durchschnittlich 3,9-mal schneller) und hat im Durchschnitt <strong>einen 3,2-mal höheren Durchsatz</strong> als OpenSearch FAISS.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d580ad53e8974bd3aa75957c413a0136c4e465c5-1600x681.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/18edce667fe36ab95033264ef8df6f352dda2425-2044x866.png" style="width: 100%;" /><h4>Rückruf @ 10 - Detailliert</h4><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a7459b87e679f4ad963d0e2f1685499b40f6f050-1600x799.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c6036f55a13377654d296eb3148c7199e1965475-1600x799.png" style="width: 100%;" />
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;">Aufgabe</td><td style="border: 1px solid #ccc; padding: 4px;">Latenz.Mittelwert</td><td style="border: 1px solid #ccc; padding: 4px;">Durchsatz.Mittelwert</td><td style="border: 1px solid #ccc; padding: 4px;">Durchschnittliche Rückrufzahl</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">10-100-100</td><td style="border: 1px solid #ccc; padding: 4px;">11,70</td><td style="border: 1px solid #ccc; padding: 4px;">513,58</td><td style="border: 1px solid #ccc; padding: 4px;">0,89</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">10-1000-100</td><td style="border: 1px solid #ccc; padding: 4px;">27,33</td><td style="border: 1px solid #ccc; padding: 4px;">250,55</td><td style="border: 1px solid #ccc; padding: 4px;">0,95</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">10-1500-1500</td><td style="border: 1px solid #ccc; padding: 4px;">35,93</td><td style="border: 1px solid #ccc; padding: 4px;">197,26</td><td style="border: 1px solid #ccc; padding: 4px;">0,95</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">10-200-200</td><td style="border: 1px solid #ccc; padding: 4px;">13.33</td><td style="border: 1px solid #ccc; padding: 4px;">456,16</td><td style="border: 1px solid #ccc; padding: 4px;">0,92</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">10-2000-2000</td><td style="border: 1px solid #ccc; padding: 4px;">44,27</td><td style="border: 1px solid #ccc; padding: 4px;">161,40</td><td style="border: 1px solid #ccc; padding: 4px;">0,95</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">10-40-40</td><td style="border: 1px solid #ccc; padding: 4px;">10,97</td><td style="border: 1px solid #ccc; padding: 4px;">539,94</td><td style="border: 1px solid #ccc; padding: 4px;">0,84</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">10-50-50</td><td style="border: 1px solid #ccc; padding: 4px;">11.00</td><td style="border: 1px solid #ccc; padding: 4px;">535,73</td><td style="border: 1px solid #ccc; padding: 4px;">0,85</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">10-500-500</td><td style="border: 1px solid #ccc; padding: 4px;">19,52</td><td style="border: 1px solid #ccc; padding: 4px;">341,45</td><td style="border: 1px solid #ccc; padding: 4px;">0,93</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">10-750-750</td><td style="border: 1px solid #ccc; padding: 4px;">22,94</td><td style="border: 1px solid #ccc; padding: 4px;">295,19</td><td style="border: 1px solid #ccc; padding: 4px;">0,94</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">10-100-100</td><td style="border: 1px solid #ccc; padding: 4px;">35,59</td><td style="border: 1px solid #ccc; padding: 4px;">200,61</td><td style="border: 1px solid #ccc; padding: 4px;">0,94</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">10-1000-1000</td><td style="border: 1px solid #ccc; padding: 4px;">156,81</td><td style="border: 1px solid #ccc; padding: 4px;">58,30</td><td style="border: 1px solid #ccc; padding: 4px;">0,96</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">10-1500-1500</td><td style="border: 1px solid #ccc; padding: 4px;">181,79</td><td style="border: 1px solid #ccc; padding: 4px;">42,97</td><td style="border: 1px solid #ccc; padding: 4px;">0,96</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">10-200-200</td><td style="border: 1px solid #ccc; padding: 4px;">47,91</td><td style="border: 1px solid #ccc; padding: 4px;">155,16</td><td style="border: 1px solid #ccc; padding: 4px;">0,95</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">10-2000-2000</td><td style="border: 1px solid #ccc; padding: 4px;">232,14</td><td style="border: 1px solid #ccc; padding: 4px;">31,84</td><td style="border: 1px solid #ccc; padding: 4px;">0,96</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">10-40-40</td><td style="border: 1px solid #ccc; padding: 4px;">27,55</td><td style="border: 1px solid #ccc; padding: 4px;">249,25</td><td style="border: 1px solid #ccc; padding: 4px;">0,92</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">10-50-50</td><td style="border: 1px solid #ccc; padding: 4px;">28,78</td><td style="border: 1px solid #ccc; padding: 4px;">245,14</td><td style="border: 1px solid #ccc; padding: 4px;">0,92</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">10-500-500</td><td style="border: 1px solid #ccc; padding: 4px;">79,44</td><td style="border: 1px solid #ccc; padding: 4px;">97,06</td><td style="border: 1px solid #ccc; padding: 4px;">0,96</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">10-750-750</td><td style="border: 1px solid #ccc; padding: 4px;">104,19</td><td style="border: 1px solid #ccc; padding: 4px;">75,49</td><td style="border: 1px solid #ccc; padding: 4px;">0,96</td>
                    </tr>
              </tbody>
            </table>
          <h3>Rückruf @ 50 - vereinfacht</h3><p>Bei diesem Rückrufniveau ist Elasticsearch BBQ <strong>bis zu 5x schneller</strong> (durchschnittlich 4,2x schneller) und hat durchschnittlich <strong>3,9x mehr Durchsatz</strong> <strong> </strong>als OpenSearch FAISS.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3189ffb330948b35854eeea9ae317d4846c14972-1600x681.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/607383d674dcf0b8f94bfb1a450063f52fcbeb15-2060x876.png" style="width: 100%;" /><h4>Detaillierte Ergebnisse – Rückruf @ 50</h4><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ebfffe0b776aad31dd03d315cfbf5aa098b41226-1600x789.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ae20cf870a65c400a2112bbad62eb56e244f549a-1600x799.png" style="width: 100%;" />
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;">Aufgabe</td><td style="border: 1px solid #ccc; padding: 4px;">Latenzmittelwert</td><td style="border: 1px solid #ccc; padding: 4px;">Durchsatzmittelwert</td><td style="border: 1px solid #ccc; padding: 4px;">Durchschnittliche Rückrufrate</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">50-1000-1000</td><td style="border: 1px solid #ccc; padding: 4px;">25,71</td><td style="border: 1px solid #ccc; padding: 4px;">246,44</td><td style="border: 1px solid #ccc; padding: 4px;">0,95</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">50-1200-1200</td><td style="border: 1px solid #ccc; padding: 4px;">28,81</td><td style="border: 1px solid #ccc; padding: 4px;">227,85</td><td style="border: 1px solid #ccc; padding: 4px;">0,95</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">50-150-150</td><td style="border: 1px solid #ccc; padding: 4px;">13.43</td><td style="border: 1px solid #ccc; padding: 4px;">362,90</td><td style="border: 1px solid #ccc; padding: 4px;">0,90</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">50-1500-1500</td><td style="border: 1px solid #ccc; padding: 4px;">33,38</td><td style="border: 1px solid #ccc; padding: 4px;">202,37</td><td style="border: 1px solid #ccc; padding: 4px;">0,95</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">50-200-200</td><td style="border: 1px solid #ccc; padding: 4px;">12,99</td><td style="border: 1px solid #ccc; padding: 4px;">406,30</td><td style="border: 1px solid #ccc; padding: 4px;">0,91</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">50-2000-2000</td><td style="border: 1px solid #ccc; padding: 4px;">42,63</td><td style="border: 1px solid #ccc; padding: 4px;">163,68</td><td style="border: 1px solid #ccc; padding: 4px;">0,95</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">50-250-250</td><td style="border: 1px solid #ccc; padding: 4px;">14.41</td><td style="border: 1px solid #ccc; padding: 4px;">373,21</td><td style="border: 1px solid #ccc; padding: 4px;">0,92</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">50-500-500</td><td style="border: 1px solid #ccc; padding: 4px;">17.15</td><td style="border: 1px solid #ccc; padding: 4px;">341,04</td><td style="border: 1px solid #ccc; padding: 4px;">0,93</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">50-750-750</td><td style="border: 1px solid #ccc; padding: 4px;">31,25</td><td style="border: 1px solid #ccc; padding: 4px;">248,60</td><td style="border: 1px solid #ccc; padding: 4px;">0,94</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">50-1000-1000</td><td style="border: 1px solid #ccc; padding: 4px;">125,35</td><td style="border: 1px solid #ccc; padding: 4px;">62,53</td><td style="border: 1px solid #ccc; padding: 4px;">0,96</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">50-1200-1200</td><td style="border: 1px solid #ccc; padding: 4px;">143,87</td><td style="border: 1px solid #ccc; padding: 4px;">54,75</td><td style="border: 1px solid #ccc; padding: 4px;">0,96</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">50-150-150</td><td style="border: 1px solid #ccc; padding: 4px;">43,64</td><td style="border: 1px solid #ccc; padding: 4px;">130,01</td><td style="border: 1px solid #ccc; padding: 4px;">0,89</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">50-1500-1500</td><td style="border: 1px solid #ccc; padding: 4px;">169,45</td><td style="border: 1px solid #ccc; padding: 4px;">46,35</td><td style="border: 1px solid #ccc; padding: 4px;">0,96</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">50-200-200</td><td style="border: 1px solid #ccc; padding: 4px;">48,05</td><td style="border: 1px solid #ccc; padding: 4px;">156,07</td><td style="border: 1px solid #ccc; padding: 4px;">0,91</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">50-2000-2000</td><td style="border: 1px solid #ccc; padding: 4px;">216,73</td><td style="border: 1px solid #ccc; padding: 4px;">36,38</td><td style="border: 1px solid #ccc; padding: 4px;">0,96</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">50-250-250</td><td style="border: 1px solid #ccc; padding: 4px;">53,52</td><td style="border: 1px solid #ccc; padding: 4px;">142,44</td><td style="border: 1px solid #ccc; padding: 4px;">0,93</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">50-500-500</td><td style="border: 1px solid #ccc; padding: 4px;">78,98</td><td style="border: 1px solid #ccc; padding: 4px;">97,82</td><td style="border: 1px solid #ccc; padding: 4px;">0,95</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">50-750-750</td><td style="border: 1px solid #ccc; padding: 4px;">103,20</td><td style="border: 1px solid #ccc; padding: 4px;">75,86</td><td style="border: 1px solid #ccc; padding: 4px;">0,96</td>
                    </tr>
              </tbody>
            </table>
          <h3>Rückruf @ 100</h3><p>Bei dieser Rückrufebene ist Elasticsearch BBQ <strong>bis zu 5-mal schneller </strong>(durchschnittlich 4,6-mal schneller) und hat im Durchschnitt <strong>einen 3,9-mal höheren Durchsatz </strong>als OpenSearch FAISS.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d3f29db64cbde9956de1fa3ae64a75f15141a2bb-1600x681.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/10aff7f8c61fdac895b9ba9c5342baf239ba3ffc-2072x864.png" style="width: 100%;" /><h4>Detaillierte Ergebnisse – Rückruf @ 100</h4><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8790fdf9512b850447f6875fb69969f6f1d4da5f-1600x799.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d47032fac6c288cd4eedd9f25001e417b2fa9d65-1600x787.png" style="width: 100%;" />
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;">Aufgabe</td><td style="border: 1px solid #ccc; padding: 4px;">Latenz.Mittelwert</td><td style="border: 1px solid #ccc; padding: 4px;">Durchsatz.Mittelwert</td><td style="border: 1px solid #ccc; padding: 4px;">Durchschnittliche Rückrufzahl</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">100-1000-1000</td><td style="border: 1px solid #ccc; padding: 4px;">27,82</td><td style="border: 1px solid #ccc; padding: 4px;">243,22</td><td style="border: 1px solid #ccc; padding: 4px;">0,95</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">100-1200-1200</td><td style="border: 1px solid #ccc; padding: 4px;">31.14</td><td style="border: 1px solid #ccc; padding: 4px;">224,04</td><td style="border: 1px solid #ccc; padding: 4px;">0,95</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">100-1500-1500</td><td style="border: 1px solid #ccc; padding: 4px;">35,98</td><td style="border: 1px solid #ccc; padding: 4px;">193,99</td><td style="border: 1px solid #ccc; padding: 4px;">0,95</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">100-200-200</td><td style="border: 1px solid #ccc; padding: 4px;">14.18</td><td style="border: 1px solid #ccc; padding: 4px;">403,86</td><td style="border: 1px solid #ccc; padding: 4px;">0,88</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">100-2000-2000</td><td style="border: 1px solid #ccc; padding: 4px;">45,36</td><td style="border: 1px solid #ccc; padding: 4px;">159,88</td><td style="border: 1px solid #ccc; padding: 4px;">0,95</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">100-250-250</td><td style="border: 1px solid #ccc; padding: 4px;">14,77</td><td style="border: 1px solid #ccc; padding: 4px;">433,06</td><td style="border: 1px solid #ccc; padding: 4px;">0,90</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">100-300-300</td><td style="border: 1px solid #ccc; padding: 4px;">14,61</td><td style="border: 1px solid #ccc; padding: 4px;">375,54</td><td style="border: 1px solid #ccc; padding: 4px;">0,91</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">100-500-500</td><td style="border: 1px solid #ccc; padding: 4px;">18,88</td><td style="border: 1px solid #ccc; padding: 4px;">340,37</td><td style="border: 1px solid #ccc; padding: 4px;">0,93</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch-9.0-BBQ</td><td style="border: 1px solid #ccc; padding: 4px;">100-750-750</td><td style="border: 1px solid #ccc; padding: 4px;">23,59</td><td style="border: 1px solid #ccc; padding: 4px;">285,79</td><td style="border: 1px solid #ccc; padding: 4px;">0,94</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">100-1000-1000</td><td style="border: 1px solid #ccc; padding: 4px;">142,90</td><td style="border: 1px solid #ccc; padding: 4px;">58,48</td><td style="border: 1px solid #ccc; padding: 4px;">0,95</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">100-1200-1200</td><td style="border: 1px solid #ccc; padding: 4px;">153,03</td><td style="border: 1px solid #ccc; padding: 4px;">51,04</td><td style="border: 1px solid #ccc; padding: 4px;">0,95</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">100-1500-1500</td><td style="border: 1px solid #ccc; padding: 4px;">181,79</td><td style="border: 1px solid #ccc; padding: 4px;">43,20</td><td style="border: 1px solid #ccc; padding: 4px;">0,96</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">100-200-200</td><td style="border: 1px solid #ccc; padding: 4px;">50,94</td><td style="border: 1px solid #ccc; padding: 4px;">131,62</td><td style="border: 1px solid #ccc; padding: 4px;">0,83</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">100-2000-2000</td><td style="border: 1px solid #ccc; padding: 4px;">232,53</td><td style="border: 1px solid #ccc; padding: 4px;">33,67</td><td style="border: 1px solid #ccc; padding: 4px;">0,96</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">100-250-250</td><td style="border: 1px solid #ccc; padding: 4px;">57,08</td><td style="border: 1px solid #ccc; padding: 4px;">131,23</td><td style="border: 1px solid #ccc; padding: 4px;">0,87</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">100-300-300</td><td style="border: 1px solid #ccc; padding: 4px;">62,76</td><td style="border: 1px solid #ccc; padding: 4px;">120,10</td><td style="border: 1px solid #ccc; padding: 4px;">0,89</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">100-500-500</td><td style="border: 1px solid #ccc; padding: 4px;">84,36</td><td style="border: 1px solid #ccc; padding: 4px;">91,54</td><td style="border: 1px solid #ccc; padding: 4px;">0,93</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch-2.19-faiss</td><td style="border: 1px solid #ccc; padding: 4px;">100-750-750</td><td style="border: 1px solid #ccc; padding: 4px;">111,33</td><td style="border: 1px solid #ccc; padding: 4px;">69,95</td><td style="border: 1px solid #ccc; padding: 4px;">0,94</td>
                    </tr>
              </tbody>
            </table>
          <h2>Verbesserungen beim Grillen</h2><p>BBQ hat seit seiner Erstveröffentlichung eine lange Entwicklung durchgemacht. Zu Vergleichszwecken haben wir bei Elasticsearch 8.16 neben dem aktuellen einen Benchmark-Lauf von 8.16 eingefügt und können sehen, wie sich Rückruf und Latenz seitdem verbessert haben.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/45a0acfe5985bff28ccada76ec4eca190fe65f72-1600x799.png" style="width: 100%;" /><p>In Elasticsearch 8.18 und 9.0 haben wir den Kernalgorithmus zur Quantisierung der Vektoren neu geschrieben. BBQ war in 8.16 zwar gut, aber die neuesten Versionen sind sogar noch besser. Sie können <a href="https://www.elastic.co/de/search-labs/blog/optimized-scalar-quantization-elasticsearch">hier</a> und <a href="https://www.elastic.co/de/search-labs/blog/scalar-quantization-optimization">hier</a> darüber lesen. Kurz gesagt, jeder Vektor wird einzeln durch optimierte skalare Quantile quantisiert. Dadurch profitieren Benutzer von einer höheren Genauigkeit bei der Vektorsuche ohne Leistungseinbußen, wodurch die Vektorabfrage von Elasticsearch noch leistungsfähiger wird.</p><h2>Fazit</h2><p>In diesem Leistungsvergleich zwischen Elasticsearch BBQ und OpenSearch FAISS übertrifft Elasticsearch OpenSearch bei der Vektorsuche deutlich und erreicht über verschiedene Rückrufebenen hinweg durchschnittlich bis zu 5-mal schnellere Abfragegeschwindigkeiten und einen 3,9-mal höheren Durchsatz.</p><p>Zu den wichtigsten Ergebnissen gehören:</p><ul><li><strong>Recall@10</strong>: Elasticsearch BBQ ist bis zu 5x schneller (durchschnittlich 3,9x schneller) und hat im Durchschnitt einen 3,2x höheren Durchsatz als OpenSearch FAISS.</li><li><strong>Recall@50</strong>: Elasticsearch BBQ ist bis zu 5x schneller (durchschnittlich 4,2x schneller) und hat im Durchschnitt einen 3,9x höheren Durchsatz als OpenSearch FAISS.</li><li><strong>Recall@100</strong>: Elasticsearch BBQ ist bis zu 5x schneller (durchschnittlich 4,6x schneller) und hat im Durchschnitt einen 3,9x höheren Durchsatz als OpenSearch FAISS.</li></ul><p>Diese Ergebnisse unterstreichen die Effizienz- und Leistungsvorteile von Elasticsearch BBQ, insbesondere in hochdimensionalen Vektorsuchszenarien. Die in Elasticsearch 8.16 eingeführte Better Binary Quantization (BBQ)-Technik bietet eine erhebliche Speicherreduzierung (~95 %) bei gleichzeitiger Beibehaltung einer hohen Ranking-Qualität und ist daher eine hervorragende Wahl für groß angelegte Vektorsuchanwendungen.</p><p>Bei Elastic arbeiten wir unermüdlich an Innovationen, um Apache Lucene und Elasticsearch zu verbessern und die beste Vektordatenbank für Such- und Abrufanwendungsfälle bereitzustellen, einschließlich RAG (Retrieval Augmented Generation). Unsere <a href="https://www.elastic.co/de/search-labs/blog/optimized-scalar-quantization-elasticsearch">jüngsten Fortschritte</a> haben die Leistung erheblich gesteigert und die Vektorsuche schneller und platzsparender gemacht als zuvor, aufbauend auf den Vorteilen von Lucene 10. Dieser Blog ist ein weiteres Beispiel für diese Innovation.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[So migrieren Sie Daten zwischen verschiedenen Versionen von Elasticsearch und zwischen Clustern]]></title>
            <description><![CDATA[Erkunden von Methoden zum Übertragen von Daten zwischen Elasticsearch-Versionen und -Clustern.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/elasticsearch-migrate-data-versions-clusters</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/elasticsearch-migrate-data-versions-clusters</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Mon, 14 Apr 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/01fde3b1d714f12bf8673140c9f2f940d443de31-1440x823.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Wenn Sie einen Elasticsearch-Cluster aktualisieren möchten, ist es manchmal einfacher, einen neuen, separaten Cluster zu erstellen und Daten vom alten Cluster auf den neuen zu übertragen. Dies bietet den Benutzern den Vorteil, dass sie alle ihre Daten und Konfigurationen auf dem neuen Cluster mit allen ihren Anwendungen testen können, ohne dass das Risiko von Ausfallzeiten oder Datenverlust besteht.</p><p>Die Nachteile dieses Ansatzes bestehen darin, dass er eine gewisse Duplizierung der Hardware erfordert und bei dem Versuch, alle Daten reibungslos zu übertragen und zu synchronisieren, zu Schwierigkeiten führen kann.</p><p>Ein ähnliches Verfahren kann auch erforderlich sein, wenn Sie Anwendungen von einem Rechenzentrum in ein anderes migrieren müssen.</p><p>In diesem Artikel besprechen und beschreiben wir drei Möglichkeiten zur Datenübertragung zwischen Elasticsearch-Clustern.</p><p><strong>Wie migriere ich Daten zwischen Elasticsearch-Clustern?</strong></p><p>Es gibt drei Möglichkeiten, Daten zwischen Elasticsearch-Clustern zu übertragen:</p><ol><li><a href="https://www.elastic.co/de/search-labs/blog/elasticsearch-migrate-data-versions-clusters#1.-reindexing-data-from-a-remote-cluster">Neuindizierung von einem Remote-Cluster</a></li><li><a href="https://www.elastic.co/de/search-labs/blog/elasticsearch-migrate-data-versions-clusters#2.-transferring-data-using-snapshots">Übertragen von Daten mithilfe von Snapshots</a></li><li><a href="https://www.elastic.co/de/search-labs/blog/elasticsearch-migrate-data-versions-clusters#3.-transferring-data-using-logstash">Datenübertragung mit Logstash</a></li></ol><p>Die Verwendung von Snapshots ist normalerweise die schnellste und zuverlässigste Methode zur Datenübertragung. Bedenken Sie jedoch, dass Sie einen Snapshot nur auf einem Cluster mit gleicher oder höherer Version wiederherstellen können und niemals mit einem Unterschied von mehr als einer Hauptversion. Das bedeutet, dass Sie einen 6.x-Snapshot auf einem 7.x-Cluster wiederherstellen können, aber nicht auf einem 8.x-Cluster.</p><p>Wenn Sie um mehr als eine Hauptversion erhöhen müssen, müssen Sie neu indizieren oder Logstash verwenden.</p><p>Sehen wir uns nun jede der drei Optionen zum Übertragen von Daten zwischen Elasticsearch-Clustern im Detail an.</p><h2>1. Neuindizierung von Daten aus einem Remote-Cluster</h2><p>Bevor Sie mit der Neuindizierung beginnen, denken Sie daran, dass Sie für alle Indizes im neuen Cluster entsprechende Zuordnungen einrichten müssen. Dazu müssen Sie die Indizes entweder direkt mit den entsprechenden Zuordnungen erstellen oder Indexvorlagen verwenden.</p><h3>Neuindizierung von Remote – Konfiguration erforderlich</h3><p>Um eine Neuindizierung aus der Ferne durchzuführen, sollten Sie die folgende Konfiguration zur Datei elasticseearch.yml für den Cluster hinzufügen, der die Daten empfängt. In Linux-Systemen befindet sich diese Datei normalerweise hier: /etc/elasticsearch/elasticsearch.yml. Die hinzuzufügende Konfiguration lautet wie folgt:</p><pre><code>reindex.remote.whitelist: "192.168.1.11:9200"</code></pre><p>Wenn Sie SSL verwenden, sollten Sie jedem Knoten das CA-Zertifikat hinzufügen und Folgendes in den Befehl für jeden Knoten in elasticsearch.yml aufnehmen:</p><pre><code>reindex.ssl.certificate_authorities: “/path/to/ca.pem”</code></pre><p>Alternativ können Sie allen Elasticsearch-Knoten die folgende Zeile hinzufügen, um die SSL-Verifizierung zu deaktivieren. Dieser Ansatz ist jedoch weniger empfehlenswert, da er nicht so sicher ist wie die vorherige Option:</p><pre><code>reindex.remote.whitelist: "192.168.1.11:9200"
reindex.ssl.verification_mode: none
systemctl restart elasticsearch service </code></pre><p>Sie müssen diese Änderungen auf jedem Knoten vornehmen und einen rollierenden Neustart durchführen. Weitere Informationen hierzu finden Sie in <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/8.17/restart-cluster.html#restart-cluster-rolling">unserem Handbuch</a>.</p><h3>Befehl zur Neuindizierung</h3><p>Nachdem Sie den Remote-Host in der Datei elasticsearch.yml definiert und ggf. die SSL-Zertifikate hinzugefügt haben, können Sie mit dem folgenden Befehl mit der Neuindizierung der Daten beginnen:</p><pre><code>POST _reindex
{
  "source": {
    "remote": {
      "host": "http://192.168.1.11:9200",
      "username": "elastic",
      "password": "123456",
     "socket_timeout": "1m",
      "connect_timeout": "1m"

    },
    "index": "companydatabase"
  },
  "dest": {
    "index": "my-new-index-000001"
  }
}</code></pre><p>Dabei können Timeout-Fehler auftreten. Daher kann es sinnvoll sein, großzügige Werte für Timeouts festzulegen, anstatt sich auf Standardwerte zu verlassen.</p><p>Sehen wir uns nun einige andere häufige Fehler an, die bei der Neuindizierung aus der Ferne auftreten können.</p><h3>Häufige Fehler bei der Neuindizierung von Remote</h3><h4>1. Neuindizierung nicht auf der Whitelist</h4><pre><code>{
  "error": {
    "root_cause": [
      {
        "type": "illegal_argument_exception",
        "reason": "[192.168.1.11:9200] not whitelisted in reindex.remote.whitelist"
      }
    ],
    "type": "illegal_argument_exception",
    "reason": "[192.168.1.11:9200] not whitelisted in reindex.remote.whitelist"
  },
  "status": 400
}</code></pre><p>Wenn dieser Fehler auftritt, bedeutet dies, dass Sie die IP-Adresse des Remote-Hosts oder den DNS-Knotennamen in Elasticsearch nicht wie oben beschrieben definiert haben oder vergessen haben, die Elasticsearch-Dienste neu zu starten.</p><p>Um dies für den Elasticsearch-Cluster zu beheben, müssen Sie den Remote-Host zu allen Elasticsearch-Knoten hinzufügen und die Elasticsearch-Dienste neu starten.</p><h4>2. SSL-Handshake-Ausnahme</h4><pre><code>{
  "error": {
    "root_cause": [
      {
        "type": "s_s_l_handshake_exception",
        "reason": "PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target"
      }
    ],
    "type": "s_s_l_handshake_exception",
    "reason": "PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target",
    "caused_by": {
      "type": "validator_exception",
      "reason": "PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target",
      "caused_by": {
        "type": "sun_cert_path_builder_exception",
        "reason": "unable to find valid certification path to requested target"
      }
    }
  },
  "status": 500
}</code></pre><p>Dieser Fehler bedeutet, dass Sie vergessen haben, reindex.ssl.certificate_authorities wie oben beschrieben zu elasticsearch.yml hinzuzufügen. So fügen Sie es hinzu:</p><pre><code>#elasticsearch.yml
reindex.ssl.certificate_authorities: "/path/to/ca.pem"</code></pre><h2>2. Daten mit Snapshots übertragen</h2><p>Denken Sie daran, dass Sie, wie oben erwähnt, einen Snapshot nur auf einem Cluster einer gleichen oder höheren Version wiederherstellen können und niemals mit einem Unterschied von mehr als einer Hauptversion</p><p>Wenn Sie um mehr als eine Hauptversion erhöhen müssen, müssen Sie neu indizieren oder Logstash verwenden.</p><p>Für die Datenübertragung per Snapshot sind folgende Schritte erforderlich:</p><p>Schritt 1. Hinzufügen des Repository-Plugins zum ersten Elasticsearch-Cluster – Um Daten über Snapshots zwischen Clustern zu übertragen, müssen Sie sicherstellen, dass sowohl vom neuen als auch vom alten Cluster auf das Repository zugegriffen werden kann. Cloud-Speicher-Repositories wie AWS, Google und Azure sind hierfür grundsätzlich ideal. Um Schnappschüsse zu machen, lesen Sie bitte <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/snapshot-restore.html">unsere Anleitung</a> und befolgen Sie die darin beschriebenen Schritte.</p><p>Schritt 2. Starten Sie den Elasticsearch-Dienst neu (Rolling Restart).</p><p>Schritt 3. Erstellen Sie ein Repository für den ersten Elasticsearch-Cluster.</p><p>Schritt 4: Fügen Sie das Repository-Plugin zum zweiten Elasticsearch-Cluster hinzu.</p><p>Schritt 5 – Repository als schreibgeschützt zum zweiten Elasticsearch-Cluster hinzufügen – Sie müssen ein Repository hinzufügen, indem Sie dieselben Schritte wiederholen, die Sie zum Erstellen des ersten Elasticsearch-Clusters ausgeführt haben.</p><p>Wichtiger Hinweis: Wenn Sie den zweiten Elasticsearch-Cluster mit demselben AWS S3-Repository verbinden, sollten Sie das Repository als schreibgeschütztes Repository definieren:</p><pre><code>PUT _snapshot/my_s3_repository
{
  "type": "s3",
  "settings": {
    "bucket": "my-analytic-data",
    "endpoint": "s3.eu-de.cloud-object-storage.appdomain.cloud",
    "readonly": "true"
  }
}</code></pre><p>Dies ist wichtig, da Sie das Risiko einer Vermischung von Elasticsearch-Versionen im selben Snapshot-Repository vermeiden möchten.</p><p>Schritt 6 – Wiederherstellen der Daten im zweiten Elasticsearch-Cluster – Nachdem Sie die oben genannten Schritte ausgeführt haben, können Sie die Daten wiederherstellen und in den neuen Cluster übertragen. Befolgen Sie die in <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/snapshot-restore.html">diesem Artikel</a> beschriebenen Schritte, um Daten im neuen Cluster wiederherzustellen. </p><h2>3. Datenübertragung mit Logstash</h2><p>Bevor Sie mit der Datenübertragung mit Logstash beginnen, denken Sie daran, dass Sie für alle Indizes im neuen Cluster entsprechende Zuordnungen einrichten müssen. Dazu müssen Sie die Indizes entweder direkt erstellen oder Indexvorlagen verwenden.</p><p>Um Daten zwischen zwei Elasticsearch-Clustern zu übertragen, können Sie einen temporären Logstash-Server einrichten und ihn zum Übertragen Ihrer Daten zwischen zwei Clustern verwenden. Für kleine Cluster sollte eine Instanz mit 2 GB RAM ausreichen. Für größere Cluster können Sie Vierkern-CPUs mit 8 GB RAM verwenden.</p><p>Eine Anleitung zur Installation von Logstash <a href="https://www.elastic.co/de/guide/en/logstash/current/installing-logstash.html">finden Sie hier</a>.</p><h3>Logstash-Konfiguration zum Übertragen von Daten von einem Cluster zu einem anderen</h3><p>Eine grundlegende Konfiguration zum Kopieren eines einzelnen Indexes von Cluster A nach Cluster B ist:</p><pre><code>iinput
{
elasticsearch
      {
        hosts => ["192.168.1.11:9200"]
        index => "index_name"
       docinfo => true      
      }
}

output 
{
  elasticsearch {
        hosts => "https://192.168.1.12:9200"
        index => "index_name"
        
  }
}</code></pre><p>Für eine sichere Elasticsearch können Sie die folgende Konfiguration verwenden:</p><pre><code>input
{
  elasticsearch
      {
        hosts => ["192.168.1.11:9200"]
        index => "index_name"
        docinfo => true 
        user => "elastic"
        password => "elastic_password"
        ssl => true
        ssl_certificate_verification => false
            
      }
}

output 
{
  elasticsearch {
        hosts => "https://192.168.1.12:9200"
        index => "index_name"
        user => "elastic"
        password => "elastic_password"
        ssl => true
        ssl_certificate_verification => false
  }
}</code></pre><h3>Index-Metadaten</h3><p>Die obigen Befehle schreiben in einen einzelnen benannten Index. Wenn Sie mehrere Indizes übertragen und die Indexnamen beibehalten möchten, müssen Sie der Logstash-Ausgabe die folgende Zeile hinzufügen:</p><pre><code>index => "%{[@metadata][_index]}"</code></pre><p>Wenn Sie die ursprüngliche ID des Dokuments beibehalten möchten, müssen Sie außerdem Folgendes hinzufügen:</p><pre><code>document_id => "%{[@metadata][_id]}"</code></pre><p>Bedenken Sie, dass die Datenübertragung durch das Festlegen der Dokument-ID erheblich langsamer wird. Behalten Sie die Original-ID daher nur bei, wenn es unbedingt erforderlich ist.</p><h2>Synchronisierung von Updates</h2><p>Alle oben beschriebenen Methoden dauern relativ lange und Sie stellen möglicherweise fest, dass Daten im ursprünglichen Cluster aktualisiert wurden, während Sie auf den Abschluss des Vorgangs gewartet haben.</p><p>Es gibt verschiedene Strategien, um die Synchronisierung aller Aktualisierungen zu ermöglichen, die während des Datenübertragungsprozesses aufgetreten sind. Sie sollten sich vor dem Starten dieses Prozesses einige Gedanken über diese Probleme machen. Insbesondere müssen Sie über Folgendes nachdenken:</p><ul><li>Über welche Methode können Sie Daten identifizieren, die seit Beginn des Datenübertragungsprozesses aktualisiert/hinzugefügt wurden (z. B. ein Feld „last_update_time“ in den Daten)?</li><li>Mit welcher Methode können Sie die letzten Daten übertragen?</li><li>Besteht die Gefahr, dass Datensätze dupliziert werden? Normalerweise ist dies der Fall, es sei denn, die von Ihnen verwendete Methode setzt die Dokument-ID während der Neuindizierung auf einen bekannten Wert.</li></ul><p>Nachfolgend werden die verschiedenen Methoden zum Aktivieren der Synchronisierung von Updates beschrieben.</p><h3>1. Einsatz von Warteschlangensystemen</h3><p>Einige Aufnahme-/Aktualisierungssysteme verwenden Warteschlangen, die es Ihnen ermöglichen, in den letzten x Tagen empfangene Datenänderungen „wiederzugeben“. Dies kann eine Möglichkeit bieten, alle durchgeführten Änderungen zu synchronisieren. </p><h3>2. Neuindizierung von Remote</h3><p>Wiederholen Sie den Neuindizierungsprozess für alle Elemente, bei denen „last_update_time“ &gt; x Tage her ist. Sie können dies tun, indem Sie der Neuindizierungsanforderung einen „Abfrage“-Parameter hinzufügen.</p><h3>3. Logstash</h3><p>In der Logstash-Eingabe können Sie eine Abfrage hinzufügen, um alle Elemente zu filtern, bei denen „last_update_time“ &gt; x Tage her ist. Dieser Vorgang führt jedoch zu Duplikaten in Nicht-Zeitreihendaten, sofern Sie die Dokument-ID nicht festgelegt haben.</p><h3>4. Schnappschüsse</h3><p>Es ist nicht möglich, nur einen Teil eines Index wiederherzustellen. Sie müssten daher eine der anderen oben beschriebenen Datenübertragungsmethoden (oder ein Skript) verwenden, um alle Änderungen zu aktualisieren, die seit der Durchführung des Datenübertragungsprozesses stattgefunden haben.</p><p>Allerdings ist die Wiederherstellung von Snapshots ein viel schnellerer Prozess als die Neuindizierung/Logstash. Daher ist es möglicherweise möglich, Aktualisierungen für einen kurzen Zeitraum auszusetzen, während Snapshots übertragen werden, um das Problem insgesamt zu vermeiden.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Elasticsearch-Vektordatenbank für native Erdung in der Vertex AI-Plattform von Google Cloud]]></title>
            <description><![CDATA[Erfahren Sie, wie Elasticsearch, die erste native Drittanbieter-Engine für Google Clouds Vertex AI, es Ihnen ermöglicht, individuelle GenAI-Erlebnisse zu erstellen, indem Sie Gemini-Modelle in Unternehmensdaten verankern.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/elasticsearch-google-cloud-vertex-ai-native-grounding</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/elasticsearch-google-cloud-vertex-ai-native-grounding</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Valerio Arvizzigno]]></dc:creator>
            <pubDate>Wed, 09 Apr 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d1f1c8860f1f0b989ad698a882f869de7284ab78-1200x628.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Elastic freut sich, bekannt geben zu können, dass die Vektordatenbank von Elasticsearch jetzt als nativ unterstützte Informationsabruf-Engine in die Vertex AI-Plattform von Google Cloud integriert ist. Dadurch können Benutzer die multimodalen Stärken der Gemini-Modelle von Google mit den erweiterten KI-gestützten semantischen und hybriden Suchfunktionen von Elasticsearch nutzen.</p><p>Entwickler können ihre RAG-Anwendungen jetzt im Rahmen eines einheitlichen Prozesses erstellen und ihre Chat-Erlebnisse auf Basis ihrer privaten Daten auf eine flexible und codearme Weise gestalten. Egal, ob Sie KI-Agenten für Ihre Kunden und internen Mitarbeiter entwickeln oder die Generierung von LLMs innerhalb Ihrer Software nutzen, die Vertex AI-Plattform stellt Ihnen die Relevanz von Elasticsearch mit minimalem Konfigurationsaufwand zur Verfügung. Diese Integration ermöglicht eine einfachere und schnellere Einführung von Gemini-Modellen in Produktionsumgebungen und treibt GenAI von Machbarkeitsstudien hin zu realen Anwendungsszenarien voran.</p><p>In diesem Blog führen wir Sie durch die Integration von Elasticsearch mit der Vertex AI-Plattform von Google Cloud für eine nahtlose Datenerstellung und die Erstellung vollständig anpassbarer GenAI-Anwendungen. Lassen Sie uns herausfinden, wie.</p><h2>Die Vertex AI- und Gemini-Modelle von Google Cloud basieren auf Ihren Daten mithilfe von Elasticsearch.</h2><p>Benutzer, die Vertex AI-Dienste und -Tools zum Erstellen von GenAI-Anwendungen nutzen, können jetzt auf die neue Option „Grounding“ zugreifen, um ihre privaten Daten automatisch in ihre Konversationsinteraktion einzubringen. Elasticsearch ist jetzt Teil dieser Funktion und kann über beides verwendet werden:</p><ul><li>Vertex AI <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/inference">LLM-APIs</a>, die die Gemini-Modelle von Google zum Zeitpunkt der Generierung direkt anreichern (bevorzugt);</li><li><a href="https://cloud.google.com/generative-ai-app-builder/docs/grounded-gen">Grounded Generation API</a>, wird stattdessen im Vertex AI Agent Builder-Ökosystem zum Erstellen von Agentenerlebnissen verwendet.</li></ul><p>Mit dieser Integration bringt Elasticsearch – die am häufigsten heruntergeladene und eingesetzte <a href="https://www.elastic.co/de/elasticsearch/vector-database">Vektordatenbank</a> – Ihre relevanten Unternehmensdaten überall dorthin, wo sie in Ihren internen Chats mit Endkunden benötigt werden. Dies ist für die praktische Einführung von GenAI in Geschäftsprozesse von entscheidender Bedeutung.</p><p>Die oben genannten APIs ermöglichen es Entwicklern, diese neue Partnerfunktion in ihren Code zu übernehmen. Allerdings bleiben die schnelle Entwicklung und das Testen entscheidende Schritte bei der Anwendungsentwicklung und dienen als erste Entdeckungsplattform. Um dies zu unterstützen, ist Elasticsearch für eine einfache Auswertung durch Benutzer innerhalb des Vertex AI Studio-Konsolentools konzipiert.</p><p>Es sind nur ein paar einfache Schritte erforderlich, um die Elastic-Endpunkte mit den gewünschten Parametern (zu durchsuchender Index, Anzahl der abzurufenden Dokumente und gewünschte Suchvorlage) auf der Registerkarte „Customize Grounding“ in der Benutzeroberfläche zu konfigurieren, wie unten gezeigt (beachten Sie, dass Sie den API-Schlüssel mit dem Wort „ApiKey“ in die Benutzeroberfläche und die Codebeispiele unten eingeben müssen, damit es funktioniert). Jetzt sind Sie bereit, mit Ihrem privaten Wissen zu generieren!</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/69b3d979d18fa90742d9397f57975c586edf5d9f-1003x710.gif" style="width: 100%;" /><h2>Produktionsreife GenAI-Anwendungen mit Leichtigkeit</h2><p>Elastic und Google Cloud arbeiten daran, Entwicklern umfassende und angenehme Erfahrungen zu bieten. Durch die native Verbindung mit Elastic sowohl in LLM als auch in der Grounding Generation API werden Komplexität und Aufwand beim Erstellen von GAI-Anwendungen auf Vertex AI reduziert, wodurch unnötige zusätzliche APIs und Datenorchestrierung vermieden werden und die Erdung in nur einem einheitlichen Aufruf erfolgt.</p><p>Sehen wir uns an, wie es in beiden Szenarien funktioniert.</p><p>Das erste Beispiel wird mit der LLM-API ausgeführt:</p><pre><code>curl -X POST \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \https://us-central1-aiplatform.googleapis.com/v1beta1/projects/<PROJECT_ID>/locations/us-central1/publishers/google/models/gemini-2.0-flash-001:generateContent \
  -d '
{
  "contents": [
    {
      "role": "user",
      "parts": [
        {
          "text": "What's my company car policy?"
        }
      ]
    }
  ],
  "tools": [{
    "retrieval": {
      "externalApi": {
        "api_spec": "ELASTIC_SEARCH",
    "endpoint": "https://<my-elastic-cluster>.gcp.elastic-cloud.com:9243",
    "apiAuth": {
      "apiKeyConfig": {
            "apiKeyString": "ApiKey <API_KEY>"
      }
    },
    "elasticSearchParams": {
      "index": "<my-index>",
      "searchTemplate": "<my-search-template>"
    }
      }
    }
  }]
}</code></pre><p>Im obigen Beispiel können wir mit dem Feld <code>retrieval</code> der API, die die Inhaltsgenerierung für Gemini 2.0 Flash anfordert, kontextbezogen eine Abruf-Engine für die Anforderung festlegen. Wenn Sie <code>api_spec</code> auf „ELASTIC_SEARCH“ setzen, können Sie zusätzliche Konfigurationsparameter verwenden, beispielsweise den API-Schlüssel und den Cluster-Endpunkt (erforderlich, um eine Anfrage an Ihren Elastic-Cluster weiterzuleiten), den Index, aus dem die Daten abgerufen werden sollen, und die Suchvorlage, die für Ihre Suchlogik verwendet werden soll.</p><p>Dasselbe Ergebnis könnte mit der Grounding Generation API durch Festlegen des Parameters <code>groundingSpec</code> erreicht werden:</p><pre><code>curl -X POST -H "Authorization: Bearer $(gcloud auth print-access-token)" -H "Content-Type: application/json" https://us-discoveryengine.googleapis.com/v1alpha/projects/<PROJECT_ID>/locations/global:generateGroundedContent -d '
{
  "contents": [{
    "role": "user",
    "parts": [{
      "text": "What do I need to patch a hole in my drywall?"
    }]
  }],
  "groundingSpec": {
    "groundingSources": [{
      "elasticSource": {
        "endpoint": "https://<my-elastic-cluster>.gcp.elastic-cloud.com:9243",
        "index": "<my-index>",
        "searchTemplate": "<my-search-template",
        "apiKey": "projects/<PROJECT_ID>/secrets/api-key/versions/latest"
      }
    }]
  }
}
'</code></pre><p>Bei beiden Ansätzen erhalten Sie in der Antwort eine Antwort mit den relevantesten privaten Dokumenten, die in Elasticsearch gefunden wurden – und den damit verbundenen Datenquellen – um Ihre Anfrage zu unterstützen.</p><p>Einfachheit sollte jedoch nicht mit einem Mangel an Personalisierung verwechselt werden, um Ihre spezifischen Anforderungen und Anwendungsfälle zu erfüllen. Vor diesem Hintergrund haben wir es so konzipiert, dass Sie die Suchkonfiguration perfekt an Ihr Szenario anpassen können.</p><h2>Vollständig anpassbare Suche auf Knopfdruck: Suchvorlagen</h2><p>Um eine maximale Anpassung an Ihr Suchszenario zu ermöglichen, haben wir in Zusammenarbeit mit Google Cloud die Erfahrung auf der Grundlage unserer bekannten <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/search-template.html">Suchvorlagen</a> aufgebaut. Elasticsearch-Suchvorlagen sind ein hervorragendes Tool zum Erstellen dynamischer, wiederverwendbarer und wartbarer Suchanfragen. Sie ermöglichen Ihnen, Abfragestrukturen vorzudefinieren und wiederzuverwenden. Sie sind besonders nützlich, wenn ähnliche Abfragen mit unterschiedlichen Parametern ausgeführt werden, da sie Entwicklungszeit sparen und die Wahrscheinlichkeit von Fehlern verringern. Vorlagen können Platzhalter für Variablen enthalten, wodurch die Abfragen dynamisch und an unterschiedliche Suchanforderungen anpassbar werden.</p><p>Wenn Sie Vertex AI-APIs und Elasticsearch zur Erdung verwenden, müssen Sie auf eine gewünschte Suchvorlage verweisen – wie in den obigen Codeausschnitten gezeigt –, in der die Suchlogik implementiert und an Elasticsearch weitergeleitet wird. Elastic Power-User können die Suchansätze asynchron verwalten, konfigurieren und aktualisieren und sie auf die spezifischen Indizes, Modelle und Daten zuschneiden. Dies geschieht auf völlig transparente Weise für Vertex AI-Benutzer, Web-App-Entwickler oder KI-Ingenieure, die lediglich den Namen der Vorlage in der Grounding-API angeben müssen.</p><p>Dieses Design ermöglicht eine vollständige Anpassung und stellt Ihnen die umfangreichen Elasticsearch-Abruffunktionen in einer Google Cloud AI-Umgebung zur Verfügung. Gleichzeitig wird Modularität, Transparenz und Benutzerfreundlichkeit für verschiedene Entwickler gewährleistet, auch für diejenigen, die mit Elastic nicht vertraut sind.</p><p>Wann immer Sie eine BM25-Suche, eine semantische Suche oder einen hybriden Ansatz aus beiden benötigen (Haben Sie <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/retrievers-overview.html">Retriever</a> bereits erkundet? Zusammensetzbare Abruftechniken in einem einzigen Such-API-Aufruf) können Sie Ihre benutzerdefinierte Logik in einer Suchvorlage definieren, die Vertex AI automatisch nutzen kann.</p><p>Dies gilt auch für Einbettungen und Neurangmodelle, die Sie zur Verwaltung von Vektoren und Ergebnissen auswählen. Je nach Anwendungsfall möchten Sie möglicherweise Modelle auf den ML-Knoten von Elastic hosten, einen Service-Endpunkt eines Drittanbieters über die Inference-API verwenden oder Ihr lokales Modell vor Ort ausführen. Dies ist über eine Suchvorlage möglich und wir werden im nächsten Abschnitt sehen, wie es funktioniert.</p><h2>Beginnen Sie mit Referenzvorlagen und erstellen Sie dann Ihre eigenen</h2><p>Damit Sie schnell loslegen können, haben wir eine Reihe kompatibler Suchvorlagenbeispiele bereitgestellt, die Sie als erste Referenz verwenden können. Anschließend können Sie Ihre eigenen Vorlagen ändern und darauf aufbauen:</p><ul><li>Semantische Suche mit ELSER-Modell (spärliche Vektoren und Chunking)</li><li>Semantische Suche mit dem mehrsprachigen e5-Modell (dichte Vektoren und Chunking)</li><li>Hybridsuche mit dem Text-Embedding-Modell von Vertex AI</li></ul><p>Sie finden sie in diesem <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/Cloud-Vertex-AI/search-templates">GitHub-Repo</a>.</p><p>Sehen wir uns ein Beispiel an: Erstellen von Einbettungen mit den Vertex AI-APIs von Google Cloud in einem Produktkatalog. Zuerst müssen wir die Suchvorlage in Elasticsearch wie unten gezeigt erstellen:</p><pre><code>PUT _scripts/google-template-knn
{
  "script": {
    "lang": "mustache",
    "source": {
      "_source": {
        "excludes": [ "title_embedding", "description_embedding", "images" ]
      },
        "size": "{{num_hits}}",
          "knn" : [
          { 
            "field": "description_embedding",
            "k": 5,
            "num_candidates": 10,
            "query_vector_builder": {
              "text_embedding": {
                "model_id": "googlevertexai_embeddings_004",
                "model_text": "{{query}}"
              }
            },
            "boost": 0.4
          },
          {
            "field": "title_embedding",
            "k": 5,
            "num_candidates": 10,
            "query_vector_builder": {
              "text_embedding": {
                "model_id": "googlevertexai_embeddings_004",
                "model_text": "{{query}}"
            }
          },
          "boost": 0.6
          }
          ]
    }  
  }
}</code></pre><p>In diesem Beispiel führen wir die KNN-Suche in zwei Feldern innerhalb einer einzigen Suche aus: <code>title_embedding</code> – das Vektorfeld, das den Namen des Produkts enthält – und <code>description_embedding</code> – das Feld, das die Darstellung seiner Beschreibung enthält.</p><p>Sie können die <code>excludes</code> -Syntax nutzen, um die Rückgabe unnötiger Felder an das LLM zu vermeiden, da dies zu Störungen bei der Verarbeitung führen und die Qualität der endgültigen Antwort beeinträchtigen kann. In unserem Beispiel haben wir die Felder ausgeschlossen, die Vektoren und Bild-URLs enthalten.</p><p>Vektoren werden zum Zeitpunkt der Abfrage auf der übermittelten Eingabe über einen Inferenzendpunkt zur Vertex AI-Einbettungs-API <code>googlevertexai_embeddings_004</code> im laufenden Betrieb erstellt, der zuvor wie folgt definiert wurde:</p><pre><code>PUT /_inference/text_embedding/googlevertexai_embeddings_004
{
    "service": "googlevertexai",
    "service_settings": {
        "service_account_json": "<your_service_account_key>",
        "model_id": "text-embedding-004",
        "location": "us-central1",
        "project_id": "<your_gcp_project>"
    }
}</code></pre><p>Weitere Informationen zur Verwendung der Inference API von Elastic finden Sie <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/inference-apis.html">hier</a>.</p><p>Wir sind jetzt bereit, unsere Vorlagensuche zu testen:</p><pre><code>GET product-catalog-with-embeddings/_search/template
{
  "id": "google-template-knn",
  "params": {
    "query": "What do I need to patch a hole in my drywall?",
    "index_name": "product-catalog-with-embeddings",
    "num_hits": 3
  }
}</code></pre><p>Die Felder <code>params</code> ersetzen die Variablen, die wir in den Vorlagenskripten in doppelten geschweiften Klammern festgelegt haben. Derzeit können die APIs Vertex AI LLM und Grounded Generation die folgenden Eingabevariablen an Elastic senden:</p><ul><li>„query“ – die zu durchsuchende Benutzerabfrage</li><li>„index_name“ – der Name des Index, in dem gesucht werden soll</li><li>„num_hits“ – wie viele Dokumente wir in der endgültigen Ausgabe abrufen möchten</li></ul><p>Hier ist eine Beispielausgabe:</p><pre><code>{
        "_index": "product-catalog-with-embeddings",
        "_id": "9ZQCm5IBcrGI1ivqV-f_",
        "_score": 0.4925191,
        "_ignored": [
          "description.keyword",
          "images.keyword"
        ],
        "_source": {
          "description": "DAP Eclipse Rapid Wall Repair Patch is a new, revolutionary product solution for repairing drywall damage. No more waiting for spackling to dry or messy sanding. DAP Eclipse allows you to patch drywall damage and paint immediately, allowing you to finish your project faster. This all-in-1, mess free solution not only provides a permanent, long-lasting repair but also superior impact resistance for areas that may see reoccurring impact, such as behind a door.",
          "availability": "InStock",
          "model_id": "googlevertexai_embeddings_004",
          "title": "4 in. Eclipse Wall Repair Patch (2-Pack)",
          "url": "https://www.myDIYwebsite.com/p/DAP-4-in-Eclipse-Wall-Repair-Patch-2-Pack-7079809164/317967195",
          "price": 23.96,
          "product_id": 317967195,
          "currency": "USD",
          "brand": "DAP"
        }</code></pre><p>Die obige Abfrage ist genau das, was Vertex AI von Google Cloud im Hintergrund auf Elasticsearch ausführt, wenn auf die zuvor erstellte Suchvorlage verwiesen wird. Gemini-Modelle verwenden die Ausgabedokumente, um ihre Antwort zu untermauern: Wenn Sie fragen „Was brauche ich, um meine Trockenbauwand zu flicken?“, erhalten Sie vom Chat-Agenten keinen allgemeinen Vorschlag, sondern konkrete Produkte!</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8c8b1374da7e9b2c17df8758acb448eed5b74e2d-1473x913.png" style="width: 100%;" /><h2>End-to-End-GenAI-Reise mit Elastic und Google Cloud</h2><p>Elastic arbeitet mit Google Cloud zusammen, um produktionsreife, durchgängige GenAI-Erlebnisse und -Lösungen zu schaffen. Wie wir gerade gesehen haben, ist Elastic der erste ISV, der direkt in die Benutzeroberfläche und das SDK für die Vertex AI-Plattform integriert ist, was nahtlose, fundierte Eingabeaufforderungen und Agenten für Gemini-Modelle mithilfe unserer Vektorsuchfunktionen ermöglicht. Darüber hinaus lässt sich Elastic in die Einbettungs-, Neubewertungs- und Vervollständigungsmodelle von <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/infer-service-google-vertex-ai.html">Vertex AI</a> und <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/infer-service-google-ai-studio.html">Google AI Studio</a>integrieren, um Vektoren zu erstellen und zu bewerten, ohne die Google Cloud-Landschaft zu verlassen, und so die Grundsätze <a href="https://cloud.google.com/responsible-ai?hl=en">verantwortungsvoller KI </a>sicherzustellen. Durch die Unterstützung multimodaler Ansätze ermöglichen wir gemeinsam Anwendungen über verschiedene Datenformate hinweg.</p><p>Sie können Ihren GenAI-Suchcode über unseren <a href="https://www.elastic.co/de/search-labs/blog/vertex-ai-elasticsearch-playground-fast-rag-apps">Playground</a> optimieren, testen und exportieren.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b1fc48dba1713cd01c4d7f3296587d0c4e6e7e0c-862x651.png" style="width: 100%;" /><p>Aber es geht nicht nur um die Entwicklung von Such-Apps: Elastic nutzt Gemini-Modelle, um den IT-Betrieb zu stärken, beispielsweise in den <a href="https://www.elastic.co/de/blog/elastic-google-vertex-ai-integration">Elastic AI Assistants, der Angriffserkennung und den Funktionen für den automatischen Import</a>. Dadurch wird die tägliche Ermüdung von Sicherheitsanalysten und SREs bei Aufgaben mit geringem Wert verringert und es ihnen ermöglicht, sich auf die Verbesserung ihres Geschäfts zu konzentrieren. Elastic ermöglicht außerdem eine umfassende <a href="https://www.elastic.co/de/guide/en/integrations/current/gcp_vertexai.html">Überwachung der Vertex AI-Nutzung</a> und verfolgt Metriken und Protokolle wie Antwortzeiten, Token und Ressourcen, um eine optimale Leistung sicherzustellen. Gemeinsam verwalten wir den gesamten GenAI-Lebenszyklus, von der Datenaufnahme und Einbettungsgenerierung bis hin zur Verankerung mit hybrider Suche, und gewährleisten gleichzeitig eine robuste Beobachtbarkeit und Sicherheit der GenAI-Tools mit LLM-gestützten Aktionen.</p><h2>Entdecken Sie mehr und probieren Sie es aus!</h2><p>Haben Sie Interesse, dies auszuprobieren? Die Funktion ist derzeit in Ihren Google Cloud-Projekten allgemein verfügbar!</p><p>Falls Sie dies noch nicht getan haben, können Sie am einfachsten mit der Elastic Search AI Platform beginnen und unsere Funktionen erkunden, indem Sie Ihre <a href="https://cloud.elastic.co/registration">kostenlose Elastic Cloud-Testversion</a> nutzen oder sich über <a href="https://console.cloud.google.com/marketplace/product/elastic-prod/elastic-cloud?pli=1">den Google Cloud Marketplace</a> anmelden.</p><p><em>Die Entscheidung über die Veröffentlichung der in diesem Blogeintrag beschriebenen Leistungsmerkmale und Features sowie deren Zeitpunkt liegt allein bei Elastic. Es ist möglich, dass noch nicht verfügbare Leistungsmerkmale oder Features nicht rechtzeitig oder überhaupt nicht veröffentlicht werden. Elastic, Elasticsearch und zugehörige Marken, Waren- und Dienstleistungszeichen sind Marken oder eingetragene Marken von Elastic N.V. in den USA und anderen Ländern. Alle weiteren Marken- oder Warenzeichen sind eingetragene Marken oder eingetragene Warenzeichen der jeweiligen Eigentümer.</em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Beschleunigung der Zusammenführung von HNSW-Diagrammen]]></title>
            <description><![CDATA[Informieren Sie sich über unsere Arbeit zur Reduzierung des Aufwands beim Erstellen mehrerer HNSW-Diagramme, insbesondere zur Reduzierung der Kosten für das Zusammenführen von Diagrammen.]]></description>
            <link>https://www.elastic.co/search-labs/de/blog/hnsw-graphs-speed-up-merging</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/de/blog/hnsw-graphs-speed-up-merging</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Mayya Sharipova]]></dc:creator>
            <pubDate>Mon, 07 Apr 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/49a6c880f5dedd0fa502ece5be124824ee218cc0-1792x1024.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>In der Vergangenheit <a href="https://www.elastic.co/de/search-labs/blog/multi-graph-vector-search">haben wir einige der Herausforderungen erörtert</a> , die mit der Suche in mehreren <a href="https://www.elastic.co/de/search-labs/blog/hnsw-graph">HNSW-Graphen</a> einhergehen, und wie wir diese bewältigen konnten. Damals deuteten wir einige weitere Verbesserungen an, die wir geplant hatten. Dieser Beitrag ist der Höhepunkt dieser Arbeit.</p><p>Sie fragen sich vielleicht, warum Sie überhaupt mehrere Diagramme verwenden? Dies ist ein Nebeneffekt einer Architekturentscheidung in Lucene: unveränderliche Segmente. Wie bei den meisten architektonischen Entscheidungen gibt es Vor- und Nachteile. Beispielsweise haben wir vor Kurzem Serverless Elasticsearch allgemein zugänglich gemacht. In diesem Zusammenhang haben wir durch unveränderliche Segmente erhebliche Vorteile erzielt, darunter eine effiziente Indexreplikation und die Möglichkeit, die Index- und Abfrageberechnung zu entkoppeln und unabhängig voneinander automatisch zu skalieren. Bei der Vektorquantisierung bieten uns Segmentzusammenführungen die Möglichkeit, Parameter zu aktualisieren, um sie an die Dateneigenschaften anzupassen. In diesem Sinne sind wir der Meinung, dass die Möglichkeit, Datenmerkmale zu messen und Indexierungsentscheidungen zu überprüfen, noch weitere Vorteile mit sich bringt.</p><p>In diesem Beitrag besprechen wir die Arbeit, die wir geleistet haben, um den Aufwand für die Erstellung mehrerer HNSW-Diagramme deutlich zu reduzieren und insbesondere die Kosten für das Zusammenführen von Diagrammen zu senken.</p><h3>Hintergrund</h3><p>Um eine überschaubare Anzahl von Segmenten beizubehalten, prüft Lucene regelmäßig, ob Segmente zusammengeführt werden sollen. Dies läuft darauf hinaus, zu prüfen, ob die aktuelle Segmentanzahl eine Zielsegmentanzahl überschreitet, die durch die Basissegmentgröße und die Zusammenführungsrichtlinie bestimmt wird. Wenn die Anzahl überschritten wird, führt Lucene Segmentgruppen zusammen, während die Einschränkung verletzt wird. Dieser Vorgang wurde <a href="https://blog.mikemccandless.com/2011/02/visualizing-lucenes-segment-merges.html">an anderer Stelle</a> ausführlich beschrieben.</p><p>Lucene entscheidet sich für die Zusammenführung ähnlich großer Segmente, da dadurch ein logarithmisches Wachstum der Schreibverstärkung erreicht wird. Im Fall eines Vektorindex ist die Schreibverstärkung die Anzahl der Male, die ein Vektor in ein Diagramm eingefügt wird. Lucene versucht, Segmente in Gruppen von etwa 10 zusammenzuführen. Folglich werden Vektoren in einen Graphen eingefügt, der ungefähr $1+\frac{9}{10}\log_{10}\left(\frac{n}{n_0}\right)$beträgt.<em> </em>mal, wobei $n$ die Anzahl der Indexvektoren und $n_0$ die erwartete Anzahl der Basissegmentvektoren ist. Aufgrund des logarithmischen Wachstums liegt die Schreibverstärkung selbst bei großen Indizes im einstelligen Bereich. Die Gesamtzeit, die zum Zusammenführen von Graphen benötigt wird, ist jedoch linear proportional zur Schreibverstärkung.</p><p>Beim Zusammenführen von HNSW-Graphen nehmen wir bereits eine kleine Optimierung vor: Wir behalten den Graphen für das größte Segment bei und fügen Vektoren aus den anderen Segmenten darin ein. Dies ist der Grund für den oben genannten 9/10-Faktor. Im Folgenden zeigen wir, wie wir durch die Verwendung von Informationen aus allen Diagrammen, die wir zusammenführen, deutlich bessere Ergebnisse erzielen können.</p><h3>HNSW-Graphzusammenführung</h3><p>Bisher haben wir den größten Graphen beibehalten und Vektoren aus den anderen eingefügt, wobei wir die Graphen, die sie enthalten, ignoriert haben. Die zentrale Erkenntnis, die wir im Folgenden nutzen, ist, dass jeder HNSW-Graph, den wir verwerfen, wichtige Näheinformationen über die darin enthaltenen Vektoren enthält. Wir möchten diese Informationen nutzen, um das Einfügen zumindest einiger Vektoren zu beschleunigen.</p><p>Wir konzentrieren uns auf das Problem des Einfügens eines kleineren Graphen $G_s=(V_s,E_s)$ in einen größeren Graphen $G_l=(V_l,E_l)$, da dies eine atomare Operation ist, die wir zum Erstellen beliebiger Zusammenführungsrichtlinien verwenden können.</p><p>Die Strategie besteht darin, eine Teilmenge von Eckpunkten von $J\subset V_s$ zu finden, die in den großen Graphen eingefügt werden soll. Wir verwenden dann die Konnektivität dieser Knoten im kleinen Graphen, um das Einfügen der verbleibenden Knoten $V_s \setminus J$ zu beschleunigen. Im Folgenden verwenden wir $N_s(u)$ und $N_l(u),$ um die Nachbarn eines Knotens $u$ im kleinen bzw. großen Graphen zu bezeichnen. Schematisch ist der Ablauf wie folgt.</p><p><code>MERGE-HNSW</code></p><p><code>Inputs $G_s$ and $G_l$</code></p><p><code>1$\;\;$Find $J\subset V_s$ to insert into $G_l$ using COMPUTE-JOIN-SET</code><br/><code>2$\;\;$Insert each vertex $u\in J$ into $G_l$</code><br/><code>3$\;\;$for $u\in V_s \setminus J$ do</code><br/><code>4$\;\;\;\;J_u\leftarrow J\cap N_s(u)$</code><br/><code>5$\;\;\;\;E_u\leftarrow \cup_{v\in J_u} N_l(u)$</code><br/><code>6$\;\;\;\;W\leftarrow\,$FAST-SEARCH-LAYER$(J_u, E_u)$</code><br/><code>7$\;\;\;\;neighbors \leftarrow\,$SELECT-NEIGHBORS-HEURISTIC$(u, W)$</code><br/><code>8$\;\;\;\;J\leftarrow J \cup \{u\}$</code></p><p>Wir berechnen die Menge $J$ mithilfe eines Verfahrens, das wir unten besprechen (Zeile 1). Dann fügen wir jeden Knoten in $J$ mithilfe des Standard-HNSW-Einfügeverfahrens in den großen Graphen ein (Zeile 2). Für jeden Knoten, den wir nicht eingefügt haben, finden wir seine Nachbarn, die wir eingefügt haben, und deren Nachbarn im großen Graphen (Zeilen 4 und 5). Wir verwenden ein mit diesem Set (Zeile 6) gesätes <code>FAST-SEARCH-LAYER</code> -Verfahren, um die Kandidaten für <code>SELECT-NEIGHBORS-HEURISTIC</code> aus dem HNSW- <a href="https://arxiv.org/pdf/1603.09320">Papier</a> (Zeile 7) zu finden. Tatsächlich ersetzen wir <code>SEARCH-LAYER</code> , um den Kandidatensatz in der Methode <code>INSERT</code> (Algorithmus 1 aus dem Dokument) zu finden, die ansonsten unverändert bleibt. Schließlich fügen wir den Scheitelpunkt hinzu, den wir gerade in $J$ eingefügt haben (Zeile 8).</p><p>Damit dies funktioniert, ist klar, dass jeder Scheitelpunkt in $V_s \setminus J$ mindestens einen Nachbarn in $J$ haben muss. Tatsächlich verlangen wir, dass für jeden Scheitelpunkt in $u\in V_s \setminus J$ $|J\cap N_s(u) |\geq k_u$ für ein gewisses $k_u&lt;M$, die maximale Schichtkonnektivität, gilt. Wir beobachten, dass in realen HNSW-Diagrammen eine ziemliche Streuung der Scheitelpunktgrade zu sehen ist. Die folgende Abbildung zeigt eine typische kumulative Dichtefunktion des Scheitelpunktgrads für die unterste Ebene eines Lucene HNSW-Diagramms.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/44f001b3a1bc6627e172fed5c52a02cfdcb4cd66-1324x898.png" style="width: 100%;" /><p>Wir haben die Verwendung eines festen Werts für $k_u$ sowie die Möglichkeit untersucht, ihn zu einer Funktion des Scheitelpunktgrads zu machen. Diese zweite Wahl führt zu größeren Beschleunigungen bei minimalen Auswirkungen auf die Grafikqualität, daher habe ich mich für Folgendes entschieden</p><div class="katex">k_u=\max\left(2,\frac{1}{4}|N_s(u)|\right)</div><p>Beachten Sie, dass $|N_s(u)$| per Definition gleich dem Grad des Scheitelpunkts $u$ im kleinen Graphen ist. Eine Untergrenze von zwei bedeutet, dass wir jeden Scheitelpunkt einfügen, dessen Grad kleiner als zwei ist.</p><p>Ein einfaches Zählargument legt nahe, dass wir bei sorgfältiger Wahl von $J$ nur etwa $\frac{1}{5}|V_s|$ direkt in $G_l$ einfügen müssen. Konkret färben wir eine Kante des Graphen, wenn wir genau einen ihrer Endknoten in $J$ einfügen. Dann wissen wir, dass wir für jeden Knoten in $V_s \setminus J$ mindestens $\sum_{u\in V_s\setminus J} k_u$ Kanten einfärben müssen, um mindestens $k_u$ Nachbarn in $J zu haben.$ Darüber hinaus erwarten wir, dass</p><div class="katex">\sum_{u\in V_s\setminus J} k_u\approx \left(|V_s|-|J|\right)\frac{1}{4}\mathbb{E}_U\left[|N_s(U)|\right]</div><p>Dabei ist $\mathbb{E}_U\left[N_s(U)|\right]$ der durchschnittliche Knotengrad im kleinen Graphen. Für jeden Knoten $u\in J$ färben wir höchstens $|N_s(u)|$ Kanten. Daher beträgt die Gesamtzahl der Kanten, die wir voraussichtlich einfärben werden, höchstens $|J|\, \mathbb{E}_U\left[|N_s(U)|\right]$. Wir hoffen, dass wir durch sorgfältige Wahl von $J$ annähernd diese Anzahl von Kanten einfärben können. Um alle Eckpunkte abzudecken, muss $|J|$ folgende Bedingung erfüllen:</p><div class="katex">|J|\, \mathbb{E}_U\left[|N_s(U)|\right] =\left(|V_s|-|J|\right)\frac{1}{4}\mathbb{E}_U\left[|N_s(U)|\right]</div><p>Dies impliziert, dass $|J|=\frac{1}{4}(|V_s|-|J|)=\frac{4}{5}\frac{1}{4}|V_s|=\frac{1}{5}|V_s|$.</p><p>Vorausgesetzt, <code>SEARCH-LAYER</code> dominiert die Laufzeit, lässt dies darauf schließen, dass wir eine bis zu $fünffache$ Beschleunigung der Zusammenführungszeit erreichen könnten. Angesichts des logarithmischen Wachstums der Schreibverstärkung bedeutet dies, dass wir selbst bei sehr großen Indizes die Erstellungszeit im Vergleich zum Erstellen eines Diagramms normalerweise nur verdoppeln würden.</p><p>Das Risiko bei dieser Strategie besteht darin, dass wir die Grafikqualität beeinträchtigen. Wir haben es zunächst mit einem No-Op <code>FAST-SEARCH-LAYER</code> versucht. Wir haben festgestellt, dass dies die Qualität des Diagramms in dem Maße verschlechtert, dass die Rückrufrate als Funktion der Latenz beeinträchtigt wird, insbesondere beim Zusammenführen auf ein einzelnes Segment. Anschließend haben wir mithilfe einer eingeschränkten Suche im Diagramm verschiedene Alternativen untersucht. Am Ende war die effektivste Wahl die einfachste. Verwenden Sie <code>SEARCH-LAYER</code> , aber mit einem niedrigen <code>ef_construction</code>. Mit dieser Parametrisierung konnten wir Grafiken von hervorragender Qualität erzielen und die Zusammenführungszeit dennoch durchschnittlich um etwas mehr als 30 % verkürzen.</p><h3>Berechnung der Join-Menge</h3><p>Die Suche nach einer guten Join-Menge kann als HNSW-Graphüberdeckungsproblem formuliert werden. Eine Greedy-Heuristik ist eine einfache und effektive Heuristik zur Annäherung an optimale Graphüberdeckungen. Bei unserem Ansatz werden die Knotenpunkte nacheinander in absteigender Reihenfolge der Verstärkung zu $J$ hinzugefügt. Der Gewinn ist wie folgt definiert:</p><div class="katex">Gain(v)=\max(k_v-c(v),0)+\sum_{u\in N_s(v)\setminus J} 1\left\{c(u)<k_u\right\}</div><p>Dabei bezeichnet $c(v)$ die Anzahl der Nachbarn eines Vektors $v$ in $J$ und $1\{\cdot\}$ ist die Indikatorfunktion. Der Gewinn beinhaltet die Änderung der Anzahl der Scheitelpunkte, die wir zu $J$ hinzugefügt haben, also $\max(k_v-c(v),0)$, da wir unserem Ziel durch das Hinzufügen eines weniger abgedeckten Scheitelpunkts näher kommen. Die Gewinnberechnung wird in der folgenden Abbildung für den zentralen orangefarbenen Scheitelpunkt veranschaulicht.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/fe11cd55a7d94d9e0b0f4d47ea309a99075c355d-540x474.png" style="width: 100%;" /><p>Wir behalten für jeden Knoten $v$ den folgenden Zustand bei:</p><ol><li>Ob es abgestanden ist,</li><li>Sein Gewinn $Gain(v)$,</li><li>Die Anzahl der benachbarten Eckpunkte in $J$ wird $mit c(v)$ bezeichnet,</li><li>Eine Zufallszahl im Bereich [0,1], die zum Auflösen eines Gleichstands verwendet wird.</li></ol><p>Der Pseudocode zum Berechnen des Join-Sets lautet wie folgt.</p><p><code>COMPUTE-JOIN-SET</code></p><p><code>Inputs $G_s$</code></p><p><code>1$\;\;C\leftarrow\emptyset$</code><br/><code>2$\;\;Gain_{exit}\leftarrow 0$</code><br/><code>3$\;\;$for $u\in G_s$ do</code><br/><code>4$\;\;\;\;C\leftarrow C \cup \{(\text{false}, k_u+deg(u), 0, \text{rand in }[0,1])\}$</code><br/><code>5$\;\;\;\;Gain_{exit}\leftarrow Gain_{exit}+k_u$</code><br/><code>6$\;\;Gain_{tot}\leftarrow 0$</code><br/><code>7$\;\;$while $Gain_{tot}&lt;Gain_{exit}$ do<br/>8$\;\;\;\;v^*\leftarrow\,$ maximum gain vertex in $C$</code><br/><code>9$\;\;\;\;$Remove the state for $v^*$ from $C$</code><br/><code>10$\;\;\;$if $v^*$ is not stale then</code><br/><code>11$\;\;\;\;\;J\leftarrow J\cup\{v^*\}$</code><br/><code>12$\;\;\;\;\;Gain_{tot}\leftarrow Gain_{tot}+Gain(v^*)$</code><br/><code>13$\;\;\;\;\;$for $u \in N_s(v^*)$ do</code><br/><code>14$\;\;\;\;\;\;\;$mark $u$ as stale if $c(v^*)&lt;k_{v^*}$</code><br/><code>15$\;\;\;\;\;\;\;$mark neighbors of $u$ stale if $c(u)=k_u-1$<br/>16$\;\;\;\;\;\;\;c(u)\leftarrow c(u)+1$<br/>17$\;\;\;$else<br/>18$\;\;\;\;\;Gain(v^*)\leftarrow \max(k_v-c(v^*),0)+\sum_{u\in N_s(v^*)\setminus J} 1\left\{c(u)&lt;k_u\right\}$<br/>19$\;\;\;\;\;$if $Gain(v^*)&gt;0$ then<br/>20$\;\;\;\;\;\;\;C\leftarrow C \cup \{(\text{false},Gain(v^*),c(v^*),\text{copy rand})\}$<br/>21$\;\;$return $J$</code></p><p>Wir initialisieren zunächst den Status in den Zeilen 1-5.</p><p>In jeder Iteration der Hauptschleife extrahieren wir zunächst den Scheitelpunkt mit der maximalen Verstärkung (Zeile 8) und lösen Gleichstände nach dem Zufallsprinzip auf. Bevor wir Änderungen vornehmen, müssen wir prüfen, ob die Verstärkung des Scheitelpunkts veraltet ist. Insbesondere beeinflussen wir jedes Mal, wenn wir einen Scheitelpunkt zu $J$ hinzufügen, den Gewinn anderer Scheitelpunkte:</p><ol><li>Da alle seine Nachbarn einen zusätzlichen Nachbarn in $J$ haben, können sich ihre Gewinne ändern (Zeile 14).</li><li>Wenn einer seiner Nachbarn jetzt vollständig gedeckt ist, können sich die Gewinne aller seiner Nachbarn ändern (Zeilen 14-16).</li></ol><p>Wir berechnen die Gewinne auf verzögerte Weise neu, d. h. wir berechnen den Gewinn eines Scheitelpunkts nur dann neu, wenn wir ihn in $J$ einfügen möchten (Zeilen 18–20). Da die Gewinne immer nur abnehmen, können wir nie einen Scheitelpunkt verpassen, den wir einfügen sollten.</p><p>Beachten Sie, dass wir lediglich den Gesamtgewinn der zu $J$ hinzugefügten Scheitelpunkte im Auge behalten müssen, um zu bestimmen, wann wir aussteigen müssen. Darüber hinaus wird, obwohl $Gain_{tot}&lt;Gain_{exit}ist,$ mindestens ein Scheitelpunkt einen von Null verschiedenen Gewinn aufweisen, sodass wir immer Fortschritte machen.</p><h3>Ergebnisse</h3><p>Wir haben Experimente mit vier Datensätzen durchgeführt, die zusammen unsere drei unterstützten Distanzmetriken (euklidisch, Kosinus und inneres Produkt) abdecken:</p><ol><li>quora-E5-small: 522931 Dokumente, 384 Dimensionen und verwendet Kosinusähnlichkeit,</li><li>cohere-wikipedia-v2: 1 Million Dokumente, 768 Dimensionen und verwendet Kosinusähnlichkeit,</li><li>Kern: 1 Million Dokumente, 960 Dimensionen und verwendet euklidische Distanz und</li><li>cohere-wikipedia-v3: 1 Mio. Dokumente, 1024 Dimensionen und verwendet das maximale innere Produkt.</li></ol><p>Für jeden Datensatz bewerten wir zwei Quantisierungsstufen:</p><ol><li>int8 – verwendet eine 1-Byte-Ganzzahl pro Dimension und</li><li>BBQ – das ein einzelnes Bit pro Dimension verwendet.</li></ol><p>Schließlich haben wir für jedes Experiment die Suchqualität bei zwei Abruftiefen bewertet und nach dem Erstellen des Index und anschließend nach der erzwungenen Zusammenführung zu einem einzelnen Segment untersucht.</p><p>Zusammenfassend lässt sich sagen, dass wir durchgängig erhebliche Beschleunigungen bei der Indizierung und Zusammenführung erzielen und dabei in allen Fällen die Grafikqualität und damit die Suchleistung beibehalten.</p><h4>Experiment 1: int8-Quantisierung</h4><p>Die durchschnittlichen Beschleunigungen von der Basislinie bis zum Kandidaten, die vorgeschlagenen Änderungen, sind:</p><p>Beschleunigung der Indexzeit: <strong>1,28$\times$</strong></p><p>Beschleunigung der Force Merge: <strong>1,72$\times$</strong></p><p>Dies entspricht folgender Laufzeitaufteilung</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5f9f6d487b4c74c4998aa47465912c1a9743e928-734x479.png" style="width: 100%;" /><p>Der Vollständigkeit halber sind die genauen Zeiten</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;">Index</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;">Verschmelzen</td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Datensatz</td><td style="border: 1px solid #ccc; padding: 4px;">Basislinie</td><td style="border: 1px solid #ccc; padding: 4px;">Kandidat</td><td style="border: 1px solid #ccc; padding: 4px;">Entwickeln</td><td style="border: 1px solid #ccc; padding: 4px;">Kandidat</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Quora-E5-klein</td><td style="border: 1px solid #ccc; padding: 4px;">112,41 s</td><td style="border: 1px solid #ccc; padding: 4px;">81,55 s</td><td style="border: 1px solid #ccc; padding: 4px;">113,81 s</td><td style="border: 1px solid #ccc; padding: 4px;">70,87 s</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">wiki-cohere-v2</td><td style="border: 1px solid #ccc; padding: 4px;">158,1 s</td><td style="border: 1px solid #ccc; padding: 4px;">122,95 s</td><td style="border: 1px solid #ccc; padding: 4px;">425,20 s</td><td style="border: 1px solid #ccc; padding: 4px;">239,28 s</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Kern</td><td style="border: 1px solid #ccc; padding: 4px;">141,82 s</td><td style="border: 1px solid #ccc; padding: 4px;">119,26 s</td><td style="border: 1px solid #ccc; padding: 4px;">536,07 s</td><td style="border: 1px solid #ccc; padding: 4px;">279,05 s</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">wiki-cohere-v3</td><td style="border: 1px solid #ccc; padding: 4px;">211,86 s</td><td style="border: 1px solid #ccc; padding: 4px;">168,22 s</td><td style="border: 1px solid #ccc; padding: 4px;">654,97 s</td><td style="border: 1px solid #ccc; padding: 4px;">414,12 s</td>
                    </tr>
              </tbody>
            </table>
          <p>Unten zeigen wir die Diagramme „Rückruf vs. Latenz“, die den Kandidaten (gestrichelte Linien) mit der Basislinie bei zwei Abruftiefen vergleichen: Rückruf@10 und Rückruf@100 für Indizes mit mehreren Segmenten (das Endergebnis unserer Standard-Zusammenführungsstrategie nach der Indizierung aller Vektoren) und nach der erzwungenen Zusammenführung zu einem einzelnen Segment. Eine Kurve, die höher und weiter links liegt, ist besser, da sie eine höhere Rückrufrate bei geringerer Latenz bedeutet.</p><p>Wie Sie sehen, ist der Kandidat für mehrere Segmentindizes für den Cohere v3-Datensatz besser und für alle anderen Datensätze etwas schlechter, aber fast vergleichbar. Nach der Zusammenführung zu einem einzigen Segment sind die Erinnerungskurven in allen Fällen nahezu identisch.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9195297f19f90b172d832ba56bdada7b0d8a768b-985x392.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c866f32d579ae2b841e92ca733dd29c0e214ac6b-986x386.png" style="width: 100%;" /><h4>Experiment 2: BBQ-Quantisierung</h4><p>Die durchschnittlichen Beschleunigungen von der Basislinie zum Kandidaten sind:</p><p>Beschleunigung der Indexzeit: <strong>1,33$\times$</strong></p><p>Beschleunigung der Force Merge: <strong>1,34$\times$</strong></p><p>Dies entspricht folgender Laufzeitaufteilung</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/11be0cf6b63480dc703409329357ea4847b55051-740x415.png" style="width: 100%;" /><p>Der Vollständigkeit halber sind die genauen Zeiten</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;">Index</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;">Verschmelzen</td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Datensatz</td><td style="border: 1px solid #ccc; padding: 4px;">Basislinie</td><td style="border: 1px solid #ccc; padding: 4px;">Kandidat</td><td style="border: 1px solid #ccc; padding: 4px;">Entwickeln</td><td style="border: 1px solid #ccc; padding: 4px;">Kandidat</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Quora-E5-klein</td><td style="border: 1px solid #ccc; padding: 4px;">70,71 s</td><td style="border: 1px solid #ccc; padding: 4px;">58,25 s</td><td style="border: 1px solid #ccc; padding: 4px;">59,38 s</td><td style="border: 1px solid #ccc; padding: 4px;">40,15 s</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">wiki-cohere-v2</td><td style="border: 1px solid #ccc; padding: 4px;">203,08 s</td><td style="border: 1px solid #ccc; padding: 4px;">142,27 s</td><td style="border: 1px solid #ccc; padding: 4px;">107,27 s</td><td style="border: 1px solid #ccc; padding: 4px;">85,68 s</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Kern</td><td style="border: 1px solid #ccc; padding: 4px;">110,35 s</td><td style="border: 1px solid #ccc; padding: 4px;">105,52 s</td><td style="border: 1px solid #ccc; padding: 4px;">323,66 s</td><td style="border: 1px solid #ccc; padding: 4px;">202,2 s</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">wiki-cohere-v3</td><td style="border: 1px solid #ccc; padding: 4px;">313,43 s</td><td style="border: 1px solid #ccc; padding: 4px;">190,63 s</td><td style="border: 1px solid #ccc; padding: 4px;">165,98 s</td><td style="border: 1px solid #ccc; padding: 4px;">159,95 s</td>
                    </tr>
              </tbody>
            </table>
          <p>Bei Indizes mit mehreren Segmenten ist der Kandidat für fast alle Datensätze besser, mit Ausnahme von Cohere v2, wo die Basislinie etwas besser ist. Für die einzelnen Segmentindizes sind die Recall-Kurven in allen Fällen nahezu identisch.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7da1be27bab5a7f5f9c9a1882ea35761a4a0ba5c-973x383.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a26af30a56c55a12addee7e4fb7e8f606f366668-979x386.png" style="width: 100%;" /><h3>Fazit</h3><p>Der in diesem Blog besprochene Algorithmus wird im kommenden Lucene 10.2 und in der darauf basierenden Elasticsearch-Version verfügbar sein. Benutzer können in diesen neuen Versionen von der verbesserten Zusammenführungsleistung und der verkürzten Indexerstellungszeit profitieren. Diese Änderung ist Teil unserer kontinuierlichen Bemühungen, Lucene und Elasticsearch für die Vektor- und Hybridsuche schnell und effizient zu machen.</p>]]></content:encoded>
        </item>
    </channel>
</rss>