Presentamos LangChain4j para simplificar la integración de LLM en aplicaciones Java
LangChain4j (LangChain para Java) es un conjunto de herramientas poderosa para construir tu aplicación RAG en Java puro.
El marco LangChain4j se creó en 2023 con este objetivo:
El objetivo de LangChain4j es simplificar la integración de LLMs en aplicaciones Java.
LangChain4j está proporcionando una forma estándar de:
crear incrustaciones (vectores) a partir de un contenido dado, por ejemplo un texto
Almacenar incrustaciones en un almacén de incrustaciones
Busca vectores similares en la tienda de incrustación
discute con los LLMs
usar una memoria de chat para recordar el contexto de una conversación con un LLM
Esta lista no es exhaustiva y la comunidad de LangChain4j siempre está implementando nuevas funcionalidades.
Esta entrada cubrirá las primeras partes principales del marco.
Agregando LangChain4j OpenAI a nuestro proyecto
Como en todos los proyectos Java, es solo cuestión de dependencias. Aquí usaremos Maven, pero lo mismo podría lograr con cualquier otro gestor de dependencias.
Como primer paso para el proyecto que queremos construir aquí, usaremos OpenAI, así que solo necesitamos agregar el langchain4j-open-ai artefacto:
<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>Para el resto del código usaremos nuestra propia clave API, que puedes obtener registrándote en OpenAI, o la que proporciona el proyecto LangChain4j solo para demostraciones:
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;
}Ahora podemos crear una instancia de nuestro ChatLanguageModel:
ChatLanguageModel model = OpenAiChatModel.withApiKey(getOpenAiApiKey());Y finalmente podemos hacer una pregunta sencilla y obtener la respuesta:
String answer = model.generate("Who is Thomas Pesquet?");
Logger.info("Answer is: {}", answer);La respuesta dada podría ser algo así:
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.Si quieres ejecutar este código, por favor echa un vistazo a la clase Step1AiChatTest.java .
Aportando más contexto con langchain4j
Vamos a agregar el artefacto langchain4j :
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>${langchain4j.version}</version>
</dependency>Este está proporcionando un conjunto de herramientas que puede ayudarnos a construir una integración LLM más avanzada para construir nuestro asistente. Aquí simplemente crearemos una interfaz Assistant que proporcione el método chat que llamará automáticamente a la ChatLanguageModel que definimos antes:
interface Assistant {
String chat(String userMessage);
}Solo tenemos que pedirle a LangChain4j AiServices clase que construya una instancia para nosotros:
Assistant assistant = AiServices.create(Assistant.class, model);Y luego llama al método chat(String) :
String answer = assistant.chat("Who is Thomas Pesquet?");
Logger.info("Answer is: {}", answer);Esto es tener el mismo comportamiento que antes. ¿Entonces por qué cambiamos el código? En primer lugar, es más elegante, pero más que eso, ahora puedes dar algunas instrucciones al LLM usando anotaciones sencillas:
interface Assistant {
@SystemMessage("Please answer in a funny way.")
String chat(String userMessage);
}Esto ahora es dar:
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?Si quieres ejecutar este código, por favor echa un vistazo a la clase Step2AssistantTest.java .
Cambiar a otro LLM: langchain4j-ollama
Podemos aprovechar el gran proyecto Ollama. Ayuda tener un LLM localmente en tu máquina.
Vamos a agregar el artefacto langchain4j-ollama :
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-ollama</artifactId>
<version>${langchain4j.version}</version>
</dependency>Como ejecutamos el código de ejemplo usando pruebas, vamos a agregar Testcontainers a nuestro proyecto:
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>ollama</artifactId>
<version>1.20.1</version>
<scope>test</scope>
</dependency>Ahora podemos iniciar/detener contenedores Docker:
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();
}Simplemente tenemos que cambiar el objeto model para que se convierta en un OllamaChatModel en lugar del OpenAiChatModel que usamos antes:
OllamaChatModel model = OllamaChatModel.builder()
.baseUrl(ollama.getEndpoint())
.modelName(MODEL_NAME)
.build();Ten en cuenta que puede tardar un tiempo en extraer la imagen con su modelo, pero luego de un rato, podrías obtener la respuesta:
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!
🚀🌍🇫🇷 #FrenchSpaceHeroMejor con la memoria
Si hacemos varias preguntas, por defecto el sistema no recordará las preguntas frecuentes anteriores. Así que si preguntamos luego de la primera pregunta "¿Cuándo nació?", Nuestra solicitud responderá:
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.Lo cual es una tontería. En su lugar, deberíamos usar Chat Memory:
ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
Assistant assistant = AiServices.builder(Assistant.class)
.chatLanguageModel(model)
.chatMemory(chatMemory)
.build();Hacer las mismas preguntas ahora da una respuesta significativa:
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*Conclusión
En la próxima publicación, descubriremos cómo podemos hacer preguntas a nuestro conjunto de datos privado usando Elasticsearch como almacén de incrustaciones. Eso nos dará una forma de transformar nuestra búsqueda de aplicaciones al siguiente nivel.

