<?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[JD Armada - Elasticsearch Labs]]></title>
    <description><![CDATA[Articles and tutorials from the Search team at Elastic]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[JD Armada - Elasticsearch Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1121c0bf0e8a6e65/6a88da6340a1841030ef456f/search-labs-thumbnail.png</url>
      <link>https://www.elastic.co/de/search-labs/author/jd-armada</link>
    </image>
    <link>https://www.elastic.co/de/search-labs/author/jd-armada</link>
    <atom:link href="https://www.elastic.co/de/search-labs/rss/author/jd-armada.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[de]]></language>
    <lastBuildDate>Mon, 28 Sep 2026 08:27:34 GMT</lastBuildDate>
  <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>
    <content:encoded><![CDATA[<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><p>Problem</p></li><li><p>Kontext</p></li><li><p>Code</p></li><li><p>Tags</p></li><li><p>Die Lösungsschritte wurden verifiziert.</p></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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte7bc35b6d54921e8/6a17f0176df73162760a0fe6/45a3e96f4474050a855419628c2a7338bb12c706-1600x877.png" alt="Durch Klicken auf „Lösung teilen“ werden die aktuelle Sitzung sowie relevante Metadaten erfasst." /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9967f52007fff99e/6a17f019ec0f8987c45a6701/2d65cb154d8ee32fc96ff17dfa5b0bf2636e3777-1600x1002.png" alt="Die Nutzer geben über das Web-Frontend zusätzlichen Kontext an, anschließend wird das JSON in Elasticsearch indexiert." /><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><p>Rangliste, von der Community validierte Korrekturen</p></li><li><p>Die genauen Eingabeaufforderungen, die das Problem ursprünglich gelöst haben</p></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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd4ba582f9872b65b/6a17f01b7f6f15c2d8c09c1c/7d9716cca06a047a2b3584378b5c7e592a785ba1-1284x878.png" alt="6 spezialisierte GOOGL-KI-Agenten, die den Markt analysieren" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd86ed3bfe4b8bd2b/6a17f01c5ea30f868164b6ba/5aac6a833347c0d2e596c02049ec4b4d3aae5cd7-794x764.png" alt="Die spezialisierten Agenten von GOOGL erkennen Volumenanomalien und Katastrophen." /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5af7c7c838308275/6a17f01e42022917b629f6ca/46b3da8e3d528c5dd4e2829416c5446098acb3aa-744x718.png" alt="Einheitliches „Marktenergie“-Modell der spezialisierten GOOGL-Agenten" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt775e1880aa7afacc/6a17f01f1d1b83ce1f93e528/3f000c043117b77ed4127202be5a49c12e3682ba-1600x930.png" alt="Wie man Erkenntnisse aus der Analyse spezialisierter GOOGL-Agenten visualisiert" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltffe292009e446a70/6a17f0216df731068c0a0fea/76c49a853426844f475cd6b2a74999e60af20e8c-926x1080.png" alt="" /><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><p><strong>Agent Builder ist benutzerfreundlich:</strong></p></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><p><strong>LLMs sind hervorragend in </strong><strong><code>kNN</code></strong><strong> -Abfragen, benötigen aber dennoch Unterstützung bei der Generierung von ES|QL:</strong></p></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><p><strong>Nur für Snapshots verfügbare ES|QL-Funktionen in die Dokumentation gelangten:</strong></p></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><p><strong>Fehlende Serverless-spezifische Anleitung:</strong></p></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><p><strong>Wert von Präsenzveranstaltungen:</strong></p></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>
    <link>https://www.elastic.co/search-labs/blog/agent-builder-projects-learnings-cal-hacks-12-0</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/agent-builder-projects-learnings-cal-hacks-12-0</guid>
    <category><![CDATA[KI]]></category>
    <category><![CDATA[Agentische KI]]></category>
    <dc:creator><![CDATA[JD Armada]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0f079179be9832d4/6a17f023631730a69c585b6d/8ba034a6f19b50521f541b8131756a8acdb52975-1280x960.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 25 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[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>
    <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><p>Node v18+</p></li><li><p>Elasticsearch (Version 8.15 oder neuer)</p></li><li><p>Elasticsearch API-Schlüssel</p></li><li><p><a href="https://help.openai.com/en/articles/4936850-where-do-i-find-my-openai-api-key">OpenAI API-Schlüssel</a></p></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>npm create mastra@latest<p>Sie erhalten eine Reihe von Eingabeaufforderungen, beginnend mit:</p><p>1. Gib deinem Projekt einen Namen.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt87f941f654d03827/6a16f7af67045b214d45bfa1/2b9fe559e0276140dd539e24f916a73c60870405-620x84.png" alt="Benennen einer Eingabeaufforderung in der Mastra-App" /><p>2. Wir können diese Standardeinstellung beibehalten; Sie können dieses Feld gerne leer lassen.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7dbb3d4f27435cac/6a16f7b0cdacbf29497d27de/e04729eb03bce8499e973e18c28642402340d0e5-852x68.png" alt="Mastra mitteilen, wo die Prompt-Dateien gespeichert werden sollen" /><p>3. Für dieses Projekt verwenden wir ein von OpenAI bereitgestelltes Modell.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1f654f6cb9397e94/6a16f7b2964cea899a08b942/a86596a469a71bdf8bd99cbaf528d0f0cf7272c0-436x222.png" alt="Auswahl eines von OpenAI in Mastra bereitgestellten Modells" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltff106117521a3519/6a16f7b3c1e8a5031af880d8/02b19ccc34af0bdacf52fd94b519d036540ca2e6-426x114.png" alt="Die OpenAI-Schlüsselabfrage wird vorerst übersprungen." /><p>5. Diese Option können wir auch überspringen.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcda7d9c51c3878d7/6a16f7b450916809dbe1b892/b3fe63d19d270bc2e0de1dd92033bf8b26750819-990x208.png" alt="" /><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>npm install ai @ai-sdk/openai @elastic/elasticsearch dotenv<ul><li><p><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.</p></li><li><p><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.</p></li><li><p><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.</p></li><li><p><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.</p></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>ELASTICSEARCH_ENDPOINT="your-endpoint-here"
ELASTICSEARCH_API_KEY="your-key-here"
OPENAI_API_KEY="your-key-here"<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>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;
    }
}<ul><li><p><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.</p></li><li><p><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.</p></li><li><p><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.</p></li><li><p><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.</p></li><li><p><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.</p></li><li><p><code>super()</code>: Dadurch wird der Basiskonstruktor von Mastra aufgerufen, sodass Logging, Validierungshilfsmechanismen und andere interne Hooks geerbt werden.</p></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>private async detectServerless(): Promise&lt;boolean&gt; {
    // 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;
    }
}<p>Was passiert:</p><ul><li><p>Zuerst wird geprüft, ob Sie <code>isServerless</code> explizit in der Konfiguration festgelegt haben (überspringt die automatische Erkennung).</p></li><li><p>Ruft die <code>info()</code> -API von Elasticsearch auf, um Clusterinformationen zu erhalten.</p></li><li><p>Prüft den Wert <code>build_flavor field</code> (serverlose Bereitstellungen geben <code>serverless</code> zurück)</p></li><li><p>Falls die Build-Variante nicht verfügbar ist, wird auf die Überprüfung des Slogans zurückgegriffen.</p></li><li><p>Speichert das Ergebnis im Cache, um wiederholte API-Aufrufe zu vermeiden.</p></li><li><p>Wird standardmäßig die Bereitstellung durchgeführt, wenn die Erkennung fehlschlägt.</p></li></ul><p> Anwendungsbeispiel:</p>// 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
});<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><p>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.</p></li><li><p>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.</p><ul><li><p>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.</p></li></ul></li><li><p>Serverless-Kompatibilität: Der Code lässt Shard- und Replikateinstellungen für serverlose Bereitstellungen automatisch aus, da diese von Elasticsearch Serverless automatisch verwaltet werden.</p></li></ul>async createIndex(params: CreateIndexParams): Promise&lt;void&gt; {
    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}`);
    }
}<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>async upsert(params: UpsertVectorParams): Promise&lt;string[]&gt; {
    const { indexName, vectors, metadata = [], ids } = params;

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

        const operations = vectors.flatMap((vec, index) =&gt; [
            { 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) =&gt; item.index?.error);
            const erroredIds = erroredItems.map((item: any) =&gt; item.index?._id);
            const errorDetails = erroredItems.slice(0, 3).map((item: any) =&gt; ({
                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) =&gt; 
                !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}`);
    }
}<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>async query(params: QueryVectorParams&lt;any&gt;): Promise&lt;QueryResult[]&gt; {
    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) =&gt; ({
            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}`);
    }
}<p>Unter der Haube:</p><ul><li><p>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.</p></li><li><p>Gibt die K ähnlichsten Vektoren zum Eingabeabfragevektor zurück.</p></li><li><p>Optional können Metadatenfilter angewendet werden, um die Ergebnisse einzugrenzen (z. B. nur innerhalb einer bestimmten Kategorie oder eines bestimmten Zeitraums zu suchen).</p></li><li><p>Gibt strukturierte Ergebnisse zurück, einschließlich der Dokument-ID, des Ähnlichkeitswerts und der gespeicherten Metadaten.</p></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>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,
});<p>Hier, wir:</p><ul><li><p>Verwenden Sie <code>dotenv</code> um unsere Variablen aus unserer <code>.env</code> -Datei zu laden.</p></li><li><p>Prüfen Sie, ob die Elasticsearch-Zugangsdaten korrekt eingefügt werden, dann können wir eine erfolgreiche Verbindung zum Client herstellen.</p></li><li><p>Ü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.</p></li><li><p>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.</p></li></ul><p>Als nächstes können wir den Agenten mithilfe der Klasse <code>Agent</code> von Mastra definieren.</p>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',
            },
        },
    }),
});<p>Folgende Felder können wir definieren:</p><ul><li><p><code>name</code> und <code>instructions</code>: Gib ihr eine Identität und eine primäre Funktion.</p></li><li><p><code>model</code>Wir verwenden OpenAIs <code>gpt-4o</code> über das <code>@ai-sdk/openai</code> -Paket.</p></li><li><p><code>memory</code>:</p><ul><li><p><code>vector</code>: Verweist auf unseren Elasticsearch-Speicher, sodass Einbettungen dort gespeichert und abgerufen werden.</p></li><li><p><code>embedder</code>Welches Modell soll zur Generierung von Einbettungen verwendet werden?</p></li><li><p><code>semanticRecall</code> Die Optionen bestimmen, wie der Rückruf funktioniert:</p><ul><li><p><code>topK</code>: Wie viele semantisch ähnliche Nachrichten sollen abgerufen werden?</p></li><li><p><code>messageRange</code>: Wie viel vom Gespräch soll bei jedem Spielzug einbezogen werden?</p></li><li><p><code>scope</code>: Definiert die Speichergrenze.</p></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>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 }, 
  },
});<p>Zu den weiteren Bereichen gehören:</p><ul><li><p><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>.</p></li><li><p><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.</p></li><li><p><code>observability</code>: Steuert die KI-Verfolgung und die Sichtbarkeit der Ausführung von Agenten. Es verfolgt:</p><ul><li><p>Beginn/Ende jedes Denkschritts.</p></li><li><p>Welches Modell oder Werkzeug wurde verwendet?</p></li><li><p>Ein- und Ausgänge.</p></li><li><p>Bewertungen und Beurteilungen</p></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>npm run dev<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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte5f857fddc74ffc9/6a16f7b6a6c2b995d5e794c0/8b045f70008d26aec4d2e6b59d61085555b9c5b2-686x116.png" alt="Serveradresse für Playground" /><p>Fügen Sie diese Adresse in Ihren Browser ein, und Sie gelangen zum Mastra Studio.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc7fdda6ce46ce068/6a16f7b7b0367d4f7672bacf/69bc80fe8486edd9e0cf91d87b39f465aeb23111-1600x438.png" alt="Einfügen der Playground-Adresse, um auf Mastra Studio zuzugreifen" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfec3266e81a7213b/6a16f7b92b835f6f70f4afe2/da8ebddad89874023ed440a8f1ad2cb04ed043f4-1070x288.png" alt="Chatten mit einem Wissensagenten in Mastra Studio – der Agent kann Informationen abrufen" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte428134d7bf2a43a/6a16f7bbb0367d185872bad3/3decaa0c332d288c5ae0b11c25f592c7d50c2f0f-1104x1320.png" alt="Wie man den Langzeitspeicher von Wissensagenten untersucht" /><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>
    <link>https://www.elastic.co/search-labs/blog/knowledge-agent-semantic-recall-mastra-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/knowledge-agent-semantic-recall-mastra-elasticsearch</guid>
    <category><![CDATA[Agentische KI]]></category>
    <category><![CDATA[Entwicklererfahrung]]></category>
    <category><![CDATA[Integrationen]]></category>
    <dc:creator><![CDATA[JD Armada]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt09afdbff05603865/6a16f7bd839dfabbf2dcfcb5/b8d51c2726d5573385c9246a7821d12ade4f1b0e-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 06 Nov 2025 00:00:00 GMT</pubDate>
  </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>
    <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><p>Verwendung der <code>_mapping</code> -API zum Abrufen von Feldinformationen</p></li><li><p>Verwendung der <code>_search</code> API zum Anzeigen von Feldwerten</p></li><li><p>Unterfelder anzeigen</p></li><li><p>Synthetisches Feld „_source“</p></li><li><p>Laufzeitfelder</p></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>GET /&lt;index_name&gt;/_mapping<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>GET /my_index/_mapping<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>GET /my_index/_mapping/field/my_field<p>Sie können die Zuordnungen mehrerer Felder auch abrufen, indem Sie deren Namen durch Kommas trennen, wie in der folgenden Anfrage:</p>GET /my_index/_mapping/field/my_field_1,my_field_2,my_field_3<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><p><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.</p></li><li><p><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.</p></li></ol><h3>Verwendung des Feldes _source</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>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "_source": ["title", "author"]
}<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>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "_source": {
     “includes”: [“title”, “author.*],
     “excludes”: [“author.description”]
  }
}<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><p><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.</p></li><li><p><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.</p></li><li><p><strong>Zugriff auf Laufzeitfelder:</strong> <code>fields</code> kann Laufzeitfelder zurückgeben, die im ursprünglichen <code>_source</code> nicht existieren.</p></li><li><p>Weitere Vorteile finden Sie <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrieve-selected-fields#search-fields-param">hier</a>.</p></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>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "fields": ["title", "author"],
  "_source": false
}<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 <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrieve-selected-fields#docvalue-fields"><code>docvalue_fields</code></a>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>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "docvalue_fields": [
    "title",
    "author",
    {
      "field": "published",
      "format": "epoch_millis"
    }
  ],
  "_source": false
}<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>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "stored_fields": ["summary"]
}<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>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "fields": ["title", "author", "address.city"],
  "_source": false
}<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 </a><a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-source-field#synthetic-source"><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>PUT idx
{
  "settings": {
    "index": {
      "mapping": {
        "source": {
          "mode": "synthetic"
        }
      }
    }
  }
}<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>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-index-show-fields</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-index-show-fields</guid>
    <category><![CDATA[Index-Daten]]></category>
    <category><![CDATA[Mappings]]></category>
    <dc:creator><![CDATA[JD Armada]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd041e871a8935448/6a17de320b0bedf404dd34ab/23b96aaa1a38b1f4747b4a87695d816f24c0cf70-720x421.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 06 Aug 2025 00:00:00 GMT</pubDate>
  </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>
    <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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt63ea3e7a09306fbf/6a17f1d97f6f150e22c09c50/1c73bd1dc1b5fe54f025c7a2b7c322acc9122f3a-1999x1393.png" alt="" /><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.
</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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte13f09493f217047/6a17f1db1d1b83178d93e546/443bdc00d84ed1dd49e9f9e431e86ca4b0892563-1999x977.png" alt="" /><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><p><strong>Gerüstbau für das Projekt:</strong></p><ol><li><p><strong>Backend (Mastra):</strong> Verwenden Sie npx create mastra@latest, um das Backend zu erstellen und die Agentenlogik zu definieren.</p></li><li><p><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.</p></li></ol></li><li><p><strong>Einrichten von Umgebungsvariablen</strong></p><ol><li><p>Installieren Sie dotenv, um Umgebungsvariablen zu verwalten.</p></li><li><p>Erstellen Sie eine .env-Datei Datei und geben Sie die erforderlichen Variablen an.</p></li></ol></li><li><p><strong>Elasticsearch einrichten</strong></p><ol><li><p>Erstellen Sie einen Elasticsearch-Cluster (entweder lokal oder in der Cloud).</p></li><li><p>Installieren Sie den offiziellen Elasticsearch-Client.</p></li><li><p>Stellen Sie sicher, dass Umgebungsvariablen zugänglich sind.</p></li><li><p>Verbindung zum Client herstellen.</p></li></ol></li><li><p><strong>Massenhaftes Einlesen von NBA-Daten in Elasticsearch</strong></p><ol><li><p>Erstellen Sie einen Index mit den entsprechenden Zuordnungen, um Aggregationen zu ermöglichen.</p></li><li><p>Spielerstatistiken aus einer CSV-Datei massenhaft in einen Elasticsearch-Index importieren.</p></li></ol></li><li><p><strong>Elasticsearch-Aggregationen definieren</strong></p><ol><li><p>Abfrage zur Berechnung historischer Durchschnittswerte gegen einen bestimmten Gegner.</p></li><li><p>Abfrage zur Berechnung der Saison-Durchschnittswerte gegen einen bestimmten Gegner.</p></li></ol></li><li><p><strong>Datei zum Spielervergleich</strong></p><ol><li><p>Konsolidiert Hilfsfunktionen und Elasticsearch-Aggregationen.</p></li></ol></li><li><p><strong>Agentenaufbau</strong></p><ol><li><p>Fügen Sie die Agentendefinition und die Systemeingabeaufforderung hinzu.</p></li><li><p>Installieren Sie zod und definieren Sie Tools.</p></li><li><p>Fügen Sie eine Middleware-Konfiguration zur Behandlung von CORS hinzu.</p></li></ol></li><li><p><strong>Integration des Frontends</strong></p><ol><li><p>Die Interaktion mit dem Agenten erfolgt über die useChat-Funktion des AI-SDK.</p></li><li><p>Erstellen Sie eine Benutzeroberfläche, die ordnungsgemäß formatierte Konversationen ermöglicht.</p></li></ol></li><li><p><strong>Die Anwendung ausführen</strong></p><ol><li><p>Starten Sie sowohl das Backend (Mastra-Server) als auch das Frontend (React-App).</p></li><li><p>Beispielabfragen und Anwendungsbeispiele.</p></li></ol></li><li><p><strong>Was kommt als Nächstes: Den Agenten intelligenter machen.</strong></p><ol><li><p>Durch die Integration semantischer Suchfunktionen können aussagekräftigere Empfehlungen ermöglicht werden.</p></li><li><p>Aktivieren Sie dynamische Abfragen, indem Sie die Suchlogik auf den Elasticsearch MCP (Model Context Protocol)-Server verlagern.</p></li></ol></li></ol><h3><strong>Voraussetzungen</strong></h3><ul><li><p><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).</p></li><li><p><strong>Elasticsearch-Cluster:</strong> Ein aktiver Elasticsearch-Cluster, entweder lokal oder in der Cloud.</p></li><li><p><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>.</p></li></ul><p></p><h3><strong>Projektstruktur</strong></h3><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt749baa120552e4ab/6a17f1dd1d1b83bfe993e54a/1c0bde11ad0eead523a95e03b9b905aa776e3fd1-1420x934.png" alt="" /><h4><strong>Schritt 1: Das Projektgerüst erstellen</strong></h4><ol><li><p>Erstellen Sie zunächst das Verzeichnis nba-ai-assistant-js und navigieren Sie darin mit folgendem Befehl: </p></li></ol>mkdir nba-ai-assistant-js &amp;&amp; cd nba-ai-assistant-js<p><strong>Backend:</strong></p><ol><li><p>Verwenden Sie das Mastra-Erstellungstool mit folgendem Befehl: </p></li></ol>npx create-mastra@latest<p>2. Sie sollten nun einige Eingabeaufforderungen in Ihrem Terminal erhalten. Bei der ersten nennen wir das Projekt-Backend:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt65abf68fe588e968/6a17f1de63baff2814741d5b/de2725031ed6837db99a979efcdd0ece1e197dbb-608x84.png" alt="" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt89bd829fcf0ae6b9/6a17f1e04b055dd30e432302/88919d9ff1852126395e1fcd700ecb1b59aac63c-866x116.png" alt="" /><p>4. Dann wählen wir OpenAI als unseren Standard-LLM-Anbieter.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfd167cc77a40b9a8/6a17f1e11480099e29b48863/2328761e769f3ded134e5a21e8a0bf8f41e88f68-404x210.png" alt="" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt12654151ed495370/6a17f1e22f4a5c0f84fa89f9/0662de9bd28758e377e4c63df8d08b479068ce63-444x120.png" alt="" /><p><strong>Frontend:</strong></p><ol><li><p>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></p></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><p>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>:</p></li></ol>cd backend
npm install dotenv --save<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># OpenAI Configuration
OPENAI_API_KEY=your_openai_api_key_here

# Elasticsearch Configuration
ELASTIC_ENDPOINT=your_elasticsearch_endpoint_here
ELASTIC_API_KEY=your_elasticsearch_api_key_here
<p></p><p>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><p><strong>Option A: Elasticsearch Cloud verwenden</strong></p><ul><li><p>Registrieren Sie sich für <a href="https://cloud.elastic.co/registration">Elastic Cloud</a></p></li><li><p>Erstellen Sie eine neue Bereitstellung</p></li><li><p>Rufen Sie Ihre Endpunkt-URL und Ihren API-Schlüssel (kodiert) ab.</p></li></ul></li><li><p><strong>Option B: Elasticsearch lokal ausführen</strong></p><ul><li><p>Elasticsearch lokal installieren und ausführen</p></li><li><p>Verwenden Sie http://localhost:9200 als Endpunkt.</p></li><li><p>API-Schlüssel generieren</p></li></ul></li></ul><p></p><p><strong>Installation des Elasticsearch-Clients auf dem Backend:</strong></p><ol><li><p>Installieren Sie zunächst den offiziellen Elasticsearch-Client in Ihrem Backend-Verzeichnis:</p></li></ol>npm install @elastic/elasticsearch<p>2. Erstellen Sie anschließend ein Verzeichnis „lib“, um wiederverwendbare Funktionen zu speichern, und wechseln Sie in dieses Verzeichnis:</p>mkdir lib &amp;&amp; cd lib<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>import { config } from 'dotenv';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { Client } from '@elastic/elasticsearch';

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

// Load environment variables from the correct path
config({ path: envPath });<p>5. Initialisieren Sie nun den Elasticsearch-Client mithilfe Ihrer Umgebungsvariablen und überprüfen Sie die Verbindung:</p>//Elastic client Initialization, make sure environment variables are being loaded in correctly
const config= {
    node: `${process.env.ELASTIC_ENDPOINT}`,
    auth: {
        apiKey: `${process.env.ELASTIC_API_KEY}`,
    },
};

export const elasticClient = new Client(config);

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

checkConnection();
<p>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><p><a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/data/sample_nba_data.csv">sample_player_game_stats.csv</a> - 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.)</p></li><li><p><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.</p></li></ul><p></p><p><strong>Durchführung:</strong></p><ol><li><p>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>.</p></li><li><p>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.</p></li></ol>import fs from 'fs';
import readline from 'readline';
import path from 'path';
import { fileURLToPath } from 'url';
import { elasticClient } from './elasticClient.js';

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

//Since we are using ES modules __dirname and __filename don't exist, so this is a workaround that allows us to use the absolute file path for our sample data.
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const filePath = path.resolve(__dirname, '../data/sample_nba_data.csv');<p>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: 'strict'</code> hinzufügen, damit Elasticsearch nicht dynamisch nicht erkannte Felder zuordnet. 
</p>// Function to create an index with mappings
async function createIndex() {
    try {
        // Check if the index already exists
        const exists = await elasticClient.indices.exists({ index: indexName });

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

        console.log('Index created:', response);
        return true;
    } catch (error) {
        console.error('Error creating index:', error);
        return false;
    }
}
<p>4. 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>async function bulkIngestCsv(filePath) {
    const readStream = fs.createReadStream(filePath);
    const rl = readline.createInterface({
        input: readStream,
        crlfDelay: Infinity,
    });

    const bulkBody = [];
    let lineNum = 0;

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

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

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

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

    console.log(`Parsed ${lineNum} lines from CSV`);
<p>5. 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>try {
        // Perform the bulk request
        const response = await elasticClient.bulk({ body: bulkBody });

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

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

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

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

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

    } catch (error) {
        console.error('Error performing bulk ingestion:', error);
    }
}
<p>6. 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>// Run this function
async function main() {
    const result = await createIndex();
    if (!result) {
        console.error('Index setup failed. Aborting.');
        return;
    }

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

main();
<p>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>export async function getHistoricalAveragesAgainstOpponent(player_id, opponent_team_id) {
    try {
        //Query for Historical Averages
        const historicalQuery = await elasticClient.search({
            index: 'sample-nba-player-data', 
            size: 0,
            query: {
                bool: {
                    must: [
                        {
                            term: {
                                player_id: {
                                    value: player_id,
                                },
                            },
                        },
                        {
                            term: {
                                opponent_team_id: {
                                    value: opponent_team_id,
                                },
                            },
                        },
                    ],
                },
            },
            aggs: {
                avg_points: { avg: { field: 'points' } },
                avg_rebounds: { avg: { field: 'rebounds' } },
                avg_assists: { avg: { field: 'assists' } },
                avg_steals: { avg: { field: 'steals' } },
                avg_blocks: { avg: { field: 'blocks' } },
             avg_fg_percentage: { avg: { field: 'fg_percentage' } },
            },
        });

        return {
            points: historicalQuery.aggregations.avg_points.value || 0,
            rebounds: historicalQuery.aggregations.avg_rebounds.value || 0,
            assists: historicalQuery.aggregations.avg_assists.value || 0,
            steals: historicalQuery.aggregations.avg_steals.value || 0,
            blocks: historicalQuery.aggregations.avg_blocks.value || 0,
            fgPercentage: historicalQuery.aggregations.avg_fg_percentage.value || 0,
        };
    } catch (error) {
        console.error('Query error from getHistoricalAveragesAgainstOpponent function:', error);
        return { error: 'Queries failed in getting historical averages against opponent.' };
    }
}
<p>3. 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.
</p>        {
                            range: {
                    //Range for this season, change to match current season
                                game_date: {
                                    gte: '2024-10-01',
                                    lte: '2025-06-30',
                                },
                            },
<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.
</p>import { playersByName } from '../data/playerAndTeamInfo.js';
import { teamsByName } from '../data/playerAndTeamInfo.js';
import { upcomingMatchups } from '../data/playerAndTeamInfo.js';
import { getHistoricalAveragesAgainstOpponent } from './elasticAggs.js';
import { getSeasonAveragesAgainstOpponent } from './elasticAggs.js';

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

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

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

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

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

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

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

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

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

    return [player1, player2];
}
<h4><strong>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><p><strong>Name:</strong> Geben Sie Ihrem Agenten einen Namen, der als Referenz verwendet wird, wenn er im Frontend aufgerufen wird.</p></li><li><p><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.</p></li><li><p><strong>Modell:</strong> Welches LLM soll verwendet werden (Mastra unterstützt OpenAI, Anthropic, lokale Modelle usw.)?</p></li><li><p><strong>Tools:</strong> Eine Liste der Tool-Funktionen, die der Agent aufrufen kann.</p></li><li><p><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.</p></li></ul><p></p>import { openai } from '@ai-sdk/openai';
import { Agent } from '@mastra/core/agent';
import { playerComparisonTool } from '../tools';

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

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

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

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

      Format your response using Markdown syntax. Use:

        Example output format:

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


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

        #### Fantasy Recommendation  
        Explain which player is the better fantasy pickup and why.
      
    `,
    model: openai('gpt-4o'),
    tools: { playerComparisonTool },
});
<p><strong>
Werkzeuge definieren</strong></p><ol><li><p>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> .</p></li><li><p>Installieren Sie Zod mit folgendem Befehl:</p></li></ol>npm install zod<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><p><code>id</code>Dies ist eine Beschreibung in natürlicher Sprache, die dem Agenten hilft zu verstehen, was das Tool leistet.</p></li><li><p><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.</p></li><li><p><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.</p></li><li><p><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.</p></li></ul>import { comparePlayersForNextMatchup } from '../../../lib/comparePlayers.js'
import { createTool } from "@mastra/core/tools";
import { z } from "zod";

