<?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[Banco de dados vetorial - 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[Banco de dados vetorial - 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/vector-database</link>
    </image>
    <link>https://www.elastic.co/pt/search-labs/blog/category/vector-database</link>
    <atom:link href="https://www.elastic.co/pt/search-labs/rss/category/vector-database.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[pt]]></language>
    <lastBuildDate>Tue, 22 Sep 2026 12:26:37 GMT</lastBuildDate>
  <item>
    <title><![CDATA[Banco de dados vetorial do Elasticsearch: envie em minutos, redimensione de forma acessível para centenas de bilhões]]></title>
    <description><![CDATA[As partes mais difíceis da recuperação híbrida, já prontas, com padrões otimizados, modelos de terceiros e nativos da Jina AI e inferência em GPU gerenciada, tudo pronto para uso. Crie apps de IA rápidos e escaláveis, não infraestrutura.]]></description>
    <content:encoded><![CDATA[<p>O Elasticsearch é uma das plataformas mais implantadas do mundo para cargas de trabalho vetoriais, potencializando a busca semântica, a Retrieval-Augmented Generation (RAG) e recomendações para empresas como GitHub, Docusign, Seismic e muitas outras. Hoje, estamos anunciando o Banco de dados vetorial do Elasticsearch, uma nova oferta sem servidor otimizada para aplicações baseadas em vetores. Você fornece seus documentos e suas consultas, e nós cuidamos dos embeddings, do ajuste de índices e da infraestrutura. Além disso, mantemos tudo econômico e escalável. </p><p>Para novos usuários, esta é a maneira mais rápida de colocar a busca vetorial de alta qualidade em funcionamento. Se você já usa o Elasticsearch, a nova oferta é a busca vetorial na plataforma onde seus dados já estão, sem nenhum novo sistema para adotar. O Elasticsearch Vector Database oferece suporte a uma variedade de cenários, desde a fundamentação de um grande modelo de linguagem (LLM) e o fornecimento de recuperação e memória para um agente de IA até o atendimento de centenas de bilhões de vetores. <a href="https://cloud.elastic.co/registration?onboarding_token=vector">Crie um novo projeto</a> e comece em minutos.</p><h2>Um motor, todos os casos de uso vetoriais</h2><p>O banco de dados vetorial do Elasticsearch foi desenvolvido para qualquer pessoa que crie aplicativos usando vetores:</p><ul><li><p><strong>RAG:</strong> recupere o contexto certo para o seu LLM com a recuperação de vetores densos e esparsos, ou opte pela busca híbrida combinando a recuperação vetorial e lexical. A qualidade da sua geração melhora com a qualidade da sua recuperação.</p></li><li><p><strong>Agentes de IA:</strong> forneça aos agentes recuperação rápida e filtrada de documentos e memória de conversação, com as baixas latências que loops de agentes em várias etapas exigem.</p></li><li><p><strong>Busca semântica:</strong> faça a correspondência por significado, não por palavras-chave, com um tipo de campo e zero código de pipeline.</p></li><li><p><strong>Recomendações e similaridade:</strong> encontre vizinhos mais próximos em produtos, imagens ou qualquer conteúdo que você tenha, em escala.</p></li></ul><h2>Tudo o que sua carga de trabalho vetorial precisa, otimizado e pronto para uso</h2><p>Criar uma aplicação baseada em vetores significa conectar várias partes separadas: configurar e hospedar modelos de embedding, fazer a indexação dos seus documentos por meio deles, armazenar os vetores com eficiência, aplicar o modelo de embedding a cada consulta, fazer a correspondência com o armazenamento de vetores e, por fim, recuperar os documentos por trás das correspondências. O Elasticsearch Vector Database cuida de tudo isso para você, sem necessidade de configuração ou setup adicional.</p><h3>Indexação de vetores com o modo de índice vectordb_document</h3><p>O modo de índice <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/dense-vector#dense-vector-vectordb-document-mode">vectordb_document</a>, uma nova configuração de índice desenvolvida especificamente para cargas de trabalho que priorizam vetores, vem ativado por padrão, para que você obtenha as configurações que os especialistas escolheriam. Veja o que ele ativa:</p><ul><li><p><strong>bfloat16 por padrão:</strong> os vetores são armazenados com metade do tamanho do float32 com impacto insignificante no recall, reduzindo o consumo de disco aproximadamente pela metade antes mesmo de a quantização entrar em cena.</p></li><li><p><strong>Vetores de origem excluídos:</strong> no Elasticsearch, seus embeddings já residem nas estruturas de índice usadas para busca; manter uma segunda cópia bruta no _source apenas infla o armazenamento e desacelera a recuperação de resultados. Excluímos a duplicata para que as respostas retornem mais rápido e você armazene menos.</p></li><li><p><strong>Os arquivos certos pré-carregados no cache:</strong> as estruturas de dados que as consultas vetoriais acessam primeiro são carregadas na memória antecipadamente, para que sua primeira consulta (e também a milésima) seja ultrarrápida.</p></li><li><p><strong>Mesclagem paralela:</strong> a mesclagem consolida segmentos em estruturas de vetores mais bem organizadas, o que melhora o recall e reduz a latência, e executar essas mesclagens com várias threads significa que você chega lá mais rápido.</p></li></ul><h3>Armazenamento de vetores, compressão e ajuste automático</h3><ul><li><p>Seus vetores são compactados automaticamente.<a href="https://www.elastic.co/pt/search-labs/blog/better-binary-quantization-lucene-elasticsearch"> O Better Binary Quantization (BBQ)</a> reduz o consumo de memória dos vetores em até 32x enquanto preserva o recall, e o DiskBBQ reduz ainda mais os requisitos de memória para cargas de trabalho em grande escala.<a href="https://www.elastic.co/pt/search-labs/blog/vector-quantization-auto-calibration-diskbbq"> </a></p></li><li><p>Opte pela<a href="https://www.elastic.co/pt/search-labs/blog/vector-quantization-auto-calibration-diskbbq"> calibração automática</a>, que ajusta a quantização de cada segmento aos seus dados e a reajusta a cada mesclagem conforme os dados sofrem desvios. Quando testado em 18 conjuntos de dados, o número de consultas por segundo (QPS) melhorou em uma média de 16,7%, com ganhos de recall na maioria deles.</p></li></ul><h3>Embeddings em inferência gerenciada por GPU</h3><ul><li><p>Gere embeddings com os <a href="https://www.elastic.co/pt/jina-search-models">modelos nativos de embedding e reranking da Jina AI</a>, ou traga modelos de terceiros, tudo em GPUs gerenciadas via <a href="https://www.elastic.co/docs/explore-analyze/elastic-inference/eis">Elastic Inference Service (EIS)</a>, sem precisar operar servidores de modelos. Ou faça a auto-hospedagem, se preferir usar seu próprio modelo.</p></li><li><p>O tipo de campo <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text"><strong>semantic_text</strong></a> lida automaticamente com o particionamento e a geração de embeddings, além de consultas, o caminho mais simples para a busca semântica no mercado. </p></li></ul><h3>Busca híbrida e busca vetorial filtrada</h3><ul><li><p>A <a href="https://www.elastic.co/pt/elasticsearch/hybrid-search">busca híbrida</a> é integrada, combinando a recuperação de texto completo e de vetores em uma única consulta. Combine os resultados com reciprocal rank fusion (RRF) ou qualquer outro mecanismo de combinação que você quiser. A busca vetorial normalmente é a parte mais difícil de configurar bem na busca híbrida. Com o banco de dados vetorial do Elasticsearch, você tem tudo sob controle e toda a sua stack híbrida fica ainda melhor. </p></li><li><p>Com a <a href="https://www.elastic.co/pt/search-labs/blog/filtered-hnsw-knn-search">busca vetorial filtrada</a>, aplique filtros de metadados como parte da própria recuperação vetorial, e não como uma etapa adicionada posteriormente que prejudica o recall.</p></li></ul><h3>Empresarial desde o primeiro dia</h3><p>Você também conta com controle de acesso baseado em funções (RBAC), logging de auditoria e as certificações de conformidade que geralmente faltam nos bancos de dados exclusivamente vetoriais.</p><h2>Acessível em escala e previsível</h2><p>O banco de dados vetorial do Elasticsearch foi desenvolvido para continuar acessível à medida que você cresce: a compressão com BBQ e DiskBBQ, que mantém o armazenamento linear e o uso de memória baixo, significa que redimensionar para centenas de bilhões de vetores não faz sua conta disparar. E o que você paga é calculado com base em números que já conhece: a quantidade de dados que armazena e indexa, além da capacidade de busca de que precisa. Estime a quantidade de documentos e as dimensões dos vetores, além da carga de consultas, e você poderá calcular o que vai pagar antes de criar o projeto. Você também pode entender sua conta linha por linha no fim do mês. Não há unidades de computação opacas nem cobranças surpresa por operações em segundo plano.</p><h2>Como começar com o banco de dados vetorial do Elasticsearch</h2><h3>Crie um projeto de banco de dados vetorial serverless</h3><p>Crie um novo <a href="https://cloud.elastic.co/registration?onboarding_token=vector">projeto serverless de banco de dados vetorial no Elastic Cloud</a>. Aponte seus dados para o endpoint, e você estará pronto para indexar.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt556cbdfba551f248/6aa10b4332b53038406d321a/image1.png" alt="Elastic Cloud Serverless project types: Elasticsearch, Vector Database, Observability and Security" /><h3>Criar um índice usando semantic_text</h3><p>O modo de índice vetorial gerencia a configuração do vetor. Usar semantic_text significa que a configuração de embeddings e particionamento, assim como a configuração do índice, é gerenciada para você com inferência gerenciada por GPU, sem a necessidade de criar um pipeline de embeddings.</p>PUT my-vectors
{
"mappings": {
"properties": {
"description": { "type": "semantic_text" }
    }
  }
}<h3>Ingerir documentos</h3><p>Indexe texto, e os embeddings são gerados para você.</p>POST /my-vectors/_doc
{
  "id": "park_rocky-mountain",
  "title": "Rocky Mountain",
  "description": "Bisected north to south by the Continental Divide, this portion of the Rockies has ecosystems varying from over 150 riparian lakes to montane and subalpine forests to treeless alpine tundra."
}<h3>Execute uma consulta de busca semântica</h3><p>Consulte o mesmo campo semântico que você acabou de criar:</p>GET /my-vectors/_search
{
  "query": {
    "semantic": {
      "field": "description",
      "query": "a mountain range in the middle of north america"
    }
  }
}<p>E você recebe os resultados de volta:</p>{
  "took": 80,
  "hits": {
    "max_score": 0.7792325,
    "hits": [
      {
        "_index": "my-vectors",
        "_score": 0.7792325,
        "_source": {
          "id": "park_rocky-mountain",
          "title": "Rocky Mountain",
          "description": "Bisected north to south by the Continental Divide, ..."
        }
      }
    ]
  }
}<p>A busca semântica é apenas o começo. Execute consultas totalmente textuais ou combine os dois tipos em consultas híbridas. Você pode até criar suas próprias consultas vetoriais para ter controle total. Siga o <a href="https://www.elastic.co/docs/solutions/vector-database/vector-full-text-search">guia de início rápido da busca semântica</a> na documentação para obter as instruções completas.</p><h2>O que vem por aí para a busca vetorial no Elasticsearch</h2><p>Já estamos trabalhando nas próximas melhorias:</p><ul><li><p><strong>Melhor gerenciamento multitenant:</strong> se os seus dados precisarem permanecer separados por tenant, vamos oferecer uma maneira de fazer isso mais rápido e com menos código.</p></li><li><p><strong>Otimização automática de índice:</strong> de "índice novo em folha" a "totalmente otimizado", com o mínimo de ajustes possível.</p></li><li><p><strong>Melhorias contínuas de infraestrutura:</strong> ajuste contínuo das configurações e da infraestrutura do banco de dados vetorial para que você tenha sempre a melhor taxa de transferência e as respostas mais rápidas.</p></li></ul><h2>Experimente o banco de dados vetorial do Elasticsearch no Elastic Cloud Serverless</h2><p>Vá de um projeto vazio a uma consulta vetorial híbrida e filtrada em minutos, com padrões de nível de produção fazendo o ajuste para você. Crie apps de IA rápidos e escaláveis, não infraestrutura.</p><p>Comece no <a href="https://cloud.elastic.co/registration?onboarding_token=vector">Elastic Cloud Serverless</a>, ou mergulhe na <a href="https://www.elastic.co/docs/solutions/vector-database">documentação completa </a>e na <a href="https://www.elastic.co/docs/api/doc/elastic-cloud-serverless/group/endpoint-vectordb-projects">referência da API.</a></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/vector-database-rag-serverless</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/vector-database-rag-serverless</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <category><![CDATA[Elastic Cloud Serverless]]></category>
    <category><![CDATA[Busca híbrida]]></category>
    <dc:creator><![CDATA[Dustin Coates]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4def84aae6aff861/6aa10ab1ee57e53d9b05253c/cover.png" length="0" type="image/png"/>
    <pubDate>Wed, 09 Sep 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Como medir e melhorar o recall das buscas no Elasticsearch: de 0,43 a 0,75 com a busca híbrida]]></title>
    <description><![CDATA[Aprenda a medir e melhorar o recall das buscas no Elasticsearch combinando a busca léxica BM25 com embeddings vetoriais do Jina AI, usando a API rank_eval para validar a melhoria com números reais.]]></description>
    <content:encoded><![CDATA[<p><a href="https://www.elastic.co/docs/solutions/search/full-text">A busca lexical</a> usando o <a href="https://www.elastic.co/blog/practical-bm25-part-1-how-shards-affect-relevance-scoring-in-elasticsearch">algoritmo de classificação BM25</a> é barata, rápida e muito eficaz para uma ampla gama de consultas. Mas ela tem um ponto cego: consultas que não compartilham tokens com seus documentos. Neste artigo, você vai medir exatamente onde a BM25 falha. Usaremos a <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval">API de avaliação de classificação</a> do Elasticsearch (<code>rank_eval</code>) e preencheremos essa lacuna adicionando <a href="https://www.elastic.co/search-labs/es/blog/jina-embeddings-v3-elastic-inference-service">Jina AI embeddings</a> via <a href="https://www.elastic.co/docs/explore-analyze/elastic-inference/eis">Elastic Inference Service</a> (EIS). Você verá a pontuação de recall variar de <code>0.43</code> para <code>0.75</code> e saberá o porquê.</p><h2>O que é recall?</h2><p><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#k-recall">O recall</a> mede em uma escala de <code>0</code> a <code>1</code> quantos dos documentos que seus usuários realmente querem aparecem em algum lugar dos seus resultados de busca. Se uma consulta aparecer em três produtos e sua busca devolver apenas dois deles entre os 10 primeiros, <code>recall@10 = 0.67</code> nessa consulta. É uma métrica baseada em conjuntos: ela não se importa com a posição dos documentos relevantes dentro desses <em>k</em> resultados. Um documento relevante na posição 10 conta o mesmo que um na posição 1. Ter um recall alto significa que você não está perdendo resultados relevantes.</p><p>
</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5ffd147b13705680/6a170a6fe8fbce11a539fc22/b13af2a5d0ca055535d8bfe3dfe4b3d1093ee6da-1457x796.png" alt="Diagrama de Venn ilustrando como o Recall@10 é calculado, mostrando a sobreposição entre todos os documentos relevantes e os 10 principais resultados recuperados pelo BM25, resultando em uma pontuação de Recall@10 igual a 0,40." /><p>O diagrama mostra dois conjuntos: todos os documentos relevantes (à esquerda) e o que o BM25 realmente recuperou (top 10, à direita). Apenas a interseção conta para a recuperação, <code>prod_1</code> e <code>prod_2</code> foram encontrados, enquanto <code>prod_3</code>, <code>prod_4</code> e <code>prod_6</code> foram completamente perdidos. Resultado: <code>Recall@10 = 2/5 = </code><strong><code>0.40</code></strong>.</p><h2>Pré-requisitos</h2><p>Vamos direto ao ponto para entender melhor como o recall funciona. Esta demonstração usa Python. Você pode acompanhar no notebook complementar (<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/relevance-tuning-improving-recall-adding-vectors/notebook.ipynb">notebook.ipynb</a>), onde cada bloco de código é uma célula pronta para ser executada.</p><p>O código fornecido utiliza o seguinte:</p><ul><li><p>Elasticsearch 9.3+</p></li><li><p>Python 3.10+</p></li></ul>pip install elasticsearch pandas plotly python-dotenv<ul><li><p>Um arquivo <code>.env</code> com suas credenciais do Elasticsearch</p></li></ul>ELASTICSEARCH_URL=https://your-cluster-url
ELASTICSEARCH_API_KEY=your-api-key<h2>O conjunto de dados</h2><p>Usaremos um catálogo de produtos com 1.000 produtos, abrangendo categorias como calçados, eletrônicos, ferramentas e outros.</p><p>Cada documento tem quatro campos:</p><p>Campo</p><p>Tipo</p><p>`title`</p><p>texto</p><p>`description`</p><p>texto</p><p>`marca`</p><p>palavra-chave</p><p>`category`</p><p>palavra-chave</p><p>O conjunto de dados é carregado a partir de <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/relevance-tuning-improving-recall-adding-vectors/dataset.csv"><code>dataset.csv</code></a>.</p><h2>O poder e os limites da busca lexical</h2><p>BM25 é o algoritmo padrão de ranqueamento no Elasticsearch e na maioria dos mecanismos de busca. Ele classifica os documentos de acordo com a frequência com que seus termos de consulta aparecem neles, ajustados ao tamanho do documento e à frequência desses termos em todo o índice. Você tem <a href="https://www.elastic.co/docs/reference/text-analysis/analyzer-reference">analisadores</a> na parte superior: normalização de letras minúsculas, stemming e retirada de stopwords. Uma busca por "tênis de corrida" retornará resultados como "Tênis de corrida" e provavelmente também "correr".</p><p>Isso funciona bem para uma grande classe de consultas:</p><ul><li><p>"tênis de corrida" faz a correspondência imediata dos produtos com esses tokens exatos no título.</p></li><li><p>"alto-falante Bluetooth" destaca produtos de áudio portáteis porque os tokens aparecem literalmente.</p></li></ul><p>Os resultados são determinísticos e explicáveis: um documento tem classificação alta porque os termos de consulta aparecem nele. Depurar a relevância é simples.</p><h3>Onde ocorre a falha</h3><p>Agora, vamos testar essas consultas no mesmo catálogo:</p><ul><li><p><strong>"rotina de cuidados com a pele":</strong> a palavra "rotina" não aparece em nenhum título de produto. O BM25 pode corresponder parcialmente com "cuidados com a pele", mas séruns faciais, óleos corporais e hidratantes são descritos usando termos como "vitamina C", "retinol" ou "iluminador", nenhum dos quais se sobrepõe à consulta. Produtos que formam uma rotina completa de cuidados com a pele ficam espalhados pelo índice, sem nenhum token compartilhado para ancorá-los.</p></li></ul>ID: B06XX6DS3P, Score: 9.0552, Title: Replenix Retinol Smooth + Tighten Body Lotion - Collagen-Boosting, Regenerating Anti-Aging Body Cream, Reduces Appearance of Stretch Marks, 6.7 oz.

  ID: B08XMPKJ1L, Score: 5.2699, Title: Bio-Oil Skincare Body Oil (Natural) Serum for Scars and Stretchmarks, Face and Body Moisturizer Hydrates Skin, with Organic Jojoba Oil and Vitamin E, For All Skin Types, 6.7 oz

  ID: B01CY764KQ, Score: 5.0057, Title: Nike Up Or Down Men Deodorant - Pack of 2 | Long-Lasting Fragrance, Body Spray Combo for Men | Deodorant for Active Living | Nike Men's Deo Set | Ultimate Odor Protection | Grooming Essentials | Signature Nike Scent | High-Performance Men's Deodorant<ul><li><p><strong>"acessórios de viagem para pets":</strong> é um agrupamento de casos de uso, não uma categoria de produto. Um sling para cães, uma cadeirinha para pets e uma caixa de viagem são todos relevantes, mas as descrições falam sobre portabilidade, segurança e conforto, em vez de "acessórios de viagem". O BM25 corresponde amplamente a palavra "pet", mas não tem sinal para distinguir produtos específicos para viagens do restante do catálogo de pets.</p></li></ul>ID: B0BVV7BKTW, Score: 7.4371, Title: Large Foldable Travel Duffel Bag with Shoes Compartment

ID: B07TNPHYNV, Score: 6.6455, Title: 40 Pieces Christmas Bronze Jingle Bells Craft Small Bells

ID: B08R8FRW53, Score: 6.6335, Title: CUBY Dog and Cat Sling Carrier
ID: B08QMCQYGM, Score: 6.5259, Title: YTFGGY Whiteboard Pinstripe Tape 6 Rolls 1/8"
ID: B0CP3LQSWM, Score: 6.2994, Title: Portable Dog Water Bottle 32 Oz<p>Esse é um <strong>problema de recall</strong>. Os documentos relevantes existem no seu índice. O BM25 simplesmente não os encontra porque as palavras do usuário e as do documento não coincidem o suficiente.</p><p>Adicionar sinônimos ajuda em casos conhecidos. Mas não dá para enumerar todas as formas como o usuário pode expressar uma intenção. É aí que entram os vetores.</p><h2>Por que medir o recall</h2><p>Antes de corrigir um problema, você precisa quantificá-lo.</p><p><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#k-recall"><strong>Recall@k</strong></a> mede quantos documentos que seus usuários realmente desejam aparecem nos resultados de busca. Formalmente:</p>Recall@k = (relevant documents found in top k) / (total relevant documents)<p><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#k-precision"><strong>Precision@k</strong></a> mede os k principais resultados e quantos são realmente relevantes:</p>Precision@k = (relevant documents in top k) / k<p>Alta precisão mostra que os resultados que você retorna são bons. No comércio eletrônico, perder um produto relevante (baixo recall) geralmente é pior do que mostrar um resultado um pouco imperfeito (menor precisão), porque o produto oculto é venda perdida.</p><p>A API <code>rank_eval</code> do Elasticsearch permite medir os dois de forma sistemática. Você fornece uma lista de consultas, cada uma com um conjunto de documentos avaliados, e o Elasticsearch calcula as métricas para você em todas elas.</p><h2>Configuração da avaliação</h2><p>A API <code>rank_eval</code> precisa de um <strong>conjunto de dados de avaliações</strong>: um mapeamento das consultas para os documentos relevantes para cada um, junto com uma nota de relevância (0 = não relevante, 1 = relevante, 2 = altamente relevante).</p><p>No bloco de notas, esta é a <a href="https://www.elastic.co/docs/solutions/search/ranking/learning-to-rank-ltr#learning-to-rank-judgement-list">lista de julgamentos</a>:</p>judgments = [
    # Query 1: "running shoes" BM25 handles well (tokens appear in product titles) 
    {"query_id": "q1", "doc_id": "B09NQJFRW6", "grade": 2, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B08JMD4LMM", "grade": 2, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B08VRJ6F2Q", "grade": 2, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B07S8NRRWR", "grade": 2, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B01HD620I8", "grade": 2, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B07DX86321", "grade": 2, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B0968YVLQ8", "grade": 1, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B093QJ39ZS", "grade": 1, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B096FGSC39", "grade": 1, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B01GVQWVV2", "grade": 1, "query": "running shoes"},

    # Query 2: "skincare routine" intent-based, "routine" never appears in product titles
    {"query_id": "q2", "doc_id": "B08XMPKJ1L", "grade": 2, "query": "skincare routine"},
    {"query_id": "q2", "doc_id": "B0BN3WQB92", "grade": 2, "query": "skincare routine"},
    {"query_id": "q2", "doc_id": "B0BT7B7P5T", "grade": 2, "query": "skincare routine"},
    {"query_id": "q2", "doc_id": "B00NPA2WEY", "grade": 2, "query": "skincare routine"},
    {"query_id": "q2", "doc_id": "B06XX6DS3P", "grade": 1, "query": "skincare routine"},
    {"query_id": "q2", "doc_id": "B07PDRD1KT", "grade": 1, "query": "skincare routine"},
    {"query_id": "q2", "doc_id": "B074J7869B", "grade": 1, "query": "skincare routine"},
    {"query_id": "q2", "doc_id": "B08JV31QW4", "grade": 1, "query": "skincare routine"},
    {"query_id": "q2", "doc_id": "B00K3TVJMQ", "grade": 1, "query": "skincare routine"},

    # Query 3: "study desk setup" intent-based, products are desks/stands/organizers
    {"query_id": "q3", "doc_id": "B08CS35J2T", "grade": 2, "query": "study desk setup"},
    {"query_id": "q3", "doc_id": "B09B3LFDXJ", "grade": 2, "query": "study desk setup"},
    {"query_id": "q3", "doc_id": "B07W58LMND", "grade": 1, "query": "study desk setup"},
    {"query_id": "q3", "doc_id": "B0CHYDX91L", "grade": 1, "query": "study desk setup"},

    # Query 4: "pet travel accessories" use-case grouping, products are carriers/crates/seats
    {"query_id": "q4", "doc_id": "B08R8FRW53", "grade": 2, "query": "pet travel accessories"},
    {"query_id": "q4", "doc_id": "B01MYUYX33", "grade": 2, "query": "pet travel accessories"},
    {"query_id": "q4", "doc_id": "B003C5RKE4", "grade": 2, "query": "pet travel accessories"},
    {"query_id": "q4", "doc_id": "B09GF8GBF6", "grade": 1, "query": "pet travel accessories"},
    {"query_id": "q4", "doc_id": "B0CP3LQSWM", "grade": 1, "query": "pet travel accessories"},
]<p>A combinação é intencional: <code>q1</code> é uma consulta que o BM25 lida bem (tokens exatos nos títulos dos produtos), enquanto <code>q2</code>, <code>q3</code> e <code>q4</code> são consultas orientadas à intenção, nas quais a intenção do usuário é expressa como um conceito, não como palavras-chave específicas de produto.</p><h2>Medindo o recall de base do BM25</h2><p>Primeiro, configure o cliente do Elasticsearch e indexe os dados de texto bruto:</p>import os
import json
import pandas as pd
import plotly.graph_objects as go
from elasticsearch import Elasticsearch, helpers
from dotenv import load_dotenv

load_dotenv()

es = Elasticsearch(
    os.getenv("ELASTICSEARCH_URL"),
    api_key=os.getenv("ELASTICSEARCH_API_KEY")
)

INDEX_NAME = "ecommerce-products"<p>Agora, crie a solicitação <code>rank_eval</code> para o BM25. Cada solicitação na lista combina uma consulta com as classificações correspondentes:</p>judgments_df = pd.DataFrame(judgments)

bm25_requests = []
for query_id, query_text in (
    judgments_df[["query_id", "query"]].drop_duplicates().values
):
    relevant_docs = judgments_df[judgments_df["query_id"] == query_id]
    ratings = [
        {"_index": INDEX_NAME, "_id": row["doc_id"], "rating": row["grade"]}
        for _, row in relevant_docs.iterrows()
    ]

    bm25_requests.append({
        "id": query_id,
        "request": {
            "query": {
                "multi_match": {
                    "query": query_text,
                    "fields": ["title", "description"]
                }
            }
        },
        "ratings": ratings,
    })

bm25_eval = {
    "requests": bm25_requests,
    "metric": {"recall": {"k": 10, "relevant_rating_threshold": 1}},
}

bm25_result = es.rank_eval(index=INDEX_NAME, body=bm25_eval)
print("BM25 Recall@10:", bm25_result.body["metric_score"])<p>Resultado:</p>BM25 Recall@10: 0.43<p><code>0.43</code> significa que, em todas as quatro consultas, o BM25 encontra apenas 43% dos documentos que deveria. A deficiência se concentra nas consultas baseadas na intenção: "rotina de cuidados com a pele" não inclui séruns faciais e óleos corporais, pois "rotina" nunca aparece nos títulos dos produtos. Já "acessórios de viagem para pets" retorna produtos para pets fora do tópico, enquanto não inclui transportadoras e caixas de transporte descritas em termos de portabilidade e segurança, em vez de "acessórios de viagem".</p><p>Esta é a nossa linha de base. Agora, temos um número a superar.</p><h2>Adicionando busca vetorial com embeddings do Jina</h2><p><a href="https://www.elastic.co/docs/solutions/search/vector"><code>Vector search</code></a> codifica documentos e consultas como vetores de alta dimensão, tipo de vetor composto por centenas ou milhares de valores numéricos, cada um codificando um recurso específico dos dados que representa. Documentos com significado semelhante acabam próximos uns dos outros no espaço vetorial, mesmo que não compartilhem palavras. "Equipamento de ginástica" e "conjunto de halteres" ficam próximos porque os conceitos estão relacionados. Escolhi o Elasticsearch como meu banco de dados vetorial porque ele faz busca híbrida, oferecendo compreensão semântica e precisão de palavras-chave prontas para uso.</p><p><a href="https://www.elastic.co/docs/explore-analyze/elastic-inference/eis">EIS</a> inclui suporte pronto para uso de modelos via <a href="https://www.elastic.co/docs/api/doc/elasticsearch/group/endpoint-inference">API de inferência</a>.</p><h3>Passo 1: usando embeddings Jina v5 como endpoint de inferência</h3>INFERENCE_ENDPOINT_ID = ".jina-embeddings-v5-text-small"<p>Se seu cluster tem recursos de GPU (disponíveis no Elastic Cloud e Elasticsearch 9.3+), as incorporações são geradas na GPU, o que é bem mais rápido do que a inferência da CPU e elimina o tradeoff de desempenho que historicamente encarecia os vetores em larga escala.</p><p>Por que as incorporações Jina especificamente? <a href="https://www.elastic.co/search-labs/blog/jina-embeddings-v5-text">jina-embeddings-v5-text</a> é um modelo multilíngue (mais de 119 idiomas) com uma janela de contexto de 32 mil tokens e suporte para <a href="https://arxiv.org/abs/2106.09685">adaptadores de Adaptação de Baixa Ordem (LoRA)</a> específicos para cada tarefa. Ele funciona bem para descrições curtas de produtos, prontamente utilizável. Saiba mais sobre o modelo <code>jina-embeddings-v5-text</code> <a href="https://huggingface.co/jinaai/jina-embeddings-v5-text-small">aqui</a>.</p><h3>Passo 2: criar o índice com um campo semântico</h3>index_mappings = {
    "mappings": {
        "properties": {
            "title": {"type": "text", "copy_to": "semantic_field"},
            "description": {"type": "text", "copy_to": "semantic_field"},
            "brand": {"type": "keyword"},
            "category": {"type": "keyword"},
            "semantic_field": {
                "type": "semantic_text",
                "inference_id": INFERENCE_ENDPOINT_ID,
            },
        }
    }
}

if not es.indices.exists(index=INDEX_NAME):
    es.indices.create(index=INDEX_NAME, body=index_mappings)
    print(f"Created index: {INDEX_NAME}")<p>O tipo de campo <a href="https://www.elastic.co/docs/solutions/search/semantic-search/semantic-search-semantic-text"><code>semantic_text</code></a> é essencial aqui. É uma abstração de nível mais alto sobre <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/dense-vector"><code>dense_vector</code></a>: você aponta para um endpoint de inferência, e o Elasticsearch cuida de gerar automaticamente os embeddings.</p><p>A propriedade <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/copy-to"><code>copy_to</code></a> em <code>title</code> e <code>description</code> significa que o conteúdo de ambos os campos flui para <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text"><code>semantic_field</code></a> para incorporação, de modo que um único vetor captura a representação completa do produto.</p><h3>Passo 3: indexar os produtos</h3>def bulk_index(products, index_name):
    actions = []
    for product in products:
        doc_id = product.get("_id")
        source = {k: v for k, v in product.items() if k != "_id"}
        action = {"_index": index_name, "_source": source}
        if doc_id:
            action["_id"] = doc_id
        actions.append(action)

    success, failed = helpers.bulk(es, actions, raise_on_error=False)
    if failed:
        for error in failed:
            print(f"Error: {error}")
    else:
        print(f"Successfully indexed {success} documents")

bulk_index(products, INDEX_NAME)<p>No momento do índice, o Elasticsearch chama o endpoint de inferência para cada documento e armazena a incorporação resultante em <code>semantic_field</code>. Sem código extra do seu lado.</p><h2>Busca híbrida: combinando BM25 e vetores com RRF</h2><p>Adicionar vetores melhora a recuperação, mas usar vetores sozinhos pode prejudicar a precisão em consultas de correspondência exata; "tênis de corrida" ainda deve priorizar correspondências exatas. A busca híbrida mantém o componente léxico especificamente para preservar essa precisão.</p><p>A busca híbrida com <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion">Fusão de Classificação Recíproca</a> (RRF) mantém o melhor dos dois mundos:</p><ul><li><p>O BM25 lida com consultas exatas e quase exatas com alta precisão.</p></li><li><p>A busca semântica processa consultas baseadas em intenção e multilíngues com alta precisão.</p></li><li><p>O RRF combina as duas listas classificadas em uma única classificação.</p></li></ul><p>A fórmula RRF atribui a cada documento uma pontuação baseada em sua classificação em cada lista de resultados:</p>score = sum(1 / (rank_constant + rank))<p>Um documento bem classificado em ambas as listas recebe uma pontuação combinada maior. O <code>rank_constant</code> controla quanto peso documentos de menor classificação recebem.</p>hybrid_requests = []

for query_id, query_text in (
    judgments_df[["query_id", "query"]].drop_duplicates().values
):
    relevant_docs = judgments_df[judgments_df["query_id"] == query_id]
    ratings = [
        {"_index": INDEX_NAME, "_id": row["doc_id"], "rating": row["grade"]}
        for _, row in relevant_docs.iterrows()
    ]

    hybrid_requests.append({
        "id": query_id,
        "request": {
            "retriever": {
                "rrf": {
                    "retrievers": [
                        {
                            "standard": {
                                "query": {
                                    "multi_match": {
                                        "query": query_text,
                                        "fields": ["title", "description"],
                                    }
                                }
                            }
                        },
                        {
                            "standard": {
                                "query": {
                                    "match": {
                                        "semantic_field": {"query": query_text}
                                    }
                                }
                            }
                        },
                    ],
                    "rank_window_size": 50,
                    "rank_constant": 5,
                }
            }
        },
        "ratings": ratings,
    })

hybrid_eval = {
    "requests": hybrid_requests,
    "metric": {"recall": {"k": 10, "relevant_rating_threshold": 1}},
}

hybrid_result = es.rank_eval(index=INDEX_NAME, body=hybrid_eval)
print("Hybrid Recall@10:", hybrid_result.body["metric_score"])<p>Resultado:</p>Hybrid Recall@10: 0.75<p>O Hybrid melhora substancialmente em relação ao BM25 (<code>0.43</code>) e preserva a precisão para consultas de correspondência exata como "tênis de corrida".</p><h2>Resultados: Antes e depois</h2><p>Eis a comparação completa entre as três abordagens:</p>methods = {
    "BM25 (Lexical)": bm25_requests,
    "Hybrid (BM25 + Vectors)": hybrid_requests,
}

recall_metric = {"recall": {"k": 10, "relevant_rating_threshold": 1}}

comparison_data = []
for method_name, requests in methods.items():
    result = es.rank_eval(
        index=INDEX_NAME,
        body={"requests": requests, "metric": recall_metric}
    )
    comparison_data.append({
        "method": method_name,
        "recall@10": result.body["metric_score"]
    })

comparison_df = pd.DataFrame(comparison_data)
print(comparison_df.to_string(index=False))<p>Resultado:</p><p>Método</p><p>Recall@10</p><p>BM25 (Léxico)</p><p>0,43</p><p>Híbrido (BM25 + Vetores)</p><p>0,75</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5a1d72b57056fe64/6a170a71c1e8a56c58f882ab/e49f6c10516b0a48a0ad75962c6590ee07311407-700x500.png" alt="Gráfico de barras comparando Recall@10 entre a busca lexical BM25 e a busca híbrida combinando BM25 com vetores, mostrando que a busca híbrida alcança uma recordação significativamente maior." /><p>Analisando por consulta:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt871347f754c866d0/6a170a73839dfa40abdcfeb4/40e36dcb7b34cbf4649c512bcb60cef60f1778a6-700x500.png" alt="Gráfico de barras agrupadas comparando o Recall@10 entre a busca lexical e a busca híbrida do BM25 em quatro consultas de produtos, mostrando que a busca híbrida supera consistentemente a busca lexical em cada consulta." /><h2>Conclusão</h2><p>Ao longo deste artigo, vimos que a busca léxica do BM25 é confiável quando os usuários digitam consultas exatas, mas perde a capacidade de recuperação quando buscam por intenção em vez de palavras-chave. Usando <code>rank_eval</code>, estabelecemos uma linha base reprodutível para medir essa lacuna com números reais. A partir daí, adicionamos um campo <code>semantic_text</code> alimentado por embeddings Jina e rodamos a avaliação novamente. O resultado: a buscar híbrida melhorou a capacidade de recuperação de <code>0.43</code> para <code>0.75</code> enquanto preservava a precisão nas consultas de correspondência exata, embora a margem real dependa da sua mistura de consultas.</p><p>O padrão se estende além deste exemplo: colete julgamentos das consultas reais de seus usuários, execute <code>rank_eval</code> como linha de base, adicione <code>semantic_text</code> e meça novamente. Você saberá exatamente o que melhorou e em quanto.</p><h2>Próximas etapas</h2><ul><li><p>Aprofunde-se no recall e na busca vetorial: <a href="https://www.elastic.co/search-labs/blog/recall-vector-search-quantization">quantização de busca vetorial e recall</a>, de Jeff Vestal</p></li><li><p>Adicione o reranking para melhorar ainda mais a precisão nos resultados principais</p></li><li><p>Consulte a <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rrf.html">documentação de busca híbrida do Elasticsearch</a></p></li><li><p>Leia mais sobre a <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-rank-eval.html"><code>rank_eval</code></a><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-rank-eval.html"> API</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-relevance-tuning-improve-recall</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-relevance-tuning-improve-recall</guid>
    <category><![CDATA[Busca híbrida]]></category>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt37c9d2971b5a2db3/6a170a75cf4f254223b2d149/492c9b5432a2b9e40cebb3b60f0df019a8c7bf6d-1280x720.png" length="0" type="image/png"/>
    <pubDate>Mon, 04 May 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Como criamos o Elasticsearch simdvec para que a busca vetorial seja uma das mais rápidas do mundo]]></title>
    <description><![CDATA[Como criamos o Elasticsearch simdvec, a biblioteca do kernel SIMD ajustada manualmente por trás de cada consulta de busca vetorial no Elasticsearch.]]></description>
    <content:encoded><![CDATA[<p>Elasticsearch simdvec é o motor por trás de cada cálculo de distância vetorial no Elasticsearch. Ele fornece kernels AVX-512 e NEON ajustados manualmente para cada tipo de vetor que o Elasticsearch aceita. A arquitetura de pontuação em lote oculta a latência de memória por meio de pré-busca explícita em x86 e carregamento intercalado no ARM, superando bibliotecas como FAISS e jvector por até 4x quando os dados excedem o cache da CPU. Neste post, explicamos por que o construímos, o que ele contém e como ele deixa a busca vetorial do Elasticsearch uma das mais rápidas do mundo.</p><h2>Como construímos o Elasticsearch com simdvec</h2><p>Toda consulta de busca vetorial no Elasticsearch, seja por meio de percurso <a href="https://arxiv.org/abs/1603.09320">Hierarchical Navigable Small World (HNSW)</a>, varredura de arquivo invertido (IVF) ou reclassificação, se resume ao mesmo problema: calcular as distâncias entre vetores, milhões de vezes por consulta. O Elasticsearch é compatível com uma ampla variedade de tipos de dados e estratégias de quantização, desde float32 até int8, bfloat16, binário e Quantização Binária Aprimorada (BBQ). Cada uma traz diferentes contrapartidas entre memória, taxa de transferência e recuperação. Por trás de tudo isso há um único mecanismo: simdvec.</p><p>Criamos o simdvec para tornar cada cálculo de distância o mais rápido que o hardware permite. Neste post, explicamos por que o criamos, o que está dentro e onde ele entrega o maior impacto.</p><h3>Construído como um carro de corrida</h3><p>Como entusiastas da Fórmula 1, e um de nós tendo trabalhado anteriormente com a equipe Ferrari de Fórmula 1, vemos um paralelo claro. Um carro de Fórmula 1 é projetado com um único propósito: alcançar o melhor tempo de volta. Potência do motor, aerodinâmica e design do chassi só importam na medida em que contribuem para esse resultado. O mesmo vale para um banco de dados vetorial, onde a taxa de transferência de indexação, a latência de consulta e o recall definem o sucesso.</p><p>Embora o resultado final seja o que importa, alcançar os mais altos níveis de desempenho exige que cada componente esteja no estado ideal. Não pode ser só <em>bom o suficiente</em>, tem que ser o <em>melhor </em>na categoria. O Simdvec foi construído com essa mentalidade, focando uma parte crítica do sistema: o mecanismo. Trata-se de uma biblioteca de kernel otimizada para SIMD (<a href="https://en.wikipedia.org/wiki/Single_instruction,_multiple_data">Single Instruction Multiple Data</a>), criada especificamente para fornecer funções de distância nativas em C++ ajustadas manualmente, chamadas a partir do Java via interface de função estrangeira (FFI) <a href="https://openjdk.org/projects/panama/">Panama</a>. Ele trabalha com pontuação em lote, pré-busca de linhas de cache e todos os tipos e layouts vetoriais usados no Elasticsearch.</p><p>Esse é o mecanismo por trás de cada consulta.</p><h3>Por que criamos nosso</h3><p>Começamos em 2023 com a Panama Vector API no Apache Lucene. Funcionou bem para produtos dot float32, mas as necessidades do Elasticsearch logo superaram o que ele podia oferecer. O Elasticsearch é compatível com uma ampla variedade de tipos de vetores quantizados: int8, int4, bfloat16, bit único e BBQ assimétrico. Cada um possui estratégias SIMD diferentes, layouts de empacotamento e requisitos de acumuladores. Além da cobertura de tipos, os caminhos de pontuação do Elasticsearch exigem mais do que a taxa de transferência de pares únicos: o HNSW precisa pontuar vários vizinhos do gráfico em uma única passagem, o IVF precisa da pontuação em lote de milhares de candidatos com pré-busca e a pontuação baseada em disco precisa funcionar diretamente na memória mapeada em memória (mmap) sem cópia. Vimos o que estava disponível e nada abrangia o conjunto completo.</p><p>Então, criamos o simdvec: kernels C++ nativos ajustados manualmente, chamados de Java via FFI, com pontuação em massa, pré-busca e suporte para cada tipo de vetor que o Elasticsearch usa. Ao possuir a biblioteca, controlamos toda a stack. Quando adicionamos um novo tipo de quantização como BBQ, ele recebe um kernel SIMD ajustado ligado por todo o sistema. Não esperamos uma biblioteca upstream para suportá-lo e não comprometemos o desempenho de nenhum tipo. Toda consulta vetorial no Elasticsearch, seja HNSW, IVF, de reclassificação ou híbrida, pode ser executada nesse mecanismo, construído em torno das operações e tipos que realmente usamos.</p><p>O Simdvec possui bibliotecas nativas separadas para x86 e ARM, cada uma com múltiplos níveis de arquitetura de conjunto de instruções (ISA) selecionados no início. A sobrecarga de chamadas do Java via FFI é muito baixa, com <a href="https://github.com/ldematte/simsimd-benchmarks/blob/main/COMPARISON.md#ffm-downcall-overhead-measurements">nanossegundos de um dígito</a>.</p><h3>O cenário</h3><p>Não somos os únicos a construir kernels de distância vetorial otimizados para SIMD. O ecossistema é rico, e queríamos entender como o simdvec funciona. Não para classificar projetos, mas para fornecer contexto e explicar onde o mecanismo do Elasticsearch está localizado. Selecionamos três projetos como pontos de referência, cada um representando uma abordagem diferente:</p><ul><li><p><strong>jvector:</strong> uma biblioteca Java de busca aproximada de vizinhos mais próximos (ANN) que usa a Panama Vector API para cálculo vetorizado de distância, com aceleração nativa em C opcional no x86.</p></li><li><p><strong>FAISS:</strong> um framework de busca vetorial open source amplamente utilizada, com kernels AVX2/AVX-512 otimizados manualmente.</p></li><li><p><strong>NumKong</strong> (anteriormente SimSIMD): um conjunto abrangente de mais de 2.000 kernels SIMD ajustados manualmente, abrangendo funções de distância, operações matriciais e computação geoespacial.</p></li></ul><p>Cada projeto tem um propósito diferente e realiza diferentes compensações. Incluímos números de referência deles para dar contexto sobre o desempenho do simdvec nas operações específicas que o Elasticsearch precisa.</p><h3>Como medimos</h3><p>Os <a href="https://github.com/ChrisHegarty/jvector-kernel-benchmarks">benchmarks simdvec</a> e jvector são escritos em Java com o JMH, o conjunto padrão de microbenchmark da JVM, com a sobrecarga de FFI incluída. Para os <a href="https://github.com/ldematte/simsimd-benchmarks">benchmarks NumKong</a> e <a href="https://github.com/ChrisHegarty/faiss-kernel-benchmarks">FAISS</a>, criamos programas em C/C++ reduzidos usando o Google Benchmark, que é a estrutura padrão de microbenchmarks em C++. Ambos os frameworks reportam nanossegundos por operação com calibração de aquecimento e iteração. Verificamos por meio de contadores de desempenho de hardware que todas as bibliotecas estão usando SIMD em ambas as plataformas. Todo o código do benchmark está disponível publicamente nos repositórios vinculados do GitHub (e, no caso do simdvec, no repositório <a href="https://github.com/elastic/elasticsearch">elasticsearch</a>).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt90a7d66553be3c9d/6a1701d166c4f942caf8bea5/aee116772df161cf86b7668f575ac34c733a23c5-1580x238.png" alt="Tabela listando duas plataformas: x86 com AMD EPYC Turin (Zen 5), AVX2 e AVX‑512, AWS c8a.4xlarge; e ARM com Graviton 4 (Neoverse V2), NEON e SVE2, AWS c8g.4xlarge." /><p><strong>Software:</strong> JDK 25.0.2, JMH 1.37, GCC 14, Google Benchmark (a versão mais recente).</p><h2>Um vetor de cada vez</h2><p>A operação mais fundamental na busca vetorial é calcular a distância entre dois vetores. Cada avaliação de vizinho HNSW, cada pontuação de candidata a IVF, cada comparação de reclassificação se reduz a esse ciclo interno.</p><p>Medimos a taxa de transferência de pares individuais em 1024 dimensões em ambas as plataformas, começando com float32, o tipo de referência e aquele em que o ecossistema é mais competitivo. Comparamos simdvec com FAISS e jvector; excluímos o NumKong porque ele usa acumuladores float64 para float32, tornando-o 3,2x-5,3x mais lento (dependendo da plataforma), priorizando precisão numérica em vez de throughput. Para manter a comparação comparável, comparamos o NumKong no int8, onde ele usa a mesma estratégia de acumulador do simdvec.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte3bc2ecaf3ad2b61/6a1701d2ab7f08039ddb9d0f/352cfa0fc18f123140f746d843404f80127fb1b7-1500x675.png" alt="Gráfico de barras horizontais intitulado &quot;float32 Dot Product — AMD Turin&quot; comparando cinco implementações: FAISS AVX-512 a 23,2 ns/op, ES simdvec AVX-512 a 28,3 ns/op, FAISS AVX2 a 36,4 ns/op, ES simdvec AVX2 a 38,9 ns/op e jvector a 43,9 ns/op." /><p>No x86, o FAISS AVX-512 é o kernel de par único mais rápido, com 23 ns. O Simdvec AVX-512 segue a 28 ns, uma lacuna que reflete a sobrecarga de chamadas FFI. Ambos usam FMA de 512 bits com desenrolamento de multi-acumuladores. No nível AVX2, os dois são muito mais próximos, 36 ns e 39 ns respectivamente, ambos limitados pela largura de carga de registrador e memória de 256 bits. O jvector é executado em 44 ns usando a API Java Panama Vector. O Panama gera um bom código SIMD, mas os intrínsecos do C++ ajustados manualmente mantêm uma vantagem.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcbbbe442631a575d/6a1701d42b835f81bcf4b083/95e44d9767f21ec4bccaed835e3c99aa180431ee-1500x495.png" alt="Gráfico de barras horizontais intitulado &quot;float32 Dot Product - Graviton 4 (ARM)&quot; mostrando ES simdvec a 70.2 ns/op, jvector a 110.0 ns/op e FAISS a 155.6 ns/op." /><p>No ARM, o simdvec lidera com 70 ns, bem à frente do jvector com 110 ns e do FAISS com 156 ns. O Simdvec tem kernels NEON ajustados manualmente para aarch64. O Jvector não tem código ARM nativo e depende do Panama. O FAISS depende da autovetorização do compilador em vez de intrínsecos explícitos do NEON, o que explica a lacuna maior. Isso reflete uma vantagem prática de ter a biblioteca do kernel: quando o Elasticsearch expandiu para o Graviton, adicionamos kernels NEON construídos especificamente para isso. Nem o jvector, nem o FAISS priorizaram código nativo ARM na mesma medida.</p><p>Mas o Elasticsearch não pontua apenas em float32. A quantização <strong>Int8</strong> reduz a memória em 4x, bfloat16 em 2x e BBQ em 32x. Cada tipo precisa de sua própria estratégia SIMD, e o simdvec fornece kernels nativos ajustados manualmente para todos eles.</p><p>Das bibliotecas que comparamos, apenas a NumKong tem kernels comparáveis para int8. Medimos o produto escalar int8, euclidiano ao quadrado e cosseno em 1.024 dimensões.</p><p><strong>Pontuação Int8 par único (1024 dimensões, ns/vec op – quanto menor, melhor)</strong></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3ac255d158e93504/6a1701d5a292997e48d00eb1/a0b852fd5f51d57bd2488886600472ee65ab64da-1594x378.png" alt="Tabela comparando o desempenho de x86 e ARM para operações de produto escalar, euclidiano quadrado e cosseno, listando os valores ES, NumKong e diff para cada operação em ambas as arquiteturas." /><p>Em ambas as arquiteturas, o NumKong é igual ou mais rápido em dimensões pequenas a médias, onde a diferença se deve em grande parte à menor sobrecarga de chamadas (chamada direta em C vs Java FFI). Em dimensões maiores, o simdvec alcança o desempenho do kernel, onde a implementação mais eficiente (que usa desenrolamento em cascata) amortiza o custo da chamada: conforme a dimensão aumenta, <a href="https://github.com/ldematte/simsimd-benchmarks/blob/main/COMPARISON.md#single-pair-i8-nsop-2">essa diferença diminui e eventualmente se inverte</a>. O crossover está em dimensões entre 768 e 1.536, dependendo da função e arquitetura.</p><p>Apesar da sobrecarga ligeiramente maior do Java FFI, o simdvec está no mesmo nível das bibliotecas altamente otimizadas em C/C++. Além de ser a única biblioteca com kernels otimizados tanto para float32 <em>quanto</em> para int8, também lidera em ARM e fica apenas um pouco atrás de FAISS em x86 (para float32), e muito próxima de NumKong em ambas as arquiteturas (para int8). E, para bfloat16, int4, binário e BBQ, embora existam alternativas, o simdvec se destaca graças ao SIMD ajustado manualmente, adaptado ao layout de dados de cada tipo.</p><p>Mas um mecanismo de busca de produção não pontua um vetor de cada vez; ele marca milhares por consulta. A próxima pergunta é o que acontece nessa escala.</p><h3>Milhares de uma só vez</h3><p>O desempenho de um único par é apenas parte do panorama. O que importa na prática é como os sistemas se comportam sob carga. Uma única consulta HNSW pode pontuar centenas de vizinhos do gráfico. Uma varredura de IVF pode pontuar milhares de entradas da lista de postagens. Uma passagem de reclassificação pode pontuar dezenas de milhares de candidatos. A taxa de transferência de pares individuais é importante, mas o que importa ainda mais é a rapidez com que você consegue pontuar vários vetores e a suavidade com que o desempenho se degrada à medida que o conjunto de trabalho transborda dos caches da CPU.</p><p>O Simdvec fornece pontuação em lote para todos os tipos de dados. Esses não são apenas loops sobre kernels de par único; eles usam loops internos multiacumuladores que carregam o vetor de consulta uma vez por passo dimensional e o compartilham entre múltiplos vetores de documentos, com pré-busca explícita de linha de cache para o lote seguinte. Nem jvector, nem FAISS oferecem algo equivalente (no momento em que escrevo). O Jvector não tem bulk API, então os chamadores marcam um par por vez em um loop. O FAISS expõe <code>fvec_inner_products_ny</code>, que, no momento da escrita, é implementado como um ciclo sobre a função de distância de par único, sem amortização de consulta ou pré-busca.</p><p><strong>Float32.</strong> Para medir o impacto no nível do kernel, avaliamos uma única consulta contra números crescentes de vetores de documento float32 de 1.024 dimensões, usando padrões de acesso aleatório que simulam buscas de vizinhos de gráficos dispersos semelhantes ao HNSW. Os três tamanhos de conjunto de dados, 32, 625 e 32.500 vetores, são escolhidos para que o conjunto de trabalho exceda o cache L1, L2 e L3, respectivamente.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2df64ec77a1921ae/6a1701d714b270393ae3c4de/1d90267be1c63ac82b8ba588617ebedb0be0d1b6-1334x558.png" alt="Dois gráficos de barras comparando tempos de pontuação em lote do Float32 para Elasticsearch simdvec, FAISS e jvector no AMD Turin (x86, AVX-512) e Graviton 4 (ARM, NEON) em três tamanhos: 32 vetores, 625 vetores e 32.500 vetores." /><p>Quando os dados cabem no cache, o simdvec é o mais rápido em ambas as plataformas, mas as margens são modestas, já que a aritmética do kernel predomina. A verdadeira separação surge à medida que o conjunto de trabalho cresce além do nível L3. Em x86, o simdvec atinge 95 ns por vetor, enquanto o FAISS precisa de 165 ns e o jvector, de 412 ns. Em ARM, o padrão é o mesmo: o simdvec se mantém em 162 ns, enquanto o FAISS sobe para 347 ns e o jvector para 476 ns. A pré-busca e a amortização de consultas no simdvec mantêm a latência de memória oculta de uma forma que um simples loop sobre kernels de par único não consegue igualar, e a vantagem se amplia precisamente onde as cargas de trabalho de busca reais operam, nas profundezas da memória principal.</p><p><strong>Int8.</strong> O mesmo padrão se aplica aos tipos quantizados. Medimos a pontuação em lote do produto escalar int8 em 1.024 dimensões, com tamanhos de conjuntos de dados escolhidos para exceder os mesmos limites do cache L1, L2 e L3, comparando a pontuação em lote do simdvec com a pontuação de par único do NumKong em um ciclo.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcda85e10007188d0/6a1701d9cf4f25d722b2cff7/9ee97d98b40d13b19370b76b33e67ea66bfb3250-1580x338.png" alt=" Tabela intitulada &quot;x86 — Pontuação em lote, produto escalar int8 (ns/op, quanto menor, melhor)&quot; comparando ES simdvec e NumKong em três tamanhos de vetor — 128, 2.500 e 130.000 — com valores correspondentes de ns/op e fatores de aceleração." /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt972c311b7d08ba73/6a1701dadc55dec02ee00c87/601f50a03fa3e6263cbac9700281dfe3e511de60-1580x338.png" alt="Tabela intitulada &quot;ARM — Pontuação em lote, produto escalar int8 (ns/op, quanto menor, melhor)&quot; comparando ES simdvec e NumKong em três tamanhos de vetor — 128, 2.500 e 130.000 — com valores correspondentes de ns/op e fatores de aceleração" /><p>No x86, o simdvec é de 1,2x a 1,9x mais rápido, impulsionado pela combinação de pré-busca explícita e processamento em lote. No ARM, o simdvec vence novamente (1,7x a 1,9x mais rápido) em todos os tamanhos de conjuntos de dados. A vantagem vem do processamento em lote de quatro vetores por vez, oferecendo paralelismo em nível de memória por meio de um padrão de acesso intercalado. Em ambos os casos, o resultado mais impressionante é o que ocorre no maior tamanho de conjunto de dados, onde mais importa.</p><p>Os resultados para distância ao quadrado e cosseno mostram um padrão semelhante, com acelerações de 1,4x a 1,8x para ARM e de 1,3x a 3,0x para x86 (detalhes <a href="https://github.com/ldematte/simsimd-benchmarks/blob/main/COMPARISON.md">aqui</a>).</p><h3>Quando a memória é o mais importante</h3><p>Índices vetoriais de produção normalmente não cabem no cache da CPU. Um índice int8 de 10 milhões de vetores, com 1.024 dimensões, tem 10 GB. Pontuar candidatos significa fazer streaming de dados a partir da DRAM e é aí que a arquitetura de pontuação em lote faz a diferença.</p><p>Usamos contadores de desempenho de hardware para medir o que acontece dentro da CPU durante a pontuação em lote e descobrimos que ocultar a latência de memória exige duas estratégias fundamentalmente diferentes, uma por arquitetura.</p><p><strong>No x86, a pré-busca explícita elimina os erros de cache. </strong>O kernel em massa processa os vetores sequencialmente, um totalmente computado antes do próximo, enquanto emite instruções de pré-busca para o próximo lote. Os dados futuros são puxados para L1 antes que a CPU precise deles.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt368f29b5143d0f5a/6a1701dcc1e8a51780f8817b/a39548f8060c2a4a5154521a4047dd92d8cd77be-1580x309.png" alt="Tabela intitulada “x86 (AMD Turin) — Contadores de hardware por operação int8” comparando os modos único e em massa para falhas de cache L1, falhas de IPC e dTLB, com os fatores de melhoria correspondentes." /><p>Em ARM, a mesma abordagem sequencial teve desempenho ruim, mesmo com prefetching. Em vez disso, o <strong>kernel bulk intercala leituras</strong> de quatro vetores em cada posição do stride, dando ao mecanismo de execução fora de ordem quatro fluxos de memória independentes. A CPU não está buscando dados mais rápido, mas sim esperando menos, porque sempre há outra coisa para calcular enquanto as requisições de memória estão em voo. Você pode encontrar uma análise detalhada <a href="https://github.com/elastic/elasticsearch/issues/145412">nesta edição do GitHub</a>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8751ac5b20b75508/6a1701deab7f08db83db9d13/832de3bc0d556a493b7bf3f250196018acfc1585-1580x238.png" alt="Tabela Intitulada &quot;ARM (Graviton 4) — Contadores de hardware por operação Int8&quot; comparando os modos único e em lote para falhas de cache L1 e paradas do backend, com notas de melhoria correspondentes" /><p>Os números contam duas histórias diferentes:</p><ol><li><p>Em x86, a pré-busca transforma 139K erros de cache em 19K, e as instruções por ciclo (IPC) mais que dobram. A grande vantagem aumenta com o tamanho do conjunto de dados, de 1,2x em L2 para 2,8x além de L3, porque a pré-busca oculta viagens de ida e volta de DRAM cada vez mais caras.</p></li><li><p>No ARM, as falhas de cache mal mudam. O que muda é o uso: as estagnações de backend caem 40% porque o padrão de acesso intercalado mantém o pipeline alimentado. Essa vantagem se mantém consistente em 1,8x, independentemente do tamanho do conjunto de dados, porque o paralelismo no nível da memória se aplica independentemente de os dados virem do cache ou da DRAM.</p></li></ol><p>Duas arquiteturas, duas estratégias, um resultado: em escala de produção, o simdvec mantém o pipeline da CPU ocupado mesmo quando os vetores estão espalhados pela memória principal.</p><h2>O que isso significa para os usuários do Elasticsearch</h2><p>Essas capacidades em nível de kernel se acumulam. Uma única consulta vetorial pode calcular milhões de operações de distância: percurso de gráficos HNSW, pontuação de candidatos, reclassificação. Ao longo de milhares de consultas concorrentes, nanossegundos por operação se traduzem diretamente em latência de consulta e transferência do cluster. Seja usando float32, int8, bfloat16 ou BBQ, seja seu índice na memória ou no disco, simdvec é o motor por baixo, e cada uma dessas operações executa pelo mesmo motor, ajustado até o último nanosegundo.</p><p>A principal conclusão é que, em escala de produção, o desempenho da busca vetorial não é determinado principalmente pela taxa de transferência SIMD bruta. Ele é dominado pela eficiência com que o sistema oculta a latência da memória e, ao mesmo tempo, mantém a computação em milhões de pequenas operações.</p><p>Os kernels simdvec são aprimorados em quase todas as versões do Elasticsearch. Quando surgem novos tipos de quantização e plataformas de hardware, eles recebem kernels ajustados desde o primeiro dia. E os tipos existentes continuam a ficar mais rápidos à medida que refinamos as implementações que já estão sendo lançadas.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-vector-search-simdvec-engine</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-vector-search-simdvec-engine</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <category><![CDATA[Na Elastic]]></category>
    <dc:creator><![CDATA[Chris Hegarty,Lorenzo Dematte,Simon Cooper]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta18088409621369b/6a1701dfdc55de7297e00c8b/df9646091bafbbf0a6dfd212ff8a6bd1e8589708-1280x720.png" length="0" type="image/png"/>
    <pubDate>Thu, 23 Apr 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Clustering não supervisionado de documentos com Elasticsearch + embeddings Jina]]></title>
    <description><![CDATA[Uma abordagem prática e reproduzível para clustering não supervisionado de documentos com Elasticsearch e embeddings Jina.]]></description>
    <content:encoded><![CDATA[<p>A busca vetorial começa com uma consulta, mas e se você não tiver o que consultar?</p><p>As organizações acumulam grandes coleções de documentos, como chamados de suporte, processos judiciais, notícias, artigos de pesquisa, e precisam entender o que eles contêm antes de poderem fazer as perguntas certas. Sem rótulos nem dados de treinamento, revisar manualmente milhares de documentos é impraticável. A busca tradicional não ajuda quando você não sabe o que procurar.</p><p>Esta publicação tem uma abordagem nativa do Elasticsearch para clustering de documentos não supervisionados e rastreamento de histórias temporais que lida com esse problema de descoberta. Ao final, você poderá acompanhar arcos narrativos como este ao longo de vários dias:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta8c243e0c5773440/6a17093fa6c2b98c86e7968c/100a60a7fb85da8ab3813fd071a82c93f2c3f318-1300x650.png" alt="Cadeias temporais de histórias que fluem ao longo de fevereiro de 2025, cada caminho colorido representa uma história que persiste ao longo dos dias, com a largura do link indicando a força de sobreposição do kNN" /><p><strong>O que você vai descobrir:</strong></p><ul><li><p>Por que <strong>embeddings de clustering</strong> (e não embeddings de recuperação) são importantes quando se deseja descobrir tópicos sem uma consulta?</p></li><li><p>Como a classificação de centroides sondada por densidade agrupa documentos por tópico usando Elasticsearch k-nearest neighbor (kNN) e processamento em lote <code>msearch</code>.</p></li><li><p>Como <a href="https://www.elastic.co/docs/reference/aggregations/search-aggregations-bucket-significanttext-aggregation"><code>significant_text</code></a> pode automaticamente rotular clusters para que os temas sejam legíveis sem precisar treinar um modelo?</p></li><li><p>Como as cadeias temporais de histórias conectam clusters diários para mostrar como os temas evoluem dia após dia.</p></li></ul><p>O pipeline utiliza ~8.500 artigos de fevereiro de 2025 da BBC News e do The Guardian como um corpus de teste. As notícias são convenientes porque apresentam um comportamento temporal claro, mas esse padrão se aplica a qualquer situação em que a descoberta de documentos seja importante: revisão jurídica, monitoramento de conformidade, síntese de pesquisas, triagem de suporte ao cliente.</p><p><strong>Stack:</strong></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/jina-embeddings-v5-text"><strong>Jina v5</strong></a> <strong>clustering embeddings:</strong> adaptadores LoRA (Low-Rank Adaptation) específicos para tarefas no agrupamento de tópicos. <a href="https://www.elastic.co/blog/elastic-jina-ai">Jina ingressou na Elastic</a> e os modelos estão disponíveis nativamente por meio do <a href="https://www.elastic.co/docs/explore-analyze/elastic-inference/eis">Elastic Inference Service (EIS).</a></p></li><li><p><strong>Elasticsearch:</strong> <a href="https://www.elastic.co/docs/solutions/search/vector/knn">kNN</a> escalável, rotulagem <code>significant_text</code> e armazenamento de vetores.</p></li><li><p><a href="https://www.elastic.co/search-labs/blog/diskbbq-elasticsearch-introduction"><strong>DiskBBQ:</strong></a> um formato de índice vetorial baseado em disco que combina <a href="https://www.elastic.co/docs/reference/elasticsearch/index-settings/bbq">quantização binária aprimorada (BBQ)</a> com particionamento hierárquico k-means para aceleração aproximada de vizinhos mais próximos (ANN). Essa partição de índice é interna à busca vetorial e separada do algoritmo de clustering baseado em densidade usado nesta postagem. <code>bbq_disk</code> armazena vetores quantizados em disco e mantém apenas metadados de partição no heap, reduzindo os requisitos de recursos, em comparação com <code>bbq_hnsw</code>, mantendo alta recuperação.</p></li><li><p><strong>Clustering global + vinculação temporal diária:</strong> descoberta e evolução da narrativa.</p></li></ul><p><strong>O que você precisará:</strong></p><ul><li><p>Uma implementação do Elasticsearch (Elastic Cloud, Elasticsearch Serverless ou Elastic Self-Managed 8.18+/9.0+): <code>bbq_disk</code> requer a versão 8.18 ou posterior. A seção opcional do diversificador retriever exige 9.3+ ou serverless.</p></li><li><p>Uma <a href="https://jina.ai/embeddings/">chave de API Jina</a>: o nível gratuito inclui 10 milhões de tokens, o que cobre o pipeline principal de clusterização (aproximadamente 4,25 milhões de tokens). A comparação opcional entre recuperação e clustering usa uma segunda passagem de incorporação.</p></li><li><p>Uma <a href="https://bonobo.capi.gutools.co.uk/register/developer">chave de API do Guardian</a> (gratuita).</p></li></ul><h2>Configuração</h2><p>Instale os pacotes necessários:</p>pip install elasticsearch pandas numpy plotly umap-learn python-dotenv pydantic-settings datasets requests<p>Opcional (somente se você executar ferramentas de scraping deste repositório):</p>pip install beautifulsoup4<p>Depois, configure chaves de API em um arquivo <code>.env</code> na raiz do projeto:</p>ELASTIC_CLOUD_ID=your-cloud-id        # or ELASTIC_HOST=https://...
ELASTIC_API_KEY=your-api-key
JINA_API_KEY=your-jina-key
GUARDIAN_API_KEY=your-guardian-key<p>Este notebook chama <code>load_dotenv(override=True)</code>, portanto os valores locais <code>.env</code> têm precedência.</p>Connected to Elasticsearch<h2>Parte 1: clustering de descoberta – Por que fazer clustering de embeddings?</h2><p>A maioria das buscas vetoriais utiliza <strong>embeddings de recuperação</strong> treinados para associar uma <em>consulta</em> a <em>documentos</em> relevantes. Isso é perfeito para buscas, mas não para descobertas. Quando você quer descobrir quais tópicos existem em um corpus sem qualquer consulta, precisa de embeddings que agrupem documentos semelhantes.</p><p>O Jina v5 resolve isso com <strong>adaptadores Low-Rank Adaptation (LoRA) específicos para cada tarefa</strong>. O LoRa adiciona pequenas atualizações de baixa classificação às camadas internas específicas, mantendo a maioria dos pesos do modelo base congelados, de modo que o comportamento do modelo se adapta a uma tarefa específica sem a necessidade de um novo treinamento completo. O mesmo modelo base produz embeddings diferentes dependendo do parâmetro <code>task</code>:</p><p>Tarefa</p><p>Preparado para</p><p>Caso de uso</p><p>retrieval.passage</p><p>Correspondência entre consulta e documento</p><p>Busca, retrieval augmented generation (RAG)</p><p>clustering</p><p>Agrupamento de tópicos (otimizado para clusters compactos)</p><p>Descoberta, categorização</p><p>O adaptador de clustering é treinado para <em>aproximar</em> documentos sobre o mesmo tópico no espaço de incorporação e <em>distanciar</em> documentos sobre tópicos diferentes. A comparação visual abaixo torna a diferença concreta.</p><h3>Recuperação vs. clustering: uma comparação visual</h3><p>Para ver a diferença, uma amostra de documentos recebe embedding de ambos os tipos de tarefa. O clustering é realizado no espaço de incorporação original de 1024 dimensões; a aproximação e projeção uniforme de variedades (UMAP) é usada apenas para projetar essas incorporações em 2D para visualização. A UMAP preserva a estrutura local de vizinhança, tornando-a útil para comparar a separação de clusters.</p><p>Abaixo, o mesmo exemplo de 480 documentos é incorporado com ambos os tipos de tarefas e projetado para 2D com UMAP. Procure grupos de cores mais fechados e separados no painel de clustering.</p>    Full dataset: 8,495 articles
    Sources: guardian: 5749, bbc: 2746
    Date range: 2025-02-01 to 2025-02-28


    Sample: 480 docs across 8 sections
    section
    Film              60
    World news        60
    Australia news    60
    Opinion           60
    Football          60
    US news           60
    Sport             60
    Business          60


    Clustering embeddings: 480
    Retrieval embeddings:  480


    UMAP projection complete<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4b3733dccad212b6/6a1709407d8d67aaeb70e6a4/9bcf7a744900560c1c6c63a2dc3af2f9bfd33e11-1100x500.png" alt="Comparação da UMAP entre embeddings de recuperação e clustering" /><p><em>Os embeddings de recuperação (à esquerda) espalham amplamente os tópicos; os embeddings de clustering (à direita) produzem grupos mais coesos e separados a partir dos mesmos documentos.</em></p><p>Os embeddings de clustering produzem grupos mais compactos e visualmente distintos. Os embeddings de recuperação distribuem os tópicos de maneira mais uniforme, ideais para busca (similaridade refinada); mas, para descoberta, o que importa são os clusters temáticos compactos.</p><p>É por isso que o <code>task="clustering"</code> é usado no restante deste guia.</p><h3>Carregando o conjunto de dados</h3><p>O corpus combina duas fontes de notícias para fevereiro de 2025:</p><ul><li><p><strong>BBC News</strong> através do conjunto de dados <a href="https://huggingface.co/datasets/RealTimeData/bbc_news_alltime">RealTimeData/bbc_news_alltime</a> HuggingFace.</p></li><li><p><strong>The Guardian</strong> através da <a href="https://open-platform.theguardian.com/">API da Guardian Open Platform</a>.</p></li></ul><p>Ter múltiplas fontes ajuda a validar se o clustering encontra <em>tópicos</em> em vez de <em>estilos específicos de cada fonte</em>.</p>    Total articles:  8,495
    
    Source breakdown:
    source
    guardian    5749
    bbc         2746
    
    Date range: 2025-02-01 → 2025-02-28
    Days covered: 28
    
    Sample article:
      Source:  guardian
      Title:   Carbon monoxide poisoning ruled out in death of Gene Hackman and wife, police sa
      Section: Film
      Text:    Authorities have ruled out that Gene Hackman and his wife, Betsy Arakawa, died from carbon monoxide poisoning earlier this week in their home in Santa Fe, New Mexico. The Santa Fe county sheriff, Adan...<h3>Embedding com a tarefa de clustering</h3><p>A API Jina v5 é chamada com <code>task="clustering"</code> para todos os documentos. Os embeddings são armazenados em cache no disco, portanto, as execuções subsequentes ignoram a API completamente.</p><p>A chamada da API é direta. O parâmetro <code>task</code> é a principal diferença em relação ao uso típico de embeddings:</p>payload = {
    "model": "jina-embeddings-v5-text-small",
    "input": texts,
    "task": "clustering",  # ← This selects the clustering LoRA adapter
}<p>O tempo abaixo reflete uma taxa de acerto do cache. A primeira execução contra a API demora mais, dependendo do tamanho do corpus.</p>    Embeddings ready: 8,495 vectors of dimension 1024
    Time: 0.6s<h3>Indexação em um único índice do Elasticsearch</h3><p>Para clustering de descoberta, o mês inteiro é dedicado a um índice (<code>docs-clustering-all</code>). A partição diária vem depois para a ligação temporal da história.</p><p>O mapeamento do índice usa <a href="https://www.elastic.co/docs/reference/elasticsearch/index-settings/bbq"><code>bbq_disk</code></a> para o campo vetorial:</p>{
  "embedding": {
    "type": "dense_vector",
    "dims": 1024,
    "index": true,
    "similarity": "cosine",
    "index_options": {
      "type": "bbq_disk"        // hierarchical k-means partitioning for ANN index lookup; separate from this post's clustering algorithm
    }
  }
}<p>Um vetor float32 de dimensão 1024 tem 4 KB. <a href="https://www.elastic.co/search-labs/blog/diskbbq-elasticsearch-introduction"><code>bbq_disk</code></a> utiliza k-means hierárquicos para particionar vetores em pequenos clusters, quantificá-los de forma binária e armazenar os vetores de precisão total no disco para repontuação. Apenas os metadados de partição permanecem no heap, então os requisitos de memória permanecem baixos mesmo para corpora grandes. Para cargas de trabalho que podem suportar mais heap, <a href="https://www.elastic.co/docs/reference/elasticsearch/index-settings/bbq"><code>bbq_hnsw</code></a> constrói um gráfico Hierarchical Navigable Small World (HNSW) para consultas mais rápidas com mais custo de recursos.</p><p>O tipo de campo <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/dense-vector"><code>dense_vector</code></a> suporta múltiplas estratégias de quantização: <code>bbq_disk</code> e <code>bbq_hnsw</code> são os melhores ajustes para embeddings de alta dimensão como os vetores de dimensão 1024 usados aqui.</p>    Indexed 8,495 documents into docs-clustering-all
    Time: 57.5s<h3>Clusteringo: classificação de centroides baseada em densidade</h3><p>Algoritmos de clustering tradicionais, como o HDBSCAN, pressupõem que você possa manter a matriz vetorial completa de N×d na memória e executar atualizações de passagem completa repetidas. Para 8.495 documentos em 1024 dimensões, isso é administrável (aproximadamente 35 MB), mas a abordagem não é escalável para milhões de documentos sem infraestrutura adicional.</p><p>Este algoritmo é conceitualmente semelhante à inicialização do KMeans++ com atribuição de Voronoi e um nível de ruído, mas utiliza a <a href="https://www.elastic.co/docs/solutions/search/vector/knn">busca kNN</a> do Elasticsearch como primitiva de computação, mantendo quase todo o trabalho no lado do servidor:</p><ol><li><p><strong>Amostra de 5% de documentos</strong> como sondas de densidade (amostra aleatória, mínimo de 50).</p></li><li><p><strong>Densidade da sonda por meio de lote</strong> <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-msearch"><strong><code>msearch</code></strong></a> <strong>kNN</strong>. Cada sonda dispara uma consulta kNN e registra a semelhança média dos vizinhos. Alta similaridade média = região densa do espaço de embedding. <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-msearch"><code>msearch</code></a> envia várias solicitações de pesquisa em uma única chamada HTTP, o que é fundamental aqui: a sondagem de densidade gera centenas de consultas kNN e processá-las em lote evita a sobrecarga por solicitação.</p></li><li><p><strong>Selecione sementes de alta densidade com diversificação</strong>: os candidatos acima da densidade média são classificados por densidade decrescente e aceitos avidamente somente quando a semelhança de cosseno com cada semente existente estiver abaixo de um limite de separação. Este é o único processamento do lado do cliente (~0,01s para 8k documentos).</p></li><li><p><strong>Classificar todos os documentos em relação aos centroides via</strong> <strong><code>msearch</code></strong> <strong>kNN</strong>: cada semente atua como um centroide; uma pesquisa kNN recupera documentos próximos acima de um limite de similaridade. Cada documento é atribuído ao centroide que o retornou com a maior pontuação. Pequenos clusters são dissolvidos em ruído.</p></li></ol><p>O Elasticsearch cuida do trabalho pesado: <code>msearch</code> para sondas de densidade, <code>msearch</code> para classificação e <code>significant_text</code> para rotulagem. Para esse corpus (8.495 documentos), a amostra de sonda de densidade de 5% executa consultas de sonda de 425 kNN, que <code>msearch</code> agrupam lotes em nove chamadas HTTP (no tamanho de lote 50), evitando a sobrecarga de uma solicitação por sonda. Combinado com <code>bbq_disk</code> busca ANN, isso mantém a etapa de clustering rápida e escalável. As consultas kNN usam um valor mínimo de <a href="https://www.elastic.co/docs/deploy-manage/production-guidance/optimize-performance/approximate-knn-search"><code>num_candidates</code></a> para velocidade durante a passagem de clustering; consultas de busca em produção devem usar valores de <code>num_candidates</code> mais altos para melhorar a recordação, mas isso custa latência.</p><p>Clusters têm tamanhos naturais determinados pela densidade do espaço de embedding ao redor de cada centroide, não por um limite de <code>k</code> rígido. Regiões temáticas densas produzem clusters maiores; tópicos de nicho produzem agrupamentos menores.</p><h4>Por que escolher KMeans ou HDBSCAN?</h4><p>O algoritmo KMeans pressupõe clusters esféricos e requer a matriz completa N×d na memória. Para corpora que cabem na memória, <a href="https://scikit-learn.org/stable/modules/generated/sklearn.cluster.HDBSCAN.html">HDBSCAN</a> é uma excelente alternativa. Ele lida com formatos de cluster arbitrários e possui semântica de densidade bem compreendida.</p><p>A abordagem de centroide sondado por densidade mira em um nicho diferente: corpora onde você quer armazenamento, recuperação e clustering em um único sistema, ou onde a escala torna as operações matriciais do lado do cliente impraticáveis. Ele usa o Elasticsearch kNN como primitiva de computação, lida com tamanhos arbitrários de cluster e mantém quase toda a computação no lado do servidor.</p>    Clustered global index in 31.6s
      Total clusters: 82
      Total noise:    2420 (28.5%)
      Density probes: 425 kNN queries via 9 _msearch HTTP calls<h4>Entendendo a taxa de ruído</h4><p>A taxa de ruído de ~28% é intencional, não uma falha. Documentos que não cabem em nenhum cluster denso na <code>similarity_threshold</code> configurada ficam sem atribuição, em vez de serem forçados a uma correspondência ruim. Isso funciona como um filtro de qualidade: colunas de opinião, artigos curtos e reportagens isoladas naturalmente resistem ao clustering porque falta a densidade temática que define um grupo coerente.</p><p>O limiar é ajustável: reduzir <code>similarity_threshold</code> produz clusters mais abrangentes (mais documentos atribuídos, mas clusters menos coesos), enquanto aumentá-lo torna os clusters mais compactos e aumenta a fração de ruído. Para este corpus de conteúdo de notícias misto, ~30% de ruído é um ponto de operação razoável. Implantações em produção devem ajustar o limiar com base em critérios de qualidade específicos do domínio.</p><h3>Rótulos automáticos com significant_text</h3><p>Agora, cada cluster precisa de um rótulo de fácil compreensão. A agregação <code>significant_text</code> do Elasticsearch encontra termos que aparecem com frequência incomum em um conjunto em primeiro plano (o cluster) em comparação com um conjunto em segundo plano (o corpus completo).</p><p>Nos bastidores, ele usa uma heurística estatística (pontuação JLH por padrão) que equilibra mudanças de frequência absolutas e relativas, sem machine learning, sem chamadas de grandes modelo de linguagem (LLM). Um cluster sobre política do Reino Unido pode apresentar termos como <code>starmer</code>, <code>labour</code>, <code>downing</code> porque esses termos são desproporcionalmente comuns nesse cluster em comparação ao conjunto geral de notícias.</p><p>Para essa passagem global, os rótulos são calculados diretamente contra <code>docs-clustering-all</code>, então tanto o plano de frente quanto o plano de fundo são extraídos do mês inteiro. Na parte 2, a rotulagem utiliza o padrão de indexação diário (<code>docs-clustering-*</code>), um caractere curinga que permite que consultas abranjam todos os índices correspondentes simultaneamente, para dar <code>significant_text</code> um plano de fundo mais amplo e melhor contraste.</p><p>Um formato de consulta mínimo tem a seguinte aparência:</p>{
  "size": 0,
  "query": { "term": { "cluster_id": "72" } },
  "aggs": {
    "label_terms": {
      "significant_text": {
        "field": "text",
        "size": 5,
        "filter_duplicate_text": true
      }
    }
  }
}<p><code>significant_text</code> serve também como um filtro de qualidade: clusters que não produzem termos significativos não possuem vocabulário distintivo. São agrupamentos incoerentes que devem ser dissolvidos e reduzidos a ruído, em vez de receberem um rótulo enganoso.</p><p>Uma etapa de limpeza determinística e leve remove termos de rótulos irrelevantes (tokens numéricos, palavras genéricas) e recorre a um título representativo quando necessário. Isso mantém os rótulos nativos do Elasticsearch enquanto melhora a legibilidade.</p>    Sample cluster labels:
      cluster   3  (200 docs)  arsenal | mikel | villa
      cluster   1  (198 docs)  volodymyr | ukrainian | kyiv
      cluster   0  (196 docs)  hostages | hamas | israeli
      cluster   4  (187 docs)  scrum | rugby | borthwick
      cluster  52  (185 docs)  fossil | renewable | renewables
      cluster  10  (156 docs)  labour | gwynne | mps
      cluster  40  (151 docs)  novel | novels | literary
      cluster  11  (149 docs)  mewis | sarina | wiegman
      cluster  44  (143 docs)  flooding | rainfall | rain
      cluster  13  (131 docs)  doge | musk | elon
      cluster  12  (128 docs)  murder | insp | knockholt
      cluster   5  (124 docs)  putin | backstop | starmer


    Reassigned 35 docs from incoherent clusters to noise
    Total docs: 8,495
    Clustered:  6,040 (71.1%)
    Noise:      2,455 (28.9%)<h3>Visualizando os clusters</h3><p>As visualizações abaixo mostram o que a análise de clustering global descobriu: uma análise por data de documentos agrupados versus documentos de ruído, uma projeção UMAP para o mês inteiro e um gráfico de composição de fontes confirmando que os agrupamentos refletem tópicos em vez de fontes.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4ed087d8b6dac2a0/6a17094260084b44543c4501/99099f5adaa945ae4097c50b0d7151c7dd28872e-1000x400.png" alt="Distribuição diária de documentos agrupados versus documentos com ruído" /><p>Distribuição diária de documentos agrupados versus ruídos ao longo de fevereiro de 2025.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf5ca320bc91131ab/6a17094366c4f95828f8bfbf/477c6c7177942955a942f85f5c881da50e517915-1100x700.png" alt="Projeção UMAP para o mês inteiro com todos os documentos" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5f554a6bc2bc367b/6a170945a929cfa7a1ae0947/4f4302556c8974c416842452cf33bca06e90b966-1100x700.png" alt="Projeção UMAP mostrando apenas documentos agrupados" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8e40c26f89ce5523/6a17094747d49c147d2d8974/327f96a79e382ef30614cb0570aa7fccd822b8f8-1100x700.png" alt="[Projeção UMAP destacando um único cluster]" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf1dd2c19ae628f1d/6a1709481949f7a630e7a9a3/acfb1524a10e24d6ff2412e7c3ec0f2b3ac75193-900x600.png" alt="Mistura de fontes por cluster mostrando agrupamento baseado em tópicos" /><p>Cada ilha colorida no UMAP representa um cluster: um grupo de artigos sobre o mesmo tema descobertos puramente por similaridade de incorporação. Os pontos de ruído cinza são artigos que não se encaixavam perfeitamente em nenhum cluster (artigos curtos, artigos de opinião ou histórias isoladas).</p><p>O gráfico de detalhamento da fonte confirma que os clusters contêm artigos de <strong>ambos</strong> BBC News e The Guardian. O clustering está encontrando <em>tópicos</em>, não <em>fontes</em>, exatamente o que a descoberta não supervisionada deve produzir.</p><h3>Explorando a amplitude do cluster com o diversificador</h3><p>O algoritmo kNN simples retorna os documentos mais semelhantes ao centroide de um cluster (o núcleo denso). Mas clusters reais abrangem subtópicos. O <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers/diversify-retriever"><strong>recuperador de diversificação</strong></a> usa a relevância marginal máxima (MMR) para destacar documentos que são relevantes para o centroide, mas também <em>diferentes entre si</em>.</p><p>O parâmetro chave é <strong>λ (lambda):</strong></p><ul><li><p>λ = 1,0 → relevância pura (o mesmo que kNN simples).</p></li><li><p>λ = 0,0 → diversidade pura (resultados de distribuição máxima).</p></li><li><p>λ = 0,5 → equilibrado: relevante para o tópico, mas abordando diferentes perspectivas.</p></li></ul><p>Uma forma mínima de solicitação de recuperador é assim:</p>{
  "size": 8,
  "retriever": {
    "diversify": {
      "type": "mmr",
      "field": "embedding",
      "lambda": 0.5,
      "query_vector": "&lt;cluster-centroid-vector&gt;",
      "retriever": {
        "knn": {
          "field": "embedding",
          "query_vector": "&lt;cluster-centroid-vector&gt;",
          "k": 50,
          "num_candidates": 100
        }
      }
    }
  }
}<p>Os parâmetros <code>type</code>, <code>field</code>, e <code>query_vector</code> são necessários no nível de diversificação: <code>field</code> informa à MMR qual campo dense_vector usar para similaridade entre resultados, e <code>query_vector</code> fornece o ponto de referência para a pontuação de relevância.</p><p>Isso permite que você responda: "O que esse cluster cobre de fato?" em vez de apenas "Qual é o ponto central?"</p>    Exploring cluster 52 (185 docs)
    Label: fossil | renewable | renewables
    Centroid computed (dim=1024)


    ========================================================================
    Plain kNN (closest to centroid)
    ========================================================================
      1. [0.9738] Green campaigners fear ministers are poised to award billions of pounds in fresh subsidies to Drax power station, despite strong concerns...
      2. [0.9710] Thirteen more oil and gas licences could be cancelled as ministers decide new guidance for fossil fuel extraction after a landmark court...
      3. [0.9699] Experts have accused the fossil fuel industry of seeking special treatment after lobbyists argued greenhouse gas emissions from oilfields...
      4. [0.9681] Burning wood is a terrible way of producing electricity . Chopping down trees destroys habitats for wildlife, and growing new trees cannot...
      5. [0.9649] Keir Starmer will do huge damage to the global fight against climate change if he gives in to political pressure and allows the development...
      6. [0.9641] Labour will next week be confronted with stark policy choices that threaten to expose the fault lines between the Treasury and the...
      7. [0.9638] The Drax power station near Selby in north Yorkshire burns imported wood pellets  The government has agreed a new funding arrangement with...
      8. [0.9581] If you care about the world we are handing on to future generations, the news on Thursday morning was dramatic. This January was the...
    
    ========================================================================
    Diversify retriever (MMR, lambda=0.5)
    ========================================================================
      1. [0.9738] Green campaigners fear ministers are poised to award billions of pounds in fresh subsidies to Drax power station, despite strong concerns...
      2. [0.9434] Oil and gas interests have waged a coordinated campaign to kill pro-electrification policies that ban gas connections in new buildings ,...
      3. [0.9303] It was interesting to read that new licences for oil and gas production in the North Sea are being delayed by legal action ( Thirteen more...
      4. [0.9139] The US energy secretary, Chris Wright, has said he “would love to see Australia get in the game of supplying uranium and maybe going down...
      5. [0.9077] Rachel Reeves was facing criticism on Saturday night as it was confirmed that a report she cited as evidence that a third ­runway at...
      6. [0.8996] When Margaret Thatcher opened the Hadley Centre for Climate Change in 1990 journalists suggested she was attempting to appear to be doing...
      7. [0.8993] The vast majority of governments are likely to miss a looming deadline to file vital plans that will determine whether or not the world has...
      8. [0.8987] European imports of seaborne gas shipments fell by a fifth last year to their lowest level since the pandemic, according to a new report,...
    
    Overlap: 1/8 documents appear in both result sets
    
    Avg pairwise similarity (lower = more diverse):
      Plain kNN:          0.9057
      Diversify retriever: 0.6965<p>Os resultados simples kNN se agrupam em torno de um ângulo do tema: os documentos mais semelhantes ao centroide e entre si. O recurso de recuperação de diversidade revela diferentes facetas do mesmo cluster: subtópicos, fontes diversas e perspectivas variadas.</p><p>A métrica de diversidade confirma isso quantitativamente: a similaridade média entre pares é menor para os resultados do recuperador diversificado, o que significa que os documentos retornados abrangem um espectro mais amplo.</p><p>Isso é útil para você:</p><ul><li><p><strong>Entender o que um cluster cobre</strong>, não apenas o centro, mas também as bordas.</p></li><li><p><strong>Geração de resumos</strong>. Documentos representativos diversos oferecem um material melhor para um LLM.</p></li><li><p><strong>Encontrar exemplos representativos</strong> para análise humana ou rotulagem posterior.</p></li><li><p><strong>Verificações de qualidade</strong>. Se os resultados diversos parecerem incoerentes, o cluster pode precisar ser dividido.</p></li></ul><h2>Parte 2: Cadeias de histórias temporais</h2><h3>Acompanhando histórias ao longo dos dias</h3><p>A parte 1 fez o clustering de todo o mês global para descoberta de tópicos. Para o fluxo temporal, a mesma classificação de centroides sondados por densidade é executada independentemente por dia em <strong>índices diários</strong>, e depois os clusters são vinculados ao longo de dias consecutivos. Observe que os clusters diários são independentes dos clusters globais da parte 1; cada dia produz as próprias atribuições de agrupamento e rótulos ajustados ao conteúdo daquele dia.</p><h4><strong>A abordagem de vinculação: amostragem e consulta</strong></h4><p>Para cada cluster no dia A:</p><ol><li><p>Você pode ver uma amostra de alguns documentos representativos.</p></li><li><p>Executar kNN contra o índice do dia B.</p></li><li><p>Conte quantos acessos caem em cada cluster B do dia.</p></li><li><p>Se a fração de acerto ultrapassar um limite (fração de kNN ≥ 0,4), registre um link.</p></li></ol><p>Isso é rápido (apenas alguns documentos por cluster são consultados, nem todos) e usa o kNN nativo do Elasticsearch, sem necessidade de ferramentas externas.</p>Preparing daily indices for temporal linkage...


Indexed 8,495 docs into 28 daily indices


Temporal links found: 808 in 145.4s

Strongest links:
  2025.02.01 'league | arsenal | premier' -&gt; 2025.02.02 'league | season | striker'  (100%)
  2025.02.03 'league | striker | loan' -&gt; 2025.02.04 'league | striker | season'  (100%)
  2025.02.03 'score | operator | gedling' -&gt; 2025.02.04 'league | striker | season'  (100%)
  2025.02.12 'playoff | leg | bayern' -&gt; 2025.02.13 'league | players | injury'  (100%)
  2025.02.14 'league | injury | football' -&gt; 2025.02.15 'league | premier | football'  (100%)
  2025.02.18 'russia | ukraine | talks' -&gt; 2025.02.19 'saudi | russia | arabia'  (100%)
  2025.02.18 'football | league | bayern' -&gt; 2025.02.19 'league | manchester | players'  (100%)
  2025.02.21 'league | premier | manchester' -&gt; 2025.02.22 'game | players | defeat'  (100%)
  2025.02.21 'rugby | calcutta | brilliant' -&gt; 2025.02.22 'game | players | defeat'  (100%)
  2025.02.26 'metals | kyiv | ukrainian' -&gt; 2025.02.27 'ukraine | russia | talks'  (100%)<p>Uma fração de kNN de 100% significa que todos os documentos mostrados do cluster de origem foram atribuídos ao mesmo cluster de destino, o vínculo mais forte possível entre os dias. A maioria dos links acima está relacionada ao futebol, o que faz sentido: a cobertura da Premier League é feita diariamente com alta consistência de tópicos.</p><p>O link <code>score | operator | gedling</code> → <code>league | striker | season</code> é um exemplo de um cluster de futebol local de nicho (Gedling é um clube fora da liga) sendo absorvido pelo cluster mais amplo da Premier League no dia seguinte, um efeito natural do clustering diário em diferentes granularidades.</p><h3>Criar cadeias de histórias</h3><p>Uma cadeia de histórias é uma sequência de clusters ligados ao longo de dias consecutivos.</p><p>Ligações pareadas individuais indicam que o cluster "política do Reino Unido" de segunda-feira está conectado ao de terça-feira. As cadeias revelam o arco completo: uma história que começa na segunda-feira, evolui durante a semana e encerra na sexta-feira.</p><p>As cadeias são construídas de forma ávida a partir de links com uma fração kNN ≥ 0,4, o que significa que pelo menos 40% dos documentos mostrados do cluster de origem chegaram a um único cluster de destino. A partir do cluster mais antigo, o algoritmo sempre segue o link de saída mais forte.
</p>    Strong links (kNN fraction &gt;= 0.4): 244
    Story chains spanning 3+ days: 18
      Chain 1: 'ukrainian | kyiv | eastern' (19 days: Feb 3 → Feb 21)
      Chain 2: 'playing | opposition' (19 days: Feb 10 → Feb 28)
      Chain 3: 'tadhg | maro | cadan' (10 days: Feb 1 → Feb 10)
      Chain 4: 'invade | china | putin' (8 days: Feb 21 → Feb 28)
      Chain 5: 'elected | labour | leader' (7 days: Feb 12 → Feb 18)
      Chain 6: 'film | swift | awards' (6 days: Feb 2 → Feb 7)
      Chain 7: 'amendment | termination | reporting' (6 days: Feb 12 → Feb 17)
      Chain 8: 'officers | scene | police' (5 days: Feb 1 → Feb 5)<p>A rede mais longa acompanha a cobertura Ucrânia–Rússia por 19 dias consecutivos, o que não surpreende dada a intensidade geopolítica em fevereiro de 2025. O segundo mais longo acompanha o futebol da Premier League ao longo de 19 dias do mês. Cadeias mais curtas captam a temporada de premiações (filme/prêmios, seis dias), o rúgbi Six Nations (10 dias) e a cobertura da liderança política do Reino Unido (sete dias). Cada cadeia representa um arco narrativo que o algoritmo descobriu ao incorporar similaridade entre índices diários.</p><h3>Sankey: Visualizando o fluxo da história</h3><p>Um diagrama de Sankey é uma visualização de fluxo onde a largura da ligação representa a força da conexão. Aqui, cada faixa vertical representa um dia, cada nó é um cluster diário (dimensionado pela contagem de documentos), e cada caminho colorido traça uma cadeia de histórias ao longo do tempo. A largura do link codifica a força de sobreposição kNN: links mais espessos indicam que mais documentos mostrados caíram no cluster alvo. As cores são consistentes por cadeia, então um único caminho colorido da esquerda para a direita representa o progresso de uma história.</p><p>Por exemplo, a cadeia Ucrânia-Rússia (visível como um dos caminhos mais longos) flui continuamente desde o início de fevereiro até a terceira semana, com elos consistentemente espessos indicando forte continuidade temática ao longo dos dias.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta8c243e0c5773440/6a17093fa6c2b98c86e7968c/100a60a7fb85da8ab3813fd071a82c93f2c3f318-1300x650.png" alt="Sequências temporais de histórias ao longo do mês de fevereiro de 2025" /><p><em>Cadeias temporais de histórias que fluem ao longo de fevereiro de 2025. Cada caminho colorido representa uma história que persiste ao longo dos dias; com a largura indicando a força de sobreposição do kNN.</em></p><h2>O que essa abordagem oferece</h2><p>Esta análise abordou um pipeline completo de clustering de documentos não supervisionado construído no Elasticsearch:</p><ol><li><p><strong>Embeddings de clustering</strong>: os adaptadores específicos de tarefa do Jina v5 produzem embeddings otimizadas para agrupamento de tópicos, e não apenas para correspondência de consulta-documento.</p></li><li><p><strong>Clustering de descoberta global</strong>: clustering o mês inteiro em um único índice maximiza a descoberta de tópicos ao longo dos dias.</p></li><li><p><strong>Classificação de centroides com base na densidade</strong>: amostra 5%, sondar a densidade via <code>msearch</code> kNN, selecionar sementes diversas de alta densidade, classificar todos os documentos em relação aos centroides. O Elasticsearch cuida do processamento pesado; apenas a seleção de sementes executa do lado do cliente (~0,01s).</p></li><li><p><a href="https://www.elastic.co/docs/reference/aggregations/search-aggregations-bucket-significanttext-aggregation"><strong><code>significant_text</code></strong></a> <strong>rotulagem</strong>: o teste de significância produz rótulos de cluster significativos sem qualquer modelo de ML ou anotação manual. Clusters que não produzem termos significativos são incoerentes e são rebaixados a ruído, uma porta de qualidade integrada.</p></li><li><p><strong>Vinculação temporal de histórias</strong>: índices diários e kNN de índice cruzado entre amostra e consulta rastreiam como as histórias evoluem ao longo do tempo.</p></li></ol><p><strong>Principais conclusões:</strong></p><ul><li><p>O tipo de tarefa de incorporação importa: embeddings de clustering produzem grupos tópicos mensuravelmente mais compactos.</p></li><li><p>O Elasticsearch pode atuar tanto como camada de armazenamento <em>quanto</em> como motor de clustering por meio da <a href="https://www.elastic.co/docs/solutions/search/vector/knn">busca kNN</a>.</p></li><li><p>A classificação de centroides baseada em densidade mantém quase toda a computação no lado do servidor e produz clusters com tamanhos naturais determinados pela densidade do espaço de incorporação.</p></li><li><p><code>significant_text</code> é rápido, compreensível e eficaz tanto para autorrotulagem quanto para controle de qualidade.</p></li></ul><p><strong>Quando essa abordagem é útil:</strong></p><ul><li><p>Você tem texto com carimbo de data e hora e quer descobrir tópicos sem dados de treinamento rotulados.</p></li><li><p>Você precisa de uma plataforma para armazenamento, busca vetorial, rotulagem e ligação temporal.</p></li></ul><p><strong>Extensões para explorar:</strong></p><ul><li><p>Clustering por múltiplos períodos (semanal, pacotes mensais).</p></li><li><p>Ingestão em tempo real com atribuição incremental de cluster.</p></li><li><p>Resumos de cluster gerados pelo LLM usando os termos significant_text como sementes.</p></li><li><p>Em escala maior, centroides KMeans mostrados podem servir como sementes de aquecimento para clustering baseado em densidade, reduzindo o custo da fase da sonda.</p></li></ul><h2>Experimente você mesmo</h2><p>Troque seu próprio corpus de documentos com carimbo de data; qualquer coleção de texto com datas funciona com esse pipeline. O notebook completo e o código de suporte estão disponíveis no <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/unsupervised-document-clustering-elasticsearch-jina-embeddings">repositório complementar</a>.</p><ul><li><p><a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;cta=cloud-registration&amp;tech=trial&amp;plcmt=article%20content&amp;pg=search-labs"><strong>Inicie uma avaliação gratuita do Elastic Cloud</strong></a>: instale um cluster gerenciado com suporte <code>bbq_disk</code> em questão de minutos.</p></li><li><p><a href="https://www.elastic.co/elasticsearch/serverless"><strong>Experimente o Elasticsearch Serverless</strong></a>: sem gerenciamento de cluster, escala automática e com suporte.</p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/unsupervised-document-clustering-elasticsearch-jina-embeddings</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/unsupervised-document-clustering-elasticsearch-jina-embeddings</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <category><![CDATA[Pesquisa de aprendizado de máquina]]></category>
    <category><![CDATA[Jina AI]]></category>
    <dc:creator><![CDATA[Matthew Adams]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4bd7dd10a7cd6dc8/6a17094a14b270581de3c5b6/662c00694c3e0c2fb2128098bdb6813df9e86a72-1280x720.png" length="0" type="image/png"/>
    <pubDate>Fri, 10 Apr 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Quando o TSDS encontra o ILM: projetando fluxos de dados de séries temporais que aceitam dados tardios]]></title>
    <description><![CDATA[Como os limites de tempo do TSDS interagem com as fases do ILM e como projetar políticas que tolerem métricas atrasadas.]]></description>
    <content:encoded><![CDATA[<p>Recentemente, migrei o cluster de métricas de um cliente de "tudo na camada ativa" para uma arquitetura hot/cold/frozen. Era uma mudança que eu já havia feito dezenas de vezes antes. Em poucos minutos, o Logstash parou completamente de avançar os dados.</p><p>O Elasticsearch estava rejeitando métricas de chegada tardia. Essas rejeições fizeram o pipeline ficar atrasado, resultando em dados mais tardios, o que desencadeou ainda mais rejeições. Com o tempo, o pipeline parou completamente.</p><p>Tivemos que restaurar a partir do snapshot, reindexar os dados e redesenhar o pipeline de ingestão para recuperar.</p><p>A causa raiz não era a gestão de ciclo de vida de índices (ILM) em si. Tratava-se de fluxos de dados de séries temporais (TSDS) e como eles aplicam índices de apoio com limite temporal.</p><p>O TSDS pode reduzir os requisitos de armazenamento para métricas em 40–70%, mas as mudanças na arquitetura que tornam o TSDS eficiente também alteram a forma como os índices se comportam ao longo do tempo. Essas mudanças são importantes ao projetar políticas de ILM ou quando seus pipelines de ingestão podem produzir dados tardios.</p><h2>TL;DR</h2><p>Ao usar o TSDS:</p><ul><li><p>Índices de suporte aceitam apenas documentos dentro de uma janela de tempo específica.</p></li><li><p>Se dados tardios chegarem após um índice se tornar frio ou congelado, o Elasticsearch rejeitará esses documentos ou os encaminhará para o armazenamento de falhas, caso esteja configurado.</p></li></ul><p>Regra de design:</p>warm_min_age &gt; rollover_max_age + maximum_expected_lateness<h2>O que é um fluxo de dados de séries temporais?</h2><p>Um<em> fluxo de dados de série temporal</em> (TSDS) é um fluxo de dados especializado otimizado para dados métricos. Os dados são roteados de modo que documentos relacionados fiquem localizados dentro dos mesmos fragmentos, otimizando-os para consulta e recuperação. Como o Elasticsearch faz isso:</p><p>Cada documento contém:</p><ul><li><p>Um registro de data e hora.</p></li><li><p>Campos dimensionais que identificam a série de tempo.</p></li><li><p>Campos métricos representando valores medidos.</p></li></ul><p>Alguns exemplos:</p><ul><li><p>Uso da CPU por host.</p></li><li><p>Solicitar latência por serviço.</p></li><li><p>Leituras de temperatura por sensor.</p></li></ul><p><em>As dimensões </em>identificam o que queremos medir, enquanto <em>as métricas </em>representam valores que mudam com o tempo.</p><h3>Dimensões</h3><p>Dimensões descrevem a entidade medida.</p><p>Exemplos:</p>host.name
service.name
container.id<p>Definimos eles em mapeamentos com:</p>time_series_dimension: true<h3>Métricas</h3><p>Métricas representam valores numéricos e são definidas usando:</p>time_series_metric<p>Tipos comuns de métricas:</p><ul><li><p>Indicador: Valores que sobem e descem.</p></li><li><p>Contador: valores que aumentam até serem reiniciados.</p></li></ul><p>O Elastic Agent coleta principalmente métricas e dados de log. Mesmo que você não tenha habilitado manualmente nenhum índice TSDS, ainda pode tê-los no seu cluster.</p><h3>O campo _tsid</h3><p>O Elasticsearch gera internamente um valor <code>_tsid</code> a partir dos campos de dimensão. Isso permite que documentos com dimensões idênticas sejam roteados para o mesmo shard, melhorando:</p><ul><li><p>Compressão.</p></li><li><p>Local da consulta.</p></li><li><p>Desempenho de agregações.</p></li></ul><h2>A principal diferença: índices de apoio com prazo definido</h2><p>Os fluxos de dados tradicionais sempre gravam no índice de suporte mais recente, chamado <em>índice de gravação</em>, mas o TSDS se comporta de maneira diferente.</p><p>Cada índice de apoio TSDS tem uma janela de tempo definida e aceita apenas documentos com <code>@timestamp</code> valores que se encaixam nessa janela:</p>GET _data_stream/my-metrics-data-stream


     "index_mode": "time_series",
     "time_series": {
       "temporal_ranges": [
         {
           "start": "2026-01-15T14:35:50.000Z",
           "end": "2026-03-16T11:34:40.000Z"
         }
       ]
     }<p>Quando um documento é indexado, o Elasticsearch encaminha o documento para o índice de suporte responsável por aquele timestamp, o que significa que, ao contrário dos índices tradicionais, um TSDS pode gravar em vários índices de suporte simultaneamente.</p><p>Por exemplo:</p><ul><li><p>Dados em tempo real → índice mais recente.</p></li><li><p>Dados tardios → índice anterior cobrindo esse intervalo de tempo.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7b001853af30d5f8/6a17dc2cfaa9137a7d93c751/31af2bb3b3dc24db8342e791e1db77a44659ba7a-1589x502.png" alt="Linha do tempo mostrando como um documento tardio é direcionado para um índice mais antigo, enquanto um documento atual vai para o índice mais recente." /><h2>Projetando para dados tardios</h2><p>Os pipelines de ingestão reais raramente entregam métricas perfeitamente no prazo. As métricas podem ser atrasadas por interrupções de rede, acúmulos no caminho, ingestão em lote e perda de dispositivos de borda, que se reconectam e começam a recuperar o atraso.</p><p>Índices tradicionais absorvem silenciosamente esses atrasos. O TSDS não.</p><p>Se o carimbo de data/hora de um documento estiver fora da faixa de índices de apoio graváveis, o Elasticsearch o rejeitará, o que significa que sua política de ILM deve considerar os dados tardios.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6e8eae1b2ddad142/6a17dc2e1d1b8335a793e35c/32a103b95b20e31615c214271e27811a7ee315ae-1999x691.png" alt="Linha do tempo do ciclo de vida do índice" /><h2>A restrição crítica</h2><p>Os índices de suporte precisam permanecer com permissão de escrita por tempo suficiente para receber dados com atraso.</p><p>Em termos práticos:</p>time_until_readonly &gt; maximum_expected_lateness<p>Como o ILM mede o tempo de existência a partir do rollover, a regra operacional passa a ser:</p>warm_or_cold_min_age &gt; rollover_max_age + maximum_expected_lateness<p></p><p>Por exemplo, se as métricas podem chegar até seis horas atrasadas, os índices devem permanecer graváveis pelo menos seis horas após o rollover.</p><p></p><p>Desconsiderar essa restrição foi exatamente o que causou a falha de ingestão descrita anteriormente. Os dados tardios eram direcionados para um índice anterior, que já estava na camada cold e, portanto, era bloqueado para escrita.</p><p></p><h2>Tratamento de documentos rejeitados</h2><p>Quando o TSDS rejeita um documento, o Elasticsearch retorna um erro, indicando que o carimbo de data e hora não está dentro da faixa de índices graváveis. Como seu pipeline de ingestão lida com esse erro determina se você perde dados ou trava a ingestão de dados.</p><p>O principal mecanismo para lidar com documentos rejeitados é o armazenamento de falhas.</p><h3>Repositório de falhas (recomendado no Elasticsearch 9.1+)</h3><p>O Elasticsearch 9.1 introduziu o armazenamento de falhas, que captura automaticamente documentos rejeitados. Em vez de retornar erros aos clientes, o Elasticsearch grava documentos rejeitados em um índice dedicado de falhas dentro do fluxo de dados.</p><p>Você pode inspecionar falhas usando:</p>GET metrics-myapp::failures/_search<p>O uso do armazenamento de falhas impede que os pipelines de ingestão travem devido a erros de rejeição, enquanto preserva os dados com falha para análise ou <a href="https://www.elastic.co/docs/manage-data/data-store/data-streams/reindex-tsds">reindexação</a>.</p><h2>Monitoramento de questões de rejeição</h2><p>Os problemas de chegada tardia geralmente aparecem primeiro como anomalias de ingestão. Você pode notá-los primeiro como:</p><ul><li><p>Quedas repentinas na taxa de indexação.</p></li><li><p>Picos nos documentos rejeitados.</p></li><li><p>Um número crescente de entradas de lojas que falham.</p></li><li><p>Diferenças de incompatibilidade entre entradas e saídas do pipeline contagem.</p></li></ul><p>Alertas nesses sinais permitem que os operadores detectem problemas antes que os pipelines parem. Fluxos de trabalho, trabalhos de Machine Learning e outros mecanismos podem ser usados para automatizar a detecção e notificação.</p><h2>Lista de verificação de migração para TSDS + ILM</h2><p>Se você estiver migrando um cluster de métricas para o TSDS, introduzindo a hierarquização do ILM ou atualizando para uma versão do Elasticsearch em que as métricas são TSDS por padrão, revise esses itens primeiro.</p><h3><strong>1. Medir a latência de ingestão</strong></h3><p>Antes de mudar as políticas de ILM, determine:</p><ul><li><p>Atraso normal na ingestão de dados.</p></li><li><p>Pior caso de atraso durante os incidentes.</p></li><li><p>Atrasos causados por pipelines em lote.</p></li></ul><p>O projeto do seu ILM deve acomodar o máximo de atraso realista.</p><h3><strong>2. Verificar as janelas de tempo do índice</strong></h3><p>Inspecione seus índices de respaldo de TSDS:</p>GET _data_stream/&lt;your-stream&gt;<p>Analise:</p><ul><li><p><code>time_series.start_time</code></p></li><li><p><code>time_series.end_time</code></p></li></ul><p>Esses limites determinam quais índices podem aceitar documentos. Entender essas janelas pode ajudar a determinar o quanto os dados podem estar atrasados antes de serem rejeitados.</p><h3><strong>3. Dimensione o nível hot para chegadas tardias</strong></h3><p>Garanta que os índices backing permaneçam graváveis por tempo suficiente para os dados tardios.</p><p>Regra operacional:</p><ul><li><p><code>warm_min_age &gt; rollover_max_age + maximum_expected_lateness</code></p></li></ul><p>Lembre-se, os índices devem permanecer graváveis por pelo menos seis horas se as métricas chegarem com seis horas de atraso.</p><h3><strong>4. Decida o que fazer com documentos rejeitados</strong></h3><p>Escolha uma estratégia antes de ativar o TSDS:</p><ul><li><p>Armazenamento de falhas (recomendado no Elasticsearch 9.1+).</p></li><li><p>Fila de dead letter do Logstash.</p></li><li><p>Índice de contingência para chegadas tardias.</p></li><li><p>Aceitar a perda limitada de dados.</p></li></ul><h3><strong>5. Monitorar a saúde da ingestão</strong></h3><p>Adicionar alertas para:</p><ul><li><p>A taxa de indexação cai.</p></li><li><p>Documentos rejeitados.</p></li><li><p>Crescimento do armazenamento de falhas.</p></li><li><p>Desajustes de entrada/saída do pipeline.</p></li></ul><p>Problemas de dados tardios geralmente aparecem primeiro como anomalias de ingestão.</p><h2>Resumo</h2><p>Fluxos de dados de séries temporais oferecem grandes melhorias de armazenamento e desempenho para cargas de trabalho de métricas, mas introduzem uma mudança arquitetônica importante: os índices de suporte têm limite temporal, o que afeta o comportamento do ILM.</p><p>Ao usar o TSDS:</p><ul><li><p>Os índices devem permanecer graváveis tempo suficiente para aceitar dados tardios.</p></li><li><p>Os pipelines de ingestão devem lidar com documentos rejeitados com segurança.</p></li></ul><p>A regra fundamental a lembrar é:</p>warm_min_age &gt; rollover_max_age + maximum_expected_lateness<p>Se você projetar políticas de ILM em torno dessa restrição, o TSDS funcionará extremamente bem para cargas de trabalho de métricas.</p><p>Se ignorar isso, seu pipeline de ingestão pode descobrir esses limites de tempo da pior forma.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/tsds-ilm-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/tsds-ilm-elasticsearch</guid>
    <category><![CDATA[Dados de indexação]]></category>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <dc:creator><![CDATA[Bret Wortman]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcacf154aeacb29d8/6a17dc30dbb4ffc7ddfb557f/e4c46e4a6f746d9c845857e80de036f5d51cd4e7-1280x720.png" length="0" type="image/png"/>
    <pubDate>Thu, 02 Apr 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[LINQ para Elasticsearch ES|QL: escreva Consultas em C# e Consulte o Elasticsearch]]></title>
    <description><![CDATA[Explorando o novo provedor LINQ para Elasticsearch ES|QL no cliente Elasticsearch .NET, que permite escrever código C# automaticamente traduzido para consultas ES|QL.]]></description>
    <content:encoded><![CDATA[<p>A partir das <strong>versões 9.3.4</strong> e <strong>8.19.18</strong>, o cliente Elasticsearch .NET inclui um provedor de <a href="https://learn.microsoft.com/en-us/dotnet/csharp/linq/">Consulta Integrada em Linguagem (LINQ) </a>que traduz expressões LINQ em C# para a <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql.html">Linguagem de Consulta Elasticsearch (ES|QL)</a> em tempo de execução. Em vez de escrever manualmente as strings ES|QL, você compõe consultas usando <code>Where</code>, <code>Select</code>, <code>OrderBy</code>, <code>GroupBy</code> e outros operadores padrão. O provedor cuida da tradução, parametrização e desserialização dos resultados, inclusive o streaming por linha que mantém o uso da memória constante, independentemente do tamanho do conjunto de resultados.</p><h2>Sua primeira consulta</h2><p>Comece definindo um objeto CLR simples (POCO) que mapeia para o seu índice Elasticsearch. Os nomes das propriedades são resolvidos para nomes de coluna ES|QL via atributos <code>System.Text.Json</code> padrão, como <code>[JsonPropertyName]</code>, ou via <code>JsonNamingPolicy</code> configurado. As mesmas regras <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/dotnet/source-serialization">de serialização de origem</a> que se aplicam ao restante do cliente também se aplicam aqui.</p>using System.Text.Json.Serialization;

public class Product
{
    [JsonPropertyName("product_id")]
    public string Id { get; set; }

    public string Name { get; set; }

    public string Brand { get; set; }

    [JsonPropertyName("price_usd")]
    public double Price { get; set; }

    [JsonPropertyName("in_stock")]
    public bool InStock { get; set; }
}<p>Com o tipo definido, uma consulta fica assim:</p>var minPrice = 100.0;
var brand = "TechCorp";

await foreach (var product in client.Esql.QueryAsync&lt;Product&gt;(q =&gt; q
    .From("products")
    .Where(p =&gt; p.InStock &amp;&amp; p.Price &gt;= minPrice &amp;&amp; p.Brand == brand)
    .OrderByDescending(p =&gt; p.Price)
    .Take(10)))
{
    Console.WriteLine($"{product.Name}: ${product.Price}");
}<p>O provedor traduz isso para o seguinte ES|QL:</p><p>Há alguns detalhes a serem observados:</p><ul><li><p><strong>Resolução do nome da propriedade:</strong> <code>p.Price</code> se torna <code>price_usd</code> por causa do atributo <code>[JsonPropertyName]</code>, e <code>p.Brand</code> se torna <code>brand</code> seguindo a política de nomenclatura padrão camelCase.</p></li><li><p><strong>Captura de parâmetros:</strong> As variáveis C# <code>minPrice</code> e <code>brand</code> são capturadas como parâmetros nomeados (<code>?minPrice</code>, <code>?brand</code>). Eles são enviados separadamente da string de consulta na carga JSON, o que evita injeções e permite o armazenamento em cache do plano de consulta no lado do servidor.</p></li><li><p><strong>Streaming:</strong> <code>QueryAsync&lt;T&gt;</code> retorna <code>IAsyncEnumerable&lt;T&gt;</code>. As linhas são materializadas uma de cada vez à medida que chegam do Elasticsearch.</p></li></ul><p>Você também pode inspecionar a consulta gerada e seus parâmetros sem executá-la:</p>var query = client.Esql.CreateQuery&lt;Product&gt;()
    .Where(p =&gt; p.InStock &amp;&amp; p.Price &gt;= minPrice &amp;&amp; p.Brand == brand)
    .OrderByDescending(p =&gt; p.Price)
    .Take(10);

Console.WriteLine(query.ToEsqlString());
// FROM products | WHERE (in_stock == true AND price_usd &gt;= 100) | SORT price_usd DESC | LIMIT 10

Console.WriteLine(query.ToEsqlString(inlineParameters: false));
// FROM products | WHERE (in_stock == true AND price_usd &gt;= ?minPrice AND brand == ?brand) | SORT price_usd DESC | LIMIT 10

var parameters = query.GetParameters();
// { "minPrice": 100.0, "brand": "TechCorp" }<h2>Como funciona? Uma breve revisão sobre o LINQ</h2><p>O mecanismo que torna possíveis os provedores LINQ é a distinção entre <code>IEnumerable&lt;T&gt;</code> e <code>IQueryable&lt;T&gt;</code>.</p><p>Quando você chama <code>.Where(p =&gt; p.Price &gt; 100)</code> em um <code>IEnumerable&lt;T&gt;</code>, o lambda compila para um <code>Func&lt;Product, bool&gt;</code>, um delegado regular que o runtime executa em processo. Isto é LINQ-to-Objects.</p><p>Quando você chama o mesmo método em um <code>IQueryable&lt;T&gt;</code>, o compilador C# envolve o lambda em um <code>Expression&lt;Func&lt;Product, bool&gt;&gt;</code> em vez disso. Essa é uma estrutura de dados que representa a <em>estrutura</em> do código em vez de sua forma executável. A árvore de expressões pode ser inspecionada, analisada e traduzida para outra linguagem em tempo de execução.</p>// IEnumerable: the lambda is a compiled delegate
IEnumerable&lt;Product&gt; local = products.Where(p =&gt; p.Price &gt; 100);

// IQueryable: the lambda is an expression tree, a data structure
IQueryable&lt;Product&gt; remote = queryable.Where(p =&gt; p.Price &gt; 100);<p>A interface <code>IQueryProvider</code> é o ponto de extensão. Qualquer provedor pode implementar <code>CreateQuery&lt;T&gt;</code> e <code>Execute&lt;T&gt;</code> para traduzir essas árvores de expressão para um idioma de destino. O Entity Framework usa isso para emitir SQL. O provedor LINQ to ES|QL o usa para emitir ES|QL.</p><p>A árvore de expressões para a consulta acima fica assim:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt521838e8b9c36649/6a1705b1839dfa5f40dcfdfe/f864cd18a390831f8d28503a29b5835efb1842f7-1000x720.png" alt="Árvore de expressões para a consulta de exemplo." /><p><em>Árvore de expressões para a consulta de exemplo.</em></p><p>A árvore é aninhada do avesso: <code>Take</code> envolve <code>OrderByDescending</code>, que envolve <code>Where</code>, que envolve <code>From</code>, que envolve a raiz <code>EsqlQueryable&lt;Product&gt;</code> constante. O predicado <code>Where</code> é ele próprio uma subárvore de <code>BinaryExpression</code> nós para os operadores <code>&amp;&amp;</code>, <code>&gt;=</code> e <code>==</code>, com folhas <code>MemberExpression</code> para acessos a propriedades e capturas de fechamento para as variáveis <code>minPrice</code> e <code>brand</code>. Essa é a estrutura de dados que o provedor percorre para produzir o ES|QL final.</p><h2>Nos bastidores: O pipeline de tradução</h2><p>O caminho de uma expressão LINQ até os resultados da consulta segue um pipeline de seis estágios:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt930670a505dd61ea/6a1705b3b339d58a54769ecf/2a2c772b63d720f61fc9a28b2f85668fa2db8d38-1999x1036.png" alt="Visão geral do pipeline de tradução." /><p><em>Visão geral do pipeline de tradução.</em></p><h3>1. Captura da árvore de expressão</h3><p>Quando você encadeia <code>.Where()</code>, <code>.OrderBy()</code>, <code>.Take()</code> e outros operadores em um <code>IQueryable&lt;T&gt;</code>, a infraestrutura padrão do LINQ constrói uma árvore de expressões. <code>EsqlQueryable&lt;T&gt;</code> implementa <code>IQueryable&lt;T&gt;</code> e delega para <code>EsqlQueryProvider</code>.</p><h3>2. Tradução</h3><p>Quando a consulta é executada (enumerando, chamando <code>ToList()</code> ou usando <code>await foreach)</code>), o <code>EsqlExpressionVisitor</code> percorre a árvore de expressões de dentro para fora. Ele despacha cada chamada de método LINQ para um visitante especializado:</p><p>Visitante</p><p>Traduz</p><p>Para</p><p>WhereClauseVisitor</p><p>.Where(predicate)</p><p>Condição ONDE</p><p>SelectProjectionVisitor</p><p>.Select(selector)</p><p>EVAL + KEEP + RENOMEAR</p><p>GroupByVisitor</p><p>.GroupBy().Select()</p><p>ESTATÍSTICAS ... POR</p><p>OrderByVisitor</p><p>.OrderBy() / .ThenBy()</p><p>Campo SORT [ASC\|DESC]</p><p>EsqlFunctionTranslator</p><p>EsqlFunctions.*, Math.*, métodos de string</p><p>80+ funções ES|QL</p><p>Durante a tradução, as variáveis C# referenciadas em expressões são capturadas como parâmetros nomeados.</p><h3>3. Modelo de consulta</h3><p>Os visitantes não produzem diretamente as strings. Em vez disso, produzem <code>QueryCommand</code> objetos, uma representação intermediária imutável. Um <code>FromCommand</code>, um <code>WhereCommand</code>, um <code>SortCommand</code>, e um <code>LimitCommand</code>, cada um representando um comando de processamento ES|QL. Eles são coletados para um modelo <code>EsqlQuery</code>.</p><p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt788c9936976f2f62/6a1705b50e2e4910da419ff0/2adc349b6cf655b96b7b3e826a134e8a17fe42fd-1999x1036.png" alt="Modelo de consulta e padrão de comando." /><p><em>Modelo de consulta e padrão de comando.</em></p><p>Esse modelo intermediário é desacoplado tanto da árvore de expressão quanto do formato de saída. Ele pode ser inspecionado, interceptado (via <code>IEsqlQueryInterceptor</code>) ou modificado antes da formatação.</p><h3>4. Formatação</h3><p><code>EsqlFormatter</code> visita cada <code>QueryCommand</code> em ordem e gera a string final do ES|QL. Cada comando se transforma em uma linha, separada pelo operador pipe (|), que o ES|QL utiliza para encadear comandos de processamento. Identificadores que contêm caracteres especiais são automaticamente escapados com backticks.</p><h3>5. Execução</h3><p>A string ES|QL formatada e os parâmetros capturados são enviados para o endpoint <code>/_query</code> do Elasticsearch no corpo da requisição, como JSON. A interface <code>IEsqlQueryExecutor</code> abstrai a camada de transporte, e é aí que a arquitetura de pacotes em camadas se aplica.</p><h3>6. Materialização</h3><p><code>EsqlResponseReader</code> transmite a resposta JSON sem armazenar todo o conjunto de resultados na memória. Uma árvore <code>ColumnLayout</code> , pré-computada uma vez por consulta, mapeia ES|QL nomes de colunas (como <code>address.street</code>, <code>address.city</code>) para propriedades aninhadas do POCO. Cada linha é montada em uma instância <code>T</code> e gerada uma de cada vez via <code>IEnumerable&lt;T&gt;</code> ou <code>IAsyncEnumerable&lt;T&gt;</code>.</p><h2>A arquitetura em camadas</h2><p>A funcionalidade LINQ para ES|QL é dividida em três pacotes:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt662bd0dd8861b6b6/6a1705b7a929cf7086ae08a2/41b8aae860ecdc2480edcb1c1d4cc9b03cfb78c9-1999x1036.png" alt="Arquitetura do pacote." /><p><em>Arquitetura de pacotes.</em>
<a href="https://www.nuget.org/packages/Elastic.Esql"><strong><code>Elastic.Esql</code></strong></a> é o motor de tradução puro. Ele não tem dependência HTTP e contém os visitantes de expressões, o modelo de consulta, o formatador e o leitor de resposta. Você pode usá-lo de forma independente para criar e inspecionar consultas ES|QL sem nenhuma conexão com o Elasticsearch. Isso é útil para testes, logging de consultas ou para criar a sua camada de execução.</p>// Translation-only: no Elasticsearch connection needed
var provider = new EsqlQueryProvider();
var query = new EsqlQueryable&lt;Product&gt;(provider)
    .From("products")
    .Where(p =&gt; p.InStock)
    .OrderByDescending(p =&gt; p.Price);

Console.WriteLine(query.ToEsqlString());
// FROM products | WHERE in_stock == true | SORT price_usd DESC<p><a href="https://www.nuget.org/packages/Elastic.Clients.Esql"><strong><code>Elastic.Clients.Esql</code></strong></a> é um cliente ES|QL leve e independente. Ele adiciona a execução HTTP além de <code>Elastic.Esql</code> via <code>Elastic.Transport</code>. Se sua aplicação só precisa do ES|QL e nenhuma das outras APIs do Elasticsearch, essa é a opção de dependência mínima.</p><p><a href="https://www.nuget.org/packages/Elastic.Clients.Elasticsearch"><strong><code>Elastic.Clients.Elasticsearch</code></strong></a> é o cliente completo do Elasticsearch .NET. Também se baseia em <code>Elastic.Esql</code> e expõe o provedor LINQ via espaço de nome <code>client.Esql</code>. Esse é o ponto de entrada recomendado para a maioria das aplicações.</p><p>Ambos os pacotes da camada de execução fornecem a própria implementação do <code>IEsqlQueryExecutor</code>, a interface estratégica que conecta tradução e transporte.</p><p>Todos os três pacotes são compatíveis com o Native AOT quando usados com um <code>JsonSerializerContext</code> gerado por fonte. Para as informações completas sobre o cliente, consulte a <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/dotnet/source-serialization#native-aot">documentação do Native AOT</a>.</p><h2>Além do básico</h2><p>O exemplo acima abordou filtragem, classificação e paginação. O provedor aceita um conjunto mais amplo de operações.</p><h3>Agregações</h3><p><code>GroupBy</code>, combinado com funções agregadas em <code>Select</code>, traduz-se em ES|QL <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/stats-by"><code>STATS ... BY</code></a>:</p>var stats = client.Esql.Query&lt;Product, object&gt;(q =&gt; q
    .GroupBy(p =&gt; p.Brand)
    .Select(g =&gt; new
    {
        Brand = g.Key,
        Count = g.Count(),
        AvgPrice = g.Average(p =&gt; p.Price),
        MaxPrice = g.Max(p =&gt; p.Price)
    }));

// -&gt; FROM products | STATS COUNT(*), AVG(price_usd), MAX(price_usd) BY brand<h3>Projeções</h3><p><code>Select</code>, com tipos anônimos gera os comandos <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/eval"><code>EVAL</code></a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/keep"><code>KEEP</code></a> e <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/rename"><code>RENAME</code></a>:</p>var query = client.Esql.CreateQuery&lt;Product&gt;()
    .Select(p =&gt; new { ProductName = p.Name, p.Price, p.InStock });

// -&gt; FROM products | KEEP name, price_usd, in_stock | RENAME name AS ProductName<h3>Biblioteca repleta de funções</h3><p>Mais de 80 funções ES|QL estão disponíveis via classe <code>EsqlFunctions</code>, cobrindo data/hora, string, matemática, IP, correspondência de padrões e pontuação. Métodos <code>Math.*</code> padrão e <code>string.*</code> também são traduzidos:</p>.Where(p =&gt; p.Name.Contains("Pro"))       // -&gt; WHERE name LIKE "*Pro*"
.Where(p =&gt; EsqlFunctions.CidrMatch(      // -&gt; WHERE CIDR_MATCH(ip, "10.0.0.0/8")
    p.IpAddress, "10.0.0.0/8"))<h3>PESQUISAR ENTRAR</h3><p>Consultas cruzadas de índice traduzem-se para ES|QL <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/lookup-join"><code>LOOKUP JOIN</code></a>:</p>var enriched = client.Esql.Query&lt;Product, object&gt;(q =&gt; q
    .LookupJoin&lt;Product, CategoryLookup, string, object&gt;(
        "category-lookup-index",
        product =&gt; product.Id,
        category =&gt; category.CategoryId,
        (product, category) =&gt; new { product.Name, category!.CategoryLabel }));<h3>Acesso direto ao ES|QL bruto</h3><p>Para recursos do ES|QL que ainda não são cobertos pelo provedor LINQ, você pode adicionar fragmentos brutos:</p>var results = client.Esql.Query&lt;Product&gt;(q =&gt; q
    .Where(p =&gt; p.InStock)
    .RawEsql("| EVAL discounted = price_usd * 0.9"));<h3>Consultas assíncronas do lado do servidor</h3><p>Para consultas de longa duração, envie-as para processamento em segundo plano no servidor:</p>await using var asyncQuery = await client.Esql.SubmitAsyncQueryAsync&lt;Product&gt;(
    q =&gt; q.Where(p =&gt; p.InStock),
    asyncQueryOptions: new EsqlAsyncQueryOptions
    {
        WaitForCompletionTimeout = TimeSpan.FromSeconds(5),
        KeepAlive = TimeSpan.FromMinutes(10)
    });

await asyncQuery.WaitForCompletionAsync();
await foreach (var product in asyncQuery.AsAsyncEnumerable())
    Console.WriteLine(product.Name);<p>Consultas assíncronas do lado do servidor são úteis principalmente em consultas analíticas de longa duração/processamento de grandes conjuntos de dados que podem exceder os tempos-limite típicos, ou em ambientes sensíveis a tempo-limite com balanceadores de carga, gateways de API ou proxies que impõem tempos-limite de HTTP rigorosos. Consultas assíncronas evitam quedas de conexão ao separar o envio da obtenção dos resultados.</p><h2>Para começar</h2><p>LINQ to ES|QL está disponível a partir de:</p><ul><li><p><strong>Elastic.Clients.Elasticsearch v9.3.4</strong> (9.x branch)</p></li><li><p><strong>Elastic.Clients.Elasticsearch v8.19.18</strong> (8.x branch)</p></li></ul><p>Instale do NuGet:</p><p><code>dotnet add package Elastic.Clients.Elasticsearch</code></p><p>Os pontos de entrada estão em <code>client.Esql</code>:</p><p>Método</p><p>Returns</p><p>Caso de uso</p><p>Query&lt;T&gt;(...)</p><p>IEnumerable&lt;T&gt;</p><p>Execução síncrona</p><p>QueryAsync&lt;T&gt;(...)</p><p>IAsyncEnumerable&lt;T&gt;</p><p>Streaming assíncrono</p><p>CreateQuery&lt;T&gt;()</p><p>IEsqlQueryable&lt;T&gt;</p><p>Composição avançada e inspeção</p><p>SubmitAsyncQueryAsync&lt;T&gt;(...)</p><p>EsqlAsyncQuery&lt;T&gt;</p><p>Consultas de longa duração no servidor</p><p><a href="https://www.elastic.co/docs/reference/elasticsearch/clients/dotnet/linq-to-esql">Para obter a referência completa de recursos, incluindo opções de consulta, acesso a vários campos, objetos aninhados e tratamento de campos de vários valores, consulte a documentação do LINQ to ES|QL.</a></p><h2>Conclusão</h2><p>Do LINQ para ES|QL traz toda a expressividade do C# LINQ para a linguagem de consulta ES|QL do Elasticsearch, para que você escreva consultas componíveis e com tipagem forte sem precisar criar manualmente as strings de consulta. Com captura automática de parâmetros, materialização em streaming e arquitetura de pacotes em camadas que se adapta de traduções independentes ao cliente completo do Elasticsearch, ele se integra naturalmente a aplicações .NET de qualquer tamanho. Instale o cliente mais recente, direcione suas expressões LINQ para um índice e deixe o provedor cuidar do resto.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/linq-esql-c-elasticsearch-net-client</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/linq-esql-c-elasticsearch-net-client</guid>
    <category><![CDATA[ES|QL]]></category>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <dc:creator><![CDATA[Florian Bernd,Martijn Laarman]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdfa35fbcbbf4959f/6a1705b9dc55de19a4e00d07/e54132e915217063e9ed0ec45059c6cfc38e31dd-1280x720.png" length="0" type="image/png"/>
    <pubDate>Wed, 01 Apr 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Rapidez x precisão: medindo o recall da busca vetorial quantizada]]></title>
    <description><![CDATA[Uma explicação de como medir o recall para busca vetorial no Elasticsearch com configuração mínima.]]></description>
    <content:encoded><![CDATA[<p>Todo mundo quer que a busca vetorial seja imediata. Mas os vetores de alta dimensão são pesados. Um único vetor float-32 de 1.024 dimensões ocupa bastante memória, e compará-lo com milhões de outros é computacionalmente caro.</p><p>Para resolver isso, mecanismos de busca como o Elasticsearch utilizam duas estratégias principais de otimização:</p><ol><li><p><strong>Busca aproximada (mundo pequeno hierárquico navegável [HNSW]):</strong> em vez de analisar cada documento, construímos um grafo de navegação para acessar rapidamente a vizinhança provável da resposta.</p></li><li><p><strong>Quantização:</strong> Compactamos os vetores (por exemplo, de floats de 32 bits para inteiros de 8 bits ou até mesmo valores binários de 1 bit) para reduzir o uso de memória e acelerar os cálculos.</p></li></ol><p>Mas a otimização geralmente vem acompanhada de uma taxa: <strong>a precisão</strong>.</p><p>O medo é válido: "Se eu compactar meus dados e usar atalhos durante a busca, perderei os melhores resultados?" "Essa otimização degrada a relevância do meu mecanismo de busca?"</p><p>Para provar que a quantização do Elastic não degrada os resultados, criamos um ambiente de testes repetíveis usando o <a href="https://huggingface.co/datasets/fancyzhx/dbpedia_14"><strong>DBPedia-14</strong></a><a href="https://huggingface.co/datasets/fancyzhx/dbpedia_14"> como conjunto de dados</a> para calcular exatamente quanta precisão (especificamente, <strong>recall)</strong> sacrificamos em prol da velocidade ao usar as otimizações padrão do Elasticsearch.</p><p>Resumindo: é provavelmente muito menos do que você pensa. Confira o <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/fast_vs_accurate_measuring_the_recall_of_quantized_vector_search/vector_recall_notebook.ipynb">caderno aqui</a> e teste você mesmo</p><h2><strong>As definições (para os não especialistas)</strong></h2><p>Antes de analisarmos o código, vamos definir alguns termos.</p><ul><li><p><strong>Relevância versus recuperação:</strong> <strong>A relevância</strong> é subjetiva (encontrei informações úteis?). <strong>A recuperação</strong> é matemática. Se houver 10 documentos no banco de dados que correspondam <em>perfeitamente</em> à sua consulta, e o mecanismo de busca encontrar nove deles, sua recuperação será de 90% (ou 0,9).</p></li><li><p><strong>Busca exata (plana):</strong> às vezes chamada de método "força bruta". O mecanismo de busca analisa cada documento em um índice e calcula a distância.</p><ul><li><p><em>Prós:</em> recall perfeito de 100%.</p></li><li><p><em>Contras:</em> computacionalmente caro e lento em larga escala.</p></li></ul></li><li><p><strong>Busca aproximada (HNSW):</strong> O método do "atalho". O mecanismo de busca cria um <a href="https://www.elastic.co/search-labs/blog/hnsw-graph">HNSW</a> gráfico e percorre o gráfico para encontrar os vizinhos mais próximos.</p><ul><li><p><em>Prós:</em> extremamente rápido e escalável.</p></li><li><p><em>Contras:</em> Você pode perder um vizinho se a travessia do gráfico parar muito cedo.</p></li></ul></li></ul><h2><strong>O experimento: exato x aproximado</strong></h2><p>Para testar a recuperação, usamos o conjunto de dados <strong>DBPedia-14</strong>, um grande conjunto de dados de títulos e resumos em 14 classes de ontologia, muito usado para treinar e avaliar modelos de categorização de texto. Especificamente, vamos focar a categoria "Filme". Decidimos comparar as configurações otimizadas de produção com uma verdade matematicamente perfeita.</p><p>Neste experimento, estamos usando o <a href="https://www.elastic.co/search-labs/blog/jina-embeddings-v5-text">modelo jina-embeddings-v5-text-small</a>, um modelo multilíngue de última geração que lidera os benchmarks do setor de representação de textos. Escolhemos esse modelo porque ele define o padrão atual para embeddings de alto desempenho. Ao combinar a precisão de elite do Jina v5 com a quantização nativa do Elasticsearch, podemos demonstrar uma arquitetura de busca que é computacionalmente eficiente e sem prejudicar a qualidade da recuperação.</p><p>Configuramos um índice com mapeamento duplo. Ingerimos o mesmo texto em dois campos diferentes simultaneamente:</p><ol><li><p><strong><code>content.raw</code></strong>com tipo: <code>flat</code>. Isso força o Elasticsearch a realizar uma varredura de força bruta dos vetores Float32 completos. Isso retorna resultados de correspondência exatos e será usado na nossa linha de base.</p></li><li><p><strong><code>content</code></strong>com o tipo <code>semantic_text</code>. Com padrões usando HNSW + melhor quantização binária (BBQ). Esse é o padrão otimizado de produção para correspondência aproximada.</p></li></ol><h3><strong>O teste Recall@10</strong></h3><p>Para nossa métrica, usamos o Recall@10.</p><p>Escolhemos 50 filmes aleatórios e executamos a mesma consulta nos dois campos.</p><ul><li><p>Se a <strong>busca exata (plana)</strong> indicar que os 10 principais vizinhos são IDs [1, 2, 3... 10], você pode usar a busca exata.</p></li><li><p>E a busca <strong>aproximada (HNSW)</strong> retorna IDs [1, 2, 3... 9, 99].</p></li><li><p>Encontramos corretamente nove dos 10 principais. A pontuação é <strong>0,9</strong>.</p></li></ul><p>Aqui está o mapeamento que usamos:</p># The "Control Group": Forces exact brute-force scan
"raw": {
    "type": "semantic_text",
    "inference_id": ".jina-embeddings-v5-text-small",
    "index_options": {
        "dense_vector": {
            "type": "flat"
        }
    }
}<p><strong>Os resultados: a "estagnação" do sucesso</strong></p><p>Realizamos um teste de escala, recarregando todo o conjunto de dados e testando com tamanhos de índice de 1.000 a 40.000 documentos.</p><p>Veja o que aconteceu com a pontuação de recall:</p><p>Documentos</p><p>Recall@10 score</p><p>1.000</p><p>1.000 (100%)</p><p>5.000</p><p>0,998 (100%)</p><p>10.000</p><p>0,992 (99,4%)</p><p>20.000</p><p>0,999 (99,0%)</p><p>40.000</p><p>0.992 (98,8%)</p><p>Os resultados foram incrivelmente estáveis. Mesmo com o aumento da escala, a busca aproximada coincidiu com a busca exata por força bruta <strong>em mais de 99% dos casos</strong>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8168a0a4946bade7/6a170e154a531b61b536a9eb/a4bfacb1d0cce6fdf6df0e1a9d4fc5d4007a66da-1999x1209.png" alt="estabilidade na busca vetorial: recall x tamanho do índice" /><h2><strong>Por que funcionou tão bem?</strong></h2><p>Você poderia esperar que comprimir vetores em valores binários prejudicasse mais a precisão do que isso. A razão para isso não ocorrer está em como o Elasticsearch lida com a recuperação.</p><p>A maioria dos modelos de embedding hoje gera vetores Float32, que são volumosos. Para deixar a busca eficiente, o Elasticsearch usa quantização para vetores de alta dimensionalidade. Especificamente, desde a versão 9.2, ele usa <a href="https://www.elastic.co/search-labs/blog/elasticsearch-9-1-bbq-acorn-vector-search">BBQ</a> como padrão.</p><p>O BBQ usa um mecanismo de <strong>reclassificação:</strong></p><ol><li><p><strong>Percurso:</strong> o mecanismo de busca utiliza os vetores comprimidos (quantizados) para percorrer rapidamente o gráfico HNSW. Como os vetores são pequenos, ele pode superamostrar com eficiência, reunindo uma lista maior de candidatos (p. ex., os 100 principais documentos aproximadamente semelhantes) sem penalidade no desempenho.</p></li><li><p><strong>Reclassificação:</strong> com esses candidatos, ele recupera os valores de precisão total apenas para esses poucos documentos para calcular a classificação final e precisa.</p></li></ol><p>Ele oferece o melhor dos dois mundos: rapidez na quantização para o trabalho pesado e precisão dos números de ponto flutuante para a classificação final.</p><h2><strong>Podemos fazer melhor?</strong></h2><p>É importante notar que os resultados aqui usam configurações padrão e uma amostra aleatória de dados. Pense nisso como um ponto de partida de alto desempenho. Embora o Jina v5 seja excelente, essas pontuações de recall não são garantia de que funcione em todos os conjuntos de dados. Cada coleta de dados tem as próprias peculiaridades e, embora você possa definitivamente ajustar ainda mais as coisas para ter ainda mais desempenho, deve sempre comparar seus dados específicos para saber seu limite.</p><h2><strong>Conclusão</strong></h2><p>Este é um teste em pequena escala. Mas o objetivo do exercício não é medir especificamente o modelo de embeddings nem o BBQ, e sim mostrar como medir facilmente o recall do seu conjunto de dados com o mínimo de configuração.</p><p>Se você quiser executar esse teste com seus próprios dados, pode conferir o <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/fast_vs_accurate_measuring_the_recall_of_quantized_vector_search/vector_recall_notebook.ipynb">notebook aqui</a> e tentar você mesmo.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/recall-vector-search-quantization</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/recall-vector-search-quantization</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <dc:creator><![CDATA[Jeff Vestal]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt198c7085db96aa04/6a170e17cdacbfe88c7d2a86/09f03b9239d66c36763cdab3fafcdac207ff6d83-1280x720.png" length="0" type="image/png"/>
    <pubDate>Fri, 20 Mar 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Terminação adaptativa precoce para HNSW no Elasticsearch]]></title>
    <description><![CDATA[Introdução de uma nova estratégia adaptativa de terminação antecipada para HNSW no Elasticsearch.]]></description>
    <content:encoded><![CDATA[<p>O Elasticsearch utiliza o algoritmo <a href="https://www.elastic.co/search-labs/blog/hnsw-graph">Hierarchical Navigable Small World</a> (HNSW) para realizar buscas vetoriais em um gráfico de proximidade. O HNSW é conhecido por oferecer uma boa compensação entre a qualidade dos resultados do k-nearest neighbor (KNN) e o custo associado.</p><p>No HNSW, a busca prossegue expandindo iterativamente os nós candidatos no gráfico, mantendo um conjunto limitado de vizinhos mais próximos descobertos até então. Cada expansão tem um custo (operações vetoriais, acessos aleatórios ao disco, e mais), e o benefício marginal desse custo tende a diminuir conforme a busca avança.</p><p>Uma forma de otimizar a travessia de gráficos HNSW é parar de buscar quando a probabilidade marginal de encontrar novos vizinhos verdadeiros não aumenta. Por essa razão, no <a href="https://www.elastic.co/docs/reference/elasticsearch/index-settings/index-modules#index-dense-vector-hnsw-early-termination">Elasticsearch 9.2</a> introduzimos um novo mecanismo de <a href="https://www.elastic.co/search-labs/blog/hnsw-knn-search-early-termination">terminação antecipada</a>. Isso interrompe o processo de busca quando visitar nós do gráfico não fornece vizinhos novos mais próximos suficientes, consecutivamente, por um número fixo de vezes.</p><p>Este artigo mostra como aprimoramos o mecanismo de terminação antecipada mencionado no HNSW para torná-lo mais adequado para diferentes conjuntos de dados e distribuições de dados.</p><h2><strong>Terminação antecipada no HNSW</strong></h2><p>No HNSW, a busca prossegue expandindo iterativamente os nós candidatos no gráfico de proximidade, mantendo um conjunto limitado de vizinhos mais próximos descobertos até então, até que tenha visitado todo o gráfico ou atenda a alguns critérios iniciais de parada.</p><p>Portanto, a terminação antecipada nem sempre é necessariamente uma otimização, faz <strong>parte do próprio algoritmo de busca</strong>. O momento em que decidimos parar determina o equilíbrio entre eficiência e recall. No Elasticsearch, já existem várias maneiras de uma consulta no HNSW terminar antecipadamente:</p><ul><li><p>Um número máximo fixo de nós é visitado.</p></li><li><p>Um tempo limite fixo é atingido.</p></li></ul><p>Embora simples e previsíveis, essas regras são em grande parte <strong>agnósticas em relação ao que a busca realmente está fazendo</strong>. Além disso, elas são usadas principalmente para garantir que a consulta seja concluída em um tempo razoável para o usuário final.</p><p>Em uma <a href="https://www.elastic.co/search-labs/blog/hnsw-knn-search-early-termination">postagem anterior do blog</a>, apresentamos o conceito de redundância no HNSW. Em resumo, cálculos redundantes ocorrem quando o HNSW continua a avaliar novos nós candidatos que não resultam em encontrar mais vizinhos mais próximos.</p><h2><strong>Paciência: Medindo progresso em vez de esforço</strong></h2><p>A noção de <em>paciência</em> reformula a terminação antecipada, focando no <strong>progresso em vez do esforço</strong>.</p><p>Em vez de perguntar:</p><p>"Quantos passos já demos?"</p><p>A nova pergunta passa a ser:</p><p>“Qual é a quantidade de computação que aceitamos desperdiçar até perdermos a esperança?”</p><p>Durante a busca HNSW, a exploração precoce normalmente produz melhorias de pico no conjunto de candidatos top-k. Durante as primeiras etapas da exploração do gráfico HNSW, o conjunto de vizinhos é continuamente atualizado à medida que o algoritmo continua descobrindo vizinhos cada vez mais próximos do vetor de consulta. Com o tempo, essas melhorias se tornam mais raras à medida que a busca converge. <a href="https://cs.uwaterloo.ca/~jimmylin/publications/Teofili_Lin_ECIR2025.pdf">A terminação antecipada baseada em paciência</a> monitora esse padrão e finaliza a busca assim que as melhorias cessarem por um período prolongado.</p><p>Na prática, ao visitar o gráfico HNSW, também calculamos a razão de saturação da fila ao pular entre os nós candidatos. Isso mede a porcentagem de vizinhos mais próximos que permaneceram inalterados ao visitar o nó mais recente do gráfico (ou o inverso do número de novos vizinhos introduzidos na última iteração). Quando essa proporção se torna grande demais para muitas iterações consecutivas, paramos de visitar o gráfico.</p><p>Conceitualmente, a paciência trata a busca HNSW como um <strong>processo de retornos decrescentes</strong>. Quando os retornos se estabilizam, continuar explorando o gráfico traz pouco benefício.</p><p>Esse enquadramento é poderoso porque vincula a interrupção diretamente a <em>resultados observáveis</em>, e não a limites fixos arbitrários.</p><p>A vantagem de usar essa técnica inteligente de terminação antecipada é que as explorações do gráfico HNSW tendem a visitar um número menor de nós gráficos, mantendo uma taxa de recall quase perfeita.</p><p>Para visualizar isso, podemos visualizar em um gráfico a quantidade de recall por nó visitado que obtivemos com a terminação antecipada baseada em paciência (rotulada como <em><code>et=static</code></em>), quando comparada ao comportamento padrão do HNSW (rotulado como <em><code>et=no</code></em>) em alguns conjuntos de dados, FinancialQA e Quora, e modelos, JinaV3 e E5-small.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfd0d692b9beb476a/6a170ef4dc55debf0be00e97/a9d07c5153ea64a2426c82487c36846030692bb9-1600x945.png" alt="Terminação adaptativa antecipada para HNSW " /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt93509c251a1b641e/6a170ef6dc55dea2b3e00e9b/dac56125c4b16d1b596c9876b6ca9ac7b2dc87fa-1600x944.png" alt="Terminação Adaptativa Antecipada para HNSW es" /><h2><strong>Limiares estáticos e dinâmicas do HNSW</strong></h2><p>Na prática, no Elasticsearch, isso é implementado usando <strong>limites estáticos</strong>. Um limite refere-se ao <strong>limite de saturação</strong>, ou seja, a proporção de saturação que consideramos abaixo do ideal. O outro limite refere-se ao número de nós de gráfico consecutivos que permitimos serem visitados enquanto ainda mantêm uma saturação de fila abaixo do ideal: ou seja, o <strong>limiar de paciência</strong>.</p><p>Quando introduzimos essa estratégia de terminação antecipada no Elasticsearch 9.2, decidimos optar por padrões conservadores, de modo a preservar o recall o máximo possível, enquanto ainda obtemos ganhos em termos de latência e consumo de memória. Por esse motivo, definimos o limiar de saturação para 100% e o limiar de paciência para ser definido como 30% (limitado) do <a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-knn-query#knn-query-top-level-parameters:~:text=search%20request%20size.-,num_candidates,-(Optional%2C%20integer)%20The"><em><code>num_candidates</code></em></a> na consulta KNN.</p><p>Em muitos cenários, essas configurações funcionaram bem; no entanto, duas consultas que solicitam o mesmo número de vizinhos podem ter comportamentos de convergência radicalmente diferentes. Algumas consultas encontram vizinhanças locais densas e saturam rapidamente; outras precisam percorrer caminhos longos e esparsos antes de encontrar candidatos competitivos. Este último mostrou-se o mais difícil de lidar de forma eficaz.</p><p>Como resultado, por vezes notamos:</p><ul><li><p>Exploração excessiva para consultas fáceis.</p></li><li><p>Encerramento prematuro para consultas complexas.</p></li></ul><p>Portanto, descobrimos que valores de limite fixos codificam suposições globais sobre convergência, enquanto poderíamos fazer com que o HNSW se adaptasse melhor a diferentes dinâmicas.</p><h2><strong>Tornando o HNSW adaptativo à terminação antecipada</strong></h2><p>A terminação antecipada adaptativa aborda esse problema de um ângulo diferente. Em vez de impor limiares de parada pré-definidos, o <strong>algoritmo infere quando parar a partir da própria dinâmica da busca</strong>.</p><p>Então, em vez de comparar a taxa de saturação da fila entre dois candidatos consecutivos, decidimos introduzir uma taxa de descoberta instantânea suavizada   (quantos novos vizinhos foram introduzidos para uma consulta <em>q</em>,na última visita <em>i</em>) junto com a média móvel  e o desvio padrão  dessa taxa de descoberta durante a visita ao gráfico (usando o <a href="https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm">algoritmo de Welford</a>). Essas estatísticas sobre a taxa de descoberta são calculadas por consulta, de modo que essas informações podem ser usadas para determinar diferentes níveis de paciência para cada consulta.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdbfb1e123f1b026d/6a170ef7cf4f25d9bab2d216/1958be7ca4425ade66eaf621ada3533173183598-694x118.png" alt="" /><p>Os limites anteriormente estáticos se tornam adaptáveis às estatísticas da taxa de descoberta: o limite de saturação se torna a média contínua mais o desvio padrão; enquanto fazemos com que a paciência se adapte e redimensione inversamente com o desvio padrão.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7d4d91f464a9dc9d/6a170ef8d7c0223420de656a/f7ee4a55c24853b657df26052b275e8bd76cf0f9-654x156.png" alt="" /><p>As regras de saída antecipada permanecem as mesmas; a saturação ocorre quando a taxa de descoberta instantânea é menor que o limite de saturação adaptativa. A visita ao gráfico é interrompida se a saturação persistir por um número consecutivo de visitas candidatas maior que a paciência adaptativa.</p><p>Dessa forma, obtemos um comportamento que não depende do parâmetro <em><code>num_candidates</code></em> na consulta KNN (que pode estar sempre definido ou deixado como padrão, independentemente de sair cedo) e que se adapta melhor a cada consulta e distribuição vetorial de forma dinâmica.</p><p>O recall por nó visitado no FinancialQA e Quora com a estratégia adaptativa (rotulada como <em><code>et=adaptive</code></em>) apresenta um recall maior por nó visitado, quando comparado à estratégia estática (<em><code>et=static</code></em>) e ao comportamento padrão do HNSW (<em><code>et=no</code></em>).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blteab7ba53ae14da0e/6a170ef9961e69e072c4cfd5/2a906997d9a25d74c7038bd9661bc97581e7258e-1600x938.png" alt=" estratégia adaptativa e o comportamento padrão do HNSW" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6fb9e672d3698200/6a170efb67045b7b2b45c2ab/3a114911e232c351dbb814cea20e8b0f1415a717-1600x925.png" alt="" /><p>A terminação antecipada adaptativa está ativada por padrão no Elasticsearch 9.3 para campos vetoriais densos HNSW (e pode ser desativada posteriormente por meio da <a href="https://www.elastic.co/docs/reference/elasticsearch/index-settings/index-modules#index-dense-vector-hnsw-early-termination">mesma configuração no nível do índice</a>).</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/hnsw-elasticsearch-adaptive-early-termination</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/hnsw-elasticsearch-adaptive-early-termination</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <category><![CDATA[Na Elastic]]></category>
    <dc:creator><![CDATA[Tommaso Teofili]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt27b746cc1995e6b7/6a170efda29299de8ad010c6/e6d3186f609dd56dc5ffe33d70fa9e5cfa05b51f-1280x720.png" length="0" type="image/png"/>
    <pubDate>Mon, 02 Mar 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[A busca vetorial do Elasticsearch é até 8 vezes mais rápida que a do OpenSearch]]></title>
    <description><![CDATA[Explorando os benchmarks de busca vetorial filtrada do OpenSearch x Elasticsearch e por que o desempenho de busca vetorial é fundamental para sistemas de engenharia de contexto.]]></description>
    <content:encoded><![CDATA[<h2>Por que a velocidade de pesquisa é importante para agentes de IA e engenharia de contexto</h2><p>Nossos benchmarks em um corpus de 20 milhões de documentos mostram que o Elasticsearch entrega uma taxa de transferência até 8 vezes maior que o OpenSearch para busca vetorial filtrada, além de alcançar um Recall@100 superior nas configurações que testamos. A engenharia de contexto depende de mais do que apenas uma recuperação vetorial rápida. As equipes também precisam de fortes controles de relevância, como busca e filtragem híbridas, simplicidade operacional e desempenho previsível, à medida que os fluxos de trabalho evoluem. Mas como os agentes geralmente executam loops de recuperação, raciocínio e recuperação várias vezes por solicitação, a latência de recuperação se torna um multiplicador, então as melhorias aqui se traduzem diretamente em melhor capacidade de resposta de ponta a ponta e menor custo.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9daec868eed84658/6a170bef6234e0c322db1a19/d5a52a07773f0942c2baa732dacfe782aac0f415-1600x683.png" alt="OpenSearch x Elasticsearch: taxa de transferência para benchmark de busca vetorial filtrada" /><p>Para engenharia de contexto, recuperação não é um passo único. Agentes e aplicativos executam repetidamente loops, como recuperar → raciocinar → recuperar, para refinar consultas, verificar fatos, reunir contexto fundamentado e concluir tarefas. Esse padrão é comum em fluxos de trabalho agentivos e Retrieval-Augmented Generation iterativa (RAG). Como a recuperação pode ser invocada muitas vezes por solicitação do usuário, ela adiciona atraso à resposta e/ou aumenta os custos de infraestrutura.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt718c72fb858e4c98/6a170bf00e2e496e7241a132/54ac476ff20a3cf93484298c9ae47612c12fc110-800x417.png" alt="A engenharia de contexto transforma um grande pool de contexto em uma janela de contexto limitada de LLM." /><h2>Por que o desempenho da busca vetorial é crítico?</h2><p></p><p>Imagine um assistente de compras respondendo à pergunta: “Preciso de uma mochila de mão por menos de R$ 300 que comporte um laptop de 15 polegadas, seja resistente à água e possa chegar até sexta-feira.”</p><p>Em produção, o assistente raramente emite uma consulta vetorial e para. Ele executa um ciclo de recuperação para criar o contexto certo, e cada etapa normalmente é limitada por filtros, como disponibilidade, região, promessa de envio, regras de marca e elegibilidade de políticas.</p><p><strong>Passo 1: Interprete a intenção e traduza para restrições.</strong></p><p>O agente transforma a solicitação em filtros estruturados e uma consulta semântica, como:</p><ul><li><p>Filtros: em estoque, disponível para entrega no CEP do usuário, entrega até sexta-feira, preço abaixo de R$ 300, listagem válida</p></li><li><p>Consulta vetorial: “Mochila de bordo resistente à água para notebook de 15 polegadas”</p></li></ul><p><strong>Passo 2: Recuperar candidatos e, em seguida, refinar.</strong></p><p>Frequentemente, repete a recuperação com variações para evitar perder boas correspondências:</p><ul><li><p>"Mochila de viagem com compartimento para laptop"</p></li><li><p>"Mochila urbana resistente à água 15 polegadas"</p></li><li><p>"Mochila leve para cabine"</p></li></ul><p>Cada consulta usa os mesmos filtros de elegibilidade, porque recuperar itens irrelevantes ou indisponíveis é contexto desperdiçado.</p><p><strong>Passo 3: Expanda para confirmar detalhes e reduzir riscos.</strong></p><p>O agente então recupera novamente para verificar os atributos-chave que afetam a resposta final:</p><ul><li><p>Texto sobre materiais e resistência à água</p></li><li><p>Dimensões e ajuste do compartimento para laptop</p></li><li><p>Política de devolução ou restrições de garantia</p></li><li><p>Opções alternativas se o estoque estiver baixo</p></li></ul><p>Isso é engenharia de contexto multietapas: recuperar, raciocinar, recuperar, montar.</p><h2>Por que latência e recall importam para a engenharia de contexto</h2><p>Essas interações podem envolver dezenas de chamadas de recuperação filtradas por sessão de usuário. Isso faz com que a latência por chamada seja um multiplicador direto no tempo de resposta de ponta a ponta, e o baixo recall força tentativas extras ou faz com que o agente perca itens elegíveis, degradando a qualidade da resposta.</p><p>Conclusão: em sistemas de engenharia de contexto, os vizinhos mais próximos aproximados (ANN) filtrados não são uma pesquisa única. É uma operação repetida sob restrições, portanto, o desempenho da busca vetorial aparece imediatamente em latência, taxa de transferência e custo, mesmo quando o modelo de linguagem de grande porte (LLM) é o componente mais visível.</p><h2>Benchmark</h2><h3>Resultados</h3><p>No gráfico 2, cada ponto representa uma configuração de teste. Os melhores resultados aparecem no canto superior esquerdo, o que significa maior recall com menor latência. Os resultados do Elasticsearch estão consistentemente mais próximos do canto superior esquerdo do que os do OpenSearch, indicando melhor velocidade e precisão sob as mesmas configurações de carga de trabalho.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb562b30a600cc8e1/6a170bf2cf4f253582b2d1b2/c50d1df00968cac18149a2799e6242fbe49b66a0-1600x990.png" alt=" Gráfico 2: Recall x latência média (reclassificação 1)." /><h4>Alguns insights importantes</h4><ul><li><p><code>s_n_r_value</code>: Abreviação de <code>size_numCandidates_rescoreOversample</code> (k e numCandidates=500 definidos como numCandidates nesses testes), por exemplo, <code>100_500_1</code> significa size=100, numCandidates=500 e k=500, rescore oversample=1</p></li><li><p>Recall: Recall@100 medido para essa configuração</p></li><li><p>Latência média (ms): latência média de ponta a ponta por consulta</p></li><li><p>Taxa de transferência: consultas por segundo</p></li><li><p>Recall (%): elevação relativa do recall do Elasticsearch x OpenSearch (Elasticsearch menos OpenSearch)/OpenSearch</p></li><li><p>Latência Xs: a latência média do OpenSearch dividida pela latência média do Elasticsearch</p></li><li><p>Throughput Xs: Throughput do Elasticsearch dividido pelo throughput do OpenSearch</p></li></ul><p>Mecanismo</p><p>`s_n_r_value`</p><p>Recall</p><p>Latência Média (ms)</p><p>Taxa de transferência</p><p>Recall %</p><p>Latência Xs</p><p>Taxa de transferência Xs</p><p>Elasticsearch</p><p>100_250_1</p><p>0,7704</p><p>25</p><p>534,75</p><p>9,70%</p><p>2,28</p><p>1,91</p><p>OpenSearch</p><p>100_250_1</p><p>0,7023</p><p>57,08</p><p>279,58</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_500_1</p><p>0,8577</p><p>25,42</p><p>524,14</p><p>7,20%</p><p>2,4</p><p>2</p><p>OpenSearch</p><p>100_500_1</p><p>0,8001</p><p>60,9</p><p>262,12</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_750_1</p><p>0,8947</p><p>29,67</p><p>528,09</p><p>5,72%</p><p>2,25</p><p>2,21</p><p>OpenSearch</p><p>100_750_1</p><p>0,8463</p><p>66,76</p><p>239,11</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_1000_1</p><p>0,9156</p><p>29,65</p><p>534,5</p><p>4,66%</p><p>2,46</p><p>2,44</p><p>OpenSearch</p><p>100_1000_1</p><p>0,8748</p><p>72,88</p><p>219,01</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_1500_1</p><p>0,9386</p><p>31,84</p><p>497,3</p><p>3,38%</p><p>2,71</p><p>2,68</p><p>OpenSearch</p><p>100_1500_1</p><p>0,9079</p><p>86,16</p><p>185,4</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_2000_1</p><p>0,9507</p><p>34,69</p><p>457,2</p><p>2,57%</p><p>2,98</p><p>2,96</p><p>OpenSearch</p><p>100_2000_1</p><p>0,9269</p><p>103,36</p><p>154,55</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_2500_1</p><p>0,9582</p><p>37,9</p><p>418,43</p><p>1,99%</p><p>3,28</p><p>3,26</p><p>OpenSearch</p><p>100_2500_1</p><p>0,9395</p><p>124,29</p><p>128,53</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_3000_1</p><p>0,9636</p><p>41,86</p><p>379,4</p><p>1,62%</p><p>3,46</p><p>3,44</p><p>OpenSearch</p><p>100_3000_1</p><p>0,9482</p><p>144,67</p><p>110,34</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_4000_1</p><p>0,9705</p><p>50,28</p><p>316,21</p><p>1,06%</p><p>3,87</p><p>3,85</p><p>OpenSearch</p><p>100_4000_1</p><p>0,9603</p><p>194,36</p><p>82,22</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_5000_1</p><p>0,9749</p><p>58,77</p><p>270,91</p><p>0,73%</p><p>4,43</p><p>4,41</p><p>OpenSearch</p><p>100_5000_1</p><p>0,9678</p><p>260,33</p><p>61,38</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_6000_1</p><p>0,9781</p><p>66,75</p><p>238,59</p><p>0,52%</p><p>4,91</p><p>4,89</p><p>OpenSearch</p><p>100_6000_1</p><p>0,973</p><p>327,44</p><p>48,81</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_7000_1</p><p>0,9804</p><p>74,64</p><p>213,49</p><p>0,38%</p><p>5,28</p><p>5,27</p><p>OpenSearch</p><p>100_7000_1</p><p>0,9767</p><p>394,24</p><p>40,53</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_8000_1</p><p>0,9823</p><p>82,28</p><p>193,59</p><p>0,27%</p><p>6,86</p><p>6,83</p><p>OpenSearch</p><p>100_8000_1</p><p>0,9797</p><p>564,14</p><p>28,33</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_9000_1</p><p>0,9837</p><p>90,08</p><p>176,96</p><p>0,16%</p><p>7,63</p><p>7,61</p><p>OpenSearch</p><p>100_9000_1</p><p>0,9821</p><p>687,25</p><p>23,25</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_10000_1</p><p>0,9848</p><p>97,64</p><p>163,31</p><p>0,08%</p><p>8,38</p><p>8,36</p><p>OpenSearch</p><p>100_10000_1</p><p>0,984</p><p>818,64</p><p>19,53</p><p></p><p></p><p></p><p>Por exemplo, em <code>100_9000_1</code>, o OpenSearch tem uma média de 687 milissegundos por recuperação contra 90 milissegundos no Elasticsearch, e em um ciclo de recuperação de 10 etapas, isso equivale a cerca de 10 x (687 - 90) = seis segundos de tempo adicional de espera. </p><p>Veja os <a href="https://github.com/elastic/competitive-benchmarking-studies/tree/main/es-9.3-vs-os-3.5-vector-search/jingra/results/20260220">resultados completos</a>.</p><h3>Metodologia</h3><p>Usando Python para enviar as consultas e acompanhar o tempo de resposta e outras estatísticas, enviamos as seguintes consultas para os motores. Lembre-se de que o desempenho de qualquer mecanismo de busca vetorial depende de como você ajusta seus parâmetros principais: quantos candidatos considerar, quão agressivamente reclassificar e quanto contexto devolver. Essas configurações afetam diretamente tanto o recall (a probabilidade de encontrar a resposta certa) quanto a latência (a rapidez com que você obtém resultados).</p><p>Em nossos benchmarks, usamos as mesmas configurações de candidato, reclassificação e tamanho do resultado que você normalmente ajusta em um ciclo de recuperação orientado por agente, e medimos o desempenho do Elasticsearch sob essa carga de trabalho. Depois, executamos o OpenSearch com as mesmas configurações como referência.</p><p>OpenSearch</p>GET &lt;INDEX_NAME&gt;/_search
{
  "query": {
    "knn": {
      "&lt;DENSE_VECTOR_FIELD_NAME&gt;": {
        "vector": [...],
        "k": &lt;NUMBER_OF_CANDIDATES&gt;,
        "method_parameters": {
          "ef_search": &lt;NUMBER_OF_CANDIDATES&gt;
        },
        "rescore": {
          "oversample_factor": &lt;OVERSAMPLE&gt;
        },
        "filter": {
          &lt;SOME_FILTER&gt;
        }
      }
    }
  },
  "size": &lt;RESULT_SIZE&gt;,
  "_source": {
    "excludes": [
      "&lt;DENSE_VECTOR_FIELD_NAME&gt;"
    ]
  }
}<ul><li><p><code>"size": &lt;RESULT_SIZE&gt;</code>: Número de resultados retornados ao cliente. Neste benchmark, o tamanho do resultado é 100 para calcular o Recall@100.</p></li><li><p><code>"k": &lt;NUMBER_OF_CANDIDATES&gt;</code>: O número de candidatos a vizinhos mais próximos.</p></li><li><p><code>"ef_search": &lt;NUMBER_OF_CANDIDATES&gt;</code>: O número de vetores a examinar.</p></li><li><p><code>"oversample_factor": &lt;OVERSAMPLE&gt;</code>: Quantos vetores candidatos são recuperados antes da reclassificação.</p></li></ul><p>Elasticsearch</p>GET &lt;INDEX_NAME&gt;/_search
{
  "query": {
    "knn": {
      "field": "&lt;DENSE_VECTOR_FIELD_NAME&gt;",
      "query_vector": [...],
      "k": &lt;NUMBER_OF_CANDIDATES&gt;,
      "num_candidates": &lt;NUMBER_OF_CANDIDATES&gt;,
      "rescore_vector": {
        "oversample": &lt;OVERSAMPLE&gt;
      },
      "filter": {
        &lt;SOME_FILTER&gt;
      }
    }
  },
  "size": &lt;RESULT_SIZE&gt;,
  "_source": {
    "excludes": [
      "&lt;DENSE_VECTOR_FIELD_NAME&gt;"
    ]
  }
}<ul><li><p><code>"size": &lt;RESULT_SIZE&gt;</code>: Número de resultados retornados ao cliente. Neste benchmark, o tamanho do resultado é 100 para calcular o Recall@100.</p></li><li><p><code>"k": &lt;NUMBER_OF_CANDIDATES&gt;</code>: Número de vizinhos mais próximos a retornar de cada shard.</p></li><li><p><code>"num_candidates": &lt;NUMBER_OF_CANDIDATES&gt;</code>: Número de candidatos a vizinhos mais próximos a serem considerados por shard durante a busca <code>knn</code>.</p></li><li><p><code>"oversample": &lt;OVERSAMPLE&gt;</code>: Quantos vetores candidatos são recuperados antes da reclassificação.</p></li></ul><p>Exemplo</p><p><code>Knn</code> consulta, (<code>100_500_1</code>), seria a seguinte:</p><p>OpenSearch</p>GET search_catalog_128/_search
{
  "query": {
    "knn": {
      "search_catalog_embedding": {
        "vector": [...],
        "k": 500,
        "method_parameters": {
          "ef_search": 500
        },
        "rescore": {
          "oversample_factor": 1
        },
        "filter": {
          "term": {
            "valid": true
          }
        }
      }
    }
  },
  "size": 100,
  "_source": {
    "excludes": [
      "search_catalog_embedding"
    ]
  }
}<p>Elasticsearch</p>GET search_catalog_128/_search
{
  "query": {
    "knn": {
      "field": "search_catalog_embedding",
      "query_vector": [...],
      "k": 500,
      "num_candidates": 500,
      "rescore_vector": {
        "oversample": 1
      },
      "filter": {
        "term": {
          "valid": true
        }
      }
    }
  },
  "size": 100,
  "_source": {
    "excludes": [
      "search_catalog_embedding"
    ]
  }
}<p>A configuração completa, juntamente com os scripts do Terraform, os manifestos do Kubernetes e o código de benchmark, está disponível neste <a href="https://github.com/elastic/competitive-benchmarking-studies">repositório</a> na pasta <a href="https://github.com/elastic/competitive-benchmarking-studies/tree/main/es-9.3-vs-os-3.5-vector-search">es-9.3-vs-os-3.5-vector-search</a>.</p><h3>Configuração do Cluster</h3><p>Executamos nossos testes em seis servidores em nuvem e2-standard-16, cada um com 16 vCPUs e 64 GB de RAM. Em cada servidor, alocamos 15 vCPUs e 56 GB de RAM para cada pod Kubernetes executando o Node do mecanismo de busca, com 28 GB reservados para o heap da JVM.</p><p>Os clusters executavam Elasticsearch 9.3.0 e OpenSearch 3.5.0 (Lucene 10.3.2). Como ambos os sistemas usam a mesma versão do Lucene neste teste de desempenho, as diferenças de taxa de transferência e latência que observamos não podem ser atribuídas apenas ao Lucene, mas sim refletem diferenças na forma como cada mecanismo integra e executa a recuperação e reavaliação filtrada do algoritmo k-vizinhos mais próximos (kNN). Usamos um único índice com três shards principais e uma réplica (ou seja, 6 shards no total, 1 por nó).</p><p>Também usamos um servidor separado na mesma região para executar o cliente de benchmark e coletar estatísticas de tempo.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7c7bdd567395d5b7/6a170bf3c1e8a56ee2f882f6/f81002c9186e4c2d3e92f49d72418fee9860fc5e-761x401.png" alt="Configuração de cluster para Elasticsearch e benchmarks do OpenSearch" /><h3>O conjunto de dados</h3><p></p><p>Para este benchmark, usamos um catálogo em grande escala no estilo de comércio eletrônico com conjuntos de dados de 20 milhões de documentos, projetado para refletir a recuperação vetorial filtrada do mundo real em escala.</p><p></p><p>Cada documento representa um item do catálogo e inclui:</p><p></p><ul><li><p>Um embedding vetorial denso de 128 dimensões utilizado para recuperação aproximada de kNN.</p></li><li><p>Campos de metadados estruturados usados para filtragem (por exemplo, validade e disponibilidade do item, além de outras restrições do catálogo), permitindo o padrão comum de produção de recuperar os vizinhos mais próximos, mas somente dentro de um subconjunto elegível.</p></li></ul><p></p><p>Escolhemos este conjunto de dados porque ele captura o principal desafio de desempenho que observamos em sistemas agentivos e do tipo RAG em produção: a similaridade vetorial por si só não é suficiente, a recuperação é frequentemente limitada por filtros, e o sistema deve manter um alto índice de recall enquanto mantém a latência baixa sob essas restrições. Comparado a conjuntos de dados menores no estilo QA, um corpus de 20 milhões de documentos também reflete melhor a escala e a pressão dos candidatos que sistemas ANN filtrados enfrentam na prática.</p><h2>Conclusão</h2><p>Nas arquiteturas modernas de IA, especialmente aquelas construídas baseadas em engenharia de contexto, a velocidade da busca vetorial não é um pequeno detalhe de implementação. É um multiplicador. Quando agentes e fluxos de trabalho iteram por meio de recuperar → raciocinar → recuperar, o desempenho da recuperação molda diretamente a latência de ponta a ponta, a taxa de transferência e a qualidade do contexto inserido no modelo.</p><p>Em nossos benchmarks, o Elasticsearch consistentemente entregou um recall maior com menor latência do que o OpenSearch em cenários onde a correção depende de recuperar o documento correto, e não apenas de um vetor semelhante. Em um conjunto de dados controlado, a diferença é clara, e na produção esses ganhos se acumulam em grandes volumes de chamadas de recuperação, melhorando a capacidade de resposta, aumentando a margem de capacidade e reduzindo custos de infraestrutura.</p><h3>Para ler mais</h3><ol><li><p><a href="https://www.elastic.co/search-labs/blog/context-engineering-overview">O que é engenharia de contexto?</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/series/context-engineering-hybrid-search-evolution">A evolução da busca híbrida e da engenharia de contexto</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/context-engineering-relevance-ai-agents-elasticsearch">O impacto da relevância na engenharia de contexto para agentes de IA</a></p></li></ol>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/opensearch-vs-elasticsearch-filtered-vector-search</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/opensearch-vs-elasticsearch-filtered-vector-search</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <dc:creator><![CDATA[Sachin Frayne]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4b38e5114bbf098c/6a170bf560084b3a6c3c459d/fb7ee623925ca6696d643e437ce8efe5fe749079-1280x720.png" length="0" type="image/png"/>
    <pubDate>Wed, 25 Feb 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Indexação Vetorial Até 12x Mais Rápida no Elasticsearch com NVIDIA cuVS: Aceleração por GPU - Capítulo 2]]></title>
    <description><![CDATA[Descubra como o Elasticsearch alcança uma taxa de indexação quase 12x maior com indexação vetorial acelerada por GPU e NVIDIA cuVS.]]></description>
    <content:encoded><![CDATA[<p>No início deste ano, a Elastic anunciou a <a href="https://ir.elastic.co/news/news-details/2025/Elastic-Brings-Enterprise-Data-to-NVIDIA-AI-Factories/default.aspx">colaboração</a> com a NVIDIA para trazer aceleração de GPU ao Elasticsearch, integrando-se com a <a href="https://developer.nvidia.com/cuvs">NVIDIA cuVS</a>—conforme detalhado em uma <a href="https://www.nvidia.com/en-us/on-demand/session/gtc25-S71286/">sessão na NVIDIA GTC</a> e em vários <a href="https://www.elastic.co/search-labs/blog/gpu-accelerated-vector-search-elasticsearch-nvidia">blogs</a>. Esta postagem é uma atualização sobre o esforço de coengenharia com a equipe de busca vetorial da NVIDIA.</p><h2>Resumo</h2><p>Primeiro, vamos atualizá-lo. O Elasticsearch se estabeleceu como um poderoso banco de dados vetorial, oferecendo um rico conjunto de recursos e um forte desempenho para buscas por similaridade em larga escala. Com recursos como quantização escalar, Better Binary Quantization (<a href="https://www.elastic.co/search-labs/blog/better-binary-quantization-lucene-elasticsearch">BBQ</a>), operações vetoriais <a href="https://www.elastic.co/blog/accelerating-vector-search-simd-instructions">SIMD</a> e algoritmos mais eficientes em termos de disco, como <a href="https://www.elastic.co/search-labs/blog/diskbbq-elasticsearch-introduction">DiskBBQ</a>, ele já oferece opções eficientes e flexíveis para o gerenciamento de cargas de trabalho vetoriais.</p><p>Ao integrar o NVIDIA cuVS como um módulo chamável para tarefas de busca vetorial, buscamos entregar ganhos significativos no desempenho e eficiência da indexação vetorial para melhor suportar cargas de trabalho vetoriais em grande escala.</p><h2>O desafio</h2><p>Um dos maiores desafios na construção de um banco de dados vetorial de alto desempenho é a construção do índice vetorial - o gráfico <a href="https://arxiv.org/abs/1603.09320">HNSW</a>. Rapidamente, a construção de índices se torna dominada por milhões ou até bilhões de operações aritméticas, à medida que cada vetor é comparado com muitos outros. Além disso, operações do ciclo de vida do índice, como compactação e fusões, podem aumentar ainda mais a sobrecarga total de processamento da indexação. À medida que os volumes de dados e os embeddings vetoriais associados crescem exponencialmente, GPUs de computação acelerada, construídas para paralelismo massivo e matemática de alto rendimento, estão idealmente posicionadas para lidar com essas cargas de trabalho.</p><h2>Apresentando o plugin Elasticsearch-GPU</h2><p><a href="https://developer.nvidia.com/cuvs">NVIDIA cuVS</a> é uma biblioteca open source CUDA-X para busca vetorial acelerada por GPU e clustering de dados que permite a construção rápida de índices e recuperação de embeddings para cargas de trabalho de IA e recomendação.</p><p>O Elasticsearch utiliza o cuVS através do <a href="https://mvnrepository.com/artifact/com.nvidia.cuvs/cuvs-java">cuvs-java</a>, uma biblioteca de open source desenvolvida pela comunidade e mantida pela NVIDIA. A biblioteca cuvs-java é leve e se baseia na <a href="https://docs.rapids.ai/api/cuvs/nightly/c_api/">API C do cuVS</a>, utilizando a função estrangeira <a href="https://openjdk.org/projects/panama/">Panama</a> para expor os recursos do cuVS de uma maneira idiomática em Java, mantendo-se moderna e eficiente.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc7fd7361099da05a/6a17e920be608670af00477f/5f6daa1eb07f704a6707d9e6b7ccb81d0abaa8c9-566x419.png" alt="Como o Elasticsearch funciona com NVIDIA cuVS, indexação por CPU e GPU" /><p>A biblioteca cuvs-java está integrada a um <a href="https://github.com/elastic/elasticsearch/pull/135545">novo plugin do Elasticsearch</a>; portanto, a indexação na GPU pode ocorrer no mesmo node e processo do Elasticsearch, sem a necessidade de provisionar qualquer código ou hardware externo. Durante a criação do índice, se a biblioteca cuVS estiver instalada e uma GPU estiver presente e configurada, o Elasticsearch usará a GPU para acelerar o processo de indexação vetorial. Os vetores são fornecidos à GPU, que constrói um gráfico <a href="https://arxiv.org/abs/2308.15136">CAGRA</a>. Esse gráfico é então convertido para o formato HNSW, tornando-o imediatamente disponível para busca vetorial na CPU. O formato final do gráfico construído é o mesmo que seria construído na CPU; isso permite que o Elasticsearch utilize GPUs para indexação de alto desempenho quando o hardware subjacente a suporta, liberando poder de processamento da CPU para outras tarefas (buscar, processamento de dados, etc.).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt485f55f29d6df5c4/6a17e922be6086dcf3004785/3ea255bd9bfd7983f78143c5eba999d2149d72be-671x356.png" alt="" /><h2>Aceleração de construção de índice</h2><p>Como parte da integração da aceleração de GPU no Elasticsearch, várias melhorias foram feitas no cuvs-java, focando na entrada/saída eficiente de dados e na invocação de funções. Uma melhoria importante é o uso de <a href="https://github.com/rapidsai/cuvs/blob/2cf5fa7666d703dccbe655f8214656b0952bb69b/java/cuvs-java/src/main/java/com/nvidia/cuvs/CuVSMatrix.java">cuVSMatrix</a> para modelar vetores de forma transparente, independentemente de estarem na heap do Java, fora da heap ou na memória da GPU. Isso permite que os dados se movam eficientemente entre a memória e a GPU, evitando cópias desnecessárias de potencialmente bilhões de vetores.</p><p>Graças a essa abstração subjacente de cópia zero, tanto a transferência para a memória da GPU quanto a recuperação do gráfico podem ocorrer diretamente. Durante a indexação, os vetores são primeiro armazenados em buffer na memória do heap Java e depois enviados para a GPU para construir o gráfico CAGRA. O gráfico é posteriormente recuperado da GPU, convertido para o formato HNSW e persistido no disco.</p><p>No momento da fusão, os vetores já estão armazenar no disco, ignorando completamente o heap Java. Os arquivos de índice são mapeados em memória, e os dados são transferidos diretamente para a memória da GPU. O projeto também acomoda facilmente diferentes larguras de bits, como float32 ou int8, e se estende naturalmente a outros esquemas de quantização.</p><h2>Drumroll... então, como funciona?</h2><p>Antes de entrarmos nos números, um pouco de contexto é útil. A fusão de segmentos no Elasticsearch normalmente é executada de forma automática em segundo plano durante a indexação, o que dificulta a realização de testes de desempenho isoladamente. Para obter resultados reprodutíveis, usamos a fusão forçada para desencadear explicitamente a fusão de segmentos em um experimento controlado. Como a fusão forçada realiza as mesmas operações subjacentes de fusão que a fusão em segundo plano, seu desempenho serve como um indicador útil das melhorias esperadas, mesmo que os ganhos exatos possam diferir nas cargas de trabalho de indexação do mundo real.</p><p>Agora, vamos ver os números.</p><p>Nossos resultados iniciais de benchmark são muito promissores. Executamos o benchmark em uma instância AWS <code>g6.4xlarge</code> com armazenamento NVMe conectado localmente. Um único node do Elasticsearch foi configurado para usar o número padrão e ideal de threads de indexação (8 - uma para cada núcleo físico) e para desativar <a href="https://www.elastic.co/docs/reference/elasticsearch/index-settings/merge">a limitação de mesclagem</a> (o que é menos aplicável com discos NVMe rápidos).</p><p>Para o conjunto de dados, usamos 2,6 milhões de vetores com 1.536 dimensões da <a href="https://github.com/elastic/rally-tracks/blob/master/openai_vector/README.md">trilha vetorial do OpenAI Rally</a>, codificados como <a href="https://github.com/elastic/elasticsearch/pull/137072">strings base64</a> e indexados como float32 <em>hnsw</em>. Em todos os cenários, os gráficos construídos atingem níveis de recall de até 95%. Veja o que descobrimos:</p><ul><li><p><strong>Taxa de transferência de indexação:</strong> ao transferir a construção de gráficos para a GPU durante as descargas de buffer na memória, aumentamos a taxa de transferência em cerca de 12 vezes.</p></li><li><p><strong>Fusão forçada:</strong> após a conclusão da indexação, a GPU continua acelerando a fusão de segmentos, acelerando a fase de mesclagem forçada em aproximadamente 7x.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfea4ee13b5a3b10d/6a17e923e9ea879c6aa9c616/f60ea9ee5996e456f393ffd195ee7eada6e5a7c2-948x387.png" alt="" /><ul><li><p><strong>Uso da CPU:</strong> o descarregamento da construção de gráficos para a GPU reduz significativamente a utilização média e de pico da CPU. Os gráficos abaixo ilustram o uso da CPU durante a indexação e a fusão, destacando o quanto é menor quando essas operações são executadas na GPU. Menor utilização da CPU durante a indexação da GPU libera ciclos de CPU que podem ser redirecionados para melhorar o desempenho da busca.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt80ff9c53f9b6884a/6a17e925445de9ee4b4d0187/5e680a5fc41700a877f3d8b2e5ce18ebd3f37a0b-1600x562.png" alt="" /><ul><li><p><strong>Lembrete:</strong> a precisão permanece efetivamente a mesma entre as execuções de CPU e GPU, com o gráfico construído por GPU alcançando um recall marginalmente mais alto.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5cbe084eca27b8e4/6a17e926faa913317093c8b7/48a2b7758606bd321712b7d8378cd2640e652a4e-1384x544.png" alt="" /><h2>Comparando em outra dimensão: Preço</h2><p>A comparação anterior usava intencionalmente hardware idêntico, com a única diferença sendo se a GPU era usada durante a indexação. Essa configuração é útil para isolar efeitos brutos de computação, mas também podemos olhar para a comparação dos custos.</p><p>Por aproximadamente o mesmo preço por hora da configuração acelerada por GPU, é possível provisionar uma configuração apenas CPU com aproximadamente o dobro dos recursos comparáveis de CPU e memória: 32 vCPUs (AMD EPYC) e 64 GB de RAM, permitindo dobrar o número de threads de indexação para 16.</p><p>Para manter a comparação justa e consistente, executamos esse experimento apenas com CPU em uma instância AWS g6.8xlarge, com a GPU explicitamente desativada. Isso nos permitiu manter todas as outras características de hardware constantes ao avaliar a relação custo-desempenho da aceleração da GPU em comparação  com a indexação somente da CPU.</p><p>A instância mais potente da CPU mostra desempenho melhor em comparação com os benchmarks da seção acima, como era de se esperar. No entanto, ao compararmos essa instância de CPU mais potente com os resultados originais acelerados por GPU, a GPU ainda oferece ganhos de desempenho substanciais: <strong>melhoria de aproximadamente 5 vezes</strong> na taxa de transferência de indexação e <strong>aproximadamente 6 vezes </strong>na fusão forçada, tudo isso enquanto constrói gráficos que atingem níveis de recall de até <strong>95%</strong>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt94b5eb6f95ba307d/6a17e928abe0f255d4dfea35/8ffa58cae3ad175ef2932a351aeef4c34a1407b9-948x394.png" alt="" /><h2>Conclusão</h2><p>Em cenários de ponta a ponta, a aceleração de GPU com NVIDIA cuVS proporciona quase 12x de melhoria na taxa de indexação e uma redução de 7x na latência de fusão forçada, com uma utilização significativamente menor da CPU. Isso mostra que a indexação vetorial e as cargas de trabalho de fusão se beneficiam significativamente da aceleração da GPU. Em uma comparação ajustada ao custo, a aceleração da GPU continua a gerar ganhos substanciais de desempenho, com taxa de transferência de indexação aproximadamente 5 vezes maior e operações de fusão forçada 6 vezes mais rápidas.</p><p>A indexação de vetores acelerada por GPU está atualmente planejada para Prévia Técnica no Elasticsearch 9.3, que está programada para ser lançada no início de 2026.</p><p>Fique ligado para mais.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-gpu-accelerated-vector-indexing-nvidia</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-gpu-accelerated-vector-indexing-nvidia</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <dc:creator><![CDATA[Chris Hegarty,Hemant Malik,Corey Nolet,Manas Singh,Mithun Radhakrishnan,Mayya Sharipova,Lorenzo Dematte,Ben Frederickson]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1248d51633bd75d9/6a17e92ae9ea8714b3a9c61a/08f7469a4daaf67b7c5999585aae179b6680c78d-896x746.png" length="0" type="image/png"/>
    <pubDate>Wed, 03 Dec 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Busca multimodal de picos de montanhas com Elasticsearch e SigLIP-2 ]]></title>
    <description><![CDATA[Aprenda como implementar buscas multimodais de texto para imagem e de imagem para imagem usando embeddings SigLIP-2 e busca vetorial kNN do Elasticsearch. Objetivo do projeto: encontrar fotos do pico do Monte Ama Dablam tiradas durante uma trilha no Everest.]]></description>
    <content:encoded><![CDATA[<p>Você já quis pesquisar seu álbum de fotos por significado? Experimente buscas como "mostre-me fotos minhas onde estou usando uma jaqueta azul e sentado em um banco", "mostre-me fotos do Monte Everest" ou "saquê e sushi". Pegue uma xícara de café (ou sua bebida favorita) e continue lendo. Neste blog, mostraremos como criar um aplicativo de busca híbrido multimodal. Multimodal significa que o aplicativo consegue entender e pesquisar em diferentes tipos de entrada — texto, imagens e áudio — e não apenas palavras. Híbrido significa que combina técnicas como correspondência de palavras-chave, busca vetorial kNN e geofencing para fornecer resultados mais precisos.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdfa1ec1ccd450e94/6a17da751d1b8308ee93e344/0ec6bbb45013846b59ee00d2bf73ee2182ee7392-1920x1080.gif" alt="Biblioteca de fotos de diferentes picos de montanhas da trilha até o Monte Everest." /><p>Para isso, utilizamos o SigLIP-2 do Google para gerar representações vetoriais tanto para imagens quanto para texto, e as armazenamos no banco de dados vetorial Elasticsearch. No momento da consulta, convertemos a entrada da pesquisa, seja texto ou imagem, em representações vetoriais (embeddings) e executamos buscas vetoriais kNN rápidas para recuperar os resultados. Essa configuração permite uma busca eficiente de texto para imagem e de imagem para imagem. A interface Streamlit UI dá vida a este projeto, fornecendo-nos um frontend que não só permite realizar buscas textuais para encontrar e visualizar as fotos correspondentes no álbum, como também nos permite identificar o pico da montanha na imagem carregada e visualizar outras fotos dessa montanha no álbum.
Abordamos também as medidas que tomamos para melhorar a precisão da pesquisa, juntamente com dicas e truques práticos. Para uma exploração mais aprofundada, disponibilizamos um <a href="https://github.com/navneet83/multimodal-mountain-peak-search">repositório no GitHub</a> e um <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/notebooks/multimodal_mountain_peak_search.ipynb">notebook no Colab</a>.</p><h2>Como tudo começou</h2><p>Este post do blog foi inspirado por uma criança de 10 anos que me pediu para mostrar todas as fotos do Monte Ama Dablam que tirei na minha trilha até o Acampamento Base do Everest. Enquanto examinávamos o álbum de fotos, também me pediram para identificar vários outros picos de montanhas, alguns dos quais eu não sabia o nome.</p><p>Isso me deu a ideia de que este pode ser um projeto divertido de visão computacional. O que queríamos alcançar:</p><ul><li><p>Encontre fotos de um pico de montanha pelo nome.</p></li><li><p>Adivinhe o nome do pico da montanha a partir de uma imagem e encontre picos semelhantes no álbum de fotos.</p></li><li><p>Fazer com que as consultas de conceito funcionem (<em>pessoa</em>, <em>rio</em>, <em>bandeiras de oração</em>, <em>etc.)</em></p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf82df9d7005fc3fe/6a17da78abe0f2e77bdfe8b9/e9d0d720a9b565d5b749bdc915068852d4f157ad-1200x1600.png" alt="Monte Ama Dablam " /><h2>Montando a equipe dos sonhos: SigLIP-2, Elasticsearch e Streamlit</h2><p>Rapidamente ficou claro que, para isso funcionar, precisaríamos transformar tanto o texto (“Ama Dablam”) quanto as imagens (fotos do meu álbum) em vetores que pudessem ser comparados de forma significativa, ou seja, no mesmo espaço vetorial. Uma vez feito isso, a busca se torna simplesmente "encontrar os vizinhos mais próximos".</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5f80b69a9d5bd28a/6a17da7a4b055ddd1243209e/20e6f8b7d4fa48414f407ec200adbe00ee28d517-1536x1024.png" alt="SigLIP-2, Elasticsearch e Streamlit - uma combinação dos sonhos." /><p>Para gerar representações vetoriais de imagens, usamos um<a href="https://huggingface.co/blog/vlms-2025"> codificador multilíngue de visão e linguagem</a>, de modo que uma foto de uma montanha e uma frase como "Ama Dablam" fiquem no mesmo espaço vetorial.</p><p><a href="https://huggingface.co/blog/siglip2"><strong>O SigLIP-2</strong></a>, lançado recentemente pelo Google, se encaixa bem aqui. Ele consegue gerar embeddings sem treinamento específico para a tarefa (uma configuração <strong>zero-shot</strong> ) e funciona bem para o nosso caso de uso: fotos não rotuladas e picos com nomes e idiomas diferentes. Como foi treinado para correspondência de texto ↔ imagem, uma foto da montanha tirada durante a trilha e um breve texto de exemplo resultam em representações vetoriais muito semelhantes, mesmo quando o idioma ou a ortografia da consulta variam.</p><p>O SigLIP-2 oferece um excelente equilíbrio entre qualidade e velocidade, suporta múltiplas resoluções de entrada e funciona tanto na CPU quanto na GPU. O SigLIP-2 foi projetado para ser mais resistente a fotos tiradas ao ar livre em comparação com modelos anteriores, como o CLIP original. Durante nossos testes, o SigLIP-2 gerou resultados confiáveis de forma consistente. Além disso, conta com amplo suporte, o que a torna a escolha óbvia para este projeto.</p><p>Em seguida, precisamos de um banco de dados vetorial para armazenar os embeddings e realizar buscas avançadas. Deveria suportar não apenas a busca kNN de cosseno em embeddings de imagem, mas também aplicar filtros de geolocalização e texto em uma única consulta. O Elasticsearch se encaixa bem aqui: ele lida muito bem com vetores (HNSW kNN em campos dense_vector), suporta busca híbrida que combina consultas de texto, vetores e geolocalização, e oferece filtragem e classificação prontas para uso. Ele também se adapta à escala horizontal, facilitando a expansão de um punhado de fotos para milhares. O <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/python">cliente oficial do Elasticsearch para Python</a> mantém a infraestrutura simples e se integra perfeitamente ao projeto. Por fim, precisamos de uma interface leve onde possamos inserir consultas de pesquisa e visualizar os resultados. Para uma demonstração rápida baseada em Python, o Streamlit é uma ótima opção. Ele fornece os recursos básicos de que precisamos: upload de arquivos, uma grade de imagens responsiva e menus suspensos para classificação e geolocalização. É fácil clonar e executar localmente, e também funciona em um notebook do Colab.</p><h2>Implementação</h2><h3>Design e estratégia de indexação do Elasticsearch</h3><p>Usaremos dois índices para este projeto: <code>peaks_catalog</code> e <code>photos</code>.</p><h4>Índice do catálogo de picos</h4><p>Este índice serve como um catálogo compacto dos picos de montanhas mais proeminentes que podem ser vistos durante a trilha até o Acampamento Base do Everest. Cada documento neste índice corresponde a um único pico de montanha, como o Monte Everest. Para cada documento de pico de montanha, armazenamos nomes/apelidos, coordenadas opcionais de latitude e longitude e um único vetor protótipo construído pela combinação de prompts de texto SigLIP-2 (e imagens de referência opcionais).</p><p><strong>Mapeamento do índice:</strong></p><p>Campo</p><p>Tipo</p><p>Exemplo</p><p>Objetivo/Observações</p><p>Vetor/Indexação</p><p>eu ia</p><p>palavra-chave</p><p>ama-dablam</p><p>Slug/ID estável</p><p>—</p><p>nomes</p><p>texto + subcampo de palavra-chave</p><p>["Ama Dablam","Amadablam"]</p><p>Aliases / nomes multilíngues; names.raw para filtros exatos</p><p>—</p><p>latlon</p><p>ponto_geográfico</p><p>{"lat":27.8617,"lon":86.8614}</p><p>Coordenadas GPS do pico como uma combinação de latitude/longitude (opcional)</p><p>—</p><p>elev_m</p><p>inteiro</p><p>6812</p><p>Elevação (opcional)</p><p>—</p><p>texto incorporado</p><p>dense_vector</p><p>768</p><p>Protótipo misto (com instruções e, opcionalmente, 1 a 3 imagens de referência) para este pico.</p><p>índice:true, similaridade:"cosseno", opções_de_índice:{type:"hnsw", m:16, ef_construction:128}</p><p>Este índice é usado principalmente para buscas de imagem para imagem, como identificar picos de montanhas a partir de imagens. Também utilizamos esse índice para aprimorar os resultados de busca de texto para imagem.</p><p>Em resumo, o <code>peaks_catalog</code> transforma a pergunta "Que montanha é esta?" em um problema de vizinho mais próximo focado, separando efetivamente a compreensão conceitual das complexidades dos dados da imagem.</p><p><strong>Estratégia de indexação para o índice peaks_catalog: </strong>Começamos criando uma lista dos picos mais proeminentes visíveis durante a trilha do Campo Base do Everest. Para cada pico, armazenamos sua localização geográfica, nome, sinônimos e altitude em um <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/data/peaks.yaml">arquivo YAML</a>. O próximo passo é <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L351">gerar o embedding</a> para cada pico e armazená-lo no campo <code>text_embed</code> . Para gerar embeddings robustos, utilizamos a seguinte técnica:</p><ul><li><p>Crie um protótipo de texto usando:</p><ul><li><p>nomes dos picos</p></li><li><p><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L301">Conjunto de prompts</a> (usando vários prompts diferentes para tentar responder à mesma pergunta), por exemplo:</p><ul><li><p>“uma foto natural do pico da montanha {name} no Himalaia, Nepal”</p></li><li><p>“{name} pico emblemático na região de Khumbu, paisagem alpina”</p></li><li><p>“{name} cume da montanha, neve, crista rochosa”</p></li></ul></li><li><p><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L333">Anti-conceito</a> opcional (indicando ao SigLIP-2 o que não deve ser correspondido): subtrair um pequeno vetor para "pintura, ilustração, pôster, mapa, logotipo" para que haja uma preferência por fotos reais.</p></li></ul></li><li><p>Opcionalmente, <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L388C13-L388C29">crie um protótipo de imagem</a> se forem fornecidas imagens de referência do pico.</p></li></ul><p>Em seguida, <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L392">combinamos o texto e o protótipo da imagem</a> para gerar a incorporação final. Finalmente, o documento é <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L396">indexado</a> com todos os campos necessários:</p>def l2norm(v: np.ndarray) -&gt; np.ndarray:
    return v / (np.linalg.norm(v) + 1e-12)
def compute_blended_peak_vec(
        emb: Siglip2,
        names: List[str],
        peak_id: str,
        peaks_images_root: str,
        alpha_text: float = 0.5,
        max_images: int = 3,
) -&gt; Tuple[np.ndarray, int, int, List[str]]:
    """
    Build blended vector for a single peak.

    Returns:
      vec           : np.ndarray (L2-normalized)
      found_count   : number of reference images discovered
      used_count    : number of references used (&lt;= max_images)
      used_filenames: list of filenames used (for logging)
    """
    # 1) TEXT vector
    tv = embed_text_blend(emb, names)

    # 2) IMAGE refs: prefer folder by id; fallback to slug of the primary name
    root = Path(peaks_images_root)
    candidates = [root / peak_id]
    if names:
        candidates.append(root / slugify(names[0]))

    all_refs: List[Path] = []
    for c in candidates:
        if c.exists() and c.is_dir():
            all_refs = list_ref_images(c)
            if all_refs:
                break

    found = len(all_refs)
    used_list = all_refs[:max_images] if (max_images and found &gt; max_images) else all_refs
    used = len(used_list)

    img_v = embed_image_mean(emb, used_list) if used_list else None

    # 3) Blend TEXT and IMAGE vectors, clamp alpha to [0,1]
    a = max(0.0, min(1.0, float(alpha_text)))
    vec = l2norm(tv if img_v is None else (a * tv + (1.0 - a) * img_v)).astype("float32")
    return vec, found, used, [p.name for p in used_list]<p>Documento de exemplo do índice <code>peaks_catalog</code> :</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1219f5d0e39b512c/6a17da7c57726263161bcace/bc05fbd0c4f8d721d5170c28a3884a9eda80bb7d-1210x1132.png" alt="Um documento de exemplo do índice peaks_catalog no Elasticsearch." /><h4>Índice de fotos</h4><p>Este índice principal armazena informações detalhadas sobre todas as fotos do álbum. Cada documento representa uma única fotografia, contendo as seguintes informações:</p><ul><li><p>Caminho relativo até a foto no álbum de fotos. Isso pode ser usado para visualizar a imagem correspondente ou carregar a imagem na interface de pesquisa.</p></li><li><p>Informações de GPS e horário da imagem.</p></li><li><p>Vetor denso para codificação de imagem gerado por SigLIP-2.</p></li><li><p><code>predicted_peaks</code> Isso nos permite filtrar pelo nome do pico.

<strong>Mapeamento de índice</strong></p></li></ul><p>Campo</p><p>Tipo</p><p>Exemplo</p><p>Objetivo/Observações</p><p>Vetor / Indexação</p><p>caminho</p><p>palavra-chave</p><p>dados/imagens/IMG_1234.HEIC</p><p>Como a interface do usuário abre a miniatura/imagem completa</p><p>—</p><p>imagem_recortada</p><p>dense_vector</p><p>768</p><p>Incorporação de imagem SigLIP-2</p><p>índice:true, similaridade:"cosseno", opções_de_índice:{type:"hnsw", m:16, ef_construction:128}</p><p>picos_previstos</p><p>palavra-chave</p><p>["ama-dablam","pumori"]</p><p>Top-K palpites no momento da indexação (filtro/faceta de UX barato)</p><p>—</p><p>GPS</p><p>ponto_geográfico</p><p>{"lat":27.96,"lon":86.83}</p><p>Ativa filtros geográficos</p><p>—</p><p>tempo_de_tiro</p><p>date</p><p>2023-10-18T09:41:00Z</p><p>Tempo de captura: classificar/filtrar</p><p>—</p><p><strong>Estratégia de indexação para o índice de fotos: </strong>Para cada foto no álbum, fazemos o seguinte:
 Extrair informações das imagens <code>shot_time</code> e <code>gps</code> <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L526">dos metadados da imagem</a>.</p><ul><li><p><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L511">Incorporação de imagem SigLIP-2</a>: passe a imagem pelo modelo e normalize o vetor usando a notação L2. Armazene o embedding no campo <code>clip_image</code> .</p></li><li><p><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L519">Preveja os picos</a> e armazene-os no campo <code>predicted_peaks</code> . Para fazer isso, primeiro pegamos o vetor de imagem da foto gerado na etapa anterior e, em seguida, executamos uma busca kNN rápida no campo text_embed no índice <code>peaks_catalog</code> . Mantemos os 3 ou 4 picos mais altos e ignoramos o resto.</p></li><li><p>Calculamos o campo <code>_id</code> fazendo um <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L509">hash</a> no nome e caminho da imagem. Isso garante que não teremos duplicatas após várias execuções.</p></li></ul><p>Após determinarmos todos os campos da foto, os documentos fotográficos são indexados em lotes usando indexação <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L530">em massa</a> :</p>def bulk_index_photos(
        es: Elasticsearch,
        images_root: str,
        photos_index: str = "photos",
        peaks_index: str = "peaks_catalog",
        topk_predicted: int = 5,
        batch_size: int = 200,
        refresh: str = "false",
) -&gt; None:
    """Walk a folder of images, embed + enrich, and bulk index to Elasticsearch."""
    root = Path(images_root)
    if not root.exists():
        raise SystemExit(f"Images root not found: {images_root}")

    emb = Siglip2()
    batch: List[Dict[str, Any]] = []
    n_indexed = 0

    for p in iter_images(root):
        rel = relpath_within(root, p)
        _id = id_for_path(rel)

        # 1) Image embedding (and reuse it for predicted_peaks)
        try:
            with Image.open(p) as im:
                ivec = emb.image_vec(im.convert("RGB")).astype("float32")
        except (UnidentifiedImageError, OSError) as e:
            print(f"[skip] {rel} — cannot embed: {e}")
            continue

        # 2) Predict top-k peak names
        try:
            top_names = predict_peaks(es, ivec.tolist(), peaks_index=peaks_index, k=topk_predicted)
        except Exception as e:
            print(f"[warn] predict_peaks failed for {rel}: {e}")
            top_names = []

        # 3) EXIF enrichment (safe)
        gps = get_gps_decimal(str(p))
        shot = get_shot_time(str(p))

        # 4) Build doc and stage for bulk
        doc = {"path": rel, "clip_image": ivec.tolist(), "predicted_peaks": top_names}
        if gps:
            doc["gps"] = gps
        if shot:
            doc["shot_time"] = shot

        batch.append(
            {"_op_type": "index", "_index": photos_index, "_id": _id, "_source": doc}
        )

        # 5) Periodic flush
        if len(batch) &gt;= batch_size:
            helpers.bulk(es, batch, refresh=refresh)
            n_indexed += len(batch)
            print(f"[photos] indexed {n_indexed} (last: {rel})")
            batch.clear()

    # Final flush
    if batch:
        helpers.bulk(es, batch, refresh=refresh)
        n_indexed += len(batch)
        print(f"[photos] indexed {n_indexed} total.")

    print("[done] photos indexing")<p>Exemplo de documento do índice de fotos:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt744b7e6326937cfc/6a17da7e6df731d3040a0da8/1dc1406ac2a97440b6804838795b3c2205c4c6b2-1080x1234.png" alt="Um documento de exemplo do índice de fotos no Elasticsearch." /><p>Em resumo, o índice de fotos é um armazenamento rápido, filtrável e compatível com kNN de todas as fotos do álbum. Seu mapeamento é propositalmente minimalista — apenas a estrutura necessária para recuperar rapidamente, exibir de forma clara e segmentar os resultados por espaço e tempo. Este índice serve para ambos os casos de uso de pesquisa. O script em Python para criar ambos os índices pode ser encontrado <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/create_indices.py">aqui</a>.</p><p>A visualização do mapa Kibana abaixo exibe documentos do álbum de fotos como pontos verdes e picos de montanhas do índice <code>peaks_catalog</code> como triângulos vermelhos, com os pontos verdes alinhando-se bem com a trilha da caminhada até o Acampamento Base do Everest.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb5bf016e8d9c3e84/6a17da80be608681f10045e6/1c75d0ed0ce53d28a94bf2f47a354e25581d2baf-1600x1402.png" alt="Uma visualização do mapa Kibana exibindo documentos do álbum de fotos como pontos verdes e picos de montanhas do índice peaks_catalog como triângulos vermelhos, com os pontos verdes alinhando-se bem com a trilha da caminhada até o Acampamento Base do Everest." /><h2>Pesquisar casos de uso</h2><p><strong>Busca por nome (texto para imagem):</strong> Este recurso permite que os usuários localizem fotos de picos de montanhas (e até mesmo conceitos abstratos como "bandeiras de oração") usando consultas de texto. Para isso, o texto de entrada é <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L87C5-L87C20">convertido em um vetor de texto</a> usando o SigLIP-2. Para geração robusta de vetores de texto, empregamos a mesma estratégia usada para criar embeddings de texto no índice <code>peaks_catalog</code> : <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L104">combinando</a> a entrada de texto com um pequeno <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L100">conjunto de prompts</a>, subtraindo um<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L103"> vetor de anti-conceito</a> menor e aplicando <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L104">a normalização L2</a> para produzir o vetor de consulta final. Uma <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L140">consulta</a> kNN é então executada no campo <code>photos.clip_image</code> para recuperar os picos correspondentes principais, com base na similaridade de cosseno para encontrar as imagens mais próximas. Opcionalmente, os resultados da pesquisa podem ser tornados mais relevantes aplicando <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L152">filtros</a> geográficos e de data e/ou um filtro de termo <code>photos.predicted_peaks</code> como parte da consulta (veja exemplos de consulta abaixo). Isso ajuda a excluir picos semelhantes que, na verdade, não estão visíveis durante a trilha.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9bb9abf5ce64fcbb/6a17da81e8fbce20db3a17da/b5fac28ffdbedb820505365ca07df125cd01b939-946x370.png" alt="Como funciona a busca multimodal por nome (texto para imagem) no Elasticsearch." /><p><strong>Consulta Elasticsearch com filtro geográfico:</strong></p>POST photos/_search
{
  "knn": {
    "field": "clip_image",
    "query_vector": [ ... ],
    "k": 60,
    "num_candidates": 2000
  },
  "query": {
    "bool": {
      "filter": [
        { "geo_bounding_box": { "gps": { "top_left": "...", "bottom_right": "..." } } }
      ]
    }
  },
  "_source": ["path","predicted_peaks","gps","shot_time"]
}

Response (first two documents):
{
 "hits": {
   "total": {
     "value": 56,
     "relation": "eq"
   },
   "max_score": 0.5779596,
   "hits": [
     {
       "_index": "photos",
       "_id": "d01da3a1141981486c3493f6053c79e92a788463",
       "_score": 0.5779596,
       "_source": {
         "path": "IMG_2738.HEIC",
         "predicted_peaks": [
           "Pumori",
           "Kyajo Ri",
           "Khumbila",
           "Nangkartshang",
           "Kongde Ri"
         ],
         "gps": {
           "lat": 27.97116388888889,
           "lon": 86.82331111111111
         },
         "shot_time": "2023-11-03T08:07:13"
       }
     },
     {
       "_index": "photos",
       "_id": "c79d251f07adc5efaedc53561110a7fd78e23914",
       "_score": 0.5766071,
       "_source": {
         "path": "IMG_2761.HEIC",
         "predicted_peaks": [
           "Kyajo Ri",
           "Makalu",
           "Baruntse",
           "Cho Oyu",
           "Khumbila"
         ],
         "gps": {
           "lat": 27.975558333333332,
           "lon": 86.82515
         },
         "shot_time": "2023-11-03T08:51:08"
       }
     }
}<p><strong>Busca por imagem (imagem para imagem):</strong> Este recurso permite identificar uma montanha em uma imagem e encontrar outras imagens dessa mesma montanha no álbum de fotos. Quando uma imagem é carregada, ela é processada pelo codificador de imagens SigLIP-2 para gerar um <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/identify_from_picture_find_similar_peaks.py#L228">vetor de imagem</a>. Uma <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/identify_from_picture_find_similar_peaks.py#L234">busca kNN</a> é então realizada no campo <code>peaks_catalog.text_embed</code> para identificar os nomes de picos que melhor correspondem. Em seguida, um <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/identify_from_picture_find_similar_peaks.py#L257">vetor de texto é gerado</a> a partir desses nomes de picos correspondentes, e outra <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/identify_from_picture_find_similar_peaks.py#L263">busca kNN</a> é realizada no índice de fotos para localizar as imagens correspondentes.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltab9d16333e2a9e69/6a17da827f6f155448c099cc/3a3d5635bee7a222b95529dd7f9fbee016381610-1226x550.png" alt="Como funciona a busca multimodal por imagem (imagem para imagem) no Elasticsearch." /><p><strong>Consulta do Elasticsearch:</strong></p><p>Passo 1: Encontre os nomes de pico correspondentes.</p>GET peaks_catalog/_search
{
 "knn": {
   "field": "text_embed",
   "query_vector": [...image-vector... ],
   "k": 3,
   "num_candidates": 500
 },
 "_source": [
   "id",
   "names",
   "latlon",
   "text_embed"
 ]
}


Response (first two documents):
{
 "took": 2,
 "timed_out": false,
 "_shards": {
   "total": 1,
   "successful": 1,
   "skipped": 0,
   "failed": 0
 },
 "hits": {
   "total": {
     "value": 3,
     "relation": "eq"
   },
   "max_score": 0.58039916,
   "hits": [
     {
       "_index": "peaks_catalog",
       "_id": "pumori",
       "_score": 0.58039916,
       "_source": {
         "id": "pumori",
         "names": [
           "Pumori",
           "Pumo Ri"
         ],
         "latlon": {
           "lat": 28.01472,
           "lon": 86.82806
         },
         "text_embed": [
                  ... embeddings...
         ]
       }
     },
     {
       "_index": "peaks_catalog",
       "_id": "kyajo-ri",
       "_score": 0.57942784,
       "_source": {
         "id": "kyajo-ri",
         "names": [
           "Kyajo Ri",
           "Kyazo Ri"
         ],
         "latlon": {
           "lat": 27.909167,
           "lon": 86.673611
         },
         "text_embed": [
           ... embeddings...
         ]
       }
     }
   ]
 }
}<p>Etapa 2: Realize uma busca no índice <code>photos</code> para encontrar as imagens correspondentes (mesma consulta mostrada no caso de uso de busca de texto para imagem):</p>POST photos/_search
{
 "knn": {
   "field": "clip_image",
   "query_vector": [ ...image-vector... ],
   "k": 30,
   "num_candidates": 2000
 },
 "_source": [
   "path",
   "gps",
   "shot_time",
   "predicted_peaks",
   "clip_image"
 ],
 "query": {
   "bool": {
     "filter": [
       {
         "term": {
           "predicted_peaks": "Pumori"
         }
       }
     ]
   }
 }
}


Response (first two documents):
{
 "hits": {
   "total": {
     "value": 56,
     "relation": "eq"
   },
   "max_score": 0.5779596,
   "hits": [
     {
       "_index": "photos",
       "_id": "d01da3a1141981486c3493f6053c79e92a788463",
       "_score": 0.5779596,
       "_source": {
         "path": "IMG_2738.HEIC",
         "predicted_peaks": [
           "Pumori",
           "Kyajo Ri",
           "Khumbila",
           "Nangkartshang",
           "Kongde Ri"
         ],
         "gps": {
           "lat": 27.97116388888889,
           "lon": 86.82331111111111
         },
         "shot_time": "2023-11-03T08:07:13"
       }
     },
     {
       "_index": "photos",
       "_id": "c79d251f07adc5efaedc53561110a7fd78e23914",
       "_score": 0.5766071,
       "_source": {
         "path": "IMG_2761.HEIC",
         "predicted_peaks": [
           "Kyajo Ri",
           "Makalu",
           "Baruntse",
           "Cho Oyu",
           "Khumbila"
         ],
         "gps": {
           "lat": 27.975558333333332,
           "lon": 86.82515
         },
         "shot_time": "2023-11-03T08:51:08"
       }
     }
}<h2>Interface de usuário Streamlit</h2><p>Para integrar tudo, criamos uma interface de usuário Streamlit simples que nos permite executar ambos os casos de uso de pesquisa. A barra lateral esquerda exibe uma lista rolável de picos (agregados de <code>photos.predicted_peaks</code>) com caixas de seleção e um minimapa/filtro geográfico. Na parte superior, há uma caixa <strong>de pesquisa por nome</strong> e um botão <strong>para identificar o usuário a partir de uma foto</strong> enviada. O painel central apresenta uma grade de miniaturas interativa que exibe as pontuações kNN, os indicadores de pico previsto e os horários de captura. Cada imagem inclui um botão <strong>"Ver imagem"</strong> para pré-visualizações em resolução total.</p><p><strong>Pesquisa por upload de imagem:</strong> Prevemos o pico e encontramos picos correspondentes no álbum de fotos.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd1fb2b0304a310d2/6a17da8425daab7cda08a0fa/dca540cbf5279e6d6102c5a0c0351ddd4ac91cda-1600x1112.png" alt="Uma interface de usuário simples e intuitiva que permite a busca multimodal por texto para imagem e por imagem para os picos do Monte Ama Dablam." /><p><strong>Pesquisa por texto</strong>: Encontre os picos correspondentes no álbum a partir do texto.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt496c1ae8f7886320/6a17da86abe0f2da48dfe8bd/b1e8618db746cd49ea4962d3dc73031387b975dd-1600x1166.png" alt="Como pesquisar um pico do Monte Everest usando a busca por texto na biblioteca de picos de montanhas." /><h2>Conclusão</h2><p>Tudo começou com <em>uma pergunta: "Podemos ver as </em><em> fotos</em><em><strong>do Ama Dablam ?"</strong></em> transformou-se em um pequeno sistema <strong>de busca multimodal</strong> funcional. Capturamos fotos brutas da trilha, transformamos em <strong>embeddings SigLIP-2</strong> e usamos <strong>o Elasticsearch</strong> para realizar uma rápida <strong>análise kNN</strong> sobre vetores, além de filtros geográficos/temporais simples para exibir as imagens <em>relevantes</em>. Ao longo do processo, separamos as preocupações com dois índices: um pequeno <code>peaks_catalog</code> de protótipos combinados (para identificação) e um índice escalável <code>photos</code> de vetores de imagem e EXIF (para recuperação). É prático, reproduzível e fácil de expandir.</p><p>Se você quiser ajustá-lo, existem algumas configurações que você pode modificar:</p><ul><li><p><strong>Configurações de tempo de consulta:</strong> <code>k</code> (quantos vizinhos você deseja retornar) e <code>num_candidates</code> (quão ampla a pesquisa antes da pontuação final). Essas configurações são discutidas no blog <a href="https://www.elastic.co/search-labs/blog/elasticsearch-knn-and-num-candidates-strategies">aqui</a>.</p></li><li><p><strong>Configurações de tempo de indexação:</strong> <code>m</code> (conectividade do grafo) e <code>ef_construction</code> (precisão do tempo de construção vs. memória). Para consultas, experimente também com <code>ef_search</code> — um valor maior geralmente significa melhor recuperação com alguma compensação de latência. Consulte <a href="https://www.elastic.co/search-labs/blog/hnsw-graph">este blog</a> para obter mais detalhes sobre essas configurações.</p></li></ul><p>Olhando para o futuro, modelos/reclassificadores nativos para busca <strong>multimodal</strong> e <strong>multilíngue</strong> chegarão em breve ao ecossistema Elastic, o que deverá tornar a recuperação de imagens/texto e a classificação híbrida ainda mais robustas e prontas para uso.<a href="https://ir.elastic.co/news/news-details/2025/Elastic-Completes-Acquisition-of-Jina-AI-a-Leader-in-Frontier-Models-for-Multimodal-and-Multilingual-Search/default.aspx?utm_source=chatgpt.com"> ir.elastic.co+1</a></p><p>Se você quiser experimentar você mesmo:</p><ul><li><p><strong>Repositório do GitHub:</strong> <a href="https://github.com/navneet83/multimodal-mountain-peak-search"><em>https://github.com/navneet83/multimodal-mountain-peak-search</em></a></p></li><li><p><strong>Guia rápido do Colab:</strong> <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/notebooks/multimodal_mountain_peak_search.ipynb">https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/notebooks/multimodal_mountain_peak_search.ipynb</a></p></li></ul><p>Com isso, nossa jornada chegou ao fim e é hora de voltar para casa. Espero que isso tenha sido útil e, se você quebrar alguma coisa (ou melhorar alguma coisa), adoraria saber o que você mudou.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdce2fff1569d2a8b/6a17da894b055dd1f24320a2/d324d1e1472f1bfbd8f25747f57bdeeb9c7f16b2-1600x1200.png" alt="" />]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/multimodal-search-siglip-2-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/multimodal-search-siglip-2-elasticsearch</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <category><![CDATA[Busca híbrida]]></category>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[Python]]></category>
    <dc:creator><![CDATA[Navneet Kumar]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltccb66279debb05f9/6a17da8b63baffe228741b15/ffcf93358a7c5dadcea82faf3de460bf060d003c-1600x1200.png" length="0" type="image/png"/>
    <pubDate>Tue, 04 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Melhorando a relevância de modelos de incorporação multilíngues com reclassificação de busca híbrida.]]></title>
    <description><![CDATA[Aprenda como melhorar a relevância dos resultados de busca do modelo de incorporação multilíngue E5 usando o reranker do Cohere e a busca híbrida no Elasticsearch.]]></description>
    <content:encoded><![CDATA[<h2>Introdução</h2><p>Na <a href="https://www.elastic.co/search-labs/blog/multilingual-embedding-model-deployment-elasticsearch">última parte desta série</a>, mostramos como implantar o modelo pré-treinado E5 da Elastic (bem como outros modelos de incorporação de texto multilíngue da Hugging Face) e exploramos a geração de embeddings vetoriais densos a partir de seus dados de texto usando o Elasticsearch e o Kibana. Neste blog, examinaremos os resultados dessas incorporações e destacaremos as vantagens significativas de se utilizar um modelo multilíngue.</p><p>Agora que temos nosso índice <code>coco_multilingual</code>, realizar a busca nos retornará documentos em vários idiomas, com o campo “en” para referência:</p># GET coco_multilingual/_search
    {
       "_index": "coco_multilingual",
       "_id": "WAiXQJYBgf6odR9bLohZ",
       "_score": 1,
       "_source": {
         "description": "Ein Parkmeßgerät auf einer Straße mit Autos",
         "en": "A row of parked cars sitting next to parking meters.",
         "language": "de",
         "vector_description": {...}
       }
     },
     . . .<h2>Realizar uma pesquisa em inglês</h2><p>Vamos tentar realizar a pesquisa em inglês e ver como funciona:</p>GET coco_multi/_search
{
"size": 10,
"_source": [
  "description", "language", "en"
],
"knn": {
  "field": "vector_description.predicted_value",
  "k": 10,
  "num_candidates": 100,
  "query_vector_builder": {
    "text_embedding": {
      "model_id": ".multilingual-e5-small_linux-x86_64_search",
      "model_text": "query: kitty"
    }
  }
}
}{
       "_index": "coco_multi",
       "_id": "JQiXQJYBgf6odR9b6Yz0",
       "_score": 0.9334303,
       "_source": {
         "description": "Eine Katze, die in einem kleinen, gepackten Koffer sitzt.",
         "en": "A brown and white cat is in a suitcase.",
         "language": "de"
       }
     },
      {
       "_index": "coco_multi",
       "_id": "3AiXQJYBgf6odR9bFod6",
       "_score": 0.9281012,
       "_source": {
         "description": "Una bambina che tiene un gattino vicino a una recinzione blu.",
         "en": "A little girl holding a kitten next to a blue fence.",
         "language": "it"
       }
     },
     . . .<p>Aqui, embora a consulta pareça enganosamente simples, estamos buscando, nos bastidores, as representações numéricas da palavra 'kitty' em todos os documentos e em todos os idiomas. E como estamos realizando uma busca vetorial, podemos pesquisar semanticamente todas as palavras que possam estar relacionadas a 'gatinho': “gato”, “gatinho”, “felino”, “gatto” (italiano), “mèo” (vietnamita), 고양이 (coreano), 猫 (chinês), etc. Consequentemente, mesmo que minha consulta seja em inglês, podemos pesquisar conteúdo em todos os outros idiomas também. Por exemplo, pesquisar por um gatinho l<code>ying on something</code> também retorna documentos em italiano, holandês ou vietnamita. Que eficiência!</p><h2>Realizar uma busca por conteúdo em outros idiomas.</h2>GET coco_multi/_search
{  
 "size": 100,
 "_source": [
   "description", "language", "en"
 ],
 "knn": {
   "field": "vector_description.predicted_value",
   "k": 50,
   "num_candidates": 1000,
   "query_vector_builder": {
     "text_embedding": {
       "model_id": ".multilingual-e5-small_linux-x86_64_search",
       "model_text": "query: kitty lying on something"
     }
   }
 }
}{
 "description": "A black kitten lays on her side beside remote controls.",
 "en": "A black kitten lays on her side beside remote controls.",
 "language": "en"
},
{
 "description": "un gattino sdraiato su un letto accanto ad alcuni telefoni ",
 "en": "A black kitten lays on her side beside remote controls.",
 "language": "it"
},
{
 "description": "eine Katze legt sich auf ein ausgestopftes Tier",
 "en": "a cat lays down on a stuffed animal",
 "language": "de"
},
{
 "description": "Một chú mèo con màu đen nằm nghiêng bên cạnh điều khiển từ xa.",
 "en": "A black kitten lays on her side beside remote controls.",
 "language": "vi"
}
. . .<p>Da mesma forma, realizar uma busca pela palavra-chave “gato” em coreano (“고양이”) também retornará resultados relevantes. O mais impressionante é que não temos nenhum documento em coreano neste índice!</p>GET coco_multi/_search
{
 "size": 100,
 "_source": [
   "description", "language", "en"
 ],
 "knn": {
   "field": "vector_description.predicted_value",
   "k": 50,
   "num_candidates": 1000,
   "query_vector_builder": {
     "text_embedding": {
       "model_id": ".multilingual-e5-small_linux-x86_64_search",
       "model_text": "query: 고양이"
     }
   }
 }
} {
       {
         "description": "eine Katze legt sich auf ein ausgestopftes Tier",
         "en": "a cat lays down on a stuffed animal",
         "language": "de"
       }
     },
     {
       {
         "description": "Một con chó và con mèo đang ngủ với nhau trên một chiếc ghế dài màu cam.",
         "en": "A dog and cat lying  together on an orange couch. ",
         "language": "vi"
       }
     },<p>Isso funciona porque o modelo de incorporação representa o significado em um espaço semântico compartilhado, permitindo a recuperação de imagens relevantes mesmo com uma consulta em um idioma diferente do das legendas indexadas.</p><h2>Aumentando os resultados de pesquisa relevantes com pesquisa híbrida e reclassificação.</h2><p>Estamos satisfeitos por os resultados relevantes terem surgido conforme o esperado. Mas, no mundo real, digamos, no comércio eletrônico ou em aplicações RAG que precisam filtrar os resultados para os 5 a 10 mais relevantes, podemos usar um modelo de reclassificação para priorizar os resultados mais pertinentes.</p><p>Nesse caso, uma busca em vietnamita, como "qual a cor do gato?", retornará muitos resultados, mas os dois primeiros podem não ser os mais relevantes.</p>GET coco_multi/_search
{
 "size": 20,
 "_source": [
   "description",
   "language",
   "en"
 ],
 "knn": {
   "field": "vector_description.predicted_value",
   "k": 20,
   "num_candidates": 1000,
   "query_vector_builder": {
     "text_embedding": {
       "model_id": ".multilingual-e5-small_linux-x86_64_search",
       "model_text": "query: con mèo màu gì?"
     }
   }
 }
}<p>Todos os resultados mencionam gato ou alguma forma de cor:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt979f5944b1708042/6a17ef76420229ff6829f6aa/33e1e887dbbdd1066cfedc7375f5e3b46538529e-859x847.png" alt="" /><p>Então vamos melhorar isso! Vamos integrar o modelo de reclassificação multilíngue do <a href="https://cohere.com/blog/rerank-3pt5">Cohere</a>para melhorar o raciocínio correspondente à nossa pergunta.</p>PUT _inference/rerank/cohere_rerank
{
 "service": "cohere",
 "service_settings": {
   "api_key": "your_api_key",
   "model_id": "rerank-v3.5"
 },
 "task_settings": {
   "top_n": 10,
   "return_documents": true
 }
}


GET coco_multi/_search
{
"size": 10,
"_source": [
  "description",
  "language",
  "en"
],
"retriever": {
  "text_similarity_reranker": {
    "retriever": {
      "rrf": {
        "retrievers": [
          {
            "knn": {
              "field": "vector_description.predicted_value",
              "k": 50,
              "num_candidates": 100,
              "query_vector_builder": {
                "text_embedding": {
                  "model_id": ".multilingual-e5-small_linux-x86_64_search",
                  "model_text": "query: con mèo màu gì?" // English: What color is the cat?
                }
              }
            }
          }
        ],
        "rank_window_size": 100,
        "rank_constant": 0
      }
    },
    "field": "description",
    "inference_id": "cohere_rerank",
    "inference_text": "con mèo màu gì?"
  }
}
} {
       "_index": "coco_multi",
       "_id": "rQiYQJYBgf6odR9bBYyH",
       "_score": 1.5501487,
       "_source": {
         "description": "Hai cái điện thoại được đặt trên một cái chăn cạnh một con mèo con màu đen.",
         "en": "A black kitten lays on her side beside remote controls.",
         "language": "vi"
       }
     },
     {
       "_index": "coco_multi",
       "_id": "swiXQJYBgf6odR9b04uf",
       "_score": 1.5427427,
       "_source": {
         "description": "Một con mèo sọc nâu nhìn vào máy quay.", // Real translation: A brown striped cat looks at the camera 
         "en": "This cat is sitting on a porch near a tire.",
         "language": "vi"
       }
     },<p>Agora, com os melhores resultados, nosso aplicativo pode afirmar com segurança que a cor do gatinho é preta ou marrom com listras. O que é ainda mais interessante é que nossa busca vetorial detectou uma omissão na legenda em inglês do conjunto de dados original. O programa consegue encontrar o gato marrom listrado, mesmo que a tradução de referência em inglês tenha omitido esse detalhe. Este é o poder da busca vetorial.</p><h2>Conclusão</h2><p>Neste blog, exploramos a utilidade de um modelo de incorporação multilíngue e como aproveitar o Elasticsearch para integrar os modelos, gerar incorporações e melhorar efetivamente a relevância e a precisão com uma busca e reclassificação híbrida. Você pode <a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;cta=cloud-registration&amp;tech=trial&amp;plcmt=article%20content&amp;pg=search-labs">criar seu próprio cluster na nuvem</a> para experimentar <a href="https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-e5">a busca semântica multilíngue usando nosso modelo E5 pronto para uso,</a> no idioma e conjunto de dados de sua escolha.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/multilingual-embedding-model-hybrid-search-reranking</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/multilingual-embedding-model-hybrid-search-reranking</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <category><![CDATA[Operações]]></category>
    <dc:creator><![CDATA[Quynh Nguyen]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf625e3f63fcd9f54/6a17ef7796142a61f8eb1bcd/d341b04acecc8eeec321f5404e1643447ecc8526-720x420.png" length="0" type="image/png"/>
    <pubDate>Mon, 03 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Implantação de um modelo de incorporação multilíngue no Elasticsearch]]></title>
    <description><![CDATA[Aprenda como implantar um modelo de incorporação multilíngue e5 para busca vetorial e recuperação multilíngue no Elasticsearch.]]></description>
    <content:encoded><![CDATA[<h2>Introdução</h2><p>Em um mundo de usuários globais, a recuperação de informações multilíngue (CLIR) é crucial. Em vez de limitar as buscas a um único idioma, o CLIR permite encontrar informações em <em>qualquer</em> idioma, aprimorando a experiência do usuário e simplificando as operações. Imagine um mercado global onde os clientes de comércio eletrônico possam pesquisar itens em seu idioma e os resultados corretos apareçam, sem a necessidade de localizar os dados antecipadamente. Ou seja, onde pesquisadores acadêmicos podem buscar artigos em seu idioma nativo, com nuances e complexidade, mesmo que a fonte esteja em outro idioma.</p><p>Os modelos de incorporação de texto multilíngue nos permitem fazer exatamente isso. Os embeddings são uma forma de representar o significado do texto como vetores numéricos. Esses vetores são projetados de forma que textos com significados semelhantes fiquem localizados próximos uns dos outros em um espaço de alta dimensão. Os modelos de incorporação de texto multilíngue são projetados especificamente para mapear palavras e frases com o mesmo significado em diferentes idiomas para um espaço vetorial semelhante.</p><p>Modelos como o Multilingual E5, de código aberto, são treinados com grandes quantidades de dados textuais, frequentemente utilizando técnicas como o aprendizado contrastivo. Nessa abordagem, o modelo aprende a distinguir entre pares de textos com significados semelhantes (pares positivos) e aqueles com significados diferentes (pares negativos). O modelo é treinado para ajustar os vetores que produz de forma a maximizar a similaridade entre pares positivos e minimizar a similaridade entre pares negativos. Para modelos multilíngues, esses dados de treinamento incluem pares de textos em diferentes idiomas que são traduções uns dos outros, permitindo que o modelo aprenda um espaço de representação compartilhado para vários idiomas. Os embeddings resultantes podem então ser usados para diversas tarefas de PNL (Processamento de Linguagem Natural), incluindo buscas multilíngues, onde a similaridade entre os embeddings de texto é usada para encontrar documentos relevantes independentemente do idioma da consulta.</p><h2>Benefícios da busca vetorial multilíngue</h2><ul><li><p><strong>Nuance</strong>: A busca vetorial se destaca na captura do significado semântico, indo além da correspondência por palavras-chave. Isso é crucial para tarefas que exigem a compreensão do contexto e das sutilezas da linguagem.</p></li><li><p><strong>Compreensão Interlinguística</strong>: Permite a recuperação eficaz de informações em diferentes idiomas, mesmo quando a consulta e os documentos utilizam vocabulário diferente.</p></li><li><p><strong>Relevância</strong>: Oferece resultados mais relevantes ao focar na similaridade conceitual entre consultas e documentos.</p></li></ul><p>Por exemplo, considere um pesquisador acadêmico que estuda o "impacto das mídias sociais no discurso político" em diferentes países. Com a pesquisa vetorial, eles podem inserir consultas como "l'impatto dei social media sul discorso politico" (italiano) ou "ảnh hưởng của mạng xã hội đối với diễn ngôn chính trị" (vietnamita) e encontrar artigos relevantes em inglês, espanhol ou qualquer outro idioma indexado. Isso ocorre porque a busca vetorial identifica artigos que discutem o <em>conceito</em> da influência das mídias sociais na política, e não apenas aqueles que contêm as palavras-chave exatas. Isso amplia consideravelmente o alcance e a profundidade de suas pesquisas.</p><h2>Introdução</h2><p>Veja como configurar o CLIR usando o Elasticsearch - com o modelo E5 que já vem instalado por padrão. Usaremos o <a href="https://huggingface.co/datasets/romrawinjp/multilingual-coco">conjunto de dados multilíngue de código aberto COCO</a>, que contém legendas de imagens em vários idiomas, para nos ajudar a visualizar 2 tipos de pesquisas:</p><ol><li><p>Consultas e termos de pesquisa em outros idiomas em um conjunto de dados em inglês, e</p></li><li><p>Consultas em vários idiomas sobre um conjunto de dados que contém documentos em vários idiomas.</p></li></ol><p>Em seguida, aproveitaremos o poder da busca híbrida e do reclassificação para melhorar ainda mais os resultados da pesquisa.</p><h2>Pré-requisitos</h2><ul><li><p>Python 3.6+</p></li><li><p>Elasticsearch 8+</p></li><li><p>Cliente Python do Elasticsearch: pip install elasticsearch</p></li></ul><h2>Conjunto de dados</h2><p>O <a href="https://huggingface.co/datasets/romrawinjp/multilingual-coco">conjunto de dados COCO</a> é um conjunto de dados de legendagem em larga escala. Cada imagem no conjunto de dados possui legenda em vários idiomas diferentes, com diversas traduções disponíveis para cada idioma. Para fins de demonstração, indexaremos cada tradução como um documento individual, juntamente com a primeira tradução em inglês disponível para referência.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfc7e508e9a7dffe8/6a17f3e2b1e113249579f394/d4f0632529c71a22fbdecf21c9f4f0bb64b8e69c-1600x567.png" alt="" /><h3>Passo 1: faça o download do conjunto de dados COCO multilíngue.</h3><p>Para simplificar o blog e facilitar o acompanhamento, estamos carregando as primeiras 100 linhas do restval em um arquivo JSON local com uma simples chamada de API. Alternativamente, você pode usar os conjuntos de dados da biblioteca do HuggingFace para carregar o conjunto de dados completo ou subconjuntos dele.</p>import requests
import json
import os
### Download multilingual coco dataset into a json file (for easy viewing)
### Here we are retrieving first 100 rows for this example
### Alternatively, you can use `datasets` library from Hugging Face
url = "https://datasets-server.huggingface.co/rows?dataset=romrawinjp%2Fmultilingual-coco&amp;config=default&amp;split=restval&amp;offset=0&amp;length=100"
response = requests.get(url)


if response.status_code == 200:
   data = response.json()
   output_file = "multilingual_coco_sample.json" 
   ### Loading the downloaded content into a json file locally
   with open(output_file, "w", encoding="utf-8") as f:
       json.dump(data, f, indent=4, ensure_ascii=False)
   print(f"Data successfully downloaded and saved to {output_file}")
else:
   print(f"Failed to download data: {response.status_code}")
   print(response.text)<p>Se os dados forem carregados com sucesso em um arquivo JSON, você deverá ver algo semelhante a isto:</p><p><code>Data successfully downloaded and saved to multilingual_coco_sample.json</code></p><h3>Etapa 2: (Inicie o Elasticsearch) e indexe os dados no Elasticsearch.</h3><p>a) Inicie o servidor Elasticsearch local.</p><p>b) Inicie o cliente Elasticsearch.</p>from elasticsearch import Elasticsearch
from getpass import getpass


# Initialize Elasticsearch client
es = Elasticsearch(getpass("Host: "), api_key=getpass("API Key: "))


index_name = "coco"


# Create the index if it doesn't exist
if not es.indices.exists(index=index_name):
   es.indices.create(index=index_name, body=mapping)<p>c) Dados de índice</p># Load the JSON data
with open('./multilingual_coco_sample.json', 'r') as f:
   data = json.load(f)


rows = data["rows"]
# List of languages to process
languages = ["en", "es", "de", "it", "vi", "th"]


# For each image, we will process each individual caption as its own document
bulk_data = []
for data in rows:
   row = data["row"]
   image = row.get("image")
   image_url = image["src"]


   # Process each language
   for lang in languages:
       # Skip if language not present in this row
       if lang not in row:
           continue


       # Get all descriptions for this language
 # along with first available English caption for reference
       descriptions = row[lang]
       first_eng_caption = row["en"][0]


       # Prepare bulk indexing data
       for description in descriptions:
           if description == "":
               continue
           # Add index operation
           bulk_data.append(
               {"index": {"_index": index_name}}
           )
           # Add document
           bulk_data.append({
               "language": lang,
               "description": description,
               "en": first_eng_caption,
               "image_url": image_url,
           })


# Perform bulk indexing
if bulk_data:
   try:
       response = es.bulk(operations=bulk_data)
       if response["errors"]:
           print("Some documents failed to index")
       else:
           print(f"Successfully bulk indexed {len(bulk_data)} documents")
   except Exception as e:
       print(f"Error during bulk indexing: {str(e)}")


print("Indexing complete!")<p>Após a indexação dos dados, você deverá ver algo semelhante a isto:</p><p><code>Successfully bulk indexed 4840 documents</code></p><p><code>Indexing complete!</code></p><h3>Etapa 3: Implantar o modelo treinado E5</h3><p>No Kibana, navegue até a página Gerenciamento de Pilha &gt; <strong>Modelos Treinados</strong> e clique em <strong>Implantar</strong> para o modelo .multilingual-e5-small_linux-x86_64. opção. Este modelo E5 é um sistema operacional multilíngue compacto, otimizado para Linux x86_64, que podemos usar imediatamente. Ao clicar em "Implantar", será exibida uma tela onde você poderá ajustar as configurações de implantação ou as configurações de vCPUs. Para simplificar, vamos usar as opções padrão, com recursos adaptáveis selecionados, que dimensionarão automaticamente nossa implantação dependendo do uso.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfbc09867063a8e6f/6a17f3e3148009a295b4889d/95cd8f352425d1db2d04b00c3c88d1e71d1ef19a-1600x440.png" alt="" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt264a2016341e9b6f/6a17f3f0e8fbce18de3a1aa7/1599d99949dda8267acc58f400a403a3af5373ef-1600x655.png" alt="" /><p>Opcionalmente, se desejar, você pode usar outros modelos de incorporação de texto. Por exemplo, para usar o BGE-M3, você pode usar <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/eland/machine-learning#ml-nlp-pytorch">o cliente Python Eland da Elastic</a> para importar o modelo do HuggingFace.</p>export MODEL_ID="bge-m3"
export HUB_MODEL_ID="BAAI/bge-m3"
export CLOUD_ID={{CLOUD_ID}}
export ES_API_KEY={{API_KEY}}
docker run -it --rm docker.elastic.co/eland/eland \
eland_import_hub_model --cloud-id $CLOUD_ID --es-api-key $ES_API_KEY --hub-model-id $HUB_MODEL_ID --es-model-id $MODEL_ID --task-type text_embedding --start<p>Em seguida, acesse a página Modelos Treinados para implantar o modelo importado com as configurações desejadas.</p><h3>Etapa 4: Vetorizar ou criar embeddings para os dados originais com o modelo implantado</h3><p>Para criar os embeddings, primeiro precisamos criar um pipeline de ingestão que nos permita pegar o texto e executá-lo através do modelo de inferência de embeddings de texto. Você pode fazer isso na interface do usuário do Kibana ou através da API do Elasticsearch.</p><p><strong>Para fazer isso através da interface do Kibana</strong>, após implantar o modelo treinado, clique no botão <strong>Testar </strong> . Isso lhe dará a possibilidade de testar e visualizar as imagens incorporadas geradas. Crie uma nova visualização de dados para o <code>coco</code>index, defina a visualização de dados para a visualização de dados coco recém-criada e defina o campo para <code>description</code> porque esse é o campo para o qual queremos gerar embeddings.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt24a2b9a9a5111bbc/6a17f3f13e9e452c7fba15d5/cfe189e13dc118d325e7fb90bdace0c912e29f51-1088x1600.png" alt="" /><p>Isso funciona perfeitamente! Agora podemos prosseguir com a criação do pipeline de ingestão e reindexar nossos documentos originais, passá-los pelo pipeline e criar um novo índice com os embeddings. Você pode fazer isso clicando em <strong>Criar pipeline</strong>, que o guiará pelo processo de criação do pipeline, com processadores preenchidos automaticamente, necessários para ajudá-lo a criar os embeddings.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte39c5ad52702103d/6a17f3f3e9ea87dd05a9c734/1e043c1c3279b66fbdf19c06b41e76e613043998-1600x1126.png" alt="" /><p>O assistente também pode preencher automaticamente os processadores necessários para lidar com falhas durante a ingestão e o processamento dos dados.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt63e002a79d177cf4/6a17f3f596142a3e91eb1c48/8804d31b4f869078e3b2245040bbb0ab1720a94a-1600x1084.png" alt="" /><p>Vamos agora criar o pipeline de ingestão. Estou nomeando o pipeline <code>coco_e5</code>. Após a criação bem-sucedida do pipeline, você pode usá-lo imediatamente para gerar os embeddings, reindexando os dados indexados originais para um novo índice no assistente. Clique em <strong>Reindexar </strong>para iniciar o processo.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt39243d9ad1779fdf/6a17f3f696142a13eaeb1c4c/e34b1b18f5b24420d4581fe4d657c569926c2023-1600x1126.png" alt="" /><h2>Para configurações mais complexas, podemos usar a API do Elasticsearch.</h2><p>Para alguns modelos, devido à forma como foram treinados, pode ser necessário adicionar ou acrescentar certos textos à entrada real antes de gerar os embeddings; caso contrário, observaremos uma degradação no desempenho.</p><p>Por exemplo, com o e5, o modelo espera que o texto de entrada siga “passagem: {content of passage}”. Vamos utilizar os pipelines de ingestão para realizar isso: Criaremos um novo pipeline de ingestão <strong>chamado vectorize_descriptions</strong>. Neste pipeline, criaremos um novo campo temporário <code>temp_desc</code> , adicionaremos “passagem: “ ao texto <code>description</code> , executaremos <code>temp_desc</code> através do modelo para gerar embeddings de texto e, em seguida, excluiremos o <code>temp_desc</code>.</p>PUT _ingest/pipeline/vectorize_descriptions
{
"description": "Pipeline to run the descriptions text_field through our inference text embedding model",
"processors": [
 {
   "set": {
     "field": "temp_desc",
     "value": "passage: {{description}}"
   }
 },
 {
   "inference": {     
"field_map": {
       "temp_desc": "text_field"
     },
     "model_id": ".multilingual-e5-small_linux-x86_64_search",
     "target_field": "vector_description"
   }
 },
 {
   "remove": {
     "field": "temp_desc"
   }
 }
]
}<p>Além disso, podemos querer especificar qual <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/dense-vector#dense-vector-quantization">tipo de quantização</a> desejamos usar para o vetor gerado. Por padrão, o Elasticsearch usa <code>int8_hnsw</code>, mas aqui eu quero <a href="https://www.elastic.co/search-labs/blog/better-binary-quantization-lucene-elasticsearch">Better Binary Quantization</a> (ou <code>bqq_hnsw</code>), que reduz cada dimensão a uma precisão de um único bit. Isso reduz a necessidade de memória em 96% (ou 32 vezes), ao custo de uma maior perda de precisão. Estou optando por esse tipo de quantização porque sei que usarei um reclassificador posteriormente para melhorar a perda de precisão.</p><p>Para isso, criaremos um novo índice chamado <strong>coco_multi</strong> e especificaremos os mapeamentos. A mágica está no campo vector_description, onde especificamos o tipo de <strong>index_options</strong>como <strong>bbq_hnsw</strong>.</p>PUT coco_multi
{
 "mappings": {
   "properties": {
     "description": {
       "type": "text"
     },
     "en": {
       "type": "text"
     },
     "image_url": {
       "type": "keyword"
     },
     "language": {
       "type": "keyword"
     },
     "vector_description.predicted_value": {
       "type": "dense_vector",
       "dims": 384,
       "index": "true",
       "similarity": "cosine",
       "index_options": {
         "type": "bbq_hnsw" 
       }
     }
   }
 }
}<p>Agora, podemos reindexar os documentos originais para um novo índice, com nosso pipeline de ingestão que irá "vetorizar" ou criar embeddings para o campo de descrição.</p>POST _reindex?wait_for_completion=false
{
 "source": {
   "index": "coco"
 },
 "dest": {
   "index": "coco_multilingual",
   "pipeline": "vectorize_descriptions"
 }
}<p>E é isso! Implementamos com sucesso um modelo multilíngue com Elasticsearch e Kibana e aprendemos passo a passo como criar representações vetoriais (embeddings) com seus dados usando o Elastic, seja pela interface do usuário do Kibana ou pela API do Elasticsearch. Na segunda parte desta série, exploraremos os resultados e as nuances da utilização de um modelo multilíngue. Enquanto isso, você pode <a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;cta=cloud-registration&amp;tech=trial&amp;plcmt=article%20content&amp;pg=search-labs">criar seu próprio cluster na nuvem</a> para experimentar <a href="https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-e5">a busca semântica multilíngue usando nosso modelo E5 pronto para uso</a> no idioma e conjunto de dados de sua escolha.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/multilingual-embedding-model-deployment-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/multilingual-embedding-model-deployment-elasticsearch</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <category><![CDATA[Operações]]></category>
    <dc:creator><![CDATA[Quynh Nguyen]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt59254226694f93a6/6a17f3f81480098988b488a1/8f2aa7bebb6b2f701e274ba7282273f9ab4abed6-720x432.png" length="0" type="image/png"/>
    <pubDate>Wed, 22 Oct 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Filtragem de pesquisa vetorial: Mantenha a relevância]]></title>
    <description><![CDATA[Realizar uma busca vetorial para encontrar os resultados mais semelhantes a uma consulta não é suficiente. Muitas vezes, é necessário usar filtros para refinar os resultados da pesquisa. Este artigo explica como funciona a filtragem para busca vetorial no Elasticsearch e no Apache Lucene.]]></description>
    <content:encoded><![CDATA[<p>A busca vetorial não é suficiente para encontrar resultados relevantes. É muito comum usar critérios de filtragem que ajudam a restringir os resultados da pesquisa e a eliminar os resultados irrelevantes.</p><p>Compreender como a filtragem funciona na busca vetorial ajudará você a equilibrar as vantagens e desvantagens em termos de desempenho e recall, além de descobrir algumas das otimizações usadas para tornar a busca vetorial eficiente quando a filtragem é utilizada.</p><h2>Por que filtrar?</h2><p>A busca vetorial revolucionou a forma como encontramos informações relevantes em grandes conjuntos de dados, permitindo-nos descobrir itens semanticamente semelhantes a uma consulta.</p><p>No entanto, simplesmente encontrar itens semelhantes não é suficiente. Frequentemente precisamos refinar os resultados da pesquisa com base em critérios ou atributos específicos.</p><p>Imagine que você está procurando um produto em uma loja de comércio eletrônico. Uma busca puramente vetorial pode mostrar itens visualmente semelhantes, mas você também pode querer filtrar por faixa de preço, marca, disponibilidade ou avaliações de clientes. Sem filtros, você se depararia com uma vasta gama de produtos similares, dificultando a busca exatamente pelo que procura.</p><p>A filtragem permite um controle preciso sobre os resultados da pesquisa, garantindo que os itens recuperados não apenas estejam alinhados semanticamente, mas também atendam a todos os requisitos necessários. Isso resulta em uma experiência de busca muito mais precisa, eficiente e fácil de usar.</p><p>É aqui que o Elasticsearch e o Apache Lucene se destacam — o uso de filtragem eficaz em vários tipos de dados é uma das principais diferenças em relação a outros bancos de dados vetoriais.</p><h2>Filtrar para pesquisa vetorial exata</h2><p>Existem duas maneiras principais de realizar buscas vetoriais exatas:</p><ul><li><p>Utilizando um tipo de índice <code>flat</code> para o seu campo dense_vector. Isso faz com que as buscas <code>knn</code> usem busca exata em vez de aproximada.</p></li><li><p>Utilizando uma <a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-script-score-query#vector-functions">consulta script_score</a> que usa funções vetoriais para calcular a pontuação. Isso pode ser usado com qualquer tipo de índice.</p></li></ul><p>Ao executar uma busca vetorial exata, todos os vetores são comparados à consulta. Nesse cenário, a filtragem ajudará no desempenho, pois somente os vetores que passarem pelo filtro precisarão ser comparados.</p><p>Isso não afeta a qualidade do resultado, pois todos os vetores são considerados de qualquer forma. Estamos apenas filtrando antecipadamente os resultados que não são interessantes, para que possamos reduzir o número de operações.</p><p>Isso é muito importante, pois pode ser mais eficiente executar uma pesquisa exata em vez de uma pesquisa aproximada quando os filtros aplicados resultam em um pequeno número de documentos.</p><p>A regra geral é usar a pesquisa exata quando menos de 10 mil documentos passarem pelo filtro. Os índices <a href="https://www.elastic.co/search-labs/blog/better-binary-quantization-lucene-elasticsearch">BBQ</a> são muito mais rápidos para comparações, portanto, faz sentido usar a pesquisa exata quando o número de índices baseados for inferior a 100 mil. Confira <a href="https://www.elastic.co/search-labs/blog/knn-exact-vs-approximate-search">esta postagem no blog</a> para obter mais detalhes.</p><p>Caso seus filtros sejam sempre muito restritivos, você pode considerar indexar focando na busca exata em vez da busca aproximada usando um tipo de índice <code>flat</code> em vez de um baseado em HNSW. Para obter mais detalhes, consulte <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/dense-vector#dense-vector-params">as propriedades de index_options</a>.</p><h2>Filtragem para busca vetorial aproximada</h2><p>Ao executar uma busca vetorial aproximada, trocamos precisão do resultado por desempenho. Estruturas de dados de busca vetorial, como o HNSW, pesquisam de forma eficiente os vizinhos mais próximos aproximados em milhões de vetores. Eles se concentram em recuperar os vetores mais semelhantes realizando o mínimo de comparações vetoriais possível, que são computacionalmente dispendiosas.</p><p>Isso significa que outros atributos de filtragem não fazem parte dos dados vetoriais. Diferentes tipos de dados possuem suas próprias estruturas de indexação, que são eficientes para encontrá-los e filtrá-los, como dicionários de termos, listas de postagens e valores de documentos.</p><p>Dado que essas estruturas de dados são separadas do mecanismo de busca vetorial, como aplicamos a filtragem à busca vetorial? Existem duas opções: aplicar filtros após a busca vetorial (pós-filtragem) ou antes da busca vetorial (pré-filtragem).</p><p>Cada uma dessas opções tem vantagens e desvantagens. Vamos analisar esses assuntos mais a fundo!</p><h3>Pós-filtragem</h3><p>A pós-filtragem aplica filtros após a pesquisa vetorial ter sido realizada. Isso significa que os filtros são aplicados depois que os k resultados vetoriais mais semelhantes forem encontrados.</p><p>Obviamente, podemos obter menos de k resultados após aplicar os filtros aos resultados. É claro que poderíamos obter mais resultados com a busca vetorial (valor k maior), mas não teríamos certeza de que obteríamos k ou mais resultados após a aplicação dos filtros.</p><p>A vantagem da pós-filtragem é que ela não altera o comportamento em tempo de execução da busca vetorial — a busca vetorial não leva em consideração a filtragem. Mas isso altera o número final de resultados obtidos.</p><p>Segue abaixo um exemplo de pós-filtragem usando a <a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-knn-query">consulta knn</a>. Verifique se a cláusula de filtragem está separada da consulta KNN:</p>{
  "query": {
    "bool": {
      "must": {
        "knn": {
          "field": "image-vector",
          "query_vector": [54, 10, -2],
          "k": 5,
          "num_candidates": 50
        }
      },
      "filter": {
        "term": {
          "file-type": "png"
        }
      }
    }
  }
}<p>O pós-filtro também está disponível para a pesquisa knn usando <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/filter-search-results#post-filter">o comando post-filter</a>:</p>{
  "knn": {
    "field": "image-vector",
    "query_vector": [54, 10, 2],
    "k": 5,
    "num_candidates": 50
  },
  "post_filter": {
    "term": {
      "file-type": "png"
    }
  }
}<p>Lembre-se de que é necessário usar uma seção de pós-filtragem explícita na pesquisa KNN. Se você não usar um filtro posterior, a pesquisa KNN <a href="https://www.elastic.co/docs/solutions/search/vector/knn#_combine_approximate_knn_with_other_features">combinará os resultados dos vizinhos mais próximos</a> com outras consultas ou filtros, em vez de aplicar um filtro posterior.</p><h3>Pré-filtragem</h3><p>Aplicar filtros antes da busca vetorial primeiro recuperará os documentos que atendem aos filtros e, em seguida, passará essa informação para a busca vetorial.</p><p>O Lucene utiliza <a href="https://github.com/apache/lucene/blob/7a60d7ce92392181e137361336e5196bd486cdd9/lucene/core/src/java/org/apache/lucene/util/BitSet.java">BitSets</a> para armazenar de forma eficiente os documentos que satisfazem a condição do filtro. A busca vetorial percorre então o grafo HNSW, levando em consideração os documentos que satisfazem a condição. Antes de adicionar um candidato aos resultados, o sistema verifica se ele está contido no BitSet de documentos válidos.</p><p>No entanto, o candidato deve ser analisado e comparado à consulta, mesmo que não seja um documento válido. A eficácia do HNSW depende da conexão entre os vetores no grafo — se parássemos de explorar um candidato, isso significaria que também poderíamos estar ignorando seus vizinhos.</p><p>Imagine que você está dirigindo até um posto de gasolina. Se você descartar todas as estradas que não têm um posto de gasolina, é improvável que você chegue ao seu destino. Outras estradas podem não ser o que você precisa, mas elas te <em>levam</em> ao seu destino. O mesmo se aplica aos vetores em um grafo HNSW!</p><p>Conclui-se, portanto, que aplicar pré-filtragem tem um desempenho inferior a não aplicar filtros. Precisamos processar <em>todos</em> os vetores que visitamos em nossa busca e descartar aqueles que não correspondem ao filtro. Estamos trabalhando mais e dedicando mais tempo para obter nossos melhores resultados.</p><p>Segue abaixo um exemplo de pré-filtragem na DSL de consulta do Elasticsearch. Verifique se a cláusula de filtragem agora faz parte da seção knn:</p>{
  "knn": {
    "field": "image-vector",
    "query_vector": [54, 10, -2],
    "k": 5,
    "num_candidates": 50,
    "filter": {
      "term": {
        "file-type": "png"
      }
    }
  }
}<p>O pré-filtro está disponível tanto para <a href="https://www.elastic.co/docs/solutions/search/vector/knn#knn-search-filter-example">a pesquisa KNN</a> quanto para <a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-knn-query#knn-query-filtering">a consulta KNN</a>:</p>{
  "query": {
    "knn": {
      "field": "image-vector",
      "query_vector": [-5, 9, -12],
      "k": 5,
      "filter": {
        "term": {
          "file-type": "png"
        }
      }
    }
  }
}<h4>Otimizações de pré-filtragem</h4><p>Existem algumas otimizações que podemos aplicar para garantir que a pré-filtragem seja eficiente.</p><p>Podemos alternar para a pesquisa exata se o filtro for muito restritivo. Quando há poucos vetores para comparar, é mais rápido realizar uma busca exata nos poucos documentos que satisfazem o filtro.</p><p>Essa é uma otimização que é aplicada automaticamente no <a href="https://github.com/apache/lucene/blob/eb876b618da5d04c1ad14b04a48321638318493a/lucene/core/src/java/org/apache/lucene/search/AbstractKnnVectorQuery.java#L218">Lucene</a> e no Elasticsearch.</p><p>Outro método de otimização envolve desconsiderar os vetores que não satisfazem o filtro. Em vez disso, esse método verifica os vizinhos dos vetores filtrados que passam pelo filtro. Essa abordagem reduz efetivamente o número de comparações, pois os vetores filtrados não são considerados, e continua a explorar vetores conectados ao caminho atual.</p><p>Este algoritmo é o ACORN-1, e o processo é descrito em detalhes <a href="https://www.elastic.co/search-labs/blog/filtered-hnsw-knn-search">nesta postagem do blog</a>.</p><h2>Filtragem usando segurança em nível de documento</h2><p><a href="https://www.elastic.co/docs/deploy-manage/users-roles/cluster-or-deployment-auth/controlling-access-at-document-field-level#document-level-security">A Segurança em Nível de Documento (DLS, na sigla em inglês)</a> é um recurso do Elasticsearch que especifica os documentos que as funções de usuário podem recuperar.</p><p>A DLS é realizada por meio de consultas. Uma função pode ter uma consulta associada a índices, o que efetivamente limita os documentos que um usuário pertencente a essa função pode recuperar dos índices.</p><p>A consulta de função é usada como um filtro para <a href="https://github.com/elastic/elasticsearch/blob/c3a1cb34294e902a9f46d7e840ea09965019f456/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/accesscontrol/SecurityIndexReaderWrapper.java#L92">recuperar os documentos que correspondem a ela</a> e são armazenados em cache como um BitSet. Esse BitSet é então usado para encapsular o leitor Lucene subjacente, de forma que apenas os documentos retornados pela consulta sejam considerados <em>ativos —</em>ou seja, eles existem no índice e não foram excluídos.</p><p>À medida que os documentos ativos são <a href="https://github.com/apache/lucene/blob/a211d30097a8e3264d3ef073a054bd31eb847231/lucene/core/src/java/org/apache/lucene/search/AbstractKnnVectorQuery.java#L196">recuperados do leitor</a> para executar a consulta KNN, somente os documentos disponíveis para o usuário serão considerados. Caso exista um pré-filtro, os documentos DLS serão <a href="https://github.com/apache/lucene/blob/a211d30097a8e3264d3ef073a054bd31eb847231/lucene/core/src/java/org/apache/lucene/search/AbstractKnnVectorQuery.java#L204">adicionados a ele</a>.</p><p>Isso significa que a filtragem DLS funciona como um pré-filtro para a busca vetorial aproximada, com as mesmas implicações de desempenho e otimizações.</p><p>A busca DLS com pesquisa exata terá os mesmos benefícios que a aplicação de qualquer filtro — quanto menos documentos forem recuperados da DLS, mais eficiente será a pesquisa exata. Considere também o número de documentos retornados pelo DLS — se as funções do DLS forem muito restritivas, você pode considerar usar a pesquisa exata em vez da pesquisa aproximada.</p><h2>Benchmarking</h2><p>Na Elasticsearch, queremos garantir que a filtragem por vetores seja eficiente. Temos <a href="https://elasticsearch-benchmarks.elastic.co/#tracks/so_vector/nightly/default/90d">um benchmark específico para filtragem vetorial</a> que realiza buscas vetoriais aproximadas com diferentes filtros para garantir que a busca vetorial continue recuperando resultados relevantes o mais rápido possível.</p><p>Confira as <a href="https://elasticsearch-benchmark-analytics.elastic.co/app/dashboards#/view/43b63e80-5ba2-11ed-aede-a742809feed4?_g=(refreshInterval:(pause:!t,value:60000),time:(from:'2025-05-28T01:27:58.456Z',to:'2025-06-30T13:53:26.430Z'))&amp;_a=()">melhorias implementadas</a> com a introdução do ACORN-1. Para testes em que apenas 2% dos vetores passam pelo filtro, a latência da consulta é reduzida para 55% da duração original:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt820cb0b715cf291c/6a17e227dbb4ff8d49fb5615/3eac3748a33376fc97d957364a5c1f5108d5c58b-1023x896.png" alt="" /><h2>Conclusão</h2><p>A filtragem é parte integrante da pesquisa. Garantir que a filtragem seja eficiente na busca vetorial e compreender as compensações e otimizações envolvidas é o que determina o sucesso ou o fracasso de uma busca eficiente e precisa.</p><p>A filtragem afeta o desempenho da busca vetorial:</p><ul><li><p>A busca exata é mais rápida ao usar filtros. Se a sua filtragem for suficientemente restritiva, considere usar a pesquisa exata em vez da pesquisa aproximada. Esta é uma otimização automática no Elasticsearch.</p></li><li><p>A busca aproximada é mais lenta quando se utiliza pré-filtragem. A pré-filtragem permite obter os k melhores resultados que correspondem ao filtro, ao custo de uma pesquisa mais lenta.</p></li><li><p>A pós-filtragem não recupera necessariamente os k melhores resultados, pois eles podem ser filtrados pelo filtro quando este é aplicado.</p></li></ul><p>Boa filtragem!</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/vector-search-filtering</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/vector-search-filtering</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <category><![CDATA[Lucene]]></category>
    <category><![CDATA[Na Elastic]]></category>
    <dc:creator><![CDATA[Carlos Delgado]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt39ede1736e0f1456/6a17e2282f4a5c5031fa8843/03b1dd4c7bda4fbabd8e374bc2e4f12d5be6ef5f-1600x1150.png" length="0" type="image/png"/>
    <pubDate>Wed, 03 Sep 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Mapeamento de embeddings para tipos de campo do Elasticsearch: semantic_text, dense_vector, sparse_vector]]></title>
    <description><![CDATA[Discutindo como e quando usar semantic_text, dense_vector ou sparse_vector, e como eles se relacionam com a geração de embeddings.]]></description>
    <content:encoded><![CDATA[<p>O uso de embeddings para melhorar a relevância e a precisão da recuperação de informações cresceu significativamente ao longo dos anos. Ferramentas como o Elasticsearch evoluíram para dar suporte a esse tipo de dados por meio de tipos de campos especializados, como vetores densos, vetores esparsos e texto semântico. No entanto, para obter bons resultados, é essencial entender como mapear corretamente os embeddings para os tipos de campo disponíveis no Elasticsearch: <code>semantic_text</code>, <code>dense_vector</code> e <code>sparse_vector</code>.</p><p>Neste artigo, discutiremos esses tipos de campos, quando usar cada um deles e como eles se relacionam com as estratégias de geração e uso de embeddings, tanto durante a indexação quanto na consulta.</p><h2>Tipo de vetor denso</h2><p>O tipo de campo <code>dense_vector</code> no Elasticsearch é usado para armazenar vetores densos, que são representações numéricas de dados como texto, imagens e áudio, onde quase todas as dimensões são relevantes. Esses vetores são gerados usando modelos de incorporação fornecidos por plataformas como OpenAI, Cohere ou Hugging Face, e são projetados para capturar o significado semântico geral dos dados, mesmo quando eles não compartilham termos exatos com outros documentos.</p><p>No Elasticsearch, vetores densos podem ter até 4096 dimensões, dependendo do modelo utilizado. Por exemplo, o modelo all-MiniLM-L6-v2 gera vetores com 384 dimensões, enquanto o text-embedding-ada-002 da OpenAI produz vetores com 1536 dimensões.</p><p>O campo <code>dense_vector</code> é geralmente adotado como o tipo padrão para armazenar esse tipo de incorporação quando é necessário maior controle, como usar vetores pré-gerados, aplicar funções de similaridade personalizadas ou integrar com modelos externos.</p><h3>Quando e por que usar o tipo dense_vector?</h3><p>Vetores densos são excelentes para capturar a similaridade semântica entre frases, parágrafos ou documentos inteiros. Funcionam muito bem quando o objetivo é comparar o significado geral dos textos, mesmo que não compartilhem os mesmos termos.</p><p>O campo vetorial denso é ideal quando você já possui um pipeline externo de geração de embeddings usando modelos fornecidos por plataformas como OpenAI, Cohere ou Hugging Face e deseja apenas armazenar e consultar esses vetores manualmente. Este tipo de campo oferece alta compatibilidade com modelos de incorporação e total flexibilidade na geração e consulta, permitindo controlar como os vetores são produzidos, indexados e usados durante a busca.</p><p>Além disso, oferece suporte a diferentes formas de busca semântica, com consultas como k-NN ou script_score para casos em que seja necessário ajustar a lógica de classificação. Essas possibilidades tornam o vetor denso ideal para aplicações como RAG (Retrieval-Augmented Generation), sistemas de recomendação e buscas personalizadas baseadas em similaridade.</p><p>Finalmente, o campo permite personalizar a lógica de relevância, usando funções como <code>cosineSimilarity</code>, <code>dotProduct</code> ou <code>l2norm</code> para adaptar a classificação de acordo com as necessidades do seu caso de uso. </p><p>Vetores densos continuam sendo a melhor opção para quem precisa de flexibilidade, personalização e compatibilidade com casos de uso avançados, como os mencionados acima.</p><h3>Como usar a consulta para o tipo vetor denso?</h3><p>As pesquisas em campos definidos como <strong><code>dense_vector</code></strong> usam a consulta dos k vizinhos mais próximos. Esta consulta é responsável por encontrar documentos cujo vetor denso seja o mais próximo do vetor de consulta. Abaixo, segue um exemplo de como aplicar uma consulta k-NN a um campo vetorial denso:</p>{
  "knn": {
    "field": "my_dense_vector",
    "k": 10,
    "num_candidates": 50,
    "query_vector": [/* vector generated by model */]
  }
}<p>Além da consulta k-NN, caso haja necessidade de personalizar a pontuação do documento, também é possível usar a consulta script_score, combinando-a com funções de comparação vetorial, como <strong>cossenosimilaridade, produto escalar ou norma l2,</strong> para calcular a relevância de forma mais controlada. Veja o exemplo:</p>{
"script_score": {
    "query": { "match_all": {} },
    "script": {
      "source": "cosineSimilarity(params.query_vector,
'my_dense_vector') + 1.0",
      "params": {
        "query_vector": [/* vector */]
      }
    }
  }
}<p>Se você quiser se aprofundar no assunto, recomendo explorar o artigo <a href="https://www.elastic.co/search-labs/blog/vector-search-set-up-elasticsearch">Como configurar a pesquisa vetorial no Elasticsearch.</a></p><p></p><h2>Tipo de vetor esparso</h2><p>O tipo de campo <strong><code>sparse_vector</code></strong> é usado para armazenar vetores esparsos, que são representações numéricas onde a maioria dos valores é zero e apenas alguns termos têm pesos significativos. Esse tipo de vetor é comum em modelos baseados em termos, como o SPLADE ou o ELSER (Elastic Learned Sparse EncodeR).</p><h3>Quando e por que usar vetores esparsos?</h3><p>Vetores esparsos são ideais quando você precisa de uma busca mais precisa em termos lexicais, sem sacrificar a inteligência semântica. Eles representam o texto como pares de token/valor, destacando apenas os termos mais relevantes com pesos associados, o que proporciona clareza, controle e eficiência.</p><p>Esse tipo de campo é especialmente útil quando você gera vetores com base em termos, como nos modelos ELSER ou SPLADE, que atribuem pesos diferentes a cada token com base em sua importância relativa no texto.</p><p>Para as ocasiões em que você deseja controlar a influência de palavras específicas na consulta, os tipos de vetores esparsos permitem ajustar manualmente o peso dos termos para otimizar a classificação dos resultados.</p><p>Entre os principais benefícios estão a transparência na busca, já que é possível entender claramente por que um documento foi considerado relevante, e a eficiência de armazenamento, pois apenas os tokens com valor diferente de zero são salvos, ao contrário dos vetores densos que armazenam todas as dimensões.</p><p>Além disso, vetores esparsos são o complemento ideal em estratégias de busca híbridas, podendo inclusive ser combinados com vetores densos para unir precisão lexical à compreensão semântica.</p><h3>Como usar a consulta para o tipo vetor esparso?</h3><p>A consulta <strong><code>sparse_vector</code></strong> permite pesquisar documentos com base em um vetor de consulta no formato token/valor. Veja um exemplo da consulta abaixo:</p>{
  "query": {
    "sparse_vector": {
      "field": "field_sparse",
      "query_vector": {
        "token1": 0.6,
        "token2": 0.2,
        "token3": 0.9
      }
    }
  }
}<p>Se preferir usar um modelo treinado, é possível utilizar um endpoint de inferência que transforma automaticamente o texto da consulta em um vetor esparso:</p>{
  "query": {
    "sparse_vector": {
      "field": "field_sparse",
      "inference_id": "the inference ID to produce the token/weights",
      "query": "search text"
    }
  }
}<p>Para explorar este tópico mais a fundo, sugiro a leitura de <a href="https://www.elastic.co/search-labs/blog/sparse-vector-embedding">Understanding sparse vector embeddings with trained ML models</a>.</p><h2>Tipo de texto semântico</h2><p>O tipo de campo <strong><code>semantic_text</code></strong> é a maneira mais simples e direta de usar a pesquisa semântica no Elasticsearch. Ele lida automaticamente com a geração de embeddings, tanto no momento da indexação quanto no momento da consulta, por meio de um endpoint de inferência. Isso significa que você não precisa se preocupar em gerar ou armazenar vetores manualmente.</p><h3>Quando e por que usar texto semântico?</h3><p>O campo <code>semantic_text</code> é ideal para quem quer começar com o mínimo de esforço técnico e sem ter que lidar com vetores manualmente. Este campo automatiza etapas como a geração de incorporações e o mapeamento de busca vetorial, tornando a configuração mais rápida e conveniente.</p><p>Você deve considerar usar <code>semantic_text</code> quando valoriza <strong>simplicidade e abstração</strong>, pois isso <strong>elimina a complexidade de configurar manualmente mapeamentos, geração de incorporações e pipelines de ingestão</strong>. Basta selecionar o modelo de inferência e o Elasticsearch cuida do resto.</p><p>As principais vantagens incluem <strong>a geração automática de embeddings,</strong> realizada durante a indexação e a consulta, e <strong>o mapeamento pronto para uso</strong>, que já vem pré-configurado para suportar o modelo de inferência selecionado.</p><p>Além disso, o campo oferece <strong>suporte nativo para a divisão automática de textos longos (fragmentação de texto)</strong>, permitindo que textos extensos sejam divididos em trechos menores, cada um com seu próprio embedding, o que melhora a precisão da busca. Isso aumenta consideravelmente a produtividade, especialmente para equipes que desejam entregar valor rapidamente sem se preocupar com a engenharia subjacente da busca semântica.</p><p>No entanto, embora <code>semantic_text</code> proporcione velocidade e simplicidade, esta abordagem tem algumas limitações. Permite a utilização de modelos padrão de mercado, desde que estejam disponíveis como endpoints de inferência no Elasticsearch. Mas <strong>não suporta incorporações geradas externamente</strong>, como é possível com o campo <code>dense_vector</code> .</p><p>Se você precisar de mais controle sobre como os vetores são gerados, quiser usar seus próprios embeddings ou precisar combinar vários campos para estratégias avançadas, os campos <code>dense_vector</code> e <code>sparse_vector</code> fornecem a flexibilidade necessária para cenários mais personalizados ou específicos do domínio.</p><h3>Como usar a consulta para o tipo de texto semântico</h3><p>Antes de <strong><code>semantic_text</code></strong>, era necessário usar uma consulta diferente dependendo do tipo de incorporação (densa ou esparsa). Uma consulta <code>sparse_vector</code> foi usada para campos esparsos, enquanto campos <code>dense_vector</code> exigiram consultas KNN.</p><p>Com o tipo de texto semântico, a busca é realizada utilizando a <a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-semantic-query">consulta semântica</a>, que gera automaticamente o vetor de consulta e o compara com os embeddings dos documentos indexados. O tipo <strong><code>semantic_text</code></strong> permite definir um ponto de extremidade de inferência para incorporar a consulta, mas se nenhum for especificado, o mesmo ponto de extremidade usado durante a indexação será aplicado à consulta.</p>{
  "query": {
    "semantic": {
      "field": "semantic_text_field",
      "query": "search text"
    }
  }
}<p>Para saber mais, sugiro a leitura do artigo <a href="https://www.elastic.co/search-labs/blog/semantic-search-simplified-semantic-text">Elasticsearch new semantic_text mapping: Simplifying semantic search</a>.</p><h2>Conclusão</h2><p>Ao escolher como mapear embeddings no Elasticsearch, é essencial entender como você deseja gerar os vetores e qual o nível de controle necessário sobre eles. Se você busca simplicidade, o campo de texto semântico permite buscas semânticas automáticas e escaláveis, tornando-o ideal para muitos casos de uso iniciais. Quando é necessário maior controle, desempenho mais preciso ou integração com modelos personalizados, os campos vetoriais densos e esparsos oferecem a flexibilidade necessária.</p><p>O tipo de campo ideal depende do seu caso de uso, da infraestrutura disponível e do nível de maturidade da sua pilha de aprendizado de máquina. Mais importante ainda, a Elastic oferece as ferramentas para construir sistemas de busca modernos e altamente adaptáveis.</p><h2>Referências</h2><ul><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-text.html">Tipo de campo de texto semântico</a></p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/sparse-vector.html">Tipo de campo vetorial esparso</a></p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/dense-vector.html">Tipo de campo vetorial denso</a></p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-semantic-query.html">Consulta semântica</a></p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-sparse-vector-query.html">Consulta de vetor esparso</a></p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html">pesquisa kNN</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/semantic-search-simplified-semantic-text">Novo mapeamento semantic_text do Elasticsearch: Simplificando a busca semântica</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/sparse-vector-embedding">Compreendendo incorporações vetoriais esparsas com modelos de aprendizado de máquina treinados</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/mapping-embeddings-to-elasticsearch-field-types</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/mapping-embeddings-to-elasticsearch-field-types</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <category><![CDATA[Mapeamentos]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt72cd3c2601b22886/6a17083b0c4857259901a9dc/f98fdff837db55b466780c0bae672aa6f6c3a966-1200x628.png" length="0" type="image/png"/>
    <pubDate>Tue, 13 May 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Como implementar a Quantização Binária Aprimorada (BBQ) em seu caso de uso.]]></title>
    <description><![CDATA[Descubra por que você implementaria a Quantização Binária Aprimorada (BBQ) em seu caso de uso e como fazê-lo.]]></description>
    <content:encoded><![CDATA[<p>A pesquisa vetorial fornece a base para implementar a pesquisa semântica para texto ou a pesquisa por similaridade para imagens, vídeos ou áudio. Com a pesquisa vetorial, os vetores são representações matemáticas de dados que podem ser enormes e, às vezes, lentas. A Quantização Binária Melhorada (doravante denominada BBQ) funciona como um método de compressão para vetores. Ele permite que você encontre as correspondências certas enquanto reduz os vetores para torná-los mais rápidos de pesquisar e processar. Este artigo abordará BBQ e rescore_vector, um campo disponível apenas para índices quantizados que repontuam vetores automaticamente.</p><p>Todas as consultas e saídas completas mencionadas neste artigo podem ser encontradas em nosso <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/how-and-why-bbq">repositório de código do Elasticsearch Labs</a>.</p><h2>Por que implementar a Quantização Binária Aprimorada (BBQ) no seu caso de uso?</h2>Observação: para uma compreensão mais aprofundada de como funciona a matemática por trás do churrasco, confira a <a href="https://www.elastic.co/pt/search-labs/blog/bbq-implementation-into-use-case#further-learning">seção “Aprendizado adicional”</a> abaixo. Para os propósitos deste blog, o foco está na implementação.<p>Embora a matemática seja fascinante, é crucial entender completamente por que suas buscas vetoriais permanecem precisas. Em última análise, tudo se resume à compressão, já que, com os algoritmos de busca vetorial atuais, o limite é a velocidade de leitura dos dados. Portanto, se você conseguir armazenar todos esses dados na memória, obterá um aumento significativo de velocidade em comparação com a leitura do armazenamento (<a href="https://sre.google/static/pdf/rule-of-thumb-latency-numbers-letter.pdf">a memória é aproximadamente 200 vezes mais rápida que os SSDs</a>).</p><p>Há algumas coisas que você precisa ter em mente:</p><ul><li><p>Índices baseados em gráficos como <a href="https://arxiv.org/pdf/1603.09320">HNSW</a> (Hierarchical Navigable Small World) são os mais rápidos para recuperação de vetores.</p><ul><li><p>HNSW: Um algoritmo de busca aproximado do vizinho mais próximo que constrói uma estrutura de gráfico multicamadas para permitir buscas eficientes de similaridade de alta dimensão.</p></li></ul></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt760bd95c206bfa8f/6a17e2ad505ac393f7ad8a95/590f3b3c72a76023a38a0436cd9ff90a9f80e936-1964x1262.png" alt="HNSW: Um algoritmo de busca aproximado do vizinho mais próximo que constrói uma estrutura de gráfico multicamadas para permitir buscas eficientes de similaridade de alta dimensão." /><ul><li><p>O HNSW é fundamentalmente limitado em velocidade pela velocidade de leitura de dados da memória ou, no pior caso, do armazenamento.</p><ul><li><p>O ideal é que você consiga carregar todos os seus vetores armazenados na memória.</p></li></ul></li><li><p>Os modelos de incorporação geralmente produzem vetores com precisão float32, 4 bytes por número de ponto flutuante.</p></li><li><p>E, finalmente, dependendo de quantos vetores e/ou dimensões você tem, você pode rapidamente ficar sem memória para manter todos os seus vetores.</p></li></ul><p>Considerando isso como certo, você verá que um problema surge rapidamente quando você começa a ingerir milhões ou até bilhões de vetores, cada um com potencialmente centenas ou até milhares de dimensões. A seção intitulada “<a href="https://www.elastic.co/pt/search-labs/blog/bbq-implementation-into-use-case#approximate-numbers-on-the-compression-ratios">Números aproximados sobre as taxas de compressão</a>” fornece alguns números aproximados.</p><h2>O que você precisa para começar?</h2><p>Para começar, você precisará do seguinte:</p><ul><li><p>Se estiver usando o Elastic Cloud ou no local, você precisará de uma versão do Elasticsearch superior a 8.18. Embora o BBQ tenha sido introduzido na versão 8.16, neste artigo, você usará <code>vector_rescore</code>, que foi introduzido na versão 8.18.</p></li><li><p>Além disso, você também precisará garantir que haja um <a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/8.18/ml-settings.html">nó de aprendizado de máquina (ML)</a> no seu cluster. (Observação: um nó de ML com no mínimo 4 GB é necessário para carregar o modelo, mas você provavelmente precisará de nós muito maiores para cargas de trabalho de produção completas.)</p></li><li><p>Se estiver usando o Serverless, você precisará selecionar uma instância otimizada para vetores.</p></li><li><p>Você também precisará de um nível básico de conhecimento sobre bancos de dados vetoriais. Se você ainda não estiver familiarizado com os conceitos de pesquisa vetorial no Elastic, talvez seja interessante primeiro conferir os seguintes recursos:</p><ul><li><p><a href="https://www.elastic.co/pt/search-labs/blog/elastic-vector-database-practical-example">Navegando em um banco de dados de vetores elásticos</a></p></li><li><p><a href="https://www.elastic.co/pt/blog/retrieval-augmented-generation-explained">As grandes ideias por trás da geração aumentada de recuperação</a></p></li></ul></li></ul><h2>Implementação de Quantização Binária Aprimorada (BBQ)</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt18df00df95ff2ca7/6a17e2af414c6411989450df/4d388078495566f0527e931e0c2e38facdce83c6-1503x748.png" alt="Implementação do Elasticsearch BBQ." /><p>Para manter este blog simples, você usará funções integradas quando elas estiverem disponíveis. Neste caso, você tem o modelo de incorporação vetorial <a href="https://www.elastic.co/pt/guide/en/machine-learning/8.17/ml-nlp-e5.html"><code>.multilingual-e5-small</code></a> que será executado diretamente dentro do Elasticsearch em um nó de aprendizado de máquina. Observe que você pode substituir o modelo <code>text_embedding</code> pelo incorporador de sua escolha (<a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/8.18/infer-service-openai.html">OpenAI</a>, <a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/8.18/infer-service-google-ai-studio.html">Google AI Studio</a>, <a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/8.18/infer-service-cohere.html">Cohere</a> e muitos outros). Se o seu modelo preferido ainda não estiver integrado, você também pode <a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/8.18/bring-your-own-vectors.html">trazer seus próprios embeddings de vetores densos</a>.)</p><p>Primeiro, você precisará criar um ponto final de inferência para gerar vetores para um determinado trecho de texto. Você executará todos esses comandos no Kibana <a href="https://www.elastic.co/pt/guide/en/kibana/8.18/console-kibana.html">Dev Tools Console</a>. Este comando fará o download do <code>.multilingual-e5-small</code>. Se ainda não existir, ele configurará seu endpoint; isso pode levar um minuto para ser executado. Você pode ver a saída esperada no arquivo <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/how-and-why-bbq/Outputs/01-create-an-inference-endpoint-output.json">01-create-an-inference-endpoint-output.json</a> na pasta Saídas. </p>PUT _inference/text_embedding/my_e5_model
{
  "service": "elasticsearch",
  "service_settings": {
    "num_threads": 1,
    "model_id": ".multilingual-e5-small",
    "adaptive_allocations": {
      "enabled": true,
      "min_number_of_allocations": 1
    }
  }
}<p>Quando isso retornar, seu modelo será configurado e você poderá testar se ele funciona conforme o esperado com o seguinte comando. Você pode ver a saída esperada no arquivo <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/how-and-why-bbq/Outputs/02-embed-text-output.json">02-embed-text-output.json</a> na pasta Saídas.</p>POST _inference/text_embedding/my_e5_model
{
  "input": "my awesome piece of text"
}<p>Se você tiver problemas com seu modelo treinado não sendo alocado a nenhum nó, talvez seja necessário iniciar seu modelo manualmente.</p>POST _ml/trained_models/.multilingual-e5-small/deployment/_start<p>Agora vamos criar um novo mapeamento com 2 propriedades, um campo de texto padrão (<code>my_field</code>) e um campo vetorial denso (<code>my_vector</code>) com 384 dimensões para corresponder à saída do modelo de incorporação. Você também substituirá o <code>index_options.type to bbq_hnsw</code>. Você pode ver a saída esperada no arquivo <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/how-and-why-bbq/Outputs/03-create-byte-qauntized-index-output.json">03-create-byte-qauntized-index-output.json</a> na pasta Saídas.</p>PUT bbq-my-byte-quantized-index
{
  "mappings": {
    "properties": {
      "my_field": {
        "type": "text"
      },
      "my_vector": {
        "type": "dense_vector",
        "dims": 384,
        "index_options": {
          "type": "bbq_hnsw"
        }
      }
    }
  }
}<p>Para garantir que o Elasticsearch gere seus vetores, você pode usar um <a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/8.18/ingest.html">Ingest Pipeline</a>. Este pipeline exigirá 3 coisas: o ponto final, (<code>model_id</code>), o <code>input_field</code> para o qual você deseja criar vetores e o <code>output_field</code> para armazenar esses vetores. O primeiro comando abaixo criará um pipeline de ingestão de inferência, que usa o <a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/current/inference-apis.html">serviço de inferência </a>nos bastidores, e o segundo testará se o pipeline está funcionando corretamente. Você pode ver a saída esperada no arquivo <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/how-and-why-bbq/Outputs/04-create-and-simulate-ingest-pipeline-output.json">04-create-and-simulate-ingest-pipeline-output.json</a> na pasta Outputs. </p>PUT _ingest/pipeline/my_inference_pipeline
{
  "processors": [
    {
      "inference": {
        "model_id": "my_e5_model",
        "input_output": [
          {
            "input_field": "my_field",
            "output_field": "my_vector"
          }
        ]
      }
    }
  ]
}

POST _ingest/pipeline/my_inference_pipeline/_simulate
{
  "docs": [
    {
      "_source": {
        "my_field": "my awesome text field"
      }
    }
  ]
}<p>Agora você está pronto para adicionar alguns documentos com os dois primeiros comandos abaixo e testar se suas pesquisas funcionam com o terceiro comando. Você pode verificar a saída esperada no arquivo <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/how-and-why-bbq/Outputs/05-bbq-index-output.json">05-bbq-index-output.json</a> na pasta Outputs. </p>PUT bbq-my-byte-quantized-index/_doc/1?pipeline=my_inference_pipeline
{
    "my_field": "my awesome text field"
}

PUT bbq-my-byte-quantized-index/_doc/2?pipeline=my_inference_pipeline
{
    "my_field": "some other sentence"
}

GET bbq-my-byte-quantized-index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "knn": {
            "field": "my_vector",
            "query_vector_builder": {
              "text_embedding": {
                "model_id": "my_e5_model",
                "model_text": "my awesome search field"
              }
            },
            "k": 10,
            "num_candidates": 100
          }
        }
      ]
    }
  },
  "_source": [
    "my_field"
  ]
}<p>Conforme recomendado <a href="https://www.elastic.co/pt/search-labs/blog/better-binary-quantization-lucene-elasticsearch#lucene-benchmarking">nesta publicação</a>, a repontuação e a sobreamostragem são recomendadas quando você dimensiona para quantidades não triviais de dados porque elas ajudam a manter alta precisão de recall enquanto se beneficiam das vantagens da compressão. A partir da versão 8.18 do Elasticsearch, você pode fazer isso dessa maneira usando <a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/8.18/knn-search.html#dense-vector-knn-search-rescoring">rescore_vector</a>. A saída esperada está no arquivo <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/how-and-why-bbq/Outputs/06-bbq-search-8-18-output.json">06-bbq-search-8-18-output.json</a> na pasta Outputs.</p>GET bbq-my-byte-quantized-index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "knn": {
            "field": "my_vector",
            "query_vector_builder": {
              "text_embedding": {
                "model_id": "my_e5_model",
                "model_text": "my awesome search field"
              }
            },
            "rescore_vector": {
              "oversample": 3
            },
            "k": 10,
            "num_candidates": 100
          }
        }
      ]
    }
  },
  "_source": [
    "my_field"
  ]
}<p>Como essas pontuações se comparam àquelas que você obteria com dados brutos? Se você fizer tudo acima novamente, mas com <code>index_options.type: hnsw</code>, verá que as pontuações são muito comparáveis. Você pode ver a saída esperada no arquivo <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/how-and-why-bbq/Outputs/07-raw-vector-output.json">07-raw-vector-output.json</a> na pasta Outputs.</p>PUT my-raw-vector-index
{
  "mappings": {
    "properties": {
      "my_field": {
        "type": "text"
      },
      "my_vector": {
        "type": "dense_vector",
        "dims": 384,
        "index_options": {
          "type": "hnsw"
        }
      }
    }
  }
}

PUT my-raw-vector-index/_doc/1?pipeline=my_inference_pipeline
{
    "my_field": "my awesome text field"
}

PUT my-raw-vector-index/_doc/2?pipeline=my_inference_pipeline
{
    "my_field": "some other sentence"
}

GET my-raw-vector-index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "knn": {
            "field": "my_vector",
            "query_vector_builder": {
              "text_embedding": {
                "model_id": "my_e5_model",
                "model_text": "my awesome search field"
              }
            },
            "k": 10,
            "num_candidates": 100
          }
        }
      ]
    }
  },
  "_source": [
    "my_field"
  ]
}<h2>Números aproximados sobre as taxas de compressão</h2><p>Os requisitos de armazenamento e memória podem rapidamente se tornar um desafio significativo ao trabalhar com pesquisa vetorial. A análise a seguir ilustra como diferentes técnicas de quantização reduzem drasticamente o consumo de memória de dados vetoriais.</p><p>Vetores (V)</p><p>Dimensões (D)</p><p>cru (V x D x 4)</p><p>int8 (V x (D x 1 + 4))</p><p>int4 (V x (D x 0,5 + 4))</p><p>churrasco (V x (D x 0,125 + 4))</p><p>10.000.000</p><p>384</p><p>14,31 GB</p><p>3,61 GB</p><p>1,83 GB</p><p>0,58 GB</p><p>50.000.000</p><p>384</p><p>71,53 GB</p><p>18,07 GB</p><p>9,13 GB</p><p>2,89 GB</p><p>100.000.000</p><p>384</p><p>143,05 GB</p><p>36,14 GB</p><p>18,25 GB</p><p>5,77 GB</p><h2>Conclusão</h2><p>BBQ é uma otimização que você pode aplicar aos seus dados vetoriais para compressão sem sacrificar a precisão. Ele funciona convertendo vetores em bits, permitindo que você pesquise os dados de forma eficaz e capacitando você a dimensionar seus fluxos de trabalho de IA para acelerar pesquisas e otimizar o armazenamento de dados.</p><h2>Aprendizagem adicional</h2><p>Se você estiver interessado em aprender mais sobre churrasco, não deixe de conferir os seguintes recursos:</p><ul><li><p><a href="https://www.elastic.co/pt/search-labs/blog/better-binary-quantization-lucene-elasticsearch">Quantização Binária (BBQ) em Lucene e Elasticsearch</a></p></li><li><p><a href="https://www.elastic.co/pt/search-labs/blog/bit-vectors-elasticsearch-bbq-vs-pq">Melhor Quantização Binária (BBQ) vs. Quantização de Produto</a></p></li><li><p><a href="https://www.elastic.co/pt/search-labs/blog/optimized-scalar-quantization-elasticsearch">Quantização Escalar Otimizada: Quantização Binária Ainda Melhor</a></p></li><li><p><a href="https://www.youtube.com/watch?v=04NzMt2Nigc">Melhor Quantização Binária (BBQ): De Bytes a BBQ, O Segredo para uma Melhor Busca Vetorial por Ben Trent</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/bbq-implementation-into-use-case</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/bbq-implementation-into-use-case</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <category><![CDATA[Noções básicas]]></category>
    <dc:creator><![CDATA[Sachin Frayne,Jessica Garson]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3dd0495b536b2615/6a17e2b0414c6488459450e3/66842055367cdd795532b01c167f2a4b03dc65e3-1200x628.png" length="0" type="image/png"/>
    <pubDate>Wed, 23 Apr 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearch BBQ vs. OpenSearch FAISS: Comparação de desempenho de pesquisa vetorial]]></title>
    <description><![CDATA[Uma comparação de desempenho entre o Elasticsearch BBQ e o OpenSearch FAISS.]]></description>
    <content:encoded><![CDATA[<p><strong>Pesquisa vetorial com quantização binária: Elasticsearch com BBQ é 5x mais rápido que OpenSearch com FAISS</strong>. A Elastic recebeu solicitações da nossa comunidade para esclarecer as diferenças de desempenho entre o Elasticsearch e o OpenSearch, particularmente no âmbito da Pesquisa Semântica/Pesquisa Vetorial, por isso conduzimos esses testes de desempenho para fornecer comparações claras e baseadas em dados.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta8792b5284e7a553/6a1709b9961e69a084c4cee6/7f4f8d08f7bee188423e4e65f0caefc7e34f0355-1600x681.png" alt="Elasticsearch BBQ vs. OpenSearch FAISS - Comparação de velocidade e taxa de transferência (recall)" /><h2>Confronto de quantização binária</h2><p>Armazenar vetores de alta dimensão em sua forma original pode exigir muita memória. Técnicas de quantização comprimem esses vetores em uma representação compacta, reduzindo drasticamente o consumo de memória. A busca então opera no espaço comprimido, o que reduz a complexidade computacional e torna as buscas mais rápidas, especialmente em grandes conjuntos de dados.</p><p>A Elastic está empenhada em fazer do Lucene um mecanismo vetorial de alto desempenho. Introduzimos <a href="https://www.elastic.co/pt/search-labs/blog/better-binary-quantization-lucene-elasticsearch">a Quantização Binária Aprimorada</a> (BBQ) no Elasticsearch 8.16, com base no Lucene, e aprimoramos ainda mais nas versões 8.18 e 9.0. O BBQ é baseado em uma nova abordagem de <a href="https://www.elastic.co/pt/search-labs/blog/optimized-scalar-quantization-elasticsearch">quantização escalar</a> que reduz as dimensões de ponto flutuante de 32 bits, proporcionando uma redução de memória de aproximadamente 95%, mantendo uma alta qualidade de classificação.</p><p>O OpenSearch, por outro lado, usa vários mecanismos de vetores: nmslib (agora obsoleto), Lucene e FAISS. Em um <a href="https://www.elastic.co/pt/search-labs/blog/elasticsearch-opensearch-vector-search-performance-comparison">blog anterior</a>, comparamos o Elasticsearch e o OpenSearch para pesquisa vetorial. Usamos três conjuntos de dados diferentes e testamos diferentes combinações de mecanismos e configurações em ambos os produtos.</p><p>Este blog se concentra nos algoritmos de quantização binária atualmente disponíveis em ambos os produtos. Testamos o Elasticsearch com BBQ e o OpenSearch com <a href="https://opensearch.org/docs/latest/search-plugins/knn/knn-vector-quantization/#binary-quantization">a Quantização Binária do FAISS</a> usando a trilha Rally <a href="https://github.com/elastic/rally-tracks/edit/master/openai_vector">openai_vector</a> .</p><p>O objetivo principal era avaliar o desempenho de ambas as soluções sob o mesmo nível de recall. O que significa <em>recall</em> ? Recall é uma métrica que mede quantos resultados relevantes são recuperados com sucesso por um sistema de pesquisa.</p><p>Nesta avaliação, recall@k é particularmente importante, onde <em>k</em> representa o número de resultados principais considerados. <strong>Recall@10</strong>, <strong>Recall@50 e Recall@100</strong> medem, portanto, quantos dos resultados verdadeiramente relevantes aparecem nos 10, 50 e 100 principais itens recuperados, respectivamente. A recordação é expressa em uma escala de 0 a 1 (ou precisão de 0% a 100%). E isso é importante porque estamos falando de KNN Aproximado (ANN) e não de KNN Exato, onde a recordação é sempre 1 (100%).</p><p>Para cada valor de <em>k</em> também especificamos <em>n, </em>que é o número de candidatos considerados antes de aplicar a classificação final. Isso significa que para Recall@10, Recall@50 e Recall@100, o sistema primeiro recupera <em>n</em> candidatos usando o algoritmo de quantização binária e depois os classifica para determinar se os <em>k</em> principais resultados contêm os itens relevantes esperados.</p><p>Ao controlar <em>n</em>, podemos analisar o trade-off entre eficiência e precisão. Um <em>n</em> mais alto normalmente <strong>aumenta</strong> a recuperação, pois mais candidatos estão disponíveis para classificação, mas também <strong>aumenta</strong> a latência e<strong> diminui </strong>a taxa de transferência. Por outro lado, um <em>n</em> menor acelera a recuperação, mas pode reduzir a recordação se poucos candidatos relevantes forem incluídos no conjunto inicial.</p><p>Nesta comparação, o Elasticsearch demonstrou menor latência e maior rendimento que o OpenSearch em configurações idênticas.</p><h2>Metodologia</h2><p>A configuração completa, juntamente com os scripts do Terraform, os manifestos do Kubernetes e a trilha Rally específica estão disponíveis neste <a href="https://github.com/elastic/elasticsearch-opensearch-vector-performance/tree/bbq">repositório</a> em <a href="https://github.com/elastic/elasticsearch-opensearch-vector-performance/tree/bbq/rally-custom/custom_tracks/elasticsearch/openai_vector_bq"><em>openai_vector_bq</em></a>.</p><p>Assim como nos benchmarks anteriores, usamos um cluster Kubernetes composto por:</p><ul><li><p>1 pool de nós para Elasticsearch 9.0 com 3 máquinas <code>e2-standard-32</code> (128 GB de RAM e 32 CPUs)</p></li><li><p>1 pool de nós para OpenSearch 2.19 com 3 máquinas <code>e2-standard-32</code> (128 GB de RAM e 32 CPUs)</p></li><li><p>1 pool de nós para Rally com 2 máquinas <code>e2-standard-4</code> (16 GB de RAM e 4 CPUs)</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt934e988e96a5849f/6a1709bbacf08838f9be9ac1/169bb6033f6eebfd1b177b3446bf916fde4ee5c5-1600x856.png" alt="Configuração da metodologia Elasticsearch BBQ vs Opensearch FAISS" /><p>Configuramos um cluster Elasticsearch versão 9.0 e um cluster OpenSearch versão 2.19.</p><p>Tanto o Elasticsearch quanto o OpenSearch foram testados com exatamente a mesma configuração: usamos o Rally track <a href="https://github.com/elastic/rally-tracks/edit/master/openai_vector">openai_vector</a> com <a href="https://github.com/elastic/elasticsearch-opensearch-vector-performance/commit/b97d5d95c22c8cf862f2030964524bdd156a5da3">algumas modificações</a> , que usa 2,5 milhões de documentos do <a href="https://huggingface.co/datasets/BeIR/nq">conjunto de dados NQ</a> enriquecidos com embeddings gerados usando <a href="https://openai.com/blog/new-and-improved-embedding-model">o modelo text-embedding-ada-002</a> do OpenAI.</p>{
  "source-file": "open_ai_corpus-initial-indexing.json.bz2",
  "document-count": 2580961,
  "compressed-bytes": 32076749416,
  "uncompressed-bytes": 90263571686
}<p>Os resultados relatam a latência e a taxa de transferência medidas em diferentes níveis de recall (recall@10, recall@50 e recall@100) usando 8 clientes simultâneos para executar operações de pesquisa. Usamos um único fragmento e nenhuma réplica.</p><p>Executamos as seguintes combinações de kn-rescore, por exemplo 10-2000-2000, ou <em>k:10</em>, <em>n:2000</em> e <em>rescore:2000</em> recuperariam os k (10) principais sobre n candidatos (2000) aplicando uma rescore sobre 2000 resultados (o que é equivalente a um “fator de sobreamostragem” de 1). Cada pesquisa foi executada 10.000 vezes, com 1.000 pesquisas como aquecimento:</p><p></p><p><u><strong>Recall@10</strong></u></p><ul><li><p>10-40-40</p></li><li><p>10-50-50</p></li><li><p>10-100-100</p></li><li><p>10-200-200</p></li><li><p>10-500-500</p></li><li><p>10-750-750</p></li><li><p>10-1000-1000</p></li><li><p>10-1500-1500</p></li><li><p>10-2000-2000</p></li></ul><p><u><strong>Recall@50</strong></u></p><ul><li><p>50-150-150</p></li><li><p>50-200-200</p></li><li><p>50-250-250</p></li><li><p>50-500-500</p></li><li><p>50-750-750</p></li><li><p>50-1000-1000</p></li><li><p>50-1200-1200</p></li><li><p>50-1500-1500</p></li><li><p>50-2000-2000</p></li></ul><p><u><strong>Lembre-se @100</strong></u></p><ul><li><p>100-200-200</p></li><li><p>100-250-250</p></li><li><p>100-300-300</p></li><li><p>100-500-500</p></li><li><p>100-750-750</p></li><li><p>100-1000-1000</p></li><li><p>100-1200-1200</p></li><li><p>100-1500-1500</p></li><li><p>100-2000-2000</p></li></ul><p>Para replicar o benchmark, os manifestos do Kubernetes para rally-elasticsearch e rally-opensearch têm todas as variáveis relevantes externalizadas em um ConfigMap, disponível <a href="https://github.com/elastic/elasticsearch-opensearch-vector-performance/blob/bbq/k8s/rally-openai_vector-es-bq.yml">aqui</a> (ES) e <a href="https://github.com/elastic/elasticsearch-opensearch-vector-performance/blob/bbq/k8s/rally-openai_vector-os-bq.yml">aqui</a> (OS). O parâmetro <em>search_ops</em> pode ser personalizado para testar qualquer combinação de k, n e rescore.</p><h3>Configuração do OpenSearch Rally</h3><p><code>/k8s/rally-openai_vector-os-bq.yml</code></p>apiVersion: v1
kind: ConfigMap
metadata:
  name: rally-params-os
  labels:
    app: rally-opensearch
data:
  user-tags.json: |
    {
      "product": "OpenSearch",
      "product-version": "OpenSearch-2.19.0",
      "product-label": "OpenSearch-2.19-faiss",
      "benchmark-run": "19-feb-recall@100"
    }
  track-params.json: |
    {
      "mapping_type": "vectors-only-mapping-with-docid",
      "standalone_search_clients": 8,
      "standalone_search_iterations": 5000,
      "ann_threshold": 0,
      "vector_mode": "on_disk",
      "compression_level": "32x",
      "vector_method_name": "hnsw",
      "vector_method_engine": "faiss",
      "search_ops": [
        [100, 200, 200],
        [100, 250, 250],
        [100, 300, 300],
        [100, 500, 500],
        [100, 750, 750],
        [100, 1000, 1000],
        [100, 1200, 1200],
        [100, 1500, 1500],
        [100, 2000, 2000]
      ]
    }<h3>Configuração do índice Opensearch</h3><p>As variáveis do ConfigMap são então usadas na configuração do índice, alguns parâmetros são deixados inalterados. A quantização de 1 bit no OpenSearch é configurada <a href="https://opensearch.org/docs/latest/search-plugins/knn/knn-vector-quantization/#binary-quantization">definindo o nível de compressão como “32x”</a>.</p><p><code>index-vectors-only-mapping-with-docid-mapping.json</code></p>{
  "settings": {
    {% if preload_pagecache %}
    "index.store.preload": [
      "vec", "vex", "vem", "veq", "veqm", "veb", "vebm"
    ],
    {% endif %}
    "index.number_of_shards": {{ number_of_shards | default(1) }},
    "index.number_of_replicas": {{ number_of_replicas | default(0) }},
    "index.knn": true,
    "index.knn.advanced.approximate_threshold": {{ ann_threshold | default(15000) }}
  },
  "mappings": {
    "dynamic": false,
    "properties": {
      "docid": {
        "type": "keyword"
      },
      "emb": {
        "type": "knn_vector",
        "dimension": 1536,
        "space_type": "innerproduct",
        "data_type": "float",
        "mode": {{ vector_mode | default("in_memory") | tojson }},
        "compression_level": {{ compression_level | default("32x") | tojson }},
        "method": {
          "name": {{ vector_method_name | default("hnsw") | tojson }},
          "engine": {{ vector_method_engine | default("faiss") | tojson }},
          "parameters": {
            "ef_construction": 100,
            "m": 16
          }
        }
      }
    }
  }
}<h3>Configuração do Elasticsearch Rally</h3><p><code>/k8s/rally-openai_vector-es-bq.yml</code></p>apiVersion: v1
kind: ConfigMap
metadata:
  name: rally-params-es
  labels:
    app: rally-elasticsearch
data:
  user-tags.json: |
    {
      "product": "Elasticsearch",
      "product-version": "Elasticsearch-9.0.0-ade01164",
      "product-label": "Elasticsearch-9.0-BBQ",
      "benchmark-run": "19-feb-recall@100"
    }
  track-params.json: |
    {
      "mapping_type": "vectors-only-mapping-with-docid",
      "standalone_search_clients": 8,
      "standalone_search_iterations": 5000,
      "vector_index_type": "bbq_hnsw",
      "search_ops": [
        [100, 200, 200],
        [100, 250, 250],
        [100, 300, 300],
        [100, 500, 500],
        [100, 750, 750],
        [100, 1000, 1000],
        [100, 1200, 1200],
        [100, 1500, 1500],
        [100, 2000, 2000]
      ]
    }<h3>Configuração do índice do Elasticsearch</h3><p><code>index-vectors-only-mapping-with-docid-mapping.json</code></p>{
  "settings": {
    {# non-serverless-index-settings-marker-start #}
    {%- if build_flavor != "serverless" or serverless_operator == true -%}
    {% if preload_pagecache %}
    "index.store.preload": [ "vec", "vex", "vem", "veq", "veqm", "veb", "vebm" ],
    {% endif %}
    "index.number_of_shards": {{ number_of_shards | default(1) }},
    "index.number_of_replicas": {{ number_of_replicas | default(0) }}
    {%- endif -%}
    {# non-serverless-index-settings-marker-end #}
  },
  "mappings": {
    "dynamic": false,
    "properties": {
      "docid": {
        "type": "keyword"
      },
      "emb": {
        "type": "dense_vector",
        "element_type": "float",
        "dims": 1536,
        "index": true,
        "similarity": "dot_product",
        "index_options": {
          "type": {{ vector_index_type | default("bbq_hnsw") | tojson }},
          "ef_construction": 100,
          "m": 16
        }
      }
    }
  }
}<h2>Resultados</h2><p>Há várias maneiras de interpretar os resultados. Tanto para latência quanto para taxa de transferência, criamos um gráfico simplificado e detalhado em cada nível de recall. É fácil ver diferenças se considerarmos “quanto maior, melhor” para cada métrica. No entanto, a latência é negativa (quanto menor, melhor), enquanto a taxa de transferência é positiva. Para os gráficos simplificados, usamos <strong>(recall / latência) * 10000 </strong>(chamado simplesmente de “velocidade”) e<strong> recall * throughput</strong>, então ambas as métricas significam que mais velocidade e mais throughput são melhores. Vamos lá.</p><h3>Recall @ 10 - simplificado</h3><p>Nesse nível de recall, o Elasticsearch BBQ é até <strong>5x mais rápido </strong>(3,9x mais rápido em média) e tem <strong>3,2x mais taxa de transferência</strong> em média do que o OpenSearch FAISS.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3de6b3460127a8ae/6a1709bcb339d55975769f6f/d580ad53e8974bd3aa75957c413a0136c4e465c5-1600x681.png" alt="O Elasticsearch BBQ é até 5 vezes mais rápido (3,9 vezes mais rápido em média) e tem 3,2 vezes mais capacidade de processamento em média do que o OpenSearch FAISS em termos de velocidade e capacidade de processamento (Recall@10)." /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd47045013cac5e9d/6a1709be0e2e49599a41a088/18edce667fe36ab95033264ef8df6f352dda2425-2044x866.png" alt="O Elasticsearch BBQ é até 5 vezes mais rápido (3,9 vezes mais rápido em média) e tem 3,2 vezes mais capacidade de processamento em média do que o OpenSearch FAISS." /><h4>Recall @ 10 - Detalhado</h4><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0ecb02d4a2967dbd/6a1709bf14b270c04fe3c5c6/a7459b87e679f4ad963d0e2f1685499b40f6f050-1600x799.png" alt="Comparação detalhada da latência recall@10 entre Elasticsearch BBQ e Opensearch FAISS" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt182da309ad6cfd39/6a1709c166c4f99077f8bfd8/c6036f55a13377654d296eb3148c7199e1965475-1600x799.png" alt="Comparação detalhada do desempenho de recall@10 entre Elasticsearch BBQ e Opensearch FAISS." /><p></p><p>tarefa</p><p>latência.média</p><p>rendimento.média</p><p>recuperação média</p><p>Elasticsearch-9.0-BBQ</p><p>10-100-100</p><p>11,70</p><p>513,58</p><p>0,89</p><p>Elasticsearch-9.0-BBQ</p><p>10-1000-100</p><p>27,33</p><p>250,55</p><p>0,95</p><p>Elasticsearch-9.0-BBQ</p><p>10-1500-1500</p><p>35,93</p><p>197,26</p><p>0,95</p><p>Elasticsearch-9.0-BBQ</p><p>10-200-200</p><p>13,33</p><p>456,16</p><p>0,92</p><p>Elasticsearch-9.0-BBQ</p><p>10-2000-2000</p><p>44,27</p><p>161,40</p><p>0,95</p><p>Elasticsearch-9.0-BBQ</p><p>10-40-40</p><p>10,97</p><p>539,94</p><p>0,84</p><p>Elasticsearch-9.0-BBQ</p><p>10-50-50</p><p>11h00</p><p>535,73</p><p>0,85</p><p>Elasticsearch-9.0-BBQ</p><p>10-500-500</p><p>19,52</p><p>341,45</p><p>0,93</p><p>Elasticsearch-9.0-BBQ</p><p>10-750-750</p><p>22,94</p><p>295,19</p><p>0,94</p><p>OpenSearch-2.19-faiss</p><p>10-100-100</p><p>35,59</p><p>200,61</p><p>0,94</p><p>OpenSearch-2.19-faiss</p><p>10-1000-1000</p><p>156,81</p><p>58,30</p><p>0,96</p><p>OpenSearch-2.19-faiss</p><p>10-1500-1500</p><p>181,79</p><p>42,97</p><p>0,96</p><p>OpenSearch-2.19-faiss</p><p>10-200-200</p><p>47,91</p><p>155,16</p><p>0,95</p><p>OpenSearch-2.19-faiss</p><p>10-2000-2000</p><p>232,14</p><p>31,84</p><p>0,96</p><p>OpenSearch-2.19-faiss</p><p>10-40-40</p><p>27,55</p><p>249,25</p><p>0,92</p><p>OpenSearch-2.19-faiss</p><p>10-50-50</p><p>28,78</p><p>245,14</p><p>0,92</p><p>OpenSearch-2.19-faiss</p><p>10-500-500</p><p>79,44</p><p>97,06</p><p>0,96</p><p>OpenSearch-2.19-faiss</p><p>10-750-750</p><p>104,19</p><p>75,49</p><p>0,96</p><h3>Recall @ 50 - simplificado</h3><p>Nesse nível de recall, o Elasticsearch BBQ é <strong>até 5x mais rápido</strong> (4,2x mais rápido em média) e tem <strong>3,9x mais rendimento</strong> em médiado que o OpenSearch FAISS.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt486683f2c7a1d677/6a1709c2a6c2b995bde796a3/3189ffb330948b35854eeea9ae317d4846c14972-1600x681.png" alt="Comparação de desempenho vetorial Recal @50: Elasticsearch BBQ vs Opensearch FAISS" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltba830c6741784682/6a1709c4a292990627d00ff7/607383d674dcf0b8f94bfb1a450063f52fcbeb15-2060x876.png" alt="O Elasticsearch BBQ é até 5 vezes mais rápido (4,2 vezes mais rápido em média) e tem 3,9 vezes mais capacidade de processamento em média do que o OpenSearch FAISS." /><h4>Resultados detalhados - Recall @ 50</h4><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt821775e7f87e5ede/6a1709c6a6c2b90af3e796a7/ebfffe0b776aad31dd03d315cfbf5aa098b41226-1600x789.png" alt="Resultados de latência do Recall@50 Elasticsearch BBQ e Opensearch FAISS" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4f47784ed1017c15/6a1709c7e8fbce0fbe39fbf5/ae20cf870a65c400a2112bbad62eb56e244f549a-1600x799.png" alt="Resultados de throughput do Elasticsearch BBQ e Opensearch FAISS no Recall@50" /><p></p><p>Tarefa</p><p>Média de latência</p><p>Média de rendimento</p><p>Recall médio</p><p>Elasticsearch-9.0-BBQ</p><p>50-1000-1000</p><p>25,71</p><p>246,44</p><p>0,95</p><p>Elasticsearch-9.0-BBQ</p><p>50-1200-1200</p><p>28,81</p><p>227,85</p><p>0,95</p><p>Elasticsearch-9.0-BBQ</p><p>50-150-150</p><p>13,43</p><p>362,90</p><p>0,90</p><p>Elasticsearch-9.0-BBQ</p><p>50-1500-1500</p><p>33,38</p><p>202,37</p><p>0,95</p><p>Elasticsearch-9.0-BBQ</p><p>50-200-200</p><p>12,99</p><p>406,30</p><p>0,91</p><p>Elasticsearch-9.0-BBQ</p><p>50-2000-2000</p><p>42,63</p><p>163,68</p><p>0,95</p><p>Elasticsearch-9.0-BBQ</p><p>50-250-250</p><p>14,41</p><p>373,21</p><p>0,92</p><p>Elasticsearch-9.0-BBQ</p><p>50-500-500</p><p>17h15</p><p>341,04</p><p>0,93</p><p>Elasticsearch-9.0-BBQ</p><p>50-750-750</p><p>31,25</p><p>248,60</p><p>0,94</p><p>OpenSearch-2.19-faiss</p><p>50-1000-1000</p><p>125,35</p><p>62,53</p><p>0,96</p><p>OpenSearch-2.19-faiss</p><p>50-1200-1200</p><p>143,87</p><p>54,75</p><p>0,96</p><p>OpenSearch-2.19-faiss</p><p>50-150-150</p><p>43,64</p><p>130,01</p><p>0,89</p><p>OpenSearch-2.19-faiss</p><p>50-1500-1500</p><p>169,45</p><p>46,35</p><p>0,96</p><p>OpenSearch-2.19-faiss</p><p>50-200-200</p><p>48,05</p><p>156,07</p><p>0,91</p><p>OpenSearch-2.19-faiss</p><p>50-2000-2000</p><p>216,73</p><p>36,38</p><p>0,96</p><p>OpenSearch-2.19-faiss</p><p>50-250-250</p><p>53,52</p><p>142,44</p><p>0,93</p><p>OpenSearch-2.19-faiss</p><p>50-500-500</p><p>78,98</p><p>97,82</p><p>0,95</p><p>OpenSearch-2.19-faiss</p><p>50-750-750</p><p>103,20</p><p>75,86</p><p>0,96</p><h3>Recall @ 100</h3><p>Nesse nível de recall, o Elasticsearch BBQ é <strong>até 5x mais rápido </strong>(média de 4,6x mais rápido) e tem <strong>3,9x mais taxa de transferência </strong>em média do que o OpenSearch FAISS.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt380eb9165343a7b3/6a1709c9acf0882669be9ac5/d3f29db64cbde9956de1fa3ae64a75f15141a2bb-1600x681.png" alt="Relembrando os resultados do Elasticsearch BBQ VS Opensearch FAISS @100" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt505ce8aa70898390/6a1709cb1949f73a37e7a9bb/10aff7f8c61fdac895b9ba9c5342baf239ba3ffc-2072x864.png" alt="Comparação de desempenho de latência e taxa de transferência do Elasticsearch BBQ e do Opensearch FAISS" /><h4>Resultados detalhados - Recall @ 100</h4><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb2a73e29940e9d6b/6a1709cccf4f257615b2d138/8790fdf9512b850447f6875fb69969f6f1d4da5f-1600x799.png" alt="Resultados detalhados de latência - Recall @ 100 Elasticsearch BBQ vs Opensearch FAISS" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte2c91ca21da03d96/6a1709ce0c4857d3fc01aa39/d47032fac6c288cd4eedd9f25001e417b2fa9d65-1600x787.png" alt="Resultados detalhados de desempenho - Recall @ 100 Elasticsearch BBQ vs Opensearch FAISS." /><p></p><p>tarefa</p><p>latência.média</p><p>rendimento.média</p><p>recuperação média</p><p>Elasticsearch-9.0-BBQ</p><p>100-1000-1000</p><p>27,82</p><p>243,22</p><p>0,95</p><p>Elasticsearch-9.0-BBQ</p><p>100-1200-1200</p><p>31.14</p><p>224.04</p><p>0,95</p><p>Elasticsearch-9.0-BBQ</p><p>100-1500-1500</p><p>35,98</p><p>193,99</p><p>0,95</p><p>Elasticsearch-9.0-BBQ</p><p>100-200-200</p><p>14.18</p><p>403,86</p><p>0,88</p><p>Elasticsearch-9.0-BBQ</p><p>100-2000-2000</p><p>45,36</p><p>159,88</p><p>0,95</p><p>Elasticsearch-9.0-BBQ</p><p>100-250-250</p><p>14,77</p><p>433,06</p><p>0,90</p><p>Elasticsearch-9.0-BBQ</p><p>100-300-300</p><p>14,61</p><p>375,54</p><p>0,91</p><p>Elasticsearch-9.0-BBQ</p><p>100-500-500</p><p>18,88</p><p>340,37</p><p>0,93</p><p>Elasticsearch-9.0-BBQ</p><p>100-750-750</p><p>23,59</p><p>285,79</p><p>0,94</p><p>OpenSearch-2.19-faiss</p><p>100-1000-1000</p><p>142,90</p><p>58,48</p><p>0,95</p><p>OpenSearch-2.19-faiss</p><p>100-1200-1200</p><p>153,03</p><p>51,04</p><p>0,95</p><p>OpenSearch-2.19-faiss</p><p>100-1500-1500</p><p>181,79</p><p>43,20</p><p>0,96</p><p>OpenSearch-2.19-faiss</p><p>100-200-200</p><p>50,94</p><p>131,62</p><p>0,83</p><p>OpenSearch-2.19-faiss</p><p>100-2000-2000</p><p>232,53</p><p>33,67</p><p>0,96</p><p>OpenSearch-2.19-faiss</p><p>100-250-250</p><p>57,08</p><p>131,23</p><p>0,87</p><p>OpenSearch-2.19-faiss</p><p>100-300-300</p><p>62,76</p><p>120,10</p><p>0,89</p><p>OpenSearch-2.19-faiss</p><p>100-500-500</p><p>84,36</p><p>91,54</p><p>0,93</p><p>OpenSearch-2.19-faiss</p><p>100-750-750</p><p>111,33</p><p>69,95</p><p>0,94</p><h2>Melhorias no churrasco</h2><p>BBQ percorreu um longo caminho desde seu primeiro lançamento. No Elasticsearch 8.16, para fins de comparação, incluímos uma execução de benchmark da versão 8.16 junto com a atual, e podemos ver como a recuperação e a latência melhoraram desde então.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9f1f7ae1bb425a3a/6a1709cf67045bde8e45c192/45a0acfe5985bff28ccada76ec4eca190fe65f72-1600x799.png" alt="Melhorias na latência de recall do Elasticsearch 9.0 BBQ comparadas com o Elasticsearch 8.16 BBQ" /><p>No Elasticsearch 8.18 e 9.0, reescrevemos o algoritmo principal para quantizar os vetores. Então, embora o BBQ na versão 8.16 fosse bom, as versões mais recentes são ainda melhores. Você pode ler sobre isso <a href="https://www.elastic.co/pt/search-labs/blog/optimized-scalar-quantization-elasticsearch">aqui</a> e <a href="https://www.elastic.co/pt/search-labs/blog/scalar-quantization-optimization">aqui</a>. Em resumo, cada vetor é quantizado individualmente por meio de quantis escalares otimizados. Como resultado, os usuários se beneficiam de maior precisão na pesquisa de vetores sem comprometer o desempenho, tornando a recuperação de vetores do Elasticsearch ainda mais poderosa.</p><h2>Conclusão</h2><p>Nesta comparação de desempenho entre o Elasticsearch BBQ e o OpenSearch FAISS, o Elasticsearch supera significativamente o OpenSearch para pesquisa vetorial, alcançando velocidades de consulta até 5x mais rápidas e uma taxa de transferência 3,9x maior, em média, em vários níveis de recuperação.</p><p>As principais descobertas incluem:</p><ul><li><p><strong>Recall@10</strong>: O Elasticsearch BBQ é até 5x mais rápido (3,9x mais rápido em média) e tem 3,2x mais taxa de transferência em média em comparação ao OpenSearch FAISS.</p></li><li><p><strong>Recall@50</strong>: O Elasticsearch BBQ é até 5x mais rápido (4,2x mais rápido em média) e tem 3,9x mais taxa de transferência em média em comparação ao OpenSearch FAISS.</p></li><li><p><strong>Recall@100</strong>: O Elasticsearch BBQ é até 5x mais rápido (4,6x mais rápido em média) e tem 3,9x mais taxa de transferência em média em comparação ao OpenSearch FAISS.</p></li></ul><p>Esses resultados destacam as vantagens de eficiência e desempenho do Elasticsearch BBQ, particularmente em cenários de pesquisa vetorial de alta dimensão. A técnica Better Binary Quantization (BBQ), introduzida no Elasticsearch 8.16, proporciona redução substancial de memória (~95%) enquanto mantém alta qualidade de classificação, tornando-a uma escolha superior para aplicações de pesquisa vetorial em larga escala.</p><p>Na Elastic, estamos inovando incansavelmente para melhorar o Apache Lucene e o Elasticsearch para fornecer o melhor banco de dados vetorial para casos de uso de pesquisa e recuperação, incluindo RAG (Retrieval Augmented Generation). Nossos <a href="https://www.elastic.co/pt/search-labs/blog/optimized-scalar-quantization-elasticsearch">avanços recentes</a> aumentaram drasticamente o desempenho, tornando a pesquisa vetorial mais rápida e mais eficiente em termos de espaço do que antes, aproveitando os ganhos do Lucene 10. Este blog é outra ilustração dessa inovação.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-bbq-vs-opensearch-faiss</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-bbq-vs-opensearch-faiss</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <dc:creator><![CDATA[Ugo Sangiorgi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd76ada1eebfe05cd/6a1709d1a29299ed29d00ffb/796de4829e29566f1f3efa2482f5c3e54b31b1d6-1536x1024.png" length="0" type="image/png"/>
    <pubDate>Tue, 15 Apr 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Banco de dados vetorial Elasticsearch para aterramento nativo na plataforma Vertex AI do Google Cloud]]></title>
    <description><![CDATA[Descubra como o Elasticsearch, o primeiro mecanismo de ancoragem nativo de terceiros para o Vertex AI do Google Cloud, permite criar experiências GenAI personalizadas, ancorando modelos Gemini em dados corporativos.]]></description>
    <content:encoded><![CDATA[<p>A Elastic tem o prazer de anunciar que o banco de dados de vetores Elasticsearch agora está integrado à plataforma Vertex AI do Google Cloud como um mecanismo de recuperação de informações com suporte nativo, permitindo que os usuários aproveitem os pontos fortes multimodais dos modelos Gemini do Google com os recursos avançados de pesquisa semântica e híbrida com tecnologia de IA do Elasticsearch.</p><p>Os desenvolvedores agora podem criar seus aplicativos RAG em uma jornada unificada, fundamentando suas experiências de bate-papo em seus dados privados de maneira flexível e com pouco código. Seja para criar agentes de IA para seus clientes e funcionários internos ou para aproveitar a geração de LLMs (Modelos de Aprendizagem Baseados em Aprendizagem) em seu software, a plataforma Vertex AI coloca a relevância do Elasticsearch ao seu alcance com configuração mínima. Essa integração permite uma adoção mais fácil e rápida dos modelos Gemini em casos de uso de produção, levando o GenAI de provas de conceito a cenários da vida real.</p><p>Neste blog, mostraremos como integrar o Elasticsearch com a plataforma Vertex AI do Google Cloud para obter uma base de dados perfeita e criar aplicativos GenAI totalmente personalizáveis. Vamos descobrir como.</p><h2>Os modelos Vertex AI e Gemini do Google Cloud são baseados nos seus dados com o Elasticsearch.</h2><p>Usuários que utilizam serviços e ferramentas da Vertex AI para criar aplicativos GenAI agora podem acessar a nova opção “Grounding” para trazer seus dados privados para suas interações de conversação automaticamente. O Elasticsearch agora faz parte desse recurso e pode ser usado por meio de:</p><ul><li><p><a href="https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/inference">APIs LLM da</a> Vertex AI, que enriquecem diretamente os modelos Gemini do Google no momento da geração (preferencial);</p></li><li><p><a href="https://cloud.google.com/generative-ai-app-builder/docs/grounded-gen">API Grounded Generation</a>, usada no ecossistema Vertex AI Agent Builder para criar experiências de agente.</p></li></ul><p>Com essa integração, o Elasticsearch — o <a href="https://www.elastic.co/pt/elasticsearch/vector-database">banco de dados vetorial</a> mais baixado e implantado — levará seus dados empresariais relevantes para onde forem necessários em seus chats internos com clientes finais, o que é crucial para a adoção real do GenAI em processos de negócios.</p><p>As APIs mencionadas permitirão que os desenvolvedores adotem esse novo recurso de parceria em seu código. No entanto, a engenharia e os testes rápidos continuam sendo etapas cruciais no desenvolvimento do aplicativo e servem como um playground de descoberta inicial. Para dar suporte a isso, o Elasticsearch foi projetado para fácil avaliação pelos usuários na ferramenta de console do Vertex AI Studio.</p><p>Bastam alguns passos simples para configurar os endpoints do Elastic com os parâmetros desejados (índice a ser pesquisado, número de documentos a serem recuperados e modelo de pesquisa desejado) na aba “Personalizar aterramento” na interface do usuário, conforme mostrado abaixo (observe que, para que isso funcione, você precisa digitar a chave da API com a palavra "ApiKey" na interface do usuário e nos exemplos de código abaixo). Agora você está pronto para gerar com seu conhecimento privado!</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7d313968acca1fea/6a17e624b1e113278079f250/69b3d979d18fa90742d9397f57975c586edf5d9f-1003x710.gif" alt="Modelos Vertex AI e Gemini do Google Cloud baseados em seus dados com o Elasticsearch" /><h2>Aplicações GenAI prontas para produção com facilidade</h2><p>A Elastic e o Google Cloud trabalham para proporcionar experiências completas, agradáveis e voltadas para o desenvolvedor. A conexão nativa ao Elastic no LLM e na Grounding Generation API reduz a complexidade e a sobrecarga ao criar aplicativos GAI no Vertex AI, evitando APIs adicionais desnecessárias e orquestração de dados durante o aterramento em apenas uma chamada unificada.</p><p>Vamos ver como isso funciona em ambos os cenários.</p><p>O primeiro exemplo é executado com a API LLM:</p>curl -X POST \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \https://us-central1-aiplatform.googleapis.com/v1beta1/projects/&lt;PROJECT_ID&gt;/locations/us-central1/publishers/google/models/gemini-2.0-flash-001:generateContent \
  -d '
{
  "contents": [
    {
      "role": "user",
      "parts": [
        {
          "text": "What's my company car policy?"
        }
      ]
    }
  ],
  "tools": [{
    "retrieval": {
      "externalApi": {
        "api_spec": "ELASTIC_SEARCH",
    "endpoint": "https://&lt;my-elastic-cluster&gt;.gcp.elastic-cloud.com:9243",
    "apiAuth": {
      "apiKeyConfig": {
            "apiKeyString": "ApiKey &lt;API_KEY&gt;"
      }
    },
    "elasticSearchParams": {
      "index": "&lt;my-index&gt;",
      "searchTemplate": "&lt;my-search-template&gt;"
    }
      }
    }
  }]
}<p>No exemplo acima, com o campo <code>retrieval</code> da API solicitando geração de conteúdo para o Gemini 2.0 Flash, podemos definir contextualmente um mecanismo de recuperação para a solicitação. Definir <code>api_spec</code> como “ELASTIC_SEARCH” permite o uso de parâmetros de configuração adicionais, como a chave de API e o ponto de extremidade do cluster (necessário para rotear uma solicitação para seu cluster Elastic), o índice do qual recuperar dados e o modelo de pesquisa a ser usado para sua lógica de pesquisa.</p><p>Da mesma forma, o mesmo resultado pode ser alcançado com a API Grounding Generation, definindo o parâmetro <code>groundingSpec</code> :</p>curl -X POST -H "Authorization: Bearer $(gcloud auth print-access-token)" -H "Content-Type: application/json" https://us-discoveryengine.googleapis.com/v1alpha/projects/&lt;PROJECT_ID&gt;/locations/global:generateGroundedContent -d '
{
  "contents": [{
    "role": "user",
    "parts": [{
      "text": "What do I need to patch a hole in my drywall?"
    }]
  }],
  "groundingSpec": {
    "groundingSources": [{
      "elasticSource": {
        "endpoint": "https://&lt;my-elastic-cluster&gt;.gcp.elastic-cloud.com:9243",
        "index": "&lt;my-index&gt;",
        "searchTemplate": "&lt;my-search-template",
        "apiKey": "projects/&lt;PROJECT_ID&gt;/secrets/api-key/versions/latest"
      }
    }]
  }
}
'<p>Com ambas as abordagens, a resposta fornecerá uma resposta com os documentos privados mais relevantes encontrados no Elasticsearch – e as fontes de dados conectadas relacionadas – para dar suporte à sua consulta.</p><p>Simplicidade, no entanto, não deve ser confundida com falta de personalização para atender às suas necessidades e casos de uso específicos. Pensando nisso, nós o projetamos para permitir que você adapte perfeitamente a configuração de pesquisa ao seu cenário.</p><h2>Pesquisa totalmente personalizável na ponta dos dedos: modelos de pesquisa</h2><p>Para fornecer a máxima personalização ao seu cenário de pesquisa, criamos, em colaboração com o Google Cloud, a experiência com base em nossos conhecidos <a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/current/search-template.html">Modelos de Pesquisa</a>. Os modelos de pesquisa do Elasticsearch são uma excelente ferramenta para criar consultas de pesquisa dinâmicas, reutilizáveis e fáceis de manter. Eles permitem que você predefina e reutilize estruturas de consulta. Eles são particularmente úteis ao executar consultas semelhantes com parâmetros diferentes, pois economizam tempo de desenvolvimento e reduzem a chance de erros. Os modelos podem incluir espaços reservados para variáveis, tornando as consultas dinâmicas e adaptáveis a diferentes requisitos de pesquisa.</p><p>Ao usar as APIs do Vertex AI e o Elasticsearch para aterramento, você deve referenciar um modelo de pesquisa desejado — conforme mostrado nos trechos de código acima — onde a lógica de pesquisa é implementada e enviada ao Elasticsearch. Usuários avançados do Elastic podem gerenciar, configurar e atualizar de forma assíncrona as abordagens de pesquisa e adaptá-las aos índices, modelos e dados específicos de forma totalmente transparente para usuários do Vertex AI, desenvolvedores de aplicativos da web ou engenheiros de IA, que precisam apenas especificar o nome do modelo na API de aterramento.</p><p>Este design permite personalização completa, colocando os amplos recursos de recuperação do Elasticsearch à sua disposição em um ambiente de IA do Google Cloud, ao mesmo tempo em que garante modularidade, transparência e facilidade de uso para diferentes desenvolvedores, mesmo aqueles não familiarizados com o Elastic.</p><p>Sempre que você precisar de pesquisa BM25, pesquisa semântica ou uma abordagem híbrida entre as duas (você já explorou <a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/current/retrievers-overview.html">os recuperadores</a> ? Técnicas de recuperação componíveis em uma única chamada de API de pesquisa), você pode definir sua lógica personalizada em um modelo de pesquisa, que o Vertex AI pode aproveitar automaticamente.</p><p>Isso também se aplica a incorporações e modelos de reclassificação que você escolhe para gerenciar vetores e resultados. Dependendo do seu caso de uso, você pode hospedar modelos nos nós de ML da Elastic, usar um ponto de extremidade de serviço de terceiros por meio da API de Inferência ou executar seu modelo local no local. Isso pode ser feito por meio de um modelo de pesquisa, e veremos como funciona na próxima seção.</p><h2>Comece com modelos de referência e depois crie os seus próprios</h2><p>Para ajudar você a começar rapidamente, fornecemos um conjunto de exemplos de modelos de pesquisa compatíveis para serem usados como referência inicial; você pode então modificar e criar seus próprios modelos personalizados com base em:</p><ul><li><p>Busca Semântica com modelo ELSER (vetores esparsos e fragmentação)</p></li><li><p>Pesquisa semântica com modelo multilíngue e5 (vetores densos e fragmentação)</p></li><li><p>Pesquisa híbrida com modelo de incorporação de texto Vertex AI</p></li></ul><p>Você pode encontrá-los neste <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/Cloud-Vertex-AI/search-templates">repositório do GitHub</a>.</p><p>Vejamos um exemplo: criar embeddings com as APIs Vertex AI do Google Cloud em um catálogo de produtos. Primeiro, precisamos criar o modelo de pesquisa no Elasticsearch, conforme mostrado abaixo:</p>PUT _scripts/google-template-knn
{
  "script": {
    "lang": "mustache",
    "source": {
      "_source": {
        "excludes": [ "title_embedding", "description_embedding", "images" ]
      },
        "size": "{{num_hits}}",
          "knn" : [
          { 
            "field": "description_embedding",
            "k": 5,
            "num_candidates": 10,
            "query_vector_builder": {
              "text_embedding": {
                "model_id": "googlevertexai_embeddings_004",
                "model_text": "{{query}}"
              }
            },
            "boost": 0.4
          },
          {
            "field": "title_embedding",
            "k": 5,
            "num_candidates": 10,
            "query_vector_builder": {
              "text_embedding": {
                "model_id": "googlevertexai_embeddings_004",
                "model_text": "{{query}}"
            }
          },
          "boost": 0.6
          }
          ]
    }  
  }
}<p>Neste exemplo, executaremos a pesquisa KNN em dois campos dentro de uma única pesquisa: <code>title_embedding</code> – o campo vetorial que contém o nome do produto – e <code>description_embedding</code> – aquele que contém a representação de sua descrição.</p><p>Você pode aproveitar a sintaxe <code>excludes</code> para evitar retornar campos desnecessários ao LLM, o que pode causar ruído em seu processamento e afetar a qualidade da resposta final. Em nosso exemplo, excluímos os campos contendo vetores e URLs de imagens.</p><p>Os vetores são criados dinamicamente no momento da consulta na entrada enviada por meio de um ponto de extremidade de inferência para a API de incorporação do Vertex AI, <code>googlevertexai_embeddings_004</code>, definida anteriormente da seguinte forma:</p>PUT /_inference/text_embedding/googlevertexai_embeddings_004
{
    "service": "googlevertexai",
    "service_settings": {
        "service_account_json": "&lt;your_service_account_key&gt;",
        "model_id": "text-embedding-004",
        "location": "us-central1",
        "project_id": "&lt;your_gcp_project&gt;"
    }
}<p>Você pode encontrar informações adicionais sobre como usar a API de Inferência da Elastic <a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/current/inference-apis.html">aqui</a>.</p><p>Agora estamos prontos para testar nossa pesquisa de modelo:</p>GET product-catalog-with-embeddings/_search/template
{
  "id": "google-template-knn",
  "params": {
    "query": "What do I need to patch a hole in my drywall?",
    "index_name": "product-catalog-with-embeddings",
    "num_hits": 3
  }
}<p>Os campos <code>params</code> substituirão as variáveis que definimos nos scripts de modelo entre colchetes duplos. Atualmente, as APIs Vertex AI LLM e Grounded Generation podem enviar ao Elastic as seguintes variáveis de entrada:</p><ul><li><p>“consulta” - a consulta do usuário a ser pesquisada</p></li><li><p>“index_name” - o nome do índice onde pesquisar</p></li><li><p>“num_hits” - quantos documentos queremos recuperar na saída final</p></li></ul><p>Aqui está um exemplo de saída:</p>{
        "_index": "product-catalog-with-embeddings",
        "_id": "9ZQCm5IBcrGI1ivqV-f_",
        "_score": 0.4925191,
        "_ignored": [
          "description.keyword",
          "images.keyword"
        ],
        "_source": {
          "description": "DAP Eclipse Rapid Wall Repair Patch is a new, revolutionary product solution for repairing drywall damage. No more waiting for spackling to dry or messy sanding. DAP Eclipse allows you to patch drywall damage and paint immediately, allowing you to finish your project faster. This all-in-1, mess free solution not only provides a permanent, long-lasting repair but also superior impact resistance for areas that may see reoccurring impact, such as behind a door.",
          "availability": "InStock",
          "model_id": "googlevertexai_embeddings_004",
          "title": "4 in. Eclipse Wall Repair Patch (2-Pack)",
          "url": "https://www.myDIYwebsite.com/p/DAP-4-in-Eclipse-Wall-Repair-Patch-2-Pack-7079809164/317967195",
          "price": 23.96,
          "product_id": 317967195,
          "currency": "USD",
          "brand": "DAP"
        }<p>A consulta acima é exatamente o que o Vertex AI do Google Cloud executará no Elasticsearch nos bastidores ao consultar o modelo de pesquisa criado anteriormente. Os modelos Gemini usarão os documentos de saída para fundamentar sua resposta: quando você perguntar "O que preciso para consertar minha parede de gesso?" em vez de receber uma sugestão genérica, o agente de chat fornecerá produtos específicos!</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte88a0c34242df6fa/6a17e6263e03d704504f2c2e/8c8b1374da7e9b2c17df8758acb448eed5b74e2d-1473x913.png" alt="Criando um prompt na plataforma Vertex AI do Google." /><h2>Jornada GenAI de ponta a ponta com Elastic e Google Cloud</h2><p>A Elastic faz parceria com o Google Cloud para criar experiências e soluções GenAI completas e prontas para produção. Como acabamos de ver, o Elastic é o primeiro ISV a ser integrado diretamente à interface do usuário e ao SDK da plataforma Vertex AI, permitindo prompts e agentes de modelos Gemini integrados e fundamentados usando nossos recursos de pesquisa de vetores. Além disso, o Elastic integra-se aos modelos de incorporação, reclassificação e conclusão do <a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/current/infer-service-google-vertex-ai.html">Vertex AI</a> e <a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/current/infer-service-google-ai-studio.html">do Google AI Studio</a>para criar e classificar vetores sem sair do cenário do Google Cloud, garantindo os princípios <a href="https://cloud.google.com/responsible-ai?hl=en">da IA responsável </a> . Ao oferecer suporte a abordagens multimodais, facilitamos conjuntamente aplicações em diversos formatos de dados.</p><p>Você pode ajustar, testar e exportar seu código de pesquisa GenAI por meio do nosso <a href="https://www.elastic.co/pt/search-labs/blog/vertex-ai-elasticsearch-playground-fast-rag-apps">Playground</a>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6a978835a5418eb9/6a17e628fbc5f8abff491a6a/b1fc48dba1713cd01c4d7f3296587d0c4e6e7e0c-862x651.png" alt="Jornada GenAI de ponta a ponta com Elastic e Google Cloud " /><p>Mas não se trata apenas de criar aplicativos de pesquisa: a Elastic utiliza os modelos Gemini para fortalecer as operações de TI, como nos <a href="https://www.elastic.co/pt/blog/elastic-google-vertex-ai-integration">recursos Elastic AI Assistants, Attack Discovery e Automatic Import</a>, reduzindo a fadiga diária de analistas de segurança e SREs em tarefas de baixo valor e permitindo que eles se concentrem em melhorar seus negócios. O Elastic também permite <a href="https://www.elastic.co/pt/guide/en/integrations/current/gcp_vertexai.html">o monitoramento abrangente do uso do Vertex AI</a>, rastreando métricas e logs, como tempos de resposta, tokens e recursos, para garantir o desempenho ideal. Juntos, gerenciamos o ciclo de vida completo do GenAI, desde a ingestão de dados e geração de incorporação até a consolidação com pesquisa híbrida, ao mesmo tempo em que garantimos a observabilidade e a segurança robustas das ferramentas GenAI com ações baseadas em LLM.</p><h2>Explore mais e experimente!</h2><p>Você está interessado em experimentar isso? O recurso está atualmente disponível em seus projetos do Google Cloud!</p><p>Se você ainda não o fez, uma das maneiras mais fáceis de começar a usar o Elastic Search AI Platform e explorar nossos recursos é com seu <a href="https://cloud.elastic.co/registration">teste gratuito do Elastic Cloud</a> ou assinando pelo <a href="https://console.cloud.google.com/marketplace/product/elastic-prod/elastic-cloud?pli=1">Google Cloud Marketplace</a>.</p><p><em>O lançamento e o cronograma de quaisquer recursos ou funcionalidades descritos nesta publicação permanecem a critério exclusivo da Elastic. Quaisquer recursos ou funcionalidades não disponíveis no momento podem não ser entregues no prazo ou não serem entregues. Elastic, Elasticsearch e marcas associadas são marcas comerciais, logotipos ou marcas registradas da Elasticsearch NV nos Estados Unidos e em outros países. Todos os outros nomes de empresas e produtos são marcas comerciais, logotipos ou marcas registradas de seus respectivos proprietários.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-google-cloud-vertex-ai-native-grounding</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-google-cloud-vertex-ai-native-grounding</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <dc:creator><![CDATA[Valerio Arvizzigno]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt973959b32f2070d6/6a17e62a6317304ec1585a5e/d1f1c8860f1f0b989ad698a882f869de7284ab78-1200x628.png" length="0" type="image/png"/>
    <pubDate>Wed, 09 Apr 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Acelerando a fusão de gráficos HNSW]]></title>
    <description><![CDATA[Explore o trabalho que temos feito para reduzir a sobrecarga de construção de vários gráficos HNSW, particularmente reduzindo o custo de mesclagem de gráficos.]]></description>
    <content:encoded><![CDATA[<p>No passado, <a href="https://www.elastic.co/pt/search-labs/blog/multi-graph-vector-search">discutimos</a> alguns dos desafios de ter que pesquisar em vários <a href="https://www.elastic.co/pt/search-labs/blog/hnsw-graph">grafos HNSW</a> e como conseguimos mitigá-los. Naquela ocasião, mencionamos algumas melhorias adicionais que tínhamos planejado. Este post é o culminar desse trabalho.</p><p>Você pode perguntar: por que usar vários gráficos? Este é um efeito colateral de uma escolha arquitetônica no Lucene: segmentos imutáveis. Como acontece com a maioria das escolhas arquitetônicas, há prós e contras. Por exemplo, recentemente disponibilizamos o Elasticsearch sem servidor. Nesse contexto, obtivemos benefícios muito significativos de segmentos imutáveis, incluindo replicação de índice eficiente e a capacidade de desacoplar índice e computação de consulta e dimensioná-los automaticamente de forma independente. Para quantização vetorial, as fusões de segmentos nos dão a oportunidade de atualizar parâmetros para adaptá-los às características dos dados. Nessa linha, acreditamos que há outras vantagens proporcionadas pela oportunidade de medir características de dados e revisitar opções de indexação.</p><p>Nesta postagem, discutiremos o trabalho que temos feito para reduzir significativamente a sobrecarga de construção de vários gráficos HNSW e, em particular, para reduzir o custo de mesclagem de gráficos.</p><h3>Histórico</h3><p>Para manter um número gerenciável de segmentos, o Lucene verifica periodicamente se deve mesclar segmentos. Isso equivale a verificar se a contagem de segmentos atual excede uma contagem de segmentos de destino, que é determinada pelo tamanho do segmento base e pela política de mesclagem. Se a contagem for excedida, o Lucene mescla grupos de segmentos enquanto a restrição for violada. Este processo foi descrito em detalhes <a href="https://blog.mikemccandless.com/2011/02/visualizing-lucenes-segment-merges.html">em outro lugar</a>.</p><p>Lucene opta por mesclar segmentos de tamanhos semelhantes porque isso atinge um crescimento logarítmico na amplificação de gravação. No caso de um índice vetorial, a amplificação de escrita é o número de vezes que um vetor será inserido em um gráfico. O Lucene tentará mesclar segmentos em grupos de aproximadamente 10. Consequentemente, os vetores são inseridos em um gráfico aproximadamente vezes, onde  é a contagem do vetor de índice e  é a contagem do vetor de segmento base esperado. Devido ao crescimento logarítmico, a amplificação da gravação é de um dígito, mesmo para índices grandes. Entretanto, o tempo total gasto na fusão de gráficos é linearmente proporcional à amplificação da gravação.</p><p>Ao mesclar gráficos HNSW, já fazemos uma pequena otimização: mantendo o gráfico do maior segmento e inserindo vetores dos outros segmentos nele. Esta é a razão do fator 9/10 acima. Abaixo mostramos como podemos melhorar significativamente usando informações de todos os gráficos que estamos mesclando.</p><h3>Fusão de gráficos HNSW</h3><p>Anteriormente, mantínhamos o grafo maior e inseríamos vetores dos demais, ignorando os grafos que os continham. A principal conclusão que utilizamos a seguir é que cada grafo HNSW descartado contém informações importantes sobre a proximidade dos vetores que ele contém. Gostaríamos de usar essas informações para acelerar a inserção, pelo menos de alguns, dos vetores.</p><p>Nós nos concentramos no problema de inserir um gráfico menor  em um gráfico maior , já que esta é uma operação atômica que podemos usar para construir qualquer política de mesclagem.</p><p>A estratégia é encontrar um subconjunto de vértices de  para inserir no gráfico grande. Em seguida, usamos a conectividade desses vértices no pequeno gráfico para acelerar a inserção dos vértices restantes . A seguir, usamos  e  para denotar os vizinhos de um vértice  no gráfico pequeno e grande, respectivamente. Esquematicamente o processo é o seguinte.</p><p><code>MERGE-HNSW</code></p><p><code>Inputs </code><code> and </code></p><p><code>1</code><code>Find </code><code> to insert into </code><code> using COMPUTE-JOIN-SET</code>
<code>2</code><code>Insert each vertex </code><code> into </code>
<code>3</code><code>for </code><code> do</code>
<code>4</code>
<code>5</code>
<code>6</code><code>FAST-SEARCH-LAYER</code>
<code>7</code><code>SELECT-NEIGHBORS-HEURISTIC</code>
<code>8</code></p><p>Calculamos o conjunto  usando um procedimento que discutiremos abaixo (linha 1). Em seguida, inserimos cada vértice em  no gráfico grande usando o procedimento de inserção padrão HNSW (linha 2). Para cada vértice que não inserimos, encontramos seus vizinhos que inserimos e seus vizinhos no gráfico grande (linhas 4 e 5). Usamos um procedimento <code>FAST-SEARCH-LAYER</code> semeado com este conjunto (linha 6) para encontrar os candidatos para o <code>SELECT-NEIGHBORS-HEURISTIC</code> do <a href="https://arxiv.org/pdf/1603.09320">artigo</a> HNSW (linha 7). Na verdade, estamos substituindo <code>SEARCH-LAYER</code> para encontrar o conjunto de candidatos no método <code>INSERT</code> (Algoritmo 1 do artigo), que de outra forma não será alterado. Por fim, adicionamos o vértice que acabamos de inserir em  (linha 8).</p><p>É claro que para que isso funcione, cada vértice em  deve ter pelo menos um vizinho em . Na verdade, exigimos que para cada vértice em  que  para algum , a conectividade máxima da camada. Observamos que em gráficos HNSW reais vemos uma grande dispersão de graus de vértices. A figura abaixo mostra uma função de densidade cumulativa típica do grau do vértice para a camada inferior de um gráfico Lucene HNSW.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc815e1a9c3bb0d06/6a17e178ec0f89308a5a6564/44f001b3a1bc6627e172fed5c52a02cfdcb4cd66-1324x898.png" alt="Gráfico HNSW: Exemplo de distribuição de graus dos vértices" /><p>Exploramos o uso de um valor fixo para  e também o tornamos uma função do grau do vértice. Esta segunda opção leva a maiores acelerações com impacto mínimo na qualidade do gráfico, então optei pela seguinte opção</p><p>Observe que | é igual ao grau do vértice  no gráfico pequeno por definição. Ter um limite inferior de dois significa que inseriremos todos os vértices cujo grau seja menor que dois.</p><p>Um argumento de contagem simples sugere que se escolhermos  cuidadosamente, precisamos apenas inserir em torno de  em  diretamente. Especificamente, colorimos uma aresta do gráfico se inserirmos exatamente um de seus vértices finais em . Então sabemos que para cada vértice em  ter pelo menos  vizinhos em  precisamos colorir pelo menos  arestas. Além disso, esperamos que</p><p>Aqui,  é o grau médio do vértice no pequeno gráfico. Para cada vértice  colorimos no máximo  arestas. Portanto, o número total de arestas que esperamos colorir é no máximo . Esperamos que, ao escolher  cuidadosamente, possamos colorir perto desse número de arestas e, portanto, para cobrir todos os vértices,  precisa satisfazer</p><p>Isso implica que .</p><p>Se <code>SEARCH-LAYER</code> dominar o tempo de execução, isso sugere que poderíamos atingir uma velocidade de até  maior no tempo de mesclagem. Dado o crescimento logarítmico da amplificação de gravação, isso significa que mesmo para índices muito grandes, normalmente dobraríamos apenas o tempo de construção em comparação à construção de um gráfico.</p><p>O risco dessa estratégia é que prejudicamos a qualidade do gráfico. Inicialmente tentamos com um no-op <code>FAST-SEARCH-LAYER</code>. Descobrimos que isso degrada a qualidade do gráfico a ponto de a recuperação como função da latência ser impactada, principalmente ao mesclar em um único segmento. Em seguida, exploramos várias alternativas usando uma busca limitada do gráfico. No final, a escolha mais eficaz foi a mais simples. Use <code>SEARCH-LAYER</code> mas com um <code>ef_construction</code> baixo. Com essa parametrização conseguimos obter gráficos de excelente qualidade e ainda diminuir o tempo de mesclagem em pouco mais de 30% em média.</p><h3>Calculando o conjunto de junção</h3><p>Encontrar um bom conjunto de junção pode ser formulado como um problema de cobertura de grafos HNSW. Uma heurística gulosa é uma heurística simples e eficaz para aproximar coberturas ótimas de grafos. A abordagem que adotamos seleciona os vértices um de cada vez para adicionar a  em ordem decrescente de ganho. O ganho é definido da seguinte forma:</p><p>Aqui,  denota a contagem de vizinhos de um vetor  em  e  é a função indicadora. O ganho inclui a mudança na contagem do vértice que adicionamos a , ou seja, , pois nos aproximamos do nosso objetivo adicionando um vértice menos coberto. O cálculo do ganho é ilustrado na figura abaixo para o vértice central laranja.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7a353d56fa09af5f/6a17e17a2f4a5c33f7fa8825/fe11cd55a7d94d9e0b0f4d47ea309a99075c355d-540x474.png" alt="Ganho de vértice a ser adicionado para unir o conjunto J no grafo HNSW" /><p>Mantemos o seguinte estado para cada vértice :</p><ol><li><p>Seja velho,</p></li><li><p>Seu ganho ,</p></li><li><p>A contagem de vértices adjacentes em  denotada por ,</p></li><li><p>Um número aleatório no intervalo [0,1] que é usado para desempate.</p></li></ol><p>O pseudocódigo para calcular o conjunto de junções é o seguinte.</p><p><code>COMPUTE-JOIN-SET</code></p><p><code>Inputs </code></p><p><code>1</code>
<code>2</code>
<code>3</code><code>for </code><code> do</code>
<code>4</code>
<code>5</code>
<code>6</code>
<code>7</code><code>while </code><code> do
8</code><code> maximum gain vertex in </code>
<code>9</code><code>Remove the state for </code><code> from </code>
<code>10</code><code>if </code><code> is not stale then</code>
<code>11</code>
<code>12</code>
<code>13</code><code>for </code><code> do</code>
<code>14</code><code>mark </code><code> as stale if </code>
<code>15</code><code>mark neighbors of </code><code> stale if </code><code>
16</code><code>
17</code><code>else
18</code><code>
19</code><code>if </code><code> then
20</code><code>
21</code><code>return </code></p><p>Primeiro inicializamos o estado nas linhas 1-5.</p><p>Em cada iteração do loop principal, extraímos inicialmente o vértice de ganho máximo (linha 8), desfazendo empates aleatoriamente. Antes de fazer qualquer alteração, precisamos verificar se o ganho do vértice está obsoleto. Em particular, cada vez que adicionamos um vértice em  , afetamos o ganho de outros vértices:</p><ol><li><p>Como todos os seus vizinhos têm um vizinho adicional em  seus ganhos podem mudar (linha 14)</p></li><li><p>Se algum dos seus vizinhos estiver agora totalmente coberto, os ganhos de todos os seus vizinhos podem mudar (linhas 14-16)</p></li></ol><p>Nós recalculamos os ganhos de forma lenta, então só recalculamos o ganho de um vértice se quisermos inseri-lo em  (linhas 18-20). Como os ganhos sempre diminuem, nunca podemos perder um vértice que devemos inserir.</p><p>Observe que precisamos apenas monitorar o ganho total de vértices que adicionamos a  para determinar quando sair. Além disso, enquanto  pelo menos um vértice terá ganho diferente de zero, então sempre progredimos.</p><h3>Resultados</h3><p>Realizamos experimentos em quatro conjuntos de dados que juntos abrangem nossas três métricas de distância suportadas (euclidiana, cosseno e produto interno):</p><ol><li><p>quora-E5-small: 522931 documentos, 384 dimensões e usa similaridade de cosseno,</p></li><li><p>cohere-wikipedia-v2: 1 milhão de documentos, 768 dimensões e usa similaridade de cosseno,</p></li><li><p>gist: 1M documentos, 960 dimensões e utiliza distância euclidiana e</p></li><li><p>cohere-wikipedia-v3: 1 milhão de documentos, 1.024 dimensões e usa o produto interno máximo.</p></li></ol><p>Para cada conjunto de dados, avaliamos dois níveis de quantização:</p><ol><li><p>int8 – que usa um inteiro de 1 byte por dimensão e</p></li><li><p>Churrasco – que usa um único bit por dimensão.</p></li></ol><p>Por fim, para cada experimento, avaliamos a qualidade da pesquisa em duas profundidades de recuperação e examinamos depois da construção do índice e depois da fusão forçada em um único segmento.</p><p>Em resumo, alcançamos acelerações substanciais e consistentes na indexação e mesclagem, mantendo a qualidade do gráfico e, consequentemente, o desempenho da pesquisa em todos os casos.</p><h4>Experimento 1: quantização int8</h4><p>As acelerações médias da linha de base até o candidato, as mudanças propostas, são:</p><p>Aceleração do tempo de índice: <strong>1,28</strong></p><p>Forçar aceleração de fusão: <strong>1,72</strong></p><p>Isso corresponde à seguinte repartição nos tempos de execução</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8422be122ef1a9c9/6a17e17bbe6086522e00467d/5f9f6d487b4c74c4998aa47465912c1a9743e928-734x479.png" alt="Tempos de indexação e mesclagem para as estratégias de mesclagem de base e candidatas" /><p>Para completar, os horários exatos são</p><p></p><p>Índice</p><p></p><p>Mesclar</p><p></p><p>Conjunto de dados</p><p>linha de base</p><p>candidato</p><p>Criar</p><p>candidato</p><p>quora-E5-pequeno</p><p>112,41s</p><p>81,55s</p><p>113,81s</p><p>70,87s</p><p>wiki-cohere-v2</p><p>158,1s</p><p>122,95s</p><p>425,20s</p><p>239,28s</p><p>essência</p><p>141,82s</p><p>119,26s</p><p>536,07s</p><p>279,05s</p><p>wiki-cohere-v3</p><p>211,86s</p><p>168,22s</p><p>654,97s</p><p>414,12s</p><p>Abaixo, mostramos os gráficos de recall versus latência que comparam o candidato (linhas tracejadas) à linha de base em duas profundidades de recuperação: recall@10 e recall@100 para índices com vários segmentos (o resultado final da nossa estratégia de mesclagem padrão após indexar todos os vetores) e após a mesclagem forçada para um único segmento. Uma curva mais alta e mais à esquerda é melhor, o que significa maior recuperação com menor latência.</p><p>Como você pode ver, para índices de múltiplos segmentos, o candidato é melhor para o conjunto de dados Cohere v3 e um pouco pior, mas quase comparável, para todos os outros conjuntos de dados. Após a fusão em um único segmento, as curvas de recall são quase idênticas para todos os casos.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf674622469bf6a90/6a17e17dfbc5f874a84919c9/9195297f19f90b172d832ba56bdada7b0d8a768b-985x392.png" alt="Lembre-se de @10 e @100 vs latência após a construção do índice" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltde89f7e94ac823f4/6a17e17fe9ea876429a9c507/c866f32d579ae2b841e92ca733dd29c0e214ac6b-986x386.png" alt="Lembre-se de @10 e @100 vs latência após a fusão em um único segmento" /><h4>Experimento 2: quantização BBQ</h4><p>As acelerações médias da linha de base até o candidato são:</p><p>Aceleração do tempo de índice: <strong>1,33</strong></p><p>Forçar aceleração de fusão: <strong>1,34</strong></p><p>Isso corresponde à seguinte repartição nos tempos de execução</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5b0eb305222fa5c2/6a17e18125daab514f08a18c/11be0cf6b63480dc703409329357ea4847b55051-740x415.png" alt="Índice e tempo de mesclagem para as estratégias de mesclagem de base e candidatas" /><p>Para completar, os horários exatos são</p><p></p><p>Índice</p><p></p><p>Mesclar</p><p></p><p>Conjunto de dados</p><p>linha de base</p><p>candidato</p><p>Criar</p><p>candidato</p><p>quora-E5-pequeno</p><p>70,71s</p><p>58,25s</p><p>59,38s</p><p>40,15s</p><p>wiki-cohere-v2</p><p>203,08s</p><p>142,27s</p><p>107,27s</p><p>85,68s</p><p>essência</p><p>110,35s</p><p>105,52s</p><p>323,66s</p><p>202,2s</p><p>wiki-cohere-v3</p><p>313,43s</p><p>190,63s</p><p>165,98s</p><p>159,95s</p><p>Para índices de múltiplos segmentos, o candidato é melhor para quase todos os conjuntos de dados, exceto o Cohere v2, onde a linha de base é ligeiramente melhor. Para os índices de segmento único, as curvas de recall são quase idênticas para todos os casos.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb12174abeadbcdd1/6a17e1822f4a5c4cc2fa8829/7da1be27bab5a7f5f9c9a1882ea35761a4a0ba5c-973x383.png" alt="Lembre-se de @10 e @100 vs latência após a construção do índice" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf1b88d4f7fec9905/6a17e184414c64a8de9450b4/a26af30a56c55a12addee7e4fb7e8f606f366668-979x386.png" alt="Lembre-se de @10 e @100 vs latência tendo sido mesclados em um único segmento" /><h3>Conclusão</h3><p>O algoritmo discutido neste blog estará disponível no próximo Lucene 10.2 e na versão do Elasticsearch baseada nele. Os usuários poderão aproveitar o desempenho de mesclagem aprimorado e o tempo reduzido de criação de índice nessas novas versões. Essa mudança faz parte do nosso esforço contínuo para tornar o Lucene e o Elasticsearch rápidos e eficientes para pesquisas vetoriais e híbridas.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/hnsw-graphs-speed-up-merging</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/hnsw-graphs-speed-up-merging</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <category><![CDATA[Lucene]]></category>
    <dc:creator><![CDATA[Thomas Veasey,Mayya Sharipova]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9fca6a01d0541cfb/6a17e187033c8d46486bb0e1/49a6c880f5dedd0fa502ece5be124824ee218cc0-1792x1024.png" length="0" type="image/png"/>
    <pubDate>Mon, 07 Apr 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Redimensionamento de modelos de interação tardia no Elasticsearch - parte 2]]></title>
    <description><![CDATA[Este artigo explora técnicas para redimensionar vetores de interação tardia para cargas de trabalho de produção em grande escala, como a redução do uso de espaço em disco e a melhoria da eficiência computacional.]]></description>
    <content:encoded><![CDATA[<p>Em nosso <a href="https://www.elastic.co/search-labs/blog/elastiacsearch-colpali-document-search">blog anterior sobre o ColPali</a>, exploramos como criar aplicações de busca visual com o Elasticsearch. O foco principal foi o valor que modelos como o ColPali agregam às aplicações, mas eles apresentam desvantagens de performance em comparação com a busca vetorial usando bi-encoders, como o E5.</p><p>Partindo dos exemplos da <a href="https://www.elastic.co/search-labs/blog/elastiacsearch-colpali-document-search">parte 1</a>, este post explora como usar diferentes técnicas e o conjunto avançado de ferramentas de busca vetorial do Elasticsearch para preparar vetores de interação tardia para cargas de trabalho de produção em grande escala.</p><p>Os exemplos completos de código estão disponíveis no <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/colpali">GitHub.</a></p><h2>Desafios dos modelos de interação tardia</h2><p>O ColPali cria mais de 1.000 vetores por página para os documentos do nosso índice.</p><p>Isso resulta em dois desafios ao trabalhar com vetores de interação tardia:</p><ol><li><p>Espaço em disco: salvar todos esses vetores em disco gera um volume significativo de armazenamento, o que se torna caro em ambientes de grande escala.</p></li><li><p>Computação: ao classificar documentos usando a comparação <code>maxSimDotProduct()</code>, precisamos comparar todos esses vetores de cada documento com os N vetores da consulta.</p></li></ol><p>Vamos analisar algumas técnicas para lidar com esses desafios.</p><h2>Técnicas para otimizar modelos de interação tardia</h2><h3>Vetores de bits</h3><p>Para reduzir o espaço em disco, podemos comprimir as imagens em vetores de bits. Podemos usar uma função simples em Python para transformar nossos multivetores em vetores de bits:</p>def to_bit_vectors(embeddings: list) -&gt; list:
    return [
        np.packbits(np.where(np.array(embedding) &gt; 0, 1, 0))
        .astype(np.int8)
        .tobytes()
        .hex()
        for embedding in embeddings
    ]<p>O conceito central da função é simples: valores acima de 0 se tornam 1, e valores abaixo de 0 se tornam 0. Isso resulta em uma matriz de 0s e 1s, que depois transformamos em uma string hexadecimal que representa nosso vetor de bits.</p><p>Para o mapeamento de índice, configuramos o parâmetro <code>element_type</code> para <code>bit</code>:</p>mappings = {
    "mappings": {
        "properties": {
            "col_pali_vectors": {
                "type": "rank_vectors",
                "element_type": "bit"
            }
        }
    }
}

es.indices.create(index=INDEX_NAME, body=mappings)<p>Depois de gravar todos os novos vetores de bits no índice, podemos ranquear nossos vetores de bits usando o código a seguir:</p>query = "What do companies use for recruiting?"
query_vector = to_bit_vectors(create_col_pali_query_vectors(query))
es_query = {
    "_source": False,
    "query": {
        "script_score": {
            "query": {
                "match_all": {}
            },
            "script": {
                "source": "maxSimInvHamming(params.query_vector, 'col_pali_vectors')",
                "params": {
                    "query_vector": query_vector
                }
            }
        }
    },
    "size": 5
}<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcaf0c8f999d68701/6a17f46696142a6f46eb1c56/51b989446e4099745971e1eac27d147a78d13e0a-1600x480.png" alt="" /><p>Ao abrir mão de um pouco de precisão, isso nos permite usar a distância de Hamming (<code>maxSimInvHamming(...)</code>), que consegue explorar otimizações como bit masks, SIMD, entre outras. Saiba mais sobre <a href="https://www.elastic.co/search-labs/blog/bit-vectors-in-elasticsearch">vetores de bits e distância de Hamming em nosso blog</a>.</p><p>Como alternativa, podemos não converter o vetor de consulta em vetores de bits e realizar a busca usando o vetor de interação tardia com fidelidade total:</p>query = "What do companies use for recruiting?"
query_vector = create_col_pali_query_vectors(query)
es_query = {
    "_source": False,
    "query": {
        "script_score": {
            "query": {
                "match_all": {}
            },
            "script": {
                "source": "maxSimDotProduct(params.query_vector, 'col_pali_vectors')",
                "params": {
                    "query_vector": query_vector
                }
            }
        }
    },
    "size": 5
}<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2fa6c89fb451b6e0/6a17f468af47b65da9cde0d9/89f3c795c6dc44b2f7d46801320288316b47e1b2-1600x488.png" alt="Resultados do uso de vetores de bits para otimizar modelos de interação tardia" /><p>Nesse caso, os vetores são comparados usando uma função de similaridade assimétrica.</p><p></p><p>Vamos considerar uma distância de Hamming padrão entre dois vetores de bits. Suponha que temos um vetor de documento <em>D:</em></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd4b6ece1ec4dedac/6a17f4694b055d248143234e/366a70a23e37d403788327b7aefc873fd4482f5e-1235x86.png" alt="" /><p>E um vetor de consulta <em>Q:</em></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7721df9c8f0d84f1/6a17f46b3e03d77cf54f2dcb/978ec31e0afbc0eaebf548a015b628c0cad84625-1247x84.png" alt="" /><p></p><p>A quantização binária simples transforma o vetor <em>D</em> em <code>10101101</code> e o vetor <em>Q</em> em <code>11111011</code>. Para calcular a distância de Hamming, precisamos apenas de operações diretas em bits, algo extremamente rápido. Nesse caso, a distância de Hamming é <code>01010110</code>, que possui uma contagem de bits igual a 4.Assim, a pontuação passa a ser o inverso dessa distância de Hamming. Lembre-se de que vetores mais semelhantes têm uma distância de Hamming MENOR, portanto inverter esse valor permite que vetores mais semelhantes recebam pontuações mais altas. Especificamente aqui, a pontuação seria 1/4 = <code>0.25</code>.</p><p>No entanto, observe que perdemos a magnitude de cada dimensão. Um <code>1</code> é um <code>1</code>. Assim, para <em>Q</em>, a diferença entre <code>0.01</code> e <code>0.79</code> desaparece. Como estamos simplesmente quantizando de acordo com <code>&gt;0</code>, podemos aplicar um pequeno truque em que o vetor Q não é quantizado. Isso não permite a matemática bit a bit extremamente rápida, mas mantém o custo de armazenamento baixo, já que D continua quantizado.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blted50315518e2d599/6a17f46c414c6463709452d3/508e8498ba969534d2a0131d431c75735fc27cb9-1399x611.png" alt="" /><p>Em resumo, essa abordagem preserva as informações contidas em <em>Q</em>, aumentando a qualidade da estimativa de distância e mantendo baixo o custo de armazenamento.</p><p>O uso de vetores de bits permite economizar significativamente espaço em disco e reduzir a carga computacional no momento da consulta. Mas ainda há mais que podemos fazer.</p><h3>Vetores médios</h3><p>Para redimensionar a busca para centenas de milhares de documentos, mesmo os ganhos de desempenho proporcionados pelos vetores de bits não serão suficientes. Para dar conta desse tipo de carga, será necessário aproveitar a estrutura de índice HNSW do Elasticsearch para busca vetorial.</p><p>O ColPali gera cerca de 1.000 vetores por documento, o que é excessivo para adicionar ao grafo HNSW. Portanto, precisamos reduzir a quantidade de vetores. Para isso, podemos criar uma única representação do significado do documento calculando a média de todos os vetores do documento produzidos pelo ColPali quando incorporamos a imagem.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc053425fbfcf8de8/6a17f46f148009faf1b488aa/7c3b4dffb70bd95f67deb35f8c01e73c5286c2ab-1476x1102.png" alt="Vetor médio considerando todos os vetores de interação tardia" /><p>No momento, isso não é possível diretamente no Elastic, sendo necessário pré-processar os vetores antes de ingeri-los no Elasticsearch. </p><p>Isso pode ser feito com o Logstash ou com pipelines de ingestão, mas aqui usaremos uma função simples em Python:</p>def to_avg_vector(vectors):
    vectors_array = np.array(vectors)
    
    avg_vector = np.mean(vectors_array, axis=0)
    
    norm = np.linalg.norm(avg_vector)
    if norm &gt; 0:
        normalized_avg_vector = avg_vector / norm
    else:
        normalized_avg_vector = avg_vector

    return normalized_avg_vector.tolist()<p>Também normalizamos o vetor para que possamos usar a similaridade por produto escalar.</p><p>Depois de transformar todos os vetores do ColPali em vetores médios, podemos indexá-los no campo dense_vector:</p>mappings = {
    "mappings": {
        "properties": {
            "avg_vector": {
                "type": "dense_vector",
                "dims": 128,
                "index": True,
                "similarity": "dot_product"
            },
            "col_pali_vectors": {
                "type": "rank_vectors",
                "element_type": "bit"
            }
        }
    }
}

es.indices.create(index=INDEX_NAME, body=mappings)<p>Precisamos considerar que isso aumentará o uso total de disco, pois estamos salvando mais informações junto com nossos vetores de interação tardia. Além disso, usaremos RAM adicional para manter o grafo HNSW, o que nos permite redimensionar a busca para bilhões de vetores. Para reduzir o uso de RAM, podemos recorrer ao nosso conhecido <a href="https://www.elastic.co/search-labs/blog/optimized-scalar-quantization-elasticsearch">recurso BBQ</a>. Com isso, obtemos resultados de busca rápidos em conjuntos de dados massivos que, de outra forma, não seriam viáveis.</p><p>Agora, simplesmente fazemos a busca com a consulta knn para encontrar os documentos mais relevantes.</p>query = "What do companies use for recruiting?"
query_vector = to_avg_vector(create_col_pali_query_vectors(query))
es_query = {
    "_source": False,
    "knn": {
        "field": "avg_vector",
        "query_vector": query_vector,
        "k": 10,
        "num_candidates": 100
    },
    "size": 5
}<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf4ac6b7fd444f935/6a17f471a29299d76ad02dc0/a500f9907020f032c3b1c7a48ed8c759dc2a1dd4-1600x498.png" alt="" /><p>O que antes era a melhor correspondência acabou caindo para a 3ª posição.</p><p>Para corrigir esse problema, podemos usar uma recuperação em vários estágios. No primeiro estágio, usamos a consulta knn para buscar os melhores candidatos para a consulta em meio a milhões de documentos. No segundo estágio, fazemos a reclassificação apenas dos top k (neste caso, 10) usando a maior fidelidade dos vetores de interação tardia do ColPali. </p>query = "What do companies use for recruiting?"
col_pali_vector = create_col_pali_query_vectors(query)
avg_vector = to_avg_vector(col_pali_vector)
es_query = {
  "_source": False,
  "retriever": {
    "rescorer": {
      "retriever": {
        "knn": {
          "field": "avg_vector",
          "query_vector": avg_vector,
          "k": 10,
          "num_candidates": 100
        }
      },
      "rescore": {
        "window_size": 10,
        "query": {
          "rescore_query": {
            "script_score": {
              "query": {
                "match_all": {}
              },
              "script": {
                "source": "maxSimDotProduct(params.query_vector, 'col_pali_vectors')",
                "params": {
                  "query_vector": col_pali_vector
                }
              }
            }
          }
        }
      }
    }
  },
  "size": 5
}<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt265edd5f37be40b7/6a17f4733e9e45583dba15e6/797f490860af87fcf29f34668a5c4511419c6fd0-1600x501.png" alt="Resultados do uso de vetores médios para otimizar modelos de interação tardia" /><p>Aqui, estamos usando o <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.18/retriever.html#rescorer-retriever">rescore retriever,</a> introduzido na versão 8.18, para reclassificar nossos resultados. Após a reclassificação, vemos que a melhor correspondência volta a ocupar a primeira posição. </p><p>Observação: em uma aplicação de produção, podemos usar um valor de k muito maior que 10, já que a função max sim ainda é relativamente eficiente em termos de performance.</p><h3>Agrupamento de tokens</h3><p>O agrupamento de tokens reduz o comprimento da sequência de embeddings multivetoriais ao agrupar informações redundantes, como regiões de fundo branco. Essa técnica diminui o número de embeddings ao mesmo tempo que preserva a maior parte do sinal da página.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3d643e6ac908f640/6a17f4754b055d3783432352/09eae0b768f4b450e555d7e53e57561bd52de2c0-1412x1056.png" alt="Agrupamento de tokens para otimizar modelos de interação tardia" /><p>O agrupamento de tokens funciona reunindo embeddings de tokens semelhantes dentro de um documento em clusters, usando um algoritmo de clusterização. Em seguida, calcula-se a média dos vetores em cada cluster para criar uma única representação agregada. Esse vetor agregado substitui os tokens originais do grupo, reduzindo o número total de vetores sem perda significativa do sinal do documento.</p><p>O artigo do ColPali propõe um valor inicial de agrupamento factor igual a 3 para a maioria dos conjuntos de dados, mantendo 97,8% da performance original ao mesmo tempo que reduz o número total de vetores em 66,7%. </p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2d78a15c6944a1ea/6a17f477414c64a2929452d9/343cc9eaf54af8c7a125d4115838f3c7d5659de0-1600x1007.png" alt="Fator de agrupamento para otimizar modelos de interação tardia" /><p>Mas é preciso cautela: o conjunto de dados Shift, que contém documentos muito densos e com grande volume de texto e pouco espaço em branco, apresenta uma queda rápida de desempenho conforme o fator de agrupamento aumenta.</p><p>Para criar os vetores agrupados, podemos usar a biblioteca colpali_engine:</p>from colpali_engine.compression.token_pooling import HierarchicalTokenPooler

pooler = HierarchicalTokenPooler(pool_factor=3) # test on your data for a good pool_factor

def pool_vectors(embedding: list) -&gt; list:
    tensor = torch.tensor(embedding).unsqueeze(0)
    pooled = pooler.pool_embeddings(tensor)
    return pooled.squeeze(0).tolist()<p>Agora temos um vetor que teve suas dimensões reduzidas em cerca de 66,7%. Nós o indexamos normalmente e conseguimos realizar buscas usando nossa função <code>maxSimDotProduct()</code>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc460c14031814ede/6a17f4794202292bf629f72d/2412c5db7d79a590b96d42fe01f140c42f010612-1600x481.png" alt="Resultados dos modelos de interação tardia" /><p>Conseguimos obter bons resultados de busca ao custo de uma leve perda de precisão nos resultados.</p><p>Dica: com um pool_factor mais alto (100-200), também é possível encontrar um meio-termo entre a solução de vetor médio e a abordagem discutida aqui. Com cerca de 5 a 10 vetores por documento, torna-se viável indexá-los em um campo aninhado para aproveitar o índice HNSW.</p><h2>Cross-encoder vs. interação tardia vs. bi-encoder</h2><p>Com tudo o que aprendemos até aqui, onde isso posiciona os modelos de interação tardia, como ColPali ou ColBERT, em comparação com outras técnicas de recuperação baseadas em IA?</p><p>Embora a função max sim seja mais barata do que o uso de cross-encoders, ela ainda exige muito mais comparações e processamento do que a busca vetorial com bi-encoders, em que comparamos apenas dois vetores para cada par consulta-documento. </p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbfd97f3bba3e5b17/6a17f47be31791b0052d5943/75e1fc9e601aa7a6e88f137565c1919166c7a71c-1480x458.png" alt="Cross-encoder vs. modelos de interação tardia vs. bi-encoder" /><p>Por isso, nossa recomendação para modelos de interação tardia é, em geral, usá-los apenas para reclassificação dos primeiros k resultados de busca. Também refletimos isso no nome do tipo de campo: rank_vectors.</p><p>Mas e o cross-encoder? Modelos de interação tardia são melhores por serem mais baratos de executar no momento da consulta? Como acontece com frequência, a resposta é: depende. Os cross-encoders normalmente produzem resultados de maior qualidade, mas exigem muito processamento, já que cada par consulta-documento precisa passar completamente pelo modelo transformer. Eles também se beneficiam do fato de não exigirem indexação de vetores e poderem operar de forma stateless. Isso resulta em:</p><ul><li><p>Menor uso de espaço em disco</p></li><li><p>Um sistema mais simples</p></li><li><p>Maior qualidade nos resultados de busca</p></li><li><p>Maior latência, o que limita a profundidade da reclassificação</p></li></ul><p>Por outro lado, os modelos de interação tardia podem deslocar parte desse custo computacional para o momento da indexação, tornando a consulta mais barata. O preço a pagar é a necessidade de indexar vetores, o que torna os pipelines de indexação mais complexos e exige mais espaço em disco para armazená-los.</p><p>No caso específico do ColPali, a análise de informações provenientes de imagens é muito custosa, já que elas contêm grandes volumes de dados. Nesse cenário, o equilíbrio pende a favor do uso de um modelo de interação tardia como o ColPali, pois avaliar essas informações no momento da consulta seria lento demais e exigiria muitos recursos. </p><p>Já para um modelo de interação tardia como o ColBERT, que trabalha com dados textuais, assim como a maioria dos cross-encoders, por exemplo o elastic-rerank-v1, a decisão pode favorecer o uso do cross-encoder, aproveitando a economia de disco e a maior simplicidade operacional.</p><p>Recomendamos que você avalie esses prós e contras no seu caso de uso e experimente as diferentes ferramentas que o Elasticsearch oferece para criar as melhores aplicações de busca.</p><h2>Conclusão</h2><p>Neste blog, exploramos várias técnicas para otimizar modelos de interação tardia, como o ColPali, para busca vetorial em grande escala no Elasticsearch. Embora esses modelos ofereçam um forte equilíbrio entre eficiência de recuperação e qualidade de classificação, eles também introduzem desafios relacionados a armazenamento e computação.</p><p>Para enfrentar esses desafios, analisamos diferentes abordagens e técnicas:</p><ul><li><p><strong>Vetores de bits</strong> para reduzir significativamente o uso de espaço em disco, ao mesmo tempo que aproveitam computações de similaridade eficientes, como a distância de Hamming ou a similaridade máxima assimétrica.</p></li><li><p><strong>Vetores médios</strong> para comprimir múltiplos embeddings em uma única representação densa, permitindo recuperação eficiente com indexação HNSW.</p></li><li><p><strong>Agrupamento de tokens</strong> para unir embeddings redundantes de forma inteligente, mantendo a integridade semântica e reduzindo a sobrecarga computacional no momento da consulta.</p></li></ul><p>O Elasticsearch oferece um conjunto poderoso de ferramentas para personalizar e otimizar aplicações de busca de acordo com suas necessidades. Seja priorizando velocidade de recuperação, qualidade de ranqueamento ou eficiência de armazenamento, essas técnicas permitem equilibrar desempenho e qualidade conforme as exigências de aplicações do mundo real.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/late-interaction-model-colpali-scale</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/late-interaction-model-colpali-scale</guid>
    <category><![CDATA[Relevância]]></category>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <dc:creator><![CDATA[Peter Straßer,Benjamin Trent]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt97a536033e6b0a56/6a17f47dfbc5f88c86491c0d/c780b78a07573f2df1cfef8b29a7109f839b0ab3-1200x628.png" length="0" type="image/png"/>
    <pubDate>Thu, 20 Mar 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Explorando a busca vetorial acelerada por GPU no Elasticsearch com NVIDIA: Capítulo I]]></title>
    <description><![CDATA[Com tecnologia NVIDIA cuVS, a colaboração busca fornecer aos desenvolvedores aceleração de GPU para pesquisa vetorial no Elasticsearch.]]></description>
    <content:encoded><![CDATA[<p>Nós da organização Elastic Engineering estamos ocupados otimizando o desempenho do banco de dados vetorial há algum tempo. Nossa missão: tornar o Lucene e o Elasticsearch o melhor banco de dados vetorial. Por meio de <a href="https://www.elastic.co/pt/blog/accelerating-vector-search-simd-instructions">instruções SIMD de CPU</a> aceleradas por hardware, introduzindo novas inovações em compressão de dados vetoriais (<a href="https://www.elastic.co/pt/search-labs/blog/better-binary-quantization-lucene-elasticsearch">melhor quantização binária, também conhecida como BBQ</a>) e, em seguida, superando as expectativas ao atualizar a abordagem algorítmica do BBQ para obter ainda mais benefícios, além <a href="https://www.elastic.co/pt/search-labs/blog/filtered-hnsw-knn-search">de tornar o HNSW filtrado mais rápido</a>. Você entendeu a essência: estamos construindo um sistema mais rápido, melhor e mais eficiente. banco de dados vetorial para os desenvolvedores resolverem aqueles problemas RAG-gedy!</p><p>Como parte da nossa missão de não deixar nada para trás em termos de eficiência, estamos explorando oportunidades de aceleração com esses curiosos chips de computador, dos quais você provavelmente já ouviu falar: GPUs NVIDIA! (Sério, não é mesmo?).</p><p>Quando nos preocupamos com desempenho, temos vários espaços problemáticos a explorar: como indexar exponencialmente mais dados, como recuperar insights deles e como fazer isso quando seus modelos de ML estão envolvidos. Você deve conseguir aproveitar todos os benefícios disponíveis quando tiver GPUs.</p><p>Nesta postagem, mergulhamos em nossa colaboração com a equipe de pesquisa de vetores da NVIDIA enquanto exploramos a pesquisa de vetores acelerada por GPU no Elasticsearch. Este trabalho abre caminho para casos de uso em que os desenvolvedores podem usar uma combinação de GPUs e CPUs para aplicativos reais baseados no Elasticsearch. Tempos emocionantes!</p><h2>GPUs Elasticsearch</h2><p>Estamos felizes em compartilhar que a equipe de engenharia do Elasticsearch está ajudando a criar a experiência da API Java cuVS de código aberto para desenvolvedores, que expõe vinculações para algoritmos de pesquisa vetorial. Este trabalho aproveita nossa experiência anterior com a Panama FFI. O Elasticsearch e o Apache Lucene usam a API NVIDIA cuVS para criar o gráfico durante a indexação. Certo, vamos avançar; vamos voltar um pouco.</p><p><a href="https://developer.nvidia.com/cuvs">NVIDIA cuVS</a>, uma biblioteca C++ de código aberto, está no centro desta colaboração. O objetivo é levar a aceleração da GPU para a pesquisa vetorial, fornecendo maior rendimento, menor latência e tempos de construção de índice mais rápidos. Mas o Elasticsearch e o Apache Lucene são escritos em Java; como isso funcionará?</p><p>Entre em contato com <a href="https://github.com/SearchScale/lucene-cuvs">o lucene-cuvs</a> e a colaboração Elastic-NVIDIA-SearchScale para trazê-lo ao ecossistema Lucene para explorar a pesquisa vetorial acelerada por GPU no Elasticsearch. Na versão recente do NVIDIA cuVS 25.02, adicionamos uma API Java para cuVS. A nova API é experimental e continuará evoluindo, mas atualmente está disponível para uso. Pode surgir a pergunta: as chamadas de funções nativas do Java não são lentas? Não mais! Estamos usando a nova <a href="https://openjdk.org/projects/panama/">Panama FFI</a> (Foreign Function Interface) para as vinculações, que tem sobrecarga mínima para downcalls Java para nativos.</p><p>Já faz algum tempo que usamos <a href="https://www.elastic.co/pt/search-labs/blog/lucene-and-java-moving-forward-together">o Panama FFI no Elasticsearch e no Lucene</a> . É incrível! Mas... sempre tem um “mas”, não é mesmo? O FFI tem desafios de disponibilidade nas versões do Java. Superamos isso compilando a API do cuVS para o Java 21 e encapsulando a implementação em um jar de várias versões voltado para o Java 22. Isso permite o uso do cuVS Java diretamente no Lucene e no Elasticsearch.</p><p>Ok, agora que temos a API Java do cuVS, o que mais precisaríamos?</p><h2>Um conto de dois algoritmos para CPU</h2><p>O Elasticsearch oferece suporte ao <a href="https://arxiv.org/abs/1603.09320">algoritmo HNSW</a> para pesquisa KNN aproximada e escalável. No entanto, para obter o máximo da GPU, usamos um algoritmo diferente, <a href="https://arxiv.org/pdf/2308.15136">CAGRA [</a><a href="https://arxiv.org/pdf/2308.15136"><strong>C</strong></a><a href="https://arxiv.org/pdf/2308.15136"><em>UDA</em></a> <a href="https://arxiv.org/pdf/2308.15136"><strong>A</strong></a><a href="https://arxiv.org/pdf/2308.15136"><em>NN</em></a> <a href="https://arxiv.org/pdf/2308.15136"><strong>GRA</strong></a><a href="https://arxiv.org/pdf/2308.15136"><em>ph</em></a><a href="https://arxiv.org/pdf/2308.15136">]</a>, que foi projetado especificamente para os altos níveis de paralelismo oferecidos pela GPU.</p><p>Antes de entrarmos em como pretendemos adicionar suporte ao CAGRA, vamos ver como o Elasticsearch e o Lucene acessam dados de índice por meio de um “formato de codec”. Isso consiste em</p><ol><li><p>a representação no disco,</p></li><li><p>as interfaces para leitura e escrita de dados,</p></li><li><p>e a maquinaria para lidar com a arquitetura baseada em segmentos do Lucene.</p></li></ol><p>Estamos implementando um novo <a href="https://lucene.apache.org/core/10_1_0/core/org/apache/lucene/codecs/KnnVectorsFormat.html">formato de vetor</a> KNN (k-vizinhos mais próximos) que usa internamente a API Java do cuVS para indexar e pesquisar na GPU. A partir daqui, “analisamos” esse tipo de codec por meio dos mapeamentos do Elasticsearch para um tipo de campo no índice. Como resultado, suas consultas KNN existentes continuam funcionando independentemente de o índice de apoio estar usando um gráfico CAGRA ou HNSW. É claro que isso encobre muitos detalhes, que planejamos abordar em um blog futuro. A seguir está a arquitetura de alto nível para um Elasticsearch acelerado por GPU.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb6197b631a34f8b6/6a170b0da6c2b9e60ce7970b/be6b7356c03df4dee7230625c2c9af3b019f93be-756x510.png" alt="" /><p>Este novo formato de codec tem como padrão o CAGRA. No entanto, ele também suporta a conversão de um gráfico CAGRA em um gráfico HNSW para pesquisa na CPU.</p><h2>Indexação e pesquisa na GPU: tomando algumas decisões “essenciais”</h2><p>Com a <a href="https://www.elastic.co/pt/search-labs/blog/stateless-your-new-state-of-find-with-elasticsearch">arquitetura</a> sem estado do Elasticsearch Serverless, que separa indexação e pesquisa, agora há uma delimitação clara de responsabilidades. Selecionamos o melhor perfil de hardware para cumprir cada uma dessas responsabilidades independentes.</p><p>Esperamos que os usuários considerem duas estratégias principais de implantação:</p><ol><li><p>Indexação e pesquisa na GPU: durante a indexação, crie um gráfico CAGRA e use-o durante a pesquisa — ideal quando uma pesquisa com latência extremamente baixa é necessária.</p></li><li><p>Indexar na GPU e pesquisar na CPU: durante a indexação, crie um gráfico CAGRA e converta-o em um gráfico HNSW. O gráfico HNSW é armazenado no índice, que pode ser usado posteriormente na CPU para pesquisa.</p></li></ol><p>Essa flexibilidade oferece diferentes modelos de implantação, oferecendo compensações entre custo e desempenho. Por exemplo, um serviço de indexação pode usar GPU para criar e mesclar gráficos de forma eficiente e oportuna, enquanto usa uma CPU de menor potência para pesquisa.</p><h2>Então aqui está o plano para pesquisa vetorial acelerada por GPU no Elasticsearch</h2><p>Estamos ansiosos para oferecer ganhos de desempenho e flexibilidade com estratégias de implantação aos usuários, oferecendo vários botões para equilibrar custo e desempenho. <a href="https://www.nvidia.com/gtc/session-catalog/?tab.catalogallsessionstab=16566177511100015Kus&amp;search=Lucene#/">Aqui está a sessão do NVIDIA GTC 2025</a> onde este trabalho foi apresentado em detalhes.</p><p>Gostaríamos de agradecer às equipes de engenharia da NVIDIA e da SearchScale pela fantástica colaboração. Em um próximo blog, exploraremos os detalhes da implementação e a análise de desempenho com mais profundidade. Segurem seus chapéus de curiosidade 🎩!</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/gpu-accelerated-vector-search-elasticsearch-nvidia</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/gpu-accelerated-vector-search-elasticsearch-nvidia</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <dc:creator><![CDATA[Chris Hegarty,Hemant Malik]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt298e839e708ca11c/6a170b0fb339d560c2769fc2/38bc0377a6adce7eae0099f61902fdbbe644eb4a-1440x960.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 19 Mar 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Uma breve introdução à busca vetorial]]></title>
    <description><![CDATA[Este artigo é o primeiro de uma série de três que irá explorar as complexidades da busca vetorial, também conhecida como busca semântica, e como ela é implementada no Elasticsearch.]]></description>
    <content:encoded><![CDATA[<p>Este artigo é o primeiro de uma série de três que irá explorar as complexidades da busca vetorial, também conhecida como busca semântica, e como ela é implementada no Elasticsearch.</p><p>Esta primeira parte tem como objetivo fornecer uma introdução geral aos conceitos básicos de incorporação de vetores e como a busca vetorial funciona internamente.</p><p>Munido de todo o conhecimento adquirido no primeiro artigo, a <a href="https://www.elastic.co/search-labs/blog/vector-search-set-up-elasticsearch">segunda parte</a> irá guiá-lo pelos meandros de como configurar a pesquisa vetorial no Elasticsearch.</p><p>Na <a href="https://www.elastic.co/search-labs/blog/hybrid-search-elasticsearch">terceira parte</a>, aproveitaremos o que aprendemos nas duas primeiras partes, expandiremos esse conhecimento e exploraremos como criar consultas de pesquisa híbridas poderosas no Elasticsearch.</p><p>Antes de abordarmos o tema principal deste artigo, vamos voltar no tempo e revisar um pouco da história dos vetores, um conceito fundamental na busca semântica.</p><h2>Vetores não são novidade</h2><p>Tenho quase certeza de que todos concordariam que, desde o surgimento do ChatGPT em novembro de 2022, não passa um único dia sem que se ouça ou leia sobre "busca vetorial". Está por toda parte e é tão difundida que muitas vezes temos a impressão de que se trata de uma tecnologia de ponta recém-lançada, quando na verdade ela já existe há mais de seis décadas! A pesquisa sobre o assunto começou em meados da década de 1960, e os primeiros artigos científicos foram publicados em 1978 por Gerard Salton, um especialista em recuperação de informação, e seus colegas da Universidade Cornell. O trabalho de Salton sobre modelos vetoriais densos e esparsos constitui a base da tecnologia moderna de busca vetorial.</p><p>Nos últimos 20 anos, muitos <a href="https://db-engines.com/en/ranking/vector+dbms/all">SGBDs vetoriais</a> diferentes, baseados em sua pesquisa, foram criados e lançados no mercado. Isso inclui o Elasticsearch, baseado no projeto Apache Lucene, que começou <a href="https://issues.apache.org/jira/browse/LUCENE-9004">a trabalhar em busca vetorial</a> em 2019.</p><p>Os vetores estão por toda parte e são tão difundidos que é importante primeiro compreender bem a teoria subjacente e o funcionamento interno deles antes de começar a usá-los. Antes de nos aprofundarmos nisso, vamos revisar rapidamente as diferenças entre busca lexical e busca vetorial para que possamos entender melhor como elas diferem e como podem se complementar.</p><h2>Busca vetorial versus busca lexical</h2><p>Uma maneira fácil de introduzir a busca vetorial é compará-la à busca lexical mais convencional com a qual você provavelmente já está familiarizado. A busca vetorial, também conhecida como busca semântica, e a busca lexical funcionam de maneiras muito diferentes. A busca lexical é o tipo de busca que todos nós usamos há anos no Elasticsearch. Resumindo brevemente, o algoritmo não tenta compreender o significado real do que é indexado e consultado. Em vez disso, ele se esforça para encontrar correspondências <strong>lexicais</strong> entre os literais das palavras ou suas variantes (como lematização, sinônimos etc.) digitadas pelo usuário em uma consulta e todos os literais previamente indexados no banco de dados, utilizando algoritmos de similaridade como TF-IDF.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfbb8e150865a5ec8/6a17e0c725daab50f408a16e/a4f3eba19599e5330e9b0d29ecdbbeac211cdab0-1600x583.png" alt="Exemplo de busca lexical" /><p>Como podemos ver, os três documentos no canto superior esquerdo foram tokenizados e analisados. Em seguida, os termos resultantes são indexados em um índice invertido, que simplesmente mapeia os termos analisados aos IDs dos documentos que os contêm. Note que todos os termos aparecem apenas uma vez e nenhum deles é repetido em qualquer outro documento. A busca por “professor alemão legal” retornará os três documentos com pontuações variadas, embora nenhum deles realmente capture o verdadeiro significado da consulta.</p><p>Como pode ser visto na Figura 2, abaixo, a situação fica ainda mais complicada quando se trata de polissemia ou homógrafos, ou seja, palavras que são escritas da mesma forma, mas têm <strong>significados diferentes</strong> (right, palm, bat, mean, etc.). Vamos pegar a palavra "right", que pode ter três significados diferentes, e ver o que acontece.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltea0cc829ff2c8029/6a17e0c92f4a5c8107fa8811/ebb9cc1be03475bbf68269a8dcea5f08bcda0b64-1594x754.png" alt="Busca por homógrafos com pesquisa lexical" /><p>A busca por <em>“I'm not right”</em> retorna um documento que tem o significado exatamente oposto ao do primeiro resultado encontrado. Se você pesquisar exatamente os mesmos termos, mas ordená-los de forma diferente para produzir um significado diferente, por exemplo, <em>"virar à direita"</em> e <em>"virar à direita",</em> o resultado será exatamente o mesmo (ou seja, o terceiro documento "Vire à direita"). É verdade que nossas consultas são excessivamente simplificadas e não utilizam recursos mais avançados, como a correspondência de frases, mas isso ajuda a ilustrar que a busca lexical não compreende o verdadeiro significado por trás do que é indexado e do que é pesquisado. Se isso não estiver claro, não se preocupe, voltaremos a este exemplo no terceiro artigo para ver como a busca vetorial pode ajudar neste caso.</p><p>Para fazer justiça à busca lexical, quando você tem controle sobre como indexa seus dados <strong>estruturados</strong> (pense em mapeamentos, análise de texto, pipelines de ingestão, etc.) e como elabora suas consultas (pense em consultas DSL bem elaboradas, análise de termos de consulta, etc.), você pode fazer maravilhas com mecanismos de busca lexical, não há dúvida! O histórico do Elasticsearch em relação às suas capacidades de busca lexical é simplesmente incrível. O que conseguiu alcançar e o quanto popularizou e aprimorou o campo da busca lexical nos últimos anos é verdadeiramente notável.</p><p>No entanto, quando se trata de fornecer suporte para<a href="https://www.elastic.co/what-is/unstructured-data"> consultas a</a> <a href="https://www.elastic.co/what-is/unstructured-data"><strong>dados não estruturados</strong></a> (como imagens, vídeos, áudios, texto bruto, etc.) para usuários que precisam fazer perguntas em texto livre, a busca lexical deixa a desejar. Além disso, às vezes a consulta nem sequer é um texto, pode ser uma imagem, como veremos em breve. A principal razão pela qual a busca lexical é inadequada em tais situações é que os dados não estruturados não podem ser indexados nem consultados da mesma forma que os dados estruturados. Ao lidar com dados não estruturados, <strong>a semântica</strong> entra em jogo. O que significa semântica? Muito simplesmente, o significado!</p><p>Vamos pegar o exemplo simples de um mecanismo de busca de imagens (por exemplo, a Busca de Imagens do Google ou o Lens). Você arrasta e solta uma imagem, e o mecanismo de busca semântica do Google encontrará e retornará as imagens mais semelhantes àquela que você pesquisou. Na Figura 3, abaixo, podemos ver à esquerda a imagem de um pastor alemão e à direita todas as imagens semelhantes que foram recuperadas, sendo o primeiro resultado a mesma imagem que a fornecida (ou seja, a mais semelhante).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3e0a0fb3c300537a/6a17e0cbe8fbce69d13a185d/c0dff24d4379141abd1038c9c4b5577fd2dca802-1600x657.png" alt="Exemplo de busca semântica: Busca por uma imagem" /><p>Mesmo que isso pareça simples e lógico para nós, humanos, para os computadores é uma história completamente diferente. É isso que a busca vetorial possibilita e ajuda a alcançar. O poder desbloqueado pela busca vetorial é imenso, como o mundo testemunhou recentemente. Vamos agora levantar o capô e descobrir o que se esconde por baixo.</p><h2>Incorporação de vetores</h2><p>Como vimos anteriormente, com mecanismos de busca lexical, dados estruturados como texto podem ser facilmente tokenizados em termos que podem ser correspondidos no momento da busca, independentemente do significado real dos termos. Os dados não estruturados, no entanto, podem assumir diferentes formas, como grandes objetos binários (imagens, vídeos, áudios, etc.), e não são adequados para o mesmo processo de tokenização. Além disso, o objetivo principal da busca semântica é indexar os dados de forma que possam ser pesquisados com base no significado que representam. Como podemos alcançar isso? A resposta está em duas palavras: <strong>Aprendizado de Máquina</strong>! Ou, mais precisamente, Aprendizado Profundo!</p><p><strong>Aprendizado profundo</strong> é uma área específica do aprendizado de máquina que se baseia em modelos de redes neurais artificiais compostas por múltiplas camadas de processamento, capazes de extrair progressivamente o verdadeiro significado dos dados. O funcionamento desses modelos de redes neurais é fortemente inspirado no cérebro humano. A Figura 4, abaixo, mostra a aparência de uma rede neural, com suas camadas de entrada e saída, bem como múltiplas camadas ocultas:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8ca5fd8f0e61d939/6a17e0cd7f6f152a67c09a53/aeea3bab3c29e1c591a9c9082f2e73e7d3990ef1-1600x1116.png" alt="Camadas de redes neurais na busca vetorial" /><p>A verdadeira façanha das redes neurais é a capacidade de transformar um único dado não estruturado em uma sequência de valores de ponto flutuante, conhecidos como <strong>vetores de incorporação</strong> ou simplesmente <strong>incorporações</strong>. Como seres humanos, conseguimos entender muito bem o que são vetores, desde que os visualizemos em um espaço bidimensional ou tridimensional. Cada componente do vetor representa uma coordenada em um plano xy bidimensional ou em um espaço xyz tridimensional.</p><p>No entanto, os vetores de incorporação nos quais os modelos de redes neurais funcionam podem ter centenas ou até milhares de dimensões e simplesmente representar um ponto em um espaço multidimensional. Cada dimensão vetorial representa uma <strong>característica</strong> ou um atributo dos dados não estruturados. Vamos ilustrar isso com um modelo de aprendizado profundo que transforma imagens em vetores de incorporação de 2048 dimensões. Esse modelo transformaria a imagem do pastor alemão que usamos na Figura 3 no vetor de incorporação mostrado na tabela abaixo. Note que mostramos apenas os três primeiros e os três últimos elementos, mas haveria mais 2.042 colunas/dimensões na tabela.</p><p></p><p>é vermelho</p><p>é_cachorro</p><p>céu azul</p><p>…</p><p>sem_grama</p><p>pastor_alemão</p><p>é_árvore</p><p>Pastor alemão incorporações</p><p>0,0121</p><p>0,9572</p><p>0,8735</p><p>…</p><p>0,1198</p><p>0,9712</p><p>0,0512</p><p>Cada coluna representa uma dimensão do modelo e corresponde a uma característica que a rede neural subjacente procura modelar. Cada entrada fornecida ao modelo será caracterizada dependendo de quão semelhante essa entrada é a cada uma das 2048 dimensões. Assim, o valor de cada elemento no vetor de incorporação denota a <strong>similaridade</strong> dessa entrada a uma dimensão específica. Neste exemplo, podemos ver que o modelo detectou uma alta similaridade entre cães e pastores alemães, bem como a presença de céu azul.</p><p>Ao contrário da busca lexical, onde um termo pode ser correspondido ou não, com a busca vetorial podemos ter uma noção muito melhor de quão <em>semelhante</em> um conjunto de dados não estruturados é a cada uma das dimensões suportadas pelo modelo. Dessa forma, os vetores de incorporação servem como uma representação semântica fantástica de dados não estruturados.</p><h2>O molho secreto</h2><p>Agora que sabemos como os dados não estruturados são segmentados e analisados por redes neurais de aprendizado profundo em vetores de incorporação que capturam a similaridade dos dados ao longo de um grande número de dimensões, precisamos entender como funciona a correspondência desses vetores. Acontece que a resposta é bem simples. Os vetores de incorporação que estão <strong>próximos</strong> uns dos outros representam dados <strong>semanticamente semelhantes</strong> . Assim, quando consultamos um banco de dados vetorial, a entrada da pesquisa (imagem, texto, etc.) é primeiro transformada em um vetor de incorporação usando o mesmo modelo que foi usado para indexar todos os dados não estruturados, e o objetivo final é encontrar os <strong>vetores vizinhos mais próximos</strong> desse vetor de consulta. Portanto, tudo o que precisamos fazer é descobrir como medir a "distância" ou "similaridade" entre o vetor de consulta e todos os vetores existentes indexados no banco de dados; é basicamente isso.</p><h3>Distância e semelhança</h3><p>Felizmente para nós, medir a distância entre dois vetores é um problema fácil de resolver graças à aritmética vetorial. Vamos então analisar as funções de distância e similaridade mais populares que são suportadas por bancos de dados de busca vetorial modernos, como o Elasticsearch. Atenção, contém matemática!</p><h4>Distância L1</h4><p>A distância L1, também chamada de distância de Manhattan, entre dois vetores x e y é medida somando-se a diferença absoluta entre todos os seus elementos. Obviamente, quanto menor a distância d, mais próximos estarão os dois vetores. A fórmula é bastante simples, como pode ser visto abaixo:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2df2e9bc20883491/6a17e0ce033c8d006a6bb0bf/2b17bcedfbedde61117a3e7970af55bc62318c6c-312x102.png" alt="Fórmula da distância L1 na busca vetorial" /><p>Visualmente, a distância L1 pode ser ilustrada como mostrado na Figura 5, abaixo:</p><p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb90a33e6b1cbe00b/6a17e0d0414c64eb76945096/52075441892151536ed216081817a8e852566daa-474x464.png" alt="Visualizando a distância L1 entre dois vetores" /><p>Vamos pegar dois vetores x e y, tais como x = (1, 2) e y = (4, 3), então a distância L1 de ambos os vetores seria | 1 - 4 | + | 2 - 3 | = 4.</p><h4>Distância L2</h4><p>A distância L2, também chamada de distância euclidiana, entre dois vetores x e y é medida somando-se primeiro o quadrado da diferença entre todos os seus elementos e, em seguida, extraindo-se a raiz quadrada do resultado. É basicamente o caminho mais curto entre dois pontos (também chamado de hipotenusa). De forma semelhante a L1, quanto menor a distância d, mais próximos estão os dois vetores:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltae9b63fb593ae225/6a17e0d1af47b68379cddea8/b7675aa41f4f381e954c21dacd23a51a2dde6780-384x112.png" alt="Distância L2 na busca vetorial" /><p>A distância L2 é mostrada na Figura 6 abaixo:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt32d913b46e07e79d/6a17e0d27f6f1582f7c09a57/bac99d08d6cf8a3a387a8acdc2e8f67357dad235-448x456.png" alt="Visualizando a distância L2 entre dois vetores" /><p>Vamos reutilizar os mesmos dois vetores de amostra x e y que usamos para a distância L1, e agora podemos calcular a distância L2 como . A raiz quadrada de 10 resulta em 3,16.</p><p></p><h4>Distância Linf</h4><p>A distância Linf (para L infinito), também chamada de distância de Chebyshev ou distância do tabuleiro de xadrez, entre dois vetores x e y é definida simplesmente como a maior distância entre quaisquer dois de seus elementos ou a maior distância medida ao longo de um dos eixos/dimensões. A fórmula é muito simples e está ilustrada abaixo:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt863411e15de16fa3/6a17e0d4505ac30939ad8a48/179ea6c6520a59acd97638313a2fb1b105e2ffed-436x82.png" alt="Fórmula de distância linear na busca vetorial" /><p>A representação da distância Linf é mostrada na Figura 7 abaixo:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt863ee7b51fd5fc15/6a17e0d56864a4772db686af/b75540d793cdc0645244d77dc36da9d5734ccbac-548x560.png" alt="Distância linear entre dois vetores" /><p>Novamente, tomando os mesmos dois vetores de amostra x e y, podemos calcular a distância infinita L como max ( | 1 - 4 | , | 2 - 3 | ) = max (3, 1) = 3.</p><h4>Semelhança de cosseno</h4><p>Ao contrário de L1, L2 e Linf, a similaridade de cosseno não mede a distância entre dois vetores x e y, mas sim o ângulo relativo entre eles, ou seja, se ambos apontam aproximadamente na mesma direção. Quanto maior a similaridade s, mais "próximos" estão os dois vetores. A fórmula é novamente muito simples e está apresentada abaixo:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt61d55341a6457ab7/6a17e0d7e9ea872b4ea9c4e2/931b6b90f0ee83e63a66496d06d6b4cef3affddd-304x72.png" alt="Fórmula de similaridade de cosseno na busca vetorial" /><p>Uma forma de representar a similaridade de cosseno entre dois vetores é mostrada na Figura 8 abaixo:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt50eb6399510b5380/6a17e0d83e9e45302cba139a/52092e8b5d366178e50a35f8c954ee8eaca76965-582x586.png" alt="similaridade de cosseno entre dois vetores" /><p>Além disso, como os valores do cosseno estão sempre no intervalo [-1, 1], -1 significa similaridade oposta (ou seja, um ângulo de 180° entre os dois vetores), 0 significa similaridade não relacionada (ou seja, um ângulo de 90°) e 1 significa idêntico (ou seja, um ângulo de 0°), conforme mostrado na Figura 9 abaixo:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt785e195c75a5089e/6a17e0da1d1b8355bc93e398/fd2690a845b89b69ff202bd04192893638538aec-1600x456.png" alt="O espectro de similaridade de cosseno na busca vetorial" /><p>
Mais uma vez, vamos reutilizar os mesmos vetores de amostra x e y e calcular a similaridade de cosseno usando a fórmula acima. Primeiro, podemos calcular o produto escalar de ambos os vetores como . Então, multiplicamos o comprimento (também chamado de magnitude) de ambos os vetores:  Finalmente, dividimos o produto escalar pelo comprimento multiplicado 10 / 11,18034 = 0,894427 (ou seja, um ângulo de 26°), que é bastante próximo de 1, então ambos os vetores podem ser considerados bastante semelhantes.</p><h4>similaridade do produto escalar</h4><p>Uma desvantagem da similaridade de cosseno é que ela leva em consideração apenas o ângulo entre dois vetores, mas não sua magnitude (ou seja, comprimento), o que significa que, se dois vetores apontam aproximadamente na mesma direção, mas um é muito mais longo que o outro, ambos ainda serão considerados semelhantes. A similaridade por produto escalar, também chamada de produto interno, aprimora isso ao levar em consideração tanto o ângulo quanto a magnitude dos vetores, o que proporciona uma métrica de similaridade muito mais precisa.</p><p>Duas fórmulas equivalentes são usadas para calcular a similaridade do produto escalar. A primeira é a mesma que vimos anteriormente no numerador da semelhança de cosseno:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7f1c3369f8628457/6a17e0db1d1b832a1893e39c/52e2723926ab27cd96b688e805fae15f607073c8-482x104.png" alt="Fórmula de similaridade do produto escalar na busca vetorial" /><p>A segunda fórmula simplesmente multiplica o comprimento de ambos os vetores pelo cosseno do ângulo entre eles:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt136dd2a749c2398a/6a17e0dc6df73108a80a0e1f/dc9b11fc67dd748d9f1f29b735f4726138cb7d39-452x70.png" alt="Fórmula de similaridade do produto escalar simplificada na busca vetorial" /><p>A similaridade do produto escalar é visualizada na Figura 10, abaixo:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb2c095e1c1948752/6a17e0dd6df731e30b0a0e23/1bda38cf82a1e1037f44b6e9657602c9efe1c0a6-558x574.png" alt="produto escalar: similaridade entre dois vetores" /><p>Mais uma vez, pegamos os vetores de amostra x e y e calculamos sua similaridade de produto escalar usando a primeira fórmula, como fizemos para a similaridade de cosseno anteriormente, como (1 4) + (2 3) = 10.</p><p>Usando a segunda fórmula, multiplicamos o comprimento de ambos os vetores:  e multiplicamos isso pelo cosseno do ângulo de 26° entre ambos os vetores, e obtemos 11,18034 cos(26°) = 10.</p><p>Vale ressaltar que, se todos os vetores forem <strong>normalizados</strong> primeiro (ou seja, seu comprimento for 1), a similaridade do produto escalar se torna exatamente a mesma que a similaridade do cosseno (porque |x| |y| = 1), ou seja, o cosseno do ângulo entre os dois vetores. Como veremos mais adiante, a normalização de vetores é uma boa prática a ser adotada para tornar a magnitude do vetor irrelevante, de modo que a similaridade se concentre simplesmente no ângulo. Isso também acelera o cálculo da distância no momento da indexação e da consulta, o que pode ser um grande problema ao operar com bilhões de vetores.</p><h3>Resumo rápido</h3><p>Uau, já vimos MUITA informação até agora, então vamos parar um minuto e fazer um breve resumo do que temos a dizer. Aprendemos que…</p><ul><li><p>…a busca semântica é baseada em modelos de redes neurais de aprendizado profundo que se destacam na transformação de dados não estruturados em vetores de incorporação multidimensionais.</p></li><li><p>…cada dimensão do modelo representa uma característica ou propriedade dos dados não estruturados.</p></li><li><p>…um vetor de incorporação é uma sequência de valores de similaridade (um para cada dimensão) que representam o quão semelhante a cada dimensão um determinado conjunto de dados não estruturados é.</p></li><li><p>…quanto mais “próximos” dois vetores estiverem (ou seja, quanto mais próximos forem os vizinhos), mais eles representarão conceitos semanticamente semelhantes.</p></li><li><p>…as funções de distância (L1, L2, Linf) permitem medir a proximidade entre dois vetores.</p></li><li><p>…as funções de similaridade (cosseno e produto escalar) permitem medir o quanto dois vetores estão apontando na mesma direção.</p></li></ul><p></p><p>Agora, a última peça que precisamos analisar é o próprio mecanismo de busca vetorial. Quando uma consulta é recebida, ela é primeiro vetorizada e, em seguida, o mecanismo de busca vetorial encontra os vetores vizinhos mais próximos desse vetor de consulta. A abordagem de força bruta, que consiste em medir a distância ou similaridade entre o vetor de consulta e todos os vetores no banco de dados, pode funcionar para conjuntos de dados pequenos, mas rapidamente se torna insuficiente à medida que o número de vetores aumenta. Em outras palavras, como podemos indexar milhões, bilhões ou até trilhões de vetores e encontrar os vizinhos mais próximos do vetor de consulta em um tempo razoável? É aí que precisamos usar a inteligência e descobrir maneiras ideais de indexar vetores para que possamos localizar os vizinhos mais próximos o mais rápido possível, sem comprometer muito a precisão.</p><h3>Algoritmos e técnicas de busca vetorial</h3><p>Ao longo dos anos, muitas equipes de pesquisa diferentes investiram muito esforço no desenvolvimento de algoritmos de busca vetorial muito inteligentes. Aqui, vamos apresentar brevemente os principais. Dependendo do caso de uso, alguns são mais adequados do que outros.</p><h4>Busca linear</h4><p>Já mencionamos brevemente a busca linear, ou indexação plana, quando citamos a abordagem de força bruta de comparar o vetor de consulta com todos os vetores presentes no banco de dados. Embora possa funcionar bem em conjuntos de dados pequenos, o desempenho diminui rapidamente à medida que o número de vetores e dimensões aumenta (complexidade O(n)).</p><p>Felizmente, existem abordagens mais eficientes chamadas de <strong>vizinho mais próximo aproximado</strong> (ANN, na sigla em inglês), onde as distâncias entre os vetores de incorporação são pré-computadas e vetores semelhantes são armazenados e organizados de forma a mantê-los próximos uns dos outros, por exemplo, usando clusters, árvores, hashes ou grafos. Essas abordagens são chamadas de "aproximadas" porque geralmente não garantem 100% de precisão. O objetivo final é <strong>reduzir o escopo da busca</strong> o máximo e o mais rápido possível, a fim de focar apenas nas áreas com maior probabilidade de conter vetores semelhantes, ou <strong>reduzir a dimensionalidade dos vetores</strong>.</p><h4>Árvores K-dimensionais</h4><p>Uma árvore K-dimensional, ou árvore KD, é uma generalização de uma árvore de busca binária que armazena pontos em um espaço k-dimensional e funciona dividindo continuamente o espaço de busca em árvores menores à esquerda e à direita, onde os vetores são indexados. No momento da busca, o algoritmo simplesmente precisa visitar alguns ramos da árvore ao redor do vetor de consulta (o ponto vermelho na Figura 11) para encontrar o vizinho mais próximo (o ponto verde na Figura 11). Se forem solicitados mais de k vizinhos, a área amarela será expandida até que o algoritmo encontre mais vizinhos.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt55e01707cd9fca57/6a17e0dfbe60866a90004653/e21af2d613112279089b6fa2166359233b10019d-829x860.png" alt="Algoritmo de árvore KD em busca vetorial" /><p>A maior vantagem do algoritmo da árvore KD é que ele nos permite focar rapidamente apenas em alguns ramos localizados da árvore, eliminando assim a maioria dos vetores da análise. No entanto, a eficiência desse algoritmo diminui à medida que o número de dimensões aumenta, pois é necessário visitar muito mais ramos do que em espaços de dimensões inferiores.</p><h4>Índice de arquivo invertido</h4><p>A abordagem de índice de arquivo invertido (IVF) também é um algoritmo <strong>de particionamento espacial</strong> que atribui vetores próximos uns dos outros ao seu centroide comum. No espaço bidimensional, isso é melhor visualizado com um diagrama de Voronoi, como mostrado na Figura 12:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7b7e4fe6deff5ef3/6a17e0e17b54f940eb8b381c/33ddaaa818ab2f2a5fc87982c82c2a34cb849e33-640x640.png" alt="Representação de Voronoi de um índice de arquivo invertido no espaço 2D " /><p>Podemos ver que o espaço 2D acima está dividido em 20 clusters, cada um com seu centroide representado por pontos pretos. Todos os vetores de incorporação no espaço são atribuídos ao cluster cujo centroide está mais próximo deles. No momento da busca, o algoritmo primeiro determina o cluster no qual se concentrar, encontrando o centroide mais próximo do vetor de consulta. Em seguida, ele pode simplesmente focar nessa área e nas áreas vizinhas, se necessário, para encontrar os vizinhos mais próximos.</p><p>Este algoritmo sofre do mesmo problema que as árvores KD quando usado em espaços de alta dimensionalidade. Isso é chamado de maldição da dimensionalidade e ocorre quando o volume do espaço aumenta tanto que todos os dados parecem esparsos e a quantidade de dados necessária para obter resultados mais precisos cresce exponencialmente. Quando os dados são esparsos, torna-se mais difícil para esses algoritmos de particionamento espacial organizarem os dados em clusters. Felizmente para nós, existem outros algoritmos e técnicas que atenuam esse problema, conforme detalhado abaixo.</p><h4>quantização</h4><p>A quantização é uma abordagem baseada em <strong>compressão</strong>que nos permite reduzir o tamanho total do banco de dados, diminuindo a precisão dos vetores de incorporação. Isso pode ser alcançado usando <strong>quantização escalar (SQ)</strong> , convertendo os valores vetoriais de ponto flutuante em valores inteiros. Isso não apenas reduz o tamanho do banco de dados em um fator de 8, mas também diminui o consumo de memória e acelera o cálculo da distância entre vetores no momento da busca.</p><p>Outra técnica é chamada de <strong>quantização de produto (PQ),</strong> que primeiro divide o espaço em subespaços de dimensão inferior e, em seguida, os vetores que estão próximos uns dos outros são agrupados em cada subespaço usando um algoritmo de agrupamento (semelhante ao k-means).</p><p>Note que a quantização é diferente da <strong>redução de dimensionalidade</strong>, onde o número de dimensões é reduzido, ou seja, os vetores simplesmente se tornam mais curtos.</p><h4>Mundos Pequenos Navegáveis Hierárquicos (HNSW)</h4><p>Se o nome parece complexo, não se preocupe, na verdade não é! Em resumo, o Hierarchical Navigable Small Worlds é um algoritmo baseado em grafos multicamadas muito popular e eficiente. É utilizado por diversos bancos de dados vetoriais, incluindo o Apache Lucene. Uma representação conceitual do HNSW pode ser vista na Figura 13, abaixo.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7f4f4a8e393c8d53/6a17e0e3e8fbcefa193a1861/189ef9a8bec476e379222c454644ba4c1f952085-1400x840.png" alt="Mundos Pequenos Navegáveis Hierárquicos (HNSW)" /><p>Na camada superior, podemos ver um gráfico com muito poucos vetores que possuem as ligações mais longas entre si, ou seja, um gráfico de vetores conectados com a menor similaridade. Quanto mais nos aprofundamos nas camadas mais baixas, mais vetores encontramos e mais denso o grafo se torna, com cada vez mais vetores próximos uns dos outros. Na camada mais baixa, podemos encontrar todos os vetores, sendo que os mais semelhantes estão localizados mais próximos uns dos outros.</p><p>No momento da busca, o algoritmo começa na camada superior, em um ponto de entrada arbitrário, e encontra o vetor mais próximo do vetor de consulta (indicado pelo ponto cinza). Em seguida, o algoritmo desce uma camada e repete o mesmo processo, partindo do mesmo vetor que deixou na camada anterior, e assim por diante, camada após camada, até atingir a camada mais baixa e encontrar o vizinho mais próximo do vetor de consulta.</p><h4>Hashing sensível à localidade (LSH)</h4><p>Da mesma forma que todas as outras abordagens apresentadas até agora, o hashing sensível à localidade busca reduzir drasticamente o espaço de busca para aumentar a velocidade de recuperação. Com essa técnica, os vetores de incorporação são transformados em valores de hash, preservando as informações de similaridade, de modo que o espaço de busca se torna, em última análise, uma tabela hash simples que pode ser consultada em vez de um grafo ou árvore que precisa ser percorrido. A principal vantagem dos métodos baseados em hash é que vetores contendo um número arbitrário (grande) de dimensões podem ser mapeados para hashes de tamanho fixo, o que acelera enormemente o tempo de recuperação sem sacrificar muita precisão.</p><p>Existem muitas maneiras diferentes de realizar o hash de dados em geral e de incorporar vetores em particular, mas este artigo não se aprofundará nos detalhes de cada uma delas. Os métodos convencionais de hashing geralmente produzem hashes muito diferentes para dados que parecem muito semelhantes. Como os vetores de incorporação são compostos de valores de ponto flutuante, vamos pegar dois valores de ponto flutuante de exemplo que são considerados muito próximos um do outro na aritmética vetorial (por exemplo, 0,73 e 0,74) e executá-los através de algumas funções de hash comuns. Analisando os resultados abaixo, fica bastante óbvio que as funções de hash comuns não preservam a similaridade entre as entradas.</p><p>Função de hash</p><p>0,73</p><p>0,74</p><p>MD5</p><p>1342129d04cd2924dd06cead4cf0a3ca</p><p>0aec1b15371bd979cfa66b0a50ebecc5</p><p>SHA1</p><p>49d2c3e0e44bff838e1db571a121be5ea874e8d9</p><p>a534e76482ade9d9fe4bff3035a7f31f2f363d77</p><p>SHA256</p><p>99d03fc3771fe6848d675339fc49eeb1cb8d99a12e6358173336b99a2ec530ea</p><p>5ecbc825ba5c16856edfdaf0abc5c6c41d0d8a9c508e34188239521dc7645663</p><p>Enquanto os métodos de hashing convencionais tentam <em>minimizar as colisões de hashing</em> entre dados semelhantes, o principal objetivo do hashing sensível à localidade é fazer exatamente o oposto, ou seja, <em>maximizar as colisões de hashing</em> para que dados semelhantes caiam no mesmo bucket com alta probabilidade. Dessa forma, vetores de incorporação que estejam próximos uns dos outros em um espaço multidimensional serão agrupados em um valor de tamanho fixo, pertencendo ao mesmo grupo. Como o LSH permite que esses vetores hash mantenham sua proximidade, essa técnica é muito útil para agrupamento de dados e buscas por vizinhos mais próximos.</p><p>Todo o trabalho pesado ocorre no momento da indexação, quando os hashes precisam ser calculados, enquanto no momento da busca precisamos apenas aplicar o hash ao vetor de consulta para localizar o bucket que contém os vetores de incorporação mais próximos. Uma vez encontrado o bucket candidato, geralmente ocorre uma segunda rodada para identificar os vetores vizinhos mais próximos do vetor de consulta.</p><h2>Vamos concluir</h2><p>Para introduzir a busca vetorial, tivemos que abordar bastante conteúdo neste artigo. Após comparar as diferenças entre a busca lexical e a busca vetorial, aprendemos como os modelos de redes neurais de aprendizado profundo conseguem capturar a semântica de dados não estruturados e transcodificar seu significado em vetores de incorporação de alta dimensão, uma sequência de números de ponto flutuante que representa a similaridade dos dados ao longo de cada uma das dimensões do modelo. Vale ressaltar também que a busca vetorial e a busca lexical não são técnicas de recuperação de informação concorrentes, mas sim complementares (como veremos na terceira parte desta série, quando nos aprofundaremos na busca híbrida).</p><p>Em seguida, introduzimos um elemento fundamental da busca vetorial, ou seja, as funções de distância (e similaridade) que nos permitem medir a proximidade de dois vetores e avaliar a similaridade dos conceitos que eles representam.</p><p>Por fim, analisamos diferentes variações dos algoritmos e técnicas de busca vetorial mais populares, que podem ser baseados em árvores, grafos, clusters ou hashes, cujo objetivo é restringir rapidamente a uma área específica do espaço multidimensional para encontrar os vizinhos mais próximos sem ter que percorrer todo o espaço, como faria uma busca linear por força bruta.</p><p>Se você gostou do que leu, não deixe de conferir as outras partes desta série:</p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/vector-search-set-up-elasticsearch">Parte 2: Como configurar a pesquisa vetorial no Elasticsearch</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/hybrid-search-elasticsearch">Parte 3: Busca Híbrida usando o Elasticsearch</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/introduction-to-vector-search</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/introduction-to-vector-search</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <dc:creator><![CDATA[Valentin Crettaz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt70da374b8dc0c490/6a17e0e46864a473aab686b3/63eea8ea95b49e7241e539f65bf5aa3bb8823fff-1200x628.png" length="0" type="image/png"/>
    <pubDate>Thu, 06 Feb 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Como usar o conector Elasticsearch Vector Store para o Microsoft Semantic Kernel no desenvolvimento de agentes de IA]]></title>
    <description><![CDATA[O Microsoft Semantic Kernel é um kit de desenvolvimento leve e de código aberto que permite criar facilmente agentes de IA e integrar os modelos de IA mais recentes em seu código C#, Python ou Java. Com o lançamento do Semantic Kernel Elasticsearch Vector Store Connector, os desenvolvedores que usam o Semantic Kernel para criar agentes de IA agora podem integrar o Elasticsearch como um armazenamento vetorial escalável de nível empresarial, continuando a usar as abstrações do Semantic Kernel.]]></description>
    <content:encoded><![CDATA[<p>Em colaboração com a equipe <a href="https://learn.microsoft.com/en-us/semantic-kernel/overview/">do Microsoft Semantic Kernel</a> , estamos anunciando a disponibilidade do <a href="https://github.com/elastic/semantic-kernel-net/">Semantic Kernel Elasticsearch Vector Store Connector</a> para usuários <a href="https://learn.microsoft.com/en-us/semantic-kernel/overview/">do Microsoft Semantic Kernel</a> (.NET). O Semantic Kernel simplifica a criação de agentes de IA de nível empresarial, incluindo a capacidade de aprimorar grandes modelos de linguagem (LLMs) com respostas mais relevantes e orientadas por dados de um Repositório de Vetores. O Semantic Kernel fornece uma camada de abstração perfeita para interagir com armazenamentos vetoriais como o Elasticsearch, oferecendo recursos essenciais como criar, listar e excluir coleções de registros, além de carregar, recuperar e excluir registros individuais.</p><p><a href="https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors/out-of-the-box-connectors/elasticsearch-connector?pivots=programming-language-csharp">O conector Semantic Kernel Elasticsearch Vector Store, pronto para uso,</a> oferece suporte às <a href="https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors/?pivots=programming-language-csharp#the-vector-store-abstraction">abstrações de armazenamento vetorial</a> do Semantic Kernel, o que facilita muito para os desenvolvedores a integração do Elasticsearch como um armazenamento vetorial durante a criação de agentes de IA.</p><p>O Elasticsearch possui uma base sólida na comunidade de código aberto e adotou recentemente a <a href="https://www.elastic.co/blog/elasticsearch-is-open-source-again">licença AGPL</a>. Em conjunto com o Microsoft Semantic Kernel de código aberto, essas ferramentas oferecem uma solução poderosa e pronta para uso empresarial. Você pode começar localmente executando o Elasticsearch em poucos minutos executando este comando <code>curl -fsSL https://elastic.co/start-local | sh </code>(consulte <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/run-elasticsearch-locally.html">start-local</a> para obter detalhes) e migrar para versões <a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;utm_source=semantickernel&amp;utm_content=documentation">hospedadas na nuvem</a> ou <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.16/install-elasticsearch.html">auto-hospedadas</a> à medida que coloca seus agentes de IA em produção.</p><p>Neste blog, veremos como usar <a href="https://github.com/elastic/semantic-kernel-net/">o conector Elasticsearch Vector Store do Semantic Kernel</a> ao utilizar o Semantic Kernel. Uma versão em Python do conector será disponibilizada futuramente.</p><h2>Cenário geral: Construindo um aplicativo RAG com Semantic Kernel e Elasticsearch.</h2><p>Na seção seguinte, analisaremos um exemplo. Em linhas gerais, estamos construindo um aplicativo RAG (Retrieval Augmented Generation) que recebe uma pergunta do usuário como entrada e retorna uma resposta. Usaremos o Azure OpenAI (também é possível usar <a href="https://devblogs.microsoft.com/semantic-kernel/introducing-new-ollama-connector-for-local-models/">um LLM local</a> ) como LLM, o Elasticsearch como repositório de vetores e o Semantic Kernel (.NET) como framework para integrar todos os componentes.</p><p>Se você não estiver familiarizado com arquiteturas RAG, pode ter uma breve introdução com este artigo: <a href="https://www.elastic.co/search-labs/blog/retrieval-augmented-generation-rag">https://www.elastic.co/search-labs/blog/retrieval-augmented-generation-rag</a>.</p><p>A resposta é gerada pelo LLM, que é alimentado com contexto relevante para a pergunta, obtido do armazenamento de vetores do Elasticsearch. A resposta também inclui a fonte que foi usada como contexto pelo LLM.</p><h3>Exemplo RAG</h3><p>Neste exemplo específico, criamos um aplicativo que permite aos usuários fazer perguntas sobre hotéis armazenados em um banco de dados interno de hotéis. O usuário poderia, por exemplo... Pesquise um hotel específico com base em diferentes critérios ou solicite uma lista de hotéis.</p><p>Para o banco de dados de exemplo, geramos uma <a href="https://github.com/elastic/semantic-kernel-net/blob/main/Elastic.SemanticKernel.Playground/hotels.csv">lista de hotéis</a> contendo 100 entradas. O tamanho da amostra é intencionalmente pequeno para permitir que você experimente a demonstração do conector da maneira mais fácil possível. Em uma aplicação do mundo real, o conector Elasticsearch mostraria suas vantagens sobre outras opções, como a implementação de armazenamento vetorial `InMemory`, especialmente ao trabalhar com quantidades extremamente grandes de dados.</p><p>A aplicação de demonstração completa pode ser encontrada no <a href="https://github.com/elastic/semantic-kernel-net/tree/main/Elastic.SemanticKernel.Playground">repositório</a> do conector de armazenamento vetorial do Elasticsearch.</p><p>Vamos começar adicionando os pacotes NuGet necessários e usando as diretivas ao nosso projeto:</p>dotnet add package "Elastic.Clients.Elasticsearch" -v 8.16.2
dotnet add package "Elastic.SemanticKernel.Connectors.Elasticsearch" -v 0.1.2
dotnet add package "Microsoft.Extensions.Hosting" -v 9.0.0
dotnet add package "Microsoft.SemanticKernel.Connectors.AzureOpenAI" -v 1.30.0
dotnet add package "Microsoft.SemanticKernel.PromptTemplates.Handlebars" -v 1.30.0using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

using Elastic.Clients.Elasticsearch;
using Elastic.Transport;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.Embeddings;
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;<p>Agora podemos criar nosso modelo de dados e fornecer a ele atributos específicos do Semantic Kernel para definir o esquema do modelo de armazenamento e algumas dicas para a pesquisa de texto:</p>/// &lt;summary&gt;
/// Data model for storing a "hotel" with a name, a description, a  description embedding and an optional reference link.
/// &lt;/summary&gt;
public sealed record Hotel
{
	[VectorStoreRecordKey]
	public required string HotelId { get; set; }

	[TextSearchResultName]
	[VectorStoreRecordData(IsFilterable = true)]
	public required string HotelName { get; set; }

	[TextSearchResultValue]
	[VectorStoreRecordData(IsFullTextSearchable = true)]
	public required string Description { get; set; }

	[VectorStoreRecordVector(Dimensions: 1536, DistanceFunction.CosineSimilarity, IndexKind.Hnsw)]
	public ReadOnlyMemory&lt;float&gt;? DescriptionEmbedding { get; set; }

	[TextSearchResultLink]
	[VectorStoreRecordData]
	public string? ReferenceLink { get; set; }
}<p>Os atributos do esquema do modelo de armazenamento (`VectorStore*`) são os mais relevantes para o uso prático do conector Elasticsearch Vector Store, a saber:</p><p></p><ul><li><p><code>VectorStoreRecordKey</code> Marcar uma propriedade em uma classe de registro como a chave sob a qual o registro é armazenado em um armazenamento vetorial.</p></li><li><p><code>VectorStoreRecordData</code> Marcar uma propriedade em uma classe de registro como 'dados'.</p></li><li><p><code>VectorStoreRecordVector</code> Para marcar uma propriedade em uma classe de registro como um vetor.</p></li></ul><p>Todos esses atributos aceitam vários parâmetros opcionais que podem ser usados para personalizar ainda mais o modelo de armazenamento. No caso de <code>VectorStoreRecordKey </code>, por exemplo, é possível especificar uma função de distância diferente ou um tipo de índice diferente.</p><p>Os atributos de pesquisa de texto (<code>TextSearch*</code>) serão importantes na última etapa deste exemplo. Voltaremos a eles mais tarde.</p><p>Na próxima etapa, inicializamos o mecanismo do Kernel Semântico e obtemos referências aos serviços principais. Em uma aplicação do mundo real, <a href="https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection">a injeção de dependência</a> deve ser usada em vez de acessar diretamente a coleção de serviços. O mesmo se aplica à configuração e aos segredos embutidos no código, que devem ser lidos usando um <a href="https://learn.microsoft.com/en-us/dotnet/core/extensions/configuration">provedor de configuração</a> :</p>var builder = Host.CreateApplicationBuilder(args);

// Register AI services.
var kernelBuilder = builder.Services.AddKernel();

kernelBuilder.AddAzureOpenAIChatCompletion("gpt-4o", "https://my-service.openai.azure.com", "my_token");

kernelBuilder.AddAzureOpenAITextEmbeddingGeneration("ada-002", "https://my-service.openai.azure.com", "my_token");

// Register text search service.
kernelBuilder.AddVectorStoreTextSearch&lt;Hotel&gt;();

// Register Elasticsearch vector store.
var elasticsearchClientSettings = new ElasticsearchClientSettings(new Uri("https://my-elasticsearch-instance.cloud"))
    .Authentication(new BasicAuthentication("elastic", "my_password"));

kernelBuilder.AddElasticsearchVectorStoreRecordCollection&lt;string, Hotel&gt;("skhotels", elasticsearchClientSettings);

// Build the host.
using var host = builder.Build();

// For demo purposes, we access the services directly without using a DI context.

var kernel = host.Services.GetService&lt;Kernel&gt;()!;
var embeddings = host.Services.GetService&lt;ITextEmbeddingGenerationService&gt;()!;
var vectorStoreCollection = host.Services.GetService&lt;IVectorStoreRecordCollection&lt;string, Hotel&gt;&gt;()!;

// Register search plugin.
var textSearch = host.Services.GetService&lt;VectorStoreTextSearch&lt;Hotel&gt;&gt;()!;
kernel.Plugins.Add(textSearch.CreateWithGetTextSearchResults("SearchPlugin"));<p>O serviço <code>vectorStoreCollection</code> agora pode ser usado para criar a coleção e para inserir alguns <a href="https://github.com/elastic/semantic-kernel-net/blob/main/Elastic.SemanticKernel.Playground/hotels.csv">registros de demonstração</a>:</p>await vectorStoreCollection.CreateCollectionIfNotExistsAsync();

// CSV format: ID;Hotel Name;Description;Reference Link
var hotels = (await File.ReadAllLinesAsync("hotels.csv"))
    .Select(x =&gt; x.Split(';'));

foreach (var chunk in hotels.Chunk(25))
{
    var descriptionEmbeddings = await embeddings.GenerateEmbeddingsAsync(chunk.Select(x =&gt; x[2]).ToArray());
    
    for (var i = 0; i &lt; chunk.Length; ++i)
    {
        var hotel = chunk[i];
        await vectorStoreCollection.UpsertAsync(new Hotel
        {
            HotelId = hotel[0],
            HotelName = hotel[1],
            Description = hotel[2],
            DescriptionEmbedding = descriptionEmbeddings[i],
            ReferenceLink = hotel[3]
        });
    }
}<p>Isso demonstra como o Semantic Kernel reduz o uso de um armazenamento vetorial com toda a sua complexidade a algumas chamadas de método simples.</p><p>Nos bastidores, um novo índice é criado no Elasticsearch e todos os mapeamentos de propriedades necessários são criados. Nosso conjunto de dados é então mapeado de forma completamente transparente para o modelo de armazenamento e, finalmente, armazenado no índice. Abaixo, você pode ver como os mapeamentos aparecem no Elasticsearch.</p>{
  "mappings": {
    "properties": {
      "descriptionEmbedding": {
        "dims": 1536,
        "index": true,
        "index_options": {
          "type": "hnsw"
        },
        "similarity": "cosine",
        "type": "dense_vector"
      },
      "hotelName": {
        "type": "keyword"
      },
      "description": {
        "type": "text"
      }
    }
  }
}<p>As chamadas <code>embeddings.GenerateEmbeddingsAsync()</code> invocaram de forma transparente o serviço de geração de incorporações de IA do Azure configurado.</p><p>Ainda mais magia pode ser observada na última etapa desta demonstração.</p><p>Com apenas uma chamada para <code>InvokePromptAsync</code>, todas as seguintes operações são realizadas quando o usuário faz uma pergunta sobre os dados:</p><p>1. É gerado um elemento incorporado para a pergunta do usuário.</p><p>2. O repositório de vetores é pesquisado em busca de entradas relevantes.</p><p>3. Os resultados da consulta são inseridos em um modelo de prompt.</p><p>4. A consulta propriamente dita, na forma da mensagem final, é enviada ao serviço de autocompletar do chat por IA.</p>// Invoke the LLM with a template that uses the search plugin to
// 1. get related information to the user query from the vector store
// 2. add the information to the LLM prompt.
var response = await kernel.InvokePromptAsync(
    promptTemplate: """
                    Please use this information to answer the question:
                    {{#with (SearchPlugin-GetTextSearchResults question)}}
                      {{#each this}}
                        Name: {{Name}}
                        Value: {{Value}}
                        Source: {{Link}}
                        -----------------
                      {{/each}}
                    {{/with}}
                    
                    Include the source of relevant information in the response.

                    Question: {{question}}
                    """,
    arguments: new KernelArguments
    {
        { "question", "Please show me all hotels that have a rooftop bar." },
    },
    templateFormat: "handlebars",
    promptTemplateFactory: new HandlebarsPromptTemplateFactory());<p>Lembra-se dos atributos <code>TextSearch*</code> que definimos anteriormente em nosso modelo de dados? Esses atributos nos permitem usar marcadores correspondentes em nosso modelo de prompt, que são preenchidos automaticamente com as informações de nossas entradas no repositório vetorial.</p><p>A resposta final à nossa pergunta "Por favor, mostre-me todos os hotéis que possuem um bar na cobertura" é a seguinte:</p>Console.WriteLine(response.ToString());

// &gt; The hotel that has a rooftop bar is Skyline Suites. You can find more information about this hotel [here](https://example.com/yz567).<p>A resposta correta refere-se à seguinte entrada em nosso arquivo hotels.csv.</p>9;
Skyline Suites;
Offering panoramic city views from every suite, this hotel is perfect for those who love the urban landscape. Enjoy luxurious amenities, a rooftop bar, and close proximity to attractions. Luxurious and contemporary.;
https://example.com/yz567<p>Este exemplo demonstra muito bem como a utilização do Microsoft Semantic Kernel permite uma redução significativa da complexidade através das suas abstrações bem concebidas, além de proporcionar um elevado nível de flexibilidade. Ao alterar uma única linha de código, por exemplo, o armazenamento de vetores ou os serviços de IA utilizados podem ser substituídos sem a necessidade de refatorar qualquer outra parte do código.</p><p>Ao mesmo tempo, a estrutura fornece um enorme conjunto de funcionalidades de alto nível, como a função `InvokePrompt`, ou o sistema de plugins de modelo ou de pesquisa.</p><p>A aplicação de demonstração completa pode ser encontrada no repositório do conector de armazenamento vetorial do Elasticsearch.</p><h2>O que mais é possível fazer com o Elasticsearch?</h2><ul><li><p><a href="https://www.elastic.co/search-labs/blog/semantic-search-simplified-semantic-text">Novo mapeamento semantic_text do Elasticsearch: Simplificando a busca semântica</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/semantic-reranking-with-retrievers">Reclassificação semântica no Elasticsearch com retrievers</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1">Técnicas avançadas de RAG, parte 1: Processamento de dados</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2">Técnicas avançadas de RAG, parte 2: Consultas e testes</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/elasticsearch-rag-with-llama3-opensource-and-elastic">Construindo o RAG com Llama 3 de código aberto e Elastic</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/local-rag-agent-elasticsearch-langgraph-llama3">Um tutorial sobre como construir um agente local usando LangGraph, LLaMA3 e o armazenamento de vetores Elasticsearch do zero.</a></p></li></ul><h2>Elasticsearch e Semantic Kernel: O que vem a seguir?</h2><ul><li><p>Mostramos como o armazenamento de vetores do Elasticsearch pode ser facilmente integrado ao Semantic Kernel durante a criação de aplicações GenAI em .NET. Fique atento para a integração com Python em breve.</p></li><li><p>À medida que o Semantic Kernel cria abstrações para recursos de pesquisa avançados, como <a href="https://www.elastic.co/search-labs/tutorials/search-tutorial/vector-search/hybrid-search">a pesquisa híbrida</a>, a conexão com o Elasticsearch permitirá que os desenvolvedores .NET os implementem facilmente ao usar o Semantic Kernel.</p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-connector-microsoft-semantic-kernel</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-connector-microsoft-semantic-kernel</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[.NET]]></category>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <dc:creator><![CDATA[Florian Bernd,Srikanth Manvi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2d8725035e86f8a8/6a17fe447f6f1564f8c09d74/0564fe794e4c66d0507317822d7aa71826183d20-1311x762.jpg" length="0" type="image/jpeg"/>
    <pubDate>Fri, 06 Dec 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Como usar a busca híbrida em um catálogo de produtos de e-commerce]]></title>
    <description><![CDATA[Aprenda a usar a busca híbrida para criar um catálogo de produtos de e-commerce, utilizando filtros, promoções, personalização e análise comportamental.]]></description>
    <content:encoded><![CDATA[<p>Neste artigo, demonstraremos como implementar uma busca híbrida que combina os resultados da busca de texto completo com a busca vetorial. Ao unificar essas duas abordagens, a busca híbrida melhora a abrangência dos resultados, aproveitando o melhor de ambas as estratégias de busca.</p><p>Além de integrar a busca híbrida, demonstraremos como adicionar recursos que tornarão sua solução de busca ainda mais robusta. Isso inclui segmentação por facetas e promoções de produtos personalizadas. Além disso, mostraremos como capturar interações do usuário e gerar insights valiosos usando a ferramenta de Análise Comportamental da Elastic.</p><p>Nesta implementação, você verá como construir tanto a interface que permite aos usuários visualizar e interagir com os resultados da pesquisa quanto a API responsável por retornar as informações. Para acessar os repositórios com o código-fonte, os links são fornecidos abaixo:</p><ul><li><p><a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/product-store-search">https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/product-store-search</a></p></li><li><p><a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/app-product-store">https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/app-product-store</a> </p></li></ul><p>Dividimos este guia em várias etapas, desde a criação do índice até a implementação de recursos avançados, como facetas e personalização de resultados. Ao final, você terá uma solução de busca robusta, pronta para ser usada em um cenário de comércio eletrônico.</p><h2>Configuração do ambiente para pesquisa híbrida de comércio eletrônico</h2><p>Antes de iniciarmos a implementação, precisamos configurar o ambiente. Você pode optar por usar um serviço na Elastic Cloud ou uma solução em contêineres para gerenciar o Elasticsearch. Se você optar pela conteinerização, uma configuração via Docker Compose pode ser encontrada neste repositório: <a href="https://github.com/andreluiz1987/product-store-search/blob/main/docker/docker-compose.yml">docker-compose.yml</a>.</p><h2>Criação de índice e ingestão de catálogo de produtos</h2><p>O índice será criado com base em um catálogo de produtos cosméticos, que inclui campos como nome, descrição, foto, categoria e etiquetas. Os campos usados para pesquisa de texto completo, como "nome" e "descrição", serão mapeados como <code>text</code>, enquanto os campos usados para agregações, como "categoria" e "marca", serão mapeados como <code>keyword</code> para permitir a filtragem facetada.</p><p>O campo "descrição" será usado para a busca vetorial, pois fornece mais contexto sobre os produtos. Este campo será definido como <code>dense_vector,</code> , armazenando a representação vetorial da descrição.</p><p>O mapeamento do índice será o seguinte:</p>{
   "mappings":{
      "properties":{
         "id":{
            "type":"keyword"
         },
         "brand":{
            "type":"text",
            "fields":{
               "keyword":{
                  "type":"keyword"
               }
            }
         },
         "name":{
            "type":"text"
         },
         "price":{
            "type":"float"
         },
         "price_sign":{
            "type":"keyword"
         },
         "currency":{
            "type":"keyword"
         },
         "image_link":{
            "type":"keyword"
         },
         "description":{
            "type":"text"
         },
         "description_embeddings":{
            "type":"dense_vector",
            "dims":384
         },
         "rating":{
            "type":"keyword"
         },
         "category":{
            "type":"keyword"
         },
         "product_type":{
            "type":"keyword"
         },
         "tag_list":{
            "type":"keyword"
         }
      }
   }
}<p>O script para criar o índice pode ser encontrado <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/product-store-search/infra/create_index.py">aqui</a>.</p><h2>Geração de incorporação</h2><p>Para vetorizar as descrições dos produtos, utilizamos o modelo all-MiniLM-L6-v2. Neste caso, a aplicação é responsável por gerar os embeddings antes da indexação. Outra opção seria importar o modelo para o cluster Elasticsearch, mas para este ambiente local, optamos por realizar a vetorização diretamente dentro da aplicação.</p><p>Utilizamos o conjunto de dados de cosméticos disponível no <a href="https://www.kaggle.com/datasets/shivd24coder/cosmetic-brand-products-dataset">Kaggle</a> para preencher o índice e, para melhorar a eficiência da ingestão de dados, usamos o processamento em lote. Durante essa mesma etapa de ingestão, geraremos os embeddings para o campo "description" e os indexaremos no novo campo "description_embeddings".</p><p>Todo o processo de ingestão de dados pode ser acompanhado e executado diretamente através do <strong>Jupyter Notebook</strong> disponível no repositório. O notebook fornece um guia passo a passo sobre como os dados são lidos, processados e indexados no Elasticsearch, permitindo fácil replicação e experimentação.</p><p>Você pode acessar o notebook no seguinte link: <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/product-store-search/ingestion/ingestion.ipynb">Notebook de Ingestão.</a></p><h2>Implementação de busca híbrida</h2><p>Agora, vamos implementar a busca híbrida. Para a pesquisa baseada em palavras-chave, usamos a consulta <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html">multi_match</a> , direcionada aos campos "nome", "categoria" e "descrição". Isso garante que os documentos que contenham o termo de pesquisa em qualquer um desses campos sejam recuperados.</p><p>Para a busca vetorial, utilizamos a <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html">consulta KNN</a>. O termo de pesquisa precisa ser vetorizado antes da execução da consulta, e isso é feito usando o método que vetoriza o termo de entrada. Note que o mesmo modelo usado durante a ingestão também é usado para o termo de pesquisa.</p><p>A combinação das duas buscas é realizada utilizando o algoritmo <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rrf.html">Reciprocal Rank Fusion (RRF) </a> , que mescla os resultados das duas consultas e aumenta a precisão da busca ao reduzir o ruído. O RRF permite que as buscas baseadas em palavras-chave e em vetores funcionem em conjunto, aprimorando a compreensão da consulta do usuário.</p>query = {
   "retriever": {
       "rrf": {
           "retrievers": [
               {
                   "standard": {
                       "query": organic_query['query']
                   }
               },
               {
                   "knn": {
                       "field": "description_embeddings",
                       "query_vector": vector,
                       "k": 5,
                       "num_candidates": 20
                   }
               }
           ],
           "rank_window_size": 20,
           "rank_constant": 5
       }
   },
   "_source": organic_query['_source']
}<h3>Comparação de resultados: pesquisa por palavra-chave vs. pesquisa híbrida</h3><p>Agora, vamos comparar os resultados de uma pesquisa tradicional por palavras-chave com a pesquisa híbrida. Ao pesquisar por "base para pele seca" usando a busca por palavras-chave, obtemos os seguintes resultados:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcb5405df787bc8f5/6a17023e961e697ca1c4cdb4/52e717aa3c9c1fadfadb639f5fb77cf8e47e3b34-1600x1021.png" alt="Comparação de resultados: Busca por palavra-chave vs. busca híbrida" /><ol><li><p><strong>Base Revlon ColorStay para Pele Normal/SecaDescrição</strong>: A Base Revlon ColorStay oferece cobertura de longa duração com uma fórmula leve que não craquela, desbota ou transfere. Com a tecnologia de liberação gradual, esta fórmula sem óleo e com equilíbrio de hidratação foi especialmente desenvolvida para peles normais ou secas, proporcionando hidratação contínua. Características: Maquiagem confortável e com duração de até 24 horas. Cobertura média a total. Disponível em uma variedade de lindas cores.
</p></li><li><p><strong>Base Mousse Dream Smooth da Maybelline</strong>: Por que você vai amar? Esta base cremosa exclusiva proporciona uma pele 100% macia como a de um bebê. Pele com aparência e sensação de hidratação por 14 horas - nunca áspera ou ressecada. Fórmula leve que proporciona cobertura perfeitamente hidratante. Mistura-se perfeitamente e proporciona uma sensação de frescor o dia todo. Sem óleo, sem fragrância, testado por dermatologistas, testado contra alergias, não comedogênico – não obstrui os poros. Seguro. Para peles sensíveis.</p></li></ol><p><strong>Análise</strong>: Ao pesquisar por "base para pele seca", os resultados foram obtidos pela correspondência exata entre as palavras-chave do termo de pesquisa e os títulos e descrições dos produtos. No entanto, essa combinação nem sempre representa a melhor escolha. Por exemplo, <strong>a base Revlon ColorStay para pele normal/seca</strong> é uma boa opção, pois é formulada especificamente para pele seca. Apesar de ser isento de óleo, sua fórmula foi desenvolvida para proporcionar hidratação contínua. Em contrapartida, também recebemos <strong>a base Maybelline Dream Smooth Mousse</strong>, que, embora seja livre de óleo e mencione hidratação, geralmente é mais recomendada para peles oleosas ou mistas, já que os produtos sem óleo tendem a se concentrar no controle da oleosidade em vez de fornecer a hidratação extra necessária para peles secas. Isso evidencia a limitação das buscas baseadas em palavras-chave, que podem retornar produtos que não atendem completamente às necessidades específicas de pessoas com pele seca.</p><p>Agora, ao realizar a mesma pesquisa usando a abordagem híbrida:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt412e38b327cf6a4f/6a17024066c4f94c52f8beb9/13b562a5120619355ba0861976102e208964a49f-1600x1021.png" alt="Realizando buscas usando a abordagem híbrida" /><ol><li><p><strong>Base Cremosa CoverGirl Outlast Stay Luminous Natural (820): Descrição</strong>: A Base CoverGirl Outlast Stay Luminous é perfeita para obter um acabamento luminoso e um brilho sutil. É livre de óleo, com uma fórmula não oleosa que proporciona à sua pele uma luminosidade natural que dura o dia todo! Esta base de longa duração hidrata a pele enquanto proporciona uma cobertura impecável.

<strong>Análise: </strong>Este produto é uma excelente opção, pois prioriza a hidratação, que é fundamental para usuários com pele seca. Os termos "hidrata a pele" e "acabamento luminoso" estão alinhados com a intenção do usuário de encontrar uma base para pele seca. A busca vetorial provavelmente compreendeu o conceito de hidratação e o relacionou à necessidade de uma base que trate a pele seca.
</p></li><li><p><strong>Base Revlon ColorStay para Pele Normal/Seca: Descrição:</strong> A base Revlon ColorStay oferece cobertura de longa duração com uma fórmula leve que não craquela, desbota ou transfere. Com a tecnologia de liberação gradual, esta fórmula sem óleo e com equilíbrio de hidratação foi especialmente desenvolvida para peles normais ou secas, proporcionando hidratação contínua.

<strong>Análise: </strong>Este produto aborda diretamente as necessidades de usuários com pele seca, mencionando explicitamente que sua fórmula é indicada para pele normal ou seca. A "fórmula de equilíbrio de umidade" e a hidratação contínua são ideais para quem busca uma base que atenda às necessidades da pele seca. A busca vetorial recuperou com sucesso esse resultado, não apenas pela correspondência de palavras-chave, mas também pelo foco na hidratação e pela menção específica de pele seca como público-alvo.
</p></li><li><p><strong>Descrição da Base Sérum: </strong>As bases sérum são fórmulas leves com cobertura média, disponíveis em uma ampla gama de 21 tons. Essas bases oferecem cobertura moderada com aparência natural e uma sensação de sérum muito leve. Possuem viscosidade muito baixa e são dispensados com a bomba fornecida ou com o conta-gotas de vidro opcional, disponível para compra separadamente, caso seja de preferência.

<strong>Análise:</strong> Neste caso, a descrição enfatiza uma base sérum leve com toque natural, o que está de acordo com as necessidades de pessoas com pele seca, que geralmente buscam produtos suaves, hidratantes e que proporcionem um acabamento sem efeito craquelado. A busca vetorial provavelmente captou o contexto mais amplo de cobertura leve e natural e a textura semelhante a um sérum, que está associada à retenção de umidade e a uma aplicação confortável, tornando-a relevante para pele seca, mesmo que o termo "pele seca" não seja mencionado explicitamente.</p></li></ol><h2>Implementação de facetas</h2><p>Os filtros são essenciais para refinar e filtrar os resultados da pesquisa de forma eficiente, proporcionando aos usuários uma navegação mais direcionada, especialmente em cenários com grande variedade de produtos, como no comércio eletrônico. Elas permitem que os usuários ajustem os resultados com base em atributos como categoria, marca ou preço, tornando a busca mais precisa. Para implementar este recurso, usamos <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-terms-aggregation.html">agregações de termos</a> nos campos <code>category</code> e <code>brand</code> , que foram definidos como <code>keyword</code> durante o estágio de criação do índice.</p>    query = build_query(term, categories, product_types, brands)
    query["aggs"] = {
        "product_types": {"terms": {"field": "product_type"}},
        "categories": {"terms": {"field": "category"}},
        "brands": {"terms": {"field": "brand.keyword"}}
    }<p>O código completo da implementação pode ser encontrado <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/product-store-search/api/api.py#L144">aqui</a>.</p><p>Veja abaixo os resultados da busca por "base para pele seca":</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt31080652a60d8600/6a1702416234e0af7ddb18e7/e8907af359c021eaa38ec7a6a53f10c325ee8ade-1146x1248.png" alt="Resultados da pesquisa por &quot;base para pele seca&quot;" /><h2>Personalizando resultados: consultas fixadas</h2><p>Em alguns casos, pode ser benéfico promover determinados produtos nos resultados da pesquisa. Para isso, utilizamos <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-pinned-query.html"><strong>Consultas Fixadas</strong></a>, que permitem que produtos específicos apareçam no topo dos resultados. A seguir, realizaremos uma busca pelo termo "Fundação" sem promover nenhum produto:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt31e75c815262993e/6a170243509168ae42e1b95d/9396c4ed358e7a68eed47f17dd6915d0bad492aa-1600x1157.png" alt="Pesquise o termo &quot;Fundação&quot; sem promover nenhum produto." /><p>Em nosso exemplo, podemos promover produtos que tenham a etiqueta "Sem Glúten". Ao utilizarmos os IDs dos produtos, garantimos que eles sejam priorizados nos resultados da pesquisa. Especificamente, promoveremos os seguintes produtos: <strong>Base Sérum</strong> (ID: 1043), <strong>Base de Alta Cobertura</strong> (ID: 1042) e <strong>Pó Translúcido Realista</strong> (ID: 1039).</p>{
   "query":{
      "pinned":{
         "ids":[
            "1043",
            "1042",
            "1039"
         ],
         "organic":{
            "bool":{
               "must":[
                  {
                     "multi_match":{
                        "query":"foundation",
                        "fields":[
                           "name",
                           "category",
                           "description"
                        ]
                     }
                  }
               ]
            }
         }
      }
   }
}<p>Utilizamos IDs de produto específicos para garantir que eles sejam priorizados nos resultados da consulta. A estrutura da consulta inclui uma lista de IDs de produtos que devem ser "fixados" no topo (neste caso, os IDs 1043, 1042 e 1039), enquanto os resultados restantes seguem o fluxo orgânico da pesquisa, usando uma combinação de condições como a consulta de texto nos campos "nome", "categoria" e "descrição". Dessa forma, é possível promover itens de maneira controlada, garantindo sua visibilidade, enquanto o restante da busca permanece baseado na relevância usual.</p><p>Abaixo, você pode ver o resultado da execução da consulta com os produtos promovidos:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt53a6d5cbef227f16/6a1702458b73cb68f0189ef3/8c1a547bfe31360c405eae0891c666635051a51b-1600x1039.png" alt="Resultado da execução da consulta com os produtos promovidos" /><p>O código completo da consulta pode ser encontrado <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/product-store-search/api/api.py#L112">aqui</a>.</p><h2>Analisando o comportamento de busca com análise comportamental.</h2><p>Até o momento, já adicionamos funcionalidades para melhorar a relevância dos resultados de busca e facilitar a descoberta de produtos. Agora, vamos finalizar nossa solução de busca incluindo um recurso que nos ajudará a analisar o comportamento de busca do usuário, identificando padrões como consultas com ou sem resultados e cliques nos resultados da busca. Para isso, usaremos o recurso <strong>de Análise Comportamental</strong> fornecido pela Elastic. Com ele, em apenas alguns passos, podemos monitorar e analisar o comportamento de busca do usuário, obtendo informações valiosas para otimizar a experiência de busca.</p><h3>Criando a coleta de dados de análise comportamental</h3><p>Nossa primeira ação será criar uma coleção, que será responsável por receber todos os eventos de análise comportamental. Para criar a coleção, acesse a interface do Kibana em <strong>Pesquisa &gt; Análise Comportamental</strong>. No exemplo abaixo, criamos a coleção chamada <code>tracking-search</code>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9b4cb5480ab0bfef/6a1702474a531b189936a7e7/c05e533212a3690c5ea9f2d5226cbcdc901c482a-1600x1009.png" alt="Análise comportamental - como nomear sua coleção" /><h3>Integrar a análise comportamental à interface.</h3><p>Nosso aplicativo <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/app-product-store">front-end</a> foi desenvolvido em JavaScript e, para integrar o Behavioral Analytics, seguiremos os passos descritos na documentação oficial da Elastic para instalar o <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/behavioral-analytics-start.html#behavioral-analytics-start-ui-integration-js-client"><strong>Behavioral Analytics JavaScript Tracker</strong></a>.</p><h3>Implementando o rastreador JavaScript</h3><p>Agora, vamos importar o cliente rastreador para nosso aplicativo e usar os métodos <code>trackPageView</code>, <code>trackSearch</code> e <code>trackSearchClick</code> para capturar as interações do usuário.</p><p><strong>Aviso</strong>: Embora utilizemos uma ferramenta para coletar dados de interação do usuário, é essencial garantir a conformidade com <strong>o GDPR</strong>. Isso significa informar claramente os usuários sobre quais dados estão sendo coletados, como serão usados e fornecer a opção de desativar o rastreamento. Além disso, devemos implementar medidas de segurança robustas para proteger as informações coletadas e respeitar os direitos do usuário, como o acesso e a exclusão de dados, garantindo que todas as etapas estejam em conformidade com os princípios do GDPR.
</p><p><strong>Etapa 1: Criando a instância do rastreador</strong></p><p>Primeiro, criaremos a instância do rastreador que monitorará as interações. Nessa configuração, definimos o endpoint de destino, o nome da coleção e a chave da API:</p>createTracker({
  endpoint: "https://endpoint:443",
  collectionName: "tracking-search",
  apiKey: "api-key"
});<p><strong>Etapa 2: Capturando visualizações de página</strong></p><p>Para rastrear visualizações de página, podemos configurar o evento <code>trackPageView</code> :</p>    trackPageView({
      page: {
        title: "home-page"
      },
    });<p>Para obter mais detalhes sobre o evento <code>trackPageView</code> , você pode consultar esta <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/behavioral-analytics-event-reference.html#behavioral-analytics-event-reference-pageview-fields">documentação</a>.</p><p><strong>Etapa 3: Capturar consultas de pesquisa</strong></p><p>Para monitorar as ações de busca dos usuários, utilizaremos o método <code>trackSearch</code> :</p>      trackSearch({
        search: {
          query: searchTerm,
          results: {
            items: documents,
            total_results: response.data.length,
          },
        },
      });<p>Aqui estamos coletando o termo de pesquisa e os resultados da pesquisa.</p><p><strong>Etapa 4: Rastreamento de cliques nos resultados da pesquisa</strong></p><p>Finalmente, para capturar cliques nos resultados da pesquisa, usaremos o método <code>trackSearchClick</code> :</p>trackSearchClick({
      document: { id: product.id, index: "products-catalog"},
      search: {
        query: searchTerm,
        page: {
          current: 1,
          size: products.length,
        },
        results: {
          items: documents,
          total_results: products.length,
        },
        search_application: "app-product-store"
      },
    });<p>Coletamos informações sobre o ID do documento clicado, bem como o termo de pesquisa e os resultados da pesquisa.</p><h3>Analisando os dados no Kibana</h3><p>Agora que os eventos de interação do usuário estão sendo capturados, podemos obter dados valiosos sobre as ações de busca. O Kibana utiliza a ferramenta de Análise Comportamental para visualizar e analisar esses dados comportamentais. Para visualizar os resultados, basta navegar até <strong>Pesquisa &gt; Análise Comportamental &gt; Minha Coleção</strong>, onde será exibida uma visão geral dos eventos capturados.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdab4f4507c5a4732/6a17024860084b34ca3c4411/e219c4af2e01b0ccc1b9079459a07832835abc75-1600x1155.png" alt="Analisando os dados no Kibana" /><p>Nesta visão geral, obtemos uma perspectiva ampla dos eventos registrados para cada ação integrada à nossa interface. A partir dessas informações, podemos obter insights valiosos sobre o comportamento de busca do usuário. No entanto, se você deseja criar painéis personalizados com métricas mais relevantes para o seu cenário específico, o Kibana oferece ferramentas poderosas para a criação de painéis, permitindo que você crie diversas visualizações de suas métricas.</p><p>Abaixo, criei algumas visualizações e gráficos para monitorar, por exemplo, os termos mais pesquisados ao longo do tempo, as consultas que não retornaram resultados, uma nuvem de palavras destacando os termos mais pesquisados e, finalmente, uma visualização geográfica para identificar a origem do acesso à pesquisa.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt04ced4406436f942/6a17024a5091685116e1b961/84a2c39dab77a3f9366c258a3a45d2cbc7df125e-1600x689.png" alt="Visualizações e gráficos para monitorar" /><h2>Conclusão</h2><p>Neste artigo, implementamos uma solução de busca híbrida que combina busca por palavras-chave e busca vetorial, fornecendo resultados mais precisos e relevantes para os usuários. Exploramos também como usar recursos adicionais, como filtros e personalização de resultados com Consultas Fixadas, para criar uma experiência de busca mais completa e eficiente.</p><p>Além disso, integramos <strong>o Behavioral Analytics</strong> da Elastic para capturar e analisar o comportamento do usuário durante suas interações com o mecanismo de busca. Ao usar métodos como <code>trackPageView</code>, <code>trackSearch</code> e <code>trackSearchClick</code>, conseguimos monitorar consultas de pesquisa, cliques em resultados de pesquisa e visualizações de página, gerando informações valiosas sobre o comportamento de pesquisa.</p><h2>Referências</h2><p>Conjunto de dados</p><p><a href="https://www.kaggle.com/datasets/shivd24coder/cosmetic-brand-products-dataset">https://www.kaggle.com/datasets/shivd24coder/cosmetic-brand-products-dataset</a></p><p>Transformador</p><p><a href="https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2">https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2</a></p><p>Fusão de Classificação Recíproca</p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rrf.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/rrf.html</a></p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/retriever.html#rrf-retriever">https://www.elastic.co/guide/en/elasticsearch/reference/current/retriever.html#rrf-retriever</a></p><p>Consulta Knn</p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html</a></p><p>Consulta fixada</p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-pinned-query.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-pinned-query.html</a></p><p>APIs de análise comportamental</p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/behavioral-analytics-apis.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/behavioral-analytics-apis.html</a></p><p>https://www.elastic.co/guide/en/elasticsearch/reference/current/behavioral-analytics-overview.html</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/hybrid-search-ecommerce</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/hybrid-search-ecommerce</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt63c711d1bf9b2501/6a17024c66c4f9c2b7f8bebd/05578fc595a12f6b1ebf88a10a2a31e9971b545e-1200x628.png" length="0" type="image/png"/>
    <pubDate>Tue, 12 Nov 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Criando um app de buscas com o Blazor e o Elasticsearch]]></title>
    <description><![CDATA[Aprenda como criar um aplicativo de busca usando Blazor e Elasticsearch, e como usar o cliente Elasticsearch .NET para busca híbrida.]]></description>
    <content:encoded><![CDATA[<p>Neste artigo, você aprenderá como aproveitar suas habilidades em C# para criar um aplicativo de busca usando Blazor e Elasticsearch. Vamos usar o cliente <a href="https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/introduction.html">Elasticsearch .NET</a> para executar consultas de pesquisa de <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/full-text-queries.html">texto completo</a>, <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-search.html">semântica</a> e <a href="https://www.elastic.co/search-labs/tutorials/search-tutorial/vector-search/hybrid-search">híbrida</a> .</p><p><strong>NOTA:</strong> Se você estiver familiarizado com a versão antiga do cliente C# do Elasticsearch, <a href="https://www.elastic.co/guide/en/elasticsearch/client/net-api/7.17/nest.html">o NEST</a>, leia esta <a href="https://www.elastic.co/search-labs/blog/net-client-evolution">postagem do blog</a> sobre a descontinuação do cliente NEST e seus novos recursos. <em>O NEST era a geração anterior do cliente .NET, que foi substituído pelo </em><code>Elastic.Clients.Elasticsearch package</code></p><p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltca8d1da68641fac0/6a17f65763173044d4585bfb/18f890286ca122cd97286c09ebf740208b802d0b-650x395.png" alt="Criar aplicativo Blazor: esre com diagrama Blazor" /><ul><li><p><a href="https://www.elastic.co/search-labs/blog/search-app-with-esre-blazor#what-is-blazor?">O que é Blazor?</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/search-app-with-esre-blazor#what-is-esre?">O que é ESRE?</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/search-app-with-esre-blazor#configuring-elser">Configurando o ELSER</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/search-app-with-esre-blazor#indexing-data">Dados de indexação</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/search-app-with-esre-blazor#building-the-app-with-blazor-&amp;-elasticsearch">Aplicação de construção</a></p></li></ul><h2>O que é Blazor?</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt91278ae422883e28/6a17f6592f4a5c3213fa8a95/622741915d016b68bf94f742332d10d736d60052-707x461.png" alt="Servidor Blazor" /><p><a href="https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor">Blazor</a> é um framework web de código aberto baseado em HTML, CSS e C#, criado pela Microsoft para permitir que desenvolvedores criem aplicações web que rodam tanto no cliente quanto no servidor. O Blazor também permite criar componentes reutilizáveis para desenvolver aplicações mais rapidamente; ele possibilita que os desenvolvedores criem a visualização HTML e as ações em C# no mesmo arquivo, o que ajuda a manter um código legível e limpo. Além disso, com o Blazor Hybrid, você pode criar aplicativos móveis nativos acessando os recursos nativos da plataforma por meio de código .NET.</p><p>Algumas das características que fazem do Blazor uma ótima estrutura para se trabalhar:</p><ul><li><p>Opções de renderização do lado do servidor e do lado do cliente</p></li><li><p>Componentes de interface do usuário reutilizáveis</p></li><li><p>Atualizações em tempo real com SignalR</p></li><li><p>Gerenciamento de estado integrado</p></li><li><p>Sistema de roteamento integrado</p></li><li><p>Tipagem forte e verificações em tempo de compilação</p></li></ul><h3>Por que Blazor?</h3><p>O Blazor oferece diversas vantagens em relação a outros frameworks e bibliotecas: permite que os desenvolvedores usem C# tanto para o código do cliente quanto para o código do servidor, fornecendo tipagem forte e verificação em tempo de compilação, o que aumenta a confiabilidade. Ele se integra perfeitamente ao ecossistema .NET, permitindo a reutilização de bibliotecas e ferramentas .NET, e oferece suporte robusto para depuração.</p><h2>O que é ESRE?</h2><p><a href="https://www.elastic.co/elasticsearch/elasticsearch-relevance-engine">O Elasticsearch Relevance Engine ™ (ESRE)</a> é um conjunto de <a href="https://www.elastic.co/guide/en/esre/current/learn.html">ferramentas para criar aplicações de busca</a> utilizando aprendizado de máquina e inteligência artificial sobre o poderoso mecanismo de busca Elasticsearch.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf313ba986de01b93/6a17d851505ac306fcad8975/4c0f2645ed1c27fe3ef61a1a9126adadfd8d5368-721x421.png" alt="esre" /><p>Para saber mais sobre ESRE, você pode ler nossa postagem esclarecedora no blog, que está disponível <a href="https://www.elastic.co/search-labs/blog/introducing-elasticsearch-relevance-engine-esre">aqui.</a></p><h2>Configurando o ELSER</h2><p>Para aproveitar os recursos <a href="https://www.elastic.co/elasticsearch/elasticsearch-relevance-engine">do ESRE</a> da Elastic, usaremos <a href="https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-elser.html">o ELSER</a> como nosso provedor de modelos.</p><p><em>Para usar os modelos ELSER do Elasticsearch, você precisa de uma licença Platinum ou Enterprise e de um nó dedicado para Machine Learning (ML) com no mínimo 4 GB de espaço. Saiba mais</em> <a href="https://www.elastic.co/guide/en/machine-learning/8.15/ml-nlp-elser.html#elser-req"><em>aqui.</em></a></p><p>Comece criando o ponto de extremidade de inferência:</p>PUT _inference/sparse_embedding/my-elser-model
{
  "service": "elser",
  "service_settings": {
    "num_allocations": 1,
    "num_threads": 1
  }
}<p>Se esta for a sua primeira vez usando o ELSER, você poderá encontrar um erro 502 Bad Gateway enquanto o modelo é carregado em segundo plano. Você pode verificar o status do modelo em <code>Machine Learning &gt; Trained Models</code> no Kibana. Após a implantação, você poderá prosseguir para a próxima etapa.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltee295a3516338302/6a17f65b414c640deb94531d/a7ff94b72d337cde892165d744b9f42fba702a87-1440x649.png" alt="Verificando modelos treinados" /><h2>Dados de indexação</h2><p>Você pode baixar o conjunto de dados <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/esre-with-blazor/books.zip">aqui</a> e, em seguida, importar os dados usando o Kibana. Para isso, acesse a página inicial e clique em "Carregar dados". Em seguida, carregue o arquivo e clique em <code>Import</code>. Por fim, acesse a aba <code>Advanced</code> e cole os seguintes mapeamentos:</p>{
   "properties":{
      "authors":{
         "type":"keyword"
      },
      "categories":{
         "type":"keyword"
      },
      "longDescription":{
         "type":"semantic_text",
         "inference_id":"my-elser-model",
         "model_settings":{
            "task_type":"sparse_embedding"
         }
      },
      "pageCount":{
         "type":"integer"
      },
      "publishedDate":{
         "type":"date"
      },
      "shortDescription":{
         "type":"text"
      },
      "status":{
         "type":"keyword"
      },
      "thumbnailUrl":{
         "type":"keyword"
      },
      "title":{
         "type":"text"
      }
   }
}<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0f07300647edd2ca/6a17f65dfaa913172a93ca0c/1aea0b9c51e275f89339f5d463fcaef799fc3943-1235x1083.png" alt="Importar dados" /><p>Vamos criar um índice capaz de executar consultas semânticas e de texto completo. O tipo de campo <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-text.html">semantic_text</a> cuidará do agrupamento e da incorporação dos dados. <em>Observe que estamos indexando</em> <em><code>longDescription</code></em> <em>como</em> <em><code>semantic_text</code></em><em>, você pode usar copy_to se quiser indexar um campo como</em> <em><code>semantic_text</code></em> <em>e text`</em>.</p><h2>Construindo o aplicativo com Blazor e Elasticsearch</h2><h3>Chave de API</h3><p>A primeira coisa que precisamos fazer é criar uma chave de API para autenticar nossas solicitações ao Elasticsearch. A chave da API deve ser somente leitura e permitir apenas consultas ao índice <code>books-blazor</code> .</p>POST /_security/api_key
{
  "name": "books-blazor-key",
  "role_descriptors": {
    "books-blazor-reader": {
      "indices": [
        {
          "names": ["books-blazor"],
          "privileges": ["read"]
        }
      ]
    }
  }
}<p>Você verá algo parecido com isto:
</p>{
  "id": "XXXXXXXXXXXXXXXXXXXXXXXX",
  "name": "books-blazor-key",
  "api_key": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "encoded": "XXXXXXXXXXXXXXXXXXXXXXXX=="
}<p>Salve o valor do campo de resposta <code>encoded</code> , pois ele será necessário posteriormente. Se você estiver usando o <a href="https://www.elastic.co/cloud/">Elastic Cloud</a>, também precisará do seu ID da nuvem. (Você pode encontrá-lo <a href="https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud#finding-your-cloud-id">aqui</a>).</p><h4>Criando o projeto Blazor</h4><p>Comece instalando o Blazor e criando um projeto de exemplo seguindo as <a href="https://dotnet.microsoft.com/en-us/learn/aspnet/blazor-tutorial/install">instruções oficiais</a>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb5c20b896fe1e98e/6a17f65f6317301566585bff/f5720867d960c91fd1b3c4ae9be174e062e6b2fc-975x830.png" alt="Tutorial de Blazor - Criando um projeto Blazor" /><p>Após a criação do projeto, a estrutura de pastas e arquivos deve ser semelhante a esta:</p>BlazorApp/
|-- BlazorApp.csproj
|-- BlazorApp.sln
|-- Program.cs
|-- appsettings.Development.json
|-- appsettings.json
|-- Properties/
|   `-- launchSettings.json
|-- Components/
|   |-- App.razor
|   |-- Routes.razor
|   |-- _Imports.razor
|   |-- Layout/
|   |   |-- MainLayout.razor
|   |   |-- MainLayout.razor.css
|   |   |-- NavMenu.razor
|   |   `-- NavMenu.razor.css
|   `-- Pages/
|       |-- Counter.razor
|       |-- Error.razor
|       |-- Home.razor
|       `-- Weather.razor
|-- wwwroot/
|-- bin/
`-- obj/ <p>O aplicativo modelo inclui <a href="https://blog.getbootstrap.com/2021/08/04/bootstrap-5-1-0/">o Bootstrap v5.1.0.</a> para estilização.</p><p>Finalize a configuração do projeto instalando o cliente <a href="https://www.elastic.co/guide/en/elasticsearch/client/net-api/8.0/installation.html">Elasticsearch .NET</a> :</p>dotnet add package Elastic.Clients.Elasticsearch<p>Após concluir esta etapa, sua página deverá ter esta aparência:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3b90e394ded7ff56/6a17f660faa913d49f93ca10/ef9d2186c2cf49e78a307c4aa69c51632e84578d-940x529.png" alt="Blazor olá mundo" /><h3>Estrutura de pastas</h3><p>Agora, vamos organizar nossas pastas da seguinte forma:</p>BlazorApp/
|-- Components/
|   |-- Pages/
|   |   |-- Search.razor
|   |   `-- Search.razor.css
|   `-- Elasticsearch/
|       |-- SearchBar.razor
|       |-- Results.razor
|       `-- Facet.razor
|-- Models/
|   |-- Book.cs
|   `-- Response.cs
`-- Services/
    `-- ElasticsearchService.cs<p>Explicação dos arquivos:</p><ul><li><p>Components/Pages/Search.razor: página principal contendo a barra de pesquisa, os resultados e os filtros.</p></li><li><p>Components/Pages/Search.razor.css: estilos de página.</p></li><li><p>Components/Elasticsearch/SearchBar.razor: componente da barra de pesquisa.</p></li><li><p>Components/Elasticsearch/Results.razor: componente de resultados.</p></li><li><p>Components/Elasticsearch/Facet.razor: componente de filtros.</p></li><li><p>Components/Svg/GlassIcon.razor: ícone de pesquisa.</p></li><li><p>Components/_Imports.razor: este arquivo importará todos os componentes.</p></li><li><p>Models/Book.cs: este arquivo armazenará o esquema do campo do livro.</p></li><li><p>Models/Response.cs: este arquivo armazenará o esquema de resposta, incluindo os resultados da pesquisa, as facetas e o total de resultados.</p></li><li><p>Services/ElasticsearchService.cs: Serviço Elasticsearch. Ele será responsável pela conexão e pelas consultas ao Elasticsearch.</p></li></ul><h4>Configuração inicial</h4><p>Vamos começar com uma limpeza.</p><p>Apague os arquivos:</p><ul><li><p>Components/Pages/Counter.razor</p></li><li><p>Componentes/Páginas/Weather.razor</p></li><li><p>Componentes/Páginas/Home.razor</p></li><li><p>Components/Layout/NavMenu.razor</p></li><li><p>Components/Layout/NavMenu.razor.css</p></li></ul><p>Verifique o arquivo <code>/Components/_Imports.razor</code> . Você deve ter as seguintes importações:</p>@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using BlazorApp
@using BlazorApp.Components<h4>Integrando o Elastic ao projeto</h4><p>Agora, vamos importar os componentes do Elasticsearch:</p>@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using BlazorApp
@using BlazorApp.Components
@using BlazorApp.Components.Elasticsearch @* &lt;--- Add this line *@<p>Vamos remover a barra lateral padrão para termos mais espaço para nossa aplicação, removendo-a do arquivo <code>/Components/Layout/MainLayout.razor</code> :</p>@inherits LayoutComponentBase

&lt;div class="page"&gt;
    &lt;main&gt;
        &lt;article class="content"&gt;
            @Body
        &lt;/article&gt;
    &lt;/main&gt;
&lt;/div&gt;

&lt;div id="blazor-error-ui"&gt;
    An unhandled error has occurred.
    &lt;a href="" class="reload"&gt;Reload&lt;/a&gt;
    &lt;a class="dismiss"&gt;🗙&lt;/a&gt;
&lt;/div&gt;<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt441012c5aeac85f8/6a17f661ec0f8949f15a67ac/55bfff607f9167a532f83ad074b479364a346c6e-816x472.png" alt="Barra de navegação removida do layout" /><p>Agora vamos inserir as credenciais do Elasticsearch para os <a href="https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-8.0&amp;tabs=linux#secret-manager">segredos do usuário</a>:</p>dotnet user-secrets init
dotnet user-secrets set ElasticsearchCloudId "your Cloud ID"
dotnet user-secrets set ElasticsearchApiKey "your API Key"<p>Usando esta abordagem, o .Net 8 armazena dados sensíveis em um local separado, fora da pasta do projeto e os torna acessíveis usando a interface <code>IConfiguration</code> . Essas variáveis estarão disponíveis para qualquer projeto .NET que utilize os mesmos segredos de usuário.</p><p>Em seguida, vamos modificar o arquivo <code>Program.cs</code> para ler os segredos e montar o cliente Elasticsearch:</p><p>Primeiro, importe as bibliotecas necessárias:</p>using BlazorApp.Services;
using Elastic.Clients.Elasticsearch;
using Elastic.Transport;<ul><li><p>BlazorApp.Services: contém o serviço Elasticsearch.</p></li><li><p>Elastic.Clients.Elasticsearch: importa a biblioteca cliente Elasticsearch para .NET 8.</p></li><li><p>Elastic.Transport: importa a biblioteca de transporte do Elasticsearch, que nos permite usar a classe ApiKey para autenticar nossas solicitações.</p></li></ul><p>Em segundo lugar, insira o seguinte código antes da linha <code>var app = builder.Build()</code> :</p>// Initialize the Elasticsearch client.
builder.Services.AddScoped(sp =&gt;
{
    // Getting access to the configuration service to read the Elasticsearch credentials.
    var configuration = sp.GetRequiredService&lt;IConfiguration&gt;();
    var cloudId = configuration["ElasticsearchCloudId"];
    var apiKey = configuration["ElasticsearchApiKey"];

    if (string.IsNullOrEmpty(cloudId) || string.IsNullOrEmpty(apiKey))
    {
        throw new InvalidOperationException(
            "Elasticsearch credentials are missing in configuration."
        );
    }

    var settings = new ElasticsearchClientSettings(cloudId, new ApiKey(apiKey)).EnableDebugMode();
    return new ElasticsearchClient(settings);
});<p>Este código lerá as credenciais do Elasticsearch a partir dos segredos do usuário e criará uma instância de cliente Elasticsearch.</p><p>Após a inicialização do cliente Elasticsearch, adicione a seguinte linha para registrar o serviço Elasticsearch:</p>builder.Services.AddScoped&lt;ElasticsearchService&gt;();<p>O próximo passo será construir a lógica de busca no arquivo <code>/Services/ElasticsearchService.cs</code> :</p><p>Primeiro, importe as bibliotecas e os modelos necessários:</p>using BlazorApp.Models;
using Elastic.Clients.Elasticsearch;
using Elastic.Clients.Elasticsearch.QueryDsl;<p>Em segundo lugar, adicione a classe <code>ElasticsearchService</code>, o construtor e as variáveis:</p>namespace BlazorApp.Services
{
    public class ElasticsearchService
    {
        private readonly ElasticsearchClient _client;

        // The logger is used to log information, warnings and errors about the Elasticsearch service and requests.
        private readonly ILogger&lt;ElasticsearchService&gt; _logger;

        public ElasticsearchService(
            ElasticsearchClient client,
            ILogger&lt;ElasticsearchService&gt; logger
        )
        {
            _client = client ?? throw new ArgumentNullException(nameof(client));
            _logger = logger;
        }
    }
}<h4>Configurando a pesquisa</h4><p>Agora, vamos construir nossa lógica de busca:</p>private static Action&lt;RetrieverDescriptor&lt;BookDoc&gt;&gt; BuildHybridQuery(
    string searchTerm,
    Dictionary&lt;string, List&lt;string&gt;&gt; selectedFacets
)
{
    var filters = BuildFilters(selectedFacets);

    return retrievers =&gt;
        retrievers.Rrf(rrf =&gt;
            rrf.RankWindowSize(50)
                .RankConstant(20)
                .Retrievers(
                    retrievers =&gt;
                        retrievers.Standard(std =&gt;
                            std.Query(q =&gt;
                                q.Bool(b =&gt;
                                    b.Must(m =&gt;
                                            m.MultiMatch(mm =&gt;
                                                mm.Query(searchTerm)
                                                    .Fields(
                                                        new[]
                                                        {
                                                            "title",
                                                            "shortDescription",
                                                        }
                                                    )
                                            )
                                        )
                                        .Filter(filters.ToArray())
                                )
                            )
                        ),
                    retrievers =&gt;
                        retrievers.Standard(std =&gt;
                            std.Query(q =&gt;
                                q.Bool(b =&gt;
                                    b.Must(m =&gt;
                                            m.Semantic(sem =&gt;
                                                sem.Field("longDescription")
                                                    .Query(searchTerm)
                                            )
                                        )
                                        .Filter(filters.ToArray())
                                )
                            )
                        )
                )
        );
}

public static List&lt;Action&lt;QueryDescriptor&lt;BookDoc&gt;&gt;&gt; BuildFilters(
    Dictionary&lt;string, List&lt;string&gt;&gt; selectedFacets
)
{
    var filters = new List&lt;Action&lt;QueryDescriptor&lt;BookDoc&gt;&gt;&gt;();

    if (selectedFacets != null)
    {
        foreach (var facet in selectedFacets)
        {
            foreach (var value in facet.Value)
            {
                var field = facet.Key.ToLower();
                if (!string.IsNullOrEmpty(field))
                {
                    filters.Add(m =&gt; m.Term(t =&gt; t.Field(new Field(field)).Value(value)));
                }
            }
        }
    }

    return filters;
}<ul><li><p><code>BuildFilters</code> irá construir os filtros para a consulta de pesquisa usando as facetas selecionadas pelo usuário.</p></li><li><p><code>BuildHybridQuery</code> irá construir uma consulta <a href="https://www.elastic.co/search-labs/tutorials/search-tutorial/vector-search/hybrid-search">de pesquisa híbrida</a> que combina pesquisa de texto completo e pesquisa semântica.</p></li></ul><p>Em seguida, adicione o método de pesquisa:</p>public async Task&lt;ElasticResponse&gt; SearchBooksAsync(
    string searchTerm,
    Dictionary&lt;string, List&lt;string&gt;&gt; selectedFacets
)
{
    try
    {
        _logger.LogInformation($"Performing search for: {searchTerm}");

        // Retrieve the hybrid query with filters applied.
        var retrieverQuery = BuildHybridQuery(searchTerm, selectedFacets);

        var response = await _client.SearchAsync&lt;BookDoc&gt;(s =&gt;
            s.Index("elastic-blazor-books")
                .Retriever(retrieverQuery)
                .Aggregations(aggs =&gt;
                    aggs.Add("Authors", agg =&gt; agg.Terms(t =&gt; t.Field(p =&gt; p.Authors)))
                        .Add(
                            "Categories",
                            agg =&gt; agg.Terms(t =&gt; t.Field(p =&gt; p.Categories))
                        )
                        .Add("Status", agg =&gt; agg.Terms(t =&gt; t.Field(p =&gt; p.Status)))
                )
        );

        if (response.IsValidResponse)
        {
            _logger.LogInformation($"Found {response.Documents.Count} documents");

            var hits = response.Total;
            var facets =
                response.Aggregations != null
                    ? FormatFacets(response.Aggregations)
                    : new Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt;();

            var elasticResponse = new ElasticResponse
            {
                TotalHits = hits,
                Documents = response.Documents.ToList(),
                Facets = facets,
            };

            return elasticResponse;
        }
        else
        {
            _logger.LogWarning($"Invalid response: {response.DebugInformation}");
            return new ElasticResponse();
        }
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Error performing search");
        return new ElasticResponse();
    }
}

public static Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt; FormatFacets(
    Elastic.Clients.Elasticsearch.Aggregations.AggregateDictionary aggregations
)
{
    var facets = new Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt;();

    foreach (var aggregation in aggregations)
    {
        if (
            aggregation.Value
            is Elastic.Clients.Elasticsearch.Aggregations.StringTermsAggregate termsAggregate
        )
        {
            var facetName = aggregation.Key;
            var facetDictionary = ConvertFacetDictionary(
                termsAggregate.Buckets.ToDictionary(b =&gt; b.Key, b =&gt; b.DocCount)
            );
            facets[facetName] = facetDictionary;
        }
    }

    return facets;
}

private static Dictionary&lt;string, long&gt; ConvertFacetDictionary(
    Dictionary&lt;Elastic.Clients.Elasticsearch.FieldValue, long&gt; original
)
{
    var result = new Dictionary&lt;string, long&gt;();
    foreach (var kvp in original)
    {
        result[kvp.Key.ToString()] = kvp.Value;
    }
    return result;
}<ul><li><p><code>SearchBooksAsync</code>A função realizará a busca usando a consulta híbrida e retornará os resultados, incluindo as agregações para a construção das facetas.</p></li><li><p><code>FormatFacets</code>: irá formatar a resposta de agregações em um dicionário.</p></li><li><p><code>ConvertFacetDictionary</code>: irá converter o dicionário de facetas em um formato mais legível.</p></li></ul><p>O próximo passo é criar os modelos que representarão os dados retornados no <code>hits</code> da consulta Elasticsearch que serão impressos como resultados em nossa página de pesquisa.</p><p>Começamos criando o arquivo <code>/Models/Book.cs</code> e adicionando o seguinte:</p>namespace BlazorApp.Models
{
    public class BookDoc
    {
        public string? Title { get; set; }
        public int? PageCount { get; set; }
        public string? PublishedDate { get; set; }
        public string? ThumbnailUrl { get; set; }
        public string? ShortDescription { get; set; }
        public LongDescription? LongDescription { get; set; }
        public string? Status { get; set; }
        public List&lt;string&gt;? Authors { get; set; }
        public List&lt;string&gt;? Categories { get; set; }
    }

    public class LongDescription
    {
        public string? Text { get; set; }
    }
}<p>Em seguida, configure a resposta do Elastic no arquivo <code>/Models/Response.cs</code> e adicione o seguinte:</p>namespace BlazorApp.Models
{
    public class ElasticResponse
    {
        public ElasticResponse()
        {
            Documents = new List&lt;BookDoc&gt;();
            Facets = new Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt;();
        }

        public long TotalHits { get; set; }
        public List&lt;BookDoc&gt; Documents { get; set; }
        public Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt; Facets { get; set; }
    }
}<h4>Configurando uma interface de usuário básica</h4><p>Em seguida, adicione o componente SearchBar. No arquivo <code>/Components/Elasticsearch/SearchBar.razor</code> , adicione o seguinte:</p>@using System.Threading.Tasks

&lt;form @onsubmit="SubmitSearch"&gt;
  &lt;div class="input-group mb-3"&gt;
    &lt;input type="text" @bind-value="searchTerm" class="form-control" placeholder="Enter search term..." /&gt;
    &lt;button type="submit" class="btn btn-primary input-btn"&gt;
      &lt;span class="input-group-svg"&gt;
        Search
      &lt;/span&gt;
    &lt;/button&gt;
  &lt;/div&gt;
&lt;/form&gt;

@code {
  [Parameter]
  public EventCallback&lt;string&gt; OnSearch { get; set; }

  private string searchTerm = "";

  private async Task SubmitSearch()
  {
    await OnSearch.InvokeAsync(searchTerm);
  }
}<p>Este componente contém uma barra de pesquisa e um botão para realizar a pesquisa.</p><p>O Blazor oferece grande flexibilidade ao permitir a geração dinâmica de HTML usando código C# dentro do mesmo arquivo.</p><p>Em seguida, no arquivo <code>/Components/Elasticsearch/Results.razor</code> construiremos o componente de resultados que exibirá os resultados da pesquisa:</p>@using BlazorApp.Models

@if (SearchResults != null &amp;&amp; SearchResults.Any())
{
  &lt;div class="row"&gt;
  @foreach (var result in SearchResults)
    {
      &lt;div class="col-12 mb-3"&gt;
        &lt;div class="card"&gt;
          &lt;div class="row g-0"&gt;
            &lt;div class="col-md-3 image-container"&gt;
              @if (!string.IsNullOrEmpty(result?.ThumbnailUrl))
              {
                &lt;img src="@result?.ThumbnailUrl" class="img-fluid rounded-start" alt="Thumbnail"&gt;
              }
              else
              {
                &lt;div class="placeholder"&gt;
                  @result?.Title
                &lt;/div&gt;
              }
            &lt;/div&gt;

            &lt;div class="col-md-9"&gt; &lt;!-- Adjusted to use the remaining 75% --&gt;
              &lt;div class="card-body"&gt;
                &lt;h4 class="card-title"&gt;
                  @result?.Title
                &lt;/h4&gt;

                &lt;div class="details-container"&gt;
                  &lt;div class=""&gt;

                    @if (result?.Authors?.Any() == true)
                    {
                      &lt;p class="card-text p-first"&gt;
                        Authors: &lt;small class="text-muted"&gt;@string.Join(", ", result.Authors)&lt;/small&gt;
                      &lt;/p&gt;
                    }

                    @if (result?.Categories?.Any() == true)
                    {
                      &lt;p class="card-text p-second"&gt;
                        Categories: &lt;small class="text-muted"&gt;@string.Join(", ", result.Categories)&lt;/small&gt;
                      &lt;/p&gt;
                    }
                  &lt;/div&gt;
                  &lt;div class="numPages-status"&gt;
                    @if (result?.PageCount != null)
                    {
                      &lt;p class="card-text p-first"&gt;
                        Pages: &lt;small class="text-muted"&gt;@result.PageCount&lt;/small&gt;
                      &lt;/p&gt;
                    }

                    @if (result?.Status != null)
                    {
                      &lt;p class="card-text p-second"&gt;
                        Status: &lt;small class="text-muted"&gt;@result.Status&lt;/small&gt;
                      &lt;/p&gt;
                    }
                  &lt;/div&gt;
                &lt;/div&gt;

                &lt;div class="long-text-container"&gt;
                  &lt;p class="card-text"&gt;&lt;small class="text-muted"&gt;@result?.LongDescription?.Text&lt;/small&gt;&lt;/p&gt;
                &lt;/div&gt;
                @if (!string.IsNullOrEmpty(result?.PublishedDate))
                {
                  &lt;div class="date-container"&gt;
                    &lt;p class="card-text"&gt;
                      Published Date: &lt;small class="text-muted small-date"&gt;@FormatDate(result.PublishedDate)&lt;/small&gt;
                    &lt;/p&gt;
                  &lt;/div&gt;
                }
              &lt;/div&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    }
  &lt;/div&gt;
}
else if (SearchResults != null)
{
  &lt;p&gt;No results found.&lt;/p&gt;
}

@code {
  [Parameter]
  public List&lt;BookDoc&gt; SearchResults { get; set; } = new List&lt;BookDoc&gt;();

  private string FormatDate(string? date)
  {
    if (DateTime.TryParse(date, out DateTime parsedDate))
    {
      return parsedDate.ToString("MMMM dd, yyyy");
    }
    return "";
  }
}<p>Por fim, precisaremos criar facetas para filtrar os resultados da pesquisa.</p><p><em>Nota: Os facets são filtros que permitem aos usuários refinar os resultados da pesquisa com base em atributos ou categorias específicos, como tipo de produto, faixa de preço ou marca. Esses filtros geralmente são apresentados como opções clicáveis, frequentemente na forma de caixas de seleção, para ajudar os usuários a refinar sua pesquisa e encontrar resultados relevantes com mais facilidade. No contexto do Elasticsearch, os facets são criados usando</em> <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html"><em>agregações</em></a><em>.</em></p><p>Configuramos as facetas inserindo o seguinte código no arquivo <code>/Components/Elasticsearch/Facet.razor</code>:</p>@if (Facets != null)
{
  &lt;div class="facets-container"&gt;
  @foreach (var facet in Facets)
    {
      &lt;h3&gt;@facet.Key&lt;/h3&gt;
      @foreach (var option in facet.Value)
      {
        &lt;div&gt;
          &lt;input type="checkbox" checked="@IsFacetSelected(facet.Key, option.Key)"
            @onclick="() =&gt; ToggleFacet(facet.Key, option.Key)" /&gt;
          @option.Key (@option.Value)
        &lt;/div&gt;
      }
    }
  &lt;/div&gt;
}


@code {
  [Parameter]
  public Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt;? Facets { get; set; }

  [Parameter]
  public EventCallback&lt;Dictionary&lt;string, List&lt;string&gt;&gt;&gt; OnFacetChanged { get; set; }

  private Dictionary&lt;string, List&lt;string&gt;&gt; selectedFacets = new();

  private void ToggleFacet(string facetName, string facetValue)
  {
    if (!selectedFacets.TryGetValue(facetName, out var facetValues))
    {
      facetValues = selectedFacets[facetName] = new List&lt;string&gt;();
    }

    if (!facetValues.Remove(facetValue))
    {
      facetValues.Add(facetValue);
    }

    OnFacetChanged.InvokeAsync(selectedFacets);
  }

  private bool IsFacetSelected(string facetName, string facetValue)
  {
    return selectedFacets.ContainsKey(facetName) &amp;&amp; selectedFacets[facetName].Contains(facetValue);
  }
}<p>Este componente lê de uma agregação <code>terms</code> nos campos <code>author</code>, <code>categories</code> e <code>status</code> e, em seguida, produz uma lista de filtros para enviar de volta ao Elasticsearch.</p><p>Agora, vamos juntar tudo.</p><p>No arquivo <code>/Components/Pages/Search.razor</code> :</p>@page "/"
@rendermode InteractiveServer
@using BlazorApp.Models
@using BlazorApp.Services
@inject ElasticsearchService ElasticsearchService
@inject ILogger&lt;Search&gt; Logger

&lt;PageTitle&gt;Search&lt;/PageTitle&gt;

&lt;div class="top-row px-4 "&gt;

    &lt;div class="searchbar-container"&gt;
        &lt;h4&gt;Semantic Search with Elasticsearch and Blazor&lt;/h4&gt;

        &lt;SearchBar OnSearch="PerformSearch" /&gt;
    &lt;/div&gt;

    &lt;a href="https://www.elastic.co/search-labs/esre-with-blazor" target="_blank"&gt;About&lt;/a&gt;
&lt;/div&gt;

&lt;div class="px-4"&gt;

    &lt;div class="search-details-container"&gt;
        &lt;p role="status"&gt;Current search term: @currentSearchTerm&lt;/p&gt;
        &lt;p role="status"&gt;Total results: @totalResults&lt;/p&gt;
    &lt;/div&gt;

    &lt;div class="results-facet-container"&gt;
        &lt;div class="facets-container"&gt;
            &lt;Facet Facets="facets" OnFacetChanged="OnFacetChanged" /&gt;
        &lt;/div&gt;
        &lt;div class="results-container"&gt;
            &lt;Results SearchResults="searchResults" /&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

@code {
    private string currentSearchTerm = "";
    private long totalResults = 0;
    private List&lt;BookDoc&gt; searchResults = new List&lt;BookDoc&gt;();
    private Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt; facets = new Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt;();
    private Dictionary&lt;string, List&lt;string&gt;&gt; selectedFacets = new Dictionary&lt;string, List&lt;string&gt;&gt;();

    protected override async Task OnInitializedAsync()
    {
        await PerformSearch();
    }

    private async Task PerformSearch(string searchTerm = "")
    {
        try
        {
            currentSearchTerm = searchTerm;

            var response = await ElasticsearchService.SearchBooksAsync(currentSearchTerm, selectedFacets);
            if (response != null)
            {
                searchResults = response.Documents;
                facets = response.Facets;
                totalResults = response.TotalHits;
            }
            else
            {
                Logger.LogWarning("Search response is null.");
            }

            StateHasChanged();
        }
        catch (Exception ex)
        {
            Logger.LogError(ex, "Error performing search.");
        }
    }

    private async Task OnFacetChanged(Dictionary&lt;string, List&lt;string&gt;&gt; newSelectedFacets)
    {
        selectedFacets = newSelectedFacets;
        await PerformSearch(currentSearchTerm);
    }
}<p>Nossa página está funcionando!</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc750e55fff43de4b/6a17f6636864a4ab08b68931/5f0f25cb029a34df7f577a9f307278226ed07c3f-816x473.png" alt="Exemplo de página Blazor" /><p>Como você pode ver, a página é funcional, mas carece de estilo. Vamos adicionar um pouco de CSS para deixar a aparência mais organizada e responsiva.</p><p>Vamos começar a substituir os estilos de layout. No arquivo <code>Components/Layout/MainLayout.razor.css</code> :</p>.page {
  position: relative;
  display: flex;
  flex-direction: column;
}

main {
  flex: 1;
}

#blazor-error-ui {
  background: lightyellow;
  bottom: 0;
  box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
  display: none;
  left: 0;
  padding: 0.6rem 1.25rem 0.7rem 1.25rem;
  position: fixed;
  width: 100%;
  z-index: 1000;
}

#blazor-error-ui .dismiss {
  cursor: pointer;
  position: absolute;
  right: 0.75rem;
  top: 0.5rem;
}<p>Adicione os estilos para a página de pesquisa no arquivo <code>Components/Pages/Search.razor.css</code> :</p>.input-group .input-group-svg {
  background: transparent;
  border: transparent;
  pointer-events: none;
}

.results-facet-container {
  display: flex;
  margin-top: 1rem;
  overflow-x: auto;
}

.search-details-container {
  display: flex;
  justify-content: space-between;
  margin-top: 1rem;
}

.searchbar-container {
  padding-top: 2rem;
  display: flex;
  flex-direction: column; 
  flex-grow: 1;
  height: 100%;
  max-width: 100%; 
}

.searchbar-container h4 {
  margin: 0;
}

.top-row {
  margin-top: -1.1rem;
  position: relative; 
  background-color: hsl(216, 29%, 67%);
  border-bottom: 1px solid #d6d5d5;
  display: flex;
  align-items: center;
  height: 100%;
  padding: 0 1rem;
}

.top-row a {
  margin-left: auto;
  margin-top: -4rem; 
  color: #000000;
  text-decoration: none;
}

.top-row a:hover {
  text-decoration: underline;
}

@media (max-width: 640.98px) {
  .top-row {
    justify-content: space-between;
  }

  .top-row ::deep a,
  .top-row ::deep .btn-link {
    margin-left: 0;
  }
}

@media (min-width: 641px) {
  .top-row.auth ::deep a:first-child {
    flex: 1;
    text-align: right;
    width: 0;
  }

  .top-row,
  article {
    padding-left: 2rem !important;
    padding-right: 1.5rem !important;
  }
}<p>Nossa página está começando a ficar melhor:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3ecc49f404f18591/6a17f6654b055d959b432384/3dadd7ade2dd9070300a4e0514a4da2ae9cc9fb9-817x473.png" alt="Página Blazor após a adição de estilos para a página de pesquisa." /><p>Vamos dar os toques finais:</p><p>Crie os seguintes arquivos:</p><ul><li><p>Components/Elasticsearch/Facet.razor.css</p></li><li><p>Components/Elasticsearch/Results.razor.css</p></li></ul><p>E adicione os estilos para <code>Facet.razor.css</code>:</p>.facets-container {
  font-size: 15px;
  margin-right: 4rem;
  overflow-x: auto;
  white-space: nowrap;
  max-width: 300px;
}

.results-facet-container {
  display: flex;
  margin-top: 1rem;
  overflow-x: auto;
}

.results-facet-container &gt; * {
  flex-shrink: 0;
}<p>Para <code>Results.razor.css</code>:</p>.image-container {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
  padding: 1rem;
  box-sizing: border-box;
}

.image-container img {
  max-width: 100%;
  height: auto;
  border-radius: 0.5rem;
}

.placeholder {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
  width: 100%;
  background-color: #f0f0f0;
  border: 1px solid #ccc;
  font-size: 0.9rem;
  color: #888;
  text-align: center;
  padding: 1rem;
  border-radius: 0.5rem;
}

.card-body {
  padding: 1rem;
}

.details-container {
  display: flex;
  justify-content: space-between;
  padding: 1.5rem 0;
}

.date-container {
  margin-top: 1rem;
  display: flex;
  justify-content: flex-end;
}

.date-container .small-date {
  font-weight: bold;
}<p>Resultado final:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt78bbd8e6f9e23988/6a17f6664b055d2ed8432388/3e68ec4a38775fdfbeaa1a990e6a1e11dfb081d8-816x472.png" alt="Resultado final da criação da página do aplicativo Blazor" /><p>Para executar o aplicativo, você pode usar o seguinte comando:</p><p><code>dotnet watch</code></p><p>Você conseguiu! Agora você pode pesquisar livros em seu índice Elasticsearch usando a barra de pesquisa e filtrar os resultados por autor, categoria e status.</p><h3>Realização de buscas semânticas e de texto completo</h3><p>Por padrão, nosso aplicativo realizará uma <a href="https://www.elastic.co/search-labs/tutorials/search-tutorial/vector-search/hybrid-search">pesquisa híbrida</a> usando tanto <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html">pesquisa de texto completo</a> quanto <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-semantic-query.html">pesquisa semântica</a>. Você pode alterar a lógica de pesquisa criando dois métodos separados, um para pesquisa de texto completo e outro para pesquisa semântica, e então selecionando um dos métodos para construir a consulta com base na entrada do usuário.</p><p>Adicione os seguintes métodos à classe <code>ElasticsearchService</code> no arquivo <code>/Services/ElasticsearchService.cs</code> :</p>private static Action&lt;QueryDescriptor&lt;BookDoc&gt;&gt; BuildSemanticQuery(
    string searchTerm,
    Dictionary&lt;string, List&lt;string&gt;&gt; selectedFacets
)
{
    var filters = BuildFilters(selectedFacets);

    return query =&gt;
        query.Bool(b =&gt;
            b.Must(m =&gt; m.Semantic(sem =&gt; sem.Field("longDescription").Query(searchTerm)))
                .Filter(filters.ToArray())
        );
}

private static Action&lt;QueryDescriptor&lt;BookDoc&gt;&gt; BuildMultiMatchQuery(
    string searchTerm,
    Dictionary&lt;string, List&lt;string&gt;&gt; selectedFacets
)
{
    var filters = BuildFilters(selectedFacets);

    if (string.IsNullOrEmpty(searchTerm))
    {
        return query =&gt; query.Bool(b =&gt; b.Filter(filters.ToArray()));
    }

    return query =&gt;
        query.Bool(b =&gt;
            b.Should(m =&gt;
                    m.MultiMatch(mm =&gt;
                        mm.Query(searchTerm).Fields(new[] { "title", "shortDescription" })
                    )
                )
                .Filter(filters.ToArray())
        );
}<p>Ambos os métodos funcionam de forma semelhante ao método <code>BuildHybridQuery</code> , mas realizam apenas pesquisa de texto completo ou pesquisa semântica.</p><p>Você pode modificar o método <code>SearchBooksAsync</code> para usar o método de pesquisa selecionado:</p>public async Task&lt;ElasticResponse&gt; SearchBooksAsync(
    string searchTerm,
    Dictionary&lt;string, List&lt;string&gt;&gt; selectedFacets
)
{
    try
    {
        _logger.LogInformation($"Performing search for: {searchTerm}");
        
        // Modify the query builder to use the selected search method.
        var multiMatchQuery = BuildMultiMatchQuery(searchTerm, selectedFacets); // For full text search
        var semanticQuery = BuildSemanticQuery(searchTerm, selectedFacets); // For semantic search

        // In this case we will not use retrievers, but you can add them if you want to use them.
        var response = await _client.SearchAsync&lt;BookDoc&gt;(s =&gt;
            s.Index("elastic-blazor-books")
                .Query(multiMatchQuery) // Change this line to use different search methods, for example: .Query(semanticQuery) for semantic search
                .Aggregations(aggs =&gt;
                    aggs.Add("Authors", agg =&gt; agg.Terms(t =&gt; t.Field(p =&gt; p.Authors)))
                        .Add(
                            "Categories",
                            agg =&gt; agg.Terms(t =&gt; t.Field(p =&gt; p.Categories))
                        )
                        .Add("Status", agg =&gt; agg.Terms(t =&gt; t.Field(p =&gt; p.Status)))
                )
        );

        if (response.IsValidResponse)
        {
            _logger.LogInformation($"Found {response.Documents.Count} documents");

            var hits = response.Total;
            var facets =
                response.Aggregations != null
                    ? FormatFacets(response.Aggregations)
                    : new Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt;();

            var elasticResponse = new ElasticResponse
            {
                TotalHits = hits,
                Documents = response.Documents.ToList(),
                Facets = facets,
            };

            return elasticResponse;
        }
        else
        {
            _logger.LogWarning($"Invalid response: {response.DebugInformation}");
            return new ElasticResponse();
        }
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Error performing search");
        return new ElasticResponse();
    }
}<p>Você pode encontrar o formulário de inscrição completo <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/esre-with-blazor">aqui.</a></p><h2>Conclusão</h2><p>Blazor é uma estrutura eficaz que permite criar aplicações web usando C#. O Elasticsearch é um mecanismo de busca poderoso que permite criar aplicativos de busca. Combinando ambos, você pode facilmente criar aplicativos de busca robustos, aproveitando o poder do ESRE para criar uma <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-search.html">experiência de busca semântica</a> em pouco tempo.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/search-app-with-esre-blazor</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/search-app-with-esre-blazor</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <category><![CDATA[.NET]]></category>
    <dc:creator><![CDATA[Gustavo Llermaly]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte7424ac5f0b223b4/6a17f668414c641971945323/7ba0d6bec908bfcae966b7f38626fabd682c6f3d-1200x628.png" length="0" type="image/png"/>
    <pubDate>Wed, 09 Oct 2024 00:00:00 GMT</pubDate>
  </item>
  <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[Técnicas avançadas de RAG, parte 2: Consultas e testes]]></title>
    <description><![CDATA[Discutir e implementar técnicas que possam aumentar o desempenho do RAG. Parte 2 de 2, com foco em consultas e testes de um pipeline RAG avançado.]]></description>
    <content:encoded><![CDATA[<p><em>Todo o código pode ser encontrado </em><a href="https://github.com/elastic/elasticsearch-labs/tree/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques"><em>no repositório Searchlabs, na branch advanced-rag-techniques</em></a><em>.</em></p><p>Bem-vindo(a) à Parte 2 do nosso artigo sobre Técnicas Avançadas de RAG! Na <a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1">parte 1 desta série</a>, configuramos, discutimos e implementamos os componentes de processamento de dados do pipeline RAG avançado:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9a4691874a19d8da/6a170b3f47d49c99f22d8a24/72b51ba2ae5e5977b56e5b915674753d6cfd0e56-1440x840.jpg" alt="Gasoduto RAG avançado" /><p>Nesta parte, vamos prosseguir com a consulta e o teste da nossa implementação. Vamos direto ao assunto!</p><h3>Índice</h3><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#searching-and-retrieving,-generating-answers">Pesquisar e recuperar, gerar respostas</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#enriching-queries-with-synonyms">Enriquecendo as consultas com sinônimos</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#hyde-hypothetical-document-embedding">HyDE (Incorporação Hipotética de Documentos)</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#hybrid-search">Busca híbrida</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#experiments">Experimentos</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#summary-of-results">Resumo dos resultados</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#test-1-who-audits-elastic">Teste 1: Quem audita a Elastic?</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#advancedrag">RAG Avançado</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#simplerag">SimpleRAG</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#test-2--total-revenue-2023">Teste 2: receita total em 2023</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#advancedrag-1">RAG Avançado</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#simplerag-1">SimpleRAG</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#test-3-what-product-does-growth-primarily-depend-on-how-much">Teste 3: De qual produto depende principalmente o crescimento? Quanto?</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#advancedrag-2">RAG Avançado</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#simplerag-2">SimpleRAG</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#test-4-describe-employee-benefit-plan">Teste 4: Descreva o plano de benefícios para funcionários</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#advancedrag-3">RAG Avançado</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#simplerag-3">SimpleRAG</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#test-5-which-companies-did-elastic-acquire">Teste 5: Quais empresas a Elastic adquiriu?</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#advancedrag-4">RAG Avançado</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#simplerag-4">SimpleRAG</a></p></li></ul></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#conclusion">Conclusão</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#appendix">Apêndice</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#prompts">Prompts</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#rag-question-answering-prompt">Pergunta RAG para responder:</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#elastic-query-generator-prompt">prompt do gerador de consultas elásticas</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#potential-questions-generator-prompt">Possíveis perguntas para o gerador</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#hyde-generator-prompt">prompt do gerador HyDE</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#sample-hybrid-search-query">Exemplo de consulta de pesquisa híbrida</a></p></li></ul></li></ul><h2>Pesquisar e recuperar, gerar respostas</h2><p>Vamos fazer nossa primeira pergunta, idealmente alguma informação encontrada principalmente no relatório anual. Que tal:</p>Who audits Elastic?"
<p>Agora, vamos aplicar algumas de nossas técnicas para aprimorar a consulta.</p><h3>Enriquecendo as consultas com sinônimos</h3><p>Em primeiro lugar, vamos aumentar a diversidade na formulação da consulta e transformá-la em um formato que possa ser facilmente processado em uma consulta do Elasticsearch. Vamos utilizar o GPT-4o para converter a consulta em uma lista de cláusulas OR. Vamos escrever esta pergunta:</p>
ELASTIC_SEARCH_QUERY_GENERATOR_PROMPT = '''
You are an AI assistant specialized in generating Elasticsearch query strings. Your task is to create the most effective query string for the given user question. This query string will be used to search for relevant documents in an Elasticsearch index.

Guidelines:
1. Analyze the user's question carefully.
2. Generate ONLY a query string suitable for Elasticsearch's match query.
3. Focus on key terms and concepts from the question.
4. Include synonyms or related terms that might be in relevant documents.
5. Use simple Elasticsearch query string syntax if helpful (e.g., OR, AND).
6. Do not use advanced Elasticsearch features or syntax.
7. Do not include any explanations, comments, or additional text.
8. Provide only the query string, nothing else.

For the question "What is Clickthrough Data?", we would expect a response like:
clickthrough data OR click-through data OR click through rate OR CTR OR user clicks OR ad clicks OR search engine results OR web analytics

AND operator is not allowed. Use only OR.

User Question:
[The user's question will be inserted here]

Generate the Elasticsearch query string:
'''
<p>Quando aplicado à nossa consulta, o GPT-4o gera sinônimos da consulta base e vocabulário relacionado.</p>'audits elastic OR 
elasticsearch audits OR 
elastic auditor OR 
elasticsearch auditor OR 
elastic audit firm OR 
elastic audit company OR 
elastic audit organization OR 
elastic audit service'
<p>Na classe <code>ESQueryMaker</code> , defini uma função para dividir a consulta:</p>def parse_or_query(self, query_text: str) -&gt; List[str]:
    # Split the query by 'OR' and strip whitespace from each term
    # This converts a string like "term1 OR term2 OR term3" into a list ["term1", "term2", "term3"]
    return [term.strip() for term in query_text.split(' OR ')]
<p>Sua função é pegar essa sequência de cláusulas OR e dividi-las em uma lista de termos, permitindo-nos fazer uma correspondência múltipla em nossos campos-chave do documento:</p>["original_text", 'keyphrases', 'potential_questions', 'entities']
<p>Finalmente, cheguei a esta pergunta:</p> 'query': {
    'bool': {
        'must': [
            {
                'multi_match': {
                'query': 'audits Elastic Elastic auditing Elastic audit process Elastic compliance Elastic security audit Elasticsearch auditing Elasticsearch compliance Elasticsearch security audit',
                'fields': [
                    'original_text',
                'keyphrases',
                'potential_questions',
                'entities'
                ],
                'type': 'best_fields',
                'operator': 'or'
                }
            }
      ]
<p>Isso abrange muito mais aspectos do que a consulta original, reduzindo, esperamos, o risco de perder um resultado de pesquisa por termos esquecido um sinônimo. Mas podemos fazer mais.</p><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#table-of-contents">Voltar ao topo</a></p><h3>HyDE (Incorporação Hipotética de Documentos)</h3><p>Vamos recorrer ao GPT-4o novamente, desta vez para implementar <a href="https://arxiv.org/abs/2212.10496">o HyDE</a>.</p><p>A premissa básica do HyDE é gerar um documento hipotético – o tipo de documento que provavelmente conteria a resposta à consulta original. A veracidade ou exatidão do documento não é uma preocupação. Com isso em mente, vamos escrever a seguinte pergunta:</p>HYDE_DOCUMENT_GENERATOR_PROMPT = '''
You are an AI assistant specialized in generating hypothetical documents based on user queries. Your task is to create a detailed, factual document that would likely contain the answer to the user's question. This hypothetical document will be used to enhance the retrieval process in a Retrieval-Augmented Generation (RAG) system.

Guidelines:
1. Carefully analyze the user's query to understand the topic and the type of information being sought.
2. Generate a hypothetical document that:
   a. Is directly relevant to the query
   b. Contains factual information that would answer the query
   c. Includes additional context and related information
   d. Uses a formal, informative tone similar to an encyclopedia or textbook entry
3. Structure the document with clear paragraphs, covering different aspects of the topic.
4. Include specific details, examples, or data points that would be relevant to the query.
5. Aim for a document length of 200-300 words.
6. Do not use citations or references, as this is a hypothetical document.
7. Avoid using phrases like "In this document" or "This text discusses" - write as if it's a real, standalone document.
8. Do not mention or refer to the original query in the generated document.
9. Ensure the content is factual and objective, avoiding opinions or speculative information.
10. Output only the generated document, without any additional explanations or meta-text.

User Question:
[The user's question will be inserted here]

Generate a hypothetical document that would likely contain the answer to this query:
'''
<p>Como a busca vetorial normalmente opera com base na similaridade de vetores de cosseno, a premissa do HyDE é que podemos obter melhores resultados combinando documentos com documentos em vez de consultas com documentos.</p><p>O que nos interessa é a estrutura, a fluidez e a terminologia. Não se trata tanto de factualidade. O GPT-4o gera um documento HyDE como este:</p>'Elastic N.V., the parent company of Elastic, the organization known for developing Elasticsearch, is subject to audits to ensure financial accuracy, 
regulatory compliance, and the integrity of its financial statements. The auditing of Elastic N.V. is typically conducted by an external, 
independent auditing firm. This is common practice for publicly traded companies to provide stakeholders with assurance regarding the company\'s 
financial position and operations.\n\nThe primary external auditor for Elastic is the audit firm Ernst &amp; Young LLP (EY). Ernst &amp; Young is one of the 
four largest professional services networks in the world, commonly referred to as the "Big Four" audit firms. These firms handle a substantial number 
of audits for major corporations around the globe, ensuring adherence to generally accepted accounting principles (GAAP) and international financial 
reporting standards (IFRS).\n\nThe audit process conducted by EY involves several steps. Initially, the auditors perform a risk assessment to identify 
areas where misstatements due to error or fraud could occur. They then design audit procedures to test the accuracy and completeness of financial statements,
 which include examining financial transactions, assessing internal controls, and reviewing compliance with relevant laws and regulations. Upon completion of 
 the audit, Ernst &amp; Young issues an audit report, which includes the auditor’s opinion on whether the financial statements are free from material misstatement 
 and are presented fairly in accordance with the applicable financial reporting framework.\n\nIn addition to external audits by firms like Ernst &amp; Young, 
 Elastic may also be subject to internal audits. Internal audits are performed by the company’s own internal auditors to evaluate the effectiveness of internal 
 controls, risk management, and governance processes.\n\nOverall, the auditing process plays a crucial role in maintaining the transparency and reliability of 
 Elastic\'s financial information, providing confidence to investors, regulators, and other stakeholders.'
<p>Parece bastante convincente, como o candidato ideal para os tipos de documentos que gostaríamos de indexar. Vamos incorporar isso e usar para busca híbrida.</p><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#table-of-contents">Voltar ao topo</a></p><h3>Busca híbrida</h3><p>Este é o núcleo da nossa lógica de busca. Nosso componente de busca lexical serão as strings da cláusula OR geradas. Nosso componente vetorial denso será um documento HyDE incorporado (também conhecido como vetor de busca). Utilizamos o KNN para identificar de forma eficiente vários documentos candidatos mais próximos do nosso vetor de busca. Por padrão, denominamos nosso componente de busca lexical <em>como "Pontuação com TF-IDF e BM25"</em> . Finalmente, as pontuações lexicais e de vetor denso serão combinadas usando a proporção 30/70 recomendada por <a href="https://arxiv.org/abs/2407.01219">Wang et al</a>.</p>def hybrid_vector_search(self, index_name: str, query_text: str, query_vector: List[float], 
                         text_fields: List[str], vector_field: str, 
                         num_candidates: int = 100, num_results: int = 10) -&gt; Dict:
    """
    Perform a hybrid search combining text-based and vector-based similarity.

    Args:
        index_name (str): The name of the Elasticsearch index to search.
        query_text (str): The text query string, which may contain 'OR' separated terms.
        query_vector (List[float]): The query vector for semantic similarity search.
        text_fields (List[str]): List of text fields to search in the index.
        vector_field (str): The name of the field containing document vectors.
        num_candidates (int): Number of candidates to consider in the initial KNN search.
        num_results (int): Number of final results to return.

    Returns:
        Dict: A tuple containing the Elasticsearch response and the search body used.
    """
    try:
        # Parse the query_text into a list of individual search terms
        # This splits terms separated by 'OR' and removes any leading/trailing whitespace
        query_terms = self.parse_or_query(query_text)

        # Construct the search body for Elasticsearch
        search_body = {
            # KNN search component for vector similarity
            "knn": {
                "field": vector_field,  # The field containing document vectors
                "query_vector": query_vector,  # The query vector to compare against
                "k": num_candidates,  # Number of nearest neighbors to retrieve
                "num_candidates": num_candidates  # Number of candidates to consider in the KNN search
            },
            "query": {
                "bool": {
                    # The 'must' clause ensures that matching documents must satisfy this condition
                    # Documents that don't match this clause are excluded from the results
                    "must": [
                        {
                            # Multi-match query to search across multiple text fields
                            "multi_match": {
                                "query": " ".join(query_terms),  # Join all query terms into a single space-separated string
                                "fields": text_fields,  # List of fields to search in
                                "type": "best_fields",  # Use the best matching field for scoring
                                "operator": "or"  # Match any of the terms (equivalent to the original OR query)
                            }
                        }
                    ],
                    # The 'should' clause boosts relevance but doesn't exclude documents
                    # It's used here to combine vector similarity with text relevance
                    "should": [
                        {
                            # Custom scoring using a script to combine vector and text scores
                            "script_score": {
                                "query": {"match_all": {}},  # Apply this scoring to all documents that matched the 'must' clause
                                "script": {
                                    # Script to combine vector similarity and text relevance
                                    "source": """
                                    # Calculate vector similarity (cosine similarity + 1)
                                    # Adding 1 ensures the score is always positive
                                    double vector_score = cosineSimilarity(params.query_vector, params.vector_field) + 1.0;
                                    # Get the text-based relevance score from the multi_match query
                                    double text_score = _score;
                                    # Combine scores: 70% vector similarity, 30% text relevance
                                    # This weighting can be adjusted based on the importance of semantic vs keyword matching
                                    return 0.7 * vector_score + 0.3 * text_score;
                                    """,
                                    # Parameters passed to the script
                                    "params": {
                                        "query_vector": query_vector,  # Query vector for similarity calculation
                                        "vector_field": vector_field  # Field containing document vectors
                                    }
                                }
                            }
                        }
                    ]
                }
            }
        }

        # Execute the search request against the Elasticsearch index
        response = self.conn.search(index=index_name, body=search_body, size=num_results)
        # Log the successful execution of the search for monitoring and debugging
        logger.info(f"Hybrid search executed on index: {index_name} with text query: {query_text}")
        # Return both the response and the search body (useful for debugging and result analysis)
        return response, search_body
    except Exception as e:
        # Log any errors that occur during the search process
        logger.error(f"Error executing hybrid search on index: {index_name}. Error: {e}")
        # Re-raise the exception for further handling in the calling code
        raise e
<p>Finalmente, podemos montar uma função RAG. Nosso processo RAG, da pergunta à resposta, seguirá este fluxo:</p><ol><li><p>Converter consulta em cláusulas OR.</p></li><li><p>Gere o documento HyDE e incorpore-o.</p></li><li><p>Passe ambos como entradas para a Busca Híbrida.</p></li><li><p>Recuperar os n melhores resultados, inverter a ordem para que a pontuação mais relevante seja a "mais recente" na memória contextual do LLM (Empacotamento Inverso). Exemplo de Empacotamento Inverso: Consulta: "Técnicas de otimização de consultas do Elasticsearch". Documentos recuperados (ordenados por relevância): Ordem invertida para o contexto do LLM: Ao inverter a ordem, a informação mais relevante (1) aparece por último no contexto, potencialmente recebendo mais atenção do LLM durante a geração de respostas.</p><ol><li><p>"Use consultas booleanas para combinar vários critérios de pesquisa de forma eficiente."</p></li><li><p>"Implementar estratégias de cache para melhorar os tempos de resposta das consultas."</p></li><li><p>"Otimize os mapeamentos de índice para um desempenho de pesquisa mais rápido."</p></li><li><p>"Otimize os mapeamentos de índice para um desempenho de pesquisa mais rápido."</p></li><li><p>"Implementar estratégias de cache para melhorar os tempos de resposta das consultas."</p></li><li><p>"Use consultas booleanas para combinar vários critérios de pesquisa de forma eficiente."</p></li></ol></li><li><p>Passe o contexto para o LLM para geração.</p></li></ol>def get_context(index_name, 
                match_query, 
                text_query, 
                fields, 
                num_candidates=100, 
                num_results=20, 
                text_fields=["original_text", 'keyphrases', 'potential_questions', 'entities'], 
                embedding_field="primary_embedding"):

    embedding=embedder.get_embeddings_from_text(text_query)

    results, search_body = es_query_maker.hybrid_vector_search(
        index_name=index_name,
        query_text=match_query,
        query_vector=embedding[0][0],
        text_fields=text_fields,
        vector_field=embedding_field,
        num_candidates=num_candidates,
        num_results=num_results
    )

    # Concatenates the text in each 'field' key of the search result objects into a single block of text.
    context_docs=['\n\n'.join([field+":\n\n"+j['_source'][field] for field in fields]) for j in results['hits']['hits']]

    # Reverse Packing to ensure that the highest ranking document is seen first by the LLM.
    context_docs.reverse()
    return context_docs, search_body

def retrieval_augmented_generation(query_text):
    match_query= gpt4o.generate_query(query_text)
    fields=['original_text']

    hyde_document=gpt4o.generate_HyDE(query_text)

    context, search_body=get_context(index_name, match_query, hyde_document, fields)

    answer= gpt4o.basic_qa(query=query_text, context=context)
    return answer, match_query, hyde_document, context, search_body

<p>Vamos executar nossa consulta e obter a resposta:</p>According to the context, Elastic N.V. is audited by an independent registered public accounting firm, PricewaterhouseCoopers (PwC). 
This information is found in the section titled "report of independent registered public accounting firm," which states:

"We have audited the accompanying consolidated balance sheets of Elastic N.V. [...] / s / pricewaterhouseco."
<p>Legal. Isso mesmo.</p><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#table-of-contents">Voltar ao topo</a></p><h2>Experimentos</h2><p>Há uma pergunta importante a ser respondida agora. O que ganhamos investindo tanto esforço e complexidade adicional nessas implementações?</p><p>Vamos fazer uma pequena comparação. O pipeline RAG que implementamos em comparação com a busca híbrida básica, sem nenhuma das melhorias que fizemos. Realizaremos uma pequena série de testes para verificar se notamos alguma diferença significativa. Vamos nos referir ao RAG que acabamos de implementar como AdvancedRAG e ao pipeline básico como SimpleRAG.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf605c8246989df32/6a1711178b73cbc61d18a11d/8da40067835ab8b4dc12fe52a51a6c26858ad32f-1440x1095.jpg" alt="Pipeline RAG simples" /><h4>Resumo dos resultados</h4><p>Esta tabela resume os resultados de cinco testes de ambos os pipelines RAG. Avaliei a superioridade relativa de cada método com base no detalhamento e na qualidade das respostas, mas essa é uma avaliação totalmente subjetiva. As respostas corretas estão reproduzidas abaixo desta tabela para sua análise. Dito isso, vamos dar uma olhada em como eles se saíram!</p><p>O SimpleRAG não conseguiu responder às perguntas 1 e 5. O AdvancedRAG, por sua vez, apresentou respostas muito mais detalhadas nas perguntas 2, 3 e 4. Com base nesse maior nível de detalhamento, considerei as respostas do AdvancedRAG de melhor qualidade.</p><p>Teste</p><p>Pergunta</p><p>Desempenho AdvancedRAG</p><p>Desempenho SimpleRAG</p><p>Latência RAG Avançada</p><p>Latência SimpleRAG</p><p>Ganhador</p><p>1</p><p>Quem audita a Elastic?</p><p>Identificou corretamente a PwC como auditora.</p><p>Não foi possível identificar o auditor.</p><p>11,6s</p><p>4,4s</p><p>RAG Avançado</p><p>2</p><p>Qual foi a receita total em 2023?</p><p>Forneceu o valor correto da receita. Incluímos contexto adicional com a receita de anos anteriores.</p><p>Forneceu o valor correto da receita.</p><p>13,3s</p><p>2,8s</p><p>RAG Avançado</p><p>3</p><p>De qual produto depende principalmente o crescimento? Quanto?</p><p>Identificamos corretamente o Elastic Cloud como o principal fator impulsionador. Inclui contexto geral de receita e detalhes adicionais.</p><p>Identificamos corretamente o Elastic Cloud como o principal fator impulsionador.</p><p>14,1s</p><p>12,8s</p><p>RAG Avançado</p><p>4</p><p>Descreva o plano de benefícios para funcionários.</p><p>Apresentou uma descrição completa dos planos de aposentadoria, programas de saúde e outros benefícios. Inclui valores de contribuição específicos para diferentes anos.</p><p>Apresentou uma boa visão geral dos benefícios, incluindo remuneração, planos de aposentadoria, ambiente de trabalho e o programa Elastic Cares.</p><p>26,6s</p><p>11,6s</p><p>RAG Avançado</p><p>5</p><p>Quais empresas a Elastic adquiriu?</p><p>As aquisições recentes mencionadas no relatório foram listadas corretamente (CmdWatch, Build Security, Optimyze). Foram fornecidas algumas datas de aquisição e preços de compra.</p><p>Não foi possível recuperar informações relevantes do contexto fornecido.</p><p>11,9s</p><p>2,7s</p><p>RAG Avançado</p><h4>Teste 1: Quem audita a Elastic?</h4><p>RAG Avançado</p>According to the context, Elastic N.V. is audited by an independent 
registered public accounting firm, PricewaterhouseCoopers (PwC). 
This information is found in the section titled "report of 
independent registered public accounting firm," which states:
"We have audited the accompanying consolidated balance sheets 
of Elastic N.V. [...] / s / pricewaterhouseco."
<p>SimpleRAG</p>According to the provided context, no specific auditing firm 
or company is mentioned as the auditor for Elastic. 
The context provided talks about various features, strengths, 
deployment options, and other details related to Elastic and 
its operations but does not specify which auditing firm is 
responsible for auditing Elastic's financial statements or 
internal controls.
<p><strong>Resumo</strong>: A SimpleRAG não identificou a PwC como auditora.</p><p>Bem, isso é realmente surpreendente. Parece ser uma falha de busca por parte do SimpleRAG. Não foram recuperados documentos relacionados à auditoria. Vamos diminuir um pouco a dificuldade no próximo teste.</p><h4>Teste 2: receita total em 2023</h4><p>RAG Avançado</p>### Total Revenue for 2023

According to the provided context, the total revenue for the 
year ended April 30, 2023, was **$1,068,989,000**. This 
information can be found several times in the provided text. 
Specifically, one part states:

"The following table sets forth our results of operations 
for the periods presented in dollars and as a percentage of 
our total revenue. ... year ended April 30, 2023, ... 
total revenue 1,068,989".

Additionally, it is mentioned here as well:

"For the years ended April 30, 2023, 2022, and 2021, 
representing year-over-year growth of 24% for the year 
ended April 30, 2023... total revenue $1,068,989".

Therefore, the total revenue for 2023 is $1.069 billion.
<p>SimpleRAG</p>According to the context provided, the total revenue for the fiscal year ended April 30, 2023, is:

$1,068,989,000 (or approximately $1.1 billion).
<p><strong>Resumo</strong>: Ambas as equipes RAG acertaram a resposta: receita total de US$ 1.068.989.000 em 2023.</p><p>Ambos estavam bem aqui. Parece que a AdvancedRAG pode ter adquirido uma gama mais ampla de documentos? Certamente a resposta é mais detalhada e incorpora informações de anos anteriores. Isso era de se esperar, considerando as melhorias que fizemos, mas ainda é muito cedo para afirmar algo com certeza.</p><p>Vamos aumentar a dificuldade.</p><h4>Teste 3: De qual produto depende principalmente o crescimento? Quanto?</h4><p>RAG Avançado</p>### Product Growth Primarily Depends On:
According to the context, "our revenue is derived primarily from 
subscriptions of search, observability and security built into 
the elastic stack." The document mentions various strategies and 
factors contributing to the company’s growth which include investment 
in "elastic cloud," "elastic stack," and the conversion of free 
users to paid subscribers.

### Quantitative Data:
- **Revenue**: The document states, "Our total revenue was $1.1 billion, 
$862.4 million, and $608.5 million for the years ended April 30, 2023, 
2022, and 2021, respectively."
- **Subscription Revenue**: "Subscriptions accounted for 92%, 93%, 
and 93% of our total revenue for the years ended April 30, 2023, 
2022, and 2021, respectively."
- **Elastic Cloud Contribution**: "For the years ended April 30, 2023, 
2022, and 2021, Elastic Cloud contributed 40%, 35%, and 27% of our 
total revenue, respectively."
- **Customer Growth**: The number of customers increased from 
"over 15,000 as of April 30, 2021," to "approximately 20,200 as of 
April 30, 2023."

### Summary:
Product growth for the company primarily depends on the "Elastic Stack" 
(which includes search, observability, and security solutions) and 
particularly the "Elastic Cloud." The Elastic Cloud's growing contribution 
to revenue highlights its significance, which grew from 27% in 2021 to 40% in 2023.
<p>SimpleRAG</p>According to the provided context, growth primarily depends on **Elastic Cloud**, 
which is described as the company's "family of cloud-based offerings." 
The context also emphasizes the significance of Elastic Cloud in achieving 
the company's long-term growth potential and its role in increasing subscription revenue.

From the context:

- "Increasing adoption of Elastic Cloud... is an important growth opportunity 
for our business. Organizations are increasingly looking for hosted 
deployment alternatives with reduced administrative burdens."
- "We believe that offering Elastic Cloud is important for achieving 
our long-term growth potential, and we expect Elastic Cloud’s contribution 
to our subscription revenue to continue to increase."

In terms of the contribution to total revenue, here are the specific figures provided:

- Elastic Cloud contributed 40% of the total revenue for the year ended April 30, 2023.
- Elastic Cloud contributed 35% of the total revenue for the year ended April 30, 2022.
- Elastic Cloud contributed 27% of the total revenue for the year ended April 30, 2021.

This progression shows a clear upward trend in the importance and 
contribution of Elastic Cloud to the company's overall revenue.
<p><strong>Resumo</strong>: Ambos os RAGs identificaram corretamente o Elastic Cloud como o principal motor de crescimento. No entanto, o AdvancedRAG inclui mais detalhes, levando em consideração as receitas de assinaturas e o crescimento da base de clientes, e menciona explicitamente outras ofertas da Elastic.</p><h4>Teste 4: Descreva o plano de benefícios para funcionários</h4><p>RAG Avançado</p>### Employee Benefit Plan Overview

The provided context outlines various elements of the employee benefit plan at 
Elastic, often referred to as "Elasticians." Here are the key components 
described in the texts:

1. **401(k) Plan**:
   - **Coverage**: The 401(k) plan is available to substantially all U.S. 
   employees who meet minimum age and service requirements.
   - **Contributions**: Elastic makes contributions to the 401(k) plan up to 
   6% of the participating employee’s W-2 earnings and wages.
   - **Expenses**: For the fiscal years ended April 30, Elastic recorded 
   expenses of $17.9 million (2023), $15.2 million (2022), and $11.4 million (2021) 
   related to the 401(k) plan.
   - **Defined-Contribution Plans in Other Countries**: Elastic has 
   defined-contribution plans in various other countries and recorded respective 
   expenses of $9.4 million (2023), $7.2 million (2022), and $5.1 million (2021).

2. **Stock-Based Compensation**:
   - **Types of Awards**: Stock options, restricted stock units (RSUs), 
   and shares under the Employee Stock Purchase Plan (ESPP).
   - **Fair Value Measurement**: Fair value of these stock awards is 
   measured using models like Black-Scholes.
   - **Employee Stock Purchase Plan (2022 ESPP)**: 
     - Started in 2022, it allows employees to acquire ordinary 
     shares at a discount (85% of the market value at the beginning 
     or end of the offering period).
     - Offering periods are approximately six months long.

3. **Total Rewards Compensation**:
   - **Components**: Includes cash compensation as well as equity awards, 
   reflecting a comprehensive interest in the well-being of the employees.
   - **Programs**: Market competitive medical and dental programs, 
   mental health, and holistic well-being programs.
   - **Paid Time Off**: Features 16 weeks of paid leave for new 
   parents and life-planning benefits.

4. **Retirement and Income Protection Plans**:
   - **401(k) Match**: Elastic provides a dollar-for-dollar 
   match up to 6% of eligible earnings up to a plan-limit 
   maximum for U.S.-based employees.
   - **Similar Plans Outside the U.S.**: Competitive retirement 
   plans available for employees based outside the United States.

5. **Flexible Work Environment**: 
   - **Distributed Workforce**: Elastic operates with a global 
   distributed workforce model, allowing employees to work remotely 
   and providing a broad candidate pool.
   
6. **Learning and Development**:
   - **Programs**: Access to on-demand video-based learning, 
   leadership development programs, and other specific training 
   to develop managers and leaders.

7. **Employee Engagement and Surveys**:
   - **Mechanisms**: Annual employee engagement survey and 
   mid-year pulse survey.
   - **Objectives**: To monitor morale, develop action plans, 
   and maintain high engagement scores.

8. **Onboarding Program**:
   - **X-School**: An extensive new-hire orientation program to 
   help new employees meet peers and learn about the company's 
   products and solutions.

In summary, Elastic's employee benefit plan encompasses a broad 
spectrum of financial, health, well-being, and professional development 
benefits designed to support and enhance the lives of its employees. 
The plan is both competitive and comprehensive, aiming to attract,
retain, and engage its global workforce.
<p>SimpleRAG</p>The employee benefit plan at the Company includes several key 
components aimed at enhancing the overall well-being and 
professional development of employees, referred to as "Elasticians." 

1. **Compensation and Benefits:**
   - The Company provides market-competitive compensation 
   packages which typically include cash compensation and equity awards.
   - Comprehensive medical and dental programs are offered, 
   emphasizing mental health and holistic well-being.
   - Paid time off programs feature:
     - 16 weeks of paid leave for new parents.
     - Life-planning benefits.
     - Travel reimbursements for certain healthcare services.

2. **Retirement and Income Protection Plans:**
   - In the United States, employees are eligible for a 401k 
   plan with a dollar-for-dollar match by the Company up to 6% 
   of eligible earnings, subject to a plan limit.
   - Similar competitive retirement and income protection plans 
   are available for employees outside the United States.

3. **Work Environment:**
   - The Company offers a flexible work environment, allowing most 
   employees to work from anywhere as often as they would like.
   - Physical offices around the world are available for employees 
   who prefer to work on-site or need a space for face-to-face interactions.

4. **Community Involvement:**
   - The Elastic Cares Program supports charitable organizations 
   that matter to employees on both local and global levels.
   - The program includes:
     - Donation matching.
     - Providing the Company's technology for free to certain 
     nonprofit organizations.
     - Volunteer time off initiative.
<p><strong>Resumo</strong>: A AdvancedRAG aborda o assunto com muito mais profundidade e detalhes, mencionando o plano 401K para funcionários baseados nos EUA, além de definir planos de contribuição fora dos EUA. O texto também menciona planos de saúde e bem-estar, mas omite o programa Elastic Cares, que é citado pela SimpleRAG.</p><h4>Teste 5: Quais empresas a Elastic adquiriu?</h4><p>RAG Avançado</p>Elastic acquired several companies over the years to enhance its 
technology and market presence. According to the provided context, 
the specific acquisitions include:

1. **CmdWatch Security Inc.**: Acquired on September 17, 2021. 
The total purchase consideration was $77.8 million.
2. **Build Security Ltd.**: Acquired on September 2, 2021. The 
combined purchase price for Build Security Ltd. and another acquisition was $57.2 million.
3. **Optimyze Cloud Inc.**: Acquired on November 1, 2021. Part 
of the combined purchase price mentioned above with Build Security Ltd.
4. **Endgame, Inc.**: Although the context mentions an amended 
and restated stock incentive plan, it indicates that Endgame, Inc. was 
associated with Elastic, suggesting it could have been acquired at some point.

These acquisitions are part of Elastic's strategy to 
enhance the technology underlying its security and 
observability offerings.
<p>SimpleRAG</p>The provided context does not specify any companies that Elastic has acquired. 
Therefore, based on the context, there is no information available about the companies acquired by Elastic.
<p><strong>Resumo</strong>: O SimpleRAG não recupera nenhuma informação relevante sobre aquisições, resultando em uma resposta incorreta. A AdvancedRAG lista corretamente a CmdWatch, a Build Security e a Optimyze, que foram as principais aquisições mencionadas no relatório.</p><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#table-of-contents">Voltar ao topo</a></p><h2>Conclusão</h2><p>Com base em nossos testes, nossas técnicas avançadas parecem aumentar o alcance e a profundidade das informações apresentadas, potencialmente melhorando a qualidade das respostas RAG.</p><p>Além disso, pode haver melhorias na confiabilidade, já que perguntas formuladas de maneira ambígua, como <code>Which companies did Elastic acquire?</code> e <code>Who audits Elastic</code> foram respondidas corretamente pelo AdvancedRAG, mas não pelo SimpleRAG.</p><p>No entanto, vale a pena ter em mente que, em 3 de 5 casos, o pipeline RAG básico, incorporando a Busca Híbrida, mas nenhuma outra técnica, conseguiu produzir respostas que capturaram a maior parte das informações essenciais.</p><p>Devemos observar que, devido à incorporação de LLMs nas fases de preparação e consulta de dados, a latência do AdvancedRAG é geralmente de 2 a 5 vezes maior que a do SimpleRAG. Este é um custo significativo que pode tornar o AdvancedRAG adequado apenas para situações em que a qualidade da resposta é priorizada em detrimento da latência.</p><p>Os custos significativos de latência podem ser atenuados usando um modelo de linguagem latente (LLM) menor e mais barato, como o Claude Haiku ou o GPT-4o-mini, na fase de preparação dos dados. Salve os modelos avançados para geração de respostas.</p><p>Isso está de acordo com as conclusões de Wang et al. Conforme demonstram os resultados, quaisquer melhorias realizadas são relativamente incrementais. Resumindo, o método RAG básico e simples permite chegar a um produto final bastante satisfatório, sendo ainda mais barato e rápido. Para mim, é uma conclusão interessante. Para casos de uso em que velocidade e eficiência são essenciais, o SimpleRAG é a escolha sensata. Para casos de uso em que é necessário extrair o máximo desempenho possível, as técnicas incorporadas no AdvancedRAG podem oferecer uma solução.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt56b7067a9d41d5a8/6a171119acf0886fb4be9c45/ea811706b6adc4731d90b925a9fefa0ac15901b4-1440x1060.jpg" alt="Gasoduto Wang" /><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#table-of-contents">Voltar ao topo</a></p><h2>Apêndice</h2><h3>Prompts</h3><h4>Pergunta RAG para responder:</h4><p>Solicitação para que o LLM gere respostas com base na consulta e no contexto.</p>BASIC_RAG_PROMPT = '''
You are an AI assistant tasked with answering questions based primarily on the provided context, while also drawing on your own knowledge when appropriate. Your role is to accurately and comprehensively respond to queries, prioritizing the information given in the context but supplementing it with your own understanding when beneficial. Follow these guidelines:

1. Carefully read and analyze the entire context provided.
2. Primarily focus on the information present in the context to formulate your answer.
3. If the context doesn't contain sufficient information to fully answer the query, state this clearly and then supplement with your own knowledge if possible.
4. Use your own knowledge to provide additional context, explanations, or examples that enhance the answer.
5. Clearly distinguish between information from the provided context and your own knowledge. Use phrases like "According to the context..." or "The provided information states..." for context-based information, and "Based on my knowledge..." or "Drawing from my understanding..." for your own knowledge.
6. Provide comprehensive answers that address the query specifically, balancing conciseness with thoroughness.
7. When using information from the context, cite or quote relevant parts using quotation marks.
8. Maintain objectivity and clearly identify any opinions or interpretations as such.
9. If the context contains conflicting information, acknowledge this and use your knowledge to provide clarity if possible.
10. Make reasonable inferences based on the context and your knowledge, but clearly identify these as inferences.
11. If asked about the source of information, distinguish between the provided context and your own knowledge base.
12. If the query is ambiguous, ask for clarification before attempting to answer.
13. Use your judgment to determine when additional information from your knowledge base would be helpful or necessary to provide a complete and accurate answer.

Remember, your goal is to provide accurate, context-based responses, supplemented by your own knowledge when it adds value to the answer. Always prioritize the provided context, but don't hesitate to enhance it with your broader understanding when appropriate. Clearly differentiate between the two sources of information in your response.

Context:
[The concatenated documents will be inserted here]

Query:
[The user's question will be inserted here]

Please provide your answer based on the above guidelines, the given context, and your own knowledge where appropriate, clearly distinguishing between the two:
'''
<h4>prompt do gerador de consultas elásticas</h4><p>Solicitação para enriquecer as consultas com sinônimos e convertê-las para o formato OU.</p>ELASTIC_SEARCH_QUERY_GENERATOR_PROMPT = '''
You are an AI assistant specialized in generating Elasticsearch query strings. Your task is to create the most effective query string for the given user question. This query string will be used to search for relevant documents in an Elasticsearch index.

Guidelines:
1. Analyze the user's question carefully.
2. Generate ONLY a query string suitable for Elasticsearch's match query.
3. Focus on key terms and concepts from the question.
4. Include synonyms or related terms that might be in relevant documents.
5. Use simple Elasticsearch query string syntax if helpful (e.g., OR, AND).
6. Do not use advanced Elasticsearch features or syntax.
7. Do not include any explanations, comments, or additional text.
8. Provide only the query string, nothing else.

For the question "What is Clickthrough Data?", we would expect a response like:
clickthrough data OR click-through data OR click through rate OR CTR OR user clicks OR ad clicks OR search engine results OR web analytics

AND operator is not allowed. Use only OR.

User Question:
[The user's question will be inserted here]

Generate the Elasticsearch query string:
'''
<h4>Possíveis perguntas para o gerador</h4><p>Solicitação para gerar possíveis perguntas e enriquecer os metadados do documento.</p>RAG_QUESTION_GENERATOR_PROMPT = '''
You are an AI assistant specialized in generating questions for Retrieval-Augmented Generation (RAG) systems. Your task is to analyze a given document and create 10 diverse questions that would effectively test a RAG system's ability to retrieve and synthesize information from this document.

Guidelines:
1. Thoroughly analyze the entire document.
2. Generate exactly 10 questions that cover various aspects and levels of complexity within the document's content.
3. Create questions that specifically target:
   a. Key facts and information
   b. Main concepts and ideas
   c. Relationships between different parts of the content
   d. Potential applications or implications of the information
   e. Comparisons or contrasts within the document
4. Ensure questions require answers of varying lengths and complexity, from simple retrieval to more complex synthesis.
5. Include questions that might require combining information from different parts of the document.
6. Frame questions to test both literal comprehension and inferential understanding.
7. Avoid yes/no questions; focus on open-ended questions that promote comprehensive answers.
8. Consider including questions that might require additional context or knowledge to fully answer, to test the RAG system's ability to combine retrieved information with broader knowledge.
9. Number the questions from 1 to 10.
10. Output only the ten questions, without any additional text, explanations, or answers.

Document:
[The document content will be inserted here]

Generate 10 questions optimized for testing a RAG system based on this document:
'''
<h4>prompt do gerador HyDE</h4><p>Solicitação para gerar documentos hipotéticos usando o HyDE</p>HYDE_DOCUMENT_GENERATOR_PROMPT = '''
You are an AI assistant specialized in generating hypothetical documents based on user queries. Your task is to create a detailed, factual document that would likely contain the answer to the user's question. This hypothetical document will be used to enhance the retrieval process in a Retrieval-Augmented Generation (RAG) system.

Guidelines:
1. Carefully analyze the user's query to understand the topic and the type of information being sought.
2. Generate a hypothetical document that:
   a. Is directly relevant to the query
   b. Contains factual information that would answer the query
   c. Includes additional context and related information
   d. Uses a formal, informative tone similar to an encyclopedia or textbook entry
3. Structure the document with clear paragraphs, covering different aspects of the topic.
4. Include specific details, examples, or data points that would be relevant to the query.
5. Aim for a document length of 200-300 words.
6. Do not use citations or references, as this is a hypothetical document.
7. Avoid using phrases like "In this document" or "This text discusses" - write as if it's a real, standalone document.
8. Do not mention or refer to the original query in the generated document.
9. Ensure the content is factual and objective, avoiding opinions or speculative information.
10. Output only the generated document, without any additional explanations or meta-text.

User Question:
[The user's question will be inserted here]

Generate a hypothetical document that would likely contain the answer to this query:
'''
<h3>Exemplo de consulta de pesquisa híbrida</h3>{'knn': {'field': 'primary_embedding',
  'query_vector': [0.4265527129173279,
   -0.1712949573993683,
   -0.042020395398139954,
   ...],
  'k': 100,
  'num_candidates': 100},
 'query': {'bool': {'must': [{'multi_match': {'query': 'audits Elastic Elastic auditing Elastic audit process Elastic compliance Elastic security audit Elasticsearch auditing Elasticsearch compliance Elasticsearch security audit',
      'fields': ['original_text',
       'keyphrases',
       'potential_questions',
       'entities'],
      'type': 'best_fields',
      'operator': 'or'}}],
   'should': [{'script_score': {'query': {'match_all': {}},
      'script': {'source': '\n                                        double vector_score = cosineSimilarity(params.query_vector, params.vector_field) + 1.0;\n                                        double text_score = _score;\n                                        return 0.7 * vector_score + 0.3 * text_score;\n                                        ',
       'params': {'query_vector': [0.4265527129173279,
         -0.1712949573993683,
         -0.042020395398139954,
        ...],
        'vector_field': 'primary_embedding'}}}}]}},
 'size': 10}
]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Han Xiang Choong]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf605c8246989df32/6a1711178b73cbc61d18a11d/8da40067835ab8b4dc12fe52a51a6c26858ad32f-1440x1095.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 15 Aug 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Técnicas avançadas de RAG, parte 1: Processamento de dados]]></title>
    <description><![CDATA[Discutir e implementar técnicas que possam aumentar o desempenho do RAG. Parte 1 de 2, com foco no componente de processamento e ingestão de dados de um pipeline RAG avançado.]]></description>
    <content:encoded><![CDATA[<p><em>Esta é a Parte 1 da nossa exploração das Técnicas Avançadas RAG. </em><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2"><em>Clique aqui para a Parte 2!</em></a></p><p>O artigo recente <a href="https://arxiv.org/abs/2407.01219">"Searching for Best Practices in Retrieval-Augmented Generation"</a> avalia empiricamente a eficácia de várias técnicas de aprimoramento de RAG (Geração Aumentada de Recuperação), com o objetivo de convergir para um conjunto de melhores práticas para RAG.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt671704ff06a4011d/6a170b3ea929cf2d19ae09d8/dafa7250e7c4ead4d9b4aed7c407509131929749-1440x572.png" alt="Gasoduto RAG recomendado por Wang" /><p>Implementaremos algumas dessas boas práticas propostas, principalmente aquelas que visam melhorar a qualidade da busca <strong>(Fragmentação de Sentenças, HyDE, Empacotamento Reverso)</strong>.</p><p>Por uma questão de brevidade, omitiremos as técnicas focadas na melhoria da eficiência <strong>(Classificação e Sumarização de Consultas)</strong>.</p><p>Implementaremos também algumas técnicas que não foram abordadas, mas que eu pessoalmente considero úteis e interessantes <strong>(Inclusão de Metadados, Incorporação Composta de Múltiplos Campos, Enriquecimento de Consultas)</strong>.</p><p>Por fim, realizaremos um breve teste para verificar se a qualidade dos nossos resultados de pesquisa e das respostas geradas melhorou em comparação com a linha de base. Vamos lá!</p><h2>Visão geral do RAG</h2><p>O RAG visa aprimorar os LLMs (Modelos de Aprendizagem Baseados em Aprendizagem) recuperando informações de bases de conhecimento externas para enriquecer as respostas geradas. Ao fornecer informações específicas do domínio, os LLMs podem ser rapidamente adaptados para casos de uso fora do escopo de seus dados de treinamento; sendo significativamente mais baratos do que o ajuste fino e mais fáceis de manter atualizados.</p><p>As medidas para melhorar a qualidade do RAG normalmente se concentram em duas vertentes:</p><ol><li><p>Aprimorar a qualidade e a clareza da base de conhecimento.</p></li><li><p>Melhorar a abrangência e a especificidade das consultas de pesquisa.</p></li></ol><p>Essas duas medidas alcançarão o objetivo de aumentar as chances de o mestrando em Direito ter acesso a fatos e informações relevantes e, portanto, ser menos propenso a ter alucinações ou recorrer ao seu próprio conhecimento, que pode estar desatualizado ou ser irrelevante.</p><p>A diversidade de métodos é difícil de esclarecer em poucas frases. Vamos direto à implementação para que as coisas fiquem mais claras.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9a4691874a19d8da/6a170b3f47d49c99f22d8a24/72b51ba2ae5e5977b56e5b915674753d6cfd0e56-1440x840.jpg" alt="Gasoduto RAG avançado" /><h3>Índice</h3><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#overview">Visão geral</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#table-of-contents">Índice</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#set-up">Configurar</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#ingesting-processing-and-embedding-documents">Ingestão, processamento e incorporação de documentos</a>  </p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#data-ingestion">Ingestão de dados</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#sentence-level-token-wise-chunking">Segmentação por tokens e em nível de frase</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#metadata-inclusion-and-generation">Inclusão e geração de metadados</a> </p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#keyphrases-extracted-by-textrank">Palavras-chave extraídas pelo TextRank</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#potential-questions-generated-by-gpt-4o">Possíveis perguntas geradas pelo GPT-4o</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#entities-extracted-by-spacy">Entidades extraídas pelo Spacy</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#composite-multi-field-embeddings">Incorporações compostas de múltiplos campos</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#indexing-to-elastic">Indexação para Elastic</a></p></li></ul></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#cat-break">Pausa para o gato</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#appendix">Apêndice</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#definitions">Definições</a></p></li></ul></li></ul><h2>Configurar</h2><p><em>Todo o código pode ser encontrado </em><a href="https://github.com/elastic/elasticsearch-labs/tree/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques"><em>no repositório Searchlabs</em></a><em>.</em></p><p>Em primeiro lugar, o mais importante. Você precisará do seguinte:</p><ol><li><p>Implantação na Nuvem Elástica</p></li><li><p>Uma API LLM - Neste notebook, estamos usando uma implantação do GPT-4o no Azure OpenAI.</p></li><li><p>Python versão 3.12.4 ou posterior</p></li></ol><p>Executaremos todo o código <a href="https://github.com/elastic/elasticsearch-labs/blob/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques/main.ipynb">do notebook main.ipynb.</a></p><p>Faça o clone do repositório usando o Git, navegue até supporting-blog-content/advanced-rag-techniques e execute os seguintes comandos:</p># Create a new virtual environment named 'rag_env'
python -m venv rag_env

# Activate the virtual environment (for Unix-based systems)
source rag_env/bin/activate

# (For Windows)
.\rag_env\Scripts\activate

# Install packages listed in requirements.txt
pip install -r requirements.txt
<p>Feito isso, crie um <em>arquivo .env.</em> Abra o arquivo e preencha os seguintes campos (Referenciado em <a href="https://github.com/elastic/elasticsearch-labs/blob/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques/.env.example"><em>.env.example</em></a>). Agradecimentos ao meu coautor, Claude-3.5, pelos comentários úteis.</p># Elastic Cloud: Found in the 'Deployment' page of your Elastic Cloud 
# console
ELASTIC_CLOUD_ENDPOINT=""
ELASTIC_CLOUD_ID=""

# Elastic Cloud: Created during deployment setup or in 'Security' 
# settings
ELASTIC_USERNAME=""
ELASTIC_PASSWORD=""

# Elastic Cloud: The name of the index you created in Kibana or via API
ELASTIC_INDEX_NAME=""

# Azure AI Studio: Found in 'Keys and Endpoint' section of your Azure 
# OpenAI resource
AZURE_OPENAI_KEY_1=""
AZURE_OPENAI_KEY_2=""
AZURE_OPENAI_REGION=""
AZURE_OPENAI_ENDPOINT=""

# Azure AI Studio: Found in 'Deployments' section of your Azure OpenAI 
# resource
AZURE_OPENAI_DEPLOYMENT_NAME=""

# Using BAAI/bge-small-en-v1.5 because I think it is a good balance of 
# resource efficiency and performance. 
HUGGINGFACE_EMBEDDING_MODEL="BAAI/bge-small-en-v1.5"
<p>Em seguida, selecionaremos o documento a ser importado e o colocaremos na pasta de documentos. Para este artigo, usaremos o <a href="https://s201.q4cdn.com/217177842/files/doc_downloads/OtherDocuments/2023/AnnualMeeting/Annual-Report-Fiscal-Year-2023.pdf">Relatório Anual da Elastic NV de 2023</a>. É um documento bastante complexo e denso, perfeito para testar a resistência das nossas técnicas RAG.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte292dc6030d496cc/6a170b40dc55de9b03e00dfc/e513b9d67adac43da794c25a5969b893127bbbe3-1440x395.jpg" alt="Relatório Anual da Elastic 2023" /><p>Agora que está tudo pronto, vamos à ingestão. Abra o <em>arquivo main.ipynb</em> e execute as duas primeiras células para importar todos os pacotes e inicializar todos os serviços.</p><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#table-of-contents">Voltar ao topo</a></p><h2>Ingestão, processamento e incorporação de documentos</h2><h3>Ingestão de dados</h3><ul><li><p><em>Nota pessoal: Estou impressionado com a praticidade do LlamaIndex. Antigamente, antes dos mestrados em direito e do LlamaIndex, importar documentos de vários formatos era um processo doloroso de coletar pacotes esotéricos de todos os cantos. Agora, tudo se resume a uma única chamada de função. Selvagem.</em></p></li></ul><p>O <code>SimpleDirectoryReader</code> carregará todos os documentos em <code>directory_path.</code> Para arquivos <code>.pdf</code> , ele retorna uma lista de objetos de documento, que eu converto em dicionários Python porque acho mais fácil trabalhar com eles.</p># llamaindex_processor.py
from llama_index.core import SimpleDirectoryReader

class LlamaIndexProcessor:
   def __init__(self):
       pass 
   
   def load_documents(self, directory_path):
       ''' 
       Load all documents in directory
       '''
       reader = SimpleDirectoryReader(input_dir=directory_path)
       return reader.load_data()

# main.ipynb
llamaindex_processor=LlamaIndexProcessor()
documents=llamaindex_processor.load_documents('./documents/')
documents=[dict(doc_obj) for doc_obj in documents]
<p>Cada dicionário contém o conteúdo da chave no campo <code>text</code> . Também contém metadados úteis, como número da página, nome do arquivo, tamanho do arquivo e tipo.</p>{
  'id_': '5f76f0b3-22d8-49a8-9942-c2bbab14f63f',
  'metadata': {'page_label': '5',
   'file_name': 'Elastic_NV_Annual-Report-Fiscal-Year-2023.pdf',
   'file_path': '/Users/han/Desktop/Projects/truckasaurus/documents/Elastic_NV_Annual-Report-Fiscal-Year-2023.pdf',
   'file_type': 'application/pdf',
   'file_size': 3724426,
   'creation_date': '2024-07-27',
   'last_modified_date': '2024-07-27'},
   'text': 'Table of Contents\nPage\nPART I\nItem 1. Business 3\n15 Item 1A. Risk Factors\nItem 1B. Unresolved Staff Comments 48\nItem 2. Properties 48\nItem 3. Legal Proceedings 48\nItem 4. Mine Safety Disclosures 48\nPART II\nItem 5. Market for Registrant's Common Equity, Related Stockholder Matters and Issuer Purchases of \nEquity Securities49\nItem 6. [Reserved] 49\nItem 7. Management's Discussion and Analysis of Financial Condition and Results of Operations 50\nItem 7A. Quantitative and Qualitative Disclosures About Market Risk 64\nItem 8. Financial Statements and Supplementary Data 66\nItem 9. Changes in and Disagreements With Accountants on Accounting and Financial Disclosure 100\n100\n101Item 9A. Controls and Procedures\nItem 9B. Other Information\nItem 9C. Disclosure Regarding Foreign Jurisdictions That Prevent Inspections 101\nPART III\n102\n102\n102\n102Item 10. Directors, Executive Officers and Corporate Governance\nItem 11. Executive Compensation\nItem 12. Security Ownership of Certain Beneficial Owners and Management, and Related Stockholder Matters  \nItem 13. Certain Relationships and Related Transactions, and Director Independence\nItem 14. Principal Accountant Fees and Services 102\nPART IV\n103\n105Item 15. Exhibits and Financial Statement Schedules  \nItem 16. Form 10-K Summary\nSignatures 106\ni',
   ...
}
<p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#table-of-contents">Voltar ao topo</a></p><h3>Segmentação por tokens e em nível de frase</h3><p>A primeira coisa a fazer é reduzir nossos documentos a blocos de tamanho padrão (para garantir consistência e facilidade de gerenciamento). Os modelos de incorporação possuem limites de token únicos (tamanho máximo de entrada que podem processar). Os tokens são as unidades básicas de texto que modelam o processo. Para evitar a perda de informações (truncamento ou omissão de conteúdo), devemos fornecer textos que não excedam esses limites (dividindo textos mais longos em segmentos menores).</p><p>O particionamento (chunking) tem um impacto significativo no desempenho. Idealmente, cada bloco representaria uma informação autossuficiente, capturando informações contextuais sobre um único tópico. Os métodos de fragmentação incluem a fragmentação ao nível da palavra, em que os documentos são divididos pela contagem de palavras, e a fragmentação semântica, que utiliza um modelo de lógica de divisão (LLM) para identificar pontos de quebra lógicos.</p><p>A segmentação em nível de palavra é barata, rápida e fácil, mas apresenta o risco de dividir frases e, assim, quebrar o contexto. A fragmentação semântica torna-se lenta e dispendiosa, especialmente se estivermos lidando com documentos como o Relatório Anual da Elastic, com 116 páginas.</p><p>Vamos optar por uma abordagem intermediária. A segmentação em nível de frase ainda é simples, mas pode preservar o contexto de forma mais eficaz do que a segmentação em nível de palavra, além de ser significativamente mais barata e rápida. Além disso, implementaremos uma janela deslizante para capturar parte do contexto circundante e atenuar o impacto da divisão de parágrafos.</p># chunker.py 

import uuid
import re


class Chunker: 
    def __init__(self, tokenizer):
        self.tokenizer = tokenizer 
    
    def split_into_sentences(self, text):
        """Split text into sentences."""
        return re.split(r'(?&lt;=[.!?])\s+', text)
 
    def sentence_wise_tokenized_chunk_documents(self, documents, chunk_size=512, overlap=20, min_chunk_size=50):
        '''
        1. Split text into sentences.
        2. Tokenize using the provided tokenizer method.
        3. Build chunks up to the chunk_size limit.
        4. Create an overlap based on tokens - to preserve context.
        5. Only keep chunks that meet the minimum token size requirement.
        '''
        chunked_documents = []

        for doc in documents:
            sentences = self.split_into_sentences(doc['text'])
            tokens = []
            sentence_boundaries = [0]

            # Tokenize all sentences and keep track of sentence boundaries
            for sentence in sentences:
                sentence_tokens = self.tokenizer.encode(sentence, add_special_tokens=True)
                tokens.extend(sentence_tokens)
                sentence_boundaries.append(len(tokens))

            # Create chunks
            chunk_start = 0
            while chunk_start &lt; len(tokens):
                chunk_end = chunk_start + chunk_size

                # Find the last complete sentence that fits in the chunk
                sentence_end = next((i for i in sentence_boundaries if i &gt; chunk_end), len(tokens))
                chunk_end = min(chunk_end, sentence_end)

                # Create the chunk
                chunk_tokens = tokens[chunk_start:chunk_end]

                # Check if the chunk meets the minimum size requirement
                if len(chunk_tokens) &gt;= min_chunk_size:
                    # Create a new document object for this chunk
                    chunk_doc = {
                        'id_': str(uuid.uuid4()),
                        'chunk': chunk_tokens,
                        'original_text': self.tokenizer.decode(chunk_tokens),
                        'chunk_index': len(chunked_documents),
                        'parent_id': doc['id_'],
                        'chunk_token_count': len(chunk_tokens)
                    }

                    # Copy all other fields from the original document
                    for key, value in doc.items():
                        if key != 'text' and key not in chunk_doc:
                            chunk_doc[key] = value

                    chunked_documents.append(chunk_doc)

                # Move to the next chunk start, considering overlap
                chunk_start = max(chunk_start + chunk_size - overlap, chunk_end - overlap)

        return chunked_documents

# main.ipynb 
# Initialize Embedding Model
HUGGINGFACE_EMBEDDING_MODEL = os.environ.get('HUGGINGFACE_EMBEDDING_MODEL')
embedder=EmbeddingModel(model_name=HUGGINGFACE_EMBEDDING_MODEL)

# Initialize Chunker
chunker=Chunker(embedder.tokenizer)
<p>A classe <code>Chunker</code> recebe o tokenizador do modelo de incorporação para codificar e decodificar o texto. Agora vamos construir blocos de 512 tokens cada, com uma sobreposição de 20 tokens. Para isso, vamos dividir o texto em frases, tokenizar essas frases e, em seguida, adicionar as frases tokenizadas ao nosso bloco atual até que não possamos adicionar mais sem ultrapassar nosso limite de tokens.</p><p>Finalmente, decodifique as frases de volta ao texto original para incorporação, armazenando-o em um campo chamado <code>original_text</code>. Os blocos são armazenados em um campo chamado <code>chunk</code>. Para reduzir o ruído (ou seja, documentos inúteis), descartaremos quaisquer documentos com menos de 50 tokens de comprimento.</p><p>Vamos executar o programa em nossos documentos:</p>chunked_documents=chunker.sentence_wise_tokenized_chunk_documents(documents, chunk_size=512)
<p>E receba trechos de texto com a seguinte aparência:</p>print(chunked_documents[4]['original_text'])

[CLS] the aggregate market value of the ordinary shares held by non - affiliates of the registrant, 
based on the closing price of the shares of ordinary shares on the new york stock exchange on 
october 31, 2022 ( the last business day of the registrant 's second fiscal quarter ), was 
approximately $ 6. 1 billion. [SEP] [CLS] as of may 31, 2023, the registrant had 97, 390, 886 
ordinary shares, par value €0. 01 per share, outstanding. [SEP] [CLS] documents incorporated by 
reference portions of the registrant 's definitive proxy statement relating to the registrant 's 2
023 annual general meeting of shareholders are incorporated by reference into part iii of this annual 
...
...
<p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#table-of-contents">Voltar ao topo</a></p><h3>Inclusão e geração de metadados</h3><p>Dividimos nossos documentos em partes menores. Agora é hora de enriquecer os dados. Desejo gerar ou extrair metadados adicionais. Esses metadados adicionais podem ser usados para influenciar e melhorar o desempenho da busca.</p><p>Vamos definir uma classe <code>DocumentEnricher</code> , cuja função é receber uma lista de documentos (dicionários Python) e uma lista de funções de processamento. Essas funções serão executadas na coluna <code>original_text</code> dos documentos e armazenarão suas saídas em novos campos.</p><p>Primeiro, extraímos as palavras-chave usando <a href="https://github.com/elastic/elasticsearch-labs/blob/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques/nltk_processor.py">o TextRank</a>. O TextRank é um algoritmo baseado em grafos que extrai frases e sentenças-chave de um texto, classificando sua importância com base nas relações entre as palavras.</p><p>Em seguida, vamos <a href="https://github.com/elastic/elasticsearch-labs/blob/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques/llm.py">gerar perguntas potenciais usando o GPT-4o</a>.</p><p>Por fim, vamos <a href="https://github.com/elastic/elasticsearch-labs/blob/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques/entity_extractor.py">extrair as entidades</a> usando <a href="https://spacy.io/">o Spacy</a>.</p><p>Como o código para cada um deles é bastante extenso e complexo, vou evitar reproduzi-lo aqui. Caso tenha interesse, os arquivos estão marcados nos exemplos de código abaixo.</p><p>Vamos executar o enriquecimento de dados:</p># documentenricher.py
from tqdm import tqdm

class DocumentEnricher:

    def __init__(self):
        pass 

    def enrich_document(self, documents, processors, text_col='text'):
        for doc in tqdm(documents, desc="Enriching documents using processors: "+str(processors)): 
            for (processor, field) in processors: 
                metadata=processor(doc[text_col])
                if isinstance(metadata, list):
                    metadata='\n'.join(metadata)
                doc.update({field: metadata})
 
# main.ipynb
# Initialize processor classes 
nltkprocessor=NLTKProcessor() // nltk_processor.py
entity_extractor=EntityExtractor() // entity_extractor.py
gpt4o = LLMProcessor(model='gpt-4o') // llm.py

# Initialize LLM
documentenricher=DocumentEnricher()

# Create new fields in the documents - These are the outputs of the processor functions.
processors=[
    (nltkprocessor.textrank_phrases, "keyphrases"),
    (gpt4o.generate_questions, "potential_questions"),
    (entity_extractor.extract_entities, "entities")
    ]

# .enrich_document() will modify chunked_docs in place. 
# To view the results, we'll print chunked_docs in the next few cells!
documentenricher.enrich_document(chunked_docs, text_col='original_text', processors=processors)
<p>E veja os resultados:</p><h4>Palavras-chave extraídas pelo TextRank</h4><p>Essas palavras-chave representam os tópicos principais do bloco. Se a consulta estiver relacionada à segurança cibernética, a pontuação desse segmento será aumentada.</p>print(chunked_documents[25]['keyphrases'])

'elastic agent stop', 'agent stop malware', 
'stop malware ransomware', 'malware ransomware environment', 
'ransomware environment wide', 'environment wide visibility', 
'wide visibility threat', 'visibility threat detection', 
'sep cl key', 'cl key feature'
<h4>Possíveis perguntas geradas pelo GPT-4o</h4><p>Essas perguntas em potencial podem corresponder diretamente às consultas do usuário, oferecendo um aumento na pontuação. Solicitamos ao GPT-4o que gere perguntas que possam ser respondidas usando as informações encontradas no bloco atual.</p>print(chunked_documents[25]['potential_questions'])

1. What are the primary functions that Elastic Agent provides in terms of cybersecurity?
2. Describe how Logstash contributes to data management within an IT environment.
3. List and explain any key features of Logstash mentioned in the document.
4. How does Elastic Agent enhance environment-wide visibility in threat detection?
5. What capabilities does Logstash offer for handling data beyond simple collection?
6. In what ways does the document suggest that Elastic Agent stops malware and ransomware?
7. Can you identify any relationships between the functionalities of Elastic Agent and Logstash in an integrated environment?
8. What implications might the advanced threat detection capabilities of Elastic Agent have for organizational security policies?
9. Compare and contrast the roles of Elastic Agent and Logstash based on their described functions.
10. How might the centralized collection ability of Logstash support the threat detection capabilities of Elastic Agent?
<h4>Entidades extraídas pelo Spacy</h4><p>Essas entidades têm uma finalidade semelhante à das palavras-chave, mas capturam os nomes de organizações e indivíduos, que a extração por palavras-chave pode não incluir.</p>print(chunked_documents[29]['entities'])

'appdynamics', 'apm data', 'azure sentinel', 
'microsoft', 'mcafee', 'broadcom', 'cisco', 
'dynatrace', 'coveo', 'lucidworks'
<p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#table-of-contents">Voltar ao topo</a></p><h3>Incorporações compostas de múltiplos campos</h3><p>Agora que enriquecemos nossos documentos com metadados adicionais, podemos aproveitar essas informações para criar representações vetoriais mais robustas e sensíveis ao contexto.</p><p>Vamos recapitular o ponto em que nos encontramos no processo. Cada documento contém quatro áreas de interesse.</p>{
    "chunk": "...",
    "keyphrases": "...", 
    "potential_questions": "...", 
    "entities": "..." 
}
<p>Cada campo representa uma perspectiva diferente sobre o contexto do documento, podendo destacar uma área-chave para o mestrado em Direito (LLM) se concentrar.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt84cb328fce6aae23/6a170b42964cea3e4408bbc4/aea1f513009a0c7c8545a79fad8f072a5bcae24c-1440x1067.jpg" alt="Pipeline de enriquecimento de metadados no RAG" /><p>O plano é incorporar cada um desses campos e, em seguida, criar uma soma ponderada das incorporações, conhecida como Incorporação Composta.</p><p>Com sorte, essa incorporação composta permitirá que o sistema se torne mais sensível ao contexto, além de introduzir mais um hiperparâmetro ajustável para controlar o comportamento de busca.</p><p>Primeiro, vamos incorporar cada campo e atualizar cada documento no local, usando nosso modelo de incorporação definido localmente e importado no início do notebook main.ipynb.</p># EmbeddingModel defined in embedding_model.py
embedder=EmbeddingModel(model_name=HUGGINGFACE_EMBEDDING_MODEL)

cols_to_embed=['keyphrases', 'potential_questions', 'entities']

embedding_cols=[]
for col in cols_to_embed:
    # Works on text input
    embedding_col=embedder.embed_documents_text_wise(chunked_documents, text_field=col)
    embedding_cols.append(embedding_col)
# Works on token input
embedding_col=embedder.embed_documents_token_wise(chunked_documents, token_field="chunk")
embedding_cols.append(embedding_col)
<p>Cada função de incorporação retorna o campo da incorporação, que é simplesmente o campo de entrada original com um sufixo <code>_embedding</code> .</p><p>Vamos agora definir os pesos da nossa incorporação composta:</p>embedding_cols=[
                'keyphrases_embedding',
                'potential_questions_embedding',
                'entities_embedding',
                'chunk_embedding']
combination_weights=[
                    0.1,
                    0.15,
                    0.05,
                    0.7
                ]
<p>Os pesos permitem atribuir prioridades a cada componente, com base no seu caso de uso e na qualidade dos seus dados. Intuitivamente, a magnitude dessas ponderações depende do valor semântico de cada componente. Como o texto em si é de longe o mais rico, atribuo uma ponderação de 70%. Como as entidades são as menores, sendo apenas uma lista de nomes de organizações ou pessoas, atribuo a elas uma ponderação de 5%. A configuração precisa desses valores deve ser determinada empiricamente, caso a caso, para cada situação específica.</p><p>Finalmente, vamos escrever uma função para aplicar as ponderações e criar nossa representação composta. Também excluiremos todos os elementos incorporados dos componentes para economizar espaço.</p>from tqdm import tqdm 
def combine_embeddings(objects, embedding_cols, combination_weights, primary_embedding='primary_embedding'):
    # Ensure the number of weights matches the number of embedding columns
    assert len(embedding_cols) == len(combination_weights), "Number of embedding columns must match number of weights"
    
    # Normalize weights to sum to 1
    weights = np.array(combination_weights) / np.sum(combination_weights)
    
    for obj in tqdm(objects, desc="Combining embeddings"):
        # Initialize the combined embedding
        combined = np.zeros_like(obj[embedding_cols[0]])
        
        # Compute the weighted sum
        for col, weight in zip(embedding_cols, weights):
            combined += weight * np.array(obj[col])
        
        # Add the new combined embedding to the object
        obj.update({primary_embedding:combined.tolist()})
        
        # Remove the original embedding columns
        for col in embedding_cols:
            obj.pop(col, None)

combine_embeddings(chunked_documents, embedding_cols, combination_weights)
<p>Com isso, concluímos o processamento de nossos documentos. Agora temos uma lista de objetos de documento com a seguinte aparência:</p>{ 'id_': '7fe71686-5cd0-4831-9e79-998c6dbeae0c', 'chunk': [2312, 14613, ...], 'original_text': 'if an emerging growth company, indicate by check mark if the registrant has elected not to use the extended ...', 'chunk_index': 3, 'chunk_token_count': 399, 'metadata': {'page_label': '3', 'file_name': 'Elastic_NV_Annual-Report-Fiscal-Year-2023.pdf', ... 'keyphrases': 'sep cl unk\ncheck mark registrant\ncl unk indicate\nunk indicate check\nindicate check mark\nprincipal executive office\naccelerate filer unk\ncompany unk emerge\nunk emerge growth\nemerge growth company', 'potential_questions': '1. What are the different types of registrant statuses mentioned in the document?\n2. Under what section of the Sarbanes-Oxley Act must registrants file a report on the effectiveness of their internal ...', 'entities': 'the effe ctiveness of\nsection 13\nSEP\nUNK\nsection 21e\n1934\n1933\nu. s. c.\nsection 404\nsection 12\nal', 'primary_embedding': [-0.3946287803351879, -0.17586839850991964, ...] }
<h4>Indexação para Elastic</h4><p>Vamos fazer o upload em massa de nossos documentos para o Elastic Search. Para esse propósito, há muito tempo defini um conjunto de funções auxiliares elásticas em <a href="https://github.com/elastic/elasticsearch-labs/blob/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques/elastic_helpers.py"><code>elastic_helpers.py</code></a>. É um trecho de código muito extenso, então vamos nos ater à análise das chamadas de função.</p><p><code>es_bulk_indexer.bulk_upload_documents</code> Funciona com qualquer lista de objetos de dicionário, aproveitando os convenientes mapeamentos dinâmicos do Elasticsearch.</p># Initialize Elasticsearch
ELASTIC_CLOUD_ID = os.environ.get('ELASTIC_CLOUD_ID')
ELASTIC_USERNAME = os.environ.get('ELASTIC_USERNAME')
ELASTIC_PASSWORD = os.environ.get('ELASTIC_PASSWORD')
ELASTIC_CLOUD_AUTH = (ELASTIC_USERNAME, ELASTIC_PASSWORD)
es_bulk_indexer = ESBulkIndexer(cloud_id=ELASTIC_CLOUD_ID, credentials=ELASTIC_CLOUD_AUTH)
es_query_maker = ESQueryMaker(cloud_id=ELASTIC_CLOUD_ID, credentials=ELASTIC_CLOUD_AUTH)

# Define Index Name
index_name=os.environ.get('ELASTIC_INDEX_NAME')


# Create index and bulk upload 
index_exists = es_bulk_indexer.check_index_existence(index_name=index_name)
if not index_exists:
    logger.info(f"Creating new index: {index_name}")
    es_bulk_indexer.create_es_index(es_configuration=BASIC_CONFIG, index_name=index_name)

success_count = es_bulk_indexer.bulk_upload_documents(
    index_name=index_name, 
    documents=chunked_documents, 
    id_col='id_',
    batch_size=32
)
<p>Acesse o Kibana e verifique se todos os documentos foram indexados. Deveriam ser 224. Nada mal para um documento tão grande!</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8efeface6effe01d/6a170b447d8d67652870e72a/1b3b07f6b98ceb65f6594ce4be83c5b0ed7e7cf9-1440x1380.jpg" alt="Índice Kibana" /><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#table-of-contents">Voltar ao topo</a></p><h2>Pausa para o gato</h2><p>Vamos fazer uma pausa, o artigo está um pouco denso, eu sei. Vejam só o meu gato:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc1db5595f71c12ff/6a170b450e2e49940241a0fe/baca4eb52b801b21ced97352cc55462f0a12d6b0-969x996.jpg" alt="Gasoduto Han" /><p>Adorável. O chapéu sumiu e eu suspeito que ela o roubou e escondeu em algum lugar :(</p><p>Parabéns por ter chegado até aqui :)</p><p>Junte-se a mim na <a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2">Parte 2</a> para testes e avaliação do nosso pipeline RAG!</p><h2>Apêndice</h2><h3>Definições</h3><p><strong>1. Segmentação de Frases</strong></p><ul><li><p>Uma técnica de pré-processamento usada em sistemas RAG para dividir o texto em unidades menores e significativas.</p></li><li><p><em>Processo:</em> </p><ol><li><p>Entrada: Bloco grande de texto (ex.: documento, parágrafo)</p></li><li><p>Saída: Segmentos de texto menores (normalmente frases ou pequenos grupos de frases)</p></li></ol></li><li><p><em>Propósito:</em> </p><ul><li><p>Cria segmentos de texto granulares e específicos ao contexto.</p></li><li><p>Permite uma indexação e recuperação mais precisas.</p></li><li><p>Melhora a relevância das informações recuperadas em sistemas RAG.</p></li></ul></li><li><p><em>Características:</em> </p><ul><li><p>Os segmentos têm significado semântico.</p></li><li><p>Podem ser indexados e recuperados independentemente.</p></li><li><p>Frequentemente preserva algum contexto para garantir a compreensibilidade de forma independente.</p></li></ul></li><li><p><em>Benefícios:</em> </p><ul><li><p>Aumenta a precisão de recuperação</p></li><li><p>Permite uma ampliação mais focada em pipelines RAG.</p></li></ul></li></ul><p><strong>2. HyDE (Incorporação Hipotética de Documentos)</strong></p><ul><li><p>Uma técnica que utiliza um modelo de lógica latente (LLM) para gerar um documento hipotético para expansão de consultas em sistemas RAG.</p></li><li><p><em>Processo:</em>  </p><ol><li><p>Inserir consulta em um LLM</p></li><li><p>O LLM gera um documento hipotético que responde à pergunta.</p></li><li><p>Incorpore o documento gerado</p></li><li><p>Use o embedding para busca vetorial</p></li></ol></li><li><p><em>Principal diferença:</em> </p><ul><li><p>RAG tradicional: relaciona a consulta aos documentos.</p></li><li><p>HyDE: Correspondência entre documentos.</p></li></ul></li><li><p><em>Propósito:</em> </p><ul><li><p>Melhorar o desempenho de recuperação de dados, especialmente para consultas complexas ou ambíguas.</p></li><li><p>Captura um contexto semântico mais rico do que uma consulta curta.</p></li></ul></li><li><p><em>Benefícios:</em> </p><ul><li><p>Aproveita o conhecimento do LLM para ampliar as consultas.</p></li><li><p>Pode potencialmente melhorar a relevância dos documentos recuperados.</p></li></ul></li><li><p><em>Desafios:</em> </p><ul><li><p>Requer inferência LLM adicional, aumentando a latência e o custo.</p></li><li><p>O desempenho depende da qualidade do documento hipotético gerado.</p></li></ul></li></ul><p><strong>3. Reembalagem reversa</strong></p><ul><li><p>Uma técnica utilizada em sistemas RAG para reordenar os resultados da pesquisa antes de passá-los para o LLM.</p></li><li><p><em>Processo:</em> </p><ol><li><p>O mecanismo de busca (por exemplo, Elasticsearch) retorna documentos em ordem decrescente de relevância.</p></li><li><p>A ordem é invertida, colocando o documento mais relevante por último.</p></li></ol></li><li><p><em>Propósito:</em> </p><ul><li><p>Explora o viés de recência dos mestrados em direito, que tendem a se concentrar mais nas informações mais recentes em seu contexto.</p></li><li><p>Garante que as informações mais relevantes sejam as mais "atualizadas" na janela de contexto do LLM.</p></li></ul></li><li><p><em>Exemplo:</em> Ordem original: [Mais relevante, Segundo mais relevante, Terceiro mais relevante, ...] Ordem inversa: [..., Terceiro mais relevante, Segundo mais relevante, Mais relevante]</p></li></ul><p><strong>4. Classificação de consultas</strong></p><ul><li><p>Uma técnica para otimizar a eficiência do sistema RAG, determinando se uma consulta requer RAG ou se pode ser respondida diretamente pelo LLM.</p></li><li><p><em>Processo:</em> </p><ol><li><p>Desenvolver um conjunto de dados personalizado específico para o LLM em uso.</p></li><li><p>Treinar um modelo de classificação especializado</p></li><li><p>Utilize o modelo para categorizar as consultas recebidas.</p></li></ol></li><li><p><em>Propósito:</em> </p><ul><li><p>Melhore a eficiência do sistema evitando o processamento desnecessário de RAG (raiz, grafite e agregação).</p></li><li><p>Direcione as consultas para o mecanismo de resposta mais apropriado.</p></li></ul></li><li><p><em>Requisitos:</em> </p><ul><li><p>Conjunto de dados e modelo específicos para LLM</p></li><li><p>Aperfeiçoamento contínuo para manter a precisão.</p></li></ul></li><li><p><em>Benefícios:</em> </p><ul><li><p>Reduz a sobrecarga computacional para consultas simples.</p></li><li><p>Potencialmente melhora o tempo de resposta para consultas que não sejam RAG.</p></li></ul></li></ul><p><strong>5. Resumo</strong></p><ul><li><p>Uma técnica para condensar documentos recuperados em sistemas RAG.</p></li><li><p><em>Processo:</em> </p><ol><li><p>Recuperar documentos relevantes</p></li><li><p>Gere resumos concisos de cada documento.</p></li><li><p>Utilize resumos em vez de documentos completos no pipeline RAG.</p></li></ol></li><li><p><em>Propósito:</em> </p><ul><li><p>Melhore o desempenho RAG concentrando-se em informações essenciais.</p></li><li><p>Reduzir o ruído e a interferência de conteúdo menos relevante</p></li></ul></li><li><p><em>Benefícios:</em> </p><ul><li><p>Potencialmente melhora a relevância das respostas do LLM</p></li><li><p>Permite a inclusão de mais documentos dentro dos limites do contexto.</p></li></ul></li><li><p><em>Desafios:</em> </p><ul><li><p>Risco de perder detalhes importantes na sumarização.</p></li><li><p>Sobrecarga computacional adicional para geração de resumos.</p></li></ul></li></ul><p><strong>6. Inclusão de Metadados</strong></p><ul><li><p>Uma técnica para enriquecer documentos com informações contextuais adicionais.</p></li><li><p><em>Tipos de metadados:</em>  </p><ul><li><p>Palavras-chave</p></li><li><p>Títulos</p></li><li><p>Datas</p></li><li><p>Detalhes da autoria</p></li><li><p>Resumos</p></li></ul></li><li><p><em>Propósito:</em> </p><ul><li><p>Aumentar a informação contextual disponível para o sistema RAG</p></li><li><p>Proporcionar aos alunos de mestrado em Direito uma compreensão mais clara do conteúdo e da relevância dos documentos.</p></li></ul></li><li><p><em>Benefícios:</em> </p><ul><li><p>Potencialmente melhora a precisão da recuperação.</p></li><li><p>Aumenta a capacidade do LLM de avaliar a utilidade dos documentos.</p></li></ul></li><li><p><em>Implementação:</em> </p><ul><li><p>Pode ser feito durante o pré-processamento do documento.</p></li><li><p>Pode exigir etapas adicionais de extração ou geração de dados.</p></li></ul></li></ul><p><strong>7. Incorporações compostas de múltiplos campos</strong></p><ul><li><p>Uma técnica avançada de incorporação para sistemas RAG que cria incorporações separadas para diferentes componentes do documento.</p></li><li><p><em>Processo:</em> </p><ol><li><p>Identifique os campos relevantes (ex.: título, palavras-chave, sinopse, conteúdo principal).</p></li><li><p>Gere embeddings separados para cada campo.</p></li><li><p>Combine ou armazene esses embeddings para uso na recuperação de informações.</p></li></ol></li><li><p><em>Diferença em relação à abordagem padrão:</em> </p><ul><li><p>Tradicional: Incorporação única para todo o documento.</p></li><li><p>Composição: Incorporação múltipla para diferentes aspectos do documento</p></li></ul></li><li><p><em>Propósito:</em> </p><ul><li><p>Criar representações de documentos mais matizadas e sensíveis ao contexto.</p></li><li><p>Capturar informações de uma variedade maior de fontes em um documento.</p></li></ul></li><li><p><em>Benefícios:</em> </p><ul><li><p>Potencialmente melhora o desempenho em consultas ambíguas ou multifacetadas.</p></li><li><p>Permite uma ponderação mais flexível de diferentes aspectos do documento na recuperação.</p></li></ul></li><li><p><em>Desafios:</em> </p><ul><li><p>Aumento da complexidade na incorporação de processos de armazenamento e recuperação</p></li><li><p>Pode exigir algoritmos de correspondência mais sofisticados.</p></li></ul></li></ul><p><strong>8. Enriquecimento de consultas</strong></p><ul><li><p>Uma técnica para expandir a consulta original com termos relacionados, a fim de melhorar o alcance da pesquisa.</p></li><li><p><em>Processo:</em> </p><ol><li><p>Analise a consulta original</p></li><li><p>Gere sinônimos e frases semanticamente relacionadas.</p></li><li><p>Aprimore a consulta com estes termos adicionais.</p></li></ol></li><li><p><em>Propósito:</em> </p><ul><li><p>Aumentar o leque de correspondências potenciais no conjunto de documentos.</p></li><li><p>Melhorar o desempenho de recuperação de dados para consultas com linguagem específica ou técnica.</p></li></ul></li><li><p><em>Benefícios:</em> </p><ul><li><p>Pode recuperar documentos relevantes que não correspondam exatamente aos termos da consulta original.</p></li><li><p>Pode ajudar a superar a incompatibilidade de vocabulário entre consultas e documentos.</p></li></ul></li><li><p><em>Desafios:</em> </p><ul><li><p>Risco de desvio de consulta se não for implementado com cuidado.</p></li><li><p>Pode aumentar a sobrecarga computacional no processo de recuperação.</p></li></ul></li></ul><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#table-of-contents">Voltar ao topo</a></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Han Xiang Choong]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9a4691874a19d8da/6a170b3f47d49c99f22d8a24/72b51ba2ae5e5977b56e5b915674753d6cfd0e56-1440x840.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 14 Aug 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearch x OpenSearch: comparação de desempenho da busca vetorial]]></title>
    <description><![CDATA[O Elasticsearch é, de imediato, de 2 a 12 vezes mais rápido que o OpenSearch para busca vetorial]]></description>
    <content:encoded><![CDATA[<p><a href="https://www.elastic.co/search-labs/blog/elasticsearch-opensearch-vector-search-performance-comparison#up-to-12x-faster-out-of-the-box">TLDR: o Elasticsearch é até 12x mais rápido</a> - Nós, da Elastic, recebemos inúmeras solicitações da nossa comunidade para esclarecer as diferenças de desempenho entre o Elasticsearch e o OpenSearch, particularmente no reinos da busca semântica / busca vetorial. Por isso, realizamos esse teste de desempenho para fornecer uma comparação clara e orientada por dados — sem ambiguidade, apenas fatos diretos para informar nossos usuários. Os resultados mostram que o <strong>Elasticsearch é até 12x mais rápido</strong> do que o OpenSearch para busca vetorial e, portanto, requer menos recursos computacionais. Isso reflete o foco da Elastic em consolidar o Lucene como o melhor banco de dados vetorial para casos de uso de busca e recuperação.</p><p>A busca vetorial está revolucionando a maneira como realizamos buscas de similaridade, principalmente em campos como IA e machine learning. Com a crescente adoção de modelos de incorporação vetorial, a capacidade de buscar com eficiência milhões de vetores de alta dimensão torna-se crítica.</p><p>Quando se trata de melhorar bancos de dados vetoriais, a Elastic e o OpenSearch adotaram abordagens notavelmente diferentes. A Elastic investiu muito na otimização do Apache Lucene junto com o Elasticsearch para elevá-los como a melhor opção para aplicações de busca vetorial. Em contraste, o OpenSearch ampliou seu foco, integrando outras implementações de busca vetorial e explorando além do escopo do Lucene. Nosso foco no Lucene é estratégico, permitindo-nos fornecer suporte altamente integrado na nossa versão do Elasticsearch, resultando em um conjunto aprimorado de recursos em que cada componente complementa e amplifica as capacidades do outro.</p><p>Este blog apresenta uma comparação detalhada entre o Elasticsearch 8.14 e o OpenSearch 2.14, levando em conta diferentes configurações e motores vetoriais. Nesta análise de desempenho, o Elasticsearch provou ser a plataforma superior para operações de busca vetorial, e os <a href="https://www.elastic.co/search-labs/blog/vector-similarity-computations-ludicrous-speed">próximos recursos</a> ampliarão as diferenças ainda mais <a href="https://www.elastic.co/search-labs/blog/elasticsearch-lucene-vector-database-gains">significativamente</a>. Quando comparado com o OpenSearch, ele se destacou em todas as faixas de benchmark — <strong>oferecendo desempenho de 2x a 12x mais rápido em média</strong>. Isso ocorreu em situações que usam quantidades e dimensões vetoriais variadas, incluindo <code>so_vector</code> (2 milhões de vetores, 768D), <code>openai_vector</code> (2,5 milhões de vetores, 1536D) e <code>dense_vector</code> (10 milhões de vetores, 96D), todos disponíveis <a href="https://github.com/elastic/elasticsearch-opensearch-vector-performance">neste repositório</a> junto com os scripts do Terraform para provisão de toda a infraestrutura necessária nos manifestos do Google Cloud e do Kubernetes para executar os testes.</p><p>Os resultados detalhados neste blog complementam os resultados de um <a href="https://www.elastic.co/blog/elasticsearch-opensearch-performance-gap">estudo publicado antes e validado por terceiros</a> que mostra que o Elasticsearch é 40%–140% mais rápido do que o OpenSearch nas operações de análise de busca mais comuns: consulta de texto, ordenação, intervalo, histograma de data e filtro de termos. Agora podemos adicionar outro diferenciador: busca vetorial.</p><h2>Até 12x mais rápido, pronto para uso</h2><p>Nossos benchmarks focados nos quatro conjuntos de dados vetoriais envolveram buscas de KNN aproximado e KNN exato, considerando diferentes tamanhos, dimensões e configurações, totalizando <code>40.189.820</code> solicitações de busca não armazenadas em cache. Os resultados: <strong>o Elasticsearch é até 12 vezes mais rápido</strong> do que o OpenSearch para busca vetorial e, portanto, requer menos recursos computacionais.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt34b83c6eba3bcb6e/6a17d727dbb4ff18d6fb54fb/cdb26e91f085b90e9b12aeb8fee53b04d365ecae-1440x1156.webp" alt="média p90" /><p>Figura 1: Tarefas agrupadas para ANN e KNN Exato em diferentes combinações no Elasticsearch e OpenSearch.</p><p>Os grupos como <code>knn-10-100</code> significam uma busca KNN com  e . Na busca vetorial HNSW,  determina o número de vizinhos mais próximos a serem recuperados para um vetor de consulta. Especifica quantos vetores semelhantes devem ser encontrados como resultado.  define o número de vetores candidatos a serem recuperados em cada segmento. Mais candidatos podem melhorar a precisão, mas exigem maiores recursos computacionais.</p><p>Também testamos com diferentes técnicas de quantização e aproveitamos otimizações específicas do mecanismo; os resultados detalhados para cada trilha, tarefa e mecanismo de vetor estão disponíveis abaixo.</p><h2>KNN exato e KNN aproximado</h2><p>Ao lidar com conjuntos de dados e casos de uso variados, a abordagem correta para busca vetorial será diferente. Neste blog, todas as tarefas declaradas como <code>knn-*</code> como <code>knn-10-100</code> usam <strong>KNN aproximado</strong> e <code>script-score-*</code> se referem ao <strong>KNN exato</strong>, mas qual é a diferença entre elas e por que são importantes?</p><p>Em essência, se você estiver lidando com conjuntos de dados mais substanciais, o método preferido é o Approximate K-Nearest Neighbor (ANN) devido à escalabilidade superior. Para conjuntos de dados mais modestos que podem exigir um processo de filtragem, o método Exact K-Nearest Neighbor (KNN) é ideal.</p><p>O KNN exato utiliza um método de força bruta, calculando a distância entre um vetor e todos os outros vetores no conjunto de dados. Em seguida, classifica essas distâncias para encontrar os  vizinhos mais próximos. Embora esse método garanta uma correspondência exata, ele enfrenta desafios de escalabilidade para conjuntos de dados grandes e de alta dimensão. No entanto, há muitos casos em que o KNN exato é necessário:</p><ul><li><p><strong>Reclassificação</strong>: em casos que envolvem buscas lexicais ou semânticas seguidas de reclassificação baseada em vetores, o KNN exato é essencial. Por exemplo, em um mecanismo de busca de produtos, os resultados de busca iniciais podem ser filtrados com base em consultas textuais (por exemplo, palavras-chave, categorias) e, em seguida, os vetores associados aos itens filtrados são usados para uma avaliação de similaridade mais precisa.</p></li><li><p><strong>Personalização</strong>: ao lidar com um grande número de usuários, cada um representado por um número relativamente pequeno (como 1 milhão) de vetores distintos, a ordenação do índice por metadados específicos do usuário (por exemplo, user_id) e a pontuação de força bruta com vetores torna-se eficiente. Essa abordagem permite recomendações personalizadas ou entrega de conteúdo com base em comparações precisas de vetores adaptadas às preferências individuais do usuário.</p></li></ul><p>O Exact KNN, portanto, garante que a classificação final e as recomendações baseadas na similaridade vetorial sejam precisas e adaptadas às preferências do usuário.</p><p>O KNN aproximado (ou ANN), por outro lado, utiliza métodos para deixar a busca de dados mais rápida e eficiente do que o KNN exato, principalmente em conjuntos de dados grandes e de alta dimensão. Em vez de uma abordagem de força bruta, que mede a distância exata mais próxima entre uma consulta e todos os pontos, gerando desafios de computação e redimensionamento, a ANN utiliza certas técnicas para reestruturar eficientemente os índices e as dimensões dos vetores buscáveis no conjunto de dados. Embora isso possa causar uma pequena imprecisão, aumenta significativamente a velocidade do processo de busca, sendo uma alternativa eficaz para lidar com grandes conjuntos de dados.</p><p>Neste blog, todas as tarefas declaradas como <code>knn-*</code>, como <code>knn-10-100</code>, usam <strong>KNN aproximado</strong> e <code>script-score-*</code> referem-se a <strong>KNN exato</strong>.</p><h2>Metodologia de teste</h2><p>Embora o Elasticsearch e o OpenSearch sejam semelhantes em termos de API para operações de busca do BM25, já que o último é uma bifurcação do primeiro, não é o caso do Vector Search, que foi introduzido após o fork. O OpenSearch adotou uma abordagem diferente do Elasticsearch quando se trata de algoritmos, introduzindo dois outros mecanismos — <code>nmslib</code> e <code>faiss</code> — além do <code>lucene</code>, cada um com configurações e limitações específicas (por exemplo, <code>nmslib</code> no OpenSearch não permite filtros, um recurso essencial para muitos casos de uso).</p><p>Todos os três mecanismos usam o algoritmo Hierarchical Navigable Small World (HNSW), que é eficiente para busca aproximada do vizinho mais próximo e principalmente poderoso ao lidar com dados de alta dimensão. É importante observar que o <code>faiss</code> também aceita um segundo algoritmo, <code>ivf</code>, mas como ele requer pré-treinamento no conjunto de dados, vamos nos concentrar exclusivamente no HNSW. A ideia de núcleo do HNSW é organizar os dados em várias camadas de gráficos conectados, com cada camada representando uma granularidade diferente do conjunto de dados. A busca começa na camada superior com a visualização mais grosseira e avança para camadas cada vez mais finas até chegar ao nível básico.</p><p>Ambos os mecanismos de busca foram testados sob condições idênticas em um ambiente controlado para garantir condições de teste justas. O método aplicado é semelhante a <a href="https://www.elastic.co/blog/elasticsearch-opensearch-performance-gap#testing-methodology">esta comparação de desempenho já publicada</a>, com pools de node dedicados para Elasticsearch, OpenSearch e Rally. O <a href="https://github.com/elastic/elasticsearch-opensearch-vector-performance/blob/main/terraform/main.tf">script terraform</a> está disponível (junto com todas as fontes) para fazer a provisão de um cluster Kubernetes com:</p><ul><li><p>1 Node pool para Elasticsearch com 3 máquinas <code>e2-standard-32</code> (128 GB de RAM e 32 CPUs)</p></li><li><p>1 Node pool para OpenSearch com 3 máquinas <code>e2-standard-32</code> (128 GB de RAM e 32 CPUs)</p></li><li><p>1 Node pool para Rally com 2 <code>t2a-standard-16</code> máquinas (64GB de RAM e 16 CPUs)</p></li></ul><p>Cada "trilha" (ou teste) foi executada 10 vezes em cada configuração, que incluía diferentes motores, diferentes configurações e diferentes tipos de vetor. As trilhas têm tarefas que se repetem entre 1.000 e 10.000 vezes, dependendo da trilha. Se uma das tarefas em uma trilha falhou, por exemplo, devido a um tempo-limite de rede, então todas as tarefas foram descartadas, de modo que todos os resultados representam trilhas que começaram e terminaram sem problemas. Todos os resultados dos testes são validados estatisticamente, garantindo que as melhorias não sejam coincidência.</p><h2>Resultados detalhados</h2><p>Por que comparar usando o 99º percentil e não a latência média? Considere um exemplo hipotético de preços médios de casas em um determinado bairro. O preço médio pode indicar uma área cara, mas, em uma inspeção mais detalhada, pode ser que a maioria das casas tenha um valor muito mais baixo, com apenas algumas propriedades de luxo inflando o valor médio. Isso ilustra como o preço médio pode não representar com precisão todo o espectro de valores das casas na área. Isso é semelhante a examinar os tempos de resposta, onde a média pode ocultar problemas críticos.</p><h4>Tarefas</h4><ul><li><p>KNN aproximado com k:10 n:50</p></li><li><p>KNN Aproximado com k:10 n:100</p></li><li><p>KNN aproximado com k:100 n:1000</p></li><li><p>KNN aproximado com k:10 n:50 e filtros de palavras-chave</p></li><li><p>KNN aproximado com k:10 n:100 e filtros de palavras-chave</p></li><li><p>KNN aproximado com k:100 n:1000 e filtros de palavras-chave</p></li><li><p>KNN aproximado com k:10 n:100 em conjunto com indexação</p></li><li><p>KNN exato (pontuação do script)</p></li></ul><h4>Motores vetoriais</h4><ul><li><p><code>lucene</code> no Elasticsearch e OpenSearch, ambos na versão 9.10</p></li><li><p><code>faiss</code> no OpenSearch</p></li><li><p><code>nmslib</code> no OpenSearch</p></li></ul><h4>Tipos de vetor</h4><ul><li><p><code>hnsw</code> no Elasticsearch e OpenSearch</p></li><li><p><code>int8_hnsw</code> no Elasticsearch (HNSW com quantização automática de 8 bits: <a href="https://www.elastic.co/search-labs/blog/evaluating-scalar-quantization">link</a>)</p></li><li><p><code>sq_fp16 hnsw </code>no OpenSearch (HNSW com quantização automática de 16 bits: <a href="https://opensearch.org/docs/2.14/search-plugins/knn/knn-vector-quantization#faiss-16-bit-scalar-quantization">link</a>)</p></li></ul><h4>Busca de segmento simultânea e pronta para uso</h4><p>Como você provavelmente sabe, o Lucene é uma biblioteca de mecanismo de busca de texto de alto desempenho escrita em Java que serve como espinha dorsal para muitas plataformas de busca, como Elasticsearch, OpenSearch e Solr. No núcleo, o Lucene organiza os dados em segmentos, que são essencialmente índices independentes que permitem ao Lucene executar buscas com mais eficiência. Portanto, quando você emite uma busca para qualquer mecanismo de busca baseado em Lucene, sua busca acabará sendo executada nesses segmentos, sequencialmente ou em paralelo.</p><p>O OpenSearch introduziu a busca simultânea por segmentos como uma opção e não a utiliza como padrão. Você deve habilitá-la usando uma configuração especial de índice <code>index.search.concurrent_segment_search.enabled</code> conforme detalhado <a href="https://opensearch.org/docs/latest/search-plugins/concurrent-segment-search/">aqui</a>, com algumas <a href="https://opensearch.org/docs/latest/search-plugins/concurrent-segment-search/#other-considerations">limitações</a>.</p><p>O Elasticsearch, por outro lado, busca em segmentos simultaneamente <a href="https://github.com/elastic/elasticsearch/pull/101230">prontos para uso</a>; portanto, as comparações que fazemos neste blog levarão em consideração, além dos diferentes mecanismos vetoriais e tipos de vetores, também as diferentes configurações:</p><ul><li><p>Elasticsearch ootb: Elasticsearch pronto para uso, com busca de segmento simultânea;</p></li><li><p>OpenSearch ootb: sem busca de segmento simultânea habilitada;</p></li><li><p>OpenSearch css: com busca de segmento simultânea habilitada</p></li></ul><p>Agora vamos nos aprofundar em alguns resultados detalhados para cada conjunto de dados vetoriais testado:</p><h2>2,5 milhões de vetores, 1536 dimensões (openai_vector)</h2><p>Começando com a faixa mais simples, mas também a maior em termos de dimensões, <a href="https://github.com/elastic/rally-tracks/edit/master/openai_vector">openai_vector</a> - que usa o <a href="https://huggingface.co/datasets/BeIR/nq">conjunto de dados NQ</a> enriquecido com embeddings gerados usando o <a href="https://openai.com/blog/new-and-improved-embedding-model">modelo text-embedding-ada-002</a> do OpenAI. É o mais simples, pois testa apenas o KNN aproximado e tem apenas 5 tarefas. Ele testa de forma autônoma (sem indexação) e junto com a indexação, usando um único cliente e 8 clientes simultâneos.</p><h3>Tarefas</h3><ul><li><p><strong>standalone-search-knn-10-100-multiple-clients</strong>: buscando em 2,5 milhões de vetores com 8 clientes simultaneamente, k: 10 e n:100</p></li><li><p><strong>standalone-search-knn-100-1000-multiple-clients</strong>: buscando em 2,5 milhões de vetores com 8 clientes simultaneamente, k: 100 e n:1000</p></li><li><p><strong>standalone-search-knn-10-100-single-client</strong>: buscando em 2,5 milhões de vetores com um único cliente, k: 10 e n:100</p></li><li><p><strong>standalone-search-knn-100-1000-single-client</strong>: buscando em 2,5 milhões de vetores com um único cliente, k: 100 e n:1000</p></li><li><p><strong>parallel-documents-indexing-search-knn-10-100</strong>: buscando em 2,5 milhões de vetores e, ao mesmo tempo, indexando 100.000 documentos adicionais, k:10 e n:100</p></li></ul><p>O desempenho médio do p99 é descrito abaixo:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf5791848eb0c12fb/6a17d72925daab9cae08a09e/eea0b2b49c690baada3e09d6968e513bfffe51a9-1440x318.webp" alt="tabela openai_vector" /><p>Aqui, observamos que o Elasticsearch é entre <strong>3x-8x mais rápido</strong> do que o OpenSearch ao realizar a busca vetorial junto com a indexação (i.e. leitura+escrita) com :10 e :100 e <strong>2x-3x mais rápido</strong> sem indexação para os mesmos k e n. Para :100 e :1000 (<em>standalone-search-knn-100-1000-single-client</em> e <em>standalone-search-knn-100-1000-multiple-clients</em> ), o Elasticsearch é <strong>2x a 7x</strong> mais rápido que o OpenSearch, em média.</p><p>Os resultados detalhados mostram os casos exatos e os mecanismos vetoriais comparados:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdbe41d3187ced7ec/6a17d72a445de951c44cff4c/a7a761ed631d3e6211beb83d9d93d752d10123c9-1440x1728.webp" alt="openai_vector" /><h4>Recall</h4><p></p><p>knn-recall-10-100</p><p>knn-recall-100-1000</p><p>Elasticsearch-8.14.0@lucene-hnsw</p><p>0,969485</p><p>0,995138</p><p>Elasticsearch-8.14.0@lucene-int8_hnsw</p><p>0,781445</p><p>0,784817</p><p>OpenSearch-2.14.0@lucene-hnsw</p><p>0,96519</p><p>0,995422</p><p>OpenSearch-2.14.0@faiss</p><p>0,984154</p><p>0,98049</p><p>OpenSearch-2.14.0@faiss-sq_fp16</p><p>0,980012</p><p>0,97721</p><p>OpenSearch-2.14.0@nmslib</p><p>0,982532</p><p>0,99832</p><h2>10 milhões de vetores, 96 dimensões (dense_vector)</h2><p>Em <a href="https://github.com/elastic/rally-tracks/tree/master/dense_vector">dense_vector</a> com 10 milhões de vetores e 96 dimensões. Ele é baseado no conjunto de dados de imagens <a href="https://big-ann-benchmarks.com/">Yandex DEEP1B</a>. O conjunto de dados é criado a partir dos primeiros 10 milhões de vetores do arquivo "sample data" chamado <code>learn.350M.fbin</code>. As operações de busca usam vetores da consulta do arquivo "query data".<code>public.10K.fbin</code>.</p><p>Tanto o Elasticsearch quanto o OpenSearch têm um desempenho muito bom nesse conjunto de dados, principalmente após uma <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-forcemerge.html">fusão forçada</a>, que geralmente é feita em índices somente leitura e é semelhante à desfragmentação do índice para ter uma única "tabela" para buscas.</p><h3>Tarefas</h3><p>Cada tarefa é aquecida para 100 solicitações e, em seguida, 1.000 solicitações são medidas</p><ul><li><p><strong>knn-search-10-100</strong>: buscando em 10 milhões de vetores, k: 10 e n:100</p></li><li><p><strong>knn-search-100-1000</strong>: buscando em 10 milhões de vetores, k: 100 e n:1000</p></li><li><p><strong>knn-search-10-100-force-merge</strong>: buscando em 10 milhões de vetores após uma fusão forçada, k: 10 e n:100</p></li><li><p><strong>knn-search-100-1000-force-merge</strong>: buscando em 10 milhões de vetores após uma fusão forçada, k: 100 e n:1000</p></li><li><p><strong>knn-search-100-1000-concorrente-com-indexação</strong>: buscando em 10 milhões de vetores enquanto também atualiza <a href="https://github.com/elastic/rally-tracks/blob/master/dense_vector/challenges/default.json#L76C36-L76C37">5% do conjunto de dados</a>, k: 100 e n:1000</p></li><li><p><strong>script-score-query</strong>: busca KNN exata de <a href="https://github.com/elastic/rally-tracks/blob/master/dense_vector/queries.json">2000 vetores específicos</a>.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4629d06af85eb96c/6a17d72c6864a423e7b685dc/174995e0a2156d86359cdb7aa446dfaae6312ea4-1440x316.webp" alt="dense_vector" /><p>Tanto o Elasticsearch quanto o OpenSearch tiveram bom desempenho para Approximate KNN. Quando o índice é mesclado (tem apenas um único segmento) em <em>knn-search-100-1000-force-merge</em> e <em>knn-search-10-100-force-merge</em>, o OpenSearch tem um desempenho melhor do que os outros ao usar <code>nmslib</code> e <code>faiss</code>, mesmo que todos estejam em torno de 15ms e muito próximos.</p><p>No entanto, quando o índice tem múltiplos segmentos (situação típica em que um índice recebe atualizações nos documentos) em <em>knn-search-10-100</em> e <em>knn-search-100-1000</em>, o Elasticsearch mantém a latência em cerca de ~7 ms e ~16 ms, enquanto todos os outros mecanismos OpenSearch são mais lentos.</p><p>Além disso, quando o índice está sendo buscado e gravado ao mesmo tempo (<em>knn-search-100-1000-concurrent-with-indexing</em>), o Elasticsearch mantém a latência abaixo de 15ms (13,8ms), sendo quase <strong>4 vezes mais rápido</strong> do que o OpenSearch pronto para uso (49,3ms) e ainda mais rápido quando a busca de segmento simultânea está ativada (17,9ms), mas muito próximo para ser significativo.</p><p>Quanto ao Exact KNN, a diferença é muito maior: o Elasticsearch <strong>é 6x mais rápido</strong> que o OpenSearch (~260ms vs ~1600ms).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt254f43bcaa3dbfc2/6a17d72ddbb4ffc780fb54ff/17aec6be31117440bc4d1f99984aed95df1c4f6b-1440x1728.webp" alt="dense_vector" /><h4>Recall</h4><p></p><p>knn-recall-10-100</p><p>knn-recall-100-1000</p><p>Elasticsearch-8.14.0@lucene-hnsw</p><p>0,969843</p><p>0,996577</p><p>Elasticsearch-8.14.0@lucene-int8_hnsw</p><p>0,775458</p><p>0,840254</p><p>OpenSearch-2.14.0@lucene-hnsw</p><p>0,971333</p><p>0,996747</p><p>OpenSearch-2.14.0@faiss</p><p>0,9704</p><p>0,914755</p><p>OpenSearch-2.14.0@faiss-sq_fp16</p><p>0,968025</p><p>0,913862</p><p>OpenSearch-2.14.0@nmslib</p><p>0,9674</p><p>0,910303</p><h2>2 milhões de vetores, 768 dimensões (so_vector)</h2><p>Essa <a href="https://github.com/elastic/rally-tracks/tree/master/so_vector">trilha</a>, <code>so_vector</code>, é derivada de um <a href="https://archive.org/download/stackexchange/stackoverflow.com-Posts.7z">dump de postagens do StackOverflow baixadas</a> em 21 de abril de 2022. Ele contém apenas documentos de perguntas — todos os documentos que representam respostas foram retirados. O título de cada pergunta foi codificado em um vetor usando o modelo de transformador de sentença <a href="https://huggingface.co/sentence-transformers/multi-qa-mpnet-base-cos-v1">multi-qa-mpnet-base-cos-v1</a>. Este conjunto de dados contém as primeiras 2 milhões de perguntas.</p><p>Diferente da trilha anterior, cada documento aqui contém outros campos além de vetores para aceitar recursos de teste como KNN Aproximado com filtragem e busca híbrida. <code>nmslib</code> para OpenSearch está notavelmente ausente neste teste, já que <a href="https://opensearch.org/docs/latest/search-plugins/knn/filter-search-knn/#k-nn-search-with-filters">ele não aceita filtros</a>.</p><h3>Tarefas</h3><p>Cada tarefa é aquecida com 100 solicitações e, em seguida, 100 solicitações são medidas. Observe que as tarefas foram agrupadas para simplificar, pois o teste contém 16 tipos de busca * 2 valores de k diferentes * 3 valores de n diferentes.</p><ul><li><p><strong>knn-10-50</strong>: buscando em 2 milhões de vetores sem filtros, k:10 e n:50</p></li><li><p><strong>knn-10-50-filtrado</strong>: buscando em 2 milhões de vetores <a href="https://github.com/elastic/rally-tracks/blob/master/so_vector/operations/default.json">com filtros</a>, k:10 e n:50</p></li><li><p><strong>knn-10-50-após-fusão-forçada</strong>: buscando em 2 milhões de vetores com filtros e após uma fusão forçada, k:10 e n:50</p></li><li><p><strong>knn-10-100</strong>: buscando em 2 milhões de vetores sem filtros, k:10 e n:100</p></li><li><p><strong>knn-10-100-filtered</strong>: buscando em 2 milhões de vetores <a href="https://github.com/elastic/rally-tracks/blob/master/so_vector/operations/default.json">com filtros</a>, k:10 e n:100</p></li><li><p><strong>knn-10-100-after-force-merge</strong>: buscando em 2 milhões de vetores com filtros e após uma fusão forçada, k:10 e n:100</p></li><li><p><strong>knn-100-1000</strong>: buscando em 2 milhões de vetores sem filtros, k:100 e n:1000</p></li><li><p><strong>knn-100-1000-filtered</strong>: buscando em 2 milhões de vetores <a href="https://github.com/elastic/rally-tracks/blob/master/so_vector/operations/default.json">com filtros</a>, k:100 e n:1000</p></li><li><p><strong>knn-100-1000-after-force-merge</strong>: buscando em 2 milhões de vetores com filtros e após uma fusão forçada, k:100 e n:1000</p></li><li><p><strong>exact-knn</strong>: busca KNN exata <a href="https://github.com/elastic/rally-tracks/blob/master/so_vector/operations/default.json#L56">com e sem filtros</a>.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8b44e1306af35877/6a17d72f577262aca11bca3e/d4ed2982d55370cad4b2048b23ce97caa55017c0-1440x316.webp" alt="tabela so_vector" /><p>O Elasticsearch é <strong>consistentemente mais rápido</strong> que o OpenSearch pronto para uso neste teste, apenas em dois casos o OpenSearch é mais rápido, e não muito (<em>knn-10-100</em> e <em>knn-100-1000</em>). Tarefas envolvendo <em>knn-10-50</em>, <em>knn-10-100</em> e <em>knn-100-1000</em> em combinação com filtros mostram uma diferença de até <strong>7x</strong> (112ms vs 803ms).</p><p>O desempenho de ambas as soluções parece se equilibrar após uma "force merge", o que é compreensível, conforme evidenciado por <em>knn-10-50-after-force-merge</em>, <em>knn-100-after-force-merge</em> e <em>knn-100-1000-after-force-merge</em>. Nessas tarefas, <code>faiss</code> é mais rápido.</p><p>O desempenho do Exact KNN mais uma vez é muito diferente, com o Elasticsearch sendo <strong>13 vezes mais rápido</strong> que o OpenSearch desta vez (~385ms vs ~5262ms).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf80ccc7c2df84559/6a17d7314b055de00d43203f/615cb9228eb05ddd2e9512b3a6a5bc88d4088a1a-1440x1440.webp" alt="so_vector" /><h4>Recall</h4><p></p><p>knn-recall-10-100</p><p>knn-recall-100-1000</p><p>knn-recall-10-50</p><p>Elasticsearch-8.14.0@lucene-hnsw</p><p>1</p><p>1</p><p>1</p><p>Elasticsearch-8.14.0@lucene-int8_hnsw</p><p>1</p><p>0,986667</p><p>1</p><p>OpenSearch-2.14.0@lucene-hnsw</p><p>1</p><p>1</p><p>1</p><p>OpenSearch-2.14.0@faiss</p><p>1</p><p>1</p><p>1</p><p>OpenSearch-2.14.0@faiss-sq_fp16</p><p>1</p><p>1</p><p>1</p><p>OpenSearch-2.14.0@nmslib</p><p>0,9674</p><p>0,910303</p><p>0,976394</p><h2>Elasticsearch e Lucene como vencedores claros</h2><p>Na Elastic, estamos inovando incessantemente o Apache Lucene e o Elasticsearch para garantir que sejamos capazes de fornecer o principal banco de dados vetorial para casos de uso de busca e recuperação, incluindo RAG (retrieval-augmented generation). Nossos avanços recentes aumentaram drasticamente o desempenho, tornando a busca vetorial <a href="https://search-labs.elastic.co/search-labs/blog/elasticsearch-lucene-vector-database-gains">mais rápida e mais eficiente em termos de espaço</a> do que antes, com base nos ganhos do Lucene 9.10. Esse blog apresentou um estudo que mostra que, ao comparar versões atualizadas, o Elasticsearch é até 12 vezes mais rápido que o OpenSearch.</p><p>Vale a pena observar que ambos os produtos usam a mesma versão do Lucene (<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/release-notes-8.14.0.html">notas de lançamento do Elasticsearch 8.14</a> e <a href="https://github.com/opensearch-project/OpenSearch/blob/2.14/release-notes/opensearch.release-notes-2.14.0.md">notas de lançamento do OpenSearch 2.14</a>).</p><p>O ritmo de inovação na Elastic proporcionará ainda mais não apenas para nossos clientes no local e do Elastic Cloud, mas também para aqueles que usam nossa <a href="https://www.elastic.co/search-labs/blog/stateless-your-new-state-of-find-with-elasticsearch">plataforma sem estado</a>. Recursos como suporte para <a href="https://www.elastic.co/search-labs/blog/int4-scalar-quantization-in-lucene">quantização escalar para int4</a> serão oferecidos com testes rigorosos para que os clientes possam utilizar essas técnicas sem uma queda significativa no recall, semelhante aos <a href="https://www.elastic.co/search-labs/blog/evaluating-scalar-quantization">nossos testes para int8</a>.</p><p>A eficiência da busca vetorial está sendo um recurso inegociável nos mecanismos de busca modernos devido à proliferação de aplicações de IA e machine learning. Nas organizações que procuram um mecanismo de busca poderoso, capaz de acompanhar as demandas de dados vetoriais de alto volume e alta complexidade, o Elasticsearch é a resposta definitiva.</p><p>Seja expandindo uma plataforma estabelecida ou iniciando novos projetos, a integração do Elasticsearch para as necessidades de busca vetorial é um movimento estratégico que produzirá benefícios tangíveis e de longo prazo. Com a vantagem de desempenho comprovada, o Elasticsearch está pronto para sustentar a próxima onda de inovações em busca.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-opensearch-vector-search-performance-comparison</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-opensearch-vector-search-performance-comparison</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <category><![CDATA[Lucene]]></category>
    <dc:creator><![CDATA[Ugo Sangiorgi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5d70b25967c2194e/6a17d732b1e11383f879f0ca/13c3c0053e2968fb835ba2f90f34bec3a011b5c0-880x592.webp" length="0" type="image/webp"/>
    <pubDate>Wed, 26 Jun 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Medidas e pontuação de similaridade vetorial]]></title>
    <description><![CDATA[Explore as medidas de similaridade vetorial e a pontuação no Elasticsearch, incluindo distância L1 e L2, similaridade de cosseno, similaridade de produto escalar e similaridade de produto interno máximo.]]></description>
    <content:encoded><![CDATA[<p>Quando surge a necessidade de pesquisar texto livre e Ctrl+F / Cmd+F não são mais suficientes, usar um mecanismo de busca lexical geralmente é a próxima escolha lógica que vem à mente. Os mecanismos de busca lexical são excelentes em analisar e tokenizar o texto a ser pesquisado em termos que podem ser correspondidos no momento da pesquisa, mas geralmente falham quando se trata de entender e dar sentido ao verdadeiro significado do texto que está sendo indexado e pesquisado.</p><p>É exatamente aí que os mecanismos de busca de vetores brilham. Eles podem indexar o mesmo texto de forma que ele possa ser pesquisado com base no significado que ele representa e em suas relações com outros conceitos que tenham significado semelhante ou relacionado.</p><p>Neste blog, abordaremos brevemente como os vetores são um ótimo conceito matemático para transmitir o significado do texto. Em seguida, nos aprofundaremos nas diferentes técnicas de similaridade suportadas pelo Elasticsearch quando se trata de pesquisar vetores vizinhos, ou seja, pesquisar vetores com significado semelhante, e como pontuá-los.</p><h2>O que são embeddings vetoriais?</h2><p>Este artigo não se aprofunda nas complexidades das incorporações de vetores. Se você quiser explorar mais esse tópico ou precisar de uma introdução antes de continuar, recomendamos conferir o <a href="https://www.elastic.co/pt/what-is/vector-embedding">guia a seguir</a>.</p><p>Em poucas palavras, os embeddings vetoriais são obtidos por meio de um processo de aprendizado de máquina (por exemplo, redes neurais de aprendizado profundo) que transformam qualquer tipo de dado de entrada não estruturado (por exemplo, texto bruto, imagem, vídeo, som, etc.) em dados numéricos que carregam seu significado e relacionamentos. Diferentes tipos de dados não estruturados exigem diferentes tipos de modelos de aprendizado de máquina que foram treinados para "entender" cada tipo de dado.</p><p>Cada vetor localiza um dado específico como um ponto em um espaço multidimensional e essa localização representa um conjunto de características que o modelo usa para caracterizar os dados. O número de dimensões depende do modelo de aprendizado de máquina, mas geralmente varia de algumas centenas a alguns milhares. Por exemplo, <a href="https://platform.openai.com/docs/guides/embeddings">os modelos OpenAI Embeddings</a> possuem 1536 dimensões, enquanto <a href="https://docs.cohere.com/reference/embed">os modelos Cohere Embeddings</a> podem variar de 382 a 4096 dimensões. O tipo de campo dense_vector do Elasticsearch suporta até 4.096 dimensões na versão mais recente.</p><p>O verdadeiro feito das incorporações vetoriais é que os pontos de dados que compartilham significados semelhantes ficam próximos no espaço. Outro aspecto interessante é que as incorporações vetoriais também ajudam a capturar relacionamentos entre pontos de dados.</p><h2>Como comparamos vetores?</h2><p>Sabendo que dados não estruturados são fatiados e divididos por modelos de aprendizado de máquina em incorporações vetoriais que capturam a similaridade dos dados ao longo de um grande número de dimensões, agora precisamos entender como funciona a correspondência desses vetores. Acontece que a resposta é bem simples.</p><p>Incorporações de vetores que estão <strong>próximas</strong> umas das outras representam partes de dados <strong>semanticamente semelhantes</strong> . Então, quando consultamos um banco de dados vetorial, a entrada da pesquisa (imagem, texto, etc.) é primeiro transformada em embeddings vetoriais usando o mesmo modelo de aprendizado de máquina que foi usado para indexar todos os dados não estruturados, e o objetivo final é encontrar os <strong>vetores vizinhos mais próximos</strong> daquele vetor de consulta. Portanto, tudo o que precisamos fazer é descobrir como medir a "distância" ou "similaridade" entre o vetor de consulta e todos os vetores existentes indexados no banco de dados - é simples assim.</p><h2>Distância, similaridade e pontuação</h2><p>Felizmente para nós, medir a distância ou similaridade entre dois vetores é um problema fácil de resolver graças à aritmética vetorial. Então, vamos dar uma olhada nas funções de distância e similaridade mais populares suportadas pelo Elasticsearch. Atenção, matemática à frente!</p><p>Antes de começarmos, vamos dar uma olhada rápida na pontuação. Na verdade, o Lucene só permite que as pontuações sejam positivas. Todas as funções de distância e similaridade que apresentaremos em breve fornecem uma medida de quão próximos ou similares dois vetores são, mas esses números brutos raramente são adequados para serem usados como pontuação, pois podem ser negativos. Por esse motivo, a pontuação final precisa ser derivada do valor de distância ou similaridade de uma forma que garanta que a pontuação será positiva e uma pontuação maior corresponde a uma classificação mais alta (ou seja, para vetores mais próximos).</p><h3>Distância L1</h3><p>A distância L1, também chamada de distância de Manhattan, de dois vetores  e  é medida pela soma da diferença absoluta em pares de todos os seus elementos. Obviamente, quanto menor a distância , mais próximos os dois vetores estarão. A fórmula da distância L1 (1) é bastante simples, como pode ser visto abaixo:</p><p>Visualmente, a distância L1 pode ser ilustrada conforme mostrado na imagem abaixo (em vermelho):</p><p>O cálculo da distância L1 dos dois vetores a seguir  e  resultaria em </p><p><strong>Importante:</strong> Vale ressaltar que a função de distância L1 só é suportada para <a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/current/query-dsl-script-score-query.html#vector-functions-l1">pesquisa vetorial exata</a> (também conhecida como pesquisa de força bruta) usando a consulta DSL <code>script_score</code> , mas não para <a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/8.11/dense-vector.html#dense-vector-params">pesquisa kNN aproximada</a> usando a<a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/8.11/knn-search.html#approximate-knn"> opção de pesquisa</a> <a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/8.11/knn-search.html#approximate-knn"><code>knn</code></a>ou<a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/current/query-dsl-knn-query.html"> a consulta DSL</a> <a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/current/query-dsl-knn-query.html"><code>knn</code></a> .</p><h3>Distância L2</h3><p>A distância L2, também chamada de distância euclidiana, de dois vetores  e  é medida primeiro somando o quadrado da diferença de pares de todos os seus elementos e depois tirando a raiz quadrada do resultado. É basicamente o caminho mais curto entre dois pontos. Similarmente a L1, quanto menor a distância , mais próximos os dois vetores estão:</p><p>A distância L2 é mostrada em vermelho na imagem abaixo:</p><p>Vamos reutilizar os mesmos dois vetores de amostra  e  que usamos para a distância  , e agora podemos calcular a distância  como .</p><p>Em termos de pontuação, quanto menor a distância entre dois vetores, mais próximos (ou seja, mais semelhantes) eles são. Então, para derivar uma pontuação, precisamos inverter a medida de distância, de modo que a menor distância produza a maior pontuação. A maneira como a pontuação é calculada ao usar a distância L2 é mostrada na fórmula (3) abaixo:</p><p>Reutilizando os vetores de amostra do exemplo anterior, sua pontuação seria . Dois vetores muito próximos um do outro tenderão a uma pontuação de 1, enquanto a pontuação de dois vetores muito distantes um do outro tenderá a 0.</p><p>Concluindo as funções de distância L1 e L2, uma boa analogia para compará-las é pensar em A e B como dois edifícios em Manhattan, Nova York. Um táxi indo de A para B teria que dirigir pelo caminho L1 (ruas e avenidas), enquanto um pássaro provavelmente usaria o caminho L2 (linha reta).</p><h3>Similaridade de cosseno</h3><p>Ao contrário de L1 e L2, a similaridade do cosseno não mede a distância entre dois vetores  e , mas sim seu ângulo relativo, ou seja, se ambos estão apontando aproximadamente na mesma direção. Quanto maior a similaridade , menor o ângulo  entre os dois vetores e, portanto, mais "próximos" eles são e mais "semelhantes" são seus significados transmitidos.</p><p>Para ilustrar isso, vamos pensar em duas pessoas em uma área selvagem olhando em direções diferentes. Na figura abaixo, a pessoa de azul olha na direção simbolizada pelo vetor  e a pessoa de vermelho na direção do vetor . Quanto mais eles direcionarem sua visão na mesma direção (ou seja, quanto mais próximos seus vetores estiverem), mais seu campo de visão simbolizado pelas áreas azul e vermelha se sobreporá. O quanto seus campos de visão se sobrepõem é a similaridade de seus cossenos. Entretanto, observe que a pessoa B parece mais distante do que a pessoa A (ou seja, o vetor  é maior). A pessoa B pode estar olhando para uma montanha distante no horizonte, enquanto a pessoa A pode estar olhando para uma árvore próxima. Para similaridade de cosseno, isso não desempenha nenhum papel, pois é apenas uma questão de ângulo.</p><p>Agora vamos calcular essa similaridade de cosseno. A fórmula (4) é bastante simples, onde o numerador consiste no produto escalar de ambos os vetores e o denominador contém o produto de sua magnitude (ou seja, seu comprimento):</p><p>A similaridade do cosseno entre  e  é mostrada na imagem abaixo como uma medida do ângulo entre eles (em vermelho):</p><p>Vamos fazer um rápido desvio para explicar o que esses valores de similaridade de cosseno significam concretamente. Como pode ser visto na imagem abaixo representando a função cosseno, os valores sempre oscilam no intervalo  .</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc0e4eb98ebb64cf3/6a17da287b54f983818b3783/e31145284b8c27d0bd9e3d2831f811388134fd83-1188x272.png" alt="cos-função" /><p>Lembre-se de que, para que dois vetores sejam considerados semelhantes, seu ângulo deve ser o mais agudo possível, idealmente próximo de um ângulo  , o que se resumiria a uma semelhança perfeita de . Em outras palavras, quando os vetores são...</p><ol><li><p>...<strong>próximos</strong> um do outro, o cosseno do seu ângulo se aproxima  (ou seja, próximo de )</p></li></ol><ol><li><p>...<strong>não relacionados</strong>, o cosseno do seu ângulo se aproxima  (ou seja, próximo de )</p></li></ol><ol><li><p>...<strong>oposto</strong>, o cosseno do seu ângulo se aproxima  (ou seja, próximo de )</p></li></ol><p>Agora que sabemos como calcular a similaridade do cosseno entre dois vetores e temos uma boa ideia de como interpretar o valor resultante, podemos reutilizar os mesmos vetores de amostra  e  e calcular sua similaridade do cosseno usando a fórmula (4) que vimos anteriormente.</p><p>Obtemos uma similaridade de cosseno de , que é mais próxima de  do que de , o que significa que os dois vetores são <strong>um tanto similares</strong>, ou seja, não são perfeitamente similares, mas também não são completamente independentes e certamente não têm significados opostos.</p><p>Para derivar uma pontuação positiva de qualquer valor de similaridade de cosseno, precisamos usar a seguinte fórmula (5), que transforma os valores de similaridade de cosseno que oscilam dentro do intervalo  em pontuações no intervalo  :</p><p>A pontuação para os vetores de amostra  e  seria: .</p><h3>Similaridade do produto escalar</h3><p>Uma desvantagem da similaridade de cosseno é que ela leva em conta apenas o ângulo entre dois vetores, mas não sua magnitude, o que significa que se dois vetores apontam aproximadamente na mesma direção, mas um é muito maior que o outro, ambos ainda serão considerados semelhantes. A similaridade do produto escalar, também chamada de similaridade escalar ou de produto interno, melhora isso ao levar em conta tanto o ângulo quanto a magnitude dos vetores, o que fornece uma métrica de similaridade mais precisa. Para tornar a magnitude dos vetores irrelevante, a similaridade do produto escalar exige que os vetores sejam normalizados primeiro, de modo que, em última análise, estamos apenas comparando vetores de comprimento unitário 1.</p><p>Vamos tentar ilustrar isso novamente com as mesmas duas pessoas de antes, mas, desta vez, as colocamos no meio de uma sala circular, de modo que seu alcance de visão seja exatamente o mesmo (ou seja, o raio da sala). De forma semelhante à similaridade do cosseno, quanto mais eles se voltam na mesma direção (ou seja, quanto mais próximos seus vetores ficam), mais seus campos de visão se sobrepõem. Entretanto, ao contrário da similaridade de cosseno, ambos os vetores têm o mesmo comprimento e ambas as áreas têm a mesma superfície, o que significa que as duas pessoas olham exatamente para a mesma imagem localizada à mesma distância. O quão bem essas duas áreas se sobrepõem denota sua similaridade de produto escalar.</p><p>Antes de introduzir a fórmula de similaridade do produto escalar, vamos ver rapidamente como um vetor pode ser normalizado. É bem simples e pode ser feito em duas etapas triviais:</p><ol><li><p>calcular a magnitude do vetor</p></li><li><p>divida cada componente pela magnitude obtida em 1.</p></li></ol><p>Como exemplo, vamos pegar o vetor . Podemos calcular sua magnitude  como vimos anteriormente ao revisar a similaridade do cosseno, ou seja, . Então, dividindo cada componente do vetor por sua magnitude, obtemos o seguinte vetor normalizado :</p><p>Passar pelo mesmo processo para o segundo vetor  produziria o seguinte vetor normalizado :</p><p>Para derivar a fórmula de similaridade do produto escalar, podemos calcular a similaridade do cosseno entre nossos vetores normalizados  e  usando a fórmula (4), conforme mostrado abaixo:</p><p>E como a magnitude de ambos os vetores normalizados é agora , a fórmula de similaridade do produto escalar (6) simplesmente se torna... você adivinhou, um produto escalar de ambos os vetores normalizados:</p><p>Na imagem abaixo, mostramos os vetores normalizados  e  e podemos ilustrar sua similaridade de produto escalar como a projeção de um vetor sobre o outro (em vermelho).</p><p>Usando nossa nova fórmula (6), podemos calcular a similaridade do produto escalar de nossos dois vetores normalizados, o que, sem surpresa, produz exatamente o mesmo valor de similaridade que o do cosseno:</p><p>Ao aproveitar a similaridade do produto escalar, a pontuação é calculada de forma diferente dependendo se os vetores contêm valores flutuantes ou de bytes. No primeiro caso, a pontuação é calculada da mesma forma que para a similaridade do cosseno usando a fórmula (7) abaixo:</p><p>Entretanto, quando o vetor é composto de valores de bytes, a pontuação é calculada de forma um pouco diferente, conforme mostrado na fórmula (8) abaixo, onde  é o número de dimensões do vetor:</p><p>Além disso, uma restrição para produzir pontuações precisas é que todos os vetores, incluindo o vetor de consulta, devem ter o mesmo comprimento, mas não necessariamente 1.</p><h3>Similaridade máxima do produto interno</h3><p>Desde a versão 8.11, há uma nova função de similaridade que é menos restrita do que a similaridade do produto escalar, pois os vetores não precisam ser normalizados. O principal motivo para isso é explicado detalhadamente no <a href="https://www.elastic.co/pt/search-labs/blog/lucene-bringing-maximum-inner-product-to-lucene">artigo a seguir</a>, mas, para resumir muito brevemente, certos conjuntos de dados não são muito bem adaptados para ter seus vetores normalizados (por exemplo, <a href="https://www.elastic.co/pt/search-labs/blog/elasticsearch-cohere-embeddings-support">incorporações Cohere</a>) e isso pode causar problemas de relevância.</p><p>A fórmula para calcular a similaridade máxima do produto interno é exatamente a mesma que a do produto escalar (6). O que muda é a maneira como a pontuação é calculada, escalando a similaridade máxima do produto interno usando uma função por partes cuja fórmula depende se a similaridade é positiva ou negativa, conforme mostrado na fórmula (9) abaixo:</p><p>O que essa função por partes faz é dimensionar todos os valores negativos de similaridade do produto interno máximo no intervalo  e todos os valores positivos no intervalo  .</p><h2>Resumindo</h2><p>Foi uma jornada e tanto, matematicamente falando, mas aqui estão algumas lições que você pode achar úteis.</p><p>A função de similaridade que você pode usar depende, em última análise, se seus embeddings vetoriais são normalizados ou não. Se seus vetores já estiverem normalizados ou se seu conjunto de dados for independente da normalização vetorial (ou seja, a relevância não será afetada), você pode prosseguir e normalizar seus vetores e usar a similaridade do produto escalar, pois é muito mais rápido de calcular do que o cosseno, pois não há necessidade de calcular o comprimento de cada vetor. Ao comparar milhões de vetores, esses cálculos podem somar bastante.</p><p>Se seus vetores não estiverem normalizados, você tem duas opções:</p><ol><li><p>use similaridade de cosseno se normalizar seus vetores não for uma opção</p></li><li><p>use a nova similaridade máxima do produto interno se quiser que a magnitude dos seus vetores contribua para a pontuação porque eles carregam significado (por exemplo, incorporações Cohere)</p></li></ol><p>Neste ponto, calcular a distância ou similaridade entre incorporações vetoriais e como derivar suas pontuações deve fazer sentido para você. Esperamos que você tenha achado este artigo útil.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/vector-similarity-measures-and-scoring</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/vector-similarity-measures-and-scoring</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <dc:creator><![CDATA[Valentin Crettaz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte5a3e9d39849d3ed/6a17da31a2929960a3d02b3f/4d9e89678798b9de68357b5cc06dbbd8b9c6e5e8-1440x823.webp" length="0" type="image/webp"/>
    <pubDate>Mon, 13 May 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Detecção de plágio por IA: usando o Elasticsearch]]></title>
    <description><![CDATA[Veja como verificar plágio em inteligência artificial usando o Elasticsearch, com foco em casos de uso com modelos de PNL (Processamento de Linguagem Natural) e Busca Vetorial.]]></description>
    <content:encoded><![CDATA[<p>O plágio pode ser <strong>direto</strong>, envolvendo a cópia de partes ou do conteúdo inteiro, ou <strong>parafraseado</strong>, onde a obra do autor é reformulada alterando-se algumas palavras ou frases.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb6e139760e56ec98/6a171147dc55de0ad2e00edf/5d7073187fda829438aeec8d3a1194a5bea2ba57-1440x347.png" alt="" /><p>Existe uma distinção entre inspiração e paráfrase. É possível ler um conteúdo, se inspirar e depois explorar a ideia com suas próprias palavras, mesmo que você chegue a uma conclusão semelhante.</p><p>Embora o plágio seja um tema de discussão há muito tempo, a produção e publicação aceleradas de conteúdo o mantiveram relevante e representaram um desafio constante.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc2af1678cb04506b/6a171149cf4f251c2ab2d257/a0b9a98d729db09dae0a79315c001e6763c12704-1400x1016.png" alt="" /><p>Esse desafio não se limita a livros, pesquisas acadêmicas ou documentos judiciais, onde verificações de plágio são realizadas com frequência. Isso também pode se estender a jornais e até mesmo às redes sociais.</p><p>Com a abundância de informações e o fácil acesso à publicação, como o plágio pode ser verificado de forma eficaz e em larga escala?</p><p>Universidades, entidades governamentais e empresas utilizam diversas ferramentas, mas, embora uma simples <a href="https://www.elastic.co/search-labs/lexical-and-semantic-search-with-elasticsearch">busca lexical</a> possa detectar plágio direto com eficácia, o principal desafio reside na identificação <strong>de conteúdo parafraseado.</strong></p><h2>Detecção de plágio com IA generativa</h2><p>Um novo desafio surge com a IA generativa. O conteúdo gerado por IA é considerado plágio quando copiado?</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8a1ed1b6fa3f1a56/6a17114b4a531bc40736aa69/9345b28d6d27c37469bc38e823c41780b4eabfe5-1440x875.png" alt="" /><p>Os <a href="https://openai.com/policies/terms-of-use">termos de uso</a> da <a href="https://openai.com/">OpenAI , por exemplo, especificam que a OpenAI não reivindicará direitos autorais sobre o conteúdo gerado pela API para os usuários.</a> Nesse caso, os indivíduos que utilizam sua IA generativa podem usar o conteúdo gerado como preferirem, sem necessidade de citação.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbc2e08ab5b808e38/6a17114dab7f086cbadb9f93/e1f415f69a247666f02ddc81468944920c874cd7-968x814.png" alt="" /><p>No entanto, a aceitação do uso de IA generativa para melhorar a eficiência ainda é um tema de debate.</p><p>Na tentativa de contribuir para a detecção de plágio, a OpenAI desenvolveu um <a href="https://huggingface.co/roberta-base-openai-detector">modelo de detecção</a> , mas posteriormente reconheceu que sua precisão não é suficientemente alta.</p><p><em>"Acreditamos que essa precisão não é suficiente para uma detecção independente e precisa ser combinada com abordagens baseadas em metadados, julgamento humano e educação pública para ser mais eficaz."</em></p><p>O desafio persiste; no entanto, com a disponibilidade de mais ferramentas, existem agora mais opções para detectar plágio, mesmo em casos de conteúdo parafraseado e gerado por IA.</p><h2>Detecção de plágio com Elasticsearch</h2><p>Reconhecendo isso, neste blog exploraremos mais um caso de uso com modelos de Processamento de Linguagem Natural (PLN) e Busca Vetorial: a detecção de plágio, além das buscas por metadados.</p><p>Isso é demonstrado com <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/plagiarism-detection-with-elasticsearch/plagiarism_detection_es.ipynb">exemplos em Python</a>, onde utilizamos um <a href="https://sbert.net/datasets/emnlp2016-2018.json">conjunto de dados</a> do <a href="https://www.sbert.net/">SentenceTransformers</a> contendo artigos relacionados a PNL (Processamento de Linguagem Natural). Verificamos se os resumos contêm plágio realizando uma 'similaridade textual semântica', considerando representações vetoriais de 'resumos' geradas com um <a href="https://huggingface.co/sentence-transformers/all-mpnet-base-v2">modelo de incorporação de texto</a> previamente importado para o Elasticsearch. Além disso, para identificar conteúdo gerado por IA — plágio por IA —, um <a href="https://huggingface.co/roberta-base-openai-detector">modelo de PNL</a> desenvolvido pela OpenAI também foi importado para o Elasticsearch.</p><p>A imagem a seguir ilustra o fluxo de dados:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0464f88d3ed12070/6a17114fab7f084905db9f97/1ad89c98a2f42a497548ca3947749bad54ec1172-1440x880.png" alt="" /><p>Durante o <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ingest.html">processo de ingestão</a> com um <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/inference-processor.html">processador de inferência</a>, o parágrafo 'abstract' é mapeado para um vetor de 768 dimensões, o 'abstract_vector.predicted_value'.</p><p>Mapeamento:</p>"abstract_vector.predicted_value": { # Inference results field
"type": "dense_vector", 
"dims": 768, # model embedding_size
"index": "true", 
"similarity": "dot_product" # When indexing vectors for approximate kNN search, you need to specify the similarity function for comparing the vectors.
<p>A similaridade entre representações vetoriais é medida usando uma métrica de similaridade vetorial, definida pelo <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/dense-vector.html#dense-vector-params">parâmetro</a> 'similaridade'.</p><p><a href="https://en.wikipedia.org/wiki/Cosine_similarity">O cosseno</a> é a métrica de similaridade padrão, calculada como '(1 + cosseno(consulta, vetor)) / 2'. A menos que seja necessário preservar os vetores originais e não seja possível normalizá-los antecipadamente, a maneira mais eficiente de realizar a similaridade de cosseno é normalizar todos os vetores para comprimento unitário. Isso ajuda a evitar cálculos extras de comprimento de vetor durante a busca; em vez disso, use 'produto escalar'.</p><p>Nesse mesmo fluxo de trabalho, outro processador de inferência contendo o <a href="https://huggingface.co/roberta-base-openai-detector">modelo de classificação de texto</a> detecta se o conteúdo é 'Real', provavelmente escrito por humanos, ou 'Falso', provavelmente escrito por IA, adicionando o 'openai-detector.predicted_value' a cada documento.</p><p>Pipeline de ingestão:</p>client.ingest.put_pipeline( 
    id="plagiarism-checker-pipeline",
    processors = [
    {
      "inference": { #for ml models - to infer against the data that is being ingested in the pipeline
        "model_id": "roberta-base-openai-detector", #text classification model id
        "target_field": "openai-detector", # Target field for the inference results
        "field_map": { #Maps the document field names to the known field names of the model.
        "abstract": "text_field" # Field matching our configured trained model input. 
        }
      }
    },
    {
      "inference": {
        "model_id": "sentence-transformers__all-mpnet-base-v2", #text embedding model id
        "target_field": "abstract_vector", # Target field for the inference results
        "field_map": {
        "abstract": "text_field" # Field matching our configured trained model input. Typically for NLP models, the field name is text_field.
        }
      }
    }
    
  ]
)
<p>No momento da consulta, o mesmo modelo de incorporação de texto também é empregado para gerar a representação vetorial da consulta 'model_text' em um objeto 'query_vector_builder'.</p><p>Uma busca por k-vizinhos mais próximos (kNN) encontra os k vetores mais próximos do vetor de consulta, medidos pela métrica de similaridade.</p><p>A pontuação de cada documento é derivada da similaridade, garantindo que uma pontuação maior corresponda a uma classificação mais alta. Isso significa que o documento é semanticamente mais semelhante. Como resultado, estamos apresentando três possibilidades: se a pontuação for &gt; 0,9, consideramos 'alta similaridade'; se for &lt; 0,7, 'baixa similaridade'; caso contrário, 'similaridade moderada'. Você tem a flexibilidade de definir diferentes valores de limite para determinar qual nível de _score se qualifica como plágio ou não, com base no seu caso de uso.</p><p>Além disso, é realizada uma classificação de texto para verificar também a presença de elementos gerados por IA na consulta textual.</p><p>Consulta:</p>from elasticsearch import Elasticsearch
from elasticsearch.client import MlClient

#duplicated text - direct plagiarism test

model_text = 'Understanding and reasoning about cooking recipes is a fruitful research direction towards enabling machines to interpret procedural text. In this work, we introduce RecipeQA, a dataset for multimodal comprehension of cooking recipes. It comprises of approximately 20K instructional recipes with multiple modalities such as titles, descriptions and aligned set of images. With over 36K automatically generated question-answer pairs, we design a set of comprehension and reasoning tasks that require joint understanding of images and text, capturing the temporal flow of events and making sense of procedural knowledge. Our preliminary results indicate that RecipeQA will serve as a challenging test bed and an ideal benchmark for evaluating machine comprehension systems. The data and leaderboard are available at http://hucvl.github.io/recipeqa.'

response = client.search(index='plagiarism-checker', size=1,
    knn={
        "field": "abstract_vector.predicted_value",
        "k": 9,
        "num_candidates": 974,
        "query_vector_builder": { #The 'all-mpnet-base-v2' model is also employed to generate the vector representation of the query in a 'query_vector_builder' object.
            "text_embedding": {
                "model_id": "sentence-transformers__all-mpnet-base-v2",
                "model_text": model_text
            }
        }
    }
)

for hit in response['hits']['hits']:
    score = hit['_score']
    title = hit['_source']['title']
    abstract = hit['_source']['abstract']
    openai = hit['_source']['openai-detector']['predicted_value']
    url = hit['_source']['url']

    if score &gt; 0.9:
        print(f"\nHigh similarity detected! This might be plagiarism.")
        print(f"\nMost similar document: '{title}'\n\nAbstract: {abstract}\n\nurl: {url}\n\nScore:{score}\n\n")

        if openai == 'Fake':
            print("This document may have been created by AI.\n")

    elif score &lt; 0.7:
        print(f"\nLow similarity detected. This might not be plagiarism.")

        if openai == 'Fake':
            print("This document may have been created by AI.\n")

    else:
        print(f"\nModerate similarity detected.")
        print(f"\nMost similar document: '{title}'\n\nAbstract: {abstract}\n\nurl: {url}\n\nScore:{score}\n\n")

        if openai == 'Fake':
            print("This document may have been created by AI.\n")

ml_client = MlClient(client)

model_id = 'roberta-base-openai-detector' #open ai text classification model

document = [
    {
        "text_field": model_text
    }
]

ml_response = ml_client.infer_trained_model(model_id=model_id, docs=document)

predicted_value = ml_response['inference_results'][0]['predicted_value']

if predicted_value == 'Fake':
    print("\nNote: The text query you entered may have been generated by AI.\n")
<p>Saída:</p>High similarity detected! This might be plagiarism.

Most similar document: 'RecipeQA: A Challenge Dataset for Multimodal Comprehension of Cooking Recipes'

Abstract: Understanding and reasoning about cooking recipes is a fruitful research direction towards enabling machines to interpret procedural text. In this work, we introduce RecipeQA, a dataset for multimodal comprehension of cooking recipes. It comprises of approximately 20K instructional recipes with multiple modalities such as titles, descriptions and aligned set of images. With over 36K automatically generated question-answer pairs, we design a set of comprehension and reasoning tasks that require joint understanding of images and text, capturing the temporal flow of events and making sense of procedural knowledge. Our preliminary results indicate that RecipeQA will serve as a challenging test bed and an ideal benchmark for evaluating machine comprehension systems. The data and leaderboard are available at[ http://hucvl.github.io/recipeqa](http://hucvl.github.io/recipeqa).

url:[http://aclweb.org/anthology/D18-1166](http://aclweb.org/anthology/D18-1166)

Score:1.0
<p>Neste exemplo, após utilizar um dos valores 'abstratos' do nosso conjunto de dados como a consulta de texto 'model_text', foi identificado plágio. A pontuação de similaridade é 1,0, indicando um alto nível de similaridade — <strong>plágio direto</strong>. A consulta vetorizada e o documento não foram reconhecidos como conteúdo gerado por IA, o que era esperado.</p><p>Consulta:</p>#similar text - paraphrase plagiarism test 

model_text = 'Comprehending and deducing information from culinary instructions represents a promising avenue for research aimed at empowering artificial intelligence to decipher step-by-step text. In this study, we present CuisineInquiry, a database for the multifaceted understanding of cooking guidelines. It encompasses a substantial number of informative recipes featuring various elements such as headings, explanations, and a matched assortment of visuals. Utilizing an extensive set of automatically crafted question-answer pairings, we formulate a series of tasks focusing on understanding and logic that necessitate a combined interpretation of visuals and written content. This involves capturing the sequential progression of events and extracting meaning from procedural expertise. Our initial findings suggest that CuisineInquiry is poised to function as a demanding experimental platform.'
<p>Saída:</p>High similarity detected! This might be plagiarism.

Most similar document: 'RecipeQA: A Challenge Dataset for Multimodal Comprehension of Cooking Recipes'

Abstract: Understanding and reasoning about cooking recipes is a fruitful research direction towards enabling machines to interpret procedural text. In this work, we introduce RecipeQA, a dataset for multimodal comprehension of cooking recipes. It comprises of approximately 20K instructional recipes with multiple modalities such as titles, descriptions and aligned set of images. With over 36K automatically generated question-answer pairs, we design a set of comprehension and reasoning tasks that require joint understanding of images and text, capturing the temporal flow of events and making sense of procedural knowledge. Our preliminary results indicate that RecipeQA will serve as a challenging test bed and an ideal benchmark for evaluating machine comprehension systems. The data and leaderboard are available at[ http://hucvl.github.io/recipeqa](http://hucvl.github.io/recipeqa).

url:[http://aclweb.org/anthology/D18-1166](http://aclweb.org/anthology/D18-1166)

Score:0.9302529

Note: The text query you entered may have been generated by AI.
<p>Ao atualizar a consulta de texto 'model_text' com um texto gerado por IA que transmite a mesma mensagem, minimizando a repetição de palavras semelhantes, a similaridade detectada ainda foi alta, mas a pontuação foi de 0,9302529 em vez de 1,0 — <strong>plágio por paráfrase</strong>. Também era esperado que essa consulta, gerada por IA, fosse detectada.</p><p>Por fim, considerando a consulta de texto 'model_text' como um texto sobre Elasticsearch, que não é um resumo de nenhum desses documentos, a similaridade detectada foi de 0,68991005, indicando baixa similaridade de acordo com os valores de limite considerados.</p><p>Consulta:</p>#different text - not a plagiarism

model_text = 'Elasticsearch provides near real-time search and analytics for all types of data.'
<p>Saída:</p>Low similarity detected. This might not be plagiarism.
<p>Embora o plágio tenha sido identificado com precisão na consulta de texto gerada pela IA, bem como em casos de paráfrase e conteúdo copiado diretamente, navegar pelo cenário da detecção de plágio envolve reconhecer vários aspectos.</p><p>No contexto da detecção de conteúdo gerado por IA, exploramos um modelo que oferece uma contribuição valiosa. No entanto, é crucial reconhecer as limitações inerentes à detecção isolada, o que torna necessária a incorporação de outros métodos para aumentar a precisão.</p><p>A variabilidade introduzida pela escolha dos modelos de incorporação de texto é outra consideração importante. Diferentes modelos, treinados com conjuntos de dados distintos, resultam em níveis variados de similaridade, o que destaca a importância das representações vetoriais de texto geradas.</p><p>Por fim, nesses exemplos, utilizamos o resumo do documento. No entanto, a detecção de plágio geralmente envolve documentos extensos, tornando essencial abordar o desafio do tamanho do texto. É comum que o texto exceda o limite de tokens de um modelo, exigindo segmentação em partes antes da construção dos embeddings. Uma <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.11/knn-search.html#nested-knn-search">abordagem prática</a> para lidar com isso envolve a utilização de estruturas aninhadas com dense_vector.</p><h2>Conclusão</h2><p>Neste blog, discutimos os desafios da detecção de plágio, particularmente em conteúdo parafraseado e gerado por IA, e como a similaridade textual semântica e a classificação de texto podem ser usadas para esse fim.</p><p>Ao combinar esses métodos, fornecemos um exemplo de detecção de plágio no qual identificamos com sucesso conteúdo gerado por IA, plágio direto e plágio por paráfrase.</p><p>O objetivo principal era estabelecer um sistema de filtragem que simplificasse a detecção, mas a avaliação humana continua sendo essencial para a validação.</p><p>Se você tiver interesse em aprender mais sobre similaridade textual semântica e PNL, recomendamos que você também confira estes links:</p><ul><li><p><a href="https://www.elastic.co/what-is/semantic-search">O que é a busca semântica?</a></p></li><li><p><a href="https://www.elastic.co/what-is/natural-language-processing">O que é processamento de linguagem natural (PLN)?</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/lexical-and-semantic-search-with-elasticsearch">Busca Léxica e Semântica com Elasticsearch</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/chunking-via-ingest-pipelines">Fragmentar documentos grandes por meio de pipelines de ingestão, juntamente com vetores aninhados, resulta em uma busca de trechos facilitada.</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ai-plagiarism-checker-with-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-plagiarism-checker-with-elasticsearch</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <category><![CDATA[Python]]></category>
    <dc:creator><![CDATA[Priscilla Parodi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt68a5bc2434a9b03b/6a1711510e2e49a09641a22a/83e05cd4f81799fbb7b7950ed87600e825ec81e9-1024x1024.png" length="0" type="image/png"/>
    <pubDate>Tue, 19 Dec 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Usando busca híbrida para caça de gopher com Elasticsearch e Go]]></title>
    <description><![CDATA[Aprenda como realizar buscas híbridas combinando buscas por palavra-chave e buscas vetoriais usando o Elasticsearch e o cliente Elasticsearch Go.]]></description>
    <content:encoded><![CDATA[<p>Nas partes anteriores desta série, foi demonstrado como usar o cliente Elasticsearch Go para <a href="https://www.elastic.co/search-labs/blog/perform-text-queries-with-the-elasticsearch-go-client">pesquisa tradicional por palavras-chave</a> e <a href="https://www.elastic.co/search-labs/blog/perform-vector-search-with-the-elasticsearch-go-client">pesquisa vetorial</a>. Esta terceira parte aborda a busca híbrida. Vamos compartilhar <a href="https://github.com/carlyrichmond/gopher-hunting-elasticsearch">exemplos</a> de como você pode combinar a pesquisa vetorial e a pesquisa por palavra-chave usando o <a href="https://www.elastic.co/elasticsearch/">Elasticsearch</a> e o <a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/index.html">cliente Elasticsearch Go</a>.</p><h2>Pré-requisitos</h2><p>Assim como na primeira parte desta série, os seguintes pré-requisitos são necessários para este exemplo:</p><ol><li><p>Instalação do Go versão 1.21 ou posterior</p></li><li><p>Crie seu próprio repositório Go usando a estrutura recomendada e o gerenciamento de pacotes abordados na <a href="https://go.dev/doc/code">documentação do Go.</a></p></li><li><p>Criando seu próprio cluster Elasticsearch, preenchido com um conjunto de <a href="https://github.com/carlyrichmond/gopher-hunting-elasticsearch#sources">páginas baseadas em roedores</a>, incluindo para o nosso amigável <a href="https://en.wikipedia.org/wiki/Gopher">Gopher</a>, da Wikipédia:</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6a436c42996e5172/6a1704cf0c48573d1c01a957/34fa81a9b4c292634c719b8303a9b6b7506d7920-1440x662.png" alt="Página do Gopher na Wikipédia" /><h2>Conectando-se ao Elasticsearch</h2><p>Lembrando que, em nossos exemplos, utilizaremos a <a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/typedapi.html">API tipada</a> oferecida pelo cliente Go. Para estabelecer uma conexão segura para qualquer consulta, é necessário configurar o cliente usando um dos seguintes métodos:</p><ol><li><p>ID da nuvem e chave de API, caso esteja utilizando o Elastic Cloud.</p></li><li><p>URL do cluster, nome de usuário, senha e certificado</p></li></ol><p>A conexão com nosso cluster localizado no Elastic Cloud seria feita da seguinte forma:</p>func GetElasticsearchClient() (*elasticsearch.TypedClient, error) {
	var cloudID = os.Getenv("ELASTIC_CLOUD_ID")
	var apiKey = os.Getenv("ELASTIC_API_KEY")

	var es, err = elasticsearch.NewTypedClient(elasticsearch.Config{
		CloudID: cloudID,
		APIKey:  apiKey,
		Logger:  &amp;elastictransport.ColorLogger{os.Stdout, true, true},
	})

	if err != nil {
		return nil, fmt.Errorf("unable to connect: %w", err)
	}

	return es, nil
}
<p>A conexão <code>client</code> pode então ser usada para pesquisa, como demonstrado nas seções subsequentes.</p><h2>Impulsionamento manual para pesquisa híbrida</h2><p>Ao combinar qualquer conjunto de algoritmos de busca, a abordagem tradicional tem sido configurar manualmente constantes para otimizar cada tipo de consulta. Especificamente, um fator é definido para cada consulta, e o conjunto de resultados combinados é comparado ao conjunto esperado para determinar a recuperação da consulta. Em seguida, repetimos o processo para vários conjuntos de fatores e escolhemos aquele que mais se aproxima do estado desejado.</p><p>Por exemplo, combinar uma consulta de pesquisa de texto simples, ampliada por um fator de <code>0.8</code> , com uma consulta KNN com um fator menor de <code>0.2</code> pode ser feito especificando o campo <code>Boost</code> em ambos os tipos de consulta, como mostrado no exemplo abaixo:</p>func HybridSearchWithBoost(client *elasticsearch.TypedClient, term string) ([]Rodent, error) {
	var k = 10
	var numCandidates = 10
	var knnBoost float32 = 0.2
	var queryBoost float32 = 0.8

	res, err := client.Search().
		Index("vector-search-rodents").
		Knn(types.KnnSearch{
			Field:         "text_embedding.predicted_value",
			Boost:         &amp;knnBoost,
			K:             &amp;k,
			NumCandidates: &amp;numCandidates,
			QueryVectorBuilder: &amp;types.QueryVectorBuilder{
				TextEmbedding: &amp;types.TextEmbedding{
					ModelId:   "sentence-transformers__msmarco-minilm-l-12-v3",
					ModelText: term,
				},
			}}).
		Query(&amp;types.Query{
			Match: map[string]types.MatchQuery{
				"title": {
					Query: term,
					Boost: &amp;queryBoost,
				},
			},
		}).
		Do(context.Background())

	if err != nil {
		return nil, err
	}

	return getRodents(res.Hits.Hits)
}
<p>O fator especificado na opção <code>Boost</code> para cada consulta é adicionado à pontuação do documento. Ao aumentar a pontuação da nossa consulta de correspondência por um fator maior do que a consulta knn, os resultados da consulta por palavra-chave recebem um peso maior.</p><p>O desafio da otimização manual, especialmente se você não for um especialista em buscas, é que ela exige ajustes para descobrir os fatores que levarão ao conjunto de resultados desejado. Trata-se simplesmente de experimentar valores aleatórios para ver o que o aproxima do resultado desejado.</p><h2>Fusão de classificação recíproca em pesquisa híbrida e cliente Go</h2><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rrf.html">O Reciprocal Rank Fusion</a>, ou RRF, foi lançado em versão de pré-visualização técnica para busca híbrida no Elasticsearch 8.9. Tem como objetivo reduzir a curva de aprendizado associada ao ajuste fino e diminuir o tempo gasto experimentando diferentes fatores para otimizar o conjunto de resultados.</p><p>Com o RRF, a pontuação do documento é recalculada combinando as pontuações pelo algoritmo abaixo:</p>score := 0.0
// q is a query in the set of queries (vector and keyword search)
for _, q := range queries {
    // result(q) is the results 
    if document in result(q) {
        // k is a ranking constant (default 60)
        // rank(result(q), d) is the document's rank within result(q) 
        // range from 1 to the window_size (default 100)
        score +=  1.0 / (k + rank(result(q), d))
    }
}

return score
<p>A vantagem de usar o RRF é que podemos aproveitar os valores padrão adequados do Elasticsearch. A constante de classificação <code>k</code> assume o valor padrão <code>60</code>. Para proporcionar um equilíbrio entre a relevância dos documentos retornados e o desempenho da consulta ao pesquisar em grandes conjuntos de dados, o tamanho do conjunto de resultados para cada consulta considerada é limitado ao valor de <code>window_size</code>, que por padrão é <code>100</code> conforme descrito na <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rrf.html#rrf-api">documentação</a>.</p><p><code>k</code> e <code>windows_size</code> também pode ser configurado dentro da configuração <code>Rrf</code> dentro do método <code>Rank</code> no cliente Go, conforme o exemplo abaixo:</p>func HybridSearchWithRRF(client *elasticsearch.TypedClient, term string) ([]Rodent, error) {
	var k = 10
	var numCandidates = 10

	// Minimum required window size for the default result size of 10
	var windowSize int64 = 10
	var rankConstant int64 = 42

	res, err := client.Search().
		Index("vector-search-rodents").
		Knn(types.KnnSearch{
			Field:         "text_embedding.predicted_value",
			K:             &amp;k,
			NumCandidates: &amp;numCandidates,
			QueryVectorBuilder: &amp;types.QueryVectorBuilder{
				TextEmbedding: &amp;types.TextEmbedding{
					ModelId:   "sentence-transformers__msmarco-minilm-l-12-v3",
					ModelText: term,
				},
			}}).
		Query(&amp;types.Query{
			Match: map[string]types.MatchQuery{
				"title": {Query: term},
			},
		}).
		Rank(&amp;types.RankContainer{
			Rrf: &amp;types.RrfRank{
				WindowSize:   &amp;windowSize,
				RankConstant: &amp;rankConstant,
			},
		}).
		Do(context.Background())

	if err != nil {
		return nil, err
	}

	return getRodents(res.Hits.Hits)
}
<h2>Conclusão</h2><p>Aqui discutimos como combinar a pesquisa vetorial e a pesquisa por palavra-chave no Elasticsearch usando o <a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/index.html">cliente Elasticsearch Go</a>.</p><p>Confira o <a href="https://github.com/carlyrichmond/gopher-hunting-elasticsearch">repositório do GitHub</a> para acessar todo o código desta série. Caso ainda não tenha feito, confira <a href="https://www.elastic.co/search-labs/blog/perform-text-queries-with-the-elasticsearch-go-client">a parte 1</a> e <a href="https://www.elastic.co/search-labs/blog/perform-vector-search-with-the-elasticsearch-go-client">a parte 2</a> para ver todo o código desta série.</p><p><em>Boa caçada aos esquilos!</em></p><h2>Recursos</h2><ol><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html">Guia do Elasticsearch</a></p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/index.html">Cliente Go do Elasticsearch</a></p></li><li><p><a href="https://www.elastic.co/what-is/vector-search">O que é busca vetorial? | Elástico</a></p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rrf.html">Fusão de Classificação Recíproca</a></p></li></ol>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/hybrid-search-with-the-elasticsearch-go-client</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/hybrid-search-with-the-elasticsearch-go-client</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <category><![CDATA[Go]]></category>
    <dc:creator><![CDATA[Carly Richmond,Laurent Saint-Félix]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdcae959696d55a9b/6a1704d5ab7f085a56db9d7c/491ef9efbb30b253e1d9e3b7f816a9a23c8f5264-721x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 02 Nov 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Realize buscas vetoriais no Elasticsearch com o cliente Elasticsearch Go.]]></title>
    <description><![CDATA[Aprenda como realizar buscas vetoriais no Elasticsearch usando o cliente Elasticsearch Go por meio de um exemplo prático.]]></description>
    <content:encoded><![CDATA[<p>Desenvolver software em qualquer linguagem de programação, incluindo Go, é comprometer-se com uma vida inteira de aprendizado. Ao longo de sua trajetória acadêmica e profissional, Carly explorou diversas linguagens de programação e tecnologias, incluindo as mais recentes e melhores implementações de busca vetorial. Mas isso não foi suficiente! Recentemente, Carly também começou a jogar Go.</p><p>Assim como os animais, as linguagens de programação e seu amigável autor, a busca passou por uma evolução de diferentes práticas, o que pode dificultar a escolha da melhor opção para o seu caso específico. Neste blog, compartilharemos uma visão geral da busca vetorial, juntamente com <a href="https://github.com/carlyrichmond/gopher-hunting-elasticsearch">exemplos</a> de cada abordagem usando <a href="https://www.elastic.co/elasticsearch/">o Elasticsearch</a> e o <a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/index.html">cliente Elasticsearch Go</a>. Estes exemplos mostrarão como encontrar esquilos-da-terra e determinar o que eles comem usando a busca vetorial no Elasticsearch e em Go.</p><h2>Pré-requisitos</h2><p>Para dar continuidade a este exemplo, certifique-se de que os seguintes pré-requisitos sejam atendidos:</p><ol><li><p>Instalação do Go versão 1.21 ou posterior</p></li><li><p>Criação do seu próprio repositório Go com o</p></li><li><p>Criação do seu próprio cluster Elasticsearch, preenchido com um conjunto de páginas baseadas em roedores, incluindo para o nosso amigável <a href="https://en.wikipedia.org/wiki/Gopher">Gopher</a>, da Wikipédia:</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6a436c42996e5172/6a1704cf0c48573d1c01a957/34fa81a9b4c292634c719b8303a9b6b7506d7920-1440x662.png" alt="Página do Gopher na Wikipédia" /><h2>Conectando-se ao Elasticsearch</h2><p>Em nossos exemplos, utilizaremos a <a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/typedapi.html">API tipada</a> oferecida pelo cliente Go. Para estabelecer uma conexão segura para qualquer consulta, é necessário configurar o cliente usando um dos seguintes métodos:</p><ol><li><p>ID da nuvem e chave de API, caso esteja utilizando o Elastic Cloud.</p></li><li><p>URL do cluster, nome de usuário, senha e certificado.</p></li></ol><p>A conexão com nosso cluster localizado no Elastic Cloud seria feita da seguinte forma:</p>func GetElasticsearchClient() (*elasticsearch.TypedClient, error) {
	var cloudID = os.Getenv("ELASTIC_CLOUD_ID")
	var apiKey = os.Getenv("ELASTIC_API_KEY")

	var es, err = elasticsearch.NewTypedClient(elasticsearch.Config{
		CloudID: cloudID,
		APIKey:  apiKey,
		Logger:  &amp;elastictransport.ColorLogger{os.Stdout, true, true},
	})

	if err != nil {
		return nil, fmt.Errorf("unable to connect: %w", err)
	}

	return es, nil
}
<p>A conexão <code>client</code> pode então ser usada para busca vetorial, como mostrado nas seções subsequentes.</p><h2>Busca vetorial</h2><p>A busca vetorial tenta resolver esse problema convertendo-o em uma comparação matemática usando vetores. O processo de incorporação de documentos possui uma etapa adicional de conversão do documento, utilizando um modelo, em uma representação vetorial densa, ou simplesmente em um fluxo de números. A vantagem dessa abordagem é que você pode pesquisar documentos não textuais, como imagens e áudio, convertendo-os em um vetor juntamente com uma consulta.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc4b3b9b2ad1e8f46/6a1704d06234e0800fdb1927/b113358093e367358d684f7aaf0a6684ebb2d0dd-1440x653.png" alt="Diagrama de Busca Vetorial" /><p>Em termos simples, a busca vetorial é um conjunto de cálculos de distância vetorial. Na ilustração abaixo, a representação vetorial da nossa consulta <code>Go Gopher</code>é comparada com os documentos no espaço vetorial, e os resultados mais próximos (denotados pela constante <code>k</code>) são retornados:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5c736ecfc49bb038/6a1704d2cf4f25bcb6b2d075/54a17a4b41029644e7c66f33d428e8d01a6f4ce3-1184x743.png" alt="Exemplo de espaço vetorial Gopher" /><p>Dependendo da abordagem usada para gerar os embeddings dos seus documentos, existem duas maneiras diferentes de descobrir o que os gophers comem.</p><h3>Abordagem 1: Traga seu próprio modelo</h3><p>Com uma licença Platinum, é possível gerar os embeddings dentro do Elasticsearch, carregando o modelo e usando a API de inferência. A configuração do modelo envolve seis etapas:</p><ol><li><p>Selecione um modelo PyTorch para carregar a partir de um repositório de modelos. Neste exemplo, estamos usando o <a href="https://huggingface.co/sentence-transformers/msmarco-MiniLM-L-12-v3">sentence-transformers/msmarco-MiniLM-L-12-v3</a> da Hugging Face para gerar os embeddings.</p></li><li><p>Carregue o modelo no Elastic usando o <a href="https://www.elastic.co/guide/en/elasticsearch/client/eland/current/overview.html">cliente Eland Machine Learning para Python</a> usando as credenciais para nosso cluster Elasticsearch e tipo de tarefa <code>text_embeddings</code>. Se você não tiver o Eland instalado, poderá <a href="https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-import-model.html#ml-nlp-import-docker">executar a etapa de importação usando o Docker</a>, conforme mostrado abaixo:</p></li></ol>docker run -it --rm --network host \
    docker.elastic.co/eland/eland \
    eland_import_hub_model \
      --cloud-id $ELASTIC_CLOUD_ID \
      --es-api-key $ELASTIC_API_KEY \
      --hub-model-id sentence-transformers/msmarco-MiniLM-L-12-v3 \
      --task-type text_embedding
<ol><li><p>Após o carregamento, teste rapidamente o modelo <code>sentence-transformers__msmarco-minilm-l-12-v3</code> com um documento de exemplo para garantir que os embeddings sejam gerados conforme o esperado:</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8a399e1f50ecfdaf/6a1704d4ab7f08935edb9d78/fbbdde621361a487eb07304bed029c228d0f7aa6-1440x789.png" alt="Exemplo de modelo treinado no Elastic Test" /><ol><li><p>Crie um pipeline de ingestão contendo um processador de inferência. Isso permitirá que a representação vetorial seja gerada usando o modelo carregado:</p></li></ol>PUT _ingest/pipeline/search-rodents-vector-embedding-pipeline
{
  "processors": [
    {
      "inference": {
        "model_id": "sentence-transformers__msmarco-minilm-l-12-v3",
        "target_field": "text_embedding",
        "field_map": {
          "body_content": "text_field"
        }
      }
    }
  ]
}
<ol><li><p>Crie um novo índice contendo o campo <code>text_embedding.predicted_value</code> do tipo <code>dense_vector</code> para armazenar os vetores de incorporação gerados para cada documento:</p></li></ol>PUT vector-search-rodents
{
  "mappings": {
    "properties": {
      "text_embedding.predicted_value": {
        "type": "dense_vector",
        "dims": 384,
        "index": true,
        "similarity": "cosine"
      },
      "text": {
        "type": "text"
      }
    }
  }
}
<ol><li><p>Reindexe os documentos usando o pipeline de ingestão recém-criado para gerar os embeddings de texto como o campo adicional <code>text_embedding.predicted_value</code> em cada documento:</p></li></ol>POST _reindex
{
  "source": {
    "index": "search-rodents"
  },
  "dest": {
    "index": "vector-search-rodents",
    "pipeline": "search-rodents-vector-embedding-pipeline"
  }
}
<p>Agora podemos usar a opção <code>Knn</code> na mesma API de pesquisa usando o novo índice <code>vector-search-rodents</code>, como mostrado no exemplo abaixo:</p>func VectorSearch(client *elasticsearch.TypedClient, term string) ([]Rodent, error) {
  var k = 10
	var numCandidates = 10

	res, err := client.Search().
		Index("vector-search-rodents").
		Knn(types.KnnSearch{
      # Field in document containing vector
			Field:         "text_embedding.predicted_value",
      # Number of neighbors to return
			K:             &amp;k,
      # Number of candidates to evaluate in comparison
			NumCandidates: &amp;numCandidates,
      # Generate query vector using the same model used in the inference processor
			QueryVectorBuilder: &amp;types.QueryVectorBuilder{
				TextEmbedding: &amp;types.TextEmbedding{
					ModelId:   "sentence-transformers__msmarco-minilm-l-12-v3",
					ModelText: term,
				},
			}}).Do(context.Background())

	if err != nil {
		return nil, fmt.Errorf("error in rodents vector search: %w", err)
	}

	return getRodents(res.Hits.Hits)
}
<p>A conversão do objeto JSON resultante por meio de desserialização é feita exatamente da mesma forma que no exemplo de pesquisa por palavra-chave. As constantes <code>K</code> e <code>NumCandidates</code> permitem configurar o número de documentos vizinhos a serem retornados e o número de candidatos a serem considerados por fragmento. Note que aumentar o número de candidatos aumenta a precisão dos resultados, mas leva a uma consulta mais demorada, pois mais comparações são realizadas.</p><p>Quando o código é executado usando a consulta <code>What do Gophers eat?</code>, os resultados retornados são semelhantes aos abaixo, destacando que o artigo do Gopher contém as informações solicitadas, diferentemente da pesquisa anterior por palavra-chave:</p>[
  {ID:64f74ecd4acb3df024d91112 Title:Gopher - Wikipedia Url:https://en.wikipedia.org/wiki/Gopher} 
  {ID:64f74ed34acb3d71aed91fcd Title:Squirrel - Wikipedia Url:https://en.wikipedia.org/wiki/Squirrel} 
  //Other results omitted
]
<h3>Abordagem 2: API de inferência do Hugging Face</h3><p>Outra opção é gerar esses mesmos embeddings fora do Elasticsearch e ingeri-los como parte do seu documento. Como essa opção não utiliza um nó de aprendizado de máquina do Elasticsearch, ela pode ser executada no nível gratuito.</p><p>A Hugging Face disponibiliza uma <a href="https://huggingface.co/docs/api-inference/index">API de inferência</a> gratuita e com limite de requisições que, mediante uma conta e um token de API, pode ser usada para gerar manualmente os mesmos embeddings para experimentação e prototipagem, ajudando você a começar. Não é recomendado para uso em produção. Invocar seus próprios modelos localmente para gerar embeddings ou usar a API paga também pode ser feito usando uma abordagem semelhante.</p><p>Na função abaixo <code>GetTextEmbeddingForQuery</code> usamos a API de inferência em nossa string de consulta para gerar o vetor retornado de uma solicitação <code>POST</code> ao endpoint:</p>// HuggingFace text embedding helper
func GetTextEmbeddingForQuery(term string) []float32 {
    // HTTP endpoint
    model := "sentence-transformers/msmarco-minilm-l-12-v3"
    posturl := fmt.Sprintf("https://api-inference.huggingface.co/pipeline/feature-extraction/%s", model)

    // JSON body
    body := []byte(fmt.Sprintf(`{
        "inputs": "%s",
        "options": {"wait_for_model":True}
    }`, term))

    // Create a HTTP post request
    r, err := http.NewRequest("POST", posturl, bytes.NewBuffer(body))

    if err != nil {
        log.Fatal(err)
        return nil
    }

    token := os.Getenv("HUGGING_FACE_TOKEN")
    r.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))

    client := &amp;http.Client{}
    res, err := client.Do(r)
    if err != nil {
        panic(err)
    }

    defer res.Body.Close()

    var post []float32
    derr := json.NewDecoder(res.Body).Decode(&amp;post)

    if derr != nil {
        log.Fatal(derr)
        return nil
    }

    return post
}
<p>O vetor resultante, do tipo <code>[]float32</code> é então passado como <code>QueryVector</code> em vez de usar a opção <code>QueryVectorBuilder</code> para aproveitar o modelo previamente carregado no Elastic.</p>func VectorSearchWithGeneratedQueryVector(client *elasticsearch.TypedClient, term string) ([]Rodent, error) {
	vector, err := GetTextEmbeddingForQuery(term)
	if err != nil {
		return nil, err
	}

	if vector == nil {
		return nil, fmt.Errorf("unable to generate vector: %w", err)
	}

  var k = 10
	var numCandidates = 10

	res, err := client.Search().
		Index("vector-search-rodents").
		Knn(types.KnnSearch{
      # Field in document containing vector
			Field:         "text_embedding.predicted_value",
      # Number of neighbors to return
			K:             &amp;k,
      # Number of candidates to evaluate in comparison
			NumCandidates: &amp;numCandidates,
      # Query vector returned from Hugging Face inference API
			QueryVector:   vector,
		}).
		Do(context.Background())

	if err != nil {
		return nil, err
	}

	return getRodents(res.Hits.Hits)
}
<p>Note que as opções <code>K</code> e <code>NumCandidates</code> permanecem as mesmas, independentemente das duas opções, e que os mesmos resultados são gerados, pois estamos usando o mesmo modelo para gerar os embeddings.</p><h2>Conclusão</h2><p>Aqui discutimos como realizar uma busca vetorial no Elasticsearch usando o <a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/index.html">cliente Elasticsearch Go</a>. Confira o <a href="https://github.com/carlyrichmond/gopher-hunting-elasticsearch">repositório do GitHub</a> para acessar todo o código desta série. Continue para a <a href="https://www.elastic.co/search-labs/blog/hybrid-search-with-the-elasticsearch-go-client">parte 3</a> para obter uma visão geral da combinação da pesquisa vetorial com os recursos de pesquisa por palavra-chave abordados na <a href="https://www.elastic.co/search-labs/blog/perform-text-queries-with-the-elasticsearch-go-client">parte 1</a> em Go.</p><p>Até lá, boa caçada aos esquilos!</p><h2>Recursos</h2><ol><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html">Guia do Elasticsearch</a></p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/index.html">Cliente Go do Elasticsearch</a></p></li><li><p><a href="https://www.elastic.co/what-is/vector-search">O que é busca vetorial? | Elástico</a></p></li></ol>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/perform-vector-search-with-the-elasticsearch-go-client</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/perform-vector-search-with-the-elasticsearch-go-client</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <category><![CDATA[Go]]></category>
    <dc:creator><![CDATA[Carly Richmond,Laurent Saint-Félix]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdcae959696d55a9b/6a1704d5ab7f085a56db9d7c/491ef9efbb30b253e1d9e3b7f816a9a23c8f5264-721x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 01 Nov 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Busca lexical e semântica com Elasticsearch]]></title>
    <description><![CDATA[Neste blog, exploraremos várias abordagens para recuperar informações usando o Elasticsearch, com foco em busca lexical e semântica.]]></description>
    <content:encoded><![CDATA[<p>A busca é o processo de localizar as informações mais relevantes com base na sua consulta de pesquisa ou em consultas combinadas, e os resultados de pesquisa relevantes são os documentos que melhor correspondem a essas consultas. Embora existam diversos desafios e métodos associados à busca, o objetivo final permanece o mesmo: <strong>encontrar a melhor resposta possível para sua pergunta</strong>.</p><p>Considerando esse objetivo, neste post do blog, exploraremos diferentes abordagens para recuperar informações usando o Elasticsearch, com foco específico em busca de texto: <strong>busca lexical e busca semântica.</strong></p><h2>Pré-requisitos</h2><p>Para isso, forneceremos exemplos em Python que demonstram vários cenários de pesquisa em um <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/lexical-and-semantic-search-with-elasticsearch/products-ecommerce.json">conjunto de dados</a> gerado para simular informações de produtos de comércio eletrônico.</p><p>Este conjunto de dados contém mais de 2.500 produtos, cada um com uma descrição. Esses produtos estão categorizados em 76 categorias distintas, cada uma contendo um número variável de produtos, conforme mostrado abaixo:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt34415151c00ced2b/6a17d8710b0bed6c9bdd342c/4104466050f3024b6bcaf382da2a702650f62227-1440x708.png" alt="" /><p><em>Visualização em mapa de árvore - os 22 principais valores de category.keyword (categorias de produtos)</em></p><p>Para a configuração, você precisará de:</p><ul><li><p>Python 3.6 ou posterior</p></li><li><p>O <a href="https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/index.html">cliente Elastic Python</a></p></li><li><p>Implantação do Elasticsearch 8.8 ou posterior, com nó de aprendizado de máquina de 8 GB de memória.</p></li><li><p>O modelo <a href="https://www.elastic.co/guide/en/machine-learning/8.9/ml-nlp-elser.html">Elastic Learned Sparse EncodeR</a> que vem pré-carregado no Elastic instalado e em execução na sua implementação.</p></li></ul><p>Usaremos o Elastic Cloud, e um <a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;cta=cloud-registration&amp;tech=trial&amp;plcmt=article%20content&amp;pg=search-labs">período de teste gratuito está disponível</a>.</p><p>Além das consultas de pesquisa fornecidas nesta postagem do blog, um <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/lexical-and-semantic-search-with-elasticsearch/ecommerce_dense_sparse_project.ipynb">notebook Python</a> irá guiá-lo pelos seguintes processos:</p><ul><li><p>Estabeleça uma conexão com nossa implantação Elastic usando o cliente Python.</p></li><li><p>Carregar um modelo de incorporação de texto no cluster Elasticsearch</p></li><li><p>Crie um índice com mapeamentos para indexar vetores de características e vetores densos.</p></li><li><p>Crie um pipeline de ingestão com processadores de inferência para incorporação e expansão de texto.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0e95406ece8f08d9/6a17d8731d1b83d32e93e2f4/54a9a490a3b0cf1b9c2228bee8eddd3f566bd435-1418x1102.png" alt="" /><h2>Busca lexical - recuperação esparsa</h2><p>A forma clássica como os documentos são classificados por relevância pelo Elasticsearch com base em uma consulta de texto utiliza a implementação Lucene do modelo <a href="https://en.wikipedia.org/wiki/Okapi_BM25">BM25</a> , um <strong>modelo esparso para busca lexical</strong>. Este método segue a abordagem tradicional para busca de texto, procurando por correspondências exatas dos termos.</p><p>Para tornar essa busca possível, o Elasticsearch converte os dados <strong>dos campos de texto</strong> em um formato pesquisável, realizando uma análise de texto.</p><p><strong>A análise de texto</strong> é realizada por um<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analyzer-anatomy.html"> analisador</a>, um conjunto de regras que regem o processo de extração de tokens relevantes para a pesquisa. Um analisador deve ter exatamente um<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-tokenizers.html"> tokenizador</a>. O analisador léxico recebe um fluxo de caracteres e o divide em tokens individuais (geralmente palavras individuais), como no exemplo abaixo:</p><h3>Tokenização de strings para busca lexical</h3>#Performs text analysis on a string and returns the resulting tokens.

# Define the text to be analyzed
text = "Comfortable furniture for a large balcony"

# Define the analyze request
request_body = {
  "analyzer": "standard",
  "text": text
}

# Perform the analyze request
response = client.indices.analyze(analyzer=request_body["analyzer"], text=request_body["text"])

# Extract and display the analyzed tokens
tokens = [token["token"] for token in response["tokens"]]
print("Analyzed Tokens:", tokens)
<p>Saída</p>Analyzed Tokens: ['comfortable', 'furniture', 'for', 'a', 'large', 'balcony']
<p>Neste exemplo, estamos usando o analisador <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-standard-analyzer.html">padrão</a> , que funciona bem para a maioria dos casos de uso, pois fornece tokenização baseada na gramática inglesa. A tokenização permite a correspondência de termos individuais, mas cada token ainda é correspondido literalmente.</p><p>Se você deseja personalizar sua experiência de busca, pode escolher um<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-analyzers.html"> analisador integrado</a> diferente. Por exemplo, ao atualizar o código para usar o <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-stop-analyzer.html">analisador de palavras irrelevantes,</a> o texto será dividido em tokens em qualquer caractere que não seja letra, com suporte para a remoção de palavras irrelevantes.</p>...
# Define the analyze request
request_body = {
  "analyzer": "stop",
  "text": text
}
...
<p>Saída</p>Analyzed Tokens: ['comfortable', 'furniture', 'large', 'balcony']
<p>Quando os analisadores integrados não atendem às suas necessidades, você pode criar um <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-custom-analyzer.html">analisador personalizado</a>, que utiliza a combinação apropriada de zero ou mais <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-charfilters.html">filtros de caracteres</a>, um <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-tokenizers.html">analisador léxico</a> e zero ou mais <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-tokenfilters.html">filtros de tokens</a>.</p>"analyzer":  {

  "my_analyzer": {

    "type": "custom", #For custom analyzers, use a type of custom or omit the type parameter.

    "tokenizer": "standard", #Built-in or customized tokenizer

    "filter": ["lowercase", "synonym"] #Built-in or customized token filters
  }
}
<p>No exemplo acima, que combina um analisador léxico e filtros de tokens, o texto será convertido para minúsculas pelo <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-lowercase-tokenfilter.html">filtro de minúsculas</a> antes de ser processado pelo <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-synonym-tokenfilter.html#:~:text=Elasticsearch%20will%20use%20the%20token,applied%20to%20the%20synonym%20entries.">filtro de tokens de sinônimos</a>.</p><h2>Correspondência lexical</h2><p><a href="https://www.elastic.co/blog/practical-bm25-part-2-the-bm25-algorithm-and-its-variables">O BM25</a> medirá a relevância dos documentos para uma determinada consulta de pesquisa com base na frequência dos termos e na sua importância.</p><p>O código abaixo realiza uma consulta <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html">de correspondência</a> , buscando até dois documentos considerando os valores do campo <em>"description"</em> do índice <em>"ecommerce-search"</em> e a consulta de pesquisa <strong>"</strong><em><strong>Móveis confortáveis para uma varanda grande</strong></em><strong>"</strong>.</p><p>Refinar os critérios para que um documento seja considerado uma correspondência para esta consulta pode melhorar a precisão. No entanto, resultados mais específicos têm como contrapartida uma menor tolerância a variações.</p># BM25

response = client.search(size=2,
index="ecommerce-search",
query= {
  "match": {
    "description" : {  
      "query": "Comfortable furniture for a large balcony",
      "analyzer": "stop"
    }
  }
}
)

hits = response['hits']['hits']

if not hits:
  print("No matches found")

else:
  for hit in hits:
    score = hit['_score']
    product = hit['_source']['product']
    category = hit['_source']['category']
    description = hit['_source']['description']
    print(f"\nScore: {score}\nProduct: {product}\nCategory: {category}\nDescription: {description}\n")
<p>Saída</p>Score: 15.607948
Product: Barbie Dreamhouse
Category: Toys
Description: is a classic Barbie playset with multiple rooms, furniture, a large balcony, a pool, and accessories. It allows kids to create their dream Barbie world.

Score: 9.137739
Product: Comfortable Rocking Chair
Category: Indoor Furniture
Description: enjoy relaxing moments with this comfortable rocking chair. Its smooth motion and cushioned seat make it an ideal piece of furniture for unwinding.
<p>Ao analisar os resultados, o mais relevante é o produto "<em>Casa dos Sonhos da Barbie</em>", na categoria "<em>Brinquedos</em>", cuja descrição é altamente relevante por incluir os termos "<em>móveis</em>", "<em>grande"</em> e <em>"varanda</em>". Este é o único produto com três termos na descrição que correspondem à consulta de pesquisa, sendo também o único com o termo <em>"varanda"</em> na descrição.</p><p>O segundo produto mais relevante é uma "<em>Cadeira de Balanço Confortável</em>", categorizada como "<em>Móveis para Interiores</em>", cuja descrição inclui os termos "<em>confortável</em>" e "<em>móveis</em>". Apenas 3 produtos no conjunto de dados correspondem a pelo menos 2 termos desta consulta de pesquisa, e este produto é um deles.</p><p><em>"Confortável"</em> aparece na descrição de 105 produtos e <em>"móveis"</em> na descrição de 4 produtos com 4 categorias diferentes: <em>Brinquedos</em>, <em>Móveis para Interior, Móveis para Exterior e 'Suprimentos e Brinquedos para Cães e Gatos'.</em></p><p>Como você pôde ver, o produto mais relevante considerando a consulta é um brinquedo e o segundo produto mais relevante são móveis para interiores. Se você deseja informações detalhadas sobre o cálculo da pontuação para saber por que esses documentos correspondem, pode definir o parâmetro <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-explain.html"><em>explain</em></a> __query como verdadeiro.</p><p>Apesar de ambos os resultados serem os mais relevantes, considerando tanto o número de documentos quanto a ocorrência de termos neste conjunto de dados, a intenção por trás da consulta "<em>Móveis confortáveis para uma varanda grande</em>" é buscar móveis para uma varanda grande de fato, excluindo, entre outros, brinquedos e móveis de interior.</p><p>A busca lexical é relativamente <strong>simples e rápida</strong>, mas tem limitações, já que nem sempre é possível conhecer todos os termos e sinônimos possíveis sem necessariamente conhecer a intenção e as consultas do usuário. Um fenômeno comum no uso da linguagem natural é <strong>a incompatibilidade de vocabulário</strong>. <a href="https://dl.acm.org/doi/abs/10.1145/32206.32212">Pesquisas</a> mostram que, em média, <strong>80% das vezes,</strong> pessoas diferentes (especialistas na mesma área) nomeiam a mesma coisa de maneiras diferentes.</p><p>Essas limitações nos motivam a buscar outros modelos de pontuação que incorporem conhecimento semântico. Os modelos baseados em Transformers, que se destacam no processamento de tokens de entrada sequenciais, como a linguagem natural, capturam o significado subjacente da sua pesquisa considerando representações matemáticas tanto dos documentos quanto das consultas. Isso permite uma representação vetorial densa e contextualizada do texto, impulsionando <strong>a Busca Semântica</strong>, uma maneira refinada de encontrar conteúdo relevante.</p><h2>Busca semântica - recuperação densa</h2><p>Nesse contexto, após converter seus dados em valores vetoriais significativos, o algoritmo de busca <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html">k-vizinhos mais próximos (kNN)</a> é utilizado para encontrar representações vetoriais em um conjunto de dados que sejam mais semelhantes a um vetor de consulta. O Elasticsearch suporta dois métodos para busca kNN: <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#exact-knn">kNN exato por força bruta</a> e <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#approximate-knn">kNN aproximado</a>, também conhecido como ANN.</p><p>O kNN de força bruta garante resultados precisos, mas não escala bem com grandes conjuntos de dados. O algoritmo kNN aproximado encontra de forma eficiente os vizinhos mais próximos aproximados, sacrificando um pouco de precisão em troca de um melhor desempenho.</p><p>Com o suporte do Lucene para busca kNN e índices vetoriais densos, o Elasticsearch aproveita o algoritmo Hierarchical Navigable Small World (HNSW), que demonstra um forte desempenho de busca em diversos conjuntos de <a href="http://ann-benchmarks.com/">dados de benchmark de redes neurais</a>. Uma busca aproximada por kNN pode ser realizada em Python usando o código de exemplo abaixo.</p><h3>Busca semântica com kNN aproximado</h3># KNN - approximate kNN

response = client.search(index='ecommerce-search', size=2,
knn={
  "field": "description_vector.predicted_value",
  "k": 50, # Number of nearest neighbors to return as top hits.
#The optimal value of k is dependent on the data. It can vary in different scenarios.

  "num_candidates": 500, # Number of nearest neighbor candidates to consider per shard.

#Increasing num_candidates tends to improve the accuracy of the final k results.

  "query_vector_builder": { # Object indicating how to build a query_vector. kNN search enables you to perform semantic search by using a previously deployed text embedding model, the steps for this process are demonstrated in the Python notebook.
    "text_embedding": { 
      "model_id": "sentence-transformers__all-mpnet-base-v2", # Text embedding model id
      "model_text": "Comfortable furniture for a large balcony" # Query
    }
  }
}
)

for hit in response['hits']['hits']:
        
  score = hit['_score']
  product = hit['_source']['product']
  category = hit['_source']['category']
  description = hit['_source']['description']
  print(f"\nScore: {score}\nProduct: {product}\nCategory: {category}\nDescription: {description}\n")
<p>Este bloco de código usa o kNN do Elasticsearch para retornar até dois produtos com uma descrição semelhante à consulta vetorizada (query_vector_build) de "<em>Móveis confortáveis para uma varanda grande</em>", considerando os embeddings do campo "<em>descrição</em> " no conjunto de dados de produtos.</p><p>Os embeddings dos produtos foram gerados previamente em um pipeline de ingestão com um processador de inferência contendo o modelo de embedding de texto <em>"</em><a href="https://huggingface.co/sentence-transformers/all-mpnet-base-v2"><em>all-mpnet-base-v2</em></a><em>"</em> para inferir com base nos dados que estavam sendo ingeridos no pipeline.</p><p>Este modelo foi escolhido com base na avaliação de modelos pré-treinados usando <em>"</em><a href="https://github.com/UKPLab/sentence-transformers/blob/master/docs/package_reference/sentence_transformer/evaluation.md"><em>sentence_transformers.evaluation</em></a><em>".</em> onde diferentes classes são usadas para avaliar um modelo durante o treinamento. O modelo "all-mpnet-base-v2" demonstrou o melhor desempenho médio de acordo com o <a href="https://www.sbert.net/docs/pretrained_models.html">ranking do Sentence-Transformers</a> e também garantiu uma posição favorável no ranking do <a href="https://huggingface.co/spaces/mteb/leaderboard">Massive Text Embedding Benchmark (MTEB)</a> . O modelo, pré-treinado a partir do modelo<a href="https://huggingface.co/microsoft/mpnet-base"> microsoft/mpnet-base</a> e ajustado em um conjunto de dados de 1 bilhão de pares de frases, mapeia as frases para um espaço vetorial denso de 768 dimensões.</p><p>Alternativamente, existem muitos outros modelos disponíveis que podem ser utilizados, especialmente aqueles ajustados especificamente para os dados do seu domínio.</p><p>Saída</p>Score: 0.79207325
Product: Patio Sofa Set with Ottoman
Category: Outdoor Furniture
Description: is a versatile and comfortable patio sofa set, including a sofa, ottoman, and coffee table, great for outdoor lounging.

Score: 0.7836937
Product: Patio Sofa Set with Canopy
Category: Outdoor Furniture
Description: is a luxurious and comfortable patio sofa set with a canopy, providing shade and style for outdoor lounging.
<p><em>O resultado pode variar dependendo do modelo escolhido,</em> <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#knn-search-filter-example"><em>dos filtros</em></a> <em>e</em> <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#tune-approximate-knn-for-speed-accuracy"><em>do ajuste aproximado do kNN</em></a><em>.</em></p><p>Os resultados da pesquisa kNN estão ambos na categoria "<em>Móveis para Exterior</em>", embora a palavra "<em>exterior</em>" não tenha sido mencionada explicitamente na consulta, o que destaca a importância da compreensão semântica no contexto.</p><p>A busca vetorial densa oferece diversas vantagens:</p><ul><li><p>Habilitar a pesquisa semântica</p></li><li><p>Escalabilidade para lidar com conjuntos de dados muito grandes</p></li><li><p>Flexibilidade para lidar com uma ampla variedade de tipos de dados.</p></li></ul><p>No entanto, <strong>a busca vetorial densa também apresenta seus próprios desafios</strong>:</p><ul><li><p>Selecionando o modelo de incorporação correto para o seu caso de uso.</p></li><li><p>Uma vez escolhido o modelo, pode ser necessário ajustá-lo para otimizar o desempenho em um conjunto de dados específico do domínio, um processo que exige o envolvimento de especialistas na área.</p></li><li><p>Além disso, a indexação de vetores de alta dimensão pode ser computacionalmente dispendiosa.</p></li></ul><h2>Busca semântica - recuperação esparsa aprendida</h2><p>Vamos explorar uma abordagem alternativa: recuperação esparsa aprendida, outra maneira de realizar buscas semânticas.</p><p>Como um modelo esparso, ele utiliza o índice invertido baseado em Lucene do Elasticsearch, que se beneficia de décadas de otimizações. No entanto, essa abordagem vai além da simples adição de sinônimos com funções de pontuação lexical como o BM25. Em vez disso, incorpora associações aprendidas usando um conhecimento mais profundo em escala linguística para otimizar a relevância.</p><p>Ao expandir as consultas de pesquisa para incluir termos relevantes que não estão presentes na consulta original, o <a href="https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-elser.html">Elastic Learned Sparse Encoder</a> <strong>aprimora os embeddings de vetores esparsos</strong>, como você pode ver no exemplo abaixo.</p><h3>Busca vetorial esparsa com codificador esparso de aprendizado elástico</h3># Elastic Learned Sparse Encoder

response = client.search(index='ecommerce-search', size=2,
query={
  "text_expansion": {
    "ml.tokens": {
      "model_id":"elser_model",
      "model_text":"Comfortable furniture for a large balcony"                
    }
  }
}
)

for hit in response['hits']['hits']:

  score = hit['_score']
  product = hit['_source']['product']
  category = hit['_source']['category']
  description = hit['_source']['description']
  print(f"\nScore: {score}\nProduct: {product}\nCategory: {category}\nDescription: {description}\n")
<p>Saída</p>Score: 14.405318
Product: Garden Lounge Set with Side Table
Category: Garden Furniture
Description: is a comfortable and stylish garden lounge set, including a sofa, chairs, and a side table for outdoor relaxation.

Score: 14.281318
Product: Rattan Patio Conversation Set
Category: Outdoor Furniture
Description: is a stylish and comfortable outdoor furniture set, including a sofa, two chairs, and a coffee table, all made of durable rattan material.
<p>Os resultados, neste caso, incluem a categoria "<em>Móveis de Jardim</em>", que oferece produtos bastante semelhantes aos de "<em>Móveis para Exterior</em>".</p><p>Ao analisar "ml.tokens", Ao analisar o campo "rank_features" que contém os tokens gerados pela Recuperação Esparsa Aprendida, torna-se evidente que, entre os vários tokens gerados, existem termos que, embora não façam parte da consulta de pesquisa, ainda são relevantes em significado, como "<em>relaxar</em>" (confortável), "<em>sofá</em>" (móveis) e "<em>exterior</em>" (varanda).</p><p>A imagem abaixo destaca alguns desses termos juntamente com a consulta, tanto com quanto sem expansão de termos.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7e869985347b19a7/6a17d875e31791dc2c2d56a1/dd86607fce6137d843a3ec002390eaa988b432f9-1440x502.png" alt="" /><p>Conforme observado, este modelo proporciona uma busca sensível ao contexto e ajuda a mitigar o problema de incompatibilidade de vocabulário, ao mesmo tempo que oferece resultados mais interpretáveis. Ele pode até superar modelos vetoriais densos quando nenhum re-treinamento específico do domínio é aplicado.</p><h2>Busca híbrida: resultados relevantes combinando busca lexical e semântica.</h2><p>Quando se trata de pesquisa, não existe uma solução universal. Cada um desses métodos de recuperação tem seus pontos fortes, mas também seus desafios. Dependendo do caso de uso, a melhor opção pode mudar. Muitas vezes, os melhores resultados obtidos com diferentes métodos de recuperação de informação podem ser complementares. Portanto, para melhorar a relevância, vamos analisar a possibilidade de combinar os pontos fortes de cada método.</p><p>Existem várias maneiras de implementar uma <strong>busca híbrida</strong>, incluindo combinação linear, atribuição de um peso a cada pontuação e fusão de classificação recíproca (RRF), onde especificar um peso não é necessário.</p><h3>Elasticsearch: o melhor dos dois mundos com busca lexical e semântica</h3># BM25 + Elastic Learned Sparse Encoder (Linear Combination)

response = client.search(index='ecommerce-search', size=2,

query= {
  "bool": {
    "should": [
    {
      "match": {
        "description" : {  
          "query": "A dining table and comfortable chairs for a large balcony",
          "boost": 1
        }
      }
    },                   
    {
      "text_expansion": {
        "ml.tokens": {
          "model_id": "elser_model",
          "model_text": "A dining table and comfortable chairs for a large balcony",
          "boost": 1
        }
      }
     }
    ]
  }
}
)

# The boost value is 1 for the text expansion and match query. This means that the relevance score of the results of these queries are not boosted. You can specify a boost value to give a weight to each score in the sum. The scores will be calculated as: score = boost value * match_score + boost value * text_expansion_score

for hit in response['hits']['hits']:

  score = hit['_score']
  product = hit['_source']['product']
  category = hit['_source']['category']
  description = hit['_source']['description']
  print(f"\nScore: {score}\nProduct: {product}\nCategory: {category}\nDescription: {description}\n")
<p>Neste código, realizamos uma busca híbrida com duas consultas que tinham o valor "<em>Uma mesa de jantar e cadeiras confortáveis para uma varanda grande</em>". Em vez de usar "<em>móveis</em>" como termo de busca, estamos especificando o que procuramos, e ambas as buscas consideram os mesmos valores de campo, "descrição". A classificação é determinada por uma combinação linear com pesos iguais para as pontuações BM25 e ELSER.</p><p>Saída</p>Score: 31.628141
Product: Garden Dining Set with Swivel Rockers
Category: Garden Furniture
Description: is a functional and comfortable garden dining set, including a table and chairs with swivel rockers for easy movement.

Score: 31.334227
Product: Garden Dining Set with Swivel Chairs
Category: Garden Furniture
Description: is a functional and comfortable garden dining set, including a table and chairs with swivel seats for convenience.
<p>No código abaixo, usaremos o mesmo valor para a consulta, mas combinaremos as pontuações de BM25 (parâmetro de consulta) e kNN (parâmetro knn) usando o método de fusão de classificação recíproca para combinar e classificar os documentos.</p># BM25 + KNN (RRF)

response = client.search(index='ecommerce-search', size=2,
query={
  "bool": {
    "should": [
    {
      "match": {
        "description": {
        "query": "A dining table and comfortable chairs for a large balcony"
        }
      }
    }
    ]
  }
},
knn={
  "field": "description_vector.predicted_value",
  "k": 50,
  "num_candidates": 500,
  "query_vector_builder": {
    "text_embedding": {
      "model_id": "sentence-transformers__all-mpnet-base-v2",
      "model_text": "A dining table and comfortable chairs for a large balcony"
    }
  }
},
rank={
  "rrf": { # Reciprocal rank fusion
    "window_size": 50, # This value determines the size of the individual result sets per query.
    "rank_constant": 20 # This value determines how much influence documents in individual result sets per query have over the final ranked result set.
  }
}
)

for hit in response['hits']['hits']:
        
  rank = hit['_rank']
  category = hit['_source']['category']
  product = hit['_source']['product']
  description = hit['_source']['description']
  print(f"\nRank: {rank}\nProduct: {product}\nCategory: {category}\nDescription: {description}\n")
<p><em>A funcionalidade RRF está em fase de pré-visualização técnica. A sintaxe provavelmente mudará antes do lançamento oficial.</em></p><p>Saída</p>Rank: 1
Product: Patio Dining Set with Bench
Category: Outdoor Furniture
Description: is a spacious and functional patio dining set, including a dining table, chairs, and a bench for additional seating.

Rank: 2
Product: Garden Dining Set with Swivel Chairs
Category: Garden Furniture
Description: is a functional and comfortable garden dining set, including a table and chairs with swivel seats for convenience.
<p>Aqui também poderíamos usar campos e valores diferentes; alguns desses exemplos estão disponíveis no <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/lexical-and-semantic-search-with-elasticsearch/ecommerce_dense_sparse_project.ipynb">notebook Python</a>.</p><p>Como você pode ver, com o Elasticsearch você tem o melhor dos dois mundos: a busca lexical tradicional e a busca vetorial, seja ela esparsa ou densa, para atingir seu objetivo <strong>e encontrar a melhor resposta possível para sua pergunta.</strong></p><p>Se você deseja continuar aprendendo sobre as abordagens mencionadas aqui, estes blogs podem ser úteis:</p><ul><li><p><a href="https://www.elastic.co/blog/improving-information-retrieval-elastic-stack-hybrid">Aprimorando a recuperação de informações no Elastic Stack: Recuperação híbrida</a></p></li><li><p><a href="https://www.elastic.co/blog/vector-search-elasticsearch-rationale">Busca vetorial no Elasticsearch: a lógica por trás do design</a></p></li><li><p><a href="https://www.elastic.co/blog/lexical-ai-powered-search-elastic-vector-database">Como obter o melhor da busca lexical e da busca com inteligência artificial usando o banco de dados vetorial da Elastic.</a></p></li><li><p><a href="https://www.elastic.co/blog/may-2023-launch-sparse-encoder-ai-model">Apresentando o Elastic Learned Sparse Encoder: o modelo de IA da Elastic para busca semântica.</a></p></li><li><p><a href="https://www.elastic.co/blog/may-2023-launch-information-retrieval-elasticsearch-ai-model">Aprimorando a recuperação de informações no Elastic Stack: Apresentando o Elastic Learned Sparse Encoder, nosso novo modelo de recuperação.</a></p></li></ul><p>O Elasticsearch fornece um banco de dados vetorial, juntamente com todas as ferramentas necessárias para criar pesquisas vetoriais:</p><ul><li><p><a href="https://www.elastic.co/elasticsearch/vector-database">banco de dados vetorial</a>Elasticsearch</p></li><li><p>Casos de uso <a href="https://www.elastic.co/enterprise-search/vector-search">de busca vetorial</a> com Elastic</p></li></ul><h2>Conclusão</h2><p>Neste artigo, exploramos diversas abordagens para recuperar informações usando o Elasticsearch, com foco específico em busca textual, lexical e semântica. Para demonstrar isso, fornecemos exemplos em Python que mostram diferentes cenários de pesquisa usando um conjunto de dados contendo informações de produtos de comércio eletrônico.</p><p>Analisamos a busca lexical clássica com BM25 e discutimos seus benefícios e desafios, como a incompatibilidade de vocabulário. Enfatizamos a importância de incorporar conhecimento semântico para superar esse problema. Além disso, discutimos a busca vetorial densa, que possibilita a busca semântica, e abordamos os desafios associados a esse método de recuperação, incluindo o custo computacional na indexação de vetores de alta dimensão.</p><p>Por outro lado, mencionamos que vetores esparsos se comprimem excepcionalmente bem. Assim, discutimos o Learned Sparse Encoder da Elastic, que expande as consultas de pesquisa para incluir termos relevantes não presentes na consulta original.</p><p>Não existe uma solução única que sirva para todos quando se trata de pesquisa. Cada método de recuperação tem seus pontos fortes e desafios. Portanto, também discutimos o conceito de busca híbrida.</p><p>Como você pôde ver, com o Elasticsearch, você pode ter o melhor dos dois mundos: busca lexical tradicional e busca vetorial!</p><p>Pronto para começar? Confira o <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/lexical-and-semantic-search-with-elasticsearch/ecommerce_dense_sparse_project.ipynb">notebook Python</a> disponível e inicie um <a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;cta=cloud-registration&amp;tech=trial&amp;plcmt=article%20content&amp;pg=search-labs">teste gratuito do Elastic Cloud</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/lexical-and-semantic-search-with-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/lexical-and-semantic-search-with-elasticsearch</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <category><![CDATA[Python]]></category>
    <category><![CDATA[Linguagens de consulta]]></category>
    <dc:creator><![CDATA[Priscilla Parodi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbd7e961f594005e7/6a17d80f033c8d981f6bb009/d240bfef29e9d432069059b312dd044eb76eec6c-1440x840.png" length="0" type="image/png"/>
    <pubDate>Tue, 03 Oct 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Aprimorando as capacidades de chatbots com PNL e busca vetorial no Elasticsearch.]]></title>
    <description><![CDATA[Descubra como a busca vetorial e o PNL (Processamento de Linguagem Natural) trabalham juntos para aprimorar os recursos dos chatbots e veja como o Elasticsearch facilita esse processo.]]></description>
    <content:encoded><![CDATA[<p>Interfaces conversacionais existem há algum tempo e estão se tornando cada vez mais populares como forma de auxiliar em diversas tarefas, como atendimento ao cliente, recuperação de informações e automação de tarefas. Normalmente acessadas por meio de assistentes de voz ou aplicativos de mensagens, essas interfaces simulam a conversa humana para ajudar os usuários a resolver suas dúvidas com mais eficiência.</p><p>Com o avanço da tecnologia, os chatbots são usados para lidar com tarefas mais complexas — e rapidamente — sem deixar de oferecer uma experiência personalizada aos usuários. O processamento de linguagem natural (PLN) permite que os chatbots processem a linguagem do usuário, identifiquem a intenção por trás da mensagem e extraiam informações relevantes dela. Por exemplo, o Reconhecimento de Entidades Nomeadas extrai informações importantes de um texto, classificando-as em um conjunto de categorias. A Análise de Sentimentos identifica o tom emocional, e o Question Answering (Resposta a Perguntas) a “resposta” a uma consulta. O objetivo do PNL (Processamento de Linguagem Natural) é permitir que algoritmos processem a linguagem humana e executem tarefas que historicamente apenas os humanos eram capazes de realizar, como encontrar trechos relevantes em grandes quantidades de texto, resumir textos e gerar conteúdo novo e original.</p><p>Essas capacidades avançadas de PNL (Processamento de Linguagem Natural) são baseadas em uma tecnologia conhecida como <a href="https://www.elastic.co/what-is/vector-search">busca vetorial</a>. O Elasticsearch oferece suporte nativo para busca vetorial, realizando <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#knn-search">buscas exatas e aproximadas de k-vizinhos mais próximos (kNN)</a>, e para PNL (Processamento de Linguagem Natural), permitindo o uso de <a href="https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-model-ref.html#ml-nlp-model-ref">modelos personalizados ou de terceiros</a> diretamente no Elasticsearch.</p><p>Neste artigo, exploraremos como a busca vetorial e o PNL (Processamento de Linguagem Natural) trabalham juntos para aprimorar as capacidades dos chatbots e demonstraremos como o Elasticsearch facilita esse processo. Vamos começar com uma breve visão geral da busca vetorial.</p><h2>Busca vetorial</h2><p>Embora os seres humanos consigam compreender o significado e o contexto da linguagem escrita, as máquinas não conseguem fazer o mesmo. É aí que entram os vetores. Ao converter o texto em representações vetoriais (representações numéricas do significado do texto), as máquinas podem superar essa limitação. Em comparação com uma busca tradicional, em vez de depender de palavras-chave e busca lexical baseada em frequências, os vetores permitem o processamento de dados textuais usando operações definidas para valores numéricos.</p><p>Isso permite que a busca vetorial localize dados que compartilham conceitos ou contextos semelhantes, usando distâncias no "espaço de incorporação" para representar a similaridade, dado um vetor de consulta. Quando os dados são semelhantes, os vetores correspondentes também serão semelhantes.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt53a615a8ba0ac931/6a17d795fbc5f8b257491910/08542abf8108aace288745b1aca8579b476ddc1b-1440x618.png" alt="" /><p>A busca vetorial não é utilizada apenas em aplicações de PNL (Processamento de Linguagem Natural), mas também em vários outros domínios que envolvem dados não estruturados, incluindo processamento de imagem e vídeo.</p><p>Em um fluxo de chatbot, podem existir diversas abordagens para as consultas dos usuários e, consequentemente, diferentes maneiras de aprimorar a recuperação de informações para uma melhor experiência do usuário. Como cada alternativa possui seu próprio conjunto de vantagens e possíveis desvantagens, é essencial levar em consideração os dados e recursos disponíveis, bem como o tempo de treinamento (quando aplicável) e a precisão esperada. Na seção seguinte, abordaremos esses aspectos para modelos de PNL (Processamento de Linguagem Natural) de resposta a perguntas.</p><h2>Respostas a perguntas</h2><p>Um modelo de perguntas e respostas (QA, na sigla em inglês) é um tipo de modelo de PNL (Processamento de Linguagem Natural) projetado para responder a perguntas feitas em linguagem natural. Quando os usuários têm dúvidas que exigem a inferência de respostas a partir de múltiplas fontes, sem que haja uma resposta preexistente nos documentos, os modelos generativos de perguntas e respostas podem ser úteis. No entanto, esses modelos podem ser computacionalmente dispendiosos e exigem grandes quantidades de dados para treinamento relacionado ao domínio, o que pode torná-los menos práticos em algumas situações, embora esse método possa ser particularmente valioso para lidar com questões fora do domínio.</p><p>Por outro lado, quando os usuários têm dúvidas sobre um tópico específico e a resposta correta está presente no documento, podem ser utilizados modelos de perguntas e respostas extrativas. Esses modelos extraem a resposta diretamente do documento original, fornecendo resultados transparentes e verificáveis, o que os torna uma opção mais prática para empresas ou organizações que desejam oferecer uma maneira simples e eficiente de responder a perguntas.</p><p>O exemplo abaixo demonstra o uso de um modelo de perguntas e respostas extrativo pré-treinado, <a href="https://huggingface.co/deepset/minilm-uncased-squad2">disponível no Hugging Face</a> e implantado no Elasticsearch, para extrair respostas de um determinado contexto:</p>POST _ml/trained_models/deepset__minilm-uncased-squad2/deployment/_infer
{
    "docs": [{"text_field": "Canvas is a data visualization and presentation application within Kibana. With Canvas, live data can be pulled directly from Elasticsearch and combined with colors, images, text, and other customized options to create dynamic, multi-page displays."}],
    "inference_config": {"question_answering": {"question": "What is Kibana Canvas?"}}
}


{
  "predicted_value": "a data visualization and presentation application",
  "start_offset": 10,
  "end_offset": 59,
  "prediction_probability": 0.28304219431376443
}
<p><a href="https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-deploy-models.html">Implantar modelos treinados.</a></p><p><a href="https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-ner-example.html#ex-ner-ingest">Adicionar um modelo a um pipeline de ingestão de inferência.</a></p><p>Existem diversas maneiras de lidar com consultas de usuários e recuperar informações, e o uso de múltiplos modelos de linguagem e fontes de dados pode ser uma alternativa eficaz ao lidar com dados não estruturados. Para ilustrar isso, temos um exemplo do processamento de dados de um chatbot utilizado para responder a perguntas com respostas que consideram dados extraídos de documentos selecionados.</p><h2>Processamento de dados de chatbots: PNL e busca vetorial</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta418a9c54bb16cf9/6a17d7975772624b371bca43/c2d1a2f110e937b1d3e5df0d5caac3c906c98fb0-1440x748.png" alt="" /><p>Conforme mostrado acima, o processamento de dados do nosso chatbot pode ser dividido em três partes:</p><ul><li><p><strong>Processamento vetorial:</strong> Esta etapa converte documentos em representações vetoriais.</p></li><li><p><strong>Processamento da entrada do usuário:</strong> Esta etapa extrai informações relevantes da consulta do usuário e realiza busca semântica e recuperação híbrida.</p></li><li><p><strong>Otimização:</strong> Esta etapa inclui o monitoramento e é crucial para garantir a confiabilidade do chatbot, o desempenho ideal e uma ótima experiência do usuário.</p></li></ul><h2>Processamento vetorial</h2><p>Na etapa <strong>de processamento</strong> , o primeiro passo é determinar os componentes de cada documento para, em seguida, converter cada elemento em uma representação vetorial; essas representações podem ser criadas para uma ampla variedade de formatos de dados.</p><p>Existem vários métodos que podem ser usados para calcular embeddings, incluindo modelos pré-treinados e bibliotecas.</p><p>É importante notar que a eficácia da busca e recuperação nessas representações depende dos dados existentes e da qualidade e relevância do método utilizado.</p><p>À medida que os vetores são calculados, eles são armazenados no Elasticsearch com um tipo de campo <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/dense-vector.html">dense_vector</a> .</p>PUT &lt;target&gt;
{
  "mappings": {
    "properties": {
      "doc_part_vector": {
        "type": "dense_vector",
        "dims": 3
      },
      "doc_part" : {
        "type" : "keyword"
      }
    }
  }
}
<h2>Processamento de entrada do usuário do chatbot</h2><p>Do ponto de vista <strong>do usuário</strong> , após receber uma pergunta, é útil extrair todas as informações possíveis dela antes de prosseguir. Isso ajuda a entender a intenção do usuário e, neste caso, estamos usando um <a href="https://huggingface.co/dslim/bert-base-NER">modelo de Reconhecimento de Entidades Nomeadas (NER)</a> para auxiliar nesse processo. NER é o processo de identificar e classificar entidades nomeadas em categorias de entidades predefinidas.</p>POST _ml/trained_models/dslim__bert-base-ner/deployment/_infer
{
  "docs": { "text_field": "How many people work for Elastic?"}
}


{
  "predicted_value": "How many people work for [Elastic](ORG&amp;Elastic)?",
  "entities": [
    {
      "entity": "Elastic",
      "class_name": "ORG",
      "class_probability": 0.4993975435876747,
      "start_pos": 25,
      "end_pos": 32
    }
  ]
}
<p>Embora não seja um passo necessário, ao usar dados estruturados ou o resultado do modelo de PNL acima ou de outro modelo para categorizar a consulta do usuário, podemos restringir a pesquisa kNN usando um <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#knn-search-filter-example">filtro</a>. Isso ajuda a melhorar o desempenho e a precisão, reduzindo a quantidade de dados que precisam ser processados.</p>    "filter": {
      "term": {
        "org": "Elastic"
      }
    }
<h2>Busca semântica e recuperação híbrida</h2><p>Como a solicitação surge de consultas do usuário e o chatbot precisa processar a linguagem humana com sua variabilidade e ambiguidade, <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#semantic-search">a busca semântica</a> é uma ótima opção. No Elasticsearch, você pode realizar uma busca semântica em uma única etapa, passando a string de consulta e o ID do <a href="https://huggingface.co/sentence-transformers/msmarco-MiniLM-L-12-v3">modelo de incorporação</a> para um objeto `query_vector_builder`. Isso vetorizará a consulta e realizará <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html">uma busca</a> kNN para recuperar as k correspondências mais próximas em significado à consulta:</p>POST /&lt;target&gt;/_search
{
  "knn": {
    "field": "doc_part_vector",
    "k": 5,
    "num_candidates": 20,
    "query_vector_builder": {
      "text_embedding": {
        "model_id": "&lt;text-embedding-model-id&gt;",
        "model_text": "&lt;query_string&gt;"
      }
    }
  }
 }
<p><a href="https://www.elastic.co/guide/en/machine-learning/8.7/ml-nlp-text-emb-vector-search-example.html">Exemplo completo: Como implantar um modelo de incorporação de texto e usá-lo para pesquisa semântica.</a> O Elasticsearch utiliza a implementação Lucene do Okapi BM25, um <strong>modelo esparso</strong> , para classificar as consultas de texto quanto à relevância, enquanto <strong>modelos densos</strong> são usados para <strong>busca semântica</strong>. Para <strong>combinar</strong> os <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#_combine_approximate_knn_with_other_features"><strong>pontos fortes das</strong></a> correspondências vetoriais <strong>e</strong> das correspondências obtidas a partir da consulta de texto, você pode realizar uma <strong>recuperação híbrida</strong> :</p>POST &lt;target&gt;/_search
{
  "query": {
          "match": {
            "content": {
              "query": "&lt;query_string&gt;"
            }
        }
  },
  "knn": {
    "field": "doc_part_vector",
    "query_vector_builder": {
      "text_embedding": {
    "model_id": "&lt;text-embedding-model-id&gt;",
     "model_text": "&lt;query_string&gt;"
      }
    },
    "filter": {
      "term": {
        "org": "Elastic"
      }
    }
  }
}
<h3>A combinação de modelos esparsos e densos geralmente produz os melhores resultados.</h3><p>Os modelos esparsos geralmente têm melhor desempenho em consultas curtas e terminologias específicas, enquanto os modelos densos aproveitam o contexto e as associações. Se você quiser saber mais sobre como esses métodos se comparam e se complementam, aqui comparamos o BM25 com dois modelos densos que foram treinados especificamente para recuperação de dados.</p><p>O resultado mais relevante geralmente é a primeira resposta fornecida ao usuário; o_score é um número usado para determinar a <strong>relevância</strong> do documento retornado.</p><h2>Otimização de chatbot</h2><p>Para ajudar a melhorar a experiência do usuário, o desempenho e a confiabilidade do seu chatbot, além de aplicar a pontuação híbrida, você pode incorporar as seguintes abordagens: <strong>Análise de Sentimento:</strong> Para fornecer informações sobre os comentários e reações dos usuários à medida que o diálogo se desenrola, você pode incorporar um <a href="https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english">modelo de análise de sentimento</a>:</p>POST _ml/trained_models/distilbert-base-uncased-finetuned-sst-2-english/deployment/_infer
{
  "docs": { "text_field": "That was not my question!"}
}


{
  "predicted_value": "NEGATIVE",
  "prediction_probability": 0.980080439016437
}
<p><a href="https://www.elastic.co/blog/chatgpt-elasticsearch-openai-meets-private-data"><strong>Funcionalidades do GPT</strong></a> <strong>:</strong> Como alternativa para aprimorar a experiência geral, você pode combinar a relevância de pesquisa do Elasticsearch com os recursos de resposta a perguntas do GPT da OpenAI, utilizando a <a href="https://platform.openai.com/docs/guides/chat">API Chat Completion</a> para retornar ao usuário respostas geradas pelo modelo, considerando esses k documentos principais como contexto. <em>Prompt: "responda a esta pergunta &lt;user_question&gt; usando apenas este documento &lt;top_search_result&gt;"</em></p><p><strong>Observabilidade:</strong> Garantir o desempenho de qualquer chatbot é crucial, e o monitoramento é um componente essencial para atingir esse objetivo. Além dos registros que capturam as interações do chatbot, é importante monitorar o tempo de resposta, a latência e outras métricas relevantes do chatbot. Dessa forma, você pode identificar padrões, tendências e até mesmo detectar anomalias. As ferramentas <a href="https://www.elastic.co/blog/monitor-openai-api-gpt-models-opentelemetry-elastic">do Elastic Observability</a> permitem coletar e analisar essas informações.</p><h2>Resumo</h2><p>Esta postagem do blog aborda o que são PNL (Processamento de Linguagem Natural) e busca vetorial e explora um exemplo de um chatbot usado para responder a perguntas de usuários, considerando dados extraídos da representação vetorial de documentos.</p><p>Conforme demonstrado, utilizando PNL (Processamento de Linguagem Natural) e busca vetorial, os chatbots são capazes de executar tarefas complexas que vão além de dados estruturados e direcionados. Isso inclui fazer recomendações e responder a perguntas específicas relacionadas a produtos ou negócios, usando múltiplas fontes e formatos de dados como contexto, além de proporcionar uma experiência personalizada ao usuário.</p><p>Os casos de uso variam desde o atendimento ao cliente, auxiliando os clientes com suas dúvidas, até o auxílio a desenvolvedores com suas perguntas, fornecendo orientações passo a passo, sugerindo recomendações ou até mesmo automatizando tarefas. Dependendo do objetivo e dos dados existentes, outros modelos e métodos também podem ser utilizados para alcançar resultados ainda melhores e aprimorar a experiência geral do usuário.</p><p>Aqui estão alguns links sobre o assunto que podem ser úteis:</p><ol><li><p><a href="https://www.elastic.co/blog/how-to-deploy-natural-language-processing-nlp-getting-started">Como implementar o processamento de linguagem natural (PLN): Primeiros passos</a></p></li><li><p><a href="https://www.elastic.co/blog/overview-image-similarity-search-in-elastic">Visão geral da busca por similaridade de imagens no Elasticsearch</a></p></li><li><p><a href="https://www.elastic.co/blog/chatgpt-elasticsearch-openai-meets-private-data">ChatGPT e Elasticsearch: OpenAI encontra dados privados</a></p></li><li><p><a href="https://www.elastic.co/blog/monitor-openai-api-gpt-models-opentelemetry-elastic">Monitor OpenAI API and GPT models with OpenTelemetry and Elastic (Monitore a API do OpenAI e os modelos do GPT com o OpenTelemetry e a Elastic)</a></p></li><li><p><a href="https://www.elastic.co/blog/why-technology-leaders-need-vector-search">5 motivos pelos quais os líderes de TI precisam da busca vetorial para melhorar a experiência de busca.</a></p></li></ol><p>Ao incorporar PNL (Processamento de Linguagem Natural) e busca vetorial nativa no Elasticsearch, você pode aproveitar sua velocidade, escalabilidade e recursos de busca para criar chatbots altamente eficientes e eficazes, capazes de lidar com grandes quantidades de dados, sejam eles estruturados ou não estruturados.</p><p>Pronto para começar? Inicie um <a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;cta=cloud-registration&amp;tech=trial&amp;plcmt=article%20content&amp;pg=search-labs">teste gratuito do Elastic Cloud</a>.</p><p><em>Neste artigo, podemos ter usado ou podemos fazer referência a ferramentas de IA generativa de terceiros, que são propriedade e operadas por seus respectivos proprietários. A Elastic não tem qualquer controle sobre as ferramentas de terceiros e não nos responsabilizamos pelo seu conteúdo, funcionamento ou utilização, nem por quaisquer perdas ou danos que possam resultar da sua utilização. Tenha cautela ao usar ferramentas de IA com informações pessoais, sensíveis ou confidenciais. Quaisquer dados que você enviar poderão ser usados para treinamento de IA ou outros fins. Não há garantia de que as informações fornecidas serão mantidas em segurança ou confidenciais. Você deve se familiarizar com as práticas de privacidade e os termos de uso de quaisquer ferramentas de IA generativa antes de utilizá-las.</em></p><p><em>Elastic, Elasticsearch e marcas associadas são marcas comerciais, logotipos ou marcas registradas da Elasticsearch NV nos Estados Unidos e em outros países. Todos os outros nomes de empresas e produtos são marcas comerciais, logotipos ou marcas registradas de seus respectivos proprietários.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/enhancing-chatbot-capabilities-with-nlp-and-vector-search-in-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/enhancing-chatbot-capabilities-with-nlp-and-vector-search-in-elasticsearch</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <dc:creator><![CDATA[Priscilla Parodi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5c545fc80b6d79d6/6a170214839dfad776dcfd6f/d968e646240cd3ef7c79b5124d562a5f951d812b-1440x840.png" length="0" type="image/png"/>
    <pubDate>Wed, 21 Jun 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Busca por similaridade de texto com campos vetoriais]]></title>
    <description><![CDATA[Este artigo explora como os embeddings de texto e o novo tipo dense_vector do Elasticsearch podem ser usados para dar suporte à busca por similaridade.]]></description>
    <content:encoded><![CDATA[<p>Desde seus primórdios como um <a href="https://www.elastic.co/about/history-of-elasticsearch">mecanismo de busca de receitas</a>, o Elasticsearch foi projetado para fornecer uma busca de texto completo rápida e poderosa. Dadas essas raízes, aprimorar a busca textual tem sido uma motivação importante para nosso trabalho contínuo com vetores. No Elasticsearch 7.0, introduzimos tipos de campo experimentais para vetores de alta dimensão e, agora, a versão 7.3 traz suporte para o uso desses vetores na pontuação de documentos.</p><p>Este artigo aborda uma técnica específica chamada busca por similaridade de texto. Nesse tipo de busca, o usuário insere uma breve consulta em texto livre, e os documentos são classificados com base em sua similaridade com a consulta. A similaridade textual pode ser útil em diversos casos de uso:</p><ul><li><p><strong>Resposta a perguntas:</strong> Dada uma coleção de perguntas frequentes, encontre perguntas semelhantes àquela que o usuário inseriu.</p></li><li><p><strong>Busca de artigos:</strong> Em uma coleção de artigos de pesquisa, retornar artigos com títulos intimamente relacionados à consulta do usuário.</p></li><li><p><strong>Busca por imagem:</strong> Em um conjunto de dados de imagens com legendas, encontre imagens cuja legenda seja semelhante à descrição do usuário.</p></li></ul><p>Uma abordagem direta para a busca por similaridade seria classificar os documentos com base em quantas palavras eles compartilham com a consulta. Mas um documento pode ser semelhante à consulta mesmo que tenha pouquíssimas palavras em comum — uma noção mais robusta de similaridade levaria em conta também seu conteúdo sintático e <a href="https://en.wikipedia.org/wiki/Semantic_similarity">semântico</a> .</p><p>A comunidade de processamento de linguagem natural (PLN) desenvolveu uma técnica chamada incorporação de texto que codifica palavras e frases como vetores numéricos. Essas representações vetoriais são projetadas para capturar o conteúdo linguístico do texto e podem ser usadas para avaliar a similaridade entre uma consulta e um documento.</p><p>Este artigo explora como os embeddings de texto e o tipo dense_vector do Elasticsearch podem ser usados para dar suporte à busca por similaridade. Primeiramente, apresentaremos uma visão geral das técnicas de incorporação e, em seguida, demonstraremos um protótipo simples de busca por similaridade usando o Elasticsearch.</p><strong>Nota:</strong> O uso de incorporação de texto em pesquisas é uma área complexa e em constante evolução. Este blog não constitui uma recomendação para uma arquitetura ou implementação específica. Comece aqui para aprender como você pode aprimorar sua experiência de busca com o poder da <a href="https://www.elastic.co/what-is/vector-search">busca vetorial</a>.<h2>O que são embeddings de texto?</h2><p>Vamos analisar mais de perto os diferentes tipos de incorporação de texto e como eles se comparam às abordagens de busca tradicionais.</p><h3>Incorporação de palavras</h3><p>Um modelo <a href="https://en.wikipedia.org/wiki/Word_embedding">de incorporação de palavras</a> representa uma palavra como um vetor numérico denso. Esses vetores visam capturar as propriedades semânticas da palavra — palavras cujos vetores estejam próximos uns dos outros devem ser semelhantes em termos de significado semântico. Numa boa incorporação, as direções no espaço vetorial estão ligadas a diferentes aspectos do significado da palavra. Por exemplo, o vetor para "Canadá" pode estar próximo de "França" em uma direção e próximo de "Toronto" em outra.</p><p>As comunidades de PNL (Processamento de Linguagem Natural) e de busca têm demonstrado interesse em representações vetoriais de palavras há bastante tempo. Nos últimos anos, houve um ressurgimento do interesse em word embeddings, quando muitas tarefas tradicionais foram revisitadas usando redes neurais. Alguns algoritmos de incorporação de palavras bem-sucedidos foram desenvolvidos, incluindo <a href="https://papers.nips.cc/paper/5021-distributed-representations-of-words-and-phrases-and-their-compositionality.pdf">o word2vec</a> e <a href="https://nlp.stanford.edu/pubs/glove.pdf">o GloVe</a>. Essas abordagens utilizam grandes coleções de texto e examinam o contexto em que cada palavra aparece para determinar sua representação vetorial:</p><ul><li><p>O modelo Skip-gram do word2vec treina uma rede neural para prever as palavras de contexto ao redor de uma palavra em uma frase. Os pesos internos da rede fornecem os embeddings de palavras.</p></li><li><p>Em GloVe, a similaridade entre palavras depende da frequência com que elas aparecem em conjunto com outras palavras do mesmo contexto. O algoritmo treina um modelo linear simples com base na contagem de coocorrência de palavras.</p></li></ul><p>Muitos grupos de pesquisa distribuem modelos que foram pré-treinados em grandes corpora de texto, como a Wikipédia ou o Common Crawl, tornando-os convenientes para baixar e usar em tarefas subsequentes. Embora versões pré-treinadas sejam às vezes usadas diretamente, pode ser útil ajustar o modelo para que ele se adeque ao conjunto de dados e à tarefa específicos. Isso geralmente é feito executando uma etapa de "ajuste fino" no modelo pré-treinado.</p><p>Os word embeddings provaram ser bastante robustos e eficazes, e agora é prática comum usar embeddings em vez de tokens individuais em tarefas de PNL, como tradução automática e classificação de sentimentos.</p><h3>Incorporação de frases</h3><p>Mais recentemente, os pesquisadores começaram a se concentrar em técnicas de incorporação que representam não apenas palavras, mas também trechos de texto mais longos. A maioria das abordagens atuais baseia-se em arquiteturas complexas de redes neurais e, por vezes, incorpora dados rotulados durante o treinamento para auxiliar na captura de informações semânticas.</p><p>Uma vez treinados, os modelos são capazes de pegar uma frase e produzir um vetor para cada palavra em contexto, bem como um vetor para a frase inteira. Assim como no caso do word embedding, versões pré-treinadas de muitos modelos estão disponíveis, permitindo que os usuários ignorem o dispendioso processo de treinamento. Embora o processo de treinamento possa ser bastante intensivo em recursos, a invocação do modelo é muito mais leve — os modelos de incorporação de sentenças são normalmente rápidos o suficiente para serem usados em aplicações em tempo real.</p><p>Algumas técnicas comuns de incorporação de sentenças incluem <a href="https://arxiv.org/abs/1705.02364">InferSent</a>, <a href="https://arxiv.org/abs/1803.11175">Universal Sentence Encoder</a>, <a href="https://arxiv.org/abs/1802.05365">ELMo</a> e <a href="https://arxiv.org/abs/1810.04805">BERT</a>. A melhoria dos embeddings de palavras e frases é uma área ativa de pesquisa, e é provável que modelos robustos adicionais sejam introduzidos.</p><h3>Comparação com abordagens de busca tradicionais</h3><p>Na recuperação de informação tradicional, uma forma comum de representar texto como um vetor numérico é atribuir uma dimensão para cada palavra do vocabulário. O vetor para um trecho de texto é então baseado no número de vezes que cada termo do vocabulário aparece. Essa forma de representar o texto é frequentemente chamada de "saco de palavras", porque simplesmente contamos as ocorrências de palavras sem levar em consideração a estrutura da frase.</p><p>Os embeddings de texto diferem das representações vetoriais tradicionais em alguns aspectos importantes:</p><ul><li><p>Os vetores codificados são densos e de dimensionalidade relativamente baixa, geralmente variando de 100 a 1.000 dimensões. Em contraste, os vetores de saco de palavras são esparsos e podem conter mais de 50.000 dimensões. Os algoritmos de incorporação codificam o texto em um espaço de menor dimensão como parte da modelagem de seu significado semântico. Idealmente, palavras e frases sinônimas acabam com uma representação semelhante no novo espaço vetorial.</p></li><li><p>Os embeddings de sentenças podem levar em consideração a ordem das palavras ao determinar a representação vetorial. Por exemplo, a expressão "tune in" pode ser representada por um vetor muito diferente de "in tune".</p></li><li><p>Na prática, os embeddings de frases geralmente não se generalizam bem para grandes trechos de texto. Não são comumente usados para representar textos com mais de um parágrafo curto.</p></li></ul><h2>Utilizando embeddings para busca de similaridade</h2><p>Suponhamos que tivéssemos uma grande coleção de perguntas e respostas. Um usuário pode fazer uma pergunta, e queremos recuperar a pergunta mais semelhante em nossa coleção para ajudá-lo a encontrar uma resposta.</p><p>Poderíamos usar incorporações de texto para permitir a recuperação de perguntas semelhantes:</p><ul><li><p>Durante a indexação, cada pergunta é processada por um modelo de incorporação de sentenças para produzir um vetor numérico.</p></li><li><p>Quando um usuário insere uma consulta, ela é processada pelo mesmo modelo de incorporação de sentenças para produzir um vetor. Para classificar as respostas, calculamos a similaridade vetorial entre cada pergunta e o vetor de consulta. Ao comparar vetores de incorporação, é comum usar <a href="https://en.wikipedia.org/wiki/Cosine_similarity">a similaridade de cosseno</a>.</p></li></ul><p><a href="https://github.com/jtibshirani/text-embeddings">Este repositório</a> fornece um exemplo simples de como isso pode ser feito no Elasticsearch. O script principal indexa cerca de 20.000 perguntas do <a href="https://github.com/elastic/rally-tracks/tree/master/so">conjunto de dados do StackOverflow</a> e, em seguida, permite que o usuário insira consultas de texto livre no conjunto de dados.</p><p>Em breve, analisaremos cada parte do script em detalhes, mas primeiro vamos dar uma olhada em alguns exemplos de resultados. Em muitos casos, o método consegue captar semelhanças mesmo quando não há uma sobreposição significativa de palavras entre a consulta e a pergunta indexada:</p><ul><li><p>"Compactar arquivos" retorna "Comprimir/Descomprimir Pastas e Arquivos"</p></li><li><p>"Determinar se algo é um IP" retorna "Como saber se uma string é um IP ou um nome de host?"</p></li><li><p>"Converter bytes em doubles" retorna "Converter bytes em números de ponto flutuante em Python"</p></li></ul><h3>Detalhes da implementação</h3><p><a href="https://github.com/jtibshirani/text-embeddings/blob/blog/src/main.py">O script</a> começa baixando e criando o modelo de incorporação no TensorFlow. Optamos pelo Universal Sentence Encoder do Google, mas é possível usar muitos outros métodos de incorporação. O script utiliza o modelo de incorporação tal como está, sem qualquer treinamento ou ajuste fino adicional.</p><p>Em seguida, criamos o índice do Elasticsearch, que inclui mapeamentos para o título da pergunta, tags e também o título da pergunta codificado como um vetor:</p>"mappings": {
"properties": {
"title": {
"type": "text"
},
"title_vector": {
"type": "dense_vector",
"dims": 512
}
"tags": {
"type": "keyword"
},
...
}
}
<p>No mapeamento para dense_vector, é necessário especificar o número de dimensões que os vetores conterão. Ao indexar um campo title_vector, o Elasticsearch verificará se ele possui o mesmo número de dimensões especificado no mapeamento.</p><p>Para indexar os documentos, aplicamos o modelo de incorporação ao título da pergunta para obter uma matriz numérica. Essa matriz é adicionada ao documento no campo title_vector.</p><p>Quando um usuário insere uma consulta, o texto é primeiro processado pelo mesmo modelo de incorporação e armazenado no parâmetro `query_vector`. A partir da versão 7.3, o Elasticsearch fornece uma <a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.6/query-dsl-script-score-query.html#vector-functions">função de similaridade de cosseno</a> em sua linguagem de script nativa. Para classificar as perguntas com base na sua similaridade com a consulta do usuário, utilizamos uma consulta `script_score`:</p>{
"script_score": {
"query": {"match_all": {}},
"script": {
"source": "cosineSimilarity(params.query_vector, 'title_vector') + 1.0",
"params": {"query_vector": query_vector}
}
}
}
<p>Garantimos passar o vetor de consulta como um parâmetro de script para <a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.6/modules-scripting-using.html#prefer-params">evitar recompilar o script</a>() em cada nova consulta. Como o Elasticsearch não permite pontuações negativas, é necessário adicionar um à similaridade de cosseno.</p><p><strong>Nota:</strong> esta publicação no blog usava originalmente uma <a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.3/query-dsl-script-score-query.html#vector-functions">sintaxe diferente para funções vetoriais</a> , disponível no Elasticsearch 7.3, mas que foi descontinuada na versão 7.6.
|</p><h3>Limitações importantes</h3><p>A consulta script_score foi projetada para encapsular uma consulta restritiva e modificar as pontuações dos documentos retornados. No entanto, fornecemos uma consulta match_all, o que significa que o script será executado em todos os documentos do índice. Essa é uma limitação atual da similaridade vetorial no Elasticsearch — vetores podem ser usados para pontuar documentos, mas não na etapa inicial de recuperação. O suporte para recuperação baseada na similaridade vetorial é uma importante área de <a href="https://github.com/elastic/elasticsearch/issues/42326">trabalho em andamento</a>.</p><p>Para evitar a varredura de todos os documentos e manter um desempenho rápido, a consulta match_all pode ser substituída por uma consulta mais seletiva. A consulta correta a ser usada para recuperação de dados provavelmente dependerá do caso de uso específico.</p><p>Embora tenhamos visto alguns exemplos encorajadores acima, é importante notar que os resultados também podem ser inconsistentes e pouco intuitivos. Por exemplo, "compactar arquivos" também atribui pontuações altas a ".csproj parcial". Arquivos" e "Como evitar arquivos .pyc" arquivos?". E quando o método retorna resultados inesperados, nem sempre é claro como depurar o problema — o significado de cada componente do vetor costuma ser opaco e não corresponde a um conceito interpretável. Com as técnicas tradicionais de pontuação baseadas na sobreposição de palavras, muitas vezes é mais fácil responder à pergunta "por que este documento está bem classificado?".</p><p>Conforme mencionado anteriormente, este protótipo serve como um exemplo de como os modelos de incorporação podem ser usados com campos vetoriais, e não como uma solução pronta para produção. Ao desenvolver uma nova estratégia de busca, é fundamental testar o desempenho da abordagem em seus próprios dados, certificando-se de compará-la com uma base de referência sólida, como uma consulta de correspondência. Pode ser necessário fazer mudanças significativas na estratégia antes que ela alcance resultados sólidos, incluindo o ajuste fino do modelo de incorporação para o conjunto de dados alvo ou a tentativa de diferentes maneiras de incorporar embeddings, como a expansão de consultas em nível de palavra.</p><h2>Conclusões</h2><p>As técnicas de incorporação oferecem uma maneira poderosa de capturar o conteúdo linguístico de um texto. Ao indexar representações vetoriais e atribuir pontuações com base na distância vetorial, podemos comparar documentos usando uma noção de similaridade que vai além da sobreposição em nível de palavras.</p><p>Estamos ansiosos para introduzir mais funcionalidades baseadas no tipo de campo vetorial. O uso de vetores para busca é uma área complexa e em constante desenvolvimento — como sempre, adoraríamos saber mais sobre seus casos de uso e experiências no <a href="https://github.com/elastic/elasticsearch">Github</a> e nos <a href="https://discuss.elastic.co/">fóruns de discussão</a>!</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/text-similarity-search-with-vectors-in-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/text-similarity-search-with-vectors-in-elasticsearch</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <dc:creator><![CDATA[Julie Tibshirani]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt91384bd99b05cd28/6a17e7e01d1b835cc593e467/c633ed737add7d22a7d65b3ca5c56480ef3d8b2c-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 06 Oct 2022 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>