Agentic AI mit Spring: Wie Large Language Models eigene Tools nutzen können

In seinem aktuellen Kurs Agentic AI zeigt Andrew Ng unter anderem: Large Language Models (LLMs) werden dann wirklich nützlich, wenn sie nicht nur Text generieren sondern aktiv auf Tools zugreifen können. Tools sind dabei ganz normale Programme, Services oder Methoden. Ein LLM kann über diese Tools Daten abfragen, Berechnungen durchführen oder Aktionen ausführen, die über sein eigenes „eingefrorenes“ Modellwissen hinausgehen.

Genau diesen Gedanken habe ich mit Spring AI ausprobiert und in einem kleinen Demo-Projekt umgesetzt:

Ein meschlicher Mitarbeiter und ein Roboter arbeiten zusammen an einem Schreibtisch.

Warum Tools?

LLMs sind statische Modelle: Sie wissen nur, was während des Trainings vorhanden war. Tools erweitern dieses Wissen dynamisch. Sie ermöglichen zum Beispiel:

  • Zugriff auf aktuelle Informationen
  • Nutzung unternehmensinterner Daten
  • Zugriff auf öffentliche oder eigene APIs
  • Ausführung komplexer Logik, ohne dass das Modell selbst diese kennen muss
  • Integration mit bestehenden Anwendungen und Workflows

Moderne LLMs sind darauf trainiert, Tool-Aufrufe zu verstehen und korrekt zu formulieren. Sie führen die Tools jedoch nicht selbst aus, ein Agent vermittelt zwischen Modell und Tools, interpretiert den vom LLM erzeugten Tool-Aufruf und führt ihn aus. Der agentische Aufrufzyklus sieht dabei typischerweise so aus:

  1. Der Nutzer stellt eine Anfrage an den Agenten.
  2. Der Agent reicht die Anfrage an das LLM weiter, inlusive der Tooloptionen.
  3. Das LLM entscheidet, ob zur Lösung ein Tool benötigt wird.
  4. Falls ja:
    1. das LLM erzeugt einen strukturierten Tool-Aufruf (z. B. JSON).
    2. Der Agent empfängt diesen Aufruf, führt das Tool im Backend aus und sammelt das Ergebnis.
    3. Das Ergebnis wird dem LLM als zusätzlicher Kontext übergeben.
    4. Das LLM erzeugt darauf basierend die finale Antwort für den Nutzer.
  5. Fall nein: das Ergebnis wird rein aus dem Modellwissen erzeugt
  6. Das LLM liefert das Ergebnis an den Agenten
  7. Dieser liefert das Ergebnis an den Aufrufer

So bleibt das Modell selbst statisch, während der Agent für dynamisches Verhalten sorgt. Das ganze ist natürlich nicht auf einen einzigen Toolaufruf beschränkt. Die Aufrufe des LLM mit den Tool-Ergebnissen werden solange wiederholt, bis das LLM keinen Tool-Aufruf mehr vorschlägt.

Beispiel: Ein einfacher Tool-Service

Nun sehen wir, wie das in meinem Demo-Projekt konkret umgesetzt wurde. In meinem Demo gibt es einen einfachen Service, der als Tool fungiert und zwei Funktionen anbietet. Die Methode getCurrentDateTime ist parameterlos und liefert eine aktuelle Zeitinformation des Benutzers als String. Die zweite Methode addIntegers bietet zwei Integer-Parameter and und liefert die Summe der beiden Parameter als Ergebnis zurück. Beide Methoden sind denkbar einfach und sollen lediglich das Konzept der Tools demonstrieren. Prinzipiell ist hier jede mögliche andere Funktionalität denkbar, z.B. die Abfrage von Artikeldaten in einer Datenbank oder das Anstoßen eines Bestellprozesses.

Wichtig für die Verwendung der Methoden als Tools sind die Spring AI Annotationen @Tool und @ToolParam sowie die dort hinterlegten Beschreibungen der Methoden und Parameter. Diese Informationen werden an das LLM übertragen und dort genutzt um Tool-Aufrufe anhand der Tool-Description zu erkennen und anhand der ToolParam-Description korrekt für den Aufruf zu konfigurieren.

@Service
public class AgenticTools {

Logger logger = LoggerFactory.getLogger(AgenticTools.class);

@Tool(description = "Get the current date and time in the user's timezone")
public String getCurrentDateTime() {
var now = LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();
logger.info("getCurrentDateTime call returned: {}", now);
return now;
}

@Tool(description = "Add two integers")
public Integer addIntegers(@ToolParam(description = "param1") Integer param1,
@ToolParam(description = "param2") Integer param2) {
var sum = param1 + param2;
logger.info("addIntegers({}, {}) call returned: {}", param1, param2, sum);
return sum;
}
}

Die Nutzung der Tools erfolgt dann denkbar einfach beim Aufruf des LLM über den ChatClient. Dort werden die Tools einfach zusammen mit dem Prompt als Kontext übergeben. Spring AI kümmert sich sowohl um die Übertragung der Informationen zu den Tools, als auch um den Aufruf der Tools selbst.