export const playerComparisonTool = createTool({
    id: "Compare two NBA players",
    inputSchema: z.object({
        player1:z.string(),
        player2:z.string()
    }),
    description: "Use this tool to compare two players given in the user prompt.",
    execute: async ({ context: { player1, player2 } }) =&gt; {
        return await comparePlayersForNextMatchup(player1, player2);
      },
})<p><strong>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><p><code>origin: ['http://localhost:5173']</code></p><ul><li><p>Erlaubt Anfragen nur von dieser Adresse (Vite-Standardadresse)</p></li></ul></li><li><p><code>allowMethods: ["GET", "POST"]</code></p><ul><li><p>Zulässige HTTP-Methoden. In den meisten Fällen wird POST verwendet.</p></li></ul></li><li><p><code>allowHeaders: ["Content-Type", "Authorization", "x-mastra-client-type, "x-highlight-request", "traceparent"],</code></p><ul><li><p>Diese legen fest, welche benutzerdefinierten Header in Anfragen verwendet werden können.</p></li></ul></li></ul><p></p>import { Mastra } from '@mastra/core/mastra';
import { basketballAgent } from './agents';

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

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

});

console.log('Mastra server configured.'); // Log after server configuration
<h4><strong>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>npm install @ai-sdk/react<p>2. Installieren Sie im selben Verzeichnis React Markdown, damit wir die vom Agenten generierte Antwort richtig formatieren können.</p>npm install react-markdown<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><p><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.</p></li><li><p><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.</p></li><li><p><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.</p></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>import React, { useState } from 'react';
import { useChat } from '@ai-sdk/react';
import ReactMarkdown from 'react-markdown';

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

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

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

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

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

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

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

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

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

            &lt;form onSubmit={handleSubmit}&gt;
                &lt;input
                    type="text"
                    value={input}
                    onChange={handleInputChange}
                    placeholder="Input two players you want to compare."
                    className="input-box"
                /&gt;
                &lt;button type="submit" disabled={status === 'streaming'}&gt;
                    {status === 'streaming' ? 'Thinking...' : 'Send'}
                &lt;/button&gt;
            &lt;/form&gt;
        &lt;/div&gt;
    );
}<h4><strong>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><p>Öffnen Sie ein Terminalfenster, beginnend im Stammverzeichnis, und navigieren Sie zum Backend-Verzeichnis. Starten Sie dort den Mastra-Server:</p></li></ol>cd backend

