GEDOPLAN
Quarkus

Werkzeuge für die AI – Quarkus und LangChain4j

Quarkus
langchain4j tools jpg

Im letzten Blog-Artikel zum Thema AI haben wir uns die Tools-Unterstützung mit Spring und Spring AI angeschaut (siehe hier). In diesem Artikel schauen wir uns nun dasselbe Beispiel mit Quarkus und LangChain4j an. Die Bibliothek ist zunächst mal an kein bestimmtes Framework gebunden, es gibt aber optionale Integrationen um die Verwendung mit Spring Boot oder Quarkus zu vereinfachen. In unserem Beispielprojekt verwenden wir wieder das Modell mistral (lokal mit Ollama) und ermöglichen dem LLM aktuelle Wetter- und Verkehrsinformationen abzurufen. Für dieses Beispiel füllen wir diese Schnittstellen aber zunächst nur mit Beispieldaten.

Im Gegensatz zur Umsetzung mit Spring AI kann man bei Langchain4j auch einen eher deklarativen Ansatz auf Basis von Annotationen verwenden um die LLM-Schnittstelle zu definieren. In den Code-Beispielen fokussieren wir uns diesmal auf die Wetterinformationen. Die Schnittstellen für die Verkehrsinformationen sind analog implementiert.

Tools (also Funktionalitäten, die das LLM verwenden kann) können mit @Tool und einer Beschreibung der Schnittstelle annotiert werden. Man kann auch hier beliebige Java-Typen als Parameter angeben, LLMs kommen aber mit einer flacheren Struktur oft besser klar. Mit der @P Annotation können die Parameter hier noch weiter beschrieben und als optional gekennzeichnet werden.

@ApplicationScoped
public class AiTools {
  @Tool("Get current weather information for location.")
  public WeatherResponse getCurrentWeather(@P("Request location") String location, 
                                           @P(value = "Request unit", required = false) TempUnit unit) {
    return new MockWeatherInformationService().apply(new WeatherRequest(location, unit));
  }
  [...]
}

Die Typen sind hier dieselben wie bereits im Spring AI Beispiel.

public record WeatherRequest(String location, TempUnit unit) {}
public sealed interface WeatherResponse {
  record Success(String location, double temp, TempUnit unit) implements WeatherResponse { [...] }
  record Failure(String failureMessage) implements WeatherResponse {
  }
}
public enum TempUnit{ CELSIUS, FAHRENHEIT; [...] }

Die Mock-Schnittstelle liefert dann ein paar vordefinierte Wetterinformationen.

public class MockWeatherInformationService {
  private final Map<String, WeatherResponse> temperatureMap = Map.ofEntries(
      Map.entry("Bielefeld", new WeatherResponse.Success("Bielefeld", 20.1, TempUnit.CELSIUS)),
      Map.entry("Berlin", new WeatherResponse.Success("Berlin", 25.6, TempUnit.CELSIUS)),
      Map.entry("Köln", new WeatherResponse.Success("Köln", 27.2, TempUnit.CELSIUS))
  );

  public WeatherResponse apply(WeatherRequest request) {
    if (request == null) return new WeatherResponse.Failure("Error: Could not parse request.");
    WeatherResponse weatherResponse = temperatureMap.getOrDefault(request.location(), null);
    return switch (weatherResponse) {
      case null -> new WeatherResponse.Failure("Error: No weather information for location " + request.location() + " available.");
      case WeatherResponse.Failure failure -> failure;
      case WeatherResponse.Success success -> success.withTempUnit(request.unit());
    };
  }
}

Unsere Anwendung beinhaltet auch eine einfache HTTP-Schnittstelle zum Testen, in der eine Anfrage an das LLM gesendet wird. Die Antwort bekommen wir dann entsprechend zurück.

@Path("ai")
public class ToolsAiResource{
  @Inject
  ToolsAiService toolsAiService;

  @POST
  @Path("/tools")
  @Consumes(MediaType.TEXT_PLAIN)
  @Produces(MediaType.TEXT_PLAIN)
  public Response tools(String question) {
    return Response.ok(toolsAiService.chat(question)).build();
  }
}

Der ToolsAiService kann durch die Quarkus Integration über Annotationen bereitgestellt und konfiguriert werden.

@RegisterAiService(tools = AiTools.class)
public interface ToolsAiService {
  String chat(@UserMessage String question);
}

Eine beispielhafte Interaktion kann dann so aussehen.

Q: What is the current weather in Bielefeld?
A: Currently in Bielefeld, the temperature is 20.1 degrees Celsius.

Im Gegensatz zu Spring AI können wir hier Logging für die HTTP Kommunikation mit dem LLM einfach über die Properties quarkus.langchain4j.ollama.chat-model.log-requests und quarkus.langchain4j.ollama.chat-model.log-responses aktivieren.

Das Beispiel gibt es wie immer auf GitHub.

Schreibe einen Kommentar

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

Bitte füllen Sie dieses Feld aus.
Bitte füllen Sie dieses Feld aus.
Bitte gib eine gültige E-Mail-Adresse ein.
Sie müssen den Bedingungen zustimmen, um fortzufahren.

Autor

Diesen Artikel teilen

LinkedIn
Xing

Gibt es noch Fragen?

Fragen beantworten wir sehr gerne! Schreibe uns einfach per Kontaktformular.

Kurse

weitere Blogbeiträge

IT-Training - GEDOPLAN
Webprogrammierung

Angular Directive, Hostbinding

Directive sind neben Komponenten eines der zentralen Konzepte in Angular. Anders als Components bringt eine Directive kein eigenes Template mit,…

Work Life Balance. Jobs bei Gedoplan

We are looking for you!

Lust bei GEDOPLAN mitzuarbeiten? Wir suchen immer Verstärkung – egal ob Entwickler, Dozent, Trainerberater oder für unser IT-Marketing! Schau doch einfach mal auf unsere Jobseiten! Wir freuen uns auf Dich!

Work Life Balance. Jobs bei Gedoplan

We are looking for you!

Lust bei GEDOPLAN mitzuarbeiten? Wir suchen immer Verstärkung – egal ob Entwickler, Dozent, Trainerberater oder für unser IT-Marketing! Schau doch einfach mal auf unsere Jobseiten! Wir freuen uns auf Dich!