@RestController
public class AgenticController {
...
private final ChatClient chatClient;
private final AgenticTools agenticTools;
...

@GetMapping("/withtools")
public String tools() {

logger.info("Calling /withtools endpoint");

var prompt = "get the current date and time";
return chatClient.prompt()
.tools(agenticTools)
.user(prompt)
.call()
.content();
}

...
}

Und Ausführung!

Zunächst einmal versuchen wir einen Aufruf ganz ohne Tools, wir wollen das aktuelle Datum und die Uhrzeit wissen:

@GetMapping("/notools")
public String notools() {

logger.info("Calling /notools endpoint");

var prompt = "get the current date and time";
return chatClient.prompt()
.user(prompt)
.call()
.content();
}

Der Aufruf liefert die folgende Antwort zurück:

I can’t provide real-time information, including the current date and time. However, you can easily find the current date and time on your device or by searching online. If you need help with anything else, feel free to ask!

Erwartungsgemäß kann uns das Modell (in diesem Fall ist das Modell GPT-4) keine aktuellen Informationen geben. Immerhin gibt es ein paar Tipps, wie man an die aktuelle Uhrzeit kommen kann. Versuchen wir es mit dem gleichen User-Prompt, aber jetzt mit Tools.

@GetMapping("/withtools")
public String tools() {

logger.info("Calling /withtools endpoint");

var prompt = "get the current date and time";
return chatClient.prompt()
.tools(agenticTools)
.user(prompt)
.call()
.content();
}

Jetzt scheint es zu funktionieren.

The current date and time is November 24, 2025, at 21:38:28 in the Europe/Berlin timezone.

Im Log sieht man die Ausgabe des getCurrentDateTime-Tools:

2025-11-24T21:38:24.894+01:00 INFO 93689 --- d.m.agenticspring.AgenticController : Calling /withtools endpoint
2025-11-24T21:38:28.099+01:00 INFO 93689 --- d.m.agenticspring.AgenticTools : getCurrentDateTime call returned: 2025-11-24T21:38:28.099273+01:00[Europe/Berlin]

Abschließend probieren wir noch einen anderen Prompt.

@GetMapping("/usemultipletools")
public String prompt() {

logger.info("Calling /usemultipletools endpoint");

var prompt = "get the current date and then add the day and the month. return a limerick about the result.";
var response = chatClient.prompt()
.tools(agenticTools)
.user(prompt)
.call();

return response.content();
}

Das Ergebnis ist folgendes:

Here’s a limerick based on the result of adding the day (24) and the month (11): In November, the days were quite grand, With twenty-four, I took a bold stand. I added eleven, And what was my heaven? A total of thirty-five, oh so planned!

Das macht natürlich nicht unbedingt viel Sinn. Aber wir sehen, dass in der Verarbeitung zwei Tools (getCurrentDateTime und addIntegers) in der korrekten Reihenfolge aufgerufen wurden (der zweite Aufruf mit dem Ergebnis des ersten Aufrufs als Parameter) und das LLM dann mit den Ergebnissen eine Ausgabe produziert hat. Das Log belegt die Tool-Aufrufe:

2025-11-24T21:42:29.840+01:00 INFO 93689 --- d.m.agenticspring.AgenticController : Calling /usemultipletools endpoint
2025-11-24T21:42:37.301+01:00 INFO 93689 --- d.m.agenticspring.AgenticTools : getCurrentDateTime call returned: 2025-11-24T21:42:37.301875+01:00[Europe/Berlin]
2025-11-24T21:42:39.552+01:00 INFO 93689 --- d.m.agenticspring.AgenticTools : addIntegers(24, 11) call returned: 35

Fazit – und ein Blick nach vorne

Agentic AI zeigt, wie LLMs durch Tools zu aktiven Problemlösern werden können. Spring AI macht diese Idee in Java-Umgebungen sehr gut umsetzbar: Tools werden als Teil des Modellaufrufs deklariert, der Agent vermittelt zwischen Modell und Backend-Services, und das LLM erzeugt eigenständig Tool-Aufrufe. Damit lassen sich LLMs auf aktuelle Informationen, interne Systeme und benutzerdefinierte Logik zugreifen.

Spannend wird der Bereich besonders durch aktuelle Entwicklungen wie das Model Context Protocol (MCP). MCP standardisiert den Austausch zwischen Modellen, Agenten und Tools. Es definiert, wie ein Modell Tools entdecken, aufrufen und mit einem Agenten interagieren kann, unabhängig von Programmiersprache oder Framework. Damit entsteht eine gemeinsame Sprache für agentische Systeme, die weit über einzelne Frameworks wie Spring AI hinausgeht.

In Zukunft wird es daher weniger darum gehen, wie ein bestimmtes Tool angebunden wird, sondern welche Tools einem Modell zur Verfügung stehen und wie der Agent den Kontext orchestriert. Agentic AI wird dadurch interoperabler, sicherer und wesentlich flexibler.

Wer LLM-basierte Anwendungen baut, sollte die Entwicklung rund um MCP unbedingt im Blick behalten, hier entsteht gerade der offene Standard, der entscheidet, wie Modelle künftig mit unseren Systemen interagieren.

Kommentar verfassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Nach oben scrollen