npm run dev<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>cd frontend

npm run dev<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><p>„Vergleiche LeBron James und Stephen Curry“</p></li><li><p>"Wen soll ich wählen, Jayson Tatum oder Luka Doncic?"</p></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><p><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.</p></li><li><p><strong>Elasticsearch </strong>und wie seine Datenspeicherfunktionen und leistungsstarken nativen Aggregationen es zu einem großartigen Partner als Wissensbasis für ein LLM machen.</p></li><li><p><strong>Das Mastra- </strong>Framework und wie es die Entwicklung dieser Agenten für Entwickler im JavaScript-Ökosystem vereinfacht.</p></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>
    <link>https://www.elastic.co/search-labs/blog/agentic-rag</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/agentic-rag</guid>
    <category><![CDATA[KI]]></category>
    <category><![CDATA[Agentische KI]]></category>
    <category><![CDATA[Javascript]]></category>
    <dc:creator><![CDATA[JD Armada]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8ffd561836a4cb20/6a17f1e47b54f978588b39e4/8132ed781c1ea5d46ca244182f421ed5c721f23b-1200x628.png" length="0" type="image/png"/>
    <pubDate>Tue, 01 Jul 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[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>
    <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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5ff0e141b5dfda29/6a17e7ffe8fbcee5263a1946/5eba1e59514eb58a5220bb92bb49e6328ee83cd7-674x466.png" alt="Model Context Protocol (MCP)-Architektur" /><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><p>So aktivieren Sie Remote-MCP durch Hinzufügen von Streamable HTTP</p></li><li><p>Um umfangreichere Agenteninteraktionsmodelle durch die Ergänzung von Elicitation- und Tool-Output-Schemata zu ermöglichen</p></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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4d53e7bb091b4f43/6a17e80163baff80dc741c56/2ea159116fe5e03ff800f077adf16d6ca9f1c1d1-1594x1280.png" alt="MCP-Authentifizierung für Agenten" /><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><p><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></p></li><li><p><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.</p></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><p>Bedrohung</p><p>Beschreibung</p><p>Abhilfemaßnahmen und bewährte Methoden</p><p>Sofortige Injektion &amp; Werkzeugvergiftung</p><p>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.</p><p>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.</p><p>Sampling-Angriffe</p><p>Im Kontext von MCP öffnet das Sampling dem MCP-Server die Tür für sofortige Injektionsangriffe auf das LLM.</p><p>Deaktivieren Sie die Stichprobenentnahme für nicht vertrauenswürdige Server und ziehen Sie in Erwägung, menschliche Genehmigungen für Stichprobenanfragen hinzuzufügen.</p><p>Bösartige MCP-Server</p><p>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.</p><p>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.</p><p>Schädliche MCP-Installationstools</p><p>Befehlszeileninstallationsprogramme und -skripte sind praktisch, um MCP-Server oder -Tools schnell zu implementieren, aber Sie könnten am Ende ungeprüften, kompromittierten Code installieren.</p><p>Installieren Sie es in Sandbox-Umgebungen und validieren Sie Paketsignaturen. Führen Sie niemals automatische Updates aus nicht verifizierten Quellen durch.</p><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><p><a href="https://modelcontextprotocol.io/specification/2025-06-18/basic/security_best_practices">Offizielle Best Practices für MCP-Sicherheit</a></p></li><li><p><a href="https://owasp.org/www-project-top-10-for-large-language-model-applications/">OWASP LLM-Bewerbung Top 10</a></p></li><li><p><a href="https://hiddenlayer.com/innovation-hub/">HiddenLayer-Bedrohungsforschung</a></p></li><li><p><a href="https://github.com/invariantlabs-ai/mcp-scan">MCP-Scan</a></p></li><li><p><a href="https://genai.owasp.org/llmrisk/llm01-prompt-injection/">OWASP Prompt Injection-Leitfaden</a></p></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><p>Bereitstellung einer einzigen zuverlässigen Quelle für Servermetadaten (was ein Server tut, wie er authentifiziert, installiert und aufgerufen wird)</p></li><li><p>Beseitigung unvollständiger Registrierungen von Drittanbietern und Fragmentierung, sodass ein Server bei der Registrierung nicht jede einzelne andere Registrierung im Internet aktualisieren muss.</p></li><li><p>Bereitstellung eines Serverregistrierungsflusses, der ein CLI-Tool und eine server.json-Datei umfasst, die die zuvor erwähnten Metadaten enthält.</p></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><p><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.
</p></li><li><p><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.
</p></li><li><p><strong>Postman – </strong><a href="https://www.postman.com/explore/mcp-generator"><strong>MCP-Server-Generator</strong></a><strong>: </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.
</p></li><li><p><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.
</p></li><li><p><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.
</p></li><li><p><strong>AWS – </strong><a href="https://github.com/awslabs/mcp"><strong>AWS MCP-Server</strong></a><strong>: </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.</p></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><p><code>platform.core.search</code> - Führt Suchvorgänge unter Verwendung der vollständigen Elasticsearch Query DSL aus.</p></li><li><p><code>platform.core.list_indices</code> - Listet alle verfügbaren Indizes in Elasticsearch auf (hilft Agenten, die vorhandenen Daten zu ermitteln)</p></li><li><p><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)</p></li><li><p><code>platform.core.get_document_by_id</code> - Ruft ein bestimmtes Dokument anhand seiner ID ab (für präzisen Abruf)</p></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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltca9e3d0a4e7031c0/6a17e803faa913d8c393c897/c1f6405a374b707e8e6fa36b9e21db5f3c7cd127-1376x864.png" alt="Agent Builder-Benutzeroberfläche" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf7d8b29b06c08f94/6a17e805033c8d07f06bb1b6/9f39588525ca2643475de557ea54a6bcf5c150f6-1282x616.png" alt="MCP-Tools" /><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>
    <link>https://www.elastic.co/search-labs/blog/mcp-current-state</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/mcp-current-state</guid>
    <category><![CDATA[KI]]></category>
    <dc:creator><![CDATA[JD Armada]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2f63f23bbecd2a18/6a17e8066317302039585aa7/02b8c8672ffa129e0ed91a92d6cab612a01d27f2-1200x628.png" length="0" type="image/png"/>
    <pubDate>Thu, 12 Jun 2025 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>