LangChain4j com Elasticsearch como armazenamento de incorporação

O LangChain4j (LangChain para Java) utiliza o Elasticsearch como armazenamento integrado. Descubra como usá-lo para construir sua aplicação RAG em Java puro.

Experimente o Elasticsearch na prática: Mergulhe em nossos notebooks de amostra, inicie um teste gratuito na nuvem ou experimente o Elastic em sua máquina local agora mesmo.


Na publicação anterior, descobrimos o que é LangChain4j e como:

  • Inicie uma discussão com os LLMs implementando um ChatLanguageModel e um ChatMemory
  • Manter o histórico do chat na memória para relembrar o contexto de uma discussão anterior com um LLM

Esta postagem do blog aborda como:

  • Criar vetores incorporados a partir de exemplos de texto
  • Armazene os vetores de incorporação no repositório de incorporações do Elasticsearch.
  • Buscar vetores semelhantes

Criar incorporações

Para criar embeddings, precisamos definir um EmbeddingModel para usar. Por exemplo, podemos usar o mesmo modelo mistral que usamos na postagem anterior. Estava correndo com a lhama:

Um modelo é capaz de gerar vetores a partir de texto. Aqui podemos verificar o número de dimensões geradas pelo modelo:

Para gerar vetores a partir de um texto, podemos usar:

Ou, se também quisermos fornecer metadados para nos permitir filtrar por coisas como texto, preço, data de lançamento ou qualquer outra coisa, podemos usar Metadata.from(). Por exemplo, estamos adicionando aqui o nome do jogo como um campo de metadados:

Se você quiser executar este código, consulte a classe Step5EmbedddingsTest.java .

Adicionar o Elasticsearch para armazenar nossos vetores.

LangChain4j fornece um armazenamento de incorporação em memória. Isso é útil para executar testes simples:

Mas, obviamente, isso não funcionaria com conjuntos de dados muito maiores, porque esse armazenamento de dados guarda tudo na memória e não temos memória infinita em nossos servidores. Assim, poderíamos armazenar nossos embeddings no Elasticsearch, que é, por definição, "elástico" e pode ser dimensionado verticalmente e horizontalmente conforme a demanda de dados. Para isso, vamos adicionar o Elasticsearch ao nosso projeto:

Como você deve ter notado, também adicionamos o módulo Elasticsearch TestContainers ao projeto, para que possamos iniciar uma instância do Elasticsearch a partir de nossos testes:

Para usar o Elasticsearch como um armazenamento de dados incorporado, você "simplesmente" precisa trocar o armazenamento de dados em memória do LangChain4j pelo armazenamento de dados do Elasticsearch:

Isso armazenará seus vetores no Elasticsearch em um índice default . Você também pode alterar o nome do índice para algo mais significativo:

Se você quiser executar este código, consulte a classe Step6ElasticsearchEmbedddingsTest.java .

Buscar vetores semelhantes

Para buscar vetores semelhantes, primeiro precisamos transformar nossa pergunta em uma representação vetorial usando o mesmo modelo que usamos anteriormente. Já fizemos isso, então não é difícil fazer de novo. Note que, neste caso, não precisamos dos metadados:

Podemos construir uma solicitação de pesquisa com essa representação da nossa pergunta e pedir ao repositório de embeddings para encontrar os primeiros vetores principais:

Agora podemos iterar sobre os resultados e imprimir algumas informações, como o nome do jogo, que vem dos metadados, e a pontuação:

Como era de se esperar, isso nos dá "Out Run" como o primeiro sucesso:

Se você quiser executar este código, consulte a classe Step7SearchForVectorsTest.java .

Nos bastidores

A configuração padrão do Elasticsearch Embedding Store utiliza a consulta kNN aproximada nos bastidores.

Mas isso poderia ser alterado fornecendo uma configuração diferente (ElasticsearchConfigurationScript) da configuração padrão (ElasticsearchConfigurationKnn) para o armazenamento de incorporação:

A implementação ElasticsearchConfigurationScript executa em segundo plano uma consulta script_score usando uma função cosineSimilarity .

Basicamente, ao ligar:

Isto agora exige:

Nesse caso, o resultado não muda em termos de "ordem", apenas a pontuação é ajustada, pois a chamada cosineSimilarity não usa nenhuma aproximação, mas calcula o cosseno para cada um dos vetores correspondentes:

Se você quiser executar este código, consulte a classe Step7SearchForVectorsTest.java .

Conclusão

Já abordamos a facilidade com que você pode gerar embeddings a partir do seu texto e como você pode armazenar e pesquisar os vizinhos mais próximos no Elasticsearch usando duas abordagens diferentes:

  • Usando a consulta aproximada e rápida knn com a opção padrão ElasticsearchConfigurationKnn
  • Usando a consulta exata, porém mais lenta, script_score com a opção ElasticsearchConfigurationScript

O próximo passo será construir uma aplicação RAG completa, com base no que aprendemos aqui.

Conteúdo relacionado

Pronto para criar buscas de última geração?

Uma pesquisa suficientemente avançada não se consegue apenas com o esforço de uma só pessoa. O Elasticsearch é impulsionado por cientistas de dados, especialistas em operações de aprendizado de máquina, engenheiros e muitos outros que são tão apaixonados por buscas quanto você. Vamos nos conectar e trabalhar juntos para construir a experiência de busca mágica que lhe trará os resultados desejados.

Experimente você mesmo(a)