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!
🚀🌍🇫🇷 #FrenchSpaceHeroBesser 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.

