<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title><![CDATA[Java - Elasticsearch Labs]]></title>
    <description><![CDATA[Articles and tutorials from the Search team at Elastic]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[Java - Elasticsearch Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1121c0bf0e8a6e65/6a88da6340a1841030ef456f/search-labs-thumbnail.png</url>
      <link>https://www.elastic.co/pt/search-labs/blog/category/java-programming</link>
    </image>
    <link>https://www.elastic.co/pt/search-labs/blog/category/java-programming</link>
    <atom:link href="https://www.elastic.co/pt/search-labs/rss/category/java-programming.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[pt]]></language>
    <lastBuildDate>Mon, 28 Sep 2026 06:11:59 GMT</lastBuildDate>
  <item>
    <title><![CDATA[LangChain4j com Elasticsearch como armazenamento de incorporação]]></title>
    <description><![CDATA[O LangChain4j (LangChain para Java) utiliza o Elasticsearch como armazenamento integrado. Descubra como usá-lo para construir sua aplicação RAG em Java puro.]]></description>
    <content:encoded><![CDATA[<p>
Na <a href="https://www.elastic.co/search-labs/blog/langchain4j-llm-integration-introduction">publicação anterior</a>, descobrimos o que é LangChain4j e como:</p><ul><li><p>Inicie uma discussão com os LLMs implementando um <code>ChatLanguageModel</code> e um <code>ChatMemory</code></p></li><li><p>Manter o histórico do chat na memória para relembrar o contexto de uma discussão anterior com um LLM</p></li></ul><p>Esta postagem do blog aborda como:</p><ul><li><p>Criar vetores incorporados a partir de exemplos de texto</p></li><li><p>Armazene os vetores de incorporação no repositório de incorporações do Elasticsearch. </p></li><li><p>Buscar vetores semelhantes</p></li></ul><h2>Criar incorporações</h2><p>Para criar embeddings, precisamos definir um <code>EmbeddingModel</code> para usar. Por exemplo, podemos usar o mesmo modelo mistral que usamos na <a href="https://www.elastic.co/search-labs/blog/langchain4j-llm-integration-introduction">postagem anterior</a>. Estava correndo com a lhama:</p>EmbeddingModel model = OllamaEmbeddingModel.builder()
  .baseUrl(ollama.getEndpoint())
  .modelName(MODEL_NAME)
  .build();<p>Um modelo é capaz de gerar vetores a partir de texto. Aqui podemos verificar o número de dimensões geradas pelo modelo:</p>Logger.info("Embedding model has {} dimensions.", model.dimension());
// This gives: Embedding model has 4096 dimensions.<p>Para gerar vetores a partir de um texto, podemos usar:</p>Response&lt;Embedding&gt; response = model.embed("A text here");<p>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 <code>Metadata.from()</code>. Por exemplo, estamos adicionando aqui o nome do jogo como um campo de metadados:</p>TextSegment game1 = TextSegment.from("""
    The game starts off with the main character Guybrush Threepwood stating "I want to be a pirate!"
    To do so, he must prove himself to three old pirate captains. During the perilous pirate trials, 
    he meets the beautiful governor Elaine Marley, with whom he falls in love, unaware that the ghost pirate 
    LeChuck also has his eyes on her. When Elaine is kidnapped, Guybrush procures crew and ship to track 
    LeChuck down, defeat him and rescue his love.
""", Metadata.from("gameName", "The Secret of Monkey Island"));
Response&lt;Embedding&gt; response1 = model.embed(game1);
TextSegment game2 = TextSegment.from("""
    Out Run is a pseudo-3D driving video game in which the player controls a Ferrari Testarossa 
    convertible from a third-person rear perspective. The camera is placed near the ground, simulating 
    a Ferrari driver's position and limiting the player's view into the distance. The road curves, 
    crests, and dips, which increases the challenge by obscuring upcoming obstacles such as traffic 
    that the player must avoid. The object of the game is to reach the finish line against a timer.
    The game world is divided into multiple stages that each end in a checkpoint, and reaching the end 
    of a stage provides more time. Near the end of each stage, the track forks to give the player a 
    choice of routes leading to five final destinations. The destinations represent different 
    difficulty levels and each conclude with their own ending scene, among them the Ferrari breaking 
    down or being presented a trophy.
""", Metadata.from("gameName", "Out Run"));
Response&lt;Embedding&gt; response2 = model.embed(game2);<p>Se você quiser executar este código, consulte a classe <a href="https://github.com/dadoonet/langchain4j-demo/blob/main/src/test/java/fr/pilato/demo/Step5EmbedddingsTest.java">Step5EmbedddingsTest.java</a> .</p><h2>Adicionar o Elasticsearch para armazenar nossos vetores.</h2><p>LangChain4j fornece um armazenamento de incorporação em memória. Isso é útil para executar testes simples:</p>EmbeddingStore&lt;TextSegment&gt; embeddingStore = new InMemoryEmbeddingStore&lt;&gt;();
embeddingStore.add(response1.content(), game1);
embeddingStore.add(response2.content(), game2);<p>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:</p>&lt;dependency&gt;
  &lt;groupId&gt;dev.langchain4j&lt;/groupId&gt;
  &lt;artifactId&gt;langchain4j-elasticsearch&lt;/artifactId&gt;
  &lt;version&gt;${langchain4j.version}&lt;/version&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
  &lt;groupId&gt;org.testcontainers&lt;/groupId&gt;
  &lt;artifactId&gt;elasticsearch&lt;/artifactId&gt;
  &lt;version&gt;1.20.1&lt;/version&gt;
  &lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;<p>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:</p>// Create the elasticsearch container
ElasticsearchContainer container =
  new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.15.0")
    .withPassword("changeme");

// Start the container. This step might take some time...
container.start();

// As we don't want to make our TestContainers code more complex than
// needed, we will use login / password for authentication.
// But note that you can also use API keys which is preferred.
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("elastic", "changeme"));

// Create a low level Rest client which connects to the elasticsearch container.
client = RestClient.builder(HttpHost.create("https://" + container.getHttpHostAddress()))
  .setHttpClientConfigCallback(httpClientBuilder -&gt; {
    httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
    httpClientBuilder.setSSLContext(container.createSslContextFromCa());
    return httpClientBuilder;
  })
  .build();

// Check the cluster is running
client.performRequest(new Request("GET", "/"));<p>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:</p>EmbeddingStore&lt;TextSegment&gt; embeddingStore =
  ElasticsearchEmbeddingStore.builder()
    .restClient(client)
    .build();
embeddingStore.add(response1.content(), game1);
embeddingStore.add(response2.content(), game2);<p>Isso armazenará seus vetores no Elasticsearch em um índice <code>default</code> . Você também pode alterar o nome do índice para algo mais significativo:</p>EmbeddingStore&lt;TextSegment&gt; embeddingStore =
  ElasticsearchEmbeddingStore.builder()
    .indexName("games")
    .restClient(client)
    .build();
embeddingStore.add(response1.content(), game1);
embeddingStore.add(response2.content(), game2);<p>Se você quiser executar este código, consulte a classe <a href="https://github.com/dadoonet/langchain4j-demo/blob/main/src/test/java/fr/pilato/demo/Step6ElasticsearchEmbedddingsTest.java">Step6ElasticsearchEmbedddingsTest.java</a> .</p><h2>Buscar vetores semelhantes</h2><p>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:</p>String question = "I want to pilot a car";
Embedding questionAsVector = model.embed(question).content();<p>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:</p>EmbeddingSearchResult&lt;TextSegment&gt; result = embeddingStore.search(
  EmbeddingSearchRequest.builder()
    .queryEmbedding(questionAsVector)
    .build());<p>Agora podemos iterar sobre os resultados e imprimir algumas informações, como o nome do jogo, que vem dos metadados, e a pontuação:</p>result.matches().forEach(m -&gt; Logger.info("{} - score [{}]",
  m.embedded().metadata().getString("gameName"), m.score()));<p>Como era de se esperar, isso nos dá "Out Run" como o primeiro sucesso:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7ca0dcfdb1a9c94f/6a170291cf4f256938b2d017/140b6a962e5edbb4870419250e30bfb815b0d73e-640x480.gif" alt="Out Run" />Out Run - score [0.86672974]
The Secret of Monkey Island - score [0.85569763]<p>Se você quiser executar este código, consulte a classe <a href="https://github.com/dadoonet/langchain4j-demo/blob/9ec4b1d4c7c69821f143ddf272bbfed273c67b14/src/test/java/fr/pilato/demo/Step7SearchForVectorsTest.java#L110-L129">Step7SearchForVectorsTest.java</a> . </p><h2>Nos bastidores</h2><p>A configuração padrão do Elasticsearch Embedding Store utiliza a <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.15/query-dsl-knn-query.html">consulta kNN aproximada</a> nos bastidores.</p>POST games/_search
{
  "query" : {
    "knn": {
      "field": "vector",
      "query_vector": [-0.019137882, /* ... */, -0.0148779955]
    }
  }
}<p>Mas isso poderia ser alterado fornecendo uma configuração diferente (<code>ElasticsearchConfigurationScript</code>) da configuração padrão (<code>ElasticsearchConfigurationKnn</code>) para o armazenamento de incorporação:</p>EmbeddingStore&lt;TextSegment&gt; embeddingStore =
  ElasticsearchEmbeddingStore.builder()
    .configuration(ElasticsearchConfigurationScript.builder().build())
    .indexName("games")
    .restClient(client)
    .build();<p>A implementação <code>ElasticsearchConfigurationScript</code> executa em segundo plano uma <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.15/query-dsl-script-score-query.html">consulta</a> <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.15/query-dsl-script-score-query.html"><code>script_score</code></a> usando uma <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.15/query-dsl-script-score-query.html#vector-functions-cosine">função</a> <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.15/query-dsl-script-score-query.html#vector-functions-cosine"><code>cosineSimilarity</code></a> .</p><p>Basicamente, ao ligar:</p>EmbeddingSearchResult&lt;TextSegment&gt; result = embeddingStore.search(
  EmbeddingSearchRequest.builder()
    .queryEmbedding(questionAsVector)
    .build());<p>Isto agora exige:</p>POST games/_search
{
  "query": {
    "script_score": {
      "script": {
        "source": "(cosineSimilarity(params.query_vector, 'vector') + 1.0) / 2",
        "params": {
          "queryVector": [-0.019137882, /* ... */, -0.0148779955]
        }
      }
    }
  }
}<p>Nesse caso, o resultado não muda em termos de "ordem", apenas a pontuação é ajustada, pois a chamada <code>cosineSimilarity</code> não usa nenhuma aproximação, mas calcula o cosseno para cada um dos vetores correspondentes:</p>Out Run - score [0.871952]
The Secret of Monkey Island - score [0.86380446]<p>Se você quiser executar este código, consulte a classe <a href="https://github.com/dadoonet/langchain4j-demo/blob/9ec4b1d4c7c69821f143ddf272bbfed273c67b14/src/test/java/fr/pilato/demo/Step7SearchForVectorsTest.java#L132-L155">Step7SearchForVectorsTest.java</a> .</p><h2>Conclusão</h2><p>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:</p><ul><li><p>Usando a consulta aproximada e rápida <code>knn</code> com a opção padrão <code>ElasticsearchConfigurationKnn</code></p></li><li><p>Usando a consulta exata, porém mais lenta, <code>script_score</code> com a opção <code>ElasticsearchConfigurationScript</code></p></li></ul><p>O próximo passo será construir uma aplicação RAG completa, com base no que aprendemos aqui.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/langchain4j-elasticsearch-embedding-store</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/langchain4j-elasticsearch-embedding-store</guid>
    <category><![CDATA[Java]]></category>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <dc:creator><![CDATA[David Pilato]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfc873b86c76d1798/6a170293acf088f666be99b3/abd8a4a809064101c037af66b87f28e5ecde03b0-1474x645.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 08 Oct 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Testando seu código Java com mocks e Elasticsearch real.]]></title>
    <description><![CDATA[Aprenda a escrever seus testes automatizados para Elasticsearch, usando mocks e Testcontainers.]]></description>
    <content:encoded><![CDATA[<p>Neste artigo, apresentaremos e explicaremos duas maneiras de testar software usando o Elasticsearch como uma dependência externa do sistema. Vamos abordar testes usando mocks, bem como testes de integração, mostrar algumas diferenças práticas entre eles e dar algumas dicas sobre o que fazer com cada estilo.</p><h2>Bons testes para avaliar a confiabilidade do sistema.</h2><p>Um bom teste é aquele que aumenta a confiança de todas as pessoas envolvidas no processo de criação e manutenção de um sistema de TI. Os testes não devem ser pensados para serem modernos, rápidos ou para aumentar artificialmente a cobertura de código. Os testes desempenham um papel vital para garantir que:</p><ul><li><p>O que queremos entregar é algo que funcione na produção.</p></li><li><p>O sistema satisfaz os requisitos e os contratos.</p></li><li><p>Não haverá regressões no futuro.</p></li><li><p>Os desenvolvedores (e outros membros da equipe envolvidos) estão confiantes de que o que criaram funcionará.</p></li></ul><p>É claro que isso não significa que os testes não possam ser interessantes, rápidos ou aumentar a cobertura de código. Quanto mais rápido pudermos executar nosso conjunto de testes, melhor. A questão é que, na busca por reduzir a duração total do conjunto de testes, não devemos sacrificar a confiabilidade, a facilidade de manutenção e a segurança que os testes automatizados nos proporcionam.</p><p>Bons testes automatizados aumentam a confiança dos membros da equipe:</p><ul><li><p>Desenvolvedores: eles conseguem confirmar que o que estão fazendo funciona (mesmo antes que o código em que trabalham saia de suas máquinas).</p></li><li><p>Equipe de garantia da qualidade: eles têm menos coisas para testar manualmente.</p></li><li><p>Os operadores de sistemas e os SREs (Engenheiros de Confiabilidade de Site) estão mais tranquilos, pois os sistemas são mais fáceis de implantar e manter.</p></li></ul><p>Por último, mas não menos importante: a arquitetura de um sistema. Adoramos quando os sistemas são organizados, fáceis de manter e a arquitetura é limpa e cumpre sua função. No entanto, às vezes podemos nos deparar com uma arquitetura que sacrifica demais em nome da desculpa conhecida como "assim é mais fácil de testar". Não há nada de errado em ser altamente testável – o problema surge quando o sistema é escrito principalmente para ser testável, em vez de atender às necessidades que justificam sua existência. É nesse momento que vemos a cauda abanando o cachorro.</p><h2>Existem dois tipos de testes: simulações (mocks) e testes de dependência.</h2><p>Existem muitas maneiras de visualizar os testes e, portanto, classificá-los. Neste post, vou me concentrar em apenas um aspecto da divisão dos testes: usar mocks (ou stubs, ou fakes, ou ...) versus usar dependências reais. No nosso caso, a dependência é o Elasticsearch.</p><p>Os testes que utilizam mocks são muito rápidos porque não precisam iniciar nenhuma dependência externa e tudo acontece apenas na memória. Em testes automatizados, o "mocking" consiste na utilização de objetos falsos em vez de objetos reais para testar partes de um programa sem usar as dependências reais. É por isso que são necessários e por isso que se destacam em qualquer teste de rede de detecção rápida, por exemplo. Validação de entrada. Não é necessário iniciar um banco de dados e fazer uma chamada a ele apenas para verificar se números negativos em uma solicitação não são permitidos, por exemplo.</p><p>No entanto, a introdução de simulações tem várias implicações:</p><ul><li><p>Nem tudo e em todas as situações pode ser facilmente simulado, portanto, as simulações têm impacto na arquitetura do sistema (o que às vezes é ótimo, outras vezes nem tanto).</p></li><li><p>Os testes executados em mocks podem ser rápidos, mas o desenvolvimento desses testes pode levar bastante tempo, pois os mocks que refletem fielmente os sistemas que imitam geralmente não são fornecidos gratuitamente. Quem conhece o funcionamento do sistema precisa escrever os mocks da maneira correta, e esse conhecimento pode vir da experiência prática, do estudo da documentação e assim por diante.</p></li><li><p>É necessário manter os simulados. Quando seu sistema depende de uma dependência externa e você precisa atualizar essa dependência, alguém precisa garantir que os mocks que a reproduzem também sejam atualizados com todas as alterações: alterações que quebram a compatibilidade, alterações documentadas e alterações não documentadas (que também podem ter impacto em nosso sistema). Isso se torna especialmente problemático quando você deseja atualizar uma dependência, mas seu conjunto de testes (que usa apenas mocks) não consegue garantir que todos os casos testados funcionarão corretamente.</p></li><li><p>É preciso disciplina para garantir que o esforço seja direcionado para o desenvolvimento e teste do sistema, e não para simulações.</p></li></ul><p>Por essas razões, muitas pessoas defendem seguir exatamente na direção oposta: nunca usar mocks (ou stubs, etc.), mas confiar exclusivamente em dependências reais. Essa abordagem funciona muito bem em demonstrações ou quando o sistema é pequeno e possui apenas alguns casos de teste que geram uma cobertura enorme. Esses testes podem ser testes de integração (grosso modo: verificar uma parte de um sistema em relação a algumas dependências reais) ou testes de ponta a ponta (usando todas as dependências reais ao mesmo tempo e verificando o comportamento do sistema em todas as suas extremidades, enquanto se reproduzem fluxos de trabalho do usuário que definem o sistema como utilizável e bem-sucedido). Uma clara vantagem de usar essa abordagem é que também verificamos (muitas vezes sem intenção) nossas suposições sobre as dependências e como as integramos ao sistema em que estamos trabalhando.</p><p>No entanto, quando os testes utilizam apenas dependências reais, precisamos considerar os seguintes aspectos:</p><ul><li><p>Alguns cenários de teste não precisam da dependência real (por exemplo, para verificar as invariantes estáticas de uma solicitação).</p></li><li><p>Normalmente, esses testes não são executados em conjuntos completos nas máquinas dos desenvolvedores, porque esperar por feedback levaria muito tempo.</p></li><li><p>Elas exigem mais recursos nas máquinas de CI e pode levar mais tempo para ajustar tudo e evitar desperdício de tempo e recursos.</p></li><li><p>Pode não ser trivial inicializar dependências com dados de teste.</p></li><li><p>Testes com dependências reais são ótimos para isolar o código antes de grandes refatorações, migrações ou atualizações de dependências.</p></li><li><p>É mais provável que sejam testes opacos, ou seja, que não entrem em detalhes sobre o funcionamento interno do sistema em teste, mas que se preocupem com os resultados.</p></li></ul><h2>O ponto ideal: use ambos os testes.</h2><p>Em vez de testar seu sistema com apenas um tipo de teste, você pode usar ambos os tipos quando fizer sentido e tentar melhorar o uso de ambos.</p><ul><li><p>Execute primeiro os testes baseados em mocks, pois são muito mais rápidos, e somente depois que todos forem bem-sucedidos, execute os testes de dependência, que são mais lentos.</p></li><li><p>Escolha mocks para cenários onde dependências externas não são realmente necessárias: quando a criação de mocks levaria muito tempo, o código deve ser alterado drasticamente apenas para isso; dependa de dependências externas.</p></li><li><p>Não há nada de errado em testar um trecho de código usando ambas as abordagens, desde que faça sentido.</p></li></ul><h2>Exemplo de SistemaEmTeste</h2><p>Nas próximas seções, usaremos um exemplo que pode ser encontrado <a href="https://github.com/pioorg/testing-elasticsearch">aqui</a>. Trata-se de uma pequena aplicação de demonstração escrita em Java 21, utilizando o Maven como ferramenta de compilação, dependendo do cliente Elasticsearch e utilizando a mais recente adição ao Elasticsearch, <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql.html">o ES|QL</a> (a nova linguagem de consulta procedural da Elastic). Se Java não é a sua linguagem de programação, você ainda poderá entender os conceitos que discutiremos a seguir e adaptá-los à sua pilha de tecnologias. O simples fato de usar um exemplo de código real facilita a explicação de certas coisas.</p><p>O <code>BookSearcher</code> nos ajuda a lidar com a pesquisa e a analisar dados, que no nosso caso são livros (como demonstrado em <a href="https://www.elastic.co/search-labs/blog/esql-queries-to-java-objects">uma das postagens anteriores</a>).</p><ul><li><p>Ele requer o Elasticsearch exatamente na versão <code>8.15.x</code> como sua única dependência (veja <code>isCompatibleWithBackend()</code>), por exemplo, porque não temos certeza se nosso código é compatível com versões futuras e temos certeza de que não é compatível com versões anteriores. Antes de atualizar o Elasticsearch em produção para uma versão mais recente, primeiro o atualizaremos nos testes para garantir que o comportamento do Sistema em Teste permaneça o mesmo.</p></li><li><p>Podemos usá-lo para pesquisar o número de livros publicados em um determinado ano (ver <code>numberOfBooksPublishedInYear</code>).</p></li><li><p>Também podemos usá-lo quando precisamos analisar nosso conjunto de dados e descobrir os 20 autores mais publicados entre dois anos determinados (ver <code>mostPublishedAuthorsInYears</code>).</p></li></ul>public class BookSearcher {

    private final ElasticsearchClient esClient;

    public BookSearcher(ElasticsearchClient esClient) {
        this.esClient = esClient;
        if (!isCompatibleWithBackend()) {
            throw new UnsupportedOperationException("This is not compatible with backend");
        }
    }

    private boolean isCompatibleWithBackend() {
        try (ResultSet rs = esClient.esql().query(ResultSetEsqlAdapter.INSTANCE, """
            show info
            | keep version
            | dissect version "%{major}.%{minor}.%{patch}"
            | keep major, minor
            | limit 1""")) {
            if (!rs.next()) {
                throw new RuntimeException("No version found");
            }
            return rs.getInt(1) == 8 &amp;&amp; rs.getInt(2) == 15;
        } catch (SQLException | IOException e) {
            throw new RuntimeException(e);
        }
    }

    public int numberOfBooksPublishedInYear(int year) {
        try (ResultSet rs = esClient.esql().query(ResultSetEsqlAdapter.INSTANCE, """
            from books
            | where year == ?
            | stats published = count(*) by year
            | limit 1000""", year)) {

            if (rs.next()) {
                return rs.getInt("published");
            }
        } catch (SQLException | IOException e) {
            throw new RuntimeException(e);
        }
        return 0;
    }


    public List&lt;MostPublished&gt; mostPublishedAuthorsInYears(int minYear, int maxYear) {
        assert minYear &lt;= maxYear;
        String query = """
            from books
            | where year &gt;= ? and year &lt;= ?
            | stats first_published = min(year), last_published = max(year), times = count (*) by author
            | eval years_published = last_published - first_published
            | sort years_published desc
            | drop years_published
            | limit 20
            """;

        try {
            Iterable&lt;MostPublished&gt; published = esClient.esql().query(
                ObjectsEsqlAdapter.of(MostPublished.class),
                query,
                minYear,
                maxYear);

            List&lt;MostPublished&gt; mostPublishedAuthors = new ArrayList&lt;&gt;();
            for (MostPublished mostPublished : published) {
                mostPublishedAuthors.add(mostPublished);
            }
            return mostPublishedAuthors;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public record MostPublished(
        String author,
        @JsonProperty("first_published") int firstPublished,
        @JsonProperty("last_published") int lastPublished,
        int times
    ) {
        public MostPublished {
            assert author != null;
            assert firstPublished &lt;= lastPublished;
            assert times &gt; 0;
        }
    }
}
<h2>Faça testes com simulações para começar.</h2><p>Para criar os mocks usados em nossos testes, vamos usar <a href="https://site.mockito.org/">o Mockito</a>, uma biblioteca de mocks muito popular no ecossistema Java.</p><p>Podemos começar com o seguinte, para que os mocks sejam reinicializados antes de cada teste:</p>public class BookSearcherMockingTest {

    ResultSet mockResultSet;
    ElasticsearchClient esClient;
    ElasticsearchEsqlClient esql;

    @BeforeEach
    void setUpMocks() {
        mockResultSet = mock(ResultSet.class);
        esClient = mock(ElasticsearchClient.class);
        esql = mock(ElasticsearchEsqlClient.class);

    }
}
<p>Como dissemos anteriormente, nem tudo pode ser facilmente testado usando mocks. Mas há coisas que podemos (e provavelmente até devemos) fazer. Vamos verificar se a única versão do Elasticsearch suportada é <code>8.15.x</code> por enquanto (no futuro, poderemos ampliar o intervalo assim que confirmarmos que nosso sistema é compatível com versões futuras):</p>@Test
void canCreateSearcherWithES_8_15() throws SQLException, IOException{
    // when
    when(esClient.esql()).thenReturn(esql);
    when(esql.query(eq(ResultSetEsqlAdapter.INSTANCE), anyString())).thenReturn(mockResultSet);
    when(mockResultSet.next()).thenReturn(true).thenReturn(false);
    when(mockResultSet.getInt(1)).thenReturn(8);
    when(mockResultSet.getInt(2)).thenReturn(15);

    // then
    Assertions.assertDoesNotThrow(() -&gt; new BookSearcher(esClient));
}
<p>Podemos verificar de forma semelhante (simplesmente retornando uma versão secundária diferente) que nosso <code>BookSearcher</code> ainda não funcionará com <code>8.16.x</code> , porque não temos certeza se será compatível com ele:</p>@Test
void cannotCreateSearcherWithoutES_8_15() throws SQLException, IOException {
    // when
    when(esClient.esql()).thenReturn(esql);
    when(esql.query(eq(ResultSetEsqlAdapter.INSTANCE), anyString())).thenReturn(mockResultSet);
    when(mockResultSet.next()).thenReturn(true).thenReturn(false);
    when(mockResultSet.getInt(1)).thenReturn(8);
    when(mockResultSet.getInt(2)).thenReturn(16);

    // then
    Assertions.assertThrows(UnsupportedOperationException.class, () -&gt; new BookSearcher(esClient));
}
<p>Agora vejamos como podemos alcançar algo semelhante ao testar com um Elasticsearch real. Para isso, vamos usar <a href="https://java.testcontainers.org/modules/elasticsearch/">o módulo Elasticsearch da Testcontainers</a>, que tem apenas um requisito: precisa de acesso ao Docker, pois executa contêineres Docker para você. De certo ponto de vista, o Testcontainers é simplesmente uma forma de operar contêineres Docker, mas em vez de fazer isso no seu Docker Desktop (ou similar), na sua CLI ou em scripts, você pode expressar suas necessidades na linguagem de programação que você conhece. Isso possibilita buscar imagens, iniciar contêineres, coletar o lixo após os testes, copiar arquivos de um lado para o outro, executar comandos, examinar logs, etc., diretamente do seu código de teste.</p><p>O esboço pode ter esta aparência:</p>@Testcontainers
public class BookSearcherIntTest {

    static final String ELASTICSEARCH_IMAGE = "docker.elastic.co/elasticsearch/elasticsearch:8.15.0";
    static final JacksonJsonpMapper JSONP_MAPPER = new JacksonJsonpMapper();

    RestClientTransport transport;
    ElasticsearchClient client;

    @Container
    ElasticsearchContainer elasticsearch = new ElasticsearchContainer(ELASTICSEARCH_IMAGE);

    @BeforeEach
    void setupClient() {
        transport = // setup transport here
        client = new ElasticsearchClient(transport);
    }

    @AfterEach
    void closeClient() throws IOException {
        if (transport != null) {
            transport.close();
        }
    }

}
<p>Neste exemplo, contamos com <a href="https://java.testcontainers.org/test_framework_integration/junit_5/">a integração do Testcontainers com o JUnit</a> , <code>@Testcontainers</code> e <code>@Container</code>, o que significa que não precisamos nos preocupar em iniciar o Elasticsearch antes de nossos testes e pará-lo depois. A única coisa que precisamos fazer é criar o cliente antes de cada teste e fechá-lo após cada teste (para evitar vazamentos de recursos, que poderiam afetar conjuntos de testes maiores).</p><p>Anotar um campo não estático com <code>@Container</code> significa que um novo contêiner será iniciado para cada teste, portanto não precisamos nos preocupar com dados desatualizados ou com a reinicialização do estado do contêiner. No entanto, em muitos testes, essa abordagem pode não apresentar um bom desempenho, por isso vamos compará-la com alternativas em uma das próximas publicações.</p><p><strong>Observação:</strong></p>Ao utilizar o <code>docker.elastic.co</code> (repositório oficial de imagens Docker da Elastic), você evita atingir seus limites no Docker Hub.

Recomenda-se também usar a mesma versão da sua dependência nos ambientes de teste e de produção, para garantir a máxima compatibilidade. Recomendamos também que você seja preciso ao selecionar a versão, por esse motivo, não há tag <code>latest</code> para imagens do Elasticsearch.<h2>Conectando-se ao Elasticsearch em testes</h2><p><a href="https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/index.html">O cliente Java do Elasticsearch</a> é capaz de se conectar ao Elasticsearch em execução em um contêiner de teste, mesmo com segurança e SSL/TLS habilitados (que são padrão nas versões 8.x, por isso não precisamos especificar nada relacionado à segurança na declaração do contêiner). Partindo do pressuposto que o Elasticsearch que você está usando em produção também tenha TLS e alguns recursos de segurança habilitados, recomenda-se configurar os testes de integração o mais próximo possível do cenário de produção e, portanto, não desabilitá-los nos testes.</p><p>Como obter os dados necessários para a conexão, assumindo que o contêiner está atribuído ao campo ou variável <code>elasticsearch</code>:</p><ul><li><p><code>elasticsearch.getHost()</code> irá fornecer o host no qual o contêiner está sendo executado (que na maioria das vezes provavelmente será <code>"localhost"</code>, mas, por favor, não codifique isso diretamente, pois às vezes, dependendo da sua configuração, pode ser outro nome; portanto, o host deve sempre ser obtido dinamicamente).</p></li><li><p><code>elasticsearch.getMappedPort(9200)</code> irá fornecer a porta do host que você precisa usar para se conectar ao Elasticsearch em execução dentro do contêiner (porque cada vez que você inicia o contêiner, a porta externa é diferente, então esta também precisa ser uma chamada dinâmica).</p></li><li><p>A menos que tenham sido sobrescritos, o nome de usuário e a senha padrão são <code>"elastic"</code> e <code>"changeme"</code> , respectivamente.</p></li><li><p>Caso nenhum certificado SSL/TLS tenha sido especificado durante a configuração do contêiner e a conectividade segura não esteja desativada (que é o comportamento padrão a partir das versões 8.x), um certificado autoassinado será gerado. Confiar nisso (por exemplo) como <a href="https://curl.se/docs/manpage.html#--cacert">o cURL pode fazer</a>) o certificado pode ser obtido usando <code>elasticsearch.caCertAsBytes()</code> (que retorna <code>Optional&lt;byte[]&gt;</code>), ou outra maneira conveniente é obter <code>SSLContext</code> usando <code>createSslContextFromCa()</code>.</p></li></ul><p>O resultado geral pode ser semelhante a este:</p>BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("elastic", "changeme"));

// Create a low level rest client
RestClient restClient = RestClient.builder(new HttpHost(elasticsearch.getHost(), elasticsearch.getMappedPort(9200), "https"))
    .setHttpClientConfigCallback(httpClientBuilder -&gt;
        httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)
            .setSSLContext(elasticsearch.createSslContextFromCa())
    )
    .build();

// The RestClientTransport is mainly for serialization/deserialization
RestClientTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());

// The official Java API Client for Elasticsearch
ElasticsearchClient client = new ElasticsearchClient(transport);
<p>Outro exemplo de criação de uma instância de <code>ElasticsearchClient</code> pode ser encontrado no <a href="https://github.com/pioorg/testing-elasticsearch/blob/e800b4b2ab3d706efcafb9a8182480e69e475b86/src/test/java/testing_elasticsearch/BookSearcherIntTest.java#L61">projeto de demonstração</a>.</p><p><strong>Observação</strong>:</p>Para criar um cliente em ambientes de produção, consulte <a href="https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/connecting.html#_verifying_https_with_a_certificate_fingerprint">a documentação</a>.<h2>Primeiro teste de integração</h2><p>Nosso primeiro teste, para verificar se podemos criar <code>BookSearcher</code> usando o Elasticsearch versão 8.15.x, pode ser algo como:</p>@Test
void canCreateClientWithContainerRunning_8_15() {
    Assertions.assertDoesNotThrow(() -&gt; new BookSearcher(client));
}
<p>Como podem ver, não precisamos configurar mais nada. Não precisamos simular a versão retornada pelo Elasticsearch, a única coisa que precisamos fazer é fornecer <code>BookSearcher</code> com um cliente conectado a uma instância real do Elasticsearch, que foi iniciada para nós pelo Testcontainers.</p><h2>Os testes de integração se preocupam menos com os detalhes internos.</h2><p>Vamos fazer uma pequena experiência: vamos supor que temos que parar de extrair dados do conjunto de resultados usando índices de coluna, e sim usar os nomes das colunas. Então, no método <code>isCompatibleWithBackend</code> em vez de</p>return rs.getInt(1) == 8 &amp;&amp; rs.getInt(2) == 15;
<p>Teremos:</p>return rs.getInt("major") == 8 &amp;&amp; rs.getInt("minor") == 15;
<p>Ao executarmos novamente os dois testes, notaremos que o teste de integração com o Elasticsearch real ainda é aprovado sem problemas. No entanto, os testes usando mocks pararam de funcionar, porque simulamos chamadas como <code>rs.getInt(int)</code>, não <code>rs.getInt(String)</code>. Para que sejam aprovados, agora precisamos ou simulá-los, ou simulá-los ambos, dependendo de outros casos de uso que temos em nosso conjunto de testes.</p><h2>Testes de integração podem ser como um canhão para matar uma mosca.</h2><p>Os testes de integração são capazes de verificar o comportamento do sistema, mesmo que não haja necessidade de dependências externas. No entanto, usá-los dessa forma geralmente resulta em desperdício de tempo e recursos de execução. Vamos analisar o método <code>mostPublishedAuthorsInYears(int minYear, int maxYear)</code>. As duas primeiras linhas são as seguintes:</p>assert minYear &lt;= maxYear;
String query = // here goes the query
<p>A primeira declaração verifica uma condição que não depende do Elasticsearch (ou de qualquer outra dependência externa) de forma alguma. Portanto, não precisamos iniciar nenhum contêiner apenas para verificar se <code>minYear</code> é maior que <code>maxYear</code>, uma exceção é lançada.</p><p>Um teste de simulação simples, que também seja rápido e não consuma muitos recursos, é mais do que suficiente para garantir isso. Após configurar os mocks, podemos simplesmente prosseguir para:</p>BookSearcher systemUnderTest = new BookSearcher(esClient);

Assertions.assertThrows(
    AssertionError.class,
    () -&gt; systemUnderTest.mostPublishedAuthorsInYears(2012, 2000)
);
<p>Iniciar uma dependência, em vez de usar um mock, seria um desperdício <a href="https://github.com/pioorg/testing-elasticsearch/blob/e800b4b2ab3d706efcafb9a8182480e69e475b86/src/test/java/testing_elasticsearch/BookSearcherMockingTest.java#L89">neste caso de teste,</a> pois não há chance de fazer uma chamada significativa para essa dependência.</p><p>No entanto, para verificar o comportamento a partir de <code>String query = ...</code>, que a consulta está escrita corretamente, os resultados são os esperados: a biblioteca cliente é capaz de enviar solicitações e respostas adequadas, não há alterações de sintaxe e, portanto, é muito mais fácil usar um teste de integração, por exemplo:</p>@BeforeEach
void setupDataInContainer() {
    // here we initialise data in the Elasticsearch running in a container
}

@Test
void shouldGiveMostPublishedAuthorsInGivenYears() {
    var systemUnderTest = new BookSearcher(client);
    var list = systemUnderTest.mostPublishedAuthorsInYears(1800, 2010);
    Assertions.assertEquals("Beatrix Potter", list.get(12).author(), "Beatrix Potter was 13th most published author between 1800 and 2010");
}
<p>Dessa forma, podemos ter certeza de que, ao enviar nossos dados para o Elasticsearch (nesta ou em qualquer versão futura para a qual optarmos por migrar), nossa consulta nos dará exatamente o que esperamos: o formato dos dados não mudou, a consulta ainda é válida e todo o middleware (clientes, drivers, segurança etc.) continuará funcionando. Não precisamos nos preocupar em manter os mocks atualizados; a única alteração necessária é garantir a compatibilidade com, por exemplo, <code>8.15</code> alteraria isto:</p>static final String ELASTICSEARCH_IMAGE = "docker.elastic.co/elasticsearch/elasticsearch:8.15.0";
<p>O mesmo acontece se você decidir, por exemplo, Use a boa e velha QueryDSL em vez de ES|QL: os resultados que você receber da consulta (independentemente da linguagem) ainda serão os mesmos.</p><h2>Utilize ambas as abordagens quando necessário.</h2><p>O caso do método <code>mostPublishedAuthorsInYears</code> ilustra que um único método pode ser testado usando ambos os métodos. E talvez até devesse ser.</p><ul><li><p>Usar apenas mocks significa que temos que manter o mock e não temos nenhuma confiança ao atualizar nosso sistema.</p></li><li><p>Utilizar apenas testes de integração significaria um desperdício de muitos recursos, sem necessidade.</p></li></ul><h2>Vamos recapitular</h2><ul><li><p>É possível usar tanto testes de simulação (mocking) quanto testes de integração com o Elasticsearch.</p></li><li><p>Use testes de mocking como fast-detection-net e somente se eles passarem com sucesso, inicie os testes com dependências (por exemplo, usando <code>./mvnw test '-Dtest=!TestInt*' &amp;&amp; ./mvnw test '-Dtest=TestInt*'</code> ou os plugins <a href="https://maven.apache.org/surefire/maven-failsafe-plugin/">Failsafe</a> e <a href="https://maven.apache.org/surefire/maven-surefire-plugin/">Surefire</a> ).</p></li><li><p>Use mocks ao testar o comportamento do seu sistema ("linhas de código") onde a integração com dependências externas não é realmente importante (ou pode até ser ignorada).</p></li><li><p>Utilize testes de integração para verificar suas suposições sobre a integração com sistemas externos.</p></li><li><p>Não tenha receio de testar ambas as abordagens – se fizer sentido – de acordo com os pontos acima.</p></li></ul><p>Poderíamos observar que ser tão rigoroso quanto à versão (no nosso caso, <code>8.15.x</code>) é excessivo. Usar apenas a tag de versão poderia ser suficiente, mas lembre-se de que, neste post, ela representa todos os outros recursos que podem mudar entre as versões.</p><p>Na <a href="https://www.elastic.co/search-labs/blog/automated-integration-tests-faster-elasticsearch">próxima parte desta série</a>, veremos maneiras de inicializar o Elasticsearch em um contêiner de teste, com conjuntos de dados de teste. Informe-nos se você construiu algo com base neste blog ou se tiver dúvidas em nossos <a href="https://discuss.elastic.co/">fóruns de discussão</a> e <a href="https://communityinviter.com/apps/elasticstack/elastic-community">no canal da comunidade no Slack</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/tests-with-mocks-and-real-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/tests-with-mocks-and-real-elasticsearch</guid>
    <category><![CDATA[Java]]></category>
    <dc:creator><![CDATA[Piotr Przybyl]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7e4d003f09dfbe50/6a1709aba929cf810aae0957/b6bb727815ebdb844aeb36d5c44cdf3657f0e4bc-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 03 Oct 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Apresentamos o LangChain4j para simplificar a integração do LLM em aplicações Java.]]></title>
    <description><![CDATA[LangChain4j (LangChain para Java) é um conjunto de ferramentas poderoso para construir sua aplicação RAG em Java puro.]]></description>
    <content:encoded><![CDATA[<p>O <a href="https://docs.langchain4j.dev/">framework LangChain4j</a> foi criado em 2023 com <a href="https://github.com/langchain4j/langchain4j/blob/main/README.md#introduction">este objetivo</a>:</p>O objetivo do LangChain4j é simplificar a integração de LLMs em aplicações Java.<p>LangChain4j oferece uma maneira padronizada de:</p><ul><li><p>Criar representações vetoriais (embeddings) a partir de um determinado conteúdo, digamos, um texto, por exemplo.</p></li><li><p>armazenar embeddings em um armazenamento de embeddings</p></li><li><p>pesquisar vetores semelhantes no armazenamento de embeddings</p></li><li><p>discutir com mestres em direito</p></li><li><p>Use uma memória de bate-papo para lembrar o contexto de uma discussão com um mestre em Direito.</p></li></ul><p>Esta lista não é exaustiva e a comunidade LangChain4j está sempre implementando novos recursos.</p><p>Esta postagem abordará as primeiras partes principais da estrutura.</p><h2>Adicionando LangChain4j OpenAI ao nosso projeto</h2><p>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.</p><p>Como primeiro passo para o projeto que queremos construir aqui, usaremos o OpenAI, então precisamos apenas adicionar o artefato <code>langchain4j-open-ai</code> :</p>&lt;properties&gt;
  &lt;langchain4j.version&gt;0.34.0&lt;/langchain4j.version&gt;
&lt;/properties&gt;

&lt;dependencies&gt;
  &lt;dependency&gt;
    &lt;groupId&gt;dev.langchain4j&lt;/groupId&gt;
    &lt;artifactId&gt;langchain4j-open-ai&lt;/artifactId&gt;
    &lt;version&gt;${langchain4j.version}&lt;/version&gt;
  &lt;/dependency&gt;
&lt;/dependencies&gt;
<p>Para o restante do código, usaremos nossa própria chave de API, que você pode obter criando uma conta na <a href="https://platform.openai.com/signup/">OpenAI</a>, ou a fornecida pelo projeto LangChain4j apenas para fins de demonstração:</p>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;
}
<p>Agora podemos criar uma instância do nosso ChatLanguageModel:</p>ChatLanguageModel model = OpenAiChatModel.withApiKey(getOpenAiApiKey());
<p>E, finalmente, podemos fazer uma pergunta simples e obter a resposta:</p>String answer = model.generate("Who is Thomas Pesquet?");
Logger.info("Answer is: {}", answer);
<p>A resposta fornecida poderia ser algo como:</p>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.
<p>Se você quiser executar este código, consulte a classe <a href="https://github.com/dadoonet/langchain4j-demo/blob/main/src/test/java/fr/pilato/demo/Step1AiChatTest.java">Step1AiChatTest.java</a> .</p><h2>Fornecendo mais contexto com langchain4j</h2><p>Vamos adicionar o artefato <code>langchain4j</code> :</p>&lt;dependency&gt;
  &lt;groupId&gt;dev.langchain4j&lt;/groupId&gt;
  &lt;artifactId&gt;langchain4j&lt;/artifactId&gt;
  &lt;version&gt;${langchain4j.version}&lt;/version&gt;
&lt;/dependency&gt;
<p>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 <code>Assistant</code> que fornece o método <code>chat</code> , o qual chamará automaticamente o <code>ChatLanguageModel</code> que definimos anteriormente:</p>interface Assistant {
  String chat(String userMessage);
}
<p>Basta pedirmos à classe LangChain4j <code>AiServices</code> para construir uma instância para nós:</p>Assistant assistant = AiServices.create(Assistant.class, model);
<p>Em seguida, chame o método <code>chat(String)</code> :</p>String answer = assistant.chat("Who is Thomas Pesquet?");
Logger.info("Answer is: {}", answer);
<p>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:</p>interface Assistant {
  @SystemMessage("Please answer in a funny way.")
  String chat(String userMessage);
}
<p>Isso está resultando em:</p>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?
<p>Se você quiser executar este código, consulte a classe <a href="https://github.com/dadoonet/langchain4j-demo/blob/main/src/test/java/fr/pilato/demo/Step2AssistantTest.java">Step2AssistantTest.java</a> .</p><h2>Mudando para outro LLM: langchain4j-ollama</h2><p>Podemos usar o excelente <a href="https://ollama.com/">projeto Ollama</a>. Executar um LLM localmente em sua máquina pode ser útil.</p><p>Vamos adicionar o artefato <code>langchain4j-ollama</code> :</p>&lt;dependency&gt;
  &lt;groupId&gt;dev.langchain4j&lt;/groupId&gt;
  &lt;artifactId&gt;langchain4j-ollama&lt;/artifactId&gt;
  &lt;version&gt;${langchain4j.version}&lt;/version&gt;
&lt;/dependency&gt;
<p>Como estamos executando o código de exemplo usando testes, vamos adicionar <a href="https://java.testcontainers.org/">Testcontainers</a> ao nosso projeto:</p>&lt;dependency&gt;
  &lt;groupId&gt;org.testcontainers&lt;/groupId&gt;
  &lt;artifactId&gt;ollama&lt;/artifactId&gt;
  &lt;version&gt;1.20.1&lt;/version&gt;
  &lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
<p>Agora podemos iniciar/parar contêineres Docker:</p>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();
}
<p>Nós "apenas" precisamos mudar o objeto <code>model</code> para se tornar um <code>OllamaChatModel</code> em vez do <code>OpenAiChatModel</code> que usamos anteriormente:</p>OllamaChatModel model = OllamaChatModel.builder()
  .baseUrl(ollama.getEndpoint())
  .modelName(MODEL_NAME)
  .build();
<p>Observe que pode levar algum tempo para obter a imagem com seu modelo, mas depois de um tempo, você poderá obter a resposta:</p>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
<h2>Melhor com memória</h2><p>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á:</p>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.
<p>O que é um absurdo. Em vez disso, devemos usar <a href="https://docs.langchain4j.dev/tutorials/chat-memory">a Memória de Chat</a>:</p>ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
Assistant assistant = AiServices.builder(Assistant.class)
  .chatLanguageModel(model)
  .chatMemory(chatMemory)
  .build();
<p>Executar as mesmas perguntas agora fornece uma resposta significativa:</p>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*
<h2>Conclusão</h2><p>Na <a href="https://www.elastic.co/search-labs/blog/langchain4j-elasticsearch-embedding-store">próxima publicação</a>, 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.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/langchain4j-llm-integration-introduction</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/langchain4j-llm-integration-introduction</guid>
    <category><![CDATA[Java]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[David Pilato]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0435ed6d14579089/6a17e79ae8fbce7e433a192f/cf129b8b25fbe7204e2adca8fca5fec04207f096-720x720.png" length="0" type="image/png"/>
    <pubDate>Mon, 23 Sep 2024 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>