{"id":69756,"date":"2026-09-24T12:27:35","date_gmt":"2026-09-24T10:27:35","guid":{"rendered":"https:\/\/www.inovex.de\/?p=69756"},"modified":"2026-09-24T14:27:46","modified_gmt":"2026-09-24T12:27:46","slug":"spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python","status":"publish","type":"post","link":"https:\/\/www.inovex.de\/de\/blog\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\/","title":{"rendered":"Spring AI 2.0. KI im Java-Stack \u2013 ohne den Umweg \u00fcber Python"},"content":{"rendered":"<p>KI ist in praktisch jedem Produktteam angekommen, und mit ihr eine Frage, die Java Developer lange besch\u00e4ftigt hat: Muss man f\u00fcr ernsthafte KI-Features eigentlich nach Python wechseln? Die kurze Antwort lautet seit einer Weile: Nein. Die l\u00e4ngere liefert Spring AI.<\/p>\n<p>Mit dem GA-Release von Spring AI 2.0 im Juni 2026 hat das Projekt eine stabile Basis erreicht \u2013 aufgesetzt auf Spring Boot 4 und Spring Framework 7. Dieser Artikel zeigt, was Spring AI ausmacht, wie ein minimales Setup aussieht und worauf man im Alltag achten sollte. Gedacht ist er f\u00fcr Entwickler, die sich einen fundierten ersten Eindruck verschaffen wollen, bevor sie das Framework in ein bestehendes Projekt holen.<!--more--><\/p>\n<table>\n<tbody>\n<tr>\n<td><b>Spring AI<\/b><\/td>\n<td>2.0.0 (GA, Juni 2026), verf\u00fcgbar \u00fcber Maven Central<\/td>\n<\/tr>\n<tr>\n<td><b>Plattform<\/b><\/td>\n<td>Spring Boot 4.0<\/td>\n<\/tr>\n<tr>\n<td><b>Java<\/b><\/td>\n<td>17 als Minimum, 21+ empfohlen<\/td>\n<\/tr>\n<tr>\n<td><b>Bibliotheken<\/b><\/td>\n<td>Jackson 3 statt Jackson 2; durchg\u00e4ngig null-annotiert (JSpecify)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2><b>Was ist Spring AI \u2013 und welches Problem l\u00f6st es?<\/b><\/h2>\n<p>Spring AI ist ein Framework aus der Spring-Community, das KI-Funktionen in Spring-Anwendungen einbindet, ohne den gewohnten Technologie-Stack zu verlassen. Es stellt Abstraktionen bereit, um mit Modellen verschiedener Anbieter zu arbeiten \u2013 etwa denen von OpenAI, Anthropic (Claude) oder Google (Gemini).<\/p>\n<p>Konzeptionell borgt sich das Projekt einiges bei Python-Bibliotheken wie LangChain und LlamaIndex. Der Unterschied: Spring AI denkt von Anfang an in Spring-Begriffen. Dependency Injection, Auto-Configuration, die \u00fcblichen Design-Patterns \u2013 all das bleibt erhalten. F\u00fcr ein eingespieltes Spring-Team senkt das die Einstiegsh\u00fcrde erheblich, weil kein zweites mentales Modell n\u00f6tig ist.<\/p>\n<h2><b>Spring AI oder doch Python?<\/b><\/h2>\n<p>Dass Python im KI- und ML-Umfeld die Nummer eins ist, bestreitet niemand. Warum also Spring AI? Der Grund liegt selten in der reinen KI-Leistung, sondern in der Integration.<\/p>\n<p>Man stelle sich eine \u00fcber Jahre gewachsene Anwendung vor \u2013 die Spring Petclinic ist das klassische Beispiel, aufgebaut auf Spring Boot, Thymeleaf und JPA. Solche Systeme wurden nie f\u00fcr KI entworfen. Die Alternative zu Spring AI w\u00e4re, eine zweite Infrastruktur in Python danebenzustellen: eigener Dienst, eigene Authentifizierung, zus\u00e4tzliche Netzwerk-Hops, eine weitere CI\/CD-Strecke. Das alles kostet, bevor das erste Feature \u00fcberhaupt l\u00e4uft.<\/p>\n<p>Spring AI geht den anderen Weg. So wie Spring Data eine Abstraktion \u00fcber verschiedene Datenbanken legt \u2013 man schreibt gegen ein Interface, der Starter erledigt den Rest \u2013, legt Spring AI eine Abstraktion \u00fcber Large Language Models. Wenn Gesch\u00e4ftslogik, Sicherheit und Daten ohnehin in einer Java-Anwendung liegen, ist das meist der pragmatischere Schnitt.<\/p>\n<h2><b>Kernkonzepte<\/b><\/h2>\n<p>Ein paar Begriffe sollte man kennen, bevor es losgeht.<\/p>\n<ul>\n<li><b>Models<\/b> \u2013 die eigentlichen KI-Algorithmen. Spring AI unterst\u00fctzt die gro\u00dfen Anbieter: OpenAI, Anthropic Claude, Google Gemini, Amazon Bedrock, Ollama f\u00fcr lokale Modelle und weitere. In 2.0 nutzt das Framework f\u00fcr OpenAI, Anthropic und Google direkt die offiziellen Hersteller-SDKs, was neue Modell-Features schneller verf\u00fcgbar macht.<\/li>\n<li><b>Tokens &amp; Embeddings<\/b> \u2013 Modelle rechnen nicht mit W\u00f6rtern, sondern mit Tokens, also Textfragmenten. Embeddings \u00fcbersetzen Text in numerische Vektoren, sodass sich semantische N\u00e4he mathematisch ausdr\u00fccken l\u00e4sst. Das ist die Grundlage f\u00fcr Vektorsuche und RAG.<\/li>\n<li><b>Prompts<\/b> \u2013 die Anweisungen an das Modell. F\u00fcr dynamische Prompts setzt Spring AI auf Templates (per StringTemplate), in die zur Laufzeit Variablen eingesetzt werden.<\/li>\n<li><b>ChatClient<\/b> \u2013 die zentrale, fluent gestaltete API f\u00fcr die Kommunikation mit dem Modell. In 2.0 ist der ChatClient ausdr\u00fccklich die empfohlene Einstiegsebene; das tiefer liegende ChatModel braucht man nur f\u00fcr Spezialf\u00e4lle.<\/li>\n<li><b>Advisors<\/b> \u2013 eine Art AOP f\u00fcr LLM-Aufrufe. Sie klinken sich in Anfrage und Antwort ein: Der SimpleLoggerAdvisor protokolliert Requests, der MessageChatMemoryAdvisor h\u00e4ngt automatisch den Gespr\u00e4chsverlauf an.<\/li>\n<li><b>Structured Output<\/b> \u2013 LLMs antworten von Haus aus in Flie\u00dftext. Spring AI kann das Modell dazu bringen, stattdessen maschinenlesbares JSON zu liefern, das direkt auf ein Java-Record oder POJO gemappt wird.<\/li>\n<li><b>Tool Calling<\/b> \u2013 das Modell darf definierte Java-Methoden selbst aufrufen, wenn es daf\u00fcr Daten braucht: eine Datenbankabfrage, einen Wetterdienst, eine interne API. In 2.0 l\u00e4uft diese Ausf\u00fchrungsschleife \u00fcber den ChatClient bzw. den ToolCallingAdvisor.<\/li>\n<\/ul>\n<h2><b>Das minimale Setup<\/b><\/h2>\n<p>Dank Auto-Configuration ist der Einstieg kurz.<\/p>\n<h3><b>1. Abh\u00e4ngigkeiten<\/b><\/h3>\n<p>Seit 2.0 liegen die stabilen Artefakte in Maven Central \u2013 ein zus\u00e4tzliches Snapshot- oder Milestone-Repository braucht man nicht mehr. Versionen verwaltet man am besten \u00fcber die BOM (hier Gradle Kotlin DSL):<\/p>\n<pre class=\"\">dependencies {\r\n \u00a0\u00a0\u00a0implementation(platform(\"org.springframework.ai:spring-ai-bom:2.0.0\"))\r\n \u00a0\u00a0\u00a0implementation(\"org.springframework.ai:spring-ai-starter-model-openai\")\r\n}<\/pre>\n<p>F\u00fcr Anthropic oder Google tauscht man nur den Starter (spring-ai-starter-model-anthropic, spring-ai-starter-model-google-genai) \u2013 der restliche Code bleibt gleich. Genau das ist der Punkt.<\/p>\n<h3><b>2. Konfiguration<\/b><\/h3>\n<p>Den API-Key zieht man aus einer Umgebungsvariable.<\/p>\n<pre class=\"\">spring:\r\n \u00a0ai:\r\n \u00a0\u00a0\u00a0openai:\r\n \u00a0\u00a0\u00a0\u00a0\u00a0api-key: ${OPENAI_API_KEY}\r\n \u00a0\u00a0\u00a0\u00a0\u00a0chat:\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0options:\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0model: gpt-5-mini\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0temperature: 0.0 \u00a0 # 0.0 = deterministisch, h\u00f6here Werte = kreativer<\/pre>\n<h3><b>3. Ein erster Controller<\/b><\/h3>\n<p>Mit dem ChatClient.Builder l\u00e4sst sich ein einsatzbereiter Client injizieren:<\/p>\n<pre class=\"\">@RestController\r\n@RequestMapping(\"\/api\/ai\")\r\npublic class TranslationController {\r\n \u00a0\u00a0\u00a0private final ChatClient chatClient;\r\n \u00a0\u00a0\u00a0public TranslationController(ChatClient.Builder builder) {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.chatClient = builder.build();\r\n \u00a0\u00a0\u00a0}\r\n\r\n \u00a0\u00a0\u00a0@GetMapping(\"\/translate\")\r\n \u00a0\u00a0\u00a0public Map&lt;String, String&gt; translate(@RequestParam String text) {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0String response = chatClient.prompt()\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.user(\"\u00dcbersetze den folgenden Text ins Spanische: \" + text)\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.call()\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.content();\r\n\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return Map.of(\"translation\", response);\r\n \u00a0\u00a0\u00a0}\r\n}<\/pre>\n<p>Der Aufruf erzeugt im Hintergrund den Request an den Anbieter und gibt den reinen Text zur\u00fcck. Mehr braucht es f\u00fcr den ersten Treffer nicht.<\/p>\n<h3><b>Antworten direkt als Objekt<\/b><\/h3>\n<p>Oft will man keinen Flie\u00dftext, sondern Struktur. Der ChatClient kann die Antwort \u00fcber .entity(&#8230;) direkt auf ein Record mappen:<\/p>\n<pre class=\"\">record Translation(String original, String translated, String targetLanguage) {}\r\n\r\nTranslation result = chatClient.prompt()\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.user(\"\u00dcbersetze 'Guten Morgen' ins Spanische.\")\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.call()\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.entity(Translation.class);<\/pre>\n<p>Spring AI weist das Modell an, passendes JSON zu liefern, und deserialisiert es f\u00fcr uns.<\/p>\n<h3><b>Streaming<\/b><\/h3>\n<p>Bei l\u00e4ngeren Antworten will man nicht warten, bis der ganze Text steht. .stream() liefert die Antwort als Flux, St\u00fcck f\u00fcr St\u00fcck:<\/p>\n<pre class=\"\">@GetMapping(value = \"\/chat\/stream\", produces = MediaType.TEXT_EVENT_STREAM_VALUE)\r\npublic Flux&lt;String&gt; stream(@RequestParam String message) {\r\n \u00a0\u00a0\u00a0return chatClient.prompt()\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.user(message)\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.stream()\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.content();\r\n}<\/pre>\n<h2><b>St\u00e4rken und Schw\u00e4chen \u2013 eine ehrliche Einordnung<\/b><\/h2>\n<p>Kein Stack ist nur Sonnenseite. Drei Dinge spielt Spring AI klar aus.<\/p>\n<p>Das erste ist die Austauschbarkeit der Modelle. Ein Wechsel von OpenAI zu einem lokal \u00fcber Ollama betriebenen Modell ist oft eine Sache von Dependency und ein paar Properties; der Anwendungscode bleibt unber\u00fchrt. Das macht A\/B-Tests zwischen Anbietern und das Mischen kommerzieller und lokaler Modelle unaufw\u00e4ndig.<\/p>\n<p>Das zweite ist die Integration in Spring Boot. Vektordatenbanken wie Qdrant, PgVector oder Redis h\u00e4ngen \u00fcber die gewohnten Auto-Configurations an der Anwendung \u2013 man spart sich viel Verdrahtung.<\/p>\n<p>Das dritte ist die Sicherheit. \u00dcber das Model Context Protocol (MCP) lassen sich KI-Endpunkte absichern, wahlweise per OAuth2 (so sieht es die Spezifikation vor) oder, wo keine OAuth2-Infrastruktur existiert, per API-Schl\u00fcssel.<\/p>\n<p>Dem stehen Einschr\u00e4nkungen gegen\u00fcber. Das Tempo des Projekts war lange hoch. Mit 2.0 GA ist die API-Fl\u00e4che stabilisiert und durchg\u00e4ngig null-annotiert, aber wer aus der 1.x-Welt migriert, sollte die Upgrade Notes wirklich lesen \u2013 zwischen den Versionen wurde einiges umbenannt und entfernt. Das Feintuning bleibt anspruchsvoll: Eine gute Gesamtkonfiguration aus Modell, Vector Store und Prompt-Strategie erreicht man nicht per Default, sondern durch Messen und Nachjustieren. Und lokale Open-Source-Modelle sind in der Dialogf\u00fchrung kommerziellen Modellen oft noch unterlegen; das kann das Framework nicht wegabstrahieren.<\/p>\n<h2><b>Praxis: Muster, die sich bew\u00e4hren<\/b><\/h2>\n<p>Wer tiefer einsteigt, l\u00e4uft \u00fcber dieselben Stellen wie alle anderen. Ein paar Empfehlungen.<\/p>\n<h3><b>RAG statt Fine-Tuning<\/b><\/h3>\n<p>Soll das Modell internes Firmenwissen kennen, ist Retrieval Augmented Generation (RAG) meist der g\u00fcnstigere Weg als Fine-Tuning. Unstrukturierte Inhalte \u2013 Tickets, Wiki-Artikel \u2013 landen als Embeddings in einem Vector Store. Zur Laufzeit sucht die Pipeline die semantisch passenden Fragmente und gibt sie dem Modell als Kontext mit.<\/p>\n<p>Den Vector Store konfiguriert man als Bean. Seit 1.0 nutzt Spring AI durchg\u00e4ngig Builder statt vielarmiger Konstruktoren:<\/p>\n<pre class=\"\">@Bean\r\nQdrantVectorStore vectorStore(QdrantClient client, EmbeddingModel embeddingModel) {\r\n \u00a0\u00a0\u00a0return QdrantVectorStore.builder(client, embeddingModel)\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.collectionName(\"incidents\")\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.initializeSchema(true)\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.build();\r\n}<\/pre>\n<p>Die Suche muss man nicht von Hand bauen. Der QuestionAnswerAdvisor \u00fcbernimmt Retrieval und das Anreichern des Prompts:<\/p>\n<pre class=\"\">ChatClient chatClient = builder\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.defaultAdvisors(QuestionAnswerAdvisor.builder(vectorStore).build())\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.build();<\/pre>\n<p>Wer mehr Kontrolle braucht, greift direkt auf den Store zu \u2013 auch hier per Builder:<\/p>\n<pre class=\"\">SearchRequest request = SearchRequest.builder()\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.query(text)\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.topK(5)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.build();\r\n\r\nList&lt;Document&gt; hits = vectorStore.similaritySearch(request);<\/pre>\n<h3><b>System-Prompts ernst nehmen<\/b><\/h3>\n<p>Modelle beantworten bereitwillig auch Fragen, die nichts mit der eigentlichen Aufgabe zu tun haben. \u00dcber einen System-Prompt gibt man klare Leitplanken vor:<\/p>\n<pre class=\"\">@Bean\r\nChatClient chatClient(ChatClient.Builder builder) {\r\n \u00a0\u00a0\u00a0return builder\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.defaultSystem(\"\"\"\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Du bist ein Assistent f\u00fcr die Verwaltung einer Tierklinik.\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Beantworte ausschlie\u00dflich Fragen, die die Tierklinik betreffen.\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"\"\")\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.build();\r\n}<\/pre>\n<h3><b>Prompts in eigene Dateien auslagern<\/b><\/h3>\n<p>Solche Anweisungen werden schnell lang. Im Java-Code verkettete Strings sind schwer zu pflegen \u2013 und Product Owner oder Prompt Engineers kommen gar nicht erst heran. Besser: den Prompt in eine Datei legen. Markdown eignet sich gut, weil es Struktur erlaubt, die Modelle zuverl\u00e4ssig verarbeiten. Spring AI l\u00e4dt .st-Templates von Haus aus; eine schlichte Markdown-Datei tut es genauso.<\/p>\n<p>Datei unter src\/main\/resources\/prompts\/chat-system.md:<\/p>\n<pre class=\"\"># Rolle\r\nDu bist ein Assistent f\u00fcr eine Tierklinik.\r\n\r\n# Regeln\r\n- Beantworte nur Fragen zur Tierklinik.\r\n- Gib niemals sensible Kundendaten preis.\r\n- Wenn du etwas nicht wei\u00dft, sage das offen.<\/pre>\n<p>Laden per @Value als Classpath-Ressource:<\/p>\n<pre class=\"\">@Configuration\r\npublic class ChatClientConfig {\r\n \u00a0\u00a0\u00a0@Value(\"classpath:prompts\/chat-system.md\")\r\n \u00a0\u00a0\u00a0private Resource systemPrompt;\r\n\r\n \u00a0\u00a0\u00a0@Bean\r\n \u00a0\u00a0\u00a0ChatClient chatClient(ChatClient.Builder builder) {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return builder.defaultSystem(systemPrompt).build();\r\n \u00a0\u00a0\u00a0}\r\n}<\/pre>\n<p>Vorteil: Die Java-Klasse bleibt sauber, und der Prompt l\u00e4sst sich unabh\u00e4ngig vom Code anpassen.<\/p>\n<h3><b>Tools statt blinder Antworten<\/b><\/h3>\n<p>Mit Tool Calling gibt man dem Modell Zugriff auf eigene Systeme \u2013 es entscheidet selbst, ob und wann es eine Methode aufruft. Der schlanke Weg f\u00fchrt \u00fcber die @Tool-Annotation an ganz normalen Service-Methoden. Entscheidend ist die Beschreibung: An ihr erkennt das Modell, wann das Tool passt.<\/p>\n<p>H\u00e4ufiger Stolperstein: Die Annotation liegt in org.springframework.ai.tool.annotation.Tool \u2013 nicht im chat.model-Paket.<\/p>\n<pre class=\"\">import org.springframework.ai.tool.annotation.Tool;\r\n\r\nimport org.springframework.ai.tool.annotation.ToolParam;\r\n\r\nimport org.springframework.stereotype.Service;\r\n\r\n\u00a0\r\n\r\n@Service\r\n\r\npublic class WeatherService {\r\n \u00a0\u00a0\u00a0@Tool(description = \"Liefert aktuelle Temperatur und Wetter f\u00fcr eine Stadt.\")\r\n \u00a0\u00a0\u00a0public String getCurrentWeather(\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0@ToolParam(description = \"Name der Stadt\") String city) {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return new ExternalWeatherApiClient().fetchWeatherForCity(city);\r\n \u00a0\u00a0\u00a0}\r\n}<\/pre>\n<p>Registriert wird das Tool, indem man die Bean \u00fcbergibt \u2013 nicht den Methodennamen als String. Pro Request \u00fcber .tools(&#8230;), f\u00fcr alle Requests am Builder \u00fcber .defaultTools(&#8230;):<\/p>\n<pre class=\"\">@RestController\r\n\r\npublic class WeatherChatController {\r\n\u00a0\u00a0\u00a0\u00a0private final ChatClient chatClient;\r\n\r\n \u00a0\u00a0\u00a0public WeatherChatController(ChatClient.Builder builder, WeatherService weatherService) {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.chatClient = builder\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.defaultTools(weatherService)\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.build();\r\n\u00a0\u00a0\u00a0\u00a0}\r\n\r\n \u00a0\u00a0\u00a0@GetMapping(\"\/chat\")\r\n \u00a0\u00a0\u00a0public String chat(@RequestParam String message) {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return chatClient.prompt()\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.user(message)\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.call()\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.content();\r\n \u00a0\u00a0\u00a0}\r\n}<\/pre>\n<p>Spring AI erzeugt aus der Methodensignatur automatisch das JSON-Schema, das das Modell erwartet, ruft die Methode bei Bedarf auf und reicht das Ergebnis zur\u00fcck. In 2.0 f\u00fchren die ChatModels diese Schleife nicht mehr selbst aus \u2013 der ChatClient registriert daf\u00fcr automatisch einen ToolCallingAdvisor. Wer direkt gegen das ChatModel arbeitet, muss die Tool-Ausf\u00fchrung selbst steuern.<\/p>\n<p>Die \u00e4ltere Variante \u00fcber java.util.function.Function-Beans mit @Description existiert weiterhin; f\u00fcr neue Projekte ist @Tool aber der direktere Weg.<\/p>\n<h3><b>Gespr\u00e4chsverlauf verwalten<\/b><\/h3>\n<p>LLMs sind zustandslos \u2013 ohne Verlauf versteht das Modell keine Folgefragen. Der MessageChatMemoryAdvisor h\u00e4ngt die letzten Nachrichten automatisch an. Spring Boot konfiguriert daf\u00fcr bereits eine ChatMemory-Bean (eine MessageWindowChatMemory mit In-Memory-Repository); man muss sie nur einklinken:<\/p>\n<pre class=\"\">@Bean\r\nChatClient chatClient(ChatClient.Builder builder, ChatMemory chatMemory) {\r\n \u00a0\u00a0\u00a0return builder\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.defaultAdvisors(\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0MessageChatMemoryAdvisor.builder(chatMemory).build(),\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0new SimpleLoggerAdvisor())\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.build();\r\n}<\/pre>\n<p>Wer das Fenster selbst dimensionieren will, baut die Memory explizit:<\/p>\n<pre class=\"\">ChatMemory chatMemory = MessageWindowChatMemory.builder()\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.maxMessages(20)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.build();<\/pre>\n<p>Ein Detail, das in aktuellen Versionen Pflicht ist: Jeder Aufruf \u00fcber den Memory-Advisor braucht eine Conversation-ID, damit Gespr\u00e4che getrennt bleiben:<\/p>\n<pre class=\"\">chatClient.prompt()\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.user(message)\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.advisors(a -&gt; a.param(ChatMemory.CONVERSATION_ID, conversationId))\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.call()\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.content();<\/pre>\n<p>Die fr\u00fcher verbreitete Schreibweise mit new InMemoryChatMemory() ist entfallen \u2013 Konstruktor und Klasse wurden zugunsten der Builder-API abgel\u00f6st.<\/p>\n<h3><b>Endpunkte absichern<\/b><\/h3>\n<p>In Enterprise-Umgebungen ist die Absicherung der KI-Integration kein Nice-to-have. Bindet man Systeme \u00fcber MCP an, sollen MCP-Server laut Spezifikation gesch\u00fctzt werden \u2013 prim\u00e4r \u00fcber OAuth2. Wo das nicht praktikabel ist, bietet das Community-Modul mcp-security eine API-Key-Variante. Sie stammt nicht aus Spring Security Core, sondern aus org.springaicommunity:mcp-server-security (f\u00fcr Spring AI 2.x die Versionslinie 0.1.x).<\/p>\n<pre class=\"\">@Bean\r\nSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\r\n \u00a0\u00a0\u00a0\/\/ McpApiKeyConfigurer stammt aus dem Community-Modul mcp-server-security\r\n \u00a0\u00a0\u00a0return http\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.authorizeHttpRequests(auth -&gt; auth.anyRequest().authenticated())\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.with(McpApiKeyConfigurer.mcpServerApiKey(), apiKey -&gt;\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0apiKey.apiKeyRepository(apiKeyRepository()))\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.build();\r\n}<\/pre>\n<p>Das apiKeyRepository ist Pflicht \u2013 ohne Schl\u00fcsselquelle startet die Konfiguration nicht. Aufgerufen wird der Server dann mit einem Header X-API-key: id.secret; der Secret-Teil liegt serverseitig bcrypt-gehasht.<\/p>\n<h2><b>Was 2.0 sonst noch mitbringt<\/b><\/h2>\n<p>Ein paar Punkte, die \u00fcber das Tagesgesch\u00e4ft hinaus interessant sind.<\/p>\n<p>MCP ist in 2.0 vollst\u00e4ndig in den Kern gewandert. Eine Anwendung kann gleichzeitig MCP-Client sein \u2013 und externe Tools wie Dateisystem- oder Datenbankzugriffe konsumieren \u2013 und MCP-Server, der eigene Gesch\u00e4ftslogik als Tools anbietet. Als Transport ist Streamable HTTP der neue Standard; SSE gilt als veraltet, stdio bleibt f\u00fcr lokale Prozesse.<\/p>\n<p>Observability ist eingebaut. Spring AI erzeugt Micrometer-Spans und OpenTelemetry-kompatible Metriken f\u00fcr Modell- und Tool-Aufrufe, inklusive Token-Verbrauch \u2013 was bei der Kostenkontrolle hilft.<\/p>\n<p>Die gesamte API ist \u00fcber JSpecify null-annotiert. F\u00fcr Kotlin-Nutzer \u00fcbersetzt sich das in echte nullable- und non-nullable-Typen, die der Compiler pr\u00fcft.<\/p>\n<p>Produktiv lohnt es sich au\u00dferdem, Retry- und Rate-Limit-Verhalten sowie sinnvolles Error-Handling von Anfang an einzuplanen: LLM-APIs antworten nicht immer und nicht immer schnell. Und beim Aufsetzen eines neuen Projekts nimmt einem start.spring.io die Auswahl der Modell- und Vector-Store-Starter ab.<\/p>\n<h2><b>Fazit<\/b><\/h2>\n<p>Spring AI schlie\u00dft die L\u00fccke zwischen der etablierten Java-Welt und der schnellen Entwicklung bei generativer KI, ohne dass man das \u00d6kosystem verlassen muss. \u00dcber Abstraktionen wie den ChatClient, unkompliziertes Tool Calling und die Integration von Vector Stores l\u00e4sst sich Wert schaffen, statt Infrastruktur nachzubauen.<\/p>\n<p>Mit 2.0 steht das Ganze auf einer stabilen, konsistenten Basis. Wer in der Spring-Boot-Welt zu Hause ist und LLMs einbinden will, hat damit einen geradlinigen Weg. Der pragmatische Einstieg: klein anfangen mit dem ChatClient, strukturierten Output ausprobieren und die Architektur sp\u00e4ter in Richtung RAG ausbauen.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>KI ist in praktisch jedem Produktteam angekommen, und mit ihr eine Frage, die Java Developer lange besch\u00e4ftigt hat: Muss man f\u00fcr ernsthafte KI-Features eigentlich nach Python wechseln? Die kurze Antwort lautet seit einer Weile: Nein. Die l\u00e4ngere liefert Spring AI. Mit dem GA-Release von Spring AI 2.0 im Juni 2026 hat das Projekt eine stabile [&hellip;]<\/p>\n","protected":false},"author":463,"featured_media":70113,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"ep_exclude_from_search":false,"footnotes":""},"tags":[509,723],"service":[76,425],"level":[1280],"coauthors":[{"id":463,"display_name":"Andrej Litowka","user_nicename":"alitowka"}],"class_list":["post-69756","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","tag-ai-2","tag-java","service-artificial-intelligence","service-backend"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Spring AI 2.0. KI im Java-Stack \u2013 ohne den Umweg \u00fcber Python - inovex GmbH<\/title>\n<meta name=\"description\" content=\"Braucht man f\u00fcr KI wirklich Python? Spring AI 2.0 bringt LLM-Features direkt in deinen Java-Stack. Erfahre alles zu Setup, ChatClient, RAG und Best Practices.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.inovex.de\/de\/blog\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Spring AI 2.0. KI im Java-Stack \u2013 ohne den Umweg \u00fcber Python - inovex GmbH\" \/>\n<meta property=\"og:description\" content=\"Braucht man f\u00fcr KI wirklich Python? Spring AI 2.0 bringt LLM-Features direkt in deinen Java-Stack. Erfahre alles zu Setup, ChatClient, RAG und Best Practices.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.inovex.de\/de\/blog\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\/\" \/>\n<meta property=\"og:site_name\" content=\"inovex GmbH\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/inovexde\" \/>\n<meta property=\"article:published_time\" content=\"2026-09-24T10:27:35+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-09-24T12:27:46+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.inovex.de\/wp-content\/uploads\/KI_Java_Stack.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1500\" \/>\n\t<meta property=\"og:image:height\" content=\"880\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Andrej Litowka\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/www.inovex.de\/wp-content\/uploads\/KI_Java_Stack-1024x601.png\" \/>\n<meta name=\"twitter:creator\" content=\"@inovexgmbh\" \/>\n<meta name=\"twitter:site\" content=\"@inovexgmbh\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Andrej Litowka\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"11\u00a0Minuten\" \/>\n\t<meta name=\"twitter:label3\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data3\" content=\"Andrej Litowka\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\\\/\"},\"author\":{\"name\":\"Andrej Litowka\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#\\\/schema\\\/person\\\/c024a6170e416bf9ddd152490d7e76d1\"},\"headline\":\"Spring AI 2.0. KI im Java-Stack \u2013 ohne den Umweg \u00fcber Python\",\"datePublished\":\"2026-09-24T10:27:35+00:00\",\"dateModified\":\"2026-09-24T12:27:46+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\\\/\"},\"wordCount\":1854,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/KI_Java_Stack.png\",\"keywords\":[\"Ai\",\"Java\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\\\/\",\"url\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\\\/\",\"name\":\"Spring AI 2.0. KI im Java-Stack \u2013 ohne den Umweg \u00fcber Python - inovex GmbH\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/KI_Java_Stack.png\",\"datePublished\":\"2026-09-24T10:27:35+00:00\",\"dateModified\":\"2026-09-24T12:27:46+00:00\",\"description\":\"Braucht man f\u00fcr KI wirklich Python? Spring AI 2.0 bringt LLM-Features direkt in deinen Java-Stack. Erfahre alles zu Setup, ChatClient, RAG und Best Practices.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/KI_Java_Stack.png\",\"contentUrl\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/KI_Java_Stack.png\",\"width\":1500,\"height\":880},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Spring AI 2.0. KI im Java-Stack \u2013 ohne den Umweg \u00fcber Python\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#website\",\"url\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/\",\"name\":\"inovex GmbH\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"de\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#organization\",\"name\":\"inovex GmbH\",\"url\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/2021\\\/03\\\/inovex-logo-16-9-1.png\",\"contentUrl\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/2021\\\/03\\\/inovex-logo-16-9-1.png\",\"width\":1921,\"height\":1081,\"caption\":\"inovex GmbH\"},\"image\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/inovexde\",\"https:\\\/\\\/x.com\\\/inovexgmbh\",\"https:\\\/\\\/www.instagram.com\\\/inovexlife\\\/\",\"https:\\\/\\\/www.linkedin.com\\\/company\\\/inovex\",\"https:\\\/\\\/www.youtube.com\\\/channel\\\/UC7r66GT14hROB_RQsQBAQUQ\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#\\\/schema\\\/person\\\/c024a6170e416bf9ddd152490d7e76d1\",\"name\":\"Andrej Litowka\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/2b1df2baa1bfb9637037d0e2be466c725e5853c8c4ec9dd80b6ca4498ee3d447?s=96&d=retro&r=gcffcdda02b9c65d5c11cf63dfc7caea7\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/2b1df2baa1bfb9637037d0e2be466c725e5853c8c4ec9dd80b6ca4498ee3d447?s=96&d=retro&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/2b1df2baa1bfb9637037d0e2be466c725e5853c8c4ec9dd80b6ca4498ee3d447?s=96&d=retro&r=g\",\"caption\":\"Andrej Litowka\"},\"url\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/author\\\/alitowka\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Spring AI 2.0. KI im Java-Stack \u2013 ohne den Umweg \u00fcber Python - inovex GmbH","description":"Braucht man f\u00fcr KI wirklich Python? Spring AI 2.0 bringt LLM-Features direkt in deinen Java-Stack. Erfahre alles zu Setup, ChatClient, RAG und Best Practices.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.inovex.de\/de\/blog\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\/","og_locale":"de_DE","og_type":"article","og_title":"Spring AI 2.0. KI im Java-Stack \u2013 ohne den Umweg \u00fcber Python - inovex GmbH","og_description":"Braucht man f\u00fcr KI wirklich Python? Spring AI 2.0 bringt LLM-Features direkt in deinen Java-Stack. Erfahre alles zu Setup, ChatClient, RAG und Best Practices.","og_url":"https:\/\/www.inovex.de\/de\/blog\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\/","og_site_name":"inovex GmbH","article_publisher":"https:\/\/www.facebook.com\/inovexde","article_published_time":"2026-09-24T10:27:35+00:00","article_modified_time":"2026-09-24T12:27:46+00:00","og_image":[{"width":1500,"height":880,"url":"https:\/\/www.inovex.de\/wp-content\/uploads\/KI_Java_Stack.png","type":"image\/png"}],"author":"Andrej Litowka","twitter_card":"summary_large_image","twitter_image":"https:\/\/www.inovex.de\/wp-content\/uploads\/KI_Java_Stack-1024x601.png","twitter_creator":"@inovexgmbh","twitter_site":"@inovexgmbh","twitter_misc":{"Verfasst von":"Andrej Litowka","Gesch\u00e4tzte Lesezeit":"11\u00a0Minuten","Written by":"Andrej Litowka"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.inovex.de\/de\/blog\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\/#article","isPartOf":{"@id":"https:\/\/www.inovex.de\/de\/blog\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\/"},"author":{"name":"Andrej Litowka","@id":"https:\/\/www.inovex.de\/de\/#\/schema\/person\/c024a6170e416bf9ddd152490d7e76d1"},"headline":"Spring AI 2.0. KI im Java-Stack \u2013 ohne den Umweg \u00fcber Python","datePublished":"2026-09-24T10:27:35+00:00","dateModified":"2026-09-24T12:27:46+00:00","mainEntityOfPage":{"@id":"https:\/\/www.inovex.de\/de\/blog\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\/"},"wordCount":1854,"commentCount":0,"publisher":{"@id":"https:\/\/www.inovex.de\/de\/#organization"},"image":{"@id":"https:\/\/www.inovex.de\/de\/blog\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\/#primaryimage"},"thumbnailUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/KI_Java_Stack.png","keywords":["Ai","Java"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.inovex.de\/de\/blog\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.inovex.de\/de\/blog\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\/","url":"https:\/\/www.inovex.de\/de\/blog\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\/","name":"Spring AI 2.0. KI im Java-Stack \u2013 ohne den Umweg \u00fcber Python - inovex GmbH","isPartOf":{"@id":"https:\/\/www.inovex.de\/de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.inovex.de\/de\/blog\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\/#primaryimage"},"image":{"@id":"https:\/\/www.inovex.de\/de\/blog\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\/#primaryimage"},"thumbnailUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/KI_Java_Stack.png","datePublished":"2026-09-24T10:27:35+00:00","dateModified":"2026-09-24T12:27:46+00:00","description":"Braucht man f\u00fcr KI wirklich Python? Spring AI 2.0 bringt LLM-Features direkt in deinen Java-Stack. Erfahre alles zu Setup, ChatClient, RAG und Best Practices.","breadcrumb":{"@id":"https:\/\/www.inovex.de\/de\/blog\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.inovex.de\/de\/blog\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/www.inovex.de\/de\/blog\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\/#primaryimage","url":"https:\/\/www.inovex.de\/wp-content\/uploads\/KI_Java_Stack.png","contentUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/KI_Java_Stack.png","width":1500,"height":880},{"@type":"BreadcrumbList","@id":"https:\/\/www.inovex.de\/de\/blog\/spring-ai-2-0-ki-im-java-stack-ohne-den-umweg-ueber-python\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.inovex.de\/de\/"},{"@type":"ListItem","position":2,"name":"Spring AI 2.0. KI im Java-Stack \u2013 ohne den Umweg \u00fcber Python"}]},{"@type":"WebSite","@id":"https:\/\/www.inovex.de\/de\/#website","url":"https:\/\/www.inovex.de\/de\/","name":"inovex GmbH","description":"","publisher":{"@id":"https:\/\/www.inovex.de\/de\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.inovex.de\/de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":"Organization","@id":"https:\/\/www.inovex.de\/de\/#organization","name":"inovex GmbH","url":"https:\/\/www.inovex.de\/de\/","logo":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/www.inovex.de\/de\/#\/schema\/logo\/image\/","url":"https:\/\/www.inovex.de\/wp-content\/uploads\/2021\/03\/inovex-logo-16-9-1.png","contentUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/2021\/03\/inovex-logo-16-9-1.png","width":1921,"height":1081,"caption":"inovex GmbH"},"image":{"@id":"https:\/\/www.inovex.de\/de\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/inovexde","https:\/\/x.com\/inovexgmbh","https:\/\/www.instagram.com\/inovexlife\/","https:\/\/www.linkedin.com\/company\/inovex","https:\/\/www.youtube.com\/channel\/UC7r66GT14hROB_RQsQBAQUQ"]},{"@type":"Person","@id":"https:\/\/www.inovex.de\/de\/#\/schema\/person\/c024a6170e416bf9ddd152490d7e76d1","name":"Andrej Litowka","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/secure.gravatar.com\/avatar\/2b1df2baa1bfb9637037d0e2be466c725e5853c8c4ec9dd80b6ca4498ee3d447?s=96&d=retro&r=gcffcdda02b9c65d5c11cf63dfc7caea7","url":"https:\/\/secure.gravatar.com\/avatar\/2b1df2baa1bfb9637037d0e2be466c725e5853c8c4ec9dd80b6ca4498ee3d447?s=96&d=retro&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/2b1df2baa1bfb9637037d0e2be466c725e5853c8c4ec9dd80b6ca4498ee3d447?s=96&d=retro&r=g","caption":"Andrej Litowka"},"url":"https:\/\/www.inovex.de\/de\/blog\/author\/alitowka\/"}]}},"_links":{"self":[{"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/posts\/69756","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/users\/463"}],"replies":[{"embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/comments?post=69756"}],"version-history":[{"count":5,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/posts\/69756\/revisions"}],"predecessor-version":[{"id":70140,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/posts\/69756\/revisions\/70140"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/media\/70113"}],"wp:attachment":[{"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/media?parent=69756"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/tags?post=69756"},{"taxonomy":"service","embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/service?post=69756"},{"taxonomy":"level","embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/level?post=69756"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/coauthors?post=69756"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}