Blog

Wir stellen LangChain4j vor, um die LLM-Integration in Java-Anwendungen zu vereinfachen.

LangChain4j (LangChain für Java) ist ein leistungsstarkes Toolset, mit dem Sie Ihre RAG-Anwendung in reinem Java erstellen können.

Das LangChain4j-Framework wurde 2023 mit folgendem Ziel entwickelt:

Das Ziel von LangChain4j ist es, die Integration von LLMs in Java-Anwendungen zu vereinfachen.

LangChain4j bietet eine standardisierte Methode, um:

  • Einbettungen (Vektoren) aus einem gegebenen Inhalt erstellen, beispielsweise aus einem Text

  • Einbettungen in einem Einbettungsspeicher speichern

  • Suche nach ähnlichen Vektoren im Einbettungsspeicher

  • mit LLMs sprechen

  • Nutzen Sie die Chat-Speicherfunktion, um sich an den Kontext einer Diskussion mit einem LLM zu erinnern.

Diese Liste ist nicht vollständig, und die LangChain4j-Community implementiert ständig neue Funktionen.

Dieser Beitrag behandelt die ersten Hauptbestandteile des Frameworks.

Hinzufügen von LangChain4j OpenAI zu unserem Projekt

Wie bei allen Java-Projekten ist es einfach eine Frage der Abhängigkeiten. Hier verwenden wir Maven, aber das gleiche Ergebnis ließe sich auch mit jedem anderen Abhängigkeitsmanager erzielen.

Als ersten Schritt für das Projekt, das wir hier aufbauen wollen, werden wir OpenAI verwenden, daher müssen wir lediglich das langchain4j-open-ai -Artefakt hinzufügen:

<properties>
  <langchain4j.version>0.34.0</langchain4j.version>
</properties>

<dependencies>
  <dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-open-ai</artifactId>
    <version>${langchain4j.version}</version>
  </dependency>
</dependencies>

Für den restlichen Code verwenden wir entweder unseren eigenen API-Schlüssel, den Sie durch die Registrierung eines Kontos bei OpenAI erhalten, oder den vom LangChain4j-Projekt bereitgestellten Schlüssel, der jedoch nur zu Demonstrationszwecken dient:

static String getOpenAiApiKey() {
  String apiKey = System.getenv(API_KEY_ENV_NAME);
  if (apiKey == null || apiKey.isEmpty()) {
    Logger.warn("Please provide your own key instead using [{}] env variable", API_KEY_ENV_NAME);
    return "demo";
  }
  return apiKey;
}

Wir können nun eine Instanz unseres ChatLanguageModel erstellen:

ChatLanguageModel model = OpenAiChatModel.withApiKey(getOpenAiApiKey());

Und schließlich können wir eine einfache Frage stellen und die Antwort erhalten:

String answer = model.generate("Who is Thomas Pesquet?");
Logger.info("Answer is: {}", answer);

Die gegebene Antwort könnte etwa so lauten:

Thomas Pesquet is a French aerospace engineer, pilot, and European Space Agency astronaut.
He was selected as a member of the European Astronaut Corps in 2009 and has since completed 
two space missions to the International Space Station, including serving as a flight engineer 
for Expedition 50/51 in 2016-2017. Pesquet is known for his contributions to scientific 
research and outreach activities during his time in space.

Wenn Sie diesen Code ausführen möchten, sehen Sie sich bitte die Klasse Step1AiChatTest.java an.

Mehr Kontext mit langchain4j

Fügen wir das langchain4j -Artefakt hinzu:

<dependency>
  <groupId>dev.langchain4j</groupId>
  <artifactId>langchain4j</artifactId>
  <version>${langchain4j.version}</version>
</dependency>

Dieses Tool bietet uns einen Werkzeugkasten, der uns dabei helfen kann, eine fortgeschrittenere LLM-Integration für unseren Assistenten zu entwickeln. Hier erstellen wir einfach eine Assistant -Schnittstelle, die die chat -Methode bereitstellt, welche automatisch die zuvor definierte ChatLanguageModel aufruft:

interface Assistant {
  String chat(String userMessage);
}

Wir müssen lediglich die Klasse LangChain4j AiServices bitten, eine Instanz für uns zu erstellen:

Assistant assistant = AiServices.create(Assistant.class, model);

Und rufen Sie dann die Methode chat(String) auf:

String answer = assistant.chat("Who is Thomas Pesquet?");
Logger.info("Answer is: {}", answer);

Das System verhält sich genauso wie zuvor. Warum also haben wir den Code geändert? Erstens ist es eleganter, aber darüber hinaus können Sie dem LLM nun mithilfe einfacher Annotationen Anweisungen geben:

interface Assistant {
  @SystemMessage("Please answer in a funny way.")
  String chat(String userMessage);
}

Dies ergibt nun Folgendes:

Ah, Thomas Pesquet is actually a super secret spy disguised as an astronaut! 
He's out there in space fighting aliens and saving the world one spacewalk at a time. 
Or maybe he's just a really cool French astronaut who has been to the International 
Space Station. But my spy theory is much more exciting, don't you think?

Wenn Sie diesen Code ausführen möchten, sehen Sie sich bitte die Klasse Step2AssistantTest.java an.

Wechsel zu einem anderen LLM: langchain4j-ollama

Wir können das großartige Ollama-Projekt nutzen. Es ist hilfreich, einen LLM lokal auf Ihrem Rechner auszuführen.

Fügen wir das langchain4j-ollama -Artefakt hinzu:

<dependency>
  <groupId>dev.langchain4j</groupId>
  <artifactId>langchain4j-ollama</artifactId>
  <version>${langchain4j.version}</version>
</dependency>

Da wir den Beispielcode mithilfe von Tests ausführen, fügen wir unserem Projekt Testcontainers hinzu:

<dependency>
  <groupId>org.testcontainers</groupId>
  <artifactId>ollama</artifactId>
  <version>1.20.1</version>
  <scope>test</scope>
</dependency>

Wir können jetzt Docker-Container starten/stoppen:

static String MODEL_NAME = "mistral";
static String DOCKER_IMAGE_NAME = "langchain4j/ollama-" + MODEL_NAME + ":latest";

static OllamaContainer ollama = new OllamaContainer(
  DockerImageName.parse(DOCKER_IMAGE_NAME).asCompatibleSubstituteFor("ollama/ollama"));

@BeforeAll
public static void setup() {
  ollama.start();
}

@AfterAll
public static void teardown() {
  ollama.stop();
}

Wir müssen lediglich das model -Objekt in ein OllamaChatModel ändern, anstatt des zuvor verwendeten OpenAiChatModel :

OllamaChatModel model = OllamaChatModel.builder()
  .baseUrl(ollama.getEndpoint())
  .modelName(MODEL_NAME)
  .build();

Beachten Sie, dass das Abrufen des Bildes mit dem zugehörigen Modell einige Zeit in Anspruch nehmen kann, aber nach einer Weile erhalten Sie die Antwort:

Oh, Thomas Pesquet, the man who single-handedly keeps the French space program running 
while sipping on his crisp rosé and munching on a baguette! He's our beloved astronaut 
with an irresistible accent that makes us all want to learn French just so we can 
understand him better. When he's not floating in space, he's probably practicing his 
best "je ne sais quoi" face for the next family photo. Vive le Thomas Pesquet! 
🚀🌍🇫🇷 #FrenchSpaceHero

Besser mit Speicher

Wenn wir mehrere Fragen stellen, merkt sich das System standardmäßig nicht die vorherigen Fragen und Antworten. Wenn wir also nach der ersten Frage „Wann wurde er geboren?“ fragen, Unsere Anwendung wird folgende Fragen beantworten:

Oh, you're asking about this legendary figure from history, huh? Well, let me tell 
you a hilarious tale! He was actually born on Leap Year's Day, but only every 400 
years! So, do the math... if we count backwards from 2020 (which is also a leap year), 
then he was born in... *drumroll please* ...1600! Isn't that a hoot? But remember 
folks, this is just a joke, and historical records may vary.

Das ist Unsinn. Stattdessen sollten wir Chat Memory verwenden:

ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
Assistant assistant = AiServices.builder(Assistant.class)
  .chatLanguageModel(model)
  .chatMemory(chatMemory)
  .build();

Die erneute Beantwortung derselben Fragen liefert nun ein aussagekräftiges Ergebnis:

Oh, Thomas Pesquet, the man who was probably born before sliced bread but after dinosaurs! 
You know, around the time when people started putting wheels on suitcases and calling it 
a revolution. So, roughly speaking, he came into this world somewhere in the late 70s or 
early 80s, give or take a year or two - just enough time for him to grow up, become an 
astronaut, and make us all laugh with his space-aged antics! Isn't that a hoot? 
*laughs maniacally*

Fazit

Im nächsten Beitrag werden wir herausfinden, wie wir mithilfe von Elasticsearch als Einbettungsspeicher Fragen an unseren privaten Datensatz stellen können. Das wird uns die Möglichkeit geben, unsere Anwendungssuche auf die nächste Stufe zu heben.

Zugehörige Inhalte

Testen Ihres Java-Codes mit Mocks und echtem Elasticsearch

Piotr Przybyl

LangChain4j mit Elasticsearch als Einbettungsspeicher

David Pilato

Sind Sie bereit, hochmoderne Sucherlebnisse zu schaffen?

Eine ausreichend fortgeschrittene Suche kann nicht durch die Bemühungen einer einzelnen Person erreicht werden. Elasticsearch wird von Datenwissenschaftlern, ML-Ops-Experten, Ingenieuren und vielen anderen unterstützt, die genauso leidenschaftlich an der Suche interessiert sind wie Sie. Lasst uns in Kontakt treten und zusammenarbeiten, um das magische Sucherlebnis zu schaffen, das Ihnen die gewünschten Ergebnisse liefert.

Probieren Sie es selbst aus