Apresentamos o LangChain4j para simplificar a integração do LLM em aplicações Java.
LangChain4j (LangChain para Java) é um conjunto de ferramentas poderoso para construir sua aplicação RAG em Java puro.
O framework LangChain4j foi criado em 2023 com este objetivo:
O objetivo do LangChain4j é simplificar a integração de LLMs em aplicações Java.
LangChain4j oferece uma maneira padronizada de:
Criar representações vetoriais (embeddings) a partir de um determinado conteúdo, digamos, um texto, por exemplo.
armazenar embeddings em um armazenamento de embeddings
pesquisar vetores semelhantes no armazenamento de embeddings
discutir com mestres em direito
Use uma memória de bate-papo para lembrar o contexto de uma discussão com um mestre em Direito.
Esta lista não é exaustiva e a comunidade LangChain4j está sempre implementando novos recursos.
Esta postagem abordará as primeiras partes principais da estrutura.
Adicionando LangChain4j OpenAI ao nosso projeto
Como em todos os projetos Java, é apenas uma questão de dependências. Aqui usaremos o Maven, mas o mesmo resultado poderia ser obtido com qualquer outro gerenciador de dependências.
Como primeiro passo para o projeto que queremos construir aqui, usaremos o OpenAI, então precisamos apenas adicionar o artefato langchain4j-open-ai :
<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 o restante do código, usaremos nossa própria chave de API, que você pode obter criando uma conta na OpenAI, ou a fornecida pelo projeto LangChain4j apenas para fins de demonstração:
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;
}Agora podemos criar uma instância do nosso ChatLanguageModel:
ChatLanguageModel model = OpenAiChatModel.withApiKey(getOpenAiApiKey());E, finalmente, podemos fazer uma pergunta simples e obter a resposta:
String answer = model.generate("Who is Thomas Pesquet?");
Logger.info("Answer is: {}", answer);A resposta fornecida poderia ser algo como:
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.Se você quiser executar este código, consulte a classe Step1AiChatTest.java .
Fornecendo mais contexto com langchain4j
Vamos adicionar o artefato langchain4j :
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>${langchain4j.version}</version>
</dependency>Esta ferramenta oferece um conjunto de recursos que pode nos ajudar a construir uma integração LLM mais avançada para criar nosso assistente. Aqui, criaremos uma interface Assistant que fornece o método chat , o qual chamará automaticamente o ChatLanguageModel que definimos anteriormente:
interface Assistant {
String chat(String userMessage);
}Basta pedirmos à classe LangChain4j AiServices para construir uma instância para nós:
Assistant assistant = AiServices.create(Assistant.class, model);Em seguida, chame o método chat(String) :
String answer = assistant.chat("Who is Thomas Pesquet?");
Logger.info("Answer is: {}", answer);Está apresentando o mesmo comportamento de antes. Então, por que alteramos o código? Em primeiro lugar, é mais elegante, mas além disso, agora você pode dar instruções ao LLM usando anotações simples:
interface Assistant {
@SystemMessage("Please answer in a funny way.")
String chat(String userMessage);
}Isso está resultando em:
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?Se você quiser executar este código, consulte a classe Step2AssistantTest.java .
Mudando para outro LLM: langchain4j-ollama
Podemos usar o excelente projeto Ollama. Executar um LLM localmente em sua máquina pode ser útil.
Vamos adicionar o artefato langchain4j-ollama :
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-ollama</artifactId>
<version>${langchain4j.version}</version>
</dependency>Como estamos executando o código de exemplo usando testes, vamos adicionar Testcontainers ao nosso projeto:
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>ollama</artifactId>
<version>1.20.1</version>
<scope>test</scope>
</dependency>Agora podemos iniciar/parar contêineres 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();
}Nós "apenas" precisamos mudar o objeto model para se tornar um OllamaChatModel em vez do OpenAiChatModel que usamos anteriormente:
OllamaChatModel model = OllamaChatModel.builder()
.baseUrl(ollama.getEndpoint())
.modelName(MODEL_NAME)
.build();Observe que pode levar algum tempo para obter a imagem com seu modelo, mas depois de um tempo, você poderá obter a resposta:
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!
🚀🌍🇫🇷 #FrenchSpaceHeroMelhor com memória
Se fizermos várias perguntas, por padrão o sistema não se lembrará das perguntas e respostas anteriores. Então, se perguntarmos depois da primeira pergunta "Quando ele nasceu?", Nosso aplicativo 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.O que é um absurdo. Em vez disso, devemos usar a Memória de Chat:
ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
Assistant assistant = AiServices.builder(Assistant.class)
.chatLanguageModel(model)
.chatMemory(chatMemory)
.build();Executar as mesmas perguntas agora fornece uma resposta 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*Conclusão
Na próxima publicação, descobriremos como podemos fazer perguntas ao nosso conjunto de dados privado usando o Elasticsearch como repositório de embeddings. Isso nos dará uma maneira de elevar nossa busca de aplicativos a um novo patamar